Billomat.psm1

$baseUrl = "https://guidnew.billomat.net/api";

<#
.SYNOPSIS
Connects to Billomat.
 
.DESCRIPTION
Credentials can be persisted using https://www.powershellgallery.com/packages/CredentialsManager/ by setting the parameter UseCredentialsManager to $True.
o $Credential -eq $Null -and $UseCredentialsManager -eq $Null -> credentials are requested and are not persisted
o $Credential -ne $Null -and $UseCredentialsManager -eq $Null -> passed credentials are used and not persisted
o $Credential -ne $Null -and $UseCredentialsManager -ne $Null -> passed credentials are used and persisted
o $Credential -eq $Null -and $UseCredentialsManager -ne $Null -> Persisted credentials are used
 
.PARAMETER Credential
Credential to access Billomat with. I not provided they get requested.
 
.PARAMETER UseCredentialsManager
If $True, credentials are read and written using the CredentialsManager. Only Password part, containing the APIKey, is used.
 
#>

function Connect-BMT
{
    [CmdletBinding()]
    Param(
       [PSCredential]$Credential,
       [Boolean]$UseCredentialsManager = $False
    ) #end param

    # check module prerequisites
    if($UseCredentialsManager)
    {
        $module = Get-Module -ListAvailable -Name "CredentialsManager";
        if (!$module) { throw "Module 'CredentialsManager' needed. Please install executing 'Install-Module -Name CredentialsManager' as Administrator."; }
    }
    
    if($UseCredentialsManager -and $Credential -eq $Null)
    {
        $Credential = Read-Credential -ListAvailable | Where { $_.Environment -eq "Billomat" }
    }
    if(!$Credential) { $Credential = Get-Credential -Message "Please enter API Key for Billomat as password (username can be anything)."; }
    if($UseCredentialsManager) 
    { 
        Write-Credential "Billomat" -Credential $Credential; 
    }

    $script:key = $Credential.GetNetworkCredential().password ;
}

class InvoiceItem 
{
    [string]$unit
    [decimal]$quantity
    [decimal]$unit_price
    [string]$title
    [string]$description
}

<#
.SYNOPSIS
Adds an invoice to Billomat.
 
.DESCRIPTION
 
.PARAMETER ClientId
Id of client to add the invoice for
 
.PARAMETER Label
Optional Label of Invoice
 
.PARAMETER InvoiceItems
Items of the Invoice
 
#>

function Add-BMTInvoice
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)][string]$ClientId,
        [ValidateSet('SUPPLY_DATE','DELIVERY_DATE','SUPPLY_TEXT', 'DELIVERY_TEXT')]$SupplyDateType,
        [string]$SupplyDate,
        [string]$Label,
        [InvoiceItem[]]$InvoiceItems
    ) #end param

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/invoices" $parameters

    $bodyAsJson = ConvertTo-JSON( @{ invoice= 
        @{ 
            client_id=$ClientId; 
            supply_date_type=$SupplyDateType;
            supply_date=$SupplyDate;
            label=$Label;
        }
    });

    $result = $(Invoke-RestMethod -Uri $uri -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});
    
    foreach($item in $InvoiceItems) 
    {
        Add-BMTInvoiceItem -InvoiceId $result.invoice.id -Item $item | Out-Null;
    }

    return $result.invoice;
}

<#
.SYNOPSIS
Adds an item to an invoice.
 
.DESCRIPTION
 
.PARAMETER InvoiceId
Id of invoice to add item to
 
.PARAMETER Item
Item to be added
 
#>

function Add-BMTInvoiceItem
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)][string]$InvoiceId,
        [InvoiceItem]$Item
    ) #end param

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/invoice-items" $parameters

    $bodyAsJson = ConvertTo-JSON( @{ "invoice-item"= 
        @{ 
            invoice_id=$InvoiceId; 
            unit=$Item.unit;
            unit_price=$Item.unit_price;
            quantity=$Item.quantity;
            title=$Item.title;
            description=$Item.description;
        }
    });

    $result = $(Invoke-RestMethod -Uri $uri -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});
    return $result."invoice-item"
}

class CreditNoteItem 
{
    [string]$unit
    [decimal]$quantity
    [decimal]$unit_price
    [string]$title
    [string]$tax_name
    [decimal]$tax_rate
    [string]$description
}

<#
.SYNOPSIS
Adds a credit note to Billomat.
 
.DESCRIPTION
 
.PARAMETER ClientId
Id of client to add the credit note for
 
.PARAMETER Label
Optional Label of Credit Note
 
.PARAMETER TemplateId
Optional Id of Template to use
 
.PARAMETER CurrencyCode
Optional CurrencyCode of Credit Note
 
.PARAMETER InvoiceId
Optional Id of Invoice the creditnote was created from.
 
.PARAMETER CreditNoteItems
Items of the Credit note
 
#>

function Add-BMTCreditNote
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)]
        [string]$ClientId,
        [string]$Label,
        [decimal]$TemplateId,
        [string]$CurrencyCode,
        [long]$InvoiceId,
        [CreditNoteItem[]]$CreditNoteItems
    ) #end param

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/credit-notes" $parameters

    $creditNote = @{ client_id=$ClientId; }
    if($Label) { $creditNote.Add("label", $Label); }
    if($TemplateId) { $creditNote.Add("template_id", $TemplateId); }
    if($CurrencyCode) { $creditNote.Add("currency_code", $CurrencyCode); }
    if($InvoiceId) { $creditNote.Add("invoice_id", $InvoiceId); }
    
    $bodyAsJson = ConvertTo-JSON( @{ "credit-note"= $creditNote });

    $result = $(Invoke-RestMethod -Uri $uri -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});
    
    foreach($item in $CreditNoteItems) 
    {
        Add-BMTCreditNoteItem -CreditNoteId $result."credit-note".id -Item $item | Out-Null;
    }
    return $result."credit-note";
}

<#
.SYNOPSIS
Adds an item to a credit note.
 
.DESCRIPTION
 
.PARAMETER CreditNoteId
Id of credit note to add item to
 
.PARAMETER Item
Item to be added
 
#>

function Add-BMTCreditNoteItem
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$true)][string]$CreditNoteId,
        [CreditNoteItem]$Item
    ) #end param

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/credit-note-items" $parameters

    $creditNoteItem = @{ credit_note_id=$CreditNoteId; }
    if($Item.unit) { $creditNoteItem.Add("unit", $Item.unit); }
    if($Item.unit_price) { $creditNoteItem.Add("unit_price", $Item.unit_price); }
    if($Item.quantity) { $creditNoteItem.Add("quantity", $Item.quantity); }
    if($Item.title) { $creditNoteItem.Add("title", $Item.title); }
    if($Item.tax_name) { $creditNoteItem.Add("tax_name", $Item.tax_name); }
    if($Item.tax_rate) { $creditNoteItem.Add("tax_rate", $Item.tax_rate); }
    if($Item.description) { $creditNoteItem.Add("description", $Item.description); }
    
    $bodyAsJson = ConvertTo-JSON( @{ "credit-note-item"= $creditNoteItem });

    $result = $(Invoke-RestMethod -Uri $uri -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});
    return $result."credit-note-item"
}

<#
.SYNOPSIS
Get Invoice Payments by Invoice Id or from/until
 
.PARAMETER InvoiceId
Id of Invoice to filter the payments for.
 
.PARAMETER From
Date from which on to receive payments. Only date-part is considered.
 
.PARAMETER To
Date until which on to receive payments to. Only date-part is considered.
 
#>

function Get-BMTInvoicePayments
{
    [CmdletBinding()]
    Param(
       [string]$InvoiceId,
       [DateTime]$From,
       [DateTime]$To
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    if($InvoiceId) { $parameters.Add("invoice_id", $InvoiceId) }
    if($From) { $parameters.Add("from", $From.ToString('yyyy-MM-dd')) }
    if($To) { $parameters.Add("to", $To.ToString('yyyy-MM-dd')) }
    $uri = New-HttpQueryString "${baseUrl}/invoice-payments" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response."invoice-payments"."@total" -eq 0) { return @(); }
    
    return $response."invoice-payments"."invoice-payment"
}

<#
.SYNOPSIS
Get Invoice by ClientId or Status from Billomat
 
.PARAMETER ClientId
Optional Id of client to get.
 
.PARAMETER Status
Optional Status of invoices to get (DRAFT, OPEN, PAID, OVERDUE, CANCELED). Separate by comma for multiple stati
 
.PARAMETER From
Optional startdate to get invoices from
 
.PARAMETER To
Optional enddate to get invoices until
 
#>

function Get-BMTInvoices
{
    [CmdletBinding()]
    Param(
       [string]$ClientId,
       [string]$Status,
       [DateTime]$From,
       [DateTime]$To
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    if($ClientId) { $parameters.Add("client_id", $ClientId) }
    if($Status) { $parameters.Add("status", $Status) }
    if($From) { $parameters.Add("from", $From.ToString('yyyy-MM-dd')) }
    if($To) { $parameters.Add("to", $To.ToString('yyyy-MM-dd')) }
    $uri = New-HttpQueryString "${baseUrl}/invoices" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response.invoices."@total" -eq 0) { return @(); }
    
    return $response.invoices.invoice
}

<#
.SYNOPSIS
Get Invoice by InvoiceId
 
.PARAMETER Id
Id of invoice to get.
 
#>

function Get-BMTInvoice
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/invoices/${Id}" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    return $response.invoice
}

<#
.SYNOPSIS
Return the PDF of the Invoice
 
.PARAMETER Id
Id of invoice to get PDF for.
 
#>

function Read-BMTInvoicePdf
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; format="pdf" }
    $uri = New-HttpQueryString "${baseUrl}/invoices/${Id}/pdf" $parameters

    $response = Invoke-WebRequest -Uri $uri -Method Get -ContentType "application/pdf" -Headers @{Accept="application/pdf"};

    return [byte[]]$response.Content;
}

<#
.SYNOPSIS
Get Credite Note by CreditNoteId
 
.PARAMETER Id
Id of credit note to get.
 
#>

function Get-BMTCreditNote
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/credit-notes/${Id}" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    return $response."credit-note"
}

<#
.SYNOPSIS
Return the PDF of the CreditNote
 
.PARAMETER Id
Id of creditnote to get PDF for.
 
#>

function Read-BMTCreditNotePdf
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; format="pdf" }
    $uri = New-HttpQueryString "${baseUrl}/credit-notes/${Id}/pdf" $parameters

    $response = Invoke-WebRequest -Uri $uri -Method Get -ContentType "application/pdf" -Headers @{Accept="application/pdf"};

    return [byte[]]$response.Content;
}

<#
.SYNOPSIS
Get Templates from Billomat
 
.PARAMETER type
Optional Type of template to filter for (INVOICE, OFFER, CONFIRMATION, REMINDER, DELIVERY_NOTE, CREDIT_NOTE, LETTER). Separate by comma for multiple types
 
#>

function Get-BMTTemplates
{
    [CmdletBinding()]
    Param(
       [string]$Type
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    if($Type) { $parameters.Add("type", $Type) }
    $uri = New-HttpQueryString "${baseUrl}/templates" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response.templates."@total" -eq 0) { return @(); }
    
    return $response.templates.template
}

<#
.SYNOPSIS
Get Client by Name or ClientNumber from Billomat
 
.DESCRIPTION
If no client is found, an empty array is returned.
 
.PARAMETER Name
Name of client to get. Returns all clients having the given value in their name.
 
.PARAMETER ClientNumber
Number of client to get.
 
#>

function Get-BMTClients
{
    [CmdletBinding()]
    Param(
       [string]$Name,
       [string]$ClientNumber
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    if($Name) { $parameters.Add("name", $Name) }
    if($ClientNumber) { $parameters.Add("client_number", $ClientNumber) }
    $uri = New-HttpQueryString "${baseUrl}/clients" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response.clients."@total" -eq 0) { return @(); }
    
    return $response.clients.client
}

<#
.SYNOPSIS
Get Client by Id
 
.DESCRIPTION
Custom Attributes are resolved into a flat list
 
.PARAMETER Id
Id of client to get.
 
#>

function Get-BMTClient
{
    [CmdletBinding()]
    Param(
       [long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/clients/${Id}" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};
    if($response.client.'client-property-values'.'client-property-value') {
        $response.client.'client-property-values'.'client-property-value' | ForEach-Object { $response.client | Add-Member -MemberType NoteProperty -Name "property$($_.client_property_id)" -Value $_.value }
    }
    return $response.client
}

<#
.SYNOPSIS
Get Contacts by client
 
.DESCRIPTION
If no contact is found, an empty array is returned.
 
.PARAMETER ClientId
Id of client to receive contacts for.
 
#>

function Get-BMTContacts
{
    [CmdletBinding()]
    Param(
       [int]$ClientId
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; client_id=$ClientId }
    $uri = New-HttpQueryString "${baseUrl}/contacts" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response.contacts."@total" -eq 0) { return @(); }

    return $response.contacts.contact
}

<#
.SYNOPSIS
Get Contact by Id
 
.DESCRIPTION
 
.PARAMETER Id
Id of contact to get.
 
#>

function Get-BMTContact
{
    [CmdletBinding()]
    Param(
       [int]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/contacts/${Id}" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    return $response.contact
}

<#
.SYNOPSIS
Get Offer by OfferId
 
.PARAMETER Id
Id of offer to get.
 
#>

function Get-BMTOffer
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/offers/${Id}" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    return $response.offer
}

<#
.SYNOPSIS
Return the PDF of the Offer
 
.PARAMETER Id
Id of offer to get PDF for.
 
#>

function Read-BMTOfferPdf
{
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true)][long]$Id
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; format="pdf" }
    $uri = New-HttpQueryString "${baseUrl}/offers/${Id}/pdf" $parameters

    $response = Invoke-WebRequest -Uri $uri -Method Get -ContentType "application/pdf" -Headers @{Accept="application/pdf"};

    return [byte[]]$response.Content;
}

<#
.SYNOPSIS
Get Offers by offernumber
 
.DESCRIPTION
 
.PARAMETER OfferNumber
Number of offer to get.
 
#>

function Get-BMTOffers
{
    [CmdletBinding()]
    Param(
       [string]$OfferNumber
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; offer_number=$OfferNumber; }

    $uri = New-HttpQueryString "${baseUrl}/offers" $parameters

    $response = Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};

    if($response.offers."@total" -eq 0) { return @(); }

    return $response.offers.offer
}

<#
.SYNOPSIS
Adds an Inbox document
 
.DESCRIPTION
 
.PARAMETER Content
Stream of content of document as byte[]
 
#>

function Add-BMTInboxDoc
{
    [CmdletBinding()]
    Param(
       [Parameter(ValueFromPipeline)][byte[]]$Content
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/inbox-documents" $parameters

    $convertedContent = [System.Convert]::ToBase64String($Content);
    $bodyAsJson = ConvertTo-JSON( @{ "inbox-document"= 
        @{ 
            base64file=$convertedContent; 
        }
    });

    $response = $(Invoke-RestMethod -Uri $uri -Method Post -Body $bodyAsJson -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});

    return $response."inbox-document".id;
}

<#
.SYNOPSIS
Removes an Inbox file by id
 
.DESCRIPTION
 
.PARAMETER InboxDocumentId
Id of InboxDocument to remove
 
#>

function Remove-BMTInboxDoc
{
    [CmdletBinding()]
    Param(
       [string]$InboxDocumentId
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/inbox-documents/${InboxDocumentId}" $parameters
    Invoke-RestMethod -Uri $uri -Method Delete -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"};
}

<#
.SYNOPSIS
Reads metadata from an InboxDocument by Id
 
.DESCRIPTION
 
.PARAMETER InboxDocumentId
Id of InboxDocument to get metadata for
 
.OUTPUTS hashtable containing metadata as key-value-pairs
#>

function Read-BMTInboxDoc
{
    [CmdletBinding()]
    Param(
       [string]$InboxDocumentId
    )

    if(!$script:key) { Connect-BMT -UseCredentialsManager $True; }

    $parameters = @{ api_key=$script:key; }
    $uri = New-HttpQueryString "${baseUrl}/inbox-documents/${InboxDocumentId}" $parameters
    $response = $(Invoke-RestMethod -Uri $uri -Method Get -ContentType "application/json; charset=utf-8" -Headers @{Accept="application/json"});

    $result = @{};
    foreach($data in $response."inbox-document".metadata.data) {
        $result.Add($data.key, $data.value);
    }
    return $result;
}

## Private Functions

function New-HttpQueryString
{
    [CmdletBinding()]
    param 
    (
        [Parameter(Mandatory = $true)][String]$Uri,
        [Parameter(Mandatory = $true)][Hashtable]$QueryParameter
    )
 
    # Add System.Web
    Add-Type -AssemblyName System.Web
 
    # Create a http name value collection from an empty string
    $nvCollection = [System.Web.HttpUtility]::ParseQueryString([String]::Empty)
 
    foreach ($key in $QueryParameter.Keys)
    {
        $nvCollection.Add($key, $QueryParameter.$key)
    }
 
    # Build the uri
    $uriRequest = [System.UriBuilder]$uri
    $uriRequest.Query = $nvCollection.ToString()
 
    return $uriRequest.Uri.OriginalString
}