TechDirect.psm1

<#
    .NOTES
    --------------------------------------------------------------------------------
     Code generated by: SAPIEN Technologies, Inc., PowerShell Studio 2020 v5.7.171
     Generated on: 2/21/2020 11:02 AM
     Generated by: dz053479
     Organization: CernerWorks
    --------------------------------------------------------------------------------
    .DESCRIPTION
        Script generated by PowerShell Studio 2020
#>



    <#
        ===========================================================================
         Created with: SAPIEN Technologies, Inc., PowerShell Studio 2019 v5.6.167
         Created on: 9/3/2019 8:37 AM
         Created by: D.Z.
         Organization: N/A
         Filename: TechDirect.psm1
        -------------------------------------------------------------------------
         Module Name: TechDirect
        ===========================================================================
    #>

    
    function Connect-TechDirect
    {
    <#
        .SYNOPSIS
            This function will acquire an Access Token from TechDirect
         
        .DESCRIPTION
            This function will acquire an Access Token from TechDirect for use with TechDirect Warranty and Self-Dispatch APIs
         
        .PARAMETER Warranty
            Used to incidate a request for a Warranty API Token
         
        .PARAMETER Dispatch
            Used to incidate a request for a Self-Dispatch API Token
         
        .PARAMETER Sandbox
            Used to request a token using the sandbox environment.
            Sandbox is not available for the Warranty API.
         
        .PARAMETER ClientID
            Client ID provided by TechDirect for your API access.
         
        .PARAMETER ClientSecret
            Client Secret provided by TechDirect for your API access.
         
        .EXAMPLE
            PS C:\> Connect-TechDirect -ClientID 'ABCDEFGHIJKL' -ClientSecret 'ZYXWVUTSRQPONML'
         
        .EXAMPLE
            PS C:\> Connect-TechDirect -ClientID 'ABCDEFGHIJKL' -ClientSecret 'ZYXWVUTSRQPONML' -Sandbox
         
        .NOTES
            Additional information about the function.
    #>

        
        [CmdletBinding(DefaultParameterSetName = 'Warranty',
                       ConfirmImpact = 'None',
                       PositionalBinding = $true,
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        param
        (
            [Parameter(ParameterSetName = 'Warranty',
                       Position = 1)]
            [switch]$Warranty,
            [Parameter(ParameterSetName = 'Dispatch',
                       Position = 1)]
            [switch]$Dispatch,
            [Parameter(ParameterSetName = 'Dispatch',
                       Position = 2)]
            [switch]$Sandbox,
            [Parameter(Mandatory = $true)]
            [string]$ClientID,
            [Parameter(Mandatory = $true)]
            [string]$ClientSecret
        )
        
        $Bytes = [System.Text.Encoding]::UTF8.GetBytes($($ClientID + ":" + $ClientSecret))
        $Base64Key = [System.Convert]::ToBase64String($Bytes)
        $Header = @{ Authorization = "Basic $Base64Key" }
        $Body = "grant_type=client_credentials"
        if ($sandbox)
        { $Endpoint = "apigtwb2cnp.us.dell.com" }
        else { $Endpoint = "apigtwb2c.us.dell.com" }
        
        Try
        {
            $Token = Invoke-RestMethod "https://$Endpoint/auth/oauth/v2/token" -Method Post -Body $Body -ContentType "application/x-www-form-urlencoded" -Headers $Header
            if ($sandbox)
            { $Token | Add-Member -MemberType NoteProperty -Name sandbox -Value $True }
            else { $Token | Add-Member -MemberType NoteProperty -Name sandbox -Value $False }
            $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force
            if (!(Test-Path "$env:LOCALAPPDATA\TechDirect\"))
            { New-Item -Path "$env:LOCALAPPDATA\TechDirect\" -ItemType directory -Force | Out-Null }
            if ($Warranty)
            {
                $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force
                $Token | ConvertTo-Json | Out-File $env:LOCALAPPDATA\TechDirect\Token-W.json -Force
            }
            elseif ($Dispatch)
            {
                $Token | Add-Member -MemberType NoteProperty "expire_time" -Value (Get-Date).AddSeconds($Token.expires_in) -Force
                $Token | ConvertTo-Json | Out-File $env:LOCALAPPDATA\TechDirect\Token-D.json -Force
            }
            Write-Output "`r`nSuccessfully acquired TechDirect token.`r`n"
            return $Token
        }
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($(($Response | ConvertFrom-Json).error_description), "$statuscode", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    function Get-TDAccessToken
    {
        [CmdletBinding()]
        param
        (
            [switch]$Warranty,
            [switch]$Dispatch
        )
        
        if ($Warranty)
        {
            if (Test-Path "$env:LOCALAPPDATA\TechDirect\Token-W.json")
            {
                try { $TokenInfo = Get-Content $env:LOCALAPPDATA\TechDirect\Token-W.json | ConvertFrom-Json }
                catch
                {
                    $ErrCategory = [system.management.automation.errorcategory]::SecurityError
                    $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null)
                    $PSCmdlet.ThrowTerminatingError($ErrorRecord)
                }
            }
            else
            {
                $ErrCategory = [system.management.automation.errorcategory]::SecurityError
                $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null)
                $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            }
        }
        elseif ($Dispatch)
        {
            if (Test-Path "$env:LOCALAPPDATA\TechDirect\Token-D.json")
            {
                try { $TokenInfo = Get-Content $env:LOCALAPPDATA\TechDirect\Token-D.json | ConvertFrom-Json }
                catch
                {
                    $ErrCategory = [system.management.automation.errorcategory]::SecurityError
                    $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null)
                    $PSCmdlet.ThrowTerminatingError($ErrorRecord)
                }
            }
            else
            {
                $ErrCategory = [system.management.automation.errorcategory]::SecurityError
                $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token does not exist or is corrupted, use 'Connect-TechDirect' to acquire a token", "", $ErrCategory, $Null)
                $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            }
        }
        else
        { return }
        
        $UTCOffset = [System.TimeZoneInfo]::Local.GetUtcOffset((get-date)).totalminutes
        $TokenInfo.expire_time = $TokenInfo.expire_time.AddMinutes($UTCOffset)
        $TokenInfo.expires_in = [math]::Round((New-timespan (get-date) $TokenInfo.expire_time).TotalSeconds, 0)
        if ($TokenInfo.expire_time -le (get-date))
        {
            $ErrCategory = [system.management.automation.errorcategory]::AuthenticationError
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord("Token is expired, use 'Connect-TechDirect' to acquire a new token", "", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            return
        }
        return $TokenInfo
    }
    
    function Format-DispatchPart
    {
        param ([string]$PartNumber,
            [string]$PPID,
            [int]$Quantity)
        $part = "<api:PartInfo>`r`n"
        $Part += "`t<api:PartNumber>$PartNumber</api:PartNumber>`r`n"
        if ($PPID)
        { $Part += "`t<api:PPID>$PPID</api:PPID>`r`n" }
        else { $Part += "`t<api:PPID/>`r`n" }
        $Part += "`t<api:Quanitity>$Quantity</api:Quanitity>`r`n"
        $Part += "</api:PartInfo>"
        return $part
    }
    
    function Format-DispatchAttachment
    {
        param
        (
            [string]$Description,
            [string]$FilePath
        )
        Begin { Add-Type -AssemblyName "System.Web" }
        Process
        {
            $FileName = (Get-Item $FilePath).Name
            if ($Description -eq "")
            { $Description = $FileName }
            
            $MIMEType = [System.Web.MimeMapping]::GetMimeMapping("$FilePath")
            $FileData = Convert-FileToBase64 $FilePath
            $XML = @"
<api:AttachmentInfo>
<api:Description>$Description</api:Description>
<api:FileName>$FileName</api:FileName>
<api:MIMEType>$MIMEType</api:MIMEType>
<api:Data>$FileData</api:Data>
</api:AttachmentInfo>
"@

            return $XML
        }
    }
    
    function Get-TechnicianStatus
    {
    <#
        .SYNOPSIS
            Check Technician Status.
         
        .DESCRIPTION
            Provides account level details. Account should be active and not expired
            for API transactions to complete.
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .EXAMPLE
            $Credentials = (Get-Credential)
            PS C:\> Get-TechnicianStatus -Credentials $Credentials
         
    #>

        
        [CmdletBinding(ConfirmImpact = 'None',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        param
        (
            [Parameter(Mandatory = $false)]
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:")
        )
        
        $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
        $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
        
        $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
   <soapenv:Header/>
   <soapenv:Body>
          <api:CheckUser>
             <api:CheckUserRequest>
                <api:Login>$Username</api:Login>
                <api:Password>$Password</api:Password>
             </api:CheckUserRequest>
          </api:CheckUser>
       </soapenv:Body>
</soapenv:Envelope>
"@

        $DellToken = Get-TDAccessToken -Dispatch
        if ($DellToken.sandbox)
        { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
        else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
        $Headers = @{
            Authorization = "Bearer $($DellToken.access_token)"
            SOAPAction    = "http://api.dell.com/IDispatchService/CheckUser"
        }
        
        try
        {
            $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
            $Result = $Response.Envelope.Body.CheckUserResponse.CheckUserResult.LoginResult
            ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively
            $JSON = [pscustomobject]@{
                FullName = $Result.FullName
                Role     = $Result.Role
                PasswordExpirationDate = [datetime]$Result.PasswordExpirationDate
                Inactive = $Result.Inactive
                Locked   = $Result.Locked
            }
            return $JSON
        }
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    function Get-TechnicianInfo
    {
    <#
        .SYNOPSIS
            Get detailed information about Technician
         
        .DESCRIPTION
            A detailed description of the Get-TechnicianInfo function.
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .EXAMPLE
                    PS C:\> Get-TechnicianInfo
         
        .NOTES
            Additional information about the function.
    #>

        
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory = $false)]
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:")
        )
        
        $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
        $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
        
        $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
    <soapenv:Header/>
    <soapenv:Body>
        <api:CheckLogin>
            <api:CheckLoginRequest>
                <api:Login>$Username</api:Login>
                <api:Password>$Password</api:Password>
            </api:CheckLoginRequest>
          </api:CheckLogin>
    </soapenv:Body>
</soapenv:Envelope>
"@

        
        $DellToken = Get-TDAccessToken -Dispatch
        if ($DellToken.sandbox)
        { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
        else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
        $Headers = @{
            Authorization = "Bearer $($DellToken.access_token)"
            SOAPAction    = "http://api.dell.com/IDispatchService/CheckLogin"
        }
        
        Try
        {
            $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
            $Result = $Response.Envelope.Body.CheckLoginResponse.CheckLoginResult.LoginResult
            
            
            ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively
            $Relationships = $Result.Relationships.RelationshipInfo
            $Groups = @()
            foreach ($Relationship in $Relationships)
            {
                $Group = [pscustomobject]@{
                    BranchName = $Relationship.BranchName
                    CustomerName = $Relationship.CustomerName
                    Track       = $Relationship.Track
                }
                $Groups += $Group
            }
            
            $Certs = $Result.Certificates.CertificateInfo
            $Certifications = @()
            
            foreach ($cert in $Certs)
            {
                $Certificate = [pscustomobject]@{
                    Certificate = $cert.certificate
                    ExpirationDate = $cert.ExpirationDate
                }
                $Certifications += $Certificate
            }
            
            $JSON = [pscustomobject]@{
                FullName = $Result.FullName
                Role     = $Result.Role
                PasswordExpirationDate = [datetime]$Result.PasswordExpirationDate
                HomeBranch = $Result.HomeBranch
                Relationships = $Groups
                Certificates = $Certifications
                DSP         = $Result.DSP
            }
            return $JSON
        }
        
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    function Invoke-DispatchRequest
    {
    <#
        .SYNOPSIS
            Initiate dispatch with TechDirect
         
        .DESCRIPTION
            Function to initiate a request for parts. This request is asynchronous, a Work Order is returned on successfully transacting with the Dispatch system.
            The WO number can then be queried to track the status of the Work Order.
             
            'Get-DispatchStatus' will be the primary method to track status of an WO.
            'Get-Dispatches' function can be used for querying more than one WO.
             
            For successful processing of a WO always submit -
            i. Contact information
            ii. Shipping information
            iii. Parts information
            iv. Service tag of the failed system
            v. Troubleshooting notes and evidence of failure.
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .PARAMETER TechEmail
            Contains the email of the technician
            responsible for the dispatch. In the case of a logistics user this may be different from the login email
         
        .PARAMETER Branch
            Contains the branch associated with the
            dispatch. Note that the technician must be
            authorized to dispatch parts from this branch
         
        .PARAMETER Customer
            Contains the customer associated with the dispatch.
            The customer must have a valid relationship with the branch in order to successfully dispatch
         
        .PARAMETER Track
            Contains the track associated with the Branch
            to Customer relationship (this can be obtained using 'Get-TechnicianInfo')
         
        .PARAMETER ServiceTag
            The service tag associated with the dispatch
         
        .PARAMETER PrimaryContactName
            The primary contact associated with the dispatch.
         
        .PARAMETER PrimaryContactPhone
            The primary contact phone number for the dispatch
         
        .PARAMETER PrimaryContactEmail
            The primary contact email associated with the dispatch.
         
        .PARAMETER AlternateContactName
            Additional/Alternate contact name
         
        .PARAMETER AlternateContactPhone
            Additional/Alternate contact
            number
         
        .PARAMETER ReferencePONumber
            Optional purchase order or internal reference number
         
        .PARAMETER AddressBookName
            The name of a personal or company address book entry. If this is specified the detail address parameters are not allowed.
         
        .PARAMETER CountryISOCode
            ISO country code for the ship-to address
         
        .PARAMETER City
            The ship-to city
         
        .PARAMETER State
            The ship-to state
         
        .PARAMETER ZipCode
            The ship-to zip or postal code
         
        .PARAMETER AddressLine1
            The first ship-to address line
         
        .PARAMETER AddressLine2
            The second ship-to address line
         
        .PARAMETER AddressLine3
            The third ship-to address line
         
        .PARAMETER TimeZone
            A description of the TimeZone parameter.
         
        .PARAMETER RequestCompleteCare
            A true/false parameter indicating if accidental damage applies to this dispatch
         
        .PARAMETER RequestReturnToDepot
            A true/false parameter indicating if return to depot applies to this dispatch
         
        .PARAMETER RequestOnSiteTechnician
            A true/false parameter indicating if an onsite technician is requested
         
        .PARAMETER TroubleshootingNote
            Contains troubleshooting notes, limited to 1000 characters
         
        .PARAMETER Parts
            An array of part information associated with the dispatch.
            This is limited to a maximum of 4 parts per dispatch
             
            Use 'Get-PartsByServiceTag' to query replaceable parts;
            Use 'Format-DispatchPart' to create each part;
         
        .PARAMETER Attachments
            An array of attachments to include with the dispatch.
             
            Use 'Format-DispatchAttachment' to create each attachment.
             
            PS C:\> $attachments = @(
            (Format-DispatchAttachment -filepath 'c:\temp\file.png' -description 'This thing is broken')
            (Format-DispatchAttachment -filepath 'c:\temp\secondfile.jpg' -description 'This is broken too')
            )
         
        .EXAMPLE
            PS C:\> $attachments = @(
            (Format-DispatchAttachment -filepath 'c:\temp\file.png' -description 'This thing is broken')
            (Format-DispatchAttachment -filepath 'c:\temp\secondfile.jpg' -description 'This is broken too')
            )
         
            PS C:\> $Parts = @(
            (Format-DispatchPart -PartName
            )
         
            PS C:\> Invoke-DispatchRequest -Credentials -Email 'Value2' -Branch 'Value3' -Customer 'Value4' -Track 'Value5' -ServiceTag 'Value6' -PrimaryContactName 'Value7' -PrimaryContactPhone 'Value8' -PrimaryContactEmail 'Value9' -CountryISOCode 'Value10' -City 'Value11' -State 'Value12' -ZipCode 'Value13' -AddressLine1 'Value14' -AddressLine2 'Value15' -AddressLine3 'Value16' -TimeZone 'Value17' -RequestCompleteCare $value18 -RequestReturnToDepot $value19 -RequestOnSiteTechnician $value20 -TroubleshootingNote 'Value21'
         
        .EXAMPLE
             
            PS C:\> $paramInvokeDispatchRequest = @{
                Credentials = $Credentials
                TechEmail = "apiuser_us_tech01@uatmail.com"
                Branch = "US Group"
                Customer = "Round Rock Customer"
                Track = "Tier 1"
                ServiceTag = "BR5424J"
                PrimaryContactName = "One,Technician"
                PrimaryContactPhone = "1111111111"
                PrimaryContactEmail = "apiuser_us_tech01@uatmail.com"
                CountryISOCode = $true
                AddressLine1 = "1"
                AddressLine2 = "Main"
                AddressLine3 = "Street"
                City = "New York"
                State = "NY"
                ZipCode = "11111"
                TimeZone = "US/Central"
                RequestCompleteCare = $false
                RequestReturnToDepot = $false
                RequestOnSiteTechnician = $false
                TroubleshootingNote = "THIS IS A TEST"
                Attachments = @(
                    (Format-DispatchAttachment -FilePath 'c:\Temp\file (1).xlsx')
                    (Format-DispatchAttachment -FilePath "c:\Temp\file.xlsx" -Description "This is a file")
                )
                Parts = @((Format-DispatchPart -PartNumber "MBD" -Quantity 1 -PPID "2148242621834394587"))
            }
     
            PS C:\> Invoke-DispatchRequest @paramInvokeDispatchRequest
     
     
         
    #>

        
        [CmdletBinding(DefaultParameterSetName = 'No Address Book',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        [OutputType([pscustomobject])]
        param
        (
            [Parameter(Mandatory = $false,
                       Position = 1)]
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"),
            [Parameter(Mandatory = $true,
                       Position = 2)]
            [Alias('Email')]
            [string]$TechEmail,
            [Parameter(Mandatory = $true,
                       Position = 3)]
            [string]$Branch,
            [Parameter(Mandatory = $true,
                       Position = 4)]
            [Alias('CustomerName')]
            [string]$Customer,
            [Parameter(Mandatory = $true,
                       Position = 5)]
            [string]$Track,
            [Parameter(Mandatory = $true,
                       Position = 6)]
            [Alias('SerialNumber')]
            [string]$ServiceTag,
            [Parameter(Mandatory = $true,
                       Position = 7)]
            [string]$PrimaryContactName,
            [Parameter(Mandatory = $true,
                       Position = 8)]
            [string]$PrimaryContactPhone,
            [Parameter(Mandatory = $true,
                       Position = 9)]
            [string]$PrimaryContactEmail,
            [Parameter(Position = 10)]
            [string]$AlternateContactName,
            [Parameter(Position = 11)]
            [string]$AlternateContactPhone,
            [Parameter(Mandatory = $false,
                       Position = 12)]
            [string]$ReferencePONumber,
            [Parameter(ParameterSetName = 'Using Address Book',
                       Position = 13)]
            [string]$AddressBookName,
            [Parameter(ParameterSetName = 'No Address Book',
                       Position = 13)]
            [string]$CountryISOCode,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 14)]
            [string]$City,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 15)]
            [string]$State,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 16)]
            [string]$ZipCode,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 17)]
            [string]$AddressLine1,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 18)]
            [string]$AddressLine2,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 19)]
            [string]$AddressLine3,
            [Parameter(ParameterSetName = 'No Address Book',
                       Mandatory = $false,
                       Position = 20)]
            [string]$TimeZone,
            [Parameter(Mandatory = $true,
                       Position = 21)]
            [boolean]$RequestCompleteCare,
            [Parameter(Mandatory = $true,
                       Position = 22)]
            [boolean]$RequestReturnToDepot,
            [Parameter(Mandatory = $true,
                       Position = 23)]
            [boolean]$RequestOnSiteTechnician,
            [Parameter(Mandatory = $true,
                       Position = 24)]
            [ValidateLength(0, 1000)]
            [string]$TroubleshootingNote,
            [Parameter(Mandatory = $true,
                       Position = 25)]
            [array]$Parts,
            [Parameter(Position = 26)]
            [array]$Attachments
        )
        
        $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
        $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
        
        $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
    <soapenv:Header/>
    <soapenv:Body>
        <api:CreateDispatch>
            <api:CreateDispatchRequest>
                <api:Login>$Username</api:Login>
                <api:Password>$Password</api:Password>
                    <api:Dispatch>
                        <api:TechEmail>$TechEmail</api:TechEmail>
                        <api:Branch>$Branch</api:Branch>
                        <api:Customer>$Customer</api:Customer>
                        <api:Track>$Track</api:Track>
                        <api:ServiceTag>$ServiceTag</api:ServiceTag>
                        <api:PrimaryContactName>$PrimaryContactName</api:PrimaryContactName>
                        <api:PrimaryContactPhone>$PrimaryContactPhone</api:PrimaryContactPhone>
                        <api:PrimaryContactEmail>$PrimaryContactEmail</api:PrimaryContactEmail>`r`n
"@

        if ($AlternateContactName)
        {
            $XML += @"
                        <api:AlternativeContactName>$AlternateContactName</api:AlternativeContactName>
                        <api:AlternativeContactPhone>$AlternateContactPhone</api:AlternativeContactPhone>`r`n
"@

        }
        else
        {
            $XML += @"
                        <api:AlternativeContactName/>
                        <api:AlternativeContactPhone/>`r`n
"@

        }
        $XML += @"
                        <api:ShipToAddress>`r`n
"@

        if ($AddressBookName)
        {
            $xml += @"
                            <api:AddressBookName>$AddressBookName</api:AddressBookName>`r`n
"@

        }
        else
        {
            $xml += @"
                            <api:AddressBookName/>`r`n
"@

        }
        $XML += @"
                            <api:CountryISOCode>$CountryISOCode</api:CountryISOCode>
                            <api:City>$City</api:City>
                            <api:State>$State</api:State>
                            <api:ZipPostalCode>$ZipCode</api:ZipPostalCode>
                            <api:AddressLine1>$AddressLine1</api:AddressLine1>`r`n
"@

        if ($AddressLine2)
        {
            $XML += @"
                            <api:AddressLine2>$AddressLine2</api:AddressLine2>`r`n
"@

        }
        else { $xml += "`t`t`t`t`t`t`t<api:AddressLine2/>`r`n" }
        if ($AddressLine3)
        {
            $xml += @"
                            <api:AddressLine3>$AddressLine3</api:AddressLine3>`r`n
"@

        }
        else { $xml += "`t`t`t`t`t`t`t<api:AddressLine3/>`r`n" }
        
        
        $XML += @"
                            <api:TimeZone>$TimeZone</api:TimeZone>
                           </api:ShipToAddress>
                        <api:ReferencePONumber>$ReferencePONumber</api:ReferencePONumber>
                        <api:RequestCompleteCare>$($RequestCompleteCare.ToString().ToLower())</api:RequestCompleteCare>
                           <api:RequestReturnToDepot>$($RequestReturnToDepot.ToString().ToLower())</api:RequestReturnToDepot>
                           <api:RequestOnSiteTechnician>$($RequestOnSiteTechnician.ToString().ToLower())</api:RequestOnSiteTechnician>
                           <api:TroubleshootingNote>$TroubleshootingNote</api:TroubleshootingNote>
                        <api:OverrideDPSType/>`r`n
"@

        if ($Parts)
        {
            $XML += @"
                        <api:Parts>
                                 $($Parts -join "`r`n")
                           </api:Parts>`r`n
"@

        }
        else { $XML += "<api:Parts/>`r`n" }
        
        if ($Attachments)
        {
            $XML += @"
                        <api:Attachments>
                                $($Attachments -join "`r`n")
                           </api:Attachments>`r`n
"@

        }
        else { $XML += "<api:Attachments/>`r`n" }
        $XML += @"
                </api:Dispatch>
            </api:CreateDispatchRequest>
        </api:CreateDispatch>
    </soapenv:Body>
</soapenv:Envelope>
"@

        $DellToken = Get-TDAccessToken -Dispatch
        if ($DellToken.sandbox)
        { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
        else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
        $Headers = @{
            Authorization = "Bearer $($DellToken.access_token)"
            SOAPAction    = "http://api.dell.com/IDispatchService/CreateDispatch"
        }
        
        try
        {
            $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
            $Result = $Response.Envelope.Body.CreateDispatchResponse.CreateDispatchResult.DispatchCreateResult
            ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively
            $JSON = [pscustomobject]@{
                DispatchID = $Result.DispatchID
                DispatchCode = $Result.DispatchCode
                Status       = $Result.Status
            }
            return $JSON
        }
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    function Get-DispatchStatus
    {
    <#
        .SYNOPSIS
            Get the status of a dispatch
         
        .DESCRIPTION
            The primary function to track status of the work order. Failure to provide sufficient failure evidence will result in rejection of dispatches. However, there can be other reasons for rejection.
             
            Status codes 'DSP', 'Issues' and 'QUE' indicate the WO has been approved by Dell for dispatch. A Dell dispatch number will be available for these status codes.
            Status code 'Shipped Parts' indicates the requested part(s) has/have been dispatched.
            Status code 'Dispatch Denied' indicates the request has been denied by Dell. Further action is required by the user on this dispatch request. They can contact Dell using phone or other means to proceed with the transaction. They can also use the
            ReSubmitDispatch() method to resubmit the request with more information. If a WO is denied more than three times, it is no longer valid and a new WO must be submitted.
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .PARAMETER Code
            The Dispatch Code to query the status of.
         
        .EXAMPLE
                    PS C:\> Get-DispatchStatus -Code 'Value1'
         
        .NOTES
            Additional information about the function.
    #>

        
        [CmdletBinding(ConfirmImpact = 'None',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        param
        (
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"),
            [Parameter(Mandatory = $true,
                       ValueFromPipeline = $true,
                       ValueFromPipelineByPropertyName = $true)]
            [Alias('DispatchCode')]
            [string]$Code
        )
        
        begin
        {
            $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
            $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
            
            $DellToken = Get-TDAccessToken -Dispatch
            if ($DellToken.sandbox)
            { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
            else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
            $Headers = @{
                Authorization = "Bearer $($DellToken.access_token)"
                SOAPAction    = "http://api.dell.com/IDispatchService/GetDispatchStatus"
            }
        }
        process
        {
            
            $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
   <soapenv:Header/>
   <soapenv:Body>
      <api:GetDispatchStatus>
         <api:GetDispatchStatusRequest>
            <api:Login>$Username</api:Login>
            <api:Password>$Password</api:Password>
            <api:DispatchCode>$Code</api:DispatchCode>
         </api:GetDispatchStatusRequest>
      </api:GetDispatchStatus>
   </soapenv:Body>
</soapenv:Envelope>
"@

            
            try
            {
                $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
                $Result = $Response.Envelope.Body.GetDispatchStatusResponse.GetDispatchStatusResult.DispatchStatusResult
                ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively
                $JSON = [pscustomobject]@{
                    Result = $Result.Result
                    Status = $Result.Status
                    DPSNumber = $Result.DPSNumber
                    DispatchCode = $Result.DispatchCode
                    OrderDeniedReason = $Result.OrderDeniedReason
                }
                return $JSON
            }
            catch
            {
                $result = $_.Exception.Response.GetResponseStream()
                $reader = New-Object System.IO.StreamReader($result)
                $reader.BaseStream.Position = 0
                $reader.DiscardBufferedData()
                $response = $reader.ReadToEnd();
                $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
                $statuscode = $_.Exception.Response.StatusCode.value__
                $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
                
                $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null)
                $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            }
        }
    }
    
    function Get-PartsByServiceTag
    {
    <#
        .SYNOPSIS
            Get Replaceable Parts
         
        .DESCRIPTION
            Provides the list of all replaceable parts for a particular service tag.
            Use this function to query the list of parts that can be requested for dispatch.
             
            This function should be called only once per service tag and the results should be cached at the customer system.
             
            Output of this function will be a mandatory input for 'Invoke-DispatchRequest'.
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .PARAMETER ServiceTag
            The Service Tag to get part info for.
         
        .EXAMPLE
            PS C:\> Get-PartsByServiceTag -ServiceTag 'BR5424J'
         
        .EXAMPLE
            PS C:\> 'BR5424J' | Get-PartsByServiceTag -Credentials $Credentials
     
    #>

        
        [CmdletBinding(ConfirmImpact = 'None',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        [OutputType([pscustomobject])]
        param
        (
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"),
            [Parameter(Mandatory = $true,
                       ValueFromPipeline = $true,
                       ValueFromPipelineByPropertyName = $true)]
            [Alias('SerialNumber')]
            [string]$ServiceTag
        )
        begin
        {
            $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
            $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
            
            $DellToken = Get-TDAccessToken -Dispatch
            if ($DellToken.sandbox)
            { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
            else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
            $Headers = @{
                Authorization = "Bearer $($DellToken.access_token)"
                SOAPAction    = "http://api.dell.com/IDispatchService/GetPartsbyServiceTag"
            }
        }
        process
        {
            
            $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
       <soapenv:Header/>
       <soapenv:Body>
        <api:GetPartsbyServiceTag>
          <api:GetPartsbyServiceTag>
            <api:Login>$Username</api:Login>
            <api:Password>$Password</api:Password>
               <api:ServiceTag>$ServiceTag</api:ServiceTag>
        </api:GetPartsbyServiceTag>
        </api:GetPartsbyServiceTag>
       </soapenv:Body>
</soapenv:Envelope>
"@

            
            
            
            try
            {
                $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
                $Result = $Response.Envelope.Body.GetPartsbyServiceTagResponse.GetPartsbyServiceTagResult.PartsbyTagResult
                ####### We have to parse the XML response manually, since Powershell doesn't want to convert it to JSON natively
                #Ignore these parts, they're not things that would typically be replaced in any laptop
                $PartList = $Result.Parts.PartInformation
                $Parts = @()
                foreach ($Part in $PartList)
                {
                    #Create a temporary object for each part, then add it in to the array of parts
                    $tmp = [pscustomobject]@{
                        PartTypeCode = $Part.PartTypeCode
                        PartNumber   = $part.PartNumber
                        PartDescription = $Part.PartDescription
                    }
                    $Parts += $tmp
                }
                
                #Return to Depot isn't in the response, even though it's in the documentation
                $tmp = [pscustomobject]@{
                    PartTypeCode    = "Accessory"
                    PartNumber        = "RTD"
                    PartDescription = "Return to Depot"
                }
                $Parts += $tmp
                
                
                $JSON = [pscustomobject]@{
                    Model = $Result.Model
                    ModelDescription = $Result.ModelDescription
                    Parts = $parts
                }
                
                return $JSON
            }
            catch
            {
                $result = $_.Exception.Response.GetResponseStream()
                $reader = New-Object System.IO.StreamReader($result)
                $reader.BaseStream.Position = 0
                $reader.DiscardBufferedData()
                $response = $reader.ReadToEnd();
                $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
                $statuscode = $_.Exception.Response.StatusCode.value__
                switch -Wildcard ($response)
                {
                    "*Missing parameters*"{ $ErrCategory = [system.management.automation.errorcategory]::InvalidArgument }
                    default{ $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation }
                }
                $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $Null)
                $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            }
        }
        
    }
    
    function Get-BulkDispatch
    {
    <#
        .SYNOPSIS
            Provide Status Information in Bulk
         
        .DESCRIPTION
            Provides status information for a batch of work orders
         
        .PARAMETER Credentials
            Credential Object containing the Username/Password of the Technician
         
        .PARAMETER CreatedFromDate
            Contains a date constraint to limit the result set. Work
            Orders will only be returned if they were created after this
            date.
         
        .EXAMPLE
            PS C:\> Get-Dispatches -CreatedFromDate "01/01/2019"
             
            This command will return all dispatches created since 1 January 2019 00:00.
         
        .EXAMPLE
            PS C:\> Get-Dispatches -CreatedFromDate (Get-Date).addDays(-7)
             
            This command will return all dispatches created in the last 7 days, at the current time of day.
    #>

        
        [CmdletBinding(ConfirmImpact = 'None',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        [OutputType([pscustomobject])]
        param
        (
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"),
            [Parameter(Mandatory = $true)]
            [datetime]$CreatedFromDate
        )
        
        $Username = ($PSboundparameters.Credentials).getnetworkcredential().Username
        $Password = ($PSboundparameters.Credentials).getnetworkcredential().Password
        
        $FormattedDate = (Get-date $CreatedFromDate -Format o)
        $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
   <soapenv:Header/>
   <soapenv:Body>
      <api:BulkDispatchesInquiry>
         <api:BulkDispatchesInquiryRequest>
            <api:Login>$Username</api:Login>
            <api:Password>$Password</api:Password>
            <api:CreatedFromDate>$FormattedDate</api:CreatedFromDate>
            <api:InStatuses></api:InStatuses>
            <api:Scope>All</api:Scope>
         </api:BulkDispatchesInquiryRequest>
      </api:BulkDispatchesInquiry>
   </soapenv:Body>
</soapenv:Envelope>
"@

        
        
        $DellToken = Get-TDAccessToken -Dispatch
        if ($DellToken.sandbox)
        { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
        else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
        $Headers = @{
            Authorization = "Bearer $($DellToken.access_token)"
            SOAPAction    = "http://api.dell.com/IDispatchService/BulkDispatchesInquiry"
        }
        
        try
        {
            $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
            $Result = $Response.Envelope.Body.BulkDispatchesInquiryResponse.BulkDispatchesInquiryResult.DispatchInquiryResult.DispatchInquiryResult
            
            $Dispatches = @()
            foreach ($Dispatch in $Result)
            {
                $tmp = [pscustomobject]@{
                    Code = $Dispatch.Code
                    DellDispatchNumber = $Dispatch.DellDispatchNumber
                    Status = $Dispatch.Status
                }
                $Dispatches += $tmp
            }
            $Dispatches | ForEach-Object { $_ | Add-Member -MemberType NoteProperty -Name "Status Value" -Value $(Convert-TDStatusCode $_.status) }
            return $Dispatches
        }
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "$statuscode", $ErrCategory, $null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    function Get-DeviceWarranty
    {
    <#
        .SYNOPSIS
            Get warranty information
         
        .DESCRIPTION
            Get the warranty information for a Dell device by providing one or more service tags
         
        .PARAMETER ServiceTag
            One or more Service Tags to get Warranty information for.
            (Max. 100)
         
        .EXAMPLE
            PS C:\> Get-DeviceWarranty -ServiceTags @("B68BK79","OE6W9MA")
         
        .EXAMPLE
            PS C:\> Get-DeviceWarranty -ServiceTags "B68BK79"
         
    #>

        
        [CmdletBinding(SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        param
        (
            [Parameter(Mandatory = $true,
                       ValueFromPipeline = $true,
                       ValueFromPipelineByPropertyName = $true)]
            [ValidateCount(0, 100)]
            [Alias('ServiceTags')]
            [array]$ServiceTag
        )
        
        process
        {
            $URI = "https://apigtwb2c.us.dell.com/PROD/sbil/eapi/v5/assets?servicetags="
            $URI = $URI + $($ServiceTag -join ",")
            $Token = (Get-TDAccessToken -Warranty)
            $Headers = @{ Authorization = "Bearer $($token.access_token)" }
            
            
            
            $response = Invoke-RestMethod -Method GET -Uri $URI -ContentType "application/json" -Headers $Headers
            $response | ForEach-Object {$_.shipdate = [datetime]$_.shipdate}
            return $response
        }
    }
    
    function Redo-DispatchRequest
    {
    <#
        .SYNOPSIS
            Resubmit a Dispatch Request
         
        .DESCRIPTION
            If a Dispatch is denied by Dell, further action is required on this dispatch request.
            When you check the status of SR using Get-DispatchStatus and get "Dispatch Denied", you can re submit the request adding more details.
         
        .PARAMETER Credentials
            Credential Object containing Technician Username/Password
         
        .PARAMETER Code
            The Dispatch Code associated with the denied dispatch
         
        .PARAMETER TroubleshootingNote
            The troubleshooting notes for this dispatch.
            This should explain, in detail, the reason for each part request.
         
        .PARAMETER Parts
            The parts requested in this dispatch.
            Use 'Format-DispatchPart' to create the xml object for each part.
         
        .EXAMPLE
            PS C:\> Redo-DispatchRequest -DispatchCode 'SR999999999'
         
        .NOTES
            Additional information about the function.
    #>

        
        [CmdletBinding(ConfirmImpact = 'Low',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        param
        (
            [Parameter(Mandatory = $false)]
            [pscredential]$Credentials = (Get-Credential -message "Enter TechDirect Credentials:"),
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty()]
            [Alias('DispatchCode')]
            [string]$Code,
            [ValidateNotNullOrEmpty()]
            [string]$TroubleshootingNote,
            [ValidateCount(1, 4)]
            [array]$Parts
        )
        
        $Username = ($PSboundparameters.Credentials).GetNetworkCredential().UserName
        $Password = ($PSboundparameters.Credentials).GetNetworkCredential().Password
        
        $XML = @"
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:api="http://api.dell.com">
    <soapenv:Header/>
    <soapenv:Body>
        <api:ResubmitDispatch>
            <api:ResubmitDispatchRequest>
                <api:Login>$Username</api:Login>
                <api:Password>$Password</api:Password>
                <api:Dispatch>
                    <api:DispatchCode>$Code</api:DispatchCode>
                    <api:TroubleshootingNote>$TroubleshootingNote</api:TroubleshootingNote>
                    <api:Parts>
                        $Parts
                    </api:Parts>
                </api:Dispatch>
            </api:ResubmitDispatchRequest>
        </api:ResubmitDispatch>
    </soapenv:Body>
</soapenv:Envelope>
"@

        
        
        $DellToken = Get-TDAccessToken -Dispatch
        if ($DellToken.sandbox)
        { $endpoint = "https://apigtwb2cnp.us.dell.com/Sandbox/support/dispatch/v3/service" }
        else { $endpoint = "https://apigtwb2c.us.dell.com/PROD/support/dispatch/v3/service" }
        $Headers = @{
            Authorization = "Bearer $($DellToken.access_token)"
            SOAPAction    = "http://api.dell.com/IDispatchService/ResubmitDispatch"
        }
        
        try
        {
            $Response = Invoke-RestMethod $endpoint -Method Post -Body ([xml]$XML) -Headers $Headers -ContentType "text/xml"
            $Result = $Response.Envelope.Body.ResubmitDispatchResponse.ResubmitDispatchResult.DispatchCreateResult
            
            if ($Result.notes)
            {
                $ErrCategory = [system.management.automation.errorcategory]::InvalidData
                $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($Result.Notes.string), " ", $ErrCategory, $Null)
                $PSCmdlet.ThrowTerminatingError($ErrorRecord)
            }
            else
            {return $Result}
        }
        catch
        {
            $result = $_.Exception.Response.GetResponseStream()
            $reader = New-Object System.IO.StreamReader($result)
            $reader.BaseStream.Position = 0
            $reader.DiscardBufferedData()
            $response = $reader.ReadToEnd();
            $ResponseMessage = ([xml]$Response).Envelope.Body.Fault
            $statuscode = $_.Exception.Response.StatusCode.value__
            $ErrCategory = [system.management.automation.errorcategory]::InvalidOperation
            
            $ErrorRecord = New-Object System.Management.Automation.ErrorRecord($($ResponseMessage.faultstring), "", $ErrCategory, $Null)
            $PSCmdlet.ThrowTerminatingError($ErrorRecord)
        }
    }
    
    #region Helper Functions
    function Convert-FileToBase64
    {
        param
        (
            [Parameter(ValueFromPipeline = $true)]
            $Filepath
        )
        process
        {
            $bufferSize = 9000 # should be a multiplier of 3
            $buffer = New-Object byte[] $bufferSize
            
            $reader = [System.IO.File]::OpenRead($Filepath)
            $writer = ""
            $bytesRead = 0
            do
            {
                $bytesRead = $reader.Read($buffer, 0, $bufferSize);
                $writer += ([Convert]::ToBase64String($buffer, 0, $bytesRead))
            }
            while ($bytesRead -eq $bufferSize);
            
            $reader.Dispose()
            return $writer
        }
    }
    
    
    function Convert-TDStatusCode
    {
    <#
        .SYNOPSIS
            Convert a status code to text
         
        .DESCRIPTION
            Convert a status code to its text value.
         
        .PARAMETER Status
            The Staus Code to convert
         
        .EXAMPLE
            PS C:\> Convert-TDStatusCode -Status 'HOLD'
         
    #>

        
        [CmdletBinding(ConfirmImpact = 'None',
                       SupportsPaging = $false,
                       SupportsShouldProcess = $false)]
        [OutputType([string])]
        param
        (
            [Parameter(Mandatory = $true,
                       ValueFromPipeline = $true,
                       ValueFromPipelineByPropertyName = $true)]
            [Alias('StatusCode')]
            [string]$Status
        )
        
        begin
        {
            $Codes = @{
                "Request submitted to Dell and now under review" = @(
                    "2H SBD"
                    "APJ Queue"
                    "BIL ERROR"
                    "CAR"
                    "Complete Care"
                    "HOLD"
                    "L2 Review"
                    "L2 DISPATCH"
                    "NBD"
                    "NON LATIN 1"
                    "NPO"
                    "Parts Review"
                    "Pending Supervisor"
                    "PND"
                    "Request Submitted"
                    "Request Resubmitted"
                    "Returned to Depot"
                    "SBD"
                    "SBD4"
                    "TAG/CUSTOMER MISMATCH"
                )
                "Claim Submitted"                                 = @(
                    "Claim Submitted"
                    "Parts Returned/Claim Submitted"
                )
                "CAD Deferred"                                     = @("CAD-Deferred")
                "FED Queue"                                         = @("FED")
                "Moved to Claims Process"                         = "Moved_to_Claims_Process"
                "Defective Part Received"                         = "Defective Part Received"
                "Request has been denied by Dell"                 = "Dispatch Denied"
                "Request has been approved by Dell"                 = @(
                    "Issued"
                    "DSP"
                    "ORD"
                    "QUE"
                )
                "Create Dispatch/Place Request"                     = "START"
                "Pending Request 2"                                 = "Pending 2"
                "Request has not been submitted to Dell"         = "Pending Request"
                "Shipped Parts"                                     = "Shipped Parts"
            }
            
        }
        
        Process
        {
            $codes.keys | % {
                if ($codes.$_ -contains "$status") { return $_ }
            }
        }
    }
    
    
    #endregion Helper Functions
    
    
# SIG # Begin signature block
# MIIQ/wYJKoZIhvcNAQcCoIIQ8DCCEOwCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBoP4YRYKYZrzY8
# rXzlRevdO++1X7/caBnq0aECWn0fKaCCC+8wggMIMIIB8KADAgECAhAeLeTkRNs8
# gEMIvpGunxhLMA0GCSqGSIb3DQEBCwUAMBwxGjAYBgNVBAMMEURaMDUzNDc5X0Nv
# ZGVTaWduMB4XDTIwMDIwNjE5MDUzNloXDTIxMDIwNjE5MjUzNlowHDEaMBgGA1UE
# AwwRRFowNTM0NzlfQ29kZVNpZ24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
# AoIBAQD0az+EFRw2OiqtWw74lG8NlvxrQ695XftwhBkiNUyAk7hno3dJASd0Dp7Q
# FhwS7JiGNBjVZEmoM7YbvPF3ePC4N7fYImvmRoHTeB8zXH9/VTFRTg2jrDXq5UpR
# ppL3fGVxGQuB3+YrxmWPGqlaNS4N3uUmS0XOlEseJ97bmsp4orwOwxK/NjBx15bC
# IGyD4XiTRpy3/fdKUHJA61N11umpr914sMiXEY2kR6M+1olbKZA0ABfMxJl2oZGx
# UQcApKsdYg2/yDfBMAGxBrzCaJKAVUqCR6KTWaOJ1dTvkB60Y7RE6jdAixyitxbU
# kaCpTcjgL6CRR18+imLKEbZtyHQ9AgMBAAGjRjBEMA4GA1UdDwEB/wQEAwIHgDAT
# BgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUMRbqaTD35MDPR4hmHkrBnaTp
# v6UwDQYJKoZIhvcNAQELBQADggEBAMrAn7J8yHlxcaEuRbRjwCafkZeQldpSl+cJ
# 9YJwSlqpBQporOKg5qAGZCLuXqIB/efIxyZTfmrAFRBSaxudptMce1JeA5YpJqew
# 9ZNdpgsB2PFcQhB9CXTja6gmaTlsRbmV8Td6xrfexga3LbtOlGIY0Ji5rAC6MeWE
# tUTaCKNmcyEwb40HKBygmEUWEy2ol+r9GXC5XSoJMeSq3MTw1n8YXPbmnD6r5C23
# pWtaAgmd4/msj4bhxDhr92l6EbGWfLrDNxT+Kt2pdbF7fS1HkMCLPG4KB7XssY53
# gkJ67BGqNGOeoBs6YbQ7qRyn673U5IJkmKN7INfRbumuIOz8cnEwggQVMIIC/aAD
# AgECAgsEAAAAAAExicZQBDANBgkqhkiG9w0BAQsFADBMMSAwHgYDVQQLExdHbG9i
# YWxTaWduIFJvb3QgQ0EgLSBSMzETMBEGA1UEChMKR2xvYmFsU2lnbjETMBEGA1UE
# AxMKR2xvYmFsU2lnbjAeFw0xMTA4MDIxMDAwMDBaFw0yOTAzMjkxMDAwMDBaMFsx
# CzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9iYWxTaWduIG52LXNhMTEwLwYDVQQD
# EyhHbG9iYWxTaWduIFRpbWVzdGFtcGluZyBDQSAtIFNIQTI1NiAtIEcyMIIBIjAN
# BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpuOw6sRUSUBtpaU4k/YwQj2RiPZ
# RcWVl1urGr/SbFfJMwYfoA/GPH5TSHq/nYeer+7DjEfhQuzj46FKbAwXxKbBuc1b
# 8R5EiY7+C94hWBPuTcjFZwscsrPxNHaRossHbTfFoEcmAhWkkJGpeZ7X61edK3wi
# 2BTX8QceeCI2a3d5r6/5f45O4bUIMf3q7UtxYowj8QM5j0R5tnYDV56tLwhG3NKM
# vPSOdM7IaGlRdhGLD10kWxlUPSbMQI2CJxtZIH1Z9pOAjvgqOP1roEBlH1d2zFuO
# BE8sqNuEUBNPxtyLufjdaUyI65x7MCb8eli7WbwUcpKBV7d2ydiACoBuCQIDAQAB
# o4HoMIHlMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1Ud
# DgQWBBSSIadKlV1ksJu0HuYAN0fmnUErTDBHBgNVHSAEQDA+MDwGBFUdIAAwNDAy
# BggrBgEFBQcCARYmaHR0cHM6Ly93d3cuZ2xvYmFsc2lnbi5jb20vcmVwb3NpdG9y
# eS8wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5nbG9iYWxzaWduLm5ldC9y
# b290LXIzLmNybDAfBgNVHSMEGDAWgBSP8Et/qC5FJK5NUPpjmove4t0bvDANBgkq
# hkiG9w0BAQsFAAOCAQEABFaCSnzQzsm/NmbRvjWek2yX6AbOMRhZ+WxBX4AuwEIl
# uBjH/NSxN8RooM8oagN0S2OXhXdhO9cv4/W9M6KSfREfnops7yyw9GKNNnPRFjbx
# vF7stICYePzSdnno4SGU4B/EouGqZ9uznHPlQCLPOc7b5neVp7uyy/YZhp2fyNSY
# BbJxb051rvE9ZGo7Xk5GpipdCJLxo/MddL9iDSOMXCo4ldLA1c3PiNofKLW6gWlk
# KrWmotVzr9xG2wSukdduxZi61EfEVnSAR3hYjL7vK/3sbL/RlPe/UOB74JD9IBh4
# GCJdCC6MHKCX8x2ZfaOdkdMGRE4EbnocIOM28LZQuTCCBMYwggOuoAMCAQICDCRU
# uH8eFFOtN/qheDANBgkqhkiG9w0BAQsFADBbMQswCQYDVQQGEwJCRTEZMBcGA1UE
# ChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFsU2lnbiBUaW1lc3Rh
# bXBpbmcgQ0EgLSBTSEEyNTYgLSBHMjAeFw0xODAyMTkwMDAwMDBaFw0yOTAzMTgx
# MDAwMDBaMDsxOTA3BgNVBAMMMEdsb2JhbFNpZ24gVFNBIGZvciBNUyBBdXRoZW50
# aWNvZGUgYWR2YW5jZWQgLSBHMjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
# ggEBANl4YaGWrhL/o/8n9kRge2pWLWfjX58xkipI7fkFhA5tTiJWytiZl45pyp97
# DwjIKito0ShhK5/kJu66uPew7F5qG+JYtbS9HQntzeg91Gb/viIibTYmzxF4l+lV
# ACjD6TdOvRnlF4RIshwhrexz0vOop+lf6DXOhROnIpusgun+8V/EElqx9wxA5tKg
# 4E1o0O0MDBAdjwVfZFX5uyhHBgzYBj83wyY2JYx7DyeIXDgxpQH2XmTeg8AUXODn
# 0l7MjeojgBkqs2IuYMeqZ9azQO5Sf1YM79kF15UgXYUVQM9ekZVRnkYaF5G+wcAH
# dbJL9za6xVRsX4ob+w0oYciJ8BUCAwEAAaOCAagwggGkMA4GA1UdDwEB/wQEAwIH
# gDBMBgNVHSAERTBDMEEGCSsGAQQBoDIBHjA0MDIGCCsGAQUFBwIBFiZodHRwczov
# L3d3dy5nbG9iYWxzaWduLmNvbS9yZXBvc2l0b3J5LzAJBgNVHRMEAjAAMBYGA1Ud
# JQEB/wQMMAoGCCsGAQUFBwMIMEYGA1UdHwQ/MD0wO6A5oDeGNWh0dHA6Ly9jcmwu
# Z2xvYmFsc2lnbi5jb20vZ3MvZ3N0aW1lc3RhbXBpbmdzaGEyZzIuY3JsMIGYBggr
# BgEFBQcBAQSBizCBiDBIBggrBgEFBQcwAoY8aHR0cDovL3NlY3VyZS5nbG9iYWxz
# aWduLmNvbS9jYWNlcnQvZ3N0aW1lc3RhbXBpbmdzaGEyZzIuY3J0MDwGCCsGAQUF
# BzABhjBodHRwOi8vb2NzcDIuZ2xvYmFsc2lnbi5jb20vZ3N0aW1lc3RhbXBpbmdz
# aGEyZzIwHQYDVR0OBBYEFNSHuI3m5UA8nVoGY8ZFhNnduxzDMB8GA1UdIwQYMBaA
# FJIhp0qVXWSwm7Qe5gA3R+adQStMMA0GCSqGSIb3DQEBCwUAA4IBAQAkclClDLxA
# CabB9NWCak5BX87HiDnT5Hz5Imw4eLj0uvdr4STrnXzNSKyL7LV2TI/cgmkIlue6
# 4We28Ka/GAhC4evNGVg5pRFhI9YZ1wDpu9L5X0H7BD7+iiBgDNFPI1oZGhjv2Mbe
# 1l9UoXqT4bZ3hcD7sUbECa4vU/uVnI4m4krkxOY8Ne+6xtm5xc3NB5tjuz0PYbxV
# fCMQtYyKo9JoRbFAuqDdPBsVQLhJeG/llMBtVks89hIq1IXzSBMF4bswRQpBt3yS
# br5OkmCCyltk5lXT0gfenV+boQHtm/DDXbsZ8BgMmqAc6WoICz3pZpendR4PvyjX
# CSMN4hb6uvM0MYIEZjCCBGICAQEwMDAcMRowGAYDVQQDDBFEWjA1MzQ3OV9Db2Rl
# U2lnbgIQHi3k5ETbPIBDCL6Rrp8YSzANBglghkgBZQMEAgEFAKBMMBkGCSqGSIb3
# DQEJAzEMBgorBgEEAYI3AgEEMC8GCSqGSIb3DQEJBDEiBCCbF3321rczN0mauIXE
# oJJqmcSwb21Ogp20qUzFycLnjzANBgkqhkiG9w0BAQEFAASCAQA2r/l75Myj3Lpr
# e5kT3ki1ITr3eyntHMp94cunTMVN8FJJQf2lKLD5hLZsaVz3j0HiETE+vB8EGuWp
# dh9EvbHuqckTxHE86l/CdmNUlaGwYtBCFxlBL+kD640V1m9oISWn05g8f1Wl7//h
# rU1QoUiQnE8TZ2VKsJPKDIsr9Q76mbq3bkWnDn3MMjicLX+PWaE/lBxvDx1M9KsV
# l0smA8puHogaQXhat5Vtt87oFGoOINOPkKhlLK/mGb2OTJ84UlEmcbzEPQyti66p
# SjcK98LpzU36INYRxPx79xcPDNkUMp8e4uyz7BKkbJRAzMfggcJ1Y3dPNGN8RQHa
# s1ruOIkIoYICuTCCArUGCSqGSIb3DQEJBjGCAqYwggKiAgEBMGswWzELMAkGA1UE
# BhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExMTAvBgNVBAMTKEdsb2Jh
# bFNpZ24gVGltZXN0YW1waW5nIENBIC0gU0hBMjU2IC0gRzICDCRUuH8eFFOtN/qh
# eDANBglghkgBZQMEAgEFAKCCAQwwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc
# BgkqhkiG9w0BCQUxDxcNMjAwMjIxMTcwMjM5WjAvBgkqhkiG9w0BCQQxIgQgh+d8
# 21v9WDGf8Pg0A2eHfwHfvXLBiIAIkq/Ca/EaMdowgaAGCyqGSIb3DQEJEAIMMYGQ
# MIGNMIGKMIGHBBQ+x2bV1NRy4hsfIUNSHDG3kNlLaDBvMF+kXTBbMQswCQYDVQQG
# EwJCRTEZMBcGA1UEChMQR2xvYmFsU2lnbiBudi1zYTExMC8GA1UEAxMoR2xvYmFs
# U2lnbiBUaW1lc3RhbXBpbmcgQ0EgLSBTSEEyNTYgLSBHMgIMJFS4fx4UU603+qF4
# MA0GCSqGSIb3DQEBAQUABIIBADrgOBt9TZx0eh7Y6d4B4I5kxy7aignacksLZrE3
# rv1g5wOT8DeT73lxoXyWxSVhGkAlX0voFJwRWRaK7+sxIAqRxWrqGnb4OgK8s/Xb
# mA/NIEUk7Fo3ZZSnPp0ZTbrZChUWfrTgBU1WdlESbpbFtbdhjnAjvN350sytUpfv
# 77cgnr671juv8j9eg3Pw939JYc4Dq0dG/gk7cV3ViP7VP5akA/wewDCEyLhN9v0V
# Wqn1F3WDHX4JUSZRmtf8tPX8P0gr3ImyxtcfzYcYI4ztpatuwzyk4VMXyVCgKySP
# fg690WIKfb+An0eKgRb4jNvQQ75fthW25ziZU4NMCTGHyEA=
# SIG # End signature block