PSTANSS.psm1

$script:ModuleRoot = $PSScriptRoot
$script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\PSTANSS.psd1").ModuleVersion

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName PSTANSS.Import.DoDotSource -Fallback $false
if ($PSTANSS_dotsourcemodule) { $script:doDotSource = $true }

<#
Note on Resolve-Path:
All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator.
This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS.
Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist.
This is important when testing for paths.
#>


# Detect whether at some level loading individual module files, rather than the compiled module was enforced
$importIndividualFiles = Get-PSFConfigValue -FullName PSTANSS.Import.IndividualFiles -Fallback $false
if ($PSTANSS_importIndividualFiles) { $importIndividualFiles = $true }
if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true }
if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true }

function Import-ModuleFile {
    <#
        .SYNOPSIS
            Loads files into the module on module import.
 
        .DESCRIPTION
            This helper function is used during module initialization.
            It should always be dotsourced itself, in order to proper function.
 
            This provides a central location to react to files being imported, if later desired
 
        .PARAMETER Path
            The path to the file to load
 
        .EXAMPLE
            PS C:\> . Import-ModuleFile -File $function.FullName
 
            Imports the file stored in $function according to import policy
    #>

    [CmdletBinding()]
    Param (
        [string]
        $Path
    )

    $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath
    if ($doDotSource) {
        . $resolvedPath
    } else {
        $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null)
    }
}

#region Load individual files
if ($importIndividualFiles) {
    # Execute Preimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) {
        . Import-ModuleFile -Path $path
    }

    # Import all internal functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) {
        . Import-ModuleFile -Path $function.FullName
    }

    # Import all public functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore)) {
        . Import-ModuleFile -Path $function.FullName
    }

    # Execute Postimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) {
        . Import-ModuleFile -Path $path
    }

    # End it here, do not load compiled code below
    return
}
#endregion Load individual files

#region Load compiled code
<#
This file loads the strings documents from the respective language folders.
This allows localizing messages and errors.
Load psd1 language files for each language you wish to support.
Partial translations are acceptable - when missing a current language message,
it will fallback to English or another available language.
#>

Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'PSTANSS' -Language 'en-US'

function Assert-CacheRunspaceRunning {
    <#
    .Synopsis
        Assert-CacheRunspaceRunning
 
    .DESCRIPTION
        Check cache validation runspace on status
 
    .EXAMPLE
        PS C:\> Assert-CacheRunspaceRunning
 
        Check cache validation runspace on status
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    Param(
    )

    Write-PSFMessage -Level Debug -Message "Check cache validationRunspace"

    if ([TANSS.Cache]::StopValidationRunspace -eq $true) {
        Write-PSFMessage -Level Debug -Message "ValidationRunspace is stopped. Going to start the runspace again"

        # force to stop the runspace
        [TANSS.Cache]::StopValidationRunspace = $true
        Get-PSFRunspace -Name "TANSS.LookupValidation" | Stop-PSFRunspace

        # Restart the runspace
        try {
            [TANSS.Cache]::StopValidationRunspace = $false
            Start-PSFRunspace -Name "TANSS.LookupValidation" -ErrorAction Stop -ErrorVariable invokeError
        } catch {
            Stop-PSFFunction -Message "Error Starting ValidationRunspace. Unknown module behaviour. Please restart your powershell console!" -EnableException $true -Exception $invokeError -Tag "RunSpace"
            throw $invokeError
        }
    }

}


function ConvertFrom-Base64StringWithNoPadding( [string]$Data ) {
    <#
    .SYNOPSIS
        Helper function build valid Base64 strings from JWT access tokens
 
    .DESCRIPTION
        Helper function build valid Base64 strings from JWT access tokens
 
    .PARAMETER Data
        The Token to convert
 
    .EXAMPLE
        PS C:\> ConvertFrom-Base64StringWithNoPadding -Data $data
 
        build valid base64 string the content from variable $data
    #>

    $Data = $Data.Replace('-', '+').Replace('_', '/')
    switch ($Data.Length % 4) {
        0 { break }
        2 { $Data += '==' }
        3 { $Data += '=' }
        default { throw New-Object ArgumentException('data') }
    }
    [System.Convert]::FromBase64String($Data)
}

function ConvertFrom-JWTtoken {
    <#
    .SYNOPSIS
        Converts access tokens to readable objects
 
    .DESCRIPTION
        Converts access tokens to readable objects
 
    .PARAMETER TokenText
        The Token to convert
 
    .EXAMPLE
        PS C:\> ConvertFrom-JWTtoken -Token $TokenText
 
        Converts the content from variable $TokenText to an object
    #>

    [cmdletbinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]
        $TokenText
    )

    # Validate as per https://tools.ietf.org/html/rfc7519 - Access and ID tokens are fine, Refresh tokens will not work
    if ((-not $TokenText.Contains(".")) -or (-not $TokenText.StartsWith("eyJ"))) {
        $msg = "Invalid data or not an access/refresh token. $($TokenText)"
        Stop-PSFFunction -Message $msg -Tag "JWT" -EnableException $true -Exception ([System.Management.Automation.RuntimeException]::new($msg))
    }

    # Split the token in its parts
    $tokenParts = $TokenText.Split(".")

    # Work on header
    $tokenHeader = [System.Text.Encoding]::UTF8.GetString( (ConvertFrom-Base64StringWithNoPadding $tokenParts[0]) )
    $tokenHeaderJSON = $tokenHeader | ConvertFrom-Json

    # Work on payload
    $tokenPayload = [System.Text.Encoding]::UTF8.GetString( (ConvertFrom-Base64StringWithNoPadding $tokenParts[1]) )
    $tokenPayloadJSON = $tokenPayload | ConvertFrom-Json

    # Work on signature
    $tokenSignature = ConvertFrom-Base64StringWithNoPadding $tokenParts[2]

    # Output
    $resultObject = [PSCustomObject]@{
        "alg"       = $tokenHeaderJSON.alg
        "typ"       = $tokenHeaderJSON.typ
        "kid"       = $tokenHeaderJSON.kid
        "sub"       = $tokenPayloadJSON.sub
        "exp"       = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($tokenPayloadJSON.exp).ToLocalTime()
        "type"      = $tokenPayloadJSON.type
        "signature" = $tokenSignature
    }

    $resultObject
}

function ConvertFrom-NameCache {
    <#
    .Synopsis
        ConvertFrom-NameCache
 
    .DESCRIPTION
        Convert Name to ID from cached TANSS.Lookup values
 
    .PARAMETER Name
        Name to convert into ID
 
    .PARAMETER Id
        Id to convert into Name
 
    .PARAMETER Type
        Lookup type where the name should convert from
 
    .EXAMPLE
        PS C:\> ConvertFrom-NameCache -Name "User X" -Type "Employee"
 
        Example
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "FromName",
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "FromName",
            Mandatory = $true
        )]
        [string]
        $Name,

        [Parameter(
            ParameterSetName = "FromId",
            Mandatory = $true
        )]
        [int]
        $Id,

        [Parameter(Mandatory = $true)]
        [ValidateSet("Companies", "Contracts", "CostCenters", "Departments", "Employees", "OrderBys", "Phases", "Tags", "Tickets", "TicketStates", "TicketTypes", "VacationAbsenceSubTypes", "VacationTypesPredefinedApi")]
        [string]
        $Type

    )

    $parameterSetName = $pscmdlet.ParameterSetName
    Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

    switch ($parameterSetName) {
        "FromName" {
            Write-PSFMessage -Level Verbose -Message "Start converting '$($Name)' of type '$($Type)' to ID"
            if ( ([TANSS.Lookup]::$Type).ContainsValue($Name) ) {
                foreach ($key in [TANSS.Lookup]::$Type.Keys) {
                    if ([TANSS.Lookup]::$Type[$key] -like $Name) {
                        Write-PSFMessage -Level Verbose -Message "Found ID '$key' for name '$($Name)' of type '$($Type)'"
                        return $key
                    }
                }
            } else {
                Write-PSFMessage -Level Error -Message "Unable to convert '$($Name)' of type '$($Type)' in ID. Name is not in present in cache."
            }
        }

        "FromId" {
            Write-PSFMessage -Level Verbose -Message "Start converting ID '$($Id)' of type '$($Type)' to name"
            if ( ([TANSS.Lookup]::$Type).ContainsKey("$($Id)") ) {
                $output = [TANSS.Lookup]::$Type["$($Id)"]
                Write-PSFMessage -Level Verbose -Message "Found '$output' with ID '$($Id)' of type '$($Type)'"
                return $output
            } else {
                Write-PSFMessage -Level Error -Message "Unable to convert '$($Id)' of type '$($Type)' into Name. Id is not in present in cache."
            }
        }

        Default {
            Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true
            throw
        }
    }

}


Function ConvertFrom-UnixEpochTime {
    <#
    .SYNOPSIS
        Converts UNIX Epoch Time to DateTime object
 
    .DESCRIPTION
        Converts UNIX Epoch Time to DateTime object
 
    .PARAMETER EpochTime
        The time value to convert
 
    .PARAMETER UTC
        convert the given Epoch without following the lcoal timezone
 
    .EXAMPLE
        PS C:\> ConvertFrom-UnixEpochTime -EpochTime "1641769200"
 
        Converts the content from variable $TokenText to an object
    #>

    param(
        # Parameter help description
        [Parameter(
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [int[]]
        $EpochTime,

        [switch]
        $UTC
    )

    Process {

        foreach ($item in $EpochTime) {
            if ($UTC) {
                ([datetime]'1/1/1970').AddSeconds($item)
            } else {
                [timezone]::CurrentTimeZone.ToLocalTime(([datetime]'1/1/1970').AddSeconds($item))
            }
        }

    }
}


function Format-ApiPath {
    <#
    .Synopsis
        Format-ApiPath
 
    .DESCRIPTION
        Ensure the right format and the existense of api prefix in the given path
 
    .PARAMETER Path
        Path to format
 
    .PARAMETER QueryParameter
        A hashtable for all the parameters to the api route
 
    .EXAMPLE
        PS C:\> Format-ApiPath -Path $ApiPath
 
        Api path data from variable $ApiPath will be tested and formatted.
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(Mandatory = $true)]
        [string]
        $Path,

        [hashtable]
        $QueryParameter
    )

    # Start Function
    Write-PSFMessage -Level System -Message "Formatting API path '$($Path)'"

    # receive module cental configuration for prefix on api path (default is 'backend/')
    $apiPrefix = Get-PSFConfigValue -FullName 'PSTANSS.API.RestPathPrefix' -Fallback ""

    # remove no more need slashes
    $apiPath = $Path.Trim('/')


    # check on API path prefix
    if (-not $ApiPath.StartsWith($apiPrefix)) {
        $ApiPath = $apiPrefix + $ApiPath
        Write-PSFMessage -Level Debug -Message "Add API prefix, formatting path to '$($ApiPath)'"
    } else {
        Write-PSFMessage -Level Debug -Message "Prefix API path already present"
    }


    # If specified, process hashtable QueryParameters to valid parameters into uri
    if ($MyInvocation.BoundParameters['QueryParameter'] -and $QueryParameter) {
        Write-PSFMessage -Level Debug -Message "Add query parameters '$([string]::Join("' ,'", $QueryParameter.Keys))'"

        $apiPath = "$($apiPath)?"
        $i = 0

        foreach ($key in $QueryParameter.Keys) {
            if ($i -gt 0) {
                $apiPath = "$($apiPath)&"
            }

            if ("System.Array" -in ($QueryParameter[$Key]).psobject.TypeNames) {
                $parts = $QueryParameter[$Key] | ForEach-Object { "$($key)=$($_)" }
                $apiPath = "$($apiPath)$([string]::Join("&", $parts))"
            } else {
                $apiPath = "$($apiPath)$($key)=$($QueryParameter[$Key])"
            }

            $i++
        }
    }


    # Output Result
    $ApiPath = $ApiPath.TrimEnd("?")
    $ApiPath
}


function Invoke-CacheRefresh {
    <#
    .Synopsis
        Invoke-CacheRefresh
 
    .DESCRIPTION
        Invokes api calls to fill mostly used lookup values
 
    .PARAMETER Token
        AccessToken object to register as default connection for TANSS
 
    .EXAMPLE
        PS C:\> Invoke-CacheRefresh -Token $token
 
        Example
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(Mandatory = $true)]
        [TANSS.Connection]
        $Token
    )

    Write-PSFMessage -Level Verbose -Message "Start updating lookup cache from current tickets in TANSS" -Tag "Cache"

    $tickets = @()
    $tickets += Get-TANSSTicket -MyTickets -Token $token
    $tickets += Get-TANSSTicket -NotAssigned -Token $token
    $tickets += Get-TANSSTicket -AllTechnician -Token $token
    Write-PSFMessage -Level Verbose -Message "Built cache from $($tickets.count) tickets" -Tag "Cache"

    $null = Get-TANSSVacationAbsenceSubType -Token $token
    $null = Get-TANSSDepartment -Token $token
    $null = Get-TANSSTicketStatus -Token $token
    $null = Get-TANSSTicketType -Token $token
}


function Invoke-TANSSTokenCheck {
    <#
    .Synopsis
        Test a TANSS connection- oder service-token
 
    .DESCRIPTION
        Tests validity for a TANSS.Connection object
 
    .PARAMETER Token
        TANSS.Connection Token object to check on
 
    .PARAMETER NoRefresh
        Indicates that the function will not try to update the specified token
 
    .PARAMETER DoNotRegisterConnection
        Do not register the connection as default connection
 
    .PARAMETER PassThru
        Outputs the token to the console, even when the register switch is set
 
    .EXAMPLE
        PS C:\> Invoke-TANSSTokenCheck -Token $Token
 
        Test the TANSS.Connection object from variable $Token for validity
        If the token has a lifetime under 5 percent, the function will try to update the token.
        If the token matches the registered token within the module, the updated token will also be registered.
 
    .EXAMPLE
        PS C:\> Invoke-TANSSTokenCheck -Token $Token -NoRefresh
 
        Test the TANSS.Connection object from variable $Token for validity, but will NOT try to update the token.
        Considered for testing ServiceTokes, that can't be updated
 
    .EXAMPLE
        PS C:\> Invoke-TANSSTokenCheck -Token $Token -DoNotRegisterConnection -PassThru
 
        Test the TANSS.Connection object from variable $Token for validity.
        If the token has a lifetime under 5 percent, the function will try to update the token,
        but not registered as the standard token for the module. Instead, the token will be outputted to the console.
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Connection])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [TANSS.Connection]
        $Token,

        [Parameter(ParameterSetName = "NoRefresh")]
        [switch]
        $NoRefresh,

        [Parameter(ParameterSetName = "Default")]
        [switch]
        $DoNotRegisterConnection,

        [switch]
        $PassThru
    )

    begin {
        $registeredToken = Get-TANSSRegisteredAccessToken
    }

    process {
        # General validity check
        if (-not $Token.IsValid) {
            Stop-PSFFunction -Message "$($Token.EmployeeType) token for '$($Token.UserName)' on $($Token.Server) is not valid" -Tag "AccessToken", "InvalidToken" -EnableException $true -PSCmdlet $pscmdlet
        }

        # Lifetime check
        if ($Token.PercentRemaining -lt 5) {
            Write-PSFMessage -Level Warning -Message "$($Token.EmployeeType) token for '$($Token.UserName)' on $($Token.Server) is about to expire in $($Token.TimeRemaining.Minutes) min" -Tag "AccessToken", "InvalidToken"

            if ((-not $NoRefresh) -and $Token.RefreshToken) {
                Write-PSFMessage -Level Verbose -Message "Going to try a token refresh" -Tag "AccessToken"

                # Compile parameters for Token refresh
                $paramUpdateTANSSAccessToken = @{
                    "Token"          = $Token
                    "NoCacheRefresh" = $true
                    "PassThru"       = $true
                }
                if ((([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($registeredToken.AccessToken))) -notlike [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.AccessToken))) -or $DoNotRegisterConnection) {
                    $paramUpdateTANSSAccessToken.add("DoNotRegisterConnection", $false)
                } else {
                    $paramUpdateTANSSAccessToken.add("DoNotRegisterConnection", $true)
                }

                $newToken = Update-TANSSAccessToken @paramUpdateTANSSAccessToken

                # Output result
                if ($PassThru) {
                    $newToken
                }
            } else {
                Write-PSFMessage -Level Important -Message "Please aquire a new token as soon as possible" -Tag "AccessToken", "NoAccessTokenRefresh"
            }
        }

        # Output if
        if((-not $newToken) -and $PassThru) {
            $Token
        }
    }

    end {}
}


function Push-DataToCacheRunspace {
    <#
    .Synopsis
        Push-DataToCacheRunspace
 
    .DESCRIPTION
        Push meta information to runspace cache
 
    .PARAMETER MetaData
        The metadata PSCusomobject to push to Cache
 
    .EXAMPLE
        PS C:\> Push-DataToCacheRunspace -MetaData $response.meta
 
        Push meta information to runspace cache
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Alias("Meta", "Data")]
        $MetaData
    )

    Write-PSFMessage -Level Debug -Message "Pushing data to cache validationRunspace"

    [TANSS.Cache]::Data.Add((New-Guid), $MetaData)

}


function Update-CacheLookup {
    <#
    .Synopsis
        Update-CacheLookup
 
    .DESCRIPTION
        Update a cache lookup hashtable with an object
 
    .PARAMETER LookupName
        Name of LokkupClass where to update
 
    .PARAMETER Id
        The Id to of the record to cache
 
    .PARAMETER Name
        The name of the record to cache
 
    .EXAMPLE
        PS C:\> Update-CacheLookup -LookupName "Departments" -Id $department.Id -Name $department.Name
 
        Update or insert the key from variable $department.Id of the cache-lookup-hashtable [TANSS.Lookup]::Departments with the name $department.Name
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseShouldProcessForStateChangingFunctions", "")]
    [CmdletBinding(
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [string]
        $LookupName,

        [int]
        $Id,

        [string]
        $Name
    )

    if ([TANSS.Lookup]::$LookupName["$($Id)"] -notlike $Name) {
        if ([TANSS.Lookup]::$LookupName["$($Id)"]) {
            Write-PSFMessage -Level Debug -Message "Update existing id '$($Id)' in [TANSS.Lookup]::$($LookupName) with value '$($Name)'" -Tag "Cache", $LookupName
            [TANSS.Lookup]::$LookupName["$($Id)"] = $Name
        } else {
            Write-PSFMessage -Level Debug -Message "Insert in [TANSS.Lookup]::$($LookupName): $($Id) - '$($($Name))'" -Tag "Cache", $LookupName
            ([TANSS.Lookup]::$LookupName).Add("$($Id)", $Name)
        }
    }

}


function ConvertFrom-TANSSTimeStampParameter {
    <#
    .Synopsis
        ConvertFrom-TANSSTimeStampParameter
 
    .DESCRIPTION
        Convert display names for Type & State parameter into api texts
 
    .PARAMETER Text
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER TextType
        Specifies if the text is a timestampe "state" or "type"
 
    .EXAMPLE
        PS C:\> ConvertFrom-TANSSTimeStampParameter -Text "Coming" -TextType "State"
 
        Outputs "On" as a "comming state for TANSS api
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            Mandatory=$true,
            ValueFromPipeline=$true
            )]
        [ValidateSet("Coming", "Leaving", "StartPause", "EndPause", "Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support")]
        [string]
        $Text,

        [Parameter(
            Mandatory=$true
        )]
        [ValidateSet("State", "Type")]
        [String]
        $TextType
    )

    begin {}

    process {
        Write-PSFMessage -Level Debug -Message "Start converting '$($Text)' as '$($TextType)' to ApiText value"
        $apiText = ""

        switch ($TextType) {
            "State" {
                switch ($Text) {
                    "Coming" { $apiText = "ON" }
                    "Leaving" { $apiText = "OFF" }
                    "StartPause" { $apiText = "PAUSE_START" }
                    "EndPause" { $apiText = "PAUSE_END" }
                    Default {
                        Stop-PSFFunction -Message "Unhandeled pattern for parameter Text. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                    }
                }
            }

            "Type" {
                switch ($Text) {
                    "Work" { $apiText = "WORK" }
                    "Inhouse" { $apiText = "INHOUSE" }
                    "Errand" { $apiText = "ERRAND" }
                    "Vacation" { $apiText = "VACATION" }
                    "Illness" { $apiText = "ILLNESS" }
                    "PaidAbsence" { $apiText = "ABSENCE_PAID" }
                    "UnpaidAbsence" { $apiText = "ABSENCE_UNPAID" }
                    "Overtime" { $apiText = "OVERTIME" }
                    "Support" { $apiText = "DOCUMENTED_SUPPORT" }
                    Default {
                        Stop-PSFFunction -Message "Unhandeled pattern for parameter Text. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                    }
                }
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled pattern for parameter TextType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }

        # Output
        Write-PSFMessage -Level Debug -Message "'$($Text)' as '$($TextType)' converted into ApiText value: $($apiText) done"
        $apiText
    }

    end {}
}


function Connect-TANSS {
    <#
    .Synopsis
        Connect-TANSS
 
    .DESCRIPTION
        Connect to TANSS Service
 
    .PARAMETER Server
        Name of the service to connect to
 
    .PARAMETER Credential
        The credentials to login
 
    .PARAMETER LoginToken
        If the user needs an -application specific- login token for MFA, this field must be set as well
 
    .PARAMETER Protocol
        Specifies if the connection is done with http or https
 
    .PARAMETER DoNotRegisterConnection
        Do not register the connection as default connection
 
    .PARAMETER NoCacheInit
        Do not query current existing tickets and various types to fill cache data for lookup types
 
    .PARAMETER PassThru
        Outputs the token to the console, even when the register switch is set
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Connect-TANSS -Server "tanss.company.com" -Credential (Get-Credential "username")
 
        Connects to "tanss.company.com" via HTTPS protocol and the specified credentials.
        Connection will be set as default connection for any further action.
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")]
    [CmdletBinding(
        DefaultParameterSetName = 'Credential',
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Connection])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("ComputerName", "Hostname", "Host", "ServerName")]
        [String]
        $Server,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = 'Credential'
        )]
        [System.Management.Automation.PSCredential]
        $Credential,

        [Parameter(ParameterSetName = 'Credential')]
        [string]
        $LoginToken,

        [ValidateSet("HTTP", "HTTPS")]
        [ValidateNotNullOrEmpty()]
        [String]
        $Protocol = "HTTPS",

        [Alias('NoRegistration')]
        [Switch]
        $DoNotRegisterConnection,

        [switch]
        $NoCacheInit,

        [switch]
        $PassThru
    )

    begin {
        $ApiPath = Format-ApiPath -Path "api/v1/user/login"
    }

    process {
        if ($protocol -eq 'HTTP') {
            Write-PSFMessage -Level Important -Message "Unsecure $($protocol) connection with possible security risk detected. Please consider switch to HTTPS!" -Tag "Connection"
            $prefix = 'http://'
        } else {
            Write-PSFMessage -Level System -Message "Using secure $($protocol) connection." -Tag "Connection"
            $prefix = 'https://'
        }

        if ($Server -match '//') {
            if ($Server -match '\/\/(?<Server>(\w+|\.)+)') { $Server = $Matches["Server"] }
            Remove-Variable -Name Matches -Force -Verbose:$false -Debug:$false -Confirm:$false
        }

        if ($PsCmdlet.ParameterSetName -eq 'Credential') {
            if (($credential.UserName.Split('\')).count -gt 1) {
                $userName = $credential.UserName.Split('\')[1]
            } else {
                $userName = $credential.UserName
            }

            Write-PSFMessage -Level Verbose -Message "Authenticate user '$($userName)' to service '$($Prefix)$($server)'" -Tag "Connection", "Authentication"
            $param = @{
                "Uri"           = "$($prefix)$($server)/$($ApiPath)"
                "Headers"       = @{
                    "user"       = $userName
                    "password"   = $credential.GetNetworkCredential().Password
                    "logintoken" = "$($LoginToken)"
                }
                "Verbose"       = $false
                "Debug"         = $false
                "ErrorAction"   = "Stop"
                "ErrorVariable" = "invokeError"
            }
            try {
                $response = Invoke-RestMethod @param
            } catch {
                Stop-PSFFunction -Message "Error invoking rest call on service '$($Prefix)$($server)'. $($invokeError)" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }

            if ($response.meta.text -like "Unsuccesful login attempt") {
                $msgText = "$($response.meta.text) to service '$($Prefix)$($server)'. Maybe wrong password"
                if (-not $LoginToken) {
                    $msgText = "$($msgText) or LoginToken (OTP) is needed"
                } else {
                    $msgText = "$($msgText) or LoginToken (OTP) wrong/expired"
                }
                Stop-PSFFunction -Message $msgText -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }

            if (-not $response.content.apiKey) {
                Stop-PSFFunction -Message "Something went wrong on authenticating user $($userName). No apiKey found in response. Unable login to service '$($Prefix)$($server)'" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }
        }

        Write-PSFMessage -Level System -Message "Creating TANSS.Connection" -Tag "Connection"
        $token = [TANSS.Connection]@{
            Server            = "$($Prefix)$($Server)"
            UserName          = $userName
            EmployeeId        = $response.content.employeeId
            EmployeeType      = $response.content.employeeType
            AccessToken       = ($response.content.apiKey | ConvertTo-SecureString -AsPlainText -Force)
            RefreshToken      = ($response.content.refresh | ConvertTo-SecureString -AsPlainText -Force)
            Message           = $response.meta.text
            TimeStampCreated  = Get-Date
            TimeStampExpires  = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($response.content.expire).ToLocalTime()
            TimeStampModified = Get-Date
        }

        if (-not $NoCacheInit) { Invoke-CacheRefresh -Token $token }

        if (-not $DoNotRegisterConnection) {
            # Make the connection the default connection for further commands
            Register-TANSSAccessToken -Token $token

            Write-PSFMessage -Level Significant -Message "Connected to service '($($token.Server))' as '$($token.UserName)' as default connection" -Tag "Connection"

            if ($PassThru) {
                Write-PSFMessage -Level System -Message "Outputting TANSS.Connection object" -Tag "Connection"
                $token
            }
        } else {
            Write-PSFMessage -Level Significant -Message "Connected to service '($($token.Server))' as '$($token.UserName)', outputting TANSS.Connection" -Tag "Connection"
            $token
        }
    }

    end {
    }
}


function Find-TANSSObject {
    <#
    .Synopsis
        Find-TANSSObject
 
    .DESCRIPTION
        Find a object via global search in TANSS
        The search has to be initiated on one of three areas. (Company, Employees, Tickets)
 
    .PARAMETER Company
        Initiate a search in the company area of TANSS
 
    .PARAMETER Employee
        Initiate a search within the employee/person database of TANSS
 
    .PARAMETER TicketPreview
        Initiate a search in the tickets of TANSS
 
    .PARAMETER Text
        The Text (id or name) to seach for
 
    .PARAMETER ShowInactive
        Search company records that are marked as inactive
        By default, only companies that are marked as "active"
 
        This is bound to company search only
 
    .PARAMETER ShowLocked
        Search company records that are marked as locked
        By default, only companies that are marked as "Unlocked"
 
        This is bound to company search only
 
    .PARAMETER CompanyId
        Return tickets or employees of the specified company id
 
        This is bound to ticket- and employee-search only
 
    .PARAMETER CompanyName
        Return tickets or employees of the specified company name
 
        This is bound to ticket- and employee-search only
 
    .PARAMETER Status
        Return "All", only "Active" or only "Inactive" employees.
 
    .PARAMETER GetCategories
        If true, categories will be fetches as well.
        The names are given in the "linked entities"-"employeeCategories"
 
        Default is $false
 
    .PARAMETER GetCallbacks
        If true, expected callbacks will be fetched as well
 
        Default is $false
 
    .PARAMETER PreviewContentMaxChars
        If defined, it overrides the to preview the content
 
        Default values within the api is 60
 
    .PARAMETER ResultSize
        The amount of objects the query will return
        To avoid long waitings while query a large number of items, the api
        by default only query an amount of 100 items within one call
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Find-TANSSObject -Company -Text "Customer X"
 
        Search for "Customer X" within all company data
 
    .EXAMPLE
        PS C:\> Find-TANSSObject -TicketPreview -Text "Issue Y"
 
        Search for "Issue Y" within all tickets
 
    .EXAMPLE
        PS C:\> "Mister T" | Find-TANSSObject -Employee
 
        Search "Mister T" in the employee records of all companies
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Company",
        PositionalBinding = $true,
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.TicketPreview], [TANSS.Company], [TANSS.EmployeeSearched])]
    Param(
        [Parameter(ParameterSetName = "Company")]
        [switch]
        $Company,

        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [switch]
        $Employee,

        [Parameter(ParameterSetName = "Ticket-UserFriendly")]
        [Parameter(ParameterSetName = "Ticket-ApiNative")]
        [Alias("Ticket")]
        [switch]
        $TicketPreview,

        [Parameter(ParameterSetName = "Company", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = "Employee-UserFriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = "Employee-ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = "Ticket-UserFriendly", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Parameter(ParameterSetName = "Ticket-ApiNative", Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [ValidateLength(2, [int]::MaxValue)]
        [string[]]
        $Text,

        [Parameter(ParameterSetName = "Company")]
        [switch]
        $ShowInactive,

        [Parameter(ParameterSetName = "Company")]
        [switch]
        $ShowLocked,

        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [Parameter(ParameterSetName = "Ticket-ApiNative")]
        [int]
        $CompanyId,

        [Parameter(ParameterSetName = "Employee-UserFriendly", Mandatory = $true)]
        [Parameter(ParameterSetName = "Ticket-UserFriendly", Mandatory = $true)]
        [string]
        $CompanyName,

        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [ValidateSet("All", "Active", "Inactive")]
        [string]
        $Status = "All",

        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [bool]
        $GetCategories = $false,

        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [bool]
        $GetCallbacks = $false,

        [Parameter(ParameterSetName = "Ticket-UserFriendly")]
        [Parameter(ParameterSetName = "Ticket-ApiNative")]
        [int]
        $PreviewContentMaxChars,

        [Parameter(ParameterSetName = "Company")]
        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [Parameter(ParameterSetName = "Ticket-UserFriendly")]
        [Parameter(ParameterSetName = "Ticket-ApiNative")]
        [int]
        $ResultSize,

        [Parameter(ParameterSetName = "Company")]
        [Parameter(ParameterSetName = "Employee-UserFriendly")]
        [Parameter(ParameterSetName = "Employee-ApiNative")]
        [Parameter(ParameterSetName = "Ticket-UserFriendly")]
        [Parameter(ParameterSetName = "Ticket-ApiNative")]
        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }

        Assert-CacheRunspaceRunning

        $apiPath = Format-ApiPath -Path "api/v1/search"

        if ((-not $ResultSize) -or ($ResultSize -eq 0)) { $ResultSize = 100 }

        if ($Status) {
            switch ($Status) {
                "All" { $inactive = $true }
                "Active" { $inactive = $false }
                "Inactive" { $inactive = $true }
                Default {
                    Stop-PSFFunction -Message "Unhandeled Status. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                }
            }
        }

        if($MyInvocation.BoundParameters['CompanyName'] -and $CompanyName) {
            $CompanyId = ConvertFrom-NameCache -Name CompanyName -Type "Companies"
            if(-not $CompanyId) {
                Write-PSFMessage -Level Warning -Message "No Id for company '$($Company)' found. Ticket will be created with blank value on CompanyId"
            }
        }

    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            "Company" {
                foreach ($textItem in $Text) {
                    $body = @{
                        areas   = @("COMPANY")
                        query   = $textItem.replace("*", "")
                        configs = @{
                            company = @{
                                maxResults = $ResultSize
                            }
                        }
                    }

                    $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token

                    if ($response.content.companies) {
                        $countCompanyAll = ([array]($response.content.companies)).count
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countCompanyAll) records returned"

                        if (-not $ShowInactive) {
                            $countCompanyFiltered = ([array]($response.content.companies | Where-Object { $_.inactive -like "False" })).Count
                            Write-PSFMessage -Level Verbose -Message "Filtering companies marked as inactive - keeping $($countCompanyFiltered) of $($countCompanyAll) records"
                        } else {
                            $countCompanyFiltered = $countCompanyAll
                        }

                        if (-not $ShowLocked) {
                            $countCompanyFiltered = $countCompanyFiltered - ([array]($response.content.companies | Where-Object { $_.lockout -like "True" })).Count
                            Write-PSFMessage -Level Verbose -Message "Filtering companies marked as locked - keeping $($countCompanyFiltered) of $($countCompanyAll) records"
                        }

                        foreach ($companyItem in $response.content.companies) {
                            # Filtering
                            if(-not $ShowInactive) { if($companyItem.inactive -like "True") { continue } }
                            if(-not $ShowLocked) { if($companyItem.lockout -like "True") { continue } }

                            # Output data
                            [TANSS.Company]@{
                                BaseObject = $companyItem
                                Id         = $companyItem.id
                            }
                        }
                    } else {
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search"
                    }
                }
            }

            {$_ -like "Employee-ApiNative" -or $_ -like "Employee-UserFriendly"} {
                foreach ($textItem in $Text) {
                    $body = @{
                        areas   = @("EMPLOYEE")
                        query   = $textItem
                        configs = @{
                            employee = @{
                                maxResults = $ResultSize
                            }
                        }
                    }
                    if($CompanyId) { $body.configs.employee.Add("companyId", $CompanyId) }
                    if($Status) { $body.configs.employee.Add("inactive", $inactive) }
                    if("GetCategories" -in $PSCmdlet.MyInvocation.BoundParameters.Keys) { $body.configs.employee.Add("categories", $GetCategories) }
                    if("GetCallbacks" -in $PSCmdlet.MyInvocation.BoundParameters.Keys) { $body.configs.employee.Add("callbacks", $GetCallbacks) }

                    $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token

                    if ($response.content.employees) {
                        $countEmployeeAll = ([array]($response.content.employees)).count
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countEmployeeAll) records returned"

                        Push-DataToCacheRunspace -MetaData $response.meta

                        foreach ($employeeItem in $response.content.employees) {
                            # Output data
                            [TANSS.EmployeeSearched]@{
                                BaseObject = $employeeItem
                                Id         = $employeeItem.id
                            }
                        }
                    } else {
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search"
                    }
                }
            }

            {$_ -like "Ticket-ApiNative" -or $_ -like "Ticket-UserFriendly"} {
                foreach ($textItem in $Text) {
                    $body = @{
                        areas   = @("TICKET")
                        query   = $textItem
                        configs = @{
                            employee = @{
                                maxResults = $ResultSize
                            }
                        }
                    }
                    if($CompanyId) { $body.configs.employee.Add("companyId", $CompanyId) }
                    if($PreviewContentMaxChars) { $body.configs.employee.Add("previewContentMaxChars", $PreviewContentMaxChars) }

                    $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token

                    if ($response.content.Tickets) {
                        $countTicketsAll = ([array]($response.content.Tickets)).count
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - $($countTicketsAll) records returned"

                        Push-DataToCacheRunspace -MetaData $response.meta

                        foreach ($ticketItem in $response.content.Tickets) {
                            # Output data
                            [TANSS.TicketPreview]@{
                                BaseObject = $ticketItem
                                Id         = $ticketItem.id
                            }
                        }
                    } else {
                        Write-PSFMessage -Level Verbose -Message "API response: $($response.meta.text) - no records returned from global search"
                    }
                }
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }
    }

    end {}
}


function Get-TANSSDepartment {
    <#
    .Synopsis
        Get-TANSSDepartment
 
    .DESCRIPTION
        Get department from TANSS
 
    .PARAMETER Id
        Id of the department to get
 
    .PARAMETER Name
        Name of the department to get
 
    .PARAMETER IncludeEmployeeId
        Include assigned employees
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSDepartment
 
        Get departments from TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "All",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Department])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ById"
        )]
        [int[]]
        $Id,

        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ByName"
        )]
        [string[]]
        $Name,

        [switch]
        $IncludeEmployeeId,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }

        Assert-CacheRunspaceRunning

        if ($IncludeEmployeeId) {
            Write-PSFMessage -Level Verbose -Message "IncludeEmployeeId switch is specified, going to ask for linked IDs" -Tag "Department", "IncludeEmployeeId"

            $deparmentsWithEmployeeId = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/companies/departments?withEmployees=true" -Token $Token | Select-Object -ExpandProperty content

            $departments = foreach ($department in $departments) {
                [array]$_employeeIds = $deparmentsWithEmployeeId | Where-Object id -like $department.id | Select-Object -ExpandProperty employeeIds

                $department | Add-Member -MemberType NoteProperty -Name employeeIds -Value $_employeeIds

                $department
            }
            Remove-Variable -Name deparmentsWithEmployeeId, _employeeIds, deparment -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction:Ignore -WarningAction:Ignore -InformationAction:Ignore
        } else {
            $departments = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/employees/departments" -Token $Token | Select-Object -ExpandProperty content
        }

        [array]$filteredDepartments = @()
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            "ById" {
                foreach ($item in $Id) {
                    $filteredDepartments += $departments | Where-Object id -eq $item
                }
            }

            "ByName" {
                foreach ($item in $Name) {
                    $filteredDepartments += $departments | Where-Object name -like $item
                }
            }

            "All" {
                $filteredDepartments = $departments
            }

            Default {
                Stop-PSFFunction -Message "Unhandled ParameterSet '$($parameterSetName)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "Department", "SwitchException", "ParameterSet"
            }
        }
    }

    end {
        $filteredDepartments = $filteredDepartments | Sort-Object name, id -Unique
        Write-PSFMessage -Level Verbose -Message "Going to return $($filteredDepartments.count) departments" -Tag "Department", "Output"

        foreach ($department in $filteredDepartments) {
            Write-PSFMessage -Level System -Message "Working on department '$($department.name)' with id '$($department.id)'" -Tag "Department"

            # put id and name to cache lookups
            Update-CacheLookup -LookupName "Departments" -Id $department.Id -Name $department.Name

            # output result
            [TANSS.Department]@{
                Baseobject = $department
                Id = $department.id
            }
        }
    }
}


function Get-TANSSRegisteredAccessToken {
    <#
    .Synopsis
        Get-TANSSRegisteredAccessToken
 
    .DESCRIPTION
        Retrieve the registered LoginToken for default TANSS connection
 
    .EXAMPLE
        PS C:\> Get-TANSSRegisteredAccessToken
 
        Retrieve the registered LoginToken for TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    Param(
    )

    begin {}

    process {
        Write-PSFMessage -Level Verbose -Message "Retrieving the registered LoginToken for '$($script:TANSSToken.UserName)' on '$($script:TANSSToken.Server)'" -Tag "AccessToken"
        $script:TANSSToken
    }

    end {}
}


function Invoke-TANSSRequest {
    <#
    .Synopsis
        Invoke-TANSSRequest
 
    .DESCRIPTION
        Invoke a API request to TANSS Server
 
    .PARAMETER Type
        Type of web request
 
    .PARAMETER ApiPath
        Uri path for the REST call in the API
 
    .PARAMETER QueryParameter
        A hashtable for all the parameters to the api route
 
    .PARAMETER AdditionalHeader
        Hashtable with additional values to put in the header of the request
 
    .PARAMETER Body
        The body as a hashtable for the request
 
    .PARAMETER Pdf
        if a PDF should be queried, this switch must be specified
 
    .PARAMETER BodyForceArray
        Tells the function always to invoke the data in the body as a JSON array formatted string
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Invoke-TANSSRequest -Type GET -ApiPath "api/v1/something"
 
        Invoke a GET request to API with path api/v1/something by using the default registered token within the module
 
    .EXAMPLE
        PS C:\> Invoke-TANSSRequest -Type GET -ApiPath "api/v1/something" -Token $Token
 
        Invoke a GET request to API with path api/v1/something by using the explicit token from the variale $Token
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = 'Default',
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateSet("GET", "POST", "PUT", "DELETE")]
        [string]
        $Type,

        [Parameter(Mandatory = $true)]
        [string]
        $ApiPath,

        [hashtable]
        $QueryParameter,

        [hashtable[]]
        $Body,

        [hashtable]
        $AdditionalHeader,

        [switch]
        $Pdf,

        [switch]
        $BodyForceArray,

        [TANSS.Connection]
        $Token
    )

    begin {
    }

    process {
    }

    end {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Invoke-TANSSTokenCheck -Token $Token

        $ApiPath = Format-ApiPath -Path $ApiPath -QueryParameter $QueryParameter

        # Body
        if ($Body) {
            $bodyData = $Body | ConvertTo-Json -Compress
        } else {
            $bodyData = $null
        }
        if ($BodyForceArray -and (-not ([string]$bodyData).StartsWith("["))) {
            $bodyData = "[$($bodyData)]"
        }


        # Header
        $header = @{
            "apiToken" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.AccessToken))
        }


        if ($Pdf) { $header.Add("Accept", "pdf") }


        if ($MyInvocation.BoundParameters['AdditionalHeader'] -and $AdditionalHeader) {
            foreach ($key in $AdditionalHeader.Keys) {
                $header.Add($key, $AdditionalHeader[$key])
            }
        }


        # Invoke request
        $param = [ordered]@{
            "Uri"           = "$($Token.Server)/$($ApiPath)"
            "Headers"       = $header
            "Body"          = $bodyData
            "Method"        = $Type
            "ContentType"   = 'application/json; charset=UTF-8'
            "Verbose"       = $false
            "Debug"         = $false
            "ErrorAction"   = "Stop"
            "ErrorVariable" = "invokeError"
        }

        if ($pscmdlet.ShouldProcess("$($Type) web REST call against URL '$($param.Uri)'", "Invoke")) {
            Write-PSFMessage -Level Verbose -Message "Invoke $($Type) web REST call against URL '$($param.Uri)'" -Tag "TANSSApiRequest" -Data @{ "body" = $bodyData; "Method" = $Type }

            try {
                $response = Invoke-RestMethod @param
                Write-PSFMessage -Level System -Message "API Response: $($response.meta.text)" -Tag "TANSSApiRequest", "SuccessfulRequest" -Data @{ "response" = $response }
            } catch {
                if ($invokeError[0].Message.StartsWith("{")) {
                    $response = $invokeError[0].Message | ConvertFrom-Json -ErrorAction SilentlyContinue
                }

                if ($response) {
                    Write-PSFMessage -Level Error -Message "$($response.Error.text) - $($response.Error.localizedText)" -Exception $response.Error.type -Tag "TANSSApiRequest", "FailedRequest", "REST call $($Type)" -Data @{ "Message" = $invokeError[0].Message } -PSCmdlet $pscmdlet -ErrorRecord $invokeError[0].ErrorRecord
                } else {
                    Write-PSFMessage -Level Error -Message "$($invokeError[0].Source) ($($invokeError[0].HResult)): $($invokeError[0].Message)" -Exception $invokeError[0].InnerException -Tag "TANSSApiRequest", "FailedRequest", "REST call $($Type)" -ErrorRecord $invokeError[0].ErrorRecord  -PSCmdlet $pscmdlet -Data @{ "Message" = $invokeError[0].Message }
                }

                return
            }

            # Output
            $response
        }
    }
}

function New-TANSSServiceToken {
    <#
    .Synopsis
        Create a new (user unspecific) api service access token object
 
    .DESCRIPTION
        Create a new api service access token object.
        Apart from the regular user login, there are aspects and api routes, that are
        only available via explicit service token.
 
        This function allows you to create a service token to give to other functions,
        that require such a token.
 
    .PARAMETER Server
        Name of the service the token is generated from
 
    .PARAMETER ServiceToken
        A API token generated within Tanss to access specific TANSS modules explicit via
        API service as a non-employee-account.
 
        For security reaons, the parameter only accept secure strings.
        Please avoid plain-text for sensitive informations!
        To generate secure strings use:
        $ServiceTokenSecureString = Read-Host -AsSecureString
 
    .PARAMETER Protocol
        Specifies if the service connection is done with http or https
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> $tanssServiceToken = New-TANSSServiceToken -Server "tanss.corp.company.com" -ServiceToken $ServiceTokenSecureString
 
        Outputs a ServiceToken as a TANSS.Connection object for "tanss.corp.company.com" with the api key from the variable $ServiceTokenSecureString
 
        API variable $ServiceTokenSecureString hast to be a securestring.
        ($ServiceTokenSecureString = Read-Host -AsSecureString)
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Connection])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("ComputerName", "Hostname", "Host", "ServerName")]
        [ValidateNotNull()]
        [String]
        $Server,

        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("ApiKey", "Password", "AccessToken", "Token")]
        [securestring]
        $ServiceToken,

        [ValidateSet("HTTP", "HTTPS")]
        [ValidateNotNullOrEmpty()]
        [String]
        $Protocol = "HTTPS"
    )

    begin {
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        # Ensure Prefix
        if ($protocol -eq 'HTTP') {
            Write-PSFMessage -Level Important -Message "Unsecure $($protocol) connection with possible security risk detected. Please consider switch to HTTPS!" -Tag "ServiceToken"
            $prefix = 'http://'
        } else {
            Write-PSFMessage -Level System -Message "Using secure $($protocol) connection." -Tag "ServiceToken"
            $prefix = 'https://'
        }

        # Validate Server Parameter to avoid accidentally input bearer token information in $Server
        try {
            $null = ConvertFrom-JWTtoken -TokenText $Server
            $serverIsTokenObject = $true
        } catch {
            $serverIsTokenObject = $false
        }
        if (($Server.StartsWith("Bearer")) -or ($Server.Length -gt 256) -or ($serverIsTokenObject)) {
            if ($Server.Length -gt 10) {
                $textlength = $Server.Length / 2
            } elseif ($Server.Length -gt 5) {
                $textlength = 4
            } else {
                $textlength = 2
            }
            Stop-PSFFunction -Message "The specified Server '$($Server.Substring(0, $textlength))****' looks like a service token. ServiceToken has to be piped in as a SecureString or has to be specified via parameter '-ServiceToken'. For security reason, please don't use plaintext for sensitive information." -EnableException $true -Cmdlet $pscmdlet -Tag "ServiceToken"
        }

        # Read JWT from service token
        $TokenText = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ServiceToken))
        if ($TokenText.StartsWith("Bearer")) {
            Write-PSFMessage -Level System -Message "Found Bearer token information. Going to extract JWT" -Tag "ServiceToken"
            $TokenText = $TokenText.split(" ")[-1]
        }
        Write-PSFMessage -Level Verbose -Message "Reading JWT information from serviceToken" -Tag "ServiceToken"
        $tokenInfo = ConvertFrom-JWTtoken -TokenText $TokenText

        # Create ServiceToken
        if (($tokenInfo.typ -like "JWT") -and $Server -and $prefix) {
            if ($pscmdlet.ShouldProcess("Service token for '$($UserName)'", "New")) {
                Write-PSFMessage -Level System -Message "Creating TANSS.Connection with service token" -Tag "ServiceToken"

                $serviceTokenObject = [TANSS.Connection]@{
                    Server            = "$($Prefix)$($Server)"
                    UserName          = $tokenInfo.sub
                    EmployeeId        = 0
                    EmployeeType      = "ServiceAccessToken"
                    AccessToken       = $ServiceToken
                    RefreshToken      = $null
                    Message           = "Explizit specified API token. May not work with all functions!"
                    TimeStampCreated  = (Get-Date)
                    TimeStampExpires  = $tokenInfo.exp
                    TimeStampModified = (Get-Date)
                }

                Invoke-TANSSTokenCheck -Token $serviceTokenObject -NoRefresh

                # output result
                $serviceTokenObject
            }
        } else {
            Write-PSFMessage -Level Important -Message "Unable to create TANSS ServiceToken object with specified ServiceToken '$($TokenText.Substring(0,10))*****'" -Tag "ServiceToken"
        }
    }

    end {}
}


function Register-TANSSAccessToken {
    <#
    .Synopsis
        Register-TANSSAccessToken
 
    .DESCRIPTION
        Register the AccessToken as default connection setting for TANSS
 
    .PARAMETER Token
        AccessToken object to register as default connection for TANSS
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Register-TANSSAccessToken -Token $Token
 
        Register the LoginToken from variable $Token as a default connection for TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'Medium'
    )]
    Param(
        [Parameter(Mandatory = $true)]
        [TANSS.Connection]
        $Token
    )

    begin {}

    process {

        if ($pscmdlet.ShouldProcess("AccessToken for $($Token.UserName) on '$($Token.Server)'", "Register")) {
            Write-PSFMessage -Level Verbose -Message "Registering AccessToken for $($Token.UserName) on '$($Token.Server)' valid until '$($Token.TimeStampExpires)'" -Tag "AccessToken"

            $script:TANSSToken = $Token
        }
    }

    end {}
}


function Update-TANSSAccessToken {
    <#
    .Synopsis
        Update-TANSSAccessToken
 
    .DESCRIPTION
        Updates the AccessToken from a refreshToken for TANSS connection
        By defaault, the new Access is registered to as default connection
 
    .PARAMETER NoCacheRefresh
        Do not requery tickets and various types to fill cache data for lookup types
 
    .PARAMETER DoNotRegisterConnection
        Do not register the connection as default connection
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the new token to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Update-TANSSAccessToken
 
        Updates the AccessToken from the default connection and register it as new
        AccessToken on default Connection
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingConvertToSecureStringWithPlainText", "")]
    [CmdletBinding(
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    Param(
        [TANSS.Connection]
        $Token,

        [Alias('NoRegistration')]
        [Switch]
        $DoNotRegisterConnection,

        [switch]
        $NoCacheRefresh,

        [switch]
        $PassThru
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        if ($Token.RefreshToken) {
            $refreshTokenInfo = ConvertFrom-JWTtoken -TokenText ([System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.RefreshToken))).split(" ")[1]
        } else {
            Stop-PSFFunction -Message "Invalid Token specified. No refreshToken found" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
        }

        Write-PSFMessage -Level Verbose -Message "Checking RefreshToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)'" -Tag "AccessToken", "Connection", "Authentication"
        if ( (Get-Date) -ge $refreshTokenInfo.exp ) {
            Stop-PSFFunction -Message "RefreshToken expired. Unable to refresh with current token. Please use Connect-TANSS to login again" -Tag "Connection", "Authentication"
            return
        }

        if ($pscmdlet.ShouldProcess("AccessToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)' with RefreshToken valid until '$($refreshTokenInfo.exp)'", "Update")) {
            $apiPath = Format-ApiPath -Path "api/v1/tickets/own"

            Write-PSFMessage -Level Verbose -Message "Updating AccessToken from TANSS.Connection of $($Token.UserName) on '$($Token.Server)' with RefreshToken valid until '$($refreshTokenInfo.exp)'" -Tag "AccessToken"
            $param = @{
                "Uri"           = "$($Token.Server)/$($ApiPath)"
                "Headers"       = @{
                    "refreshToken" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.RefreshToken))
                }
                "Verbose"       = $false
                "Debug"         = $false
                "ErrorAction"   = "Stop"
                "ErrorVariable" = "invokeError"
            }
            try {
                $response = Invoke-RestMethod @param
            } catch {
                Stop-PSFFunction -Message "Error invoking rest call on service '$($Token.Server)'. $($invokeError)" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }

            if ($response.meta.text -notlike "Welcome, your ApiToken is 4 hours valid.") {
                Stop-PSFFunction -Message "$($response.meta.text) to service '$($Token.Server)'. Apperantly, refreshToken is not valid" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }

            if (-not $response.content.apiKey) {
                Stop-PSFFunction -Message "Something went wrong on authenticating user $($Token.UserName). No apiKey found in response. Unable to refresh token from connection '$($Token.Server)'" -Tag "Connection", "Authentication" -EnableException $true -Cmdlet $pscmdlet
            }

            Write-PSFMessage -Level System -Message "Creating TANSS.Connection from refreshed AccessToken" -Tag "Connection"
            $token = [TANSS.Connection]@{
                Server            = $Token.Server
                UserName          = $Token.UserName
                EmployeeId        = $response.content.employeeId
                EmployeeType      = $response.content.employeeType
                AccessToken       = ($response.content.apiKey | ConvertTo-SecureString -AsPlainText -Force)
                RefreshToken      = ($response.content.refresh | ConvertTo-SecureString -AsPlainText -Force)
                Message           = $response.meta.text
                TimeStampCreated  = $Token.TimeStampCreated
                TimeStampExpires  = [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($response.content.expire).ToLocalTime()
                TimeStampModified = Get-Date
            }

            if (-not $NoCacheRefresh) { Invoke-CacheRefresh -Token $token }

            if (-not $DoNotRegisterConnection) {
                # Make the connection the default connection for further commands
                Write-PSFMessage -Level Significant -Message "Updating AccessToken for service '($($token.Server))' as '$($token.UserName)' and register it as default connection" -Tag "Connection"

                Register-TANSSAccessToken -Token $token

                if ($PassThru) {
                    Write-PSFMessage -Level System -Message "Outputting TANSS.Connection object" -Tag "Connection"
                    $token
                }
            } else {
                Write-PSFMessage -Level Significant -Message "Updating AccessToken for service '($($token.Server))' as '$($token.UserName)'" -Tag "Connection"

                $token
            }
        }
    }

    end {}
}


function Get-TANSSEmployee {
    <#
    .Synopsis
        Get-TANSSEmployee
 
    .DESCRIPTION
        Get employees out of TANSS service.
 
        You can pipe in IDs o employee objects to get refreshed data out of the service
 
        You can also pipe in company objects to receive employees of that company
 
    .PARAMETER EmployeeId
        The ID of the employee to get from TANSS service
 
    .PARAMETER Employee
        A TANSS.Employee object to query again from the service
 
    .PARAMETER CompanyId
        The ID of the company to get employees from
 
    .PARAMETER Company
        A passed in TANSS.Company object to query employees from
 
    .PARAMETER CompanyName
        The name of the company to query employees from
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSEmployee -EmployeeId 2
 
        Get the employee with ID 2 (usually the first employee created in TANSS)
 
    .EXAMPLE
        PS C:\> $employee | Get-TANSSEmployee
 
        Query the employee from variable $employee again
 
    .EXAMPLE
        PS C:\> Get-TANSSEmployee -CompanyId 100000
 
        Get all employees from company ID 100000 (your own company)
 
    .EXAMPLE
        PS C:\> $company | Get-TANSSEmployee
 
        Get all employees from company $company
 
    .EXAMPLE
        PS C:\> Get-TANSSEmployee -CompanyName "Company X"
 
        Get all employees from "Company X"
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Employee_ApiNative",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Employee])]
    Param(
        [Parameter(
            ParameterSetName = "Employee_ApiNative",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [Alias("Id")]
        [int[]]
        $EmployeeId,

        [Parameter(
            ParameterSetName = "Employee_UserFriendly",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [tanss.employee]
        $Employee,

        [Parameter(
            ParameterSetName = "Company_ApiNative",
            ValueFromPipelineByPropertyName = $true,
            Mandatory = $true
        )]
        [int[]]
        $CompanyId,

        [Parameter(
            ParameterSetName = "Company_UserFriendly",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [tanss.company]
        $Company,

        [Parameter(
            ParameterSetName = "Company_UserFriendlyByName",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [string[]]
        $CompanyName,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        # If objects are piped in -> collect IDs
        switch ($parameterSetName) {
            "Employee_UserFriendly" { $EmployeeId = $Employee.Id }
            "Company_UserFriendly" { $CompanyId = $Company.Id }
            "Company_UserFriendlyByName" {
                $CompanyId = foreach ($_name in $CompanyName) {
                    Find-TANSSObject -Company -Text $_name -Token $Token -ShowLocked | Where-Object name -like $_name | Select-Object -ExpandProperty Id
                }
            }
        }

        switch ($parameterSetName) {
            { $_ -like "Employee_*" } {
                Write-PSFMessage -Level System -Message "Query personal employee record. $(([array]$EmployeeId).count) record(s)"

                foreach ($id in $EmployeeId) {
                    Write-PSFMessage -Level Verbose -Message "Working on employee id $($id)"

                    $response = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/employees/$($id)" -Token $Token

                    if ($response.meta.text -like "Object found") {

                        # Cache refresh
                        Push-DataToCacheRunspace -MetaData $response.meta

                        Write-PSFMessage -Level Verbose -Message "Output employee $($response.content.name)"
                        $output = [TANSS.Employee]@{
                            BaseObject = $response.content
                            Id         = $response.content.id
                        }

                        # ToDo: Filtering - add parameters (Isactive, FilterName, )

                        # Output result
                        $output

                    } else {
                        Write-PSFMessage -Level Error -Message "API returned no data"
                    }

                }
            }

            { $_ -like "Company_*" } {
                Write-PSFMessage -Level System -Message "Query corporate employee record. $(([array]$CompanyId).count) record(s)"

                foreach ($id in $CompanyId) {
                    Write-PSFMessage -Level Verbose -Message "Query employee(s) for company id $($id)"

                    $response = Invoke-TANSSRequest -Type GET -ApiPath "api/v1/companies/$($id)/employees" -Token $Token

                    if ($response.meta.text -like "Object found") {

                        # Cache refresh
                        Push-DataToCacheRunspace -MetaData $response.meta

                        foreach ($responseItem in $response.content) {

                            Write-PSFMessage -Level Verbose -Message "Output corporate employee $($responseItem.name)"
                            $output = [TANSS.Employee]@{
                                BaseObject = $responseItem
                                Id         = $responseItem.id
                            }

                            # ToDo: Filtering - add parameters (Isactive, FilterName, )

                            # Output result
                            $output

                        }

                    } else {
                        Write-PSFMessage -Level Error -Message "API returned no data"
                    }
                }
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }
    }

    end {}
}


function Get-TANSSTechnician {
    <#
    .Synopsis
        Get-TANSSTechnician
 
    .DESCRIPTION
        Gets all technicians of this system from default TANSS connection
 
    .PARAMETER Id
        ID of the technician to get
        (client side filtering)
 
    .PARAMETER Name
        Name of the technician to get
        (client side filtering)
 
    .PARAMETER FreelancerCompanyId
        If this parameter is given, also fetches the freelancers of this company.
        By default all users with a license are treated as "TANSS technicians".
 
    .PARAMETER ExcludeRestrictedLicenseUser
        Do not show account/ users / technicians with limited licenses
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTechnician
 
        Gets all technicians of this system
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Employee])]
    Param(
        [String[]]
        $Name,

        [int[]]
        $Id,

        [int[]]
        $FreelancerCompanyId,

        [switch]
        $ExcludeRestrictedLicenseUser,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        $apiPath = Format-ApiPath -Path "api/v1/employees/technicians"
        Assert-CacheRunspaceRunning
        if (-not $FreelancerCompanyId) { $FreelancerCompanyId = 0 }
    }

    process {
        $response = @()

        $response += foreach ($companyId in $FreelancerCompanyId) {
            $queryParameter = @{}

            if($MyInvocation.BoundParameters['ExcludeRestrictedLicenseUser'] -and $ExcludeRestrictedLicenseUser) {
                $queryParameter.Add("restrictedLicenses", $false)
            } else {
                $queryParameter.Add("restrictedLicenses", $true)
            }

            if ($companyId -ne 0) {
                Write-PSFMessage -Level System -Message "FreelancerCompanyId specified, compiling body to query freelancers of company '$($companyId)'" -Tag "Technician", "Freelancer"
                $queryParameter.Add("FreelancerCompanyId", $companyId)
            }

            $invokeParam = @{
                "Type"    = "GET"
                "ApiPath" = (Format-ApiPath -Path $apiPath -QueryParameter $queryParameter)
                "Token"   = $Token
            }

            Invoke-TANSSRequest @invokeParam

            Remove-Variable -Name queryParameter, invokeParam -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore -InformationAction Ignore
        }


        if ($response) {
            Write-PSFMessage -Level Verbose -Message "Found $(($response.content).count) technicians" -Tag "Technician"

            foreach ($responseItem in $response) {

                # Output result
                foreach ($technician in $responseItem.content) {

                    # Do filtering on name
                    if ($MyInvocation.BoundParameters['Name'] -and $Name) {
                        $filterSuccess = $false
                        foreach ($filterName in $Name) {
                            if ($technician.Name -like $filterName) {
                                $filterSuccess = $true
                            }
                        }

                        # if filter does not hit, continue with next technician
                        if ($filterSuccess -eq $false) { continue }
                    }


                    # Do filtering on id
                    if ($MyInvocation.BoundParameters['Id'] -and $Id) {
                        $filterSuccess = $false
                        foreach ($filterId in $Id) {
                            if ([int]($technician.id) -eq $filterId) {
                                $filterSuccess = $true
                            }
                        }

                        # if filter does not hit, continue with next technician
                        if ($filterSuccess -eq $false) { continue }
                    }


                    # Query details
                    Write-PSFMessage -Level Verbose -Message "Getting details of '$($technician.name)' (Id $($technician.id))" -Tag "Technician"

                    $invokeParam = @{
                        "Type"    = "GET"
                        "ApiPath" = (Format-ApiPath -Path "api/v1/employees/$($technician.id)")
                        "Token"   = $Token
                    }

                    $employeeResponse = Invoke-TANSSRequest @invokeParam

                    if ($employeeResponse) {
                        Push-DataToCacheRunspace -MetaData $employeeResponse.meta

                        foreach ($employeeItem in $employeeResponse.content) {
                            Write-PSFMessage -Level Debug -Message "Found '$($employeeItem.Name)' with id $($employeeItem.id)"

                            # Output data
                            [TANSS.Employee]@{
                                BaseObject = $employeeItem
                                Id         = $employeeItem.id
                            }
                        }
                    } else {
                        Stop-PSFFunction -Message "Unexpected error searching '$($employeeResponse.content.name)' with ID '$($technician.id)'. TANSS is unable to find details of employee" -EnableException $true -Cmdlet $pscmdlet
                    }
                }
            }
        } else {
            Write-PSFMessage -Level Warning -Message "No technicians found." -Tag "Technician"
        }
    }

    end {
    }
}


function New-TANSSEmployee {
    <#
    .Synopsis
        New-TANSSEmployee
 
    .DESCRIPTION
        Create a new employee in TANSS
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        New-TANSSEmployee -Name "User, Test"
 
        Create a new employee (as technician) in your own company
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Userfriendly",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Employee])]
    param (
        # Fullname of the employee
        [Parameter(
            ParameterSetName = "ApiNative",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = "Userfriendly",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("Fullname", "Displayname")]
        [string]
        $Name,

        # first name of the employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("Givenname")]
        [string]
        $FirstName,

        # Last name of the employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("Surname")]
        [string]
        $LastName,


        # Id of the salutation for this employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdSalutation")]
        [Int]
        $SalutationId = 0,


        # Id of the department which tis employee is assigned to
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdDepartment")]
        [Int]
        $DepartmentId,


        # Name of the department which tis employee is assigned to
        [Parameter(ParameterSetName = "Userfriendly")]
        [string]
        $Department,

        # location / room of the employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("Location")]
        [string]
        $Room,

        # Main telephone number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("TelephoneNumber")]
        [string]
        $Phone,

        # e-Mail address
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("EmailAddress")]
        [string]
        $Email,

        # if this employee is assigned to a specific car, the id goes here
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdCar")]
        $CarId = 0,

        # mobile telephone number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("MobilePhone")]
        [string]
        $Mobile,

        # initials for this employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [string]
        $Initials,

        # Working hour model for this employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdWorkingHourModel")]
        [int]
        $WorkingHourModelId = 0,

        # Id of the accounting type for this employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdAccountingType")]
        [int]
        $accountingTypeId = 0,

        # Private telephone number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("PrivatePhoneNumber")]
        [string]
        $PrivateNumber,

        # True if the employee is active
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("Active")]
        [bool]
        $IsActive = $true,

        # Identification number in foreign ERP system
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [string]
        $ERPNumber,

        # Fax number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("PersonalFaxNumber")]
        [string]
        $Fax,

        # Role for this employee
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [string]
        $Role,

        # if the employee uses a title, the id goes here
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("IdTitle")]
        [int]
        $TitleId = 0,

        # Language which this employee uses
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [string]
        $Language,

        # Second telephone number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("TelephoneNumberTwo")]
        [string]
        $Phone2,

        # second mobile telephone number
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("MobileNumberTwo")]
        [string]
        $Mobile2,


        # Employee birthday date
        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("BirthdayDate", "DateBirthday", "DateOfBirth")]
        [datetime]
        $Birthday,

        # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company
        [Parameter(ParameterSetName = "ApiNative")]
        [Alias("CompanyAssignments", "IdCompany")]
        [int[]]
        $CompanyId,

        # Company name where the ticket should create for. Can only be set if the user has access to the company
        [Parameter(ParameterSetName = "Userfriendly")]
        [Alias("Company")]
        [String[]]
        $CompanyName,

        [Parameter(ParameterSetName = "ApiNative")]
        [Parameter(ParameterSetName = "Userfriendly")]
        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
        $apiPath = Format-ApiPath -Path "api/v1/employees"

        if ($Department) {
            $DepartmentId = ConvertFrom-NameCache -Name $Department -Type "Departments"
            if (-not $DepartmentId) {
                Write-PSFMessage -Level Warning -Message "No Id for department '$($Department)' found. Employee will be created with blank value on departmentId"
                #todo implement API call for departments
                $DepartmentId = 0
            }
        }

        if ($CompanyName) {
            $CompanyId = foreach ($companyItem in $CompanyName) {
                $_id = ConvertFrom-NameCache -Name $companyItem -Type Companies
                if (-not $_id) {
                    Write-PSFMessage -Level Warning -Message "No Id for company '$($companyItem)' found. Employee will be created without assignment to this company"
                } else {
                    $_id
                }
            }
            Remove-Variable -Name _id -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore
        }

        if (-not $CompanyId) {
            $_name = ConvertFrom-NameCache -Id 100000 -Type "Companies"
            Write-PSFMessage -Level Important -Message "No company specified. Employee will be created within your own company $(if($_name) { "($($_name)) "})as a technician"

            $CompanyId = 100000

            Remove-Variable -Name _name -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore
        }

    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "Userfriendly" -and (-not $Name)) {
            Write-PSFMessage -Level Error -Message "No name specified"
            continue
        }

        #region rest call prepare
        if ($Birthday) {
            $_birthday = Get-Date -Date $Birthday -Format "yyyy-MM-dd"
        } else {
            $_birthday = "0000-00-00"
        }

        [array]$_companies = foreach ($CompanyIdItem in $CompanyId) {
            @{
                "companyId" = $CompanyIdItem
            }
        }

        if (-not $LastName) {
            $nameParts = $Name.split(",").Trim()
            if ($nameParts.Count -eq 2) {
                # Assuming schema "<Lastname>, <Firstname>"
                $LastName = $nameParts[0]
            } elseif ($nameParts.Count -eq 1) {
                $nameParts = $Name.split(" ").Trim()
                if ($nameParts.Count -eq 2) {
                    # Assuming schema "<Firstname> <Lastname>"
                    $LastName = $nameParts[1]
                } else {
                    $LastName = $Name
                }
            } else {
                $LastName = $Name
            }
        }
        Remove-Variable -Name nameParts -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore

        if (-not $FirstName) {
            $nameParts = $Name.split(",").Trim()
            if ($nameParts.Count -eq 2) {
                # Assuming schema "<Lastname>, <Firstname>"
                $FirstName = $nameParts[1]
            } elseif ($nameParts.Count -eq 1) {
                $nameParts = $Name.split(" ").Trim()
                if ($nameParts.Count -eq 2) {
                    # Assuming schema "<Firstname> <Lastname>"
                    $FirstName = $nameParts[0]
                } else {
                    $FirstName = ""
                }
            } else {
                $FirstName = ""
            }
        }
        Remove-Variable -Name nameParts -Force -Confirm:$false -WhatIf:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore

        if (-not $Initials -and $FirstName -and $LastName) {
            $_initials = "$(([string]$FirstName)[0])$(([string]$LastName)[0])"
        } else {
            $_initials = $Initials
        }

        $body = [ordered]@{
            "id"                 = 0
            "name"               = "$Name"
            "firstName"          = "$FirstName"
            "lastName"           = "$LastName"
            "salutationId"       = $SalutationId
            "departmentId"       = $DepartmentId
            "room"               = "$Room"
            "telephoneNumber"    = "$Phone"
            "emailAddress"       = "$Email"
            "carId"              = $CarId
            "mobilePhone"        = "$Mobile"
            "initials"           = "$_initials"
            "workingHourModelId" = $WorkingHourModelId
            "accountingTypeId"   = $accountingTypeId
            "privatePhoneNumber" = "$PrivateNumber"
            "active"             = $IsActive
            "erpNumber"          = "$ERPNumber"
            "personalFaxNumber"  = "$Fax"
            "role"               = "$Role"
            "titleId"            = $TitleId
            "language"           = "$Language"
            "telephoneNumberTwo" = "$Phone2"
            "mobileNumberTwo"    = "$Mobile2"
            "birthday"           = "$_birthday"
            "companyAssignments" = $_companies
        }
        #endregion rest call prepare

        if ($pscmdlet.ShouldProcess("Employee '$($Name)' on companyID '$([string]::Join(", ", $CompanyId))'", "New")) {
            Write-PSFMessage -Level Verbose -Message "Creating new employee '$($Name)' on companyID '$([string]::Join(", ", $CompanyId))'" -Tag "Employee" -Data $body

            $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token

            if ($response) {
                Write-PSFMessage -Level Verbose -Message "API Response: $($response.meta.text)"

                Push-DataToCacheRunspace -MetaData $response.meta

                [TANSS.Employee]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }
            } else {
                Write-PSFMessage -Level Error -Message "Error creating employee, no response from API"
            }
        }
    }

    end {
    }
}

function Get-TANSSProject {
    <#
    .Synopsis
        Get-TANSSProject
 
    .DESCRIPTION
        Get all projects from TANSS
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSProject
 
        Get all projects from TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")]
    [CmdletBinding(
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Ticket])]
    param (
        [TANSS.Connection]
        $Token
    )
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Get-TANSSTicket', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd -Project @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}

function Get-TANSSProjectPhase {
    <#
    .Synopsis
        Get-TANSSProjectPhase
 
    .DESCRIPTION
        Get phases of a project in TANSS
 
    .PARAMETER ProjectID
        The ID of the poject to receive phases from
 
    .PARAMETER Project
        TANSS.Project object to receive phases from
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSProjectPhase -ProjectId 10
 
        Get phases out of project 10
 
    .EXAMPLE
        PS C:\> $projects | Get-TANSSTicketActivity
 
        Get all phases of projects in variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByProject",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.ProjectPhase])]
    Param(
        [Parameter(
            ParameterSetName = "ByProjectId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [Alias("Id")]
        [int[]]
        $ProjectID,

        [Parameter(
            ParameterSetName = "ByProject",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Project[]]
        $Project,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"


        if ($parameterSetName -like "ByProject") {
            $inputobjectProjectCount = ([array]$Project).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectProjectCount) project$(if($inputobjectProjectCount -gt 1){'s'})"  -Tag "ProjectPhase", "CollectInputObjects"
            [array]$ProjectID = $Project.id
        }


        foreach ($projectIdItem in $ProjectID) {
            Write-PSFMessage -Level Verbose -Message "Working on project ID $($projectIdItem)"  -Tag "ProjectPhase", "Query"

            # build api path
            $apiPath = Format-ApiPath -Path "api/v1/projects/$projectIdItem/phases"

            # query content
            $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token
            Push-DataToCacheRunspace -MetaData $response.meta
            Write-PSFMessage -Level Verbose -Message "$($response.meta.text): Received $($response.meta.properties.extras.count) VacationEntitlement records in year $($Year)" -Tag "ProjectPhase", "Query"

            # create output
            foreach ($phase in $response.content) {
                # output object
                [TANSS.ProjectPhase]@{
                    BaseObject = $phase
                    Id         = $phase.id
                }

                # cache Lookup refresh
                [TANSS.Lookup]::Phases[$phase.id] = $phase.name
            }
        }
    }

    end {}
}


function New-TANSSProject {
    <#
    .Synopsis
        New-TANSSProject
 
    .DESCRIPTION
        Creates a project in TANSS, that can contain multiple tickets
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSProject -Title "A new project"
 
        Create a project with title "A new project"
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")]
    [CmdletBinding(
        DefaultParameterSetName = "Userfriendly",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Ticket])]
    param (
        # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $CompanyId,

        # Company name where the ticket should create for. Can only be set if the user has access to the company
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Company,

        # If the ticket has a remitter, the id goes here. Name is stored in the "linked entities" - "employees"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('ClientId')]
        [int]
        $RemitterId,

        # If the ticket has a remitter/client, the name of the client
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Client,

        # gives infos about how the remitter gave the order. Infos are stored in the "linked entities" - "orderBys"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $OrderById,

        # gives infos about how the Client gave the order.
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $OrderBy,

        # The title / subject of the ticket
        [Parameter(
            ParameterSetName="Userfriendly",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true

        )]
        [Parameter(
            ParameterSetName="ApiNative",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true

        )]
        [string]
        $Title,

        # The content / description of the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('content')]
        [string]
        $Description,

        # External ticket id (optional)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('extTicketId')]
        [string]
        $ExternalTicketId,

        # id of employee which ticket is assigned to. Name is stored in "linked entities" - "employees"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('assignedToEmployeeId')]
        [int]
        $EmployeeIdAssigned,

        # Name of the employee the ticket is assigned to
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $EmployeeAssigned,

        # id of department the ticket is assigned to. Name is stored in "linked entities" - "departments"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('assignedToDepartmentId')]
        [int]
        $DepartmentIdAssigned,

        # Name of the department the ticket is assigned to
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Department,

        # id of the ticket state. Name is give in "linked entities" - "ticketStates"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $StatusId,

        # The name of the ticket status
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Status,

        # id of the ticket type. Name is give in "linked entities" - "ticketTypes"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $TypeId,

        # The name of the ticket type
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Type,

        # if ticket is assigned to device / employee, linktype is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('LinkTypeId')]
        [int]
        $AssignmentId,

        # If ticket has a deadline, the date is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('deadlineDate')]
        [datetime]
        $Deadline,

        # if true, this ticket is a "repair ticket"
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('repair')]
        [bool]
        $IsRepair = $false,

        # if ticket has a due date, the timestamp is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $DueDate,

        # Determines the "attention" flag state of a ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NO", "YES", "RESUBMISSION", "MAIL")]
        [string]
        $Attention = "NO",

        # If the ticket has an installation fee, this value is true
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NO", "YES", "NO_PROJECT_INSTALLATION_FEE")]
        [string]
        $InstallationFee = "NO",

        # Sets the installation fee drive mode. If it is set to NONE then the system config parameter "leistung.ip.fahrzeit_berechnen" will be used. If the company from the ticket has an installation fee drive mode set then that will be used instead of the system config parameter.
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NONE", "DRIVE_INCLUDED", "DRIVE_EXCLUDED")]
        [string]
        $InstallationFeeDriveMode = "NONE",

        #Amount for the installation fee
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $InstallationFeeAmount,

        # If true, the ticket shall be billed separately
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [bool]
        $SeparateBilling = $false,

        # if the ticket has a service cap, here the amount is given
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $ServiceCapAmount,

        # linkId of the relationship (if ticket has a relation)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $RelationshipLinkId,

        # linkTypeId of the relationship (if ticket has a relation)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $RelationshipLinkTypeId,

        # if the ticket as a resubmission date set, this is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $ResubmissionDate,

        # If a resubmission text is set, this text is returned here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $ResubmissionText,

        # Number of estimated minutes which is planned for the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $EstimatedMinutes,

        # Determines wether the ticket is assigned to a local ticket admin or not
        # NONE: "normal" ticket
        # LOCAL_ADMIN: ticket is assigned to a local ticket admin
        # TECHNICIAN: local ticket admin has forwarded the ticket to a technician
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NONE", "LOCAL_ADMIN", "TECHNICIAN")]
        [string]
        $LocalTicketAdminFlag = "NONE",

        # if the ticket is assigned to a local ticket admin, this represents the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $LocalTicketAdminEmployeeId,

        # if the ticket is assigned to a local ticket admin, this represents the name of the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateNotNullOrEmpty]
        [string]
        $EmployeeTicketAdmin,

        # Sets the order number
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $OrderNumber,

        # If the ticket has a reminder set, the timestamp is returned here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $Reminder,

        # When persisting a ticket, you can also send a list of tag assignments which will be assigned to the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string[]]
        $Tags,

        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [TANSS.Connection]
        $Token
    )
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('New-TANSSTicket', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd -IsProject $true @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}

function New-TANSSProjectPhase {
    <#
    .Synopsis
        Add a project phase into a project
 
    .DESCRIPTION
        Add a project phase into a project
 
    .PARAMETER ProjectID
        The ID of the poject where to create the specified phase
 
    .PARAMETER Project
        TANSS.Project object where to create the specified phase
 
    .PARAMETER Name
        The name of the phase
 
    .PARAMETER Rank
        The rank of the phase, when there are other phases in the project
 
    .PARAMETER StartDate
        The (planned) starting date of the phase
 
    .PARAMETER EndDate
        The (planned) ending date of the phase
 
    .PARAMETER RequiredPrePhasesComplete
        Indicates that all tickets in the previous phase has to be finished to open this phase
 
        Default: $false
 
    .PARAMETER BillingType
        Set the behavour how to bill tickets.
        Values are available via TabCompletion.
        Possible are:
            "InstantBillSupportActivities" = Support activities of sub-tickets are immediately billable
            "BillClosedTicketOnly" = Only Support activities of completed sub-tickets are billable
            "BillOnlyWhenAllTicketsClosed" = Phase may only be billed when all sub-tickets have been completed
 
        Default: "InstantBillSupportActivities"
 
    .PARAMETER ClearanceMode
        Set the behavour how support activities in tickets for the phase are treated.
        Values are available via TabCompletion.
        Possible are:
            "Default" = Setting from TANSS application base preferences is used
            "ReleaseSupportOfUnresolvedTickets" = support activities in open tickets can be released
            "LockSupportsOfUnresolvedTickets" = support activities in open tickets can not be released
 
        Default: "Default"
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSProjectPhase -Project $project -Name "Phase X"
 
        Creates "Phase X" within the project in the variable $project
 
    .EXAMPLE
        PS C:\> $project | New-TANSSProjectPhase -Name "Phase X"
 
        Creates "Phase X" within the project in the variable $project
 
    .EXAMPLE
        PS C:\> New-TANSSProjectPhase -ProjectId $project.id -Name "Phase X", "Phase Y", "Phase Z"
 
        Creates 3 phases ("Phase X", "Phase Y", "Phase Z") within the project in the variable $project
 
        .EXAMPLE
        PS C:\> "Phase X", "Phase Y", "Phase Z" | New-TANSSProjectPhase -ProjectId $project.id
 
        Creates 3 phases ("Phase X", "Phase Y", "Phase Z") within the project in the variable $project
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByProject",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.ProjectPhase])]
    Param(
        [Parameter(
            ParameterSetName = "ByPhaseName",
            Mandatory = $true
        )]
        [Alias("Id")]
        [int]
        $ProjectID,

        [Parameter(
            ParameterSetName = "ByProject",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Project]
        $Project,

        [Parameter(
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [string[]]
        $Name,

        [int]
        $Rank,

        [datetime]
        $StartDate,

        [datetime]
        $EndDate,

        [bool]
        $RequiredPrePhasesComplete = $false,

        [ValidateSet("InstantBillSupportActivities", "BillClosedTicketOnly", "BillOnlyWhenAllTicketsClosed")]
        [string]
        $BillingType = "InstantBillSupportActivities",

        [ValidateSet("Default", "ReleaseSupportOfUnresolvedTickets", "LockSupportsOfUnresolvedTickets")]
        [String]
        $ClearanceMode = "Default",

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

        $apiPath = Format-ApiPath -Path "api/v1/projects/phases"

        # Translate BillingType into api value
        if ($BillingType) {
            switch ($BillingType) {
                "InstantBillSupportActivities" { $_billingType = "DEFAULT" }
                "BillClosedTicketOnly" { $_billingType = "TICKET_MUST_BE_CLOSED" }
                "BillOnlyWhenAllTicketsClosed" { $_billingType = "ALL_TICKETS_MUST_BE_CLOSED" }
                Default {
                    Stop-PSFFunction -Message "Unhandeled Value in BillingType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                }
            }
        }

        # Translate BillingType into api value
        if ($ClearanceMode) {
            switch ($ClearanceMode) {
                "Default" { $_clearanceMode = "DEFAULT" }
                "ReleaseSupportOfUnresolvedTickets" { $_clearanceMode = "DONT_CLEAR_SUPPORTS" }
                "LockSupportsOfUnresolvedTickets" { $_clearanceMode = "MAY_CLEAR_SUPPORTS" }
                Default {
                    Stop-PSFFunction -Message "Unhandeled Value in ClearanceMode. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                }
            }
        }
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"


        if ($parameterSetName -like "ByProject") {
            $ProjectID = $Project.id
            Write-PSFMessage -Level System -Message "Identified ID '$($ProjectID) for project $($Project.Title)"  -Tag "ProjectPhase", "CollectInputObjects"
        }


        # Create body object for api call
        foreach ($nameItem in $Name) {
            Write-PSFMessage -Level Verbose -Message "Working new phase '$($nameItem)' for project ID $($ProjectID)"  -Tag "ProjectPhase", "New"

            $phaseToCreate = [ordered]@{
                projectId               = $ProjectID
                name                    = $nameItem
                closedPrePhasesRequired = $RequiredPrePhasesComplete
                billingType             = $_billingType
                clearanceMode           = $_clearanceMode
            }

            if ($Rank) { $phaseToCreate.add("rank", $Rank) }

            if ($StartDate) {
                $_startDate = [int32][double]::Parse((Get-Date -Date $StartDate.ToUniversalTime() -UFormat %s))
                $phaseToCreate.add("startDate", $_startDate)
            }

            if ($EndDate) {
                $_endDate = [int32][double]::Parse((Get-Date -Date $EndDate.ToUniversalTime() -UFormat %s))
                $phaseToCreate.add("endDate", $_endDate)
            }


            # Create phase
            if ($pscmdlet.ShouldProcess("phase '$($nameItem)' in project id $($ProjectID)", "New")) {
                Write-PSFMessage -Level Verbose -Message "New phase '$($nameItem)' in project id $($ProjectID)" -Tag "ProjectPhase", "New"

                # invoke api call
                $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $phaseToCreate -Token $Token

                Write-PSFMessage -Level Verbose -Message "$($response.meta.text): $($response.content.name) (Rank: $($response.content.rank), Id: $($response.content.id))" -Tag "ProjectPhase", "New", "CreatedSuccessfully"

                # create output
                [TANSS.ProjectPhase]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }

                # cache Lookup refresh
                [TANSS.Lookup]::Phases[$response.content.id] = $response.content.name
            }
        }
    }

    end {}
}


function Remove-TANSSProject {
    <#
        .Synopsis
            Remove-TANSSProject
 
        .DESCRIPTION
            Delete a project in TANSS
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Remove-TANSSProject -ID 100
 
        Remove project with ticketID 100 from TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")]
    [CmdletBinding(
        DefaultParameterSetName = 'ById',
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    [OutputType([TANSS.Ticket])]
    param (
        # TANSS Ticket object to remove
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [TANSS.Ticket[]]
        $InputObject,

        # Id of the ticket to remove
        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("ProjectId", "Project","TicketId", "Ticket")]
        [int[]]
        $Id,

        [TANSS.Connection]
        $Token
    )
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Remove-TANSSTicket', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}

function Remove-TANSSProjectPhase {
    <#
    .Synopsis
        Remove-TANSSProjectPhase
 
    .DESCRIPTION
        Removes a project phase from TANSS
 
    .PARAMETER InputObject
        TANSS.ProjectPhase object to remove
 
    .PARAMETER Force
        Process the removal quietly.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> $phase | Remove-TANSSProjectPhase
 
        Remove project phase in variable $phase from TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("Phase", "ProjectPhase")]
        [TANSS.ProjectPhase[]]
        $InputObject,

        [switch]
        $Force,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $processRemoval = $false

        foreach ($projectPhase in $InputObject) {
            # Check on Force parameter, otherwise process shouldprocess
            if ($Force) {
                $processRemoval = $true
            } else {
                if ($pscmdlet.ShouldProcess("ProjectPhase Id $($projectPhase.Id) '$($projectPhase.Name)' from project '$($projectPhase.Project)' (Id $($projectPhase.ProjectId))", "Remove")) {
                    $processRemoval = $true
                }
            }

            if ($processRemoval) {
                Write-PSFMessage -Level Verbose -Message "Remove ProjectPhase Id $($projectPhase.Id) '$($projectPhase.Name)' from project '$($projectPhase.Project)' (Id $($projectPhase.ProjectId))" -Tag "ProjectPhase", "Remove"

                # Remove projectPhase
                $apiPath = Format-ApiPath -Path "api/v1/projects/phases/$($projectPhase.Id)"
                Invoke-TANSSRequest -Type DELETE -ApiPath $apiPath -Token $Token -Confirm:$false -ErrorVariable "invokeError"

                # cache Lookup refresh
                [TANSS.Lookup]::Phases.Remove($($projectPhase.Id))
            }
        }
    }

    end {}
}


function Set-TANSSProjectPhase {
    <#
    .Synopsis
        Modify a phase inside project
 
    .DESCRIPTION
        Modify a phase inside project
 
    .PARAMETER Phase
        TANSS.ProjectPhase object to set
 
    .PARAMETER PhaseID
        The ID of the poject phase to set
 
    .PARAMETER Project
        TANSS.Project where to modify a phase
 
    .PARAMETER PhaseName
        The name of the phase to modify
 
    .PARAMETER Name
        The (new) name for the phase to set
 
    .PARAMETER Rank
        The order of the phase in relation to other project phases
 
    .PARAMETER StartDate
        The (planned) starting date of the phase
 
    .PARAMETER EndDate
        The (planned) ending date of the phase
 
    .PARAMETER RequiredPrePhasesComplete
        Indicates that all tickets in the previous phase has to be finished to open this phase
 
        Default: $false
 
    .PARAMETER BillingType
        Set the behavour how to bill tickets.
        Values are available via TabCompletion.
        Possible are:
            "InstantBillSupportActivities" = Support activities of sub-tickets are immediately billable
            "BillClosedTicketOnly" = Only Support activities of completed sub-tickets are billable
            "BillOnlyWhenAllTicketsClosed" = Phase may only be billed when all sub-tickets have been completed
 
        Default: "InstantBillSupportActivities"
 
    .PARAMETER ClearanceMode
        Set the behavour how support activities in tickets for the phase are treated.
        Values are available via TabCompletion.
        Possible are:
            "Default" = Setting from TANSS application base preferences is used
            "ReleaseSupportOfUnresolvedTickets" = support activities in open tickets can be released
            "LockSupportsOfUnresolvedTickets" = support activities in open tickets can not be released
 
        Default: "Default"
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Set-TANSSProjectPhase -PhaseID $phase.Id -Name "NewPhaseName X"
 
        Rename the phase from variable $phase to "NewPhaseName X"
        $Phase has to be filled with a
 
    .EXAMPLE
        PS C:\> $phase | Set-TANSSProjectPhase -Name "NewPhaseName X"
 
        Rename the phase from variable $phase to "NewPhaseName X"
        $Phase has to be filled with a
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByPhase",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.ProjectPhase])]
    Param(
        [Parameter(
            ParameterSetName = "ByPhaseId",
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [Alias("Id")]
        [int]
        $PhaseID,

        [Parameter(
            ParameterSetName = "ByPhase",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.ProjectPhase]
        $Phase,

        [Parameter(
            ParameterSetName = "ByProject",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket]
        $Project,

        [Parameter(
            ParameterSetName = "ByProject",
            Mandatory = $true
        )]
        [string]
        $PhaseName,

        [ValidateNotNullOrEmpty()]
        [Alias("NewName")]
        [string]
        $Name,

        [ValidateNotNullOrEmpty()]
        [int]
        $Rank,

        [ValidateNotNullOrEmpty()]
        [datetime]
        $StartDate,

        [ValidateNotNullOrEmpty()]
        [datetime]
        $EndDate,

        [bool]
        $RequiredPrePhasesComplete,

        [ValidateSet("InstantBillSupportActivities", "BillClosedTicketOnly", "BillOnlyWhenAllTicketsClosed")]
        [string]
        $BillingType,

        [ValidateSet("Default", "ReleaseSupportOfUnresolvedTickets", "LockSupportsOfUnresolvedTickets")]
        [String]
        $ClearanceMode,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

        # Translate BillingType into api value
        if ($BillingType) {
            switch ($BillingType) {
                "InstantBillSupportActivities" { $_billingType = "DEFAULT" }
                "BillClosedTicketOnly" { $_billingType = "TICKET_MUST_BE_CLOSED" }
                "BillOnlyWhenAllTicketsClosed" { $_billingType = "ALL_TICKETS_MUST_BE_CLOSED" }
                Default {
                    Stop-PSFFunction -Message "Unhandeled Value in BillingType. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                }
            }
        }

        # Translate BillingType into api value
        if ($ClearanceMode) {
            switch ($ClearanceMode) {
                "Default" { $_clearanceMode = "DEFAULT" }
                "ReleaseSupportOfUnresolvedTickets" { $_clearanceMode = "DONT_CLEAR_SUPPORTS" }
                "LockSupportsOfUnresolvedTickets" { $_clearanceMode = "MAY_CLEAR_SUPPORTS" }
                Default {
                    Stop-PSFFunction -Message "Unhandeled Value in ClearanceMode. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
                }
            }
        }
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"


        # ParameterSet handling
        if ($parameterSetName -like "ByPhase") {
            $PhaseID = $Phase.id
            $projectID = $Phase.ProjectId
            Write-PSFMessage -Level System -Message "Identified ID '$($PhaseID) for project phase $($Phase.Name) within project '$($Phase.Project)' (Id: $($projectID))" -Tag "ProjectPhase", "CollectInputObjects"
        }

        if ($parameterSetName -like "ByProject") {
            $projectID = $Project.Id
            $Phase = Get-TANSSProjectPhase -ProjectID $Project.Id -Token $Token | Where-Object Name -Like $PhaseName
            if (-not $Phase) {
                Write-PSFMessage -Level Error -Message "Phase '$($PhaseName)' not found in project '$($Project.Title)' (Id: $($Project.Id))" -Tag "ProjectPhase", "CollectInputObjects" -Data @{"Project" = $Project }
                continue
            }
            $PhaseID = $Phase.Id
            Write-PSFMessage -Level System -Message "Identified ID '$($PhaseID) for project phase $($Phase.Name) within project $($Phase.Project)"  -Tag "ProjectPhase", "CollectInputObjects"
        }


        # Create body object for api call
        Write-PSFMessage -Level Verbose -Message "Going to set phase ID $($PhaseID)"  -Tag "ProjectPhase", "Set"

        $paramFormatApiPath = @{
            "Path" = "api/v1/projects/phases/$($PhaseID)"
        }
        $paramFormatApiPathQueryParameter = @{}

        $phaseToSet = [ordered]@{
            Id = $PhaseID
            #projectId = $ProjectID
        }
        if ($ProjectID) { $phaseToSet.add("projectId", $ProjectID) }
        if ($Name) { $phaseToSet.add("name", $Name) }
        if ($RequiredPrePhasesComplete) { $phaseToSet.add("closedPrePhasesRequired", $RequiredPrePhasesComplete) }
        if ($BillingType) { $phaseToSet.add("billingType", $_billingType) }
        if ($ClearanceMode) { $phaseToSet.add("clearanceMode", $_clearanceMode) }
        if ($Rank) { $phaseToSet.add("rank", $Rank) }
        if ($StartDate) {
            $_startDate = [int32][double]::Parse((Get-Date -Date $StartDate.ToUniversalTime() -UFormat %s))
            $phaseToSet.add("startDate", $_startDate)

            $paramFormatApiPathQueryParameter["adjustStart"] = $true
            $paramFormatApiPathQueryParameter["adjustEnd"] = $true
        }
        if ($EndDate) {
            $_endDate = [int32][double]::Parse((Get-Date -Date $EndDate.ToUniversalTime() -UFormat %s))
            $phaseToSet.add("endDate", $_endDate)

            $paramFormatApiPathQueryParameter["adjustStart"] = $true
            $paramFormatApiPathQueryParameter["adjustEnd"] = $true
        }

        if ($paramFormatApiPathQueryParameter["adjustStart"] -or $paramFormatApiPathQueryParameter["adjustEnd"]) {
            $paramFormatApiPath.Add("QueryParameter", $paramFormatApiPathQueryParameter)
        }

        $apiPath = Format-ApiPath @paramFormatApiPath

        # Create phase
        $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $phaseToSet -Token $Token
        Write-PSFMessage -Level Verbose -Message "$($response.meta.text): $($response.content.adjustedPhases.name) (Rank: $($response.content.adjustedPhases.rank), Id: $($response.content.adjustedPhases.id))" -Tag "ProjectPhase", "Set", "Modify"


        # create output
        i($PassThru) {
            foreach($_phase in $response.content.adjustedPhases) {
                # object
                [TANSS.ProjectPhase]@{
                    BaseObject = $_phase
                    Id         = $_phase.id
                }

                # cache Lookup refresh
                [TANSS.Lookup]::Phases[$_phase.id] = $_phase.name
            }
        }
    }

    end {
        $null = Get-TANSSProject -Token $Token
    }
}


function Get-TANSSTicket {
    <#
    .Synopsis
        Get-TANSSTicket
 
    .DESCRIPTION
        Gat a ticket from TANSS service
 
    .PARAMETER Id
        The ticket Id (one or more) to query
 
    .PARAMETER CompanyId
        Get all tickets of company Id
 
    .PARAMETER MyTickets
        Get all tickets assigned to the authenticated account
 
    .PARAMETER NotAssigned
        Get all tickets not assigned to somebody
 
    .PARAMETER AllTechnician
        Get all tickets assigned to somebody
 
    .PARAMETER RepairTickets
        Get tickets marked as repair tickets
 
    .PARAMETER NotIdentified
        Get all unidentified tickets
 
    .PARAMETER Project
        Get tickets marked as a project
 
    .PARAMETER LocalTicketAdmin
        Get all tickets specified for a ticket admin
 
    .PARAMETER TicketWithTechnicianRole
        Get all tickets with a technician role
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicket
 
        Get all tickets assinged to the authenticated user
 
    .EXAMPLE
        PS C:\> Get-TANSSTicket -Id 100
 
        Get ticket with Id 100
 
    .EXAMPLE
        PS C:\> Get-TANSSTicket -CompanyId 12345
 
        Get all tickets of company Id 12345
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    [CmdletBinding(
        DefaultParameterSetName = 'MyTickets',
        SupportsShouldProcess = $false,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Ticket], [TANSS.Project])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "TicketId"
        )]
        [int[]]
        $Id,

        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $false,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "CompanyId"
        )]
        [int[]]
        $CompanyId,


        [Parameter(
            Mandatory = $false,
            ParameterSetName = "MyTickets"
        )]
        [Alias("Own", "OwnTickets", "MyOwn", "NyOwnTickets")]
        [switch]
        $MyTickets,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "NotAssigned"
        )]
        [Alias("General", "GeneralTickets")]
        [switch]
        $NotAssigned,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "AllTechnician"
        )]
        [Alias("TicketsAllTechnicians", "Assigned", "AssignedTickets")]
        [switch]
        $AllTechnician,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "RepairTickets"
        )]
        [switch]
        $RepairTickets,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "NotIdentified"
        )]
        [switch]
        $NotIdentified,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "Projects"
        )]
        [switch]
        [Alias("AllProjects")]
        $Project,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "LocalTicketAdmin"
        )]
        [Alias("AssignedToTicketAdmin")]
        [switch]
        $LocalTicketAdmin,

        [Parameter(
            Mandatory = $true,
            ParameterSetName = "TicketWithTechnicianRole"
        )]
        [switch]
        $TicketWithTechnicianRole,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"
        $response = @()

        # Construct query
        switch ($parameterSetName) {
            "TicketId" {
                # in case of "Query by Id" -> Do the query now
                $response += foreach ($ticketId in $Id) {
                    Invoke-TANSSRequest -Type GET -ApiPath "api/v1/tickets/$($ticketId)" -Token $Token
                }
            }

            "CompanyId" {
                # in case of "Query by CompanyId" -> Do the query now
                $response += foreach ($_companyId in $CompanyId) {
                    Invoke-TANSSRequest -Type GET -ApiPath "api/v1/tickets/company/$($_companyId)" -Token $Token
                }
            }

            "MyTickets" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/own"
            }

            "NotAssigned" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/general"
            }

            "AllTechnician" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/technician"
            }

            "RepairTickets" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/repair"
            }

            "NotIdentified" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/notIdentified"
            }

            "Projects" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/projects"
            }

            "LocalTicketAdmin" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/localAdminOverview"
            }

            "TicketWithTechnicianRole" {
                $apiPath = Format-ApiPath -Path "api/v1/tickets/withRole"
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }

        # Do the constructed query, as long, as variable apiPath has a value
        if ($apiPath) {
            $response += Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token
        }

        if ($response) {
            Write-PSFMessage -Level Verbose -Message "Found $(($response.content).count) tickets"

            # Push meta to cache runspace
            foreach ($responseItem in $response) {
                Push-DataToCacheRunspace -MetaData $responseItem.meta

                # Output result
                foreach($ticket in $responseItem.content) {
                    if($parameterSetName -like "Projects") {
                        [TANSS.Project]@{
                            BaseObject = $ticket
                            Id = $ticket.id
                        }
                    } else {
                        [TANSS.Ticket]@{
                            BaseObject = $ticket
                            Id = $ticket.id
                        }
                    }
                }

            }
        } else {
            Write-PSFMessage -Level Warning -Message "No tickets found." -Tag "Ticket"
        }
    }

    end {
    }
}


function Get-TANSSTicketActivity {
    <#
    .Synopsis
        Get-TANSSTicketActivity
 
    .DESCRIPTION
        Retreive support activities within a ticket
 
    .PARAMETER TicketID
        The ID of the ticket to receive activities of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive activities of
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketActivity -TicketID 555
 
        Get all support activities from ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketActivity
 
        Get all support activities from all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
 
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketComment", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketComment", "Query"

            Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Activity" -Token $Token | Select-Object -ExpandProperty Object
        }

    }

    end {
    }
}


function Get-TANSSTicketComment {
    <#
    .Synopsis
        Get-TANSSTicketComment
 
    .DESCRIPTION
        Retreive the ticket comments
 
    .PARAMETER TicketID
        The ID of the ticket to receive comments of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive comments of
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketComment -TicketID 555
 
        Get all comments from ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketComment
 
        Get all comments from all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
 
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketComment", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketComment", "Query"

            Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Comment" -Token $Token | Select-Object -ExpandProperty Object
        }

    }

    end {
    }
}


function Get-TANSSTicketContent {
    <#
    .Synopsis
        Get-TANSSTicketContent
 
    .DESCRIPTION
        Retreive the various entries from a ticket.
        Entries can be a comment, activity, mail, document, image
 
    .PARAMETER TicketID
        The ID of the ticket to receive comments of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive comments of
 
    .PARAMETER Type
        Specifies the type of content you want to get
 
        Available types:
            "All" = everthing within the ticket
            "Comment" = only comments within the ticket
            "Activity" = only activites within the ticket
            "Mail" = = only mails within the ticket
            "Document" = uploaded documents within the ticket
            "Image" = uploaded images within the ticket
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketContent -TicketID 555
 
        Get everthing out of ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketContent -Type "Mail"
 
        Get only malis out of all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [ValidateNotNullOrEmpty()]
        [ValidateSet("All", "Comment", "Activity", "Mail", "Document", "Image")]
        [string[]]
        $Type = "All",

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketContent", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketContent", "Query"
            $content = @()

            # Get documents, if included in Type filter
            if ( ($Type | Where-Object { $_ -in @("All", "Document") }) ) {
                Write-PSFMessage -Level Verbose -Message "Getting documents from ticket $($ticketIdItem)"  -Tag "TicketContent", "Query", "QueryDocuments"

                $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketIdItem/documents"
                $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token
                Push-DataToCacheRunspace -MetaData $response.meta

                if ($response.content) {
                    Write-PSFMessage -Level Verbose -Message "Found $($response.content.count) documents"  -Tag "TicketContent", "Query", "QueryDocuments"
                    $content += foreach ($document in $response.content) {
                        # create output objects, but first query download uri

                        # build api path
                        $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketIdItem/documents/$($document.id)"

                        # query download uri
                        $responseDocumentUri = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token

                        # create output
                        $object = [TANSS.TicketDocument]@{
                            BaseObject = $document
                            Id         = $document.id
                        }
                        if ($responseDocumentUri.content) {
                            $object.Key = $responseDocumentUri.content.key
                            $object.DownloadUri = Format-ApiPath -Path $responseDocumentUri.content.url
                        }

                        [TANSS.TicketContent]@{
                            TicketId = $object.TicketId
                            Type     = "Document"
                            Id       = $object.Id
                            Date     = $object.Date
                            Text     = $object.Description
                            Object   = $object
                        }
                    }
                } else {
                    Write-PSFMessage -Level Verbose -Message "No documents found"  -Tag "TicketContent", "Query", "QueryDocuments"
                }
            }

            # Get images, if included in Type filter
            if ( ($Type | Where-Object { $_ -in @("All", "Image") }) ) {
                Write-PSFMessage -Level Verbose -Message "Getting images from ticket $($ticketIdItem)"  -Tag "TicketContent", "Query", "QueryImages"

                $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketIdItem/screenshots"
                $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token
                Push-DataToCacheRunspace -MetaData $response.meta

                if ($response.content) {
                    Write-PSFMessage -Level Verbose -Message "Found $($response.content.count) images"  -Tag "TicketContent", "Query", "QueryImages"
                    $content += foreach ($image in $response.content) {
                        $object = [TANSS.TicketImage]@{
                            BaseObject = $image
                            Id         = $image.id
                        }

                        [TANSS.TicketContent]@{
                            TicketId = $object.TicketId
                            Type     = "Image"
                            Id       = $object.Id
                            Date     = $object.Date
                            Text     = $object.Description
                            Object   = $object
                        }

                    }
                } else {
                    Write-PSFMessage -Level Verbose -Message "No images found"  -Tag "TicketContent", "Query", "QueryImages"
                }
            }

            # Get Comments, Activities, Mails
            if ( ($Type | Where-Object { $_ -in @("All", "Comment", "Activity", "Mail") }) ) {
                Write-PSFMessage -Level Verbose -Message "Getting comments, mails and activities from ticket $($ticketIdItem)"  -Tag "TicketContent", "Query", "QueryHistory"

                $apiPath = Format-ApiPath -Path "api/v1/tickets/history/$ticketIdItem"
                $response = Invoke-TANSSRequest -Type GET -ApiPath "backend/api/v1/tickets/history/$ticketID" -Token $Token
                Push-DataToCacheRunspace -MetaData $response.meta

                if ($response.content) {
                    Write-PSFMessage -Level Verbose -Message "Found content in ticket $($ticketIdItem), going to parse content"  -Tag "TicketContent", "Query", "QueryHistory"

                    # Get comments
                    if ( ($Type | Where-Object { $_ -in @("All", "Comment") }) ) {
                        Write-PSFMessage -Level Verbose -Message "Working through $($response.content.comments.count) comment$(if($response.content.comments.count -gt 1) {'s'})"  -Tag "TicketContent", "Query", "QueryHistory", "Comment"

                        $content += foreach ($comment in $response.content.comments) {
                            $object = [TANSS.TicketComment]@{
                                BaseObject = $comment
                                ID         = $comment.id
                                TicketId   = $ticketIdItem
                            }

                            [TANSS.TicketContent]@{
                                TicketId = $object.TicketId
                                Type     = "Comment"
                                Id       = $object.Id
                                Date     = $object.Date
                                Text     = $object.Description
                                Object   = $object
                            }
                        }
                    }

                    # Get activities
                    if ( ($Type | Where-Object { $_ -in @("All", "Activity") }) ) {
                        Write-PSFMessage -Level Verbose -Message "Working through $($response.content.supports.count) $(if($response.content.supports.count -gt 1) {'Activities'} else {'Activity'})"  -Tag "TicketContent", "Query", "QueryHistory", "Activity"

                        $content += foreach ($activity in $response.content.supports) {
                            $object = [TANSS.TicketActivity]@{
                                BaseObject = $activity
                                ID         = $activity.id
                            }

                            [TANSS.TicketContent]@{
                                TicketId = $object.TicketId
                                Type     = "Activity"
                                Id       = $object.Id
                                Date     = $object.Date
                                Text     = $object.Description
                                Object   = $object
                            }
                        }
                    }

                    # Get mails
                    if ( ($Type | Where-Object { $_ -in @("All", "Mail") }) ) {
                        Write-PSFMessage -Level Verbose -Message "Working through $($response.content.mails.count) mail$(if($response.content.mails.count -gt 1) {'s'})"  -Tag "TicketContent", "Query", "QueryHistory", "Mail"

                        $content += foreach ($mail in $response.content.mails) {
                            $object = [TANSS.TicketMail]@{
                                BaseObject = $mail
                                ID         = $mail.id
                                TicketId   = $ticketIdItem
                            }

                            [TANSS.TicketContent]@{
                                TicketId = $object.TicketId
                                Type     = "Mail"
                                Id       = $object.Id
                                Date     = $object.Date
                                Text     = $object.Subject
                                Object   = $object
                            }
                            #>
                        }
                    }
                } else {
                    $_type = $Type | Where-Object { $_ -notlike "All" }
                    if ($_type) {
                        $_type = [string]::Join(', ', [array]$_type)
                    } else {
                        $_type = [string]::Join(', ', @("Comment", "Activity", "Mail"))
                    }
                    Write-PSFMessage -Level Verbose -Message "No $_type data found"  -Tag "TicketContent", "Query", "QueryHistory"
                }
            }

            # Output result
            if($content) {
                Write-PSFMessage -Level Verbose -Message "Output $(([array]$content).count) content record$(if(([array]$content).count -gt 1){'s'}) from ticket $($ticketIdItem)" -Tag "TicketContent", "Query", "OutputResult"

                $content | Sort-Object Date

            } else {
                Write-PSFMessage -Level Significant -Message "No $(if($Type -notlike "All") {'matching'}) content found in ticket $($ticketIdItem)" -Tag "TicketContent", "Query", "OutputResult", "NoData"
            }
        }
    }

    end {}
}


function Get-TANSSTicketDocument {
    <#
    .Synopsis
        Get-TANSSTicketDocument
 
    .DESCRIPTION
        Retreive documents within a ticket
 
    .PARAMETER TicketID
        The ID of the ticket to receive documents of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive documents of
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketDocument -TicketID 555
 
        Get all documents from ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketDocument
 
        Get all documents from all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketComment", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketComment", "Query"

            Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Document" -Token $Token | Select-Object -ExpandProperty Object
        }

    }

    end {
    }
}


function Get-TANSSTicketImage {
    <#
    .Synopsis
        Get-TANSSTicketImage
 
    .DESCRIPTION
        Retreive images within a ticket
 
    .PARAMETER TicketID
        The ID of the ticket to receive images of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive images of
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketImage -TicketID 555
 
        Get all images from ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketImage
 
        Get all images from all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketComment", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketComment", "Query"

            Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Image" -Token $Token | Select-Object -ExpandProperty Object
        }

    }

    end {
    }
}


function Get-TANSSTicketMail {
    <#
    .Synopsis
        Get-TANSSTicketMail
 
    .DESCRIPTION
        Retreive mail objects within a ticket
 
    .PARAMETER TicketID
        The ID of the ticket to receive mails of
 
    .PARAMETER Ticket
        TANSS.Ticket object to receive mails of
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketMail -TicketID 555
 
        Get all mails from ticket 555
 
    .EXAMPLE
        PS C:\> $tickets | Get-TANSSTicketMail
 
        Get all mails from all tickets from variable $tickets
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int[]]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket[]]
        $Ticket,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") {
            $inputobjectTicketCount = ([array]$Ticket).Count
            Write-PSFMessage -Level System -Message "Getting IDs of $($inputobjectTicketCount) ticket$(if($inputobjectTicketCount -gt 1){'s'})"  -Tag "TicketComment", "CollectInputObjects"
            [array]$TicketID = $Ticket.id
        }

        foreach ($ticketIdItem in $TicketID) {
            Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($ticketIdItem)"  -Tag "TicketComment", "Query"

            Get-TANSSTicketContent -TicketID $ticketIdItem -Type "Mail" -Token $Token | Select-Object -ExpandProperty Object
        }

    }

    end {
    }
}


function Get-TANSSTicketStatus {
    <#
    .Synopsis
        Get-TANSSTicketStatus
 
    .DESCRIPTION
        Get the various status types of a ticket from tanss
 
    .PARAMETER Id
        The Id of the status type to get
 
    .PARAMETER Name
        The name of the status type to get
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketStatus
 
        Get the available status types of a ticket
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketStatus -Id 2
 
        Get the status types with Id 2
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "All",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.TicketStatus])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ById"
        )]
        [int[]]
        $Id,

        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ByName"
        )]
        [string[]]
        $Name,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }

        Assert-CacheRunspaceRunning

        $apiPath = Format-ApiPath -Path "api/v1/tickets/status"
        $ticketStates = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token | Select-Object -ExpandProperty content


        [array]$filteredTicketStates = @()
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            "ById" {
                foreach ($item in $Id) {
                    $filteredTicketStates += $ticketStates | Where-Object id -eq $item
                }
            }

            "ByName" {
                foreach ($item in $Name) {
                    $filteredTicketStates += $ticketStates | Where-Object name -like $item
                }
            }

            "All" {
                $filteredTicketStates = $ticketStates
            }

            Default {
                Stop-PSFFunction -Message "Unhandled ParameterSet '$($parameterSetName)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "TicketStatus", "SwitchException", "ParameterSet"
            }
        }
    }

    end {
        $filteredTicketStates = $filteredTicketStates | Sort-Object rank, id -Unique
        Write-PSFMessage -Level Verbose -Message "Going to return $($filteredTicketStates.count) ticket status" -Tag "TicketStatus", "Output"

        foreach ($ticketStatus in $filteredTicketStates) {
            Write-PSFMessage -Level System -Message "Working on ticketstatus '$($ticketStatus.name)' with id '$($ticketStatus.id)'" -Tag "TicketStatus"

            # put id and name to cache lookups
            Update-CacheLookup -LookupName "TicketStates" -Id $ticketStatus.Id -Name $ticketStatus.Name

            # output result
            [TANSS.TicketStatus]@{
                Baseobject = $ticketStatus
                Id = $ticketStatus.id
            }
        }
    }
}


function Get-TANSSTicketType {
    <#
    .Synopsis
        Get-TANSSTicketType
 
    .DESCRIPTION
        Get the various types of a ticket from tanss
 
    .PARAMETER Id
        The Id of the ticket type to get
 
    .PARAMETER Name
        The name of the ticket type to get
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketType
 
        Get the available ticket types
 
    .EXAMPLE
        PS C:\> Get-TANSSTicketType -Id 2
 
        Get the tiket types with Id 2
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "All",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.TicketType])]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ById"
        )]
        [int[]]
        $Id,

        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true,
            ParameterSetName = "ByName"
        )]
        [string[]]
        $Name,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }

        Assert-CacheRunspaceRunning

        $apiPath = Format-ApiPath -Path "api/v1/tickets/types"
        $ticketTypes = Invoke-TANSSRequest -Type GET -ApiPath $apiPath -Token $Token | Select-Object -ExpandProperty content


        [array]$filteredTicketTypes = @()
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            "ById" {
                foreach ($item in $Id) {
                    $filteredTicketTypes += $ticketTypes | Where-Object id -eq $item
                }
            }

            "ByName" {
                foreach ($item in $Name) {
                    $filteredTicketTypes += $ticketTypes | Where-Object name -like $item
                }
            }

            "All" {
                $filteredTicketTypes = $ticketTypes
            }

            Default {
                Stop-PSFFunction -Message "Unhandled ParameterSet '$($parameterSetName)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "TicketType", "SwitchException", "ParameterSet"
            }
        }
    }

    end {
        $filteredTicketTypes = $filteredTicketTypes | Sort-Object Name, id -Unique
        Write-PSFMessage -Level Verbose -Message "Going to return $($filteredTicketTypes.count) ticket status" -Tag "TicketType", "Output"

        foreach ($ticketType in $filteredTicketTypes) {
            Write-PSFMessage -Level System -Message "Working on TicketType '$($ticketType.name)' with id '$($ticketType.id)'" -Tag "TicketType"

            # put id and name to cache lookups
            Update-CacheLookup -LookupName "TicketTypes" -Id $ticketType.Id -Name $ticketType.Name

            # output result
            [TANSS.TicketType]@{
                Baseobject = $ticketType
                Id = $ticketType.id
            }
        }
    }
}


function New-TANSSTicket {
    <#
    .Synopsis
        New-TANSSTicket
 
    .DESCRIPTION
        Creates a ticket in the database
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSTicket -Title "A new Ticket"
 
        Create a ticket
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")]
    [CmdletBinding(
        DefaultParameterSetName = "Userfriendly",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Ticket])]
    param (
        # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $CompanyId,

        # Company name where the ticket should create for. Can only be set if the user has access to the company
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Company,

        # If the ticket has a remitter, the id goes here. Name is stored in the "linked entities" - "employees"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('ClientId')]
        [int]
        $RemitterId,

        # If the ticket has a remitter/client, the name of the client
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Client,

        # gives infos about how the remitter gave the order. Infos are stored in the "linked entities" - "orderBys"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $OrderById,

        # gives infos about how the Client gave the order.
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $OrderBy,

        # The title / subject of the ticket
        [Parameter(
            ParameterSetName="Userfriendly",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true

        )]
        [Parameter(
            ParameterSetName="ApiNative",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true

        )]
        [string]
        $Title,

        # The content / description of the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('content')]
        [string]
        $Description,

        # External ticket id (optional)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('extTicketId')]
        [string]
        $ExternalTicketId,

        # id of employee which ticket is assigned to. Name is stored in "linked entities" - "employees"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('assignedToEmployeeId')]
        [int]
        $EmployeeIdAssigned,

        # Name of the employee the ticket is assigned to
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $EmployeeAssigned,

        # id of department the ticket is assigned to. Name is stored in "linked entities" - "departments"
        [Parameter(ParameterSetName="ApiNative")]
        [Alias('assignedToDepartmentId')]
        [int]
        $DepartmentIdAssigned,

        # Name of the department the ticket is assigned to
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Department,

        # id of the ticket state. Name is give in "linked entities" - "ticketStates"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $StatusId,

        # The name of the ticket status
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Status,

        # id of the ticket type. Name is give in "linked entities" - "ticketTypes"
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $TypeId,

        # The name of the ticket type
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Type,

        # if ticket is assigned to device / employee, linktype is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('LinkTypeId')]
        [int]
        $AssignmentId,

        # If ticket has a deadline, the date is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('deadlineDate')]
        [datetime]
        $Deadline,

        # if ticket is actually a project, this value is true
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('project')]
        [bool]
        $IsProject = $false,

        # if ticket is a sub-ticket of a project, the id of the project goes here. Name of the project is in the "linked entities" - "tickets"
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $ProjectId,

        # if the ticket is assignet to a project phase. The name of the phase is stored in the "linked entities" - "phases"
        [Parameter(ParameterSetName="ApiNative")]
        [Int]
        $PhaseId,

        # if the ticket is assignet to a project phase, the name of the phase
        [Parameter(ParameterSetName="Userfriendly")]
        [String]
        $Phase,

        # if true, this ticket is a "repair ticket"
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [Alias('repair')]
        [bool]
        $IsRepair = $false,

        # if ticket has a due date, the timestamp is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $DueDate,

        # Determines the "attention" flag state of a ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NO", "YES", "RESUBMISSION", "MAIL")]
        [string]
        $Attention = "NO",

        # If the ticket has an installation fee, this value is true
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NO", "YES", "NO_PROJECT_INSTALLATION_FEE")]
        [string]
        $InstallationFee = "NO",

        # Sets the installation fee drive mode. If it is set to NONE then the system config parameter "leistung.ip.fahrzeit_berechnen" will be used. If the company from the ticket has an installation fee drive mode set then that will be used instead of the system config parameter.
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NONE", "DRIVE_INCLUDED", "DRIVE_EXCLUDED")]
        [string]
        $InstallationFeeDriveMode = "NONE",

        #Amount for the installation fee
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $InstallationFeeAmount,

        # If true, the ticket shall be billed separately
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [bool]
        $SeparateBilling = $false,

        # if the ticket has a service cap, here the amount is given
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $ServiceCapAmount,

        # linkId of the relationship (if ticket has a relation)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $RelationshipLinkId,

        # linkTypeId of the relationship (if ticket has a relation)
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $RelationshipLinkTypeId,

        # if the ticket as a resubmission date set, this is given here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $ResubmissionDate,

        # If a resubmission text is set, this text is returned here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $ResubmissionText,

        # Number of estimated minutes which is planned for the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [int]
        $EstimatedMinutes,

        # Determines wether the ticket is assigned to a local ticket admin or not
        # NONE: "normal" ticket
        # LOCAL_ADMIN: ticket is assigned to a local ticket admin
        # TECHNICIAN: local ticket admin has forwarded the ticket to a technician
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateSet("NONE", "LOCAL_ADMIN", "TECHNICIAN")]
        [string]
        $LocalTicketAdminFlag = "NONE",

        # if the ticket is assigned to a local ticket admin, this represents the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName="ApiNative")]
        [int]
        $LocalTicketAdminEmployeeId,

        # if the ticket is assigned to a local ticket admin, this represents the name of the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName="Userfriendly")]
        [ValidateNotNullOrEmpty]
        [string]
        $EmployeeTicketAdmin,

        # Sets the order number
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string]
        $OrderNumber,

        # If the ticket has a reminder set, the timestamp is returned here
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [datetime]
        $Reminder,

        # When persisting a ticket, you can also send a list of tag assignments which will be assigned to the ticket
        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [string[]]
        $Tags,

        [Parameter(ParameterSetName="ApiNative")]
        [Parameter(ParameterSetName="Userfriendly")]
        [TANSS.Connection]
        $Token
    )

    begin {
        if(-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
        $apiPath = Format-ApiPath -Path "api/v1/tickets"

        if($EmployeeTicketAdmin) {
            $LocalTicketAdminEmployeeId = ConvertFrom-NameCache -Name $EmployeeTicketAdmin -Type "Employees"
            if(-not $LocalTicketAdminEmployeeId) {
                Write-PSFMessage -Level Warning -Message "No Id for employee '$($EmployeeTicketAdmin)' found. Ticket will be created with blank value on TicketAdminEmployee"
                #todo implement API call for employee
            }
        }

        if($EmployeeAssigned) {
            $EmployeeIdAssigned = ConvertFrom-NameCache -Name $EmployeeAssigned -Type "Employees"
            if(-not $EmployeeIdAssigned) {
                Write-PSFMessage -Level Warning -Message "No Id for employee '$($EmployeeAssigned)' found. Ticket will be created with blank value on EmployeeIdAssigned"
                #todo implement API call for employee
            }
        }

        if($Client) {
            $RemitterId = ConvertFrom-NameCache -Name $Client -Type "Employees"
            if(-not $RemitterId) {
                Write-PSFMessage -Level Warning -Message "No Id for client '$($Client)' found. Ticket will be created with blank value on RemitterId"
                #todo implement API call for employee
            }
        }

        if($Phase) {
            $PhaseId = ConvertFrom-NameCache -Name $Phase -Type "Phases"
            if(-not $PhaseId) {
                Write-PSFMessage -Level Warning -Message "No Id for phase '$($Phase)' found. Ticket will be created with blank value on Phase"
            }
        }

        if($Type) {
            $TypeId = ConvertFrom-NameCache -Name $Type -Type "TicketTypes"
            if(-not $TypeId) {
                Write-PSFMessage -Level Warning -Message "No Id for ticket type '$($Type)' found. Ticket will be created with blank value on TicketType"
            }
        }

        if ($OrderBy) {
            $OrderById = ConvertFrom-NameCache -Name $OrderBy -Type "OrderBys"
            if (-not $OrderById) {
                Write-PSFMessage -Level Warning -Message "No Id for OrderBy type '$($OrderBy)' found. Ticket will be created with blank value on OrderById"
            }
        }

        if($Status) {
            $StatusId = ConvertFrom-NameCache -Name $Status -Type "TicketStates"
            if(-not $StatusId) {
                Write-PSFMessage -Level Warning -Message "No Id for ticket state '$($Status)' found. Ticket will be created with blank value on TicketStatus"
            }
        }

        if($Department) {
            $DepartmentIdAssigned = ConvertFrom-NameCache -Name $Department -Type "Departments"
            if(-not $DepartmentIdAssigned) {
                Write-PSFMessage -Level Warning -Message "No Id for department '$($Department)' found. Ticket will be created with blank value on departmentIdAssigned"
            }
        }

        if($Company) {
            $CompanyId = ConvertFrom-NameCache -Name $Company -Type "Companies"
            if(-not $CompanyId) {
                Write-PSFMessage -Level Warning -Message "No Id for company '$($Company)' found. Ticket will be created with blank value on CompanyId"
            }
        }

    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "Userfriendly" -and (-not $Title)) {
            Write-PSFMessage -Level Error -Message "No title specified"
            continue
        }

        #region rest call prepare
        if ($Deadline) {
            $_deadlineDate = [int][double]::Parse((Get-Date -Date $Deadline.ToUniversalTime() -UFormat %s))
        } else {
            $_deadlineDate = 0
        }

        if ($ResubmissionDate) {
            $_resubmissionDate = [int][double]::Parse((Get-Date -Date $ResubmissionDate.ToUniversalTime() -UFormat %s))
        } else {
            $_resubmissionDate = 0
        }

        if ($Reminder) {
            $_reminder = [int][double]::Parse((Get-Date -Date $Reminder.ToUniversalTime() -UFormat %s))
        } else {
            $_reminder = 0
        }

        if ($DueDate) {
            $_dueDate = [int][double]::Parse((Get-Date -Date $DueDate.ToUniversalTime() -UFormat %s))
        } else {
            $_dueDate = 0
        }


        $body = [ordered]@{
            companyId                  = $CompanyId
            remitterId                 = $RemitterId
            orderById                  = $OrderById
            title                      = "$($Title)"
            content                    = "$($Description)"
            extTicketId                = "$($ExternalTicketId)"
            assignedToEmployeeId       = $EmployeeIdAssigned
            assignedToDepartmentId     = $DepartmentIdAssigned
            statusId                   = $StatusId
            typeId                     = $TypeId
            linkTypeId                 = $AssignmentId
            deadlineDate               = $_deadlineDate
            project                    = $IsProject
            projectId                  = $ProjectId
            phaseId                    = $PhaseId
            repair                     = $IsRepair
            dueDate                    = $_dueDate
            attention                  = $Attention
            installationFee            = $InstallationFee
            installationFeeDriveMode   = $InstallationFeeDriveMode
            installationFeeAmount      = $InstallationFeeAmount
            separateBilling            = $SeparateBilling
            serviceCapAmount           = $ServiceCapAmount
            relationshipLinkTypeId     = $RelationshipLinkTypeId
            relationshipLinkId         = $RelationshipLinkId
            resubmissionDate           = $_resubmissionDate
            resubmissionText           = "$($ResubmissionText)"
            estimatedMinutes           = $EstimatedMinutes
            localTicketAdminFlag       = $LocalTicketAdminFlag
            localTicketAdminEmployeeId = $LocalTicketAdminEmployeeId
            orderNumber                = "$($OrderNumber)"
            reminder                   = $_reminder
            #subTickets = @{}
            tags                       = $Tags
        }
        #endregion rest call prepare

        if ($pscmdlet.ShouldProcess("Ticket with title '$($Title)' on companyID '$($CompanyId)'", "New")) {
            Write-PSFMessage -Level Verbose -Message "Creating Ticket with title '$($Title)' on companyID '$($CompanyId)'" -Tag "Ticket" -Data $body

            $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token

            if($response) {
                Write-PSFMessage -Level Verbose -Message "API Response: $($response.meta.text)"

                Push-DataToCacheRunspace -MetaData $response.meta

                [TANSS.Ticket]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }
            } else {
                Write-PSFMessage -Level Error -Message "Error creating ticket, no ticket response from API"
            }
        }
    }

    end {
    }
}

function New-TANSSTicketComment {
    <#
    .Synopsis
        New-TANSSTicketComment
 
    .DESCRIPTION
        Add a comment to a ticket
 
    .PARAMETER TicketID
        ID of the ticket where to put comment in
 
    .PARAMETER Ticket
        TANSS.Ticket object where to put comment in
 
    .PARAMETER Title
        Optional. A comment can have a title text.
 
    .PARAMETER Text
        The text to put within the comment
 
    .PARAMETER IsInternal
        Boolean, that specifies if the comment will be internal or public visible to customers
 
    .PARAMETER Pinned
        Switch parameter. If specified, the comment will be pinned at the top of the ticket
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSTicketComment -TicketID 555 -Title "very important info" -Text "Please note this important information" -IsInternal $true
 
        Put a comment with text "Please note this important information" within ticket 555
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.TicketComment])]
    Param(
        [Parameter(
            ParameterSetName = "ByTicketId",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [int]
        $TicketID,

        [Parameter(
            ParameterSetName = "ByTicket",
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.Ticket]
        $Ticket,

        [string]
        $Title,

        [Alias("Description")]
        [string]
        $Text,


        [Alias("Internal")]
        [bool]
        $IsInternal = $true,

        [switch]
        $Pinned,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByTicket") { $TicketID = $Ticket.id }

        Write-PSFMessage -Level Verbose -Message "Working on ticket ID $($TicketID)" -Tag "TicketComment", "Add"

        # Prepare variables for rest call
        if ($Pinned) { $queryParameter = "?pinned=true" } else { $queryParameter = "?pinned=false" }
        $apiPath = Format-ApiPath -Path "api/v1/tickets/$ticketID/comments$($queryParameter)"
        $body = @{
            "title"    = $Title
            "content"  = $Text
            "internal" = $IsInternal
        }

        if ($pscmdlet.ShouldProcess("$(if($IsInternal -eq $true) {"Internal"}else{"Public"}) comment $(if($Title){"with title '$($Title)' "})on Ticket $($TicketID)", "New")) {
            Write-PSFMessage -Level Verbose -Message "New $(if($IsInternal -eq $true) {"Internal"}else{"Public"}) comment $(if($Title){"with title '$($Title)' "})on Ticket $($TicketID)" -Tag "TicketComment", "Add"

            $response = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token

            if ($response.content) {
                Write-PSFMessage -Level Verbose -Message "$($response.meta.text)" -Tag "TicketComment", "Added"

                # prepare comment object for output
                $object = [PSCustomObject]@{
                    id         = $response.content.id
                    date       = $response.content.date
                    employeeId = $response.content.employeeId
                    categoryId = $response.content.categoryId
                    title      = $response.content.title
                    content    = $response.content.content
                    internal   = $response.content.internal
                }

                # output result
                [TANSS.TicketComment]@{
                    BaseObject = $object
                    ID         = $object.id
                    TicketId   = $response.content.commentOfId
                }
            }
        }
    }

    end {}
}


function Remove-TANSSTicket {
    <#
    .Synopsis
        Remove-TANSSTicket
 
    .DESCRIPTION
        Delete a ticket in TANSS
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Remove-TANSSTicket -ID 10
 
        Remove ticket with ticketID 10 from TANSS
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = 'ById',
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    param (
        # TANSS Ticket object to remove
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [TANSS.Ticket[]]
        $InputObject,

        # Id of the ticket to remove
        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("TicketId", "Ticket")]
        [int[]]
        $Id,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ById") {
            $InputObject = foreach ($idItem in $Id) {
                Get-TANSSTicket -Id $idItem -ErrorAction Continue
            }
        }

        foreach ($ticket in $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Working on TicketID $($ticket.Id) '$($ticket.Title)'"
            $apiPath = Format-ApiPath -Path "api/v1/tickets/$($ticket.Id)"

            if ($pscmdlet.ShouldProcess("TicketID $($ticket.Id) '$($ticket.Title)' from TANSS", "Remove")) {
                Write-PSFMessage -Level Verbose -Message "Removing TicketID $($ticket.Id) '$($ticket.Title)' from TANSS" -Tag "Ticket"

                Invoke-TANSSRequest -Type DELETE -ApiPath $apiPath -Token $Token -ErrorAction Stop -ErrorVariable invokeError

                if(-not $invokeError) {
                    [TANSS.Lookup]::Tickets.Remove("$($ticket.Id)")
                }
            }
        }
    }

    end {
    }
}

function Remove-TANSSTicketComment {
    <#
    .Synopsis
        Remove-TANSSTicketComment
 
    .DESCRIPTION
        Remove a comment from a ticket
 
    .PARAMETER TicketID
        The ID of the ticket where to remove a comment from
 
    .PARAMETER Id
        The id of the comment to remove
 
    .PARAMETER Comment
        TANSS.TicketComment object to remove
 
    .PARAMETER Force
        Process the removal quietly.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Remove-TANSSTicketComment
 
        Description
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByTicketId",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true
        )]
        [int]
        $TicketID,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true
        )]
        [Alias("CommentID")]
        [int]
        $Id,

        [Parameter(
            ParameterSetName = "ByInputObject",
            ValueFromPipeline = $true,
            Mandatory = $true
        )]
        [TANSS.TicketComment]
        $Comment,

        [switch]
        $Force,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "ByInputObject") {
            $TicketID = $Comment.TicketId
            $Id = $Comment.Id
        }


        # Check on Force parameter, otherwise process shouldprocess
        if ($Force) {
            $processRemoval = $true
        } else {
            if ($pscmdlet.ShouldProcess("comment ID $($Id) from ticket ID $($TicketID)", "Remove")) {
                $processRemoval = $true
            }
        }

        if ($processRemoval) {
            Write-PSFMessage -Level Verbose -Message "Remove comment ID $($Id) from ticket ID $($TicketID)" -Tag "TicketComment", "Remove"

            # Remove comment from ticket
            $apiPath = Format-ApiPath -Path "api/v1/tickets/$($TicketID)/comments/$($Id)"
            Invoke-TANSSRequest -Type DELETE -ApiPath $apiPath -Token $Token -Confirm:$false
        }
    }

    end {}
}


function Set-TANSSTicket {
    <#
    .Synopsis
        Set-TANSSTicket
 
    .DESCRIPTION
        Modify a ticket in TANSS
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the token to the console, even when the register switch is set
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Set-TANSSTicket -ID 10 -NewTitle "New ticket title"
 
        Update title to "New ticket title" of ticket with ticketID 10
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidMultipleTypeAttributes", "")]
    [CmdletBinding(
        DefaultParameterSetName = 'UserFriendly-ByInputObject',
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Ticket])]
    param (
        # TANSS Ticket object to modify
        [Parameter(
            ParameterSetName = "ApiNative-ByInputObject",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = "UserFriendly-ByInputObject",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [TANSS.Ticket[]]
        $InputObject,

        # Id of the ticket to modify
        [Parameter(
            ParameterSetName = "ApiNative-ById",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Parameter(
            ParameterSetName = "UserFriendly-ById",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("TicketId", "Ticket")]
        [int[]]
        $Id,

        # Company id of the ticket. Name is stored in the "linked entities" - "companies". Can only be set if the user has access to the company
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [int]
        $CompanyId,

        # Company name where the ticket should create for. Can only be set if the user has access to the company
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Company,

        # If the ticket has a remitter, the id goes here. Name is stored in the "linked entities" - "employees"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [Alias('ClientId')]
        [int]
        $RemitterId,

        # If the ticket has a remitter/client, the name of the client
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Client,

        # gives infos about how the remitter gave the order. Infos are stored in the "linked entities" - "orderBys"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [int]
        $OrderById,

        # gives infos about how the Client gave the order.
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [string]
        $OrderBy,

        # The title / subject of the ticket
        [Alias('Title', "NewName")]
        [string]
        $NewTitle,

        # The content / description of the ticket
        [Alias('content')]
        [string]
        $Description,

        # External ticket id (optional)
        [Alias('extTicketId')]
        [string]
        $ExternalTicketId,

        # id of employee which ticket is assigned to. Name is stored in "linked entities" - "employees"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [Alias('assignedToEmployeeId')]
        [int]
        $EmployeeIdAssigned,

        # Name of the employee the ticket is assigned to
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $EmployeeAssigned,

        # id of department the ticket is assigned to. Name is stored in "linked entities" - "departments"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [Alias('assignedToDepartmentId')]
        [int]
        $DepartmentIdAssigned,

        # Name of the department the ticket is assigned to
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Department,

        # id of the ticket state. Name is give in "linked entities" - "ticketStates"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [int]
        $StatusId,

        # The name of the ticket status
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Status,

        # id of the ticket type. Name is give in "linked entities" - "ticketTypes"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [int]
        $TypeId,

        # The name of the ticket type
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Type,

        # if ticket is assigned to device / employee, linktype is given here
        [Alias('LinkTypeId')]
        [int]
        $AssignmentId,

        # If ticket has a deadline, the date is given here
        [Alias('deadlineDate')]
        [datetime]
        $Deadline,

        # if ticket is a sub-ticket of a project, the id of the project goes here. Name of the project is in the "linked entities" - "tickets"
        [int]
        $ProjectId,

        # if the ticket is assignet to a project phase. The name of the phase is stored in the "linked entities" - "phases"
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [Int]
        $PhaseId,

        # if the ticket is assignet to a project phase, the name of the phase
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [String]
        $Phase,

        # if true, this ticket is a "repair ticket"
        [Alias('repair')]
        [bool]
        $IsRepair = $false,

        # if ticket has a due date, the timestamp is given here
        [datetime]
        $DueDate,

        # Determines the "attention" flag state of a ticket
        [ValidateSet("NO", "YES", "RESUBMISSION", "MAIL")]
        [string]
        $Attention = "NO",

        # If the ticket has an installation fee, this value is true
        [ValidateSet("NO", "YES", "NO_PROJECT_INSTALLATION_FEE")]
        [string]
        $InstallationFee = "NO",

        # Sets the installation fee drive mode. If it is set to NONE then the system config parameter "leistung.ip.fahrzeit_berechnen" will be used. If the company from the ticket has an installation fee drive mode set then that will be used instead of the system config parameter.
        [ValidateSet("NONE", "DRIVE_INCLUDED", "DRIVE_EXCLUDED")]
        [string]
        $InstallationFeeDriveMode = "NONE",

        #Amount for the installation fee
        [int]
        $InstallationFeeAmount,

        # If true, the ticket shall be billed separately
        [bool]
        $SeparateBilling = $false,

        # if the ticket has a service cap, here the amount is given
        [int]
        $ServiceCapAmount,

        # linkId of the relationship (if ticket has a relation)
        [int]
        $RelationshipLinkId,

        # linkTypeId of the relationship (if ticket has a relation)
        [int]
        $RelationshipLinkTypeId,

        # if the ticket as a resubmission date set, this is given here
        [datetime]
        $ResubmissionDate,

        # If a resubmission text is set, this text is returned here
        [string]
        $ResubmissionText,

        # Number of estimated minutes which is planned for the ticket
        [int]
        $EstimatedMinutes,

        # Determines wether the ticket is assigned to a local ticket admin or not
        # NONE: "normal" ticket
        # LOCAL_ADMIN: ticket is assigned to a local ticket admin
        # TECHNICIAN: local ticket admin has forwarded the ticket to a technician
        [ValidateSet("NONE", "LOCAL_ADMIN", "TECHNICIAN")]
        [string]
        $LocalTicketAdminFlag = "NONE",

        # if the ticket is assigned to a local ticket admin, this represents the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName = "ApiNative-ByInputObject")]
        [Parameter(ParameterSetName = "ApiNative-ById")]
        [ValidateNotNullOrEmpty]
        [int]
        $LocalTicketAdminEmployeeId,

        # if the ticket is assigned to a local ticket admin, this represents the name of the employee (local ticket admin) who is assigned for this ticket
        [Parameter(ParameterSetName = "UserFriendly-ByInputObject")]
        [Parameter(ParameterSetName = "UserFriendly-ById")]
        [ValidateNotNullOrEmpty]
        [String]
        $EmployeeTicketAdmin,

        # Sets the order number
        [string]
        $OrderNumber,

        # If the ticket has a reminder set, the timestamp is returned here
        [datetime]
        $Reminder,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

        if ($EmployeeTicketAdmin) {
            $LocalTicketAdminEmployeeId = ConvertFrom-NameCache -Name $EmployeeTicketAdmin -Type "Employees"
            if (-not $LocalTicketAdminEmployeeId) {
                Write-PSFMessage -Level Warning -Message "No Id for employee '$($EmployeeTicketAdmin)' found, ticket will not be modified on TicketAdminEmployee value"
                #todo implement API call for employee
            }
        }

        if ($EmployeeAssigned) {
            $EmployeeIdAssigned = ConvertFrom-NameCache -Name $EmployeeAssigned -Type "Employees"
            if (-not $EmployeeIdAssigned) {
                Write-PSFMessage -Level Warning -Message "No Id for employee '$($EmployeeAssigned)' found, ticket will not be modified on EmployeeIdAssigned value"
                #todo implement API call for employee
            }
        }

        if ($Client) {
            $RemitterId = ConvertFrom-NameCache -Name $Client -Type "Employees"
            if (-not $RemitterId) {
                Write-PSFMessage -Level Warning -Message "No Id for client '$($Client)' found, ticket will not be modified on RemitterId value"
                #todo implement API call for employee
            }
        }

        if ($Phase) {
            $PhaseId = ConvertFrom-NameCache -Name $Phase -Type "Phases"
            if (-not $PhaseId) {
                Write-PSFMessage -Level Warning -Message "No Id for phase '$($Phase)' found, ticket will not be modified on Phase value"
            }
        }

        if ($Type) {
            $TypeId = ConvertFrom-NameCache -Name $Type -Type "TicketTypes"
            if (-not $TypeId) {
                Write-PSFMessage -Level Warning -Message "No Id for ticket type '$($Type)' found, ticket will not be modified on TicketType value"
            }
        }

        if ($OrderBy) {
            $OrderById = ConvertFrom-NameCache -Name $OrderBy -Type "OrderBys"
            if (-not $OrderById) {
                Write-PSFMessage -Level Warning -Message "No Id for OrderBy type '$($OrderBy)' found, ticket will not be modified on OrderBy value"
            }
        }

        if ($Status) {
            $StatusId = ConvertFrom-NameCache -Name $Status -Type "TicketStates"
            if (-not $StatusId) {
                Write-PSFMessage -Level Warning -Message "No Id for ticket state '$($Status)' found, ticket will not be modified on TicketStatus value"
            }
        }

        if ($Department) {
            $DepartmentIdAssigned = ConvertFrom-NameCache -Name $Department -Type "Departments"
            if (-not $DepartmentIdAssigned) {
                Write-PSFMessage -Level Warning -Message "No Id for department '$($Department)' found, ticket will not be modified on departmentIdAssigned value"
            }
        }

        if ($Company) {
            $CompanyId = ConvertFrom-NameCache -Name $Company -Type "Companies"
            if (-not $CompanyId) {
                Write-PSFMessage -Level Warning -Message "No Id for company '$($Company)' found, ticket will not be modified on CompanyId"
            }
        }


        if ($Deadline) {
            $_deadlineDate = [int][double]::Parse((Get-Date -Date $Deadline.ToUniversalTime() -UFormat %s))
        } else {
            $_deadlineDate = 0
        }

        if ($DueDate) {
            $_dueDate = [int][double]::Parse((Get-Date -Date $DueDate.ToUniversalTime() -UFormat %s))
        } else {
            $_dueDate = 0
        }

        if ($ResubmissionDate) {
            $_resubmissionDate = [int][double]::Parse((Get-Date -Date $ResubmissionDate.ToUniversalTime() -UFormat %s))
        } else {
            $_resubmissionDate = 0
        }

        if ($Reminder) {
            $_reminder = [int][double]::Parse((Get-Date -Date $Reminder.ToUniversalTime() -UFormat %s))
        } else {
            $_reminder = 0
        }
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        if ($parameterSetName -like "*ById*") {
            $InputObject = foreach ($idItem in $Id) {
                Get-TANSSTicket -Id $idItem -ErrorAction Continue
            }
        }


        foreach ($ticket in $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Working on TicketID $($ticket.Id) '$($ticket.Title)'"
            $apiPath = Format-ApiPath -Path "api/v1/tickets/$($ticket.Id)"

            $body = [ordered]@{
                "companyId"                  = (.{if ($CompanyId) { $CompanyId } else { $ticket.BaseObject.companyId }})
                "remitterId"                 = (.{if ($RemitterId) { $RemitterId } else { $ticket.BaseObject.remitterId }})
                "title"                      = (.{if ($NewTitle) { "$($NewTitle)" } else { $ticket.BaseObject.title }})
                "content"                    = (.{if ($Description) { "$($Description)" } else { $ticket.BaseObject.content }})
                "extTicketId"                = (.{if ($ExternalTicketId) { "$($ExternalTicketId)" } else { $ticket.BaseObject.extTicketId }})
                "assignedToEmployeeId"       = (.{if ($EmployeeIdAssigned) { $EmployeeIdAssigned } else { $ticket.BaseObject.assignedToEmployeeId }})
                "assignedToDepartmentId"     = (.{if ($DepartmentIdAssigned) { $DepartmentIdAssigned } else { $ticket.BaseObject.assignedToDepartmentId }})
                "statusId"                   = (.{if ($StatusId) { $StatusId } else { $ticket.BaseObject.statusId }})
                "typeId"                     = (.{if ($TypeId) { $TypeId } else { $ticket.BaseObject.typeId }})
                "linkTypeId"                 = (.{if ($AssignmentId) { $AssignmentId } else { $ticket.BaseObject.linkTypeId }})
                "linkId"                     = $ticket.BaseObject.linkId
                "deadlineDate"               = (.{if ($_deadlineDate) { $_deadlineDate } else { $ticket.BaseObject.deadlineDate }})
                "project"                    = $ticket.BaseObject.project.ToString()
                "projectId"                  = (.{if ($ProjectId) { $ProjectId } else { $ticket.BaseObject.projectId }})
                "repair"                     = (.{if ($IsRepair) { $IsRepair } else { $ticket.BaseObject.repair }})
                "dueDate"                    = (.{if ($_dueDate) { $_dueDate } else { $ticket.BaseObject.dueDate }})
                "attention"                  = (.{if ($Attention) { $Attention } else { $ticket.BaseObject.attention }})
                "orderById"                  = (.{if ($OrderById) { $OrderById } else { $ticket.BaseObject.orderById }})
                "installationFee"            = (.{if ($InstallationFee) { $InstallationFee } else { $ticket.BaseObject.installationFee }})
                "installationFeeDriveMode"   = (.{if ($InstallationFeeDriveMode) { $InstallationFeeDriveMode } else { "None" }})
                "installationFeeAmount"      = (.{if ($InstallationFeeAmount) { $InstallationFeeAmount } else { $ticket.BaseObject.installationFeeAmount }})
                "separateBilling"            = (.{if ($SeparateBilling) { $SeparateBilling.ToString() } else { $ticket.BaseObject.separateBilling }})
                "serviceCapAmount"           = (.{if ($ServiceCapAmount) { $ServiceCapAmount } else { $ticket.BaseObject.serviceCapAmount }})
                "relationshipLinkTypeId"     = (.{if ($RelationshipLinkTypeId) { $RelationshipLinkTypeId } else { $ticket.BaseObject.orderById }})
                "relationshipLinkId"         = (.{if ($RelationshipLinkId) { $RelationshipLinkId } else { $ticket.BaseObject.relationshipLinkId }})
                "resubmissionDate"           = (.{if ($_resubmissionDate) { $_resubmissionDate } else { $ticket.BaseObject.resubmissionDate }})
                "estimatedMinutes"           = (.{if ($EstimatedMinutes) { $EstimatedMinutes } else { $ticket.BaseObject.estimatedMinutes }})
                "localTicketAdminFlag"       = (.{if ($LocalTicketAdminFlag) { $LocalTicketAdminFlag } else { $ticket.BaseObject.localTicketAdminFlag }})
                "localTicketAdminEmployeeId" = (.{if ($LocalTicketAdminEmployeeId) { $LocalTicketAdminEmployeeId } else { $ticket.BaseObject.localTicketAdminEmployeeId }})
                "phaseId"                    = (.{if ($PhaseId) { $PhaseId } else { $ticket.BaseObject.phaseId }})
                "resubmissionText"           = (.{if ($ResubmissionText) { "$($ResubmissionText)" } else { $ticket.BaseObject.resubmissionText }})
                "orderNumber"                = (.{if ($OrderNumber) { "$($OrderNumber)" } else { $ticket.BaseObject.orderNumber }})
                "reminder"                   = (.{if ($_reminder) { $_reminder } else { $ticket.BaseObject.reminder }})
            }

            if ($pscmdlet.ShouldProcess("TicketID $($ticket.Id) with $(if($NewTitle){"new "})title '$(if($NewTitle){$NewTitle}else{$ticket.Title})'", "Update")) {
                Write-PSFMessage -Level Verbose -Message "Updating TicketID $($ticket.Id) with $(if($NewTitle){"new "})title '$(if($NewTitle){$NewTitle}else{$ticket.Title})'" -Tag "Ticket" -Data $body

                $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token

                if ($response) {
                    Write-PSFMessage -Level Verbose -Message "API Response: $($response.meta.text)"

                    Push-DataToCacheRunspace -MetaData $response

                    if($PassThru) {
                        foreach ($content in $response.content) {
                            [TANSS.Ticket]@{
                                BaseObject = $content
                                Id         = $content.id
                            }
                        }
                    }
                } else {
                    Write-PSFMessage -Level Error -Message "Error updating ticketID '$($ticket.Id)', no ticket response from API"
                }
            }
        }
    }

    end {
    }
}

function Get-TANSSTimeStamp {
    <#
    .Synopsis
        Get-TANSSTimeStamp
 
    .DESCRIPTION
        Get docmented timestamps from TANSS
 
    .PARAMETER Start
        Starting date of the pariod to retreive timestamps for
 
        If not specified,the current day will be received.
 
    .PARAMETER End
        Enddate of the pariod to retreive timestamps for
 
        If not specified,the current day will be received.
 
    .PARAMETER EmployeeId
        The Id of the employee to retreive timestamps for
 
        As a default, the Id of employee logged in will be used.
 
    .PARAMETER EmployeeName
        The name of the employee to retreive timestamps for
 
        Tab completion available for known names
 
    .PARAMETER State
        The status of the timestamps to retreive
 
        Available via tab completion:
        "On", "Off", "StartPause", "EndPause"
 
        As a default, all states are retreived.
 
    .PARAMETER Type
        The type for the period to stamp
 
        Available via tab completion:
        "Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support"
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSTimeStamp
 
        Get the timestamps of the current day
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    Param(
        [datetime]
        $Start,

        [datetime]
        $End,

        [Parameter(
            ParameterSetName = "ById",
            ValueFromPipeline = $true
        )]
        [int[]]
        $EmployeeId,

        [Parameter(
            ParameterSetName = "ByName",
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [string[]]
        $EmployeeName,

        [ValidateSet("Coming", "Leaving", "StartPause", "EndPause")]
        [string[]]
        $State,

        [ValidateSet("Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support")]
        [ValidateNotNullOrEmpty()]
        [String]
        $Type,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "TimeStamp"


        # check parameter set
        switch ($parameterSetName) {
            "Default" { $EmployeeId = $Token.EmployeeId }

            "ByName" {
                Write-PSFMessage -Level System -Message "Convert EmployeeId from EmployeeName" -Tag "TimeStamp", "EmployeeName"
                $EmployeeId = @()

                foreach ($name in $EmployeeName) {
                    Write-PSFMessage -Level System -Message "Working on employee name '$($name)'" -Tag "TimeStamp", "EmployeeName"
                    [int]$id = $null

                    try {
                        $id = [int]$name
                        $nameIsNumber = $true
                    } catch {
                        $nameIsNumber = $false
                    }
                    if (-not $nameIsNumber) {
                        $id = ConvertFrom-NameCache -Name $name -Type "Employees"
                    }

                    if (-not $id) {
                        Write-PSFMessage -Level Warning -Message "No Id for employee '$($name)' found" -Tag "TimeStamp", "EmployeeName", "Warning"
                    } else {
                        Write-PSFMessage -Level System -Message "Found id '$($id)' for employee '$($name)'" -Tag "TimeStamp", "EmployeeName"
                        $EmployeeId += $id
                    }
                }
            }

            "ById" {
                # Nothing to do, Id is already in place
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }


        # Ensure there are employeeIds in the array, if not, api will always output timestamps for currently logged in user. This might produce redundant and faulty results
        if ($EmployeeId) {
            # Build api path parameters
            $apiParameters = @{
                "employeeIds" = $([string]::Join(",", $EmployeeId))
            }
            if ($Start) {
                $from = [int32][double]::Parse((Get-Date -Date $Start.ToUniversalTime() -UFormat %s))
                $apiParameters.Add("from", $from)
            } else {
                $from = [int32][double]::Parse((Get-Date -Date (Get-Date).Date.ToUniversalTime() -UFormat %s))
                $apiParameters.Add("from", $from)
            }
            if ($End) {
                $till = [int32][double]::Parse((Get-Date -Date $End.ToUniversalTime() -UFormat %s))
                $apiParameters.Add("till", $till)
            } else {
                $till = [int32][double]::Parse((Get-Date -Date (Get-Date).Date.AddDays(1).ToUniversalTime() -UFormat %s))
                $apiParameters.Add("till", $till)
            }

            # Compile api path
            $apiPath = Format-ApiPath -Path "api/v1/timestamps/info" -QueryParameter $apiParameters


            # Get data from service
            $response = Invoke-TANSSRequest -Type GET -ApiPath $apiPath
            Write-PSFMessage -Level Verbose -Message "$($response.meta.text) - $($response.content.timestamps.count) timestamp$(if($response.content.timestamps.count -ne 1){"s"}) received" -Tag "TimeStamp", "TimeStampRequestResult"


            # Output response
            foreach ($item in $response.content.timestamps) {
                Write-PSFMessage -Level System -Message "Create TANSS.TimeStamp object id '$($item.id)' ($( Get-Date -Date ( [datetime]::new(1970, 1, 1, 0, 0, 0, 0, [DateTimeKind]::Utc).AddSeconds($item.date).ToLocalTime()) -Format 'yyyy-MM-dd' ), $($item.type), $($item.state) )" -Tag "TimeStamp", "TimeStampRequestResult"

                # Create object
                $output = [TANSS.TimeStamp]@{
                    BaseObject = $item
                    Id         = $item.id
                }

                # filter output
                Write-PSFMessage -Level System -Message "Client side filtering for TANSS.TimeStamp object id '$($item.id)'" -Tag "TimeStamp", "TimeStampRequestResult"
                if ($State) {
                    $output = $output | Where-Object State -in $State
                }

                if ($Type) {
                    $output = $output | Where-Object State -in $Type
                }

                if($output) {
                    # Output the result
                    Write-PSFMessage -Level System -Message "Ouput TANSS.TimeStamp object id '$($item.id)'" -Tag "TimeStamp", "TimeStampRequestResult"
                    $output
                } else {
                    Write-PSFMessage -Level System -Message "TANSS.TimeStamp object id '$($item.id)' is not going to output, because of client side filtering" -Tag "TimeStamp", "TimeStampRequestResult"
                }
            }
        }
    }

    end {}
}


function New-TANSSTimeStamp {
    <#
    .Synopsis
        New-TANSSTimeStamp
 
    .DESCRIPTION
        Add a new timestamp into the service
 
    .PARAMETER State
        The state to stamp. Has to be one of the value:
        "Coming", "Leaving", "StartPause", "EndPause"
        (Tabcompletaion available)
 
    .PARAMETER Type
        The type of record for you stamp a state.
        Available types:
        "Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support"
        Default type is: "Work"
 
    .PARAMETER Date
        The date of the timestamp
 
    .PARAMETER EmployeeId
        ID of the employee to timestamp for.
        If nothing is specified the currently logged in employee will be used
 
    .PARAMETER EmployeeName
        The name of the employee to timestamp for.
        Tabcompletion available for all known employees
        If nothing is specified the currently logged in employee will be used
 
    .PARAMETER AutoPause
        Tells the api to set autoPause to true
 
    .PARAMETER ServiceToken
        A timestamp api service token generated within TANSS.
        ServiceToken hast to be specified as a TANSS.Connection.
 
        A ServiceToken is required, if timestamps for other employees than the logged in one, are used
        to be written into the service.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSTimeStamp
 
        Description
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.TimeStamp])]
    Param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ValidateSet("Coming", "Leaving", "StartPause", "EndPause")]
        [string]
        $State,

        [Parameter(Position = 1)]
        [ValidateSet("Work", "Inhouse", "Errand", "Vacation", "Illness", "PaidAbsence", "UnpaidAbsence", "Overtime", "Support")]
        [ValidateNotNullOrEmpty()]
        [String]
        $Type = "Work",

        [Parameter(Position = 2)]
        [datetime]
        $Date,

        [bool]
        $AutoPause,

        [Parameter(
            ParameterSetName = "ById",
            ValueFromPipeline = $true
        )]
        [int[]]
        $EmployeeId,

        [Parameter(
            ParameterSetName = "ByName",
            ValueFromPipeline = $true
        )]
        [string[]]
        $EmployeeName,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = "ByName",
            Mandatory = $true
        )]
        [TANSS.Connection]
        $ServiceToken,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            "Default" {
                Write-PSFMessage -Level System -Message "Using EmployeeId '$($Token.EmployeeId)' of '$($Token.UserName)' from given Token" -Tag "TimeStamp", "ParameterSetting"
                $EmployeeId = $Token.EmployeeId
            }

            "ByName" {
                Write-PSFMessage -Level System -Message "Convert EmployeeId from EmployeeName" -Tag "TimeStamp", "ParameterSetting"
                $EmployeeId = @()

                foreach ($name in $EmployeeName) {
                    Write-PSFMessage -Level System -Message "Working on employee name '$($name)'" -Tag "TimeStamp", "ParameterSetting"

                    $id = ConvertFrom-NameCache -Name $name -Type "Employees"
                    if (-not $id) {
                        Write-PSFMessage -Level Warning -Message "No Id for employee '$($name)' found" -Tag "TimeStamp", "ParameterSetting", "Warning"
                    } else {
                        Write-PSFMessage -Level System -Message "Found id '$($id)' for employee '$($name)'" -Tag "TimeStamp", "ParameterSetting"
                        $EmployeeId += $id
                    }
                }
            }

            "ById" {
                Write-PSFMessage -Level System -Message "EmployeeId '$($Token.EmployeeId)' already given to function" -Tag "TimeStamp", "ParameterSetting"
                # Nothing to do
            }

            Default {
                Stop-PSFFunction -Message "Unhandeled ParameterSetName. Developers mistake." -EnableException $true -Cmdlet $pscmdlet
            }
        }

        $apiStateText = ConvertFrom-TANSSTimeStampParameter -Text $State -TextType State
        $apiTypeText = ConvertFrom-TANSSTimeStampParameter -Text $Type -TextType Type

        # Compile api path
        $paramFormatApiPath = @{}
        if ($AutoPause) { $paramFormatApiPath.Add("autoPause", 'true') }
        if ($parameterSetName -like "Default") {
            # Use the "personal" api path
            $apiPath = Format-ApiPath -Path "/api/v1/timestamps" -QueryParameter $paramFormatApiPath
        } else {
            # Use the api path for api keys -> this one can write timestamps for other employees then the logged in one
            $apiPath = Format-ApiPath -Path "/api/timestamps/v1" -QueryParameter $paramFormatApiPath
        }

        # Work through employees
        foreach ($id in $EmployeeId) {
            $name = ConvertFrom-NameCache -Id $id -Type "Employees"
            Write-PSFMessage -Level Verbose -Message "Working on employee '$($name)' (Id $($id))" -Tag "TimeStamp", "Stamping"

            # Compile body object
            $body = @{
                "employeeId" = $id
                "state"      = $apiStateText
                "type"       = $apiTypeText
            }
            if ($Date) { $body.date = [int32][double]::Parse((Get-Date -Date $Date.ToUniversalTime() -UFormat %s)) }

            # Check WhatIf or process request
            if ($pscmdlet.ShouldProcess("Timestamp for employee '$($name)' (ID: $($id)) with state '$($State)'", "New")) {
                Write-PSFMessage -Level Verbose -Message "New timestamp for employee '$($name)' (ID: $($id)) with state '$($State)'" -Tag "TimeStamp", "Stamping"

                # Push data into service
                $paramInvokeTANSSRequest = @{
                    "Type"    = "POST"
                    "ApiPath" = $apiPath
                    "Body"    = $body
                }
                # Choose token for "personal writing" or "delegated writing for other employees"
                if ($parameterSetName -like "Default") {
                    # Use standard user specific token to write timestamps for logged in user only
                    Write-PSFMessage -Level System -Message "Using Employee token from connection for API call" -Tag "TimeStamp", "ParameterSetting"
                    $paramInvokeTANSSRequest.Add("Token", $Token)
                } else {
                    # Use serviceToken to allow writing for other employees then the logged in one
                    Write-PSFMessage -Level System -Message "Using token from serviceToken parameter for API call" -Tag "TimeStamp", "ParameterSetting"
                    $paramInvokeTANSSRequest.Add("Token", $ServiceToken)
                }

                $response = Invoke-TANSSRequest @paramInvokeTANSSRequest
                Write-PSFMessage -Level Verbose -Message "$($response.meta.text) - Timestamp Id '$($response.content.id)' with status '$($response.content.state)'" -Tag "TimeStamp", "Stamping", "TimeStampRequestResult"

                if ($response) {
                    [TANSS.TimeStamp]@{
                        BaseObject = $response.content
                        Id         = $response.content.id
                    }
                }
            }
        }
    }

    end {}
}


function Remove-TANSSTimeStamp {
    <#
    .Synopsis
        Remove-TANSSTimeStamp
 
    .DESCRIPTION
        Remove a timestamp from TANSS
 
    .PARAMETER InputObject
        TANSS TimeStamp object to remove
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSTimeStamp | Remove-TANSSTimeStamp
 
        Remove timestamp for currently logged in employee for today
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    Param(
        [Parameter(
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [Alias("Timestamp")]
        [TANSS.TimeStamp[]]
        $InputObject,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
        $timeStamps = [System.Collections.ArrayList]@()
    }

    process {
        foreach ($timeStamp in $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Collecting TimeStamp '$(Get-Date -Date ($timeStamp.Date) -Format 'yyyy-MM-dd HH:mm')' from employee '$($timeStamp.EmployeeName)' (Id $($timeStamp.Id))"
            $null = $timeStamps.Add($timeStamp)
        }
    }

    end {
        [array]$timeStampsOfEmployees = $timeStamps | Group-Object EmployeeId
        Write-PSFMessage -Level System -Message "Collected $(([array]$timeStamps).count) timestamp(s) for $(([array]$timeStampsOfEmployees).count) employee" -Tag "TimeStamp"

        foreach ($timeStampsOfEmployeeGroup in $timeStampsOfEmployees) {
            Write-PSFMessage -Level Verbose -Message "Working on timestamps for employee '$($timeStampsOfEmployeeGroup.group[0].EmployeeName)' (Id: $($timeStampsOfEmployeeGroup.Name))" -Tag "TimeStamp"
            if ($timeStampsOfEmployeeGroup.Name -ne $Token.EmployeeId) {
                Write-PSFMessage -Level Warning -Message "Unable to remove timestamps for employee '$($timeStampsOfEmployeeGroup.Name)'. TANSS API does not support removing timestamps of other employees than the logged in employee (Id: $($Token.EmployeeId), Name: $($Token.UserName))" -Tag "TimeStamp", "ApiLimitation" -PSCmdlet $pscmdlet
                continue
            }

            [array]$timeStampsOfDays = $timeStampsOfEmployeeGroup.Group | Group-Object { ($_.Date).Date }
            Write-PSFMessage -Level System -Message "Got $(([array]($timeStampsOfEmployeeGroup.Group)).count) timestamp(s) for $(([array]$timeStampsOfDays).count) day(s)" -Tag "TimeStamp"

            foreach ($timeStampsOfDayGroup in $timeStampsOfDays) {
                [array]$timeStampsToDelete = $timeStampsOfDayGroup.Group

                $employeeId = $timeStampsToDelete[0].EmployeeId
                $employeeName = $timeStampsToDelete[0].EmployeeName
                $dateString = Get-Date -Date $timeStampsToDelete[0].Date -Format "yyyy-MM-dd"
                Write-PSFMessage -Level Verbose -Message "Working on $(([array]$timeStampsToDelete).count) timestamp(s) from $($dateString) for employee '$($employeeName)'" -Tag "TimeStamp"

                Write-PSFMessage -Level System -Message "Going to query statistics of day '$($dateString)' for employee '$($employeeName)' (Id: $($employeeId))" -Tag "TimeStamp"
                $QueryParameter = @{
                    "employeeIds" = $employeeId
                    "from"        = ( [int32][double]::Parse((Get-Date -Date $timeStampsToDelete[0].Date.Date.ToUniversalTime() -UFormat %s)) )
                    "till"        = ( [int32][double]::Parse((Get-Date -Date $timeStampsToDelete[0].Date.Date.AddDays(1).ToUniversalTime() -UFormat %s)) )
                }
                $apiPath = Format-ApiPath -Path "api/v1/timestamps/statistics" -QueryParameter $QueryParameter
                $paramInvokeTANSSRequest = @{
                    "Type"    = "GET"
                    "ApiPath" = $apiPath
                    "Token"   = $Token
                    "WhatIf"  = $false
                    "Verbose" = $false
                }
                $response = Invoke-TANSSRequest @paramInvokeTANSSRequest
                Push-DataToCacheRunspace -MetaData $response.meta -Verbose:$false
                [array]$timeStampsOfDay = $response.content.timestamps
                Write-PSFMessage -Level System -Message "Found $($timeStampsOfDay.count) in '$($dateString)' for employee '$($employeeName)' (Id: $($employeeId))" -Tag "TimeStamp"

                [array]$timeStampsRemaining = $timeStampsOfDay | Where-Object id -NotIn $timeStampsToDelete.id
                Write-PSFMessage -Level System -Message "There will remain $($timeStampsRemaining.count) timestamps for employee '$($employeeName)' (Id: $($employeeId)) on '$($dateString)' after processing the current removal" -Tag "TimeStamp"
                $apiPath = Format-ApiPath -Path "api/v1/timestamps/$($employeeId)/day/$($dateString)" -QueryParameter @{ "autoPause" = $false }
                $body = $timeStampsRemaining | ConvertTo-PSFHashtable
                $paramInvokeTANSSRequest = @{
                    "Type"           = "PUT"
                    "ApiPath"        = $apiPath
                    "Token"          = $Token
                    "BodyForceArray" = $true
                }
                if ($body) { $paramInvokeTANSSRequest.Add("body", $body) }

                if ($pscmdlet.ShouldProcess("$($timeStampsToDelete.count) timestamps for '$($employeeName)' of date '$($dateString)' (EmployeeId: $($employeeId))", "Remove")) {
                    Write-PSFMessage -Level Verbose -Message "Removing $($timeStampsToDelete.count) timestamps for '$($employeeName)' of date '$($dateString)' (EmployeeId: $($employeeId))" -Tag "TimeStamp"
                    $response = Invoke-TANSSRequest @paramInvokeTANSSRequest
                }
            }
        }
    }
}


function Approve-TANSSVacationRequest {
    <#
    .Synopsis
        Approve-TANSSVacationRequest
 
    .DESCRIPTION
        Approve a vacation request within TANSS
 
    .PARAMETER InputObject
        TANSS.Vacation.Request object to approve
 
    .PARAMETER Id
        Id of the vacation request to approve
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Id 10 | Approve-TANSSVacationRequest
 
        Approve the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> Approve-TANSSVacationRequest -Id 10
 
        Approve the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> $vacationRequests | Approve-TANSSVacationRequest -PassThru
 
        Approve all requests in variable '$vacationrequests' and output the (approved) VacationRequests on the console
 
        Assuming, the variable is build on something like:
        PS C:\>$vacationrequests = Get-TANSSVacationRequest -Year 2022 -Month 8
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")]
    [CmdletBinding(
        DefaultParameterSetName = "ById",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Set-TANSSVacationRequestStatus', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd -Status "Approve" @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}


function Deny-TANSSVacationRequest {
    <#
    .Synopsis
        Deny-TANSSVacationRequest
 
    .DESCRIPTION
        Decline a vacation request within TANSS
 
    .PARAMETER InputObject
        TANSS.Vacation.Request object to approve
 
    .PARAMETER Id
        Id of the vacation request to approve
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Id 10 | Deny-TANSSVacationRequest
 
        Decline the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> Deny-TANSSVacationRequest -Id 10
 
        Decline the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> $vacationRequests | Deny-TANSSVacationRequest -PassThru
 
        Decline all requests in variable '$vacationrequests' and output the new (declined) VacationRequests on the console
 
        Assuming, the variable is build on something like:
        PS C:\>$vacationrequests = Get-TANSSVacationRequest -Year 2022 -Month 8
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSShouldProcess", "")]
    [CmdletBinding(
        DefaultParameterSetName = "ById",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer)) {
                $PSBoundParameters['OutBuffer'] = 1
            }
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Set-TANSSVacationRequestStatus', [System.Management.Automation.CommandTypes]::Function)
            $scriptCmd = {& $wrappedCmd -Status "Decline" @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline()
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }

    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }

    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
}


function Get-TANSSVacationAbsenceSubType {
    <#
    .Synopsis
        Get-TANSSVacationAbsenceSubType
 
    .DESCRIPTION
        Retrieve the additional absence types for the vacation type "absence".
        If a absence on a employee is created, one of this types can be specified as more specific information.
 
    .PARAMETER Id
        ID of the type to get
        (client side filtering)
 
    .PARAMETER Name
        Name of the type to get
        (client side filtering)
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationAbsenceSubType
 
        Description
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseOutputTypeCorrectly", "")]
    [CmdletBinding(
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Vacation.AbsenceSubType])]
    Param(
        [string[]]
        $Name,

        [int[]]
        $Id,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
        $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/planningAdditionalTypes"

        [array]$output = @()
    }

    process {
        $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token

        if ($response) {
            Write-PSFMessage -Level Verbose -Message "Found $(($response.content).count) vacation types" -Tag "Vacation", "VacationType"

            foreach ($type in $response.content) {
                # Do filtering on name
                if ($Name) {
                    $filterSuccess = $false
                    foreach ($filterName in $Name) {
                        if ($type.Name -like $filterName) {
                            $filterSuccess = $true
                        }
                    }
                    # if filter does not hit, continue with next technician
                    if ($filterSuccess -eq $false) { continue }
                }

                # Do filtering on id
                if ($Id) {
                    $filterSuccess = $false
                    foreach ($filterId in $Id) {
                        if ([int]($type.id) -eq $filterId) {
                            $filterSuccess = $true
                        }
                    }
                    # if filter does not hit, continue with next technician
                    if ($filterSuccess -eq $false) { continue }
                }

                # Compiling additional TANSS.Vacation.AbsenceSubType
                $output += [TANSS.Vacation.AbsenceSubType]@{
                    BaseObject = $type
                    Id         = $type.id
                }

                # Check VacationType lookup cache
                if ([TANSS.Lookup]::VacationAbsenceSubTypes[$type.id] -notlike $type.name) {
                    if ([TANSS.Lookup]::VacationAbsenceSubTypes[$type.id]) {
                        Write-PSFMessage -Level Debug -Message "Update existing id '$($id)' in [TANSS.Lookup]::VacationAbsenceSubTypes with value '$($type.name)'" -Tag "Cache"
                        [TANSS.Lookup]::VacationAbsenceSubTypes[$type.id] = $type.name
                    } else {
                        Write-PSFMessage -Level Debug -Message "Insert in [TANSS.Lookup]::VacationAbsenceSubTypes: $($type.id) - '$($($type.name))'" -Tag "Cache"
                        ([TANSS.Lookup]::VacationAbsenceSubTypes).Add($type.id, $type.name)
                    }
                }
            }
        } else {
            Write-PSFMessage -Level Warning -Message "No technicians found." -Tag "Technician"
        }
    }

    end {
        # Outputting TANSS.VacationType
        $output
    }
}


function Get-TANSSVacationEntitlement {
    <#
    .Synopsis
        Get-VacationEntitlement
 
    .DESCRIPTION
        Get the available days of vacation within a year
 
        By default the current year is queried
 
    .PARAMETER Year
        The year to query.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-VacationEntitlement -Year 2022
 
        Query entitlement from all employees in year 2022
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Vacation.Entitlement])]
    Param(
        [ValidateNotNullOrEmpty()]
        [int]
        $Year = (Get-Date).Year,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        # Query entitlement for year
        $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/vacationDays/year/$($Year)"
        $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token

        # Output result
        Write-PSFMessage -Level Verbose -Message "$($response.meta.text): Received $($response.meta.properties.extras.count) VacationEntitlement records in year $($Year)" -Tag "VacationEntitlement", "Query"
        foreach ($entitlement in $response.content) {
            $_transferred = if ($entitlement.transferred) { $entitlement.transferred } else { 0 }
            [TANSS.Vacation.Entitlement]@{
                BaseObject     = $entitlement
                EmployeeId     = $entitlement.employeeId
                Year           = $entitlement.year
                NumberOfDays   = $entitlement.numberOfDays
                TransferedDays = $_transferred
            }
        }
    }

    end {}
}


function Get-TANSSVacationRequest {
    <#
    .Synopsis
        Get-TANSSVacationRequest
 
    .DESCRIPTION
        Query vacation requests of any state from TANSS
 
        By default the current year is queried
 
    .PARAMETER Id
        The explicit ID of the vacation request to get from TANSS
 
    .PARAMETER Year
        The year to list vacation requests for
 
    .PARAMETER Month
        The month of the year to list vacation requests
 
    .PARAMETER EmployeeId
        The Id of the employee to list vacation requests from
 
    .PARAMETER EmployeeName
        The name of the employee to list vacation requests from
 
    .PARAMETER DepartmentId
        Department Id filter
 
    .PARAMETER DepartmentName
        Department name filter
 
    .PARAMETER Type
        Name of the request type
        Values can be tabcompleted, so you don't have to type
 
        Available: "Urlaub", "Krankheit", "Abwesenheit", "Bereitschaft", "Überstunden abfeiern", "VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME"
 
    .PARAMETER AbsenceSubTypeId
        For type "Abwesenheit", "ABSENCE" there are subtypes available.
        This one specifies the Id of the subtype to use
 
    .PARAMETER AbsenceSubTypeName
        For type "Abwesenheit", "ABSENCE" there are subtypes available.
        This one specifies the name of the subtype to use
 
        Values can be tabcompleted, so you don't have to type
 
    .PARAMETER ExcludeVacationRequestId
        Exclude filter on specific IDs
 
    .PARAMETER State
        The status for requests to list.
 
        Available values: "NEW", "REQUESTED", "APPROVED", "DECLINED"
 
    .PARAMETER CheckPermission
        Boolean value from the api wether to check permission or not
 
    .PARAMETER AddFrontendValue
        Boolean value to add request object on
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest
 
        Get all requests from the current year
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Year ((Get-Date).Year - 1)
 
        Get all requests from last year
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Type ILLNESS
 
        Get all illness records from the current year
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ListUserFriendly",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Vacation.Request])]
    Param(
        [Parameter(
            ParameterSetName = "Id",
            Mandatory = $true,
            ValueFromPipeline = $true,
            ValueFromPipelineByPropertyName = $true
        )]
        [int[]]
        $Id,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [int]
        $Year = (Get-Date).Year,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [int]
        $Month,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [int[]]
        $EmployeeId,

        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [int[]]
        $EmployeeName,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [int[]]
        $DepartmentId,

        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [int[]]
        $DepartmentName,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [string[]]
        $Type,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [int[]]
        $AbsenceSubTypeId,

        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [string[]]
        $AbsenceSubTypeName,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [int[]]
        $ExcludeVacationRequestId,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [ValidateSet("NEW", "REQUESTED", "APPROVED", "DECLINED")]
        [string[]]
        $State,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [bool]
        $CheckPermission = $true,

        [Parameter( ParameterSetName = "ListApiNativ" )]
        [Parameter( ParameterSetName = "ListUserFriendly" )]
        [bool]
        $AddFrontendValue = $false,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        switch ($parameterSetName) {
            { $_ -like "Id" } {

                foreach ($requesterId in $Id) {
                    Write-PSFMessage -Level Verbose -Message "Query VacationRequestId $($requesterId)" -Tag "VacationRequest"

                    # Query VacationRequest by ID
                    $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($requesterId)"
                    $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token

                    # Output result
                    Write-PSFMessage -Level Verbose -Message "$($response.meta.text): VacationRequestId $($requesterId)" -Tag "VacationRequest"
                    [TANSS.Vacation.Request]@{
                        BaseObject = $response.content
                        Id         = $response.content.id
                    }
                }

            }

            { $_ -like "List*" } {
                $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/list"

                #region parameter validation
                # Parameter EmployeeName
                if ($EmployeeName) {
                    Write-PSFMessage -Level Verbose -Message "Processing lookup on filtering for Employee '$( [string]::Join("'; '", [array]$EmployeeName) )'"

                    $EmployeeId = foreach ($item in $EmployeeName) {
                        $result = ConvertFrom-NameCache -Name $item -Type Employees -Verbose:$false
                        if (-not $result) {
                            Stop-PSFFunction -Message "Employee '$($item)' not found. Unable to query VacationRequests." -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "VacationAbsenceSubTypes", "CacheException"
                        } else {
                            $result
                        }
                    }

                    Write-PSFMessage -Level System -Message "Filtering on EmployeeId '$( [string]::Join("', '", [array]$EmployeeId) )'"
                }


                # Parameter DepartmentName
                if ($DepartmentName) {
                    Write-PSFMessage -Level Verbose -Message "Processing lookup on filtering for Department '$( [string]::Join("'; '", [array]$DepartmentName) )'"

                    $DepartmentId = foreach ($item in $DepartmentName) {
                        $result = ConvertFrom-NameCache -Name $item -Type Departments -Verbose:$false
                        if (-not $result) {
                            Stop-PSFFunction -Message "Department '$($item)' not found. Unable to query VacationRequests." -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "VacationAbsenceSubTypes", "CacheException"
                        } else {
                            $result
                        }
                    }

                    Write-PSFMessage -Level System -Message "Filtering on DepartmentId '$( [string]::Join("', '", [array]$DepartmentId) )'"
                }


                # Parameter Type
                if ($Type) {
                    Write-PSFMessage -Level System -Message "Processing filtering on Type '$( [string]::Join("', '", [array]$Type) )'"

                    $planningType = @()
                    foreach ($absenceType in $Type) {
                        switch ($absenceType) {
                            { $_ -like "Urlaub" } { $planningType += "VACATION" }
                            { $_ -like "Krankheit" } { $planningType += "ILLNESS" }
                            { $_ -like "Abwesenheit*" } { $planningType += "ABSENCE" }
                            { $_ -like "Bereitschaft" } { $planningType += "STAND_BY" }
                            { $_ -like "Überstunden abfeiern" } { $planningType += "OVERTIME" }
                            { $_ -in ("VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME") } { $planningType += $_ }
                            default {
                                Stop-PSFFunction -Message "Unhandled Type '$($absenceType)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "VacationType", "SwitchException"
                            }
                        }
                    }
                    $planningType = $planningType | Sort-Object -Unique
                    Write-PSFMessage -Level Verbose -Message "Filtering on VacationRequestType '$( [string]::Join("', '", [array]$planningType) )'"
                }


                # Parameter AbsenceSubType
                if ($AbsenceSubTypeName) {
                    Write-PSFMessage -Level Verbose -Message "Processing lookup on filtering for AbsenceSubType '$( [string]::Join("', '", [array]$AbsenceSubTypeName) )'"

                    $AbsenceSubTypeId = foreach ($item in $AbsenceSubTypeName) {
                        $result = ConvertFrom-NameCache -Name $item -Type VacationAbsenceSubTypes -Verbose:$false
                        if (-not $result) {
                            Stop-PSFFunction -Message "AbsenceSubType '$($item)' not found. Unable to query VacationRequests." -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "VacationAbsenceSubTypes", "CacheException"
                        } else {
                            $result
                        }
                    }

                    Write-PSFMessage -Level System -Message "Filtering on AbsenceSubTypeId '$( [string]::Join("', '", [array]$AbsenceSubTypeId) )'"
                }
                #endregion parameter validation

                # Prepare body
                $body = @{}
                if ($Year) { $body.Add("year", $Year) }
                if ($Month) { $body.Add("month", $Month) }
                if ($EmployeeId) { $body.Add("employeeIds", [array]($EmployeeId)) }
                if ($DepartmentId) { $body.Add("departmentIds", [array]($DepartmentId)) }
                if ($planningType) { $body.Add("planningTypes", [array]($planningType)) }
                if ($AbsenceSubTypeId) { $body.Add("planningAdditionalIds", [array]($AbsenceSubTypeId)) }
                if ($ExcludeVacationRequestId) { $body.Add("excludeVacationRequestIds", [array]($ExcludeVacationRequestId)) }
                if ($State) { $body.Add("statesOnly", [array]($State)) }
                if ($CheckPermission) { $body.Add("checkPermissions", $CheckPermission) }
                if ($AddFrontendValue) { $body.Add("addFrontendValues", $AddFrontendValue) }
                Write-PSFMessage -Level Debug -Message "Prepared body for API request with parameters: '$( [string]::Join("', '", [array]($body.Keys)) )'" -Data $body -Tag "VacationRequest", "Query"

                $response = Invoke-TANSSRequest -Type "PUT" -ApiPath $apiPath -Body $body -Token $Token
                Write-PSFMessage -Level Verbose -Message "Found $( ([array]($response.content.vacationRequests)).count ) request" -Tag "VacationRequest", "Output"
                Push-DataToCacheRunspace -MetaData $response.meta

                foreach ($vacationRequest in $response.content.vacationRequests) {
                    Write-PSFMessage -Level Debug -Message "Generating '$($vacationRequest.planningType)' request Id:$($vacationRequest.Id) with status '$($vacationRequest.status)" -Tag "VacationRequest", "Output"
                    [TANSS.Vacation.Request]@{
                        BaseObject = $vacationRequest
                        Id         = $vacationRequest.id
                    }
                }
            }

            Default {
                Stop-PSFFunction -Message "Unhandled ParameterSet '$($parameterSetName)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "SwitchException", "ParameterSet"
            }
        }


    }

    end {}
}


function New-TANSSVacationRequest {
    <#
    .Synopsis
        New-TANSSVacationRequest
 
    .DESCRIPTION
        Create a new vacation/absence request within TANSS for a specified employee on a date period
        There are various types of "absence":
            - Vacation
            - Illness
            - Absence
            - Standby
            - Overtime
        The command is called with a paramiter called like the absence type to request
 
        The type "Absence" can have a subset of additional types. They can be specified by name (Tabcompletion is available),
        or by a TANSS.Vacation.AbsenceSubType object. The additional absence types can be queried by the command "Get-TANSSVacationAbsenceSubType"
 
    .PARAMETER Vacation
        Switch parameter to command the creation of a vacation request
 
    .PARAMETER Illness
        Switch parameter to command the creation of a Illness record
 
    .PARAMETER Absence
        Switch parameter to command the creation of a absence record
 
    .PARAMETER AbsenceSubType
        TANSS.Vacation.AbsenceSubType object to specify what kind of absence subtype to create
 
    .PARAMETER AbsenceSubTypeName
        The name of the absence subtype to create
 
    .PARAMETER Standby
        Switch parameter to command the creation of a Standby record
 
    .PARAMETER Overtime
        Switch parameter to command the creation of a Overtime record
 
    .PARAMETER EmployeeId
        The ID of the employee to create to request/record for
 
    .PARAMETER StartDate
        The start date
 
    .PARAMETER EndDate
        The end date
 
    .PARAMETER HalfDayStart
        Boolean to specify if the first day is created as a half day (forenoon not in the absence)
 
    .PARAMETER HalfDayEnd
        Boolean to specify if the last day is created as a half day (afternoon not in the absence)
 
    .PARAMETER Description
        Optional description for the request/record
 
    .PARAMETER Date
        The creation of the request/record
        By default this is the current date.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> New-TANSSVacationRequest -Illness -EmployeeId 10 -Start "01/02/2023" -End "01/03/2023"
 
        Creates an illness record for employee with ID 10 for second of january 2023 to third of january 2023
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    [CmdletBinding(
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Vacation.Request])]
    Param(
        [Parameter(ParameterSetName = "Vacation", Mandatory = $true)]
        [switch]
        $Vacation,

        [Parameter(ParameterSetName = "Illness", Mandatory = $true)]
        [switch]
        $Illness,

        [Parameter(ParameterSetName = "Absence", Mandatory = $true)]
        [Parameter(ParameterSetName = "AbsenceWithAbsenceObject", Mandatory = $true)]
        [Parameter(ParameterSetName = "AbsenceWithAbsenceName", Mandatory = $true)]
        [switch]
        $Absence,

        [Parameter(ParameterSetName = "AbsenceWithAbsenceObject", Mandatory = $true)]
        [TANSS.Vacation.AbsenceSubType]
        $AbsenceSubType,

        [Parameter(ParameterSetName = "AbsenceWithAbsenceName", Mandatory = $true)]
        [string]
        $AbsenceSubTypeName,

        [Parameter(ParameterSetName = "Standby", Mandatory = $true)]
        [switch]
        $Standby,

        [Parameter(ParameterSetName = "Overtime", Mandatory = $true)]
        [switch]
        $Overtime,

        [int[]]
        $EmployeeId,

        [Parameter(Mandatory = $true)]
        [datetime]
        $StartDate,

        [Parameter(Mandatory = $true)]
        [datetime]
        $EndDate,

        [Alias("StartHalfDay")]
        [bool]
        $HalfDayStart = $false,

        [Alias("EndHalfDay")]
        [bool]
        $HalfDayEnd = $false,

        [Alias("Reason", "RequestReason")]
        [string]
        $Description,

        [Alias("RequestDate")]
        [datetime]
        $Date = (Get-Date),

        [TANSS.Connection]
        $Token
    )

    begin {
        # Validation - Basic checks
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "VacationRequest"

        #region Validation
        # Check dates
        if ($StartDate -gt $EndDate) {
            Stop-PSFFunction -Message "Specified dates are not valid! Please check dates, StartDate '$($StartDate)' is greater then EndDate '$($EndDate))'" -EnableException $true -Cmdlet $pscmdlet
        }

        # Fallback to employeeId from token if no requestorId is set
        if (-not $EmployeeId) {
            Write-PSFMessage -Level Verbose -Message "No Employee specified, using current logged in employee '$($Token.UserName)' (Id:$($Token.EmployeeId))" -Tag "VacationRequest", "EmployeeId"
            $EmployeeId = $Token.EmployeeId
        }

        # Find additional AbsenceSubType from specified name
        if ($parameterSetName -like "AbsenceWithAbsenceName") {
            Write-PSFMessage -Level System -Message "Gathering TANSS absence type '$($AbsenceSubTypeName)'" -Tag "VacationRequest", "AbsenceSubType", "Lookup"

            $tmpWhatIfPreference = $WhatIfPreference
            $WhatIfPreference = $fals
            $AbsenceSubType = Get-TANSSVacationAbsenceSubType -Name $AbsenceSubTypeName -Token $Token -ErrorAction Ignore
            $WhatIfPreference = $tmpWhatIfPreference
            Remove-Variable tmpWhatIfPreference -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false

            if ($AbsenceSubType) {
                Write-PSFMessage -Level Verbose -Message "Found AbsenceSubTypeId '$($AbsenceSubType.Id)' for '$($AbsenceSubTypeName)'"
            } else {
                Stop-PSFFunction -Message "Unable to find AbsenceSubType '$($AbsenceSubTypeName)'" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "AbsenceSubType"
            }
        }

        # set planningType
        switch ($parameterSetName) {
            { $_ -like "Vacation" } { $planningType = "VACATION" }
            { $_ -like "Illness" } { $planningType = "ILLNESS" }
            { $_ -like "Absence*" } { $planningType = "ABSENCE" }
            { $_ -like "Standby" } { $planningType = "STAND_BY" }
            { $_ -like "Overtime" } { $planningType = "OVERTIME" }
            Default {
                Stop-PSFFunction -Message "Unhandled ParameterSetName. Unable to set planningType for VacationRequest. Developers mistake!" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "PlanningType"
            }
        }
        #endregion Validation - checking parameters


        foreach ($requesterId in $EmployeeId) {
            $_requestDate = [int][double]::Parse((Get-Date -Date $Date.ToUniversalTime() -UFormat %s))

            # gathering absence object
            $plannedVactionRequest = Request-TANSSVacationRequestObject -EmployeeId $requesterId -Type $planningType -StartDate $StartDate -EndDate $EndDate -Token $Token
            if(-not $plannedVactionRequest) { continue }

            Write-PSFMessage -Level Verbose -Message "Adding RequestDate and optional description to VacationRequest object" -Tag "VacationRequest", "VactionRequestObject"
            $plannedVactionRequest.BaseObject.requestReason = "$($Description)"
            $plannedVactionRequest.BaseObject.requestDate = $_requestDate
            if ($AbsenceSubType) {
                Write-PSFMessage -Level Verbose -Message "Insert additionalAbsenceSubType '$($AbsenceSubType.Name)' to VacationRequest object" -Tag "VacationRequest", "VactionRequestObject", "AbsenceSubType"
                $plannedVactionRequest.BaseObject.planningAdditionalId = $AbsenceSubType.Id
            }

            $days = $plannedVactionRequest.Days
            if($HalfDayStart) { $days[0].Afternoon = $false }
            if($HalfDayEnd) { $days[-1].Forenoon = $false }
            $plannedVactionRequest.Days = $days
            Remove-Variable -Name days -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction Ignore -WarningAction Ignore -InformationAction Ignore

            $body = $plannedVactionRequest.BaseObject | ConvertTo-PSFHashtable
            # enforce types, due to some strange conversation behaviour sometimes
            $body.days = [array]$body.days
            $body.startDate = [int]$body.startDate
            $body.endDate = [int]$body.endDate

            $apiPath = Format-ApiPath -Path "api/v1/vacationRequests"

            $daycount = 0 + ($plannedVactionRequest.days | Measure-Object | Select-Object -ExpandProperty Count)
            if ($pscmdlet.ShouldProcess("VacationRequest for employeeId '$($RequesterId)' with $($daycount) days on planningType '$($planningType)' on dates '$(Get-Date -Date $StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $EndDate -Format 'yyyy-MM-dd')'", "Add")) {
                Write-PSFMessage -Level Verbose -Message "Add VacationRequest for employeeId '$($RequesterId)' with $($daycount) days on planningType '$($planningType)' on dates '$(Get-Date -Date $StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $EndDate -Format 'yyyy-MM-dd')'" -Tag "VacationRequest", "VactionRequestObject"

                # Create the object within TANSS
                $result = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token
                Write-PSFMessage -Level Verbose -Message "$($result.meta.text) - RequestId '$($result.content.id)' with status '$($result.content.status)'" -Tag "VacationRequest", "VactionRequestObject", "VacationRequestResult"

                # output the result
                [TANSS.Vacation.Request]@{
                    BaseObject = $result.content
                    Id         = $result.content.id
                }
            }
        }
    }

    end {
    }
}


function Out-TANSSVacationRequestPdf {
    <#
    .Synopsis
        Out-TANSSVacationRequestPdf
 
    .DESCRIPTION
        Write a PDF for a vacation request from Tanss.
        This is only available for VacataRequest of Type "vacation"
 
    .PARAMETER InputObject
        TANSS.Vacation.Request object to output pdf file for
 
    .PARAMETER Id
        The Id of the vacation request
 
    .PARAMETER Path
        The path to output the pdf to
 
    .PARAMETER PassThru
        Switch parameter. If specified, the file object will be thrown out to the console
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Out-TANSSVacationRequestPdf
 
        Description
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ByID",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [string]
        $Path,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        # Validation - Basic checks
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning


        # Check path
        if ($Path) {
            if($Path.Contains("\")) {
                # Check if pdf filename is specified
                if($Path.split("\")[-1] -like "*.pdf") {
                    # assume explicit specified PDF
                    $fileName = $Path.split("\")[-1]
                    $resolved = Resolve-Path -Path $Path.TrimEnd( $fileName ) -ErrorAction Ignore
                } else {
                    # assume path
                    $resolved = Resolve-Path -Path $Path  -ErrorAction Ignore
                }

                if($resolved) {
                    $_path = $resolved | Select-Object -ExpandProperty Path
                    if($fileName) { $_path = "$($_path)\$($fileName)" }
                } else {
                    Stop-PSFFunction -Message "Path '$($Path)' is not valid" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "OutPdf", "PathException"
                }
            } else {
                if($Path -notlike "*.pdf") {
                    Write-PSFMessage -Level Warning -Message "Unusual behaviour, filename for outputfile does not contain '.pdf'" -Tag "VacationRequest", "OutPdf", "FileName"
                }
                $fileName = $Path

                $_path = "$(Resolve-Path -Path ".\" -ErrorAction Ignore | Select-Object -ExpandProperty Path)\$fileName"
                if(-not $_path) {
                    Stop-PSFFunction -Message "Path '$($Path)' is not valid" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "OutPdf", "PathException"
                }
            }
        } else {
            $_path = "$(Resolve-Path -Path ".\" | Select-Object -ExpandProperty Path)\"
        }
        Write-PSFMessage -Level System -Message "Specified path: $($_path)" -Tag "VacationRequest", "OutPdf"
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "VacationRequest", "OutPdf"

        # If Id is piped in, query vacationRequests from TANSS
        if ($parameterSetName -like "ById*") {
            $InputObject = foreach ($requestId in $Id) {
                Get-TANSSVacationRequest -Id $requestId -Token $Token
            }
        }

        foreach ($vacationRequest in $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Working on '$($vacationRequest.TypeName)' VacationRequest '$($vacationRequest.Id)' ($($vacationRequest.EmployeeName)) for range '$($vacationRequest.StartDate) - $($vacationRequest.EndDate)'" -Tag "VacationRequest", "OutPdf"

            if ($vacationRequest.Type -ne "Vacation") {
                Write-PSFMessage -Level Warning -Message "VacationRequest '$(($vacationRequest.Id))' is not of type 'Vacation'. PDF output only supported for VacationRequest of type 'vacation'" -Tag "VacationRequest", "OutPdf", "Warning"
                continue
            }

            if (-not $fileName) {
                $name = "urlaubsantrag_ID$($vacationRequest.Id)_$(Get-Date -Date $vacationRequest.StartDate -Format "yyyy-MM-dd")_$(Get-Date -Date $vacationRequest.EndDate -Format "yyyy-MM-dd").pdf"
                Write-PSFMessage -Level Verbose -Message "No name specified output file. Using filename: $($name)" -Tag "VacationRequest", "OutPdf", "FileName"
                $_path = Join-Path -Path $_path -ChildPath $name
                Write-PSFMessage -Level System -Message "Output path is: $($_path)" -Tag "VacationRequest", "OutPdf", "Path"
            }

            $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($vacationRequest.Id)/pdf"
            #$apiPath = "backend/api/v1/vacationRequests/$($vacationRequest.Id)/pdf"
            $downloadLink = Invoke-TANSSRequest -Type Get -ApiPath $apiPath -Pdf

            if ($pscmdlet.ShouldProcess("PDF for vacation request '$vacationRequest.Id' from '$($vacationRequest.EmployeeName)' to '$_path'", "Out")) {
                Write-PSFMessage -Level Verbose -Message "Ouput PDF for vacation request '$vacationRequest.Id' from '$($vacationRequest.EmployeeName)' to '$_path'" -Tag "VacationRequest", "OutPdf"
                $apiPath = Format-ApiPath -Path $downloadLink.content.url
                #$apiPath = "backend/$($downloadLink.content.url)"
                $param = @{
                    "Uri"           = "$($Token.Server)/$($apiPath)"
                    "Headers"       = @{
                        "apiToken" = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto([System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Token.AccessToken))
                    }
                    "Method"        = "GET"
                    "ContentType"   = 'application/json; charset=UTF-8'
                    "Verbose"       = $false
                    "Debug"         = $false
                    "ErrorAction"   = "Stop"
                    "ErrorVariable" = "invokeError"
                    "OutFile"       = $_path
                }
                try {
                    Invoke-RestMethod @param
                } catch {
                    Write-PSFMessage -Level Error -Message "Error on rest call: $($invokeError.ErrorRecord.Exception.Message)" -Tag "VacationRequest", "OutPdf", "RestException" -ErrorRecord $invokeError.ErrorRecord -PSCmdlet $pscmdlet
                    continue
                }

                if($PassThru) {
                    Get-Item -Path $_path
                }
            }
        }
    }

    end {
    }
}


function Remove-TANSSVacationRequest {
    <#
    .Synopsis
        Remove-TANSSVacationRequest
 
    .DESCRIPTION
        Remove a vacation request
 
    .PARAMETER InputObject
        TANSS.Vacation.Request to remove
 
    .PARAMETER Id
        The Id of the VacationRequest record to remove
 
    .PARAMETER Force
        Process the removal quietly.
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Id 10 | Remove-TANSSVacationRequest
 
        Remove the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> Remove-TANSSVacationRequest -Id 10 -Force
 
        Remove the VacationRequest Id 10 without asking for confirmation
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ById",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'High'
    )]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [switch]
        $Force,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        # If Id is piped in, query vacationRequests from TANSS
        if ($parameterSetName -like "ById") {
            $InputObject = foreach ($requesterId in $Id) {
                Write-PSFMessage -Level Verbose -Message "Query VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"

                # Query VacationRequest by ID
                $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($requesterId)"
                $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token -Confirm:$false

                # Output result
                Write-PSFMessage -Level Verbose -Message "$($response.meta.text): VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"
                [TANSS.Vacation.Request]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }
            }
        }

        if (-not $InputObject) {
            Write-PSFMessage -Level Error -Message "No VacationRequests to remove" -Tag "VacationRequest", "Set", "NoData" -PSCmdlet $pscmdlet
        } else {
            $processRemoval = $false

            foreach ($vacationRequest in $InputObject) {

                # Check on Force parameter, otherwise process shouldprocess
                if ($Force) {
                    $processRemoval = $true
                } else {
                    if ($pscmdlet.ShouldProcess("VacationRequestId '$($vacationRequest.Id)' from '$($vacationRequest.EmployeeName)' on '$($vacationRequest.StartDate)-$($vacationRequest.EndDate)'", "Remove")) {
                        $processRemoval = $true
                    }
                }

                if ($processRemoval) {
                    Write-PSFMessage -Level Verbose -Message "Remove VacationRequestId '$($vacationRequest.Id)' from '$($vacationRequest.EmployeeName)' on '$($vacationRequest.StartDate)-$($vacationRequest.EndDate)'" -Tag "VacationRequest", "Set", "Remove"

                    # Remove VacationRequest
                    $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($vacationRequest.Id)"
                    $response = Invoke-TANSSRequest -Type DELETE -ApiPath $apiPath -Token $Token -Confirm:$false
                }
            }
        }
    }

    end {
    }
}


function Request-TANSSVacationRequestObject {
    <#
    .Synopsis
        Request-TANSSVacationRequestObject
 
    .DESCRIPTION
        Retrieve a vacation request object from TANSS.
        This object can be used to create a new VacationRequest
 
    .PARAMETER EmployeeId
        The ID of the employee to request for
 
    .PARAMETER EmployeeName
        The name of the employee to request for
 
    .PARAMETER StartDate
        The start date
 
    .PARAMETER EndDate
        The end date
 
    .PARAMETER Type
        Name of the request type
        Values can be tabcompleted, so you don't have to type
 
        Available: "Urlaub", "Krankheit", "Abwesenheit", "Bereitschaft", "Überstunden abfeiern", "VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME"
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .EXAMPLE
        PS C:\> Request-TANSSVacationRequestObject -EmployeeId 10 -Type "Urlaub" -Start "01/02/2023" -End "01/03/2023"
 
        Request a object to create a new vacation request in the database for employee with ID 10. The output will be of type and from 2.1.2023 to 03.01.2023
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ApiNative",
        SupportsShouldProcess = $false,
        PositionalBinding = $true,
        ConfirmImpact = 'Low'
    )]
    [OutputType([TANSS.Vacation.Request])]
    Param(
        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            ParameterSetName = "ApiNative"
        )]
        [int[]]
        $EmployeeId,

        [Parameter(
            ValueFromPipelineByPropertyName = $true,
            ValueFromPipeline = $true,
            ParameterSetName = "UserFriendly",
            Mandatory = $true
        )]
        [string[]]
        $EmployeeName,

        [Parameter(Mandatory = $true)]
        [string]
        $Type,

        [Parameter(Mandatory = $true)]
        [datetime]
        $StartDate,

        [Parameter(Mandatory = $true)]
        [datetime]
        $EndDate,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

        # Parameter Type
        if ($Type) {
            Write-PSFMessage -Level System -Message "Processing Type '$($Type)'" -Tag "VacationRequest", "Request", "VacationType"

            switch ($Type) {
                { $_ -like "Urlaub" } { $planningType = "VACATION" }
                { $_ -like "Krankheit" } { $planningType = "ILLNESS" }
                { $_ -like "Abwesenheit*" } { $planningType = "ABSENCE" }
                { $_ -like "Bereitschaft" } { $planningType = "STAND_BY" }
                { $_ -like "Überstunden abfeiern" } { $planningType = "OVERTIME" }
                { $_ -in ("VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME") } { $planningType = $_ }
                default {
                    Stop-PSFFunction -Message "Unhandled Type '$($Type)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "Request", "VacationType", "SwitchException"
                }
            }
            Write-PSFMessage -Level System -Message "Using VacationRequestType '$($planningType)'" -Tag "VacationRequest", "Request", "VacationType"
        }
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "VacationRequest", "Request"

        if ($EmployeeName) {
            Write-PSFMessage -Level System -Message "Convert EmployeeId from EmployeeName" -Tag "VacationRequest", "Request", "EmployeeName"
            $EmployeeId = @()

            foreach ($name in $EmployeeName) {
                Write-PSFMessage -Level System -Message "Working on employee name '$($name)'" -Tag "VacationRequest", "Request", "EmployeeName"

                $id = ConvertFrom-NameCache -Name $name -Type "Employees"
                if (-not $id) {
                    Write-PSFMessage -Level Warning -Message "No Id for employee '$($name)' found" -Tag "VacationRequest", "Request", "EmployeeName", "Warning"
                } else {
                    Write-PSFMessage -Level System -Message "Found id '$($id)' for employee '$($name)'" -Tag "VacationRequest", "Request", "EmployeeName"
                }
                $EmployeeId += $id
            }
        }

        # Fallback to employeeId from token if no requestorId is set
        if (-not $EmployeeId) {
            Write-PSFMessage -Level Verbose -Message "No Employee specified, using current logged in employee '$($Token.UserName)' (Id:$($Token.EmployeeId))" -Tag "VacationRequest", "Request", "EmployeeId"
            $EmployeeId = $Token.EmployeeId
        }

        foreach ($requesterId in $EmployeeId) {
            Write-PSFMessage -Level System -Message "Request $planningType vacation object for id '$($id)' on dates '$(Get-Date -Date $StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $EndDate -Format 'yyyy-MM-dd')'" -Tag "VacationRequest", "Request"

            # gathering absence object
            $_startDate = [int][double]::Parse((Get-Date -Date $StartDate.Date.ToUniversalTime() -UFormat %s))
            $_endDate = [int][double]::Parse((Get-Date -Date $EndDate.Date.ToUniversalTime() -UFormat %s))

            $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/properties"
            $body = @{
                "requesterId"  = $requesterId
                "planningType" = $planningType
                "startDate"    = $_startDate
                "endDate"      = $_endDate
            }

            $plannedVactionRequest = Invoke-TANSSRequest -Type POST -ApiPath $apiPath -Body $body -Token $Token | Select-Object -ExpandProperty content
            if ($plannedVactionRequest) {
                Write-PSFMessage -Level Verbose -Message "Received VacationRequest object with $($plannedVactionRequest.days | Measure-Object | Select-Object -ExpandProperty Count) days on planningType '$($planningType)'" -Tag "VacationRequest", "VactionRequestObject"

                # output object
                [TANSS.Vacation.Request]@{
                    BaseObject = $plannedVactionRequest
                    Id         = $plannedVactionRequest.id
                }
            } else {
                Stop-PSFFunction -Message "Unable gathering '$($planningType)' VacationRequest object for employeeId '$($requesterId)' on dates '$(Get-Date -Date $StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $EndDate -Format 'yyyy-MM-dd')' from '$($Token.Server)'" -Cmdlet $pscmdlet
            }
        }
    }

    end {}
}


function Set-TANSSVacationEntitlement {
    <#
    .Synopsis
        Set-TANSSVacationEntitlement
 
    .DESCRIPTION
        Modifies yearly entitlements for employees
 
    .PARAMETER InputObject
        TANSS.Vacation.Entitlement object to modify
 
    .PARAMETER EmployeeId
        The id of the employee to modfiy
 
    .PARAMETER EmployeeName
        The name of the employee to modfiy
 
    .PARAMETER Year
        The year to modify
 
    .PARAMETER Days
        Amount of days to set
 
    .PARAMETER TransferedDays
        Amount of days transfered from the last year
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Set-VacationEntitlement -Year 2022 -EmployeeId 2 -Days 30
 
        Set entitlement for employee ID 2 to 30 days in year 2022
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "Default",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Vacation.Entitlement])]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Entitlement[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [int[]]
        $EmployeeId,

        [Parameter(
            ParameterSetName = "ByName",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [string[]]
        $EmployeeName,

        [ValidateNotNullOrEmpty()]
        [int]
        $Year = (Get-Date).Year,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Alias("NumberOfDays")]
        [int]
        $Days,

        [ValidateNotNullOrEmpty()]
        [Alias("DaysTransfered")]
        [int]
        $TransferedDays,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "VacationEntitlement", "Set"

        if ($parameterSetName -notlike "ByInputObject") {
            $tempWhatIf = $WhatIfPreference
            $WhatIfPreference = $false
            $InputObject = Get-TANSSVacationEntitlement -Year $Year -Token $Token
            $WhatIfPreference = $tempWhatIf
        }

        # Filter on EmployeeName
        if ($parameterSetName -like "ByName") {
            $InputObject = foreach ($_employeeName in $EmployeeName) {
                $InputObject | Where-Object EmployeeName -like $_employeeName
            }
            Write-PSFMessage -Level Verbose -Message "Select $(([array]$InputObject).Count) records, by employee name '$([string]::Join("', '", ([array]$EmployeeName)))'" -Tag "VacationEntitlement", "Set", "Filtering"
        }

        # Filter on EmployeeId
        if ($parameterSetName -like "ById") {
            $InputObject = foreach ($_employeeId in $EmployeeId) {
                $InputObject | Where-Object EmployeeId -like $_employeeId
            }
            Write-PSFMessage -Level Verbose -Message "Select $(([array]$InputObject).Count) records, by employee id '$([string]::Join("', '", ([array]$EmployeeId)))'" -Tag "VacationEntitlement", "Set", "Filtering"
        }

        # Process VacationEntitlement modification
        foreach ($entitlement in $InputObject) {
            if ($pscmdlet.ShouldProcess("Vacation entitlement for '$($entitlement.EmployeeName)' to $($Days) days in $($Year)", "Set")) {
                Write-PSFMessage -Level Verbose -Message "Set vacation entitlement for '$($entitlement.EmployeeName)' to $($Days) days in $($Year)" -Tag "VacationEntitlement", "Set"

                # Prepare request
                $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/vacationDays"
                if ($TransferedDays) { $_transferred = $TransferedDays } else { $_transferred = 0 }
                $body = @{
                    "employeeId"   = $entitlement.employeeId
                    "year"         = $entitlement.year
                    "numberOfDays" = $Days
                    "transferred"  = $_transferred
                }

                # Set entitlement
                $response = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token

                # Output result
                if ($PassThru) {
                    Write-PSFMessage -Level Verbose -Message "$($response.meta.text): Going to output $(([array]($response.content)).count) VacationEntitlement records in year $($Year)" -Tag "VacationEntitlement", "Query"
                    foreach ($newEntitlement in $response.content) {
                        $_baseObject = $entitlement.BaseObject
                        $_baseObject.numberOfDays = $newEntitlement.numberOfDays
                        if ($newEntitlement.transferred) {
                            $_transferred = $newEntitlement.transferred
                            if($_baseObject.TransferedDays) { $_baseObject.TransferedDays = $_transferred }
                        } else {
                            $_transferred = 0
                        }

                        [TANSS.Vacation.Entitlement]@{
                            BaseObject     = $_baseObject
                            EmployeeId     = $newEntitlement.employeeId
                            Year           = $newEntitlement.year
                            NumberOfDays   = $newEntitlement.numberOfDays
                            TransferedDays = $_transferred
                        }
                    }
                }
            }
        }
    }

    end {}
}


function Set-TANSSVacationRequest {
    <#
    .Synopsis
        Set-TANSSVacationRequest
 
    .DESCRIPTION
        Modfiy a vacation/absence record within TANSS
 
    .PARAMETER InputObject
        TANSS.Vacation.Request object passed in to modify
 
    .PARAMETER Id
        The Id of the VacationRequest within TANSS service
 
    .PARAMETER Type
        Name of the request type
        Values can be tabcompleted, so you don't have to type
 
        Available: "Urlaub", "Krankheit", "Abwesenheit", "Bereitschaft", "Überstunden abfeiern", "VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME"
 
    .PARAMETER AbsenceSubType
        TANSS.Vacation.AbsenceSubType object to set on the absense record
 
    .PARAMETER AbsenceSubTypeName
        For type "Abwesenheit", "ABSENCE" there are subtypes available
        This one specifies the name of the subtype to set on the absense record
 
        Values can be tabcompleted, so you don't have to type
 
    .PARAMETER StartDate
        Starting date for the record to modify
 
    .PARAMETER EndDate
        End date for the record to modify
 
    .PARAMETER Description
        Description to set on the record
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Set-TANSSVacationRequest -Id 123 -Description "new description"
 
        Change the description of the VacationRequest 123 to "new description"
 
    .EXAMPLE
        PS C:\> $vacationRequest | Set-TANSSVacationRequest -EndDate "01/31/2023"
 
        Set enddate to "01/31/2023" the request in variable $vacationRequest
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Vacation.Request])]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObjectWithSubType",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Parameter(
            ParameterSetName = "ByInputObjectWithSubTypeName",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ByIdWithSubType",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Parameter(
            ParameterSetName = "ByIdWithSubTypeName",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [ValidateNotNullOrEmpty()]
        [string]
        $Type,

        [Parameter(ParameterSetName = "ByInputObjectWithSubType")]
        [Parameter(ParameterSetName = "ByIdWithSubType")]
        [ValidateNotNullOrEmpty()]
        [TANSS.Vacation.AbsenceSubType]
        $AbsenceSubType,

        [Parameter(
            ParameterSetName = "ByInputObjectWithSubTypeName",
            Mandatory = $true
        )]
        [Parameter(
            ParameterSetName = "ByIdWithSubTypeName",
            Mandatory = $true
        )]
        [ValidateNotNullOrEmpty()]
        [string]
        $AbsenceSubTypeName,

        [ValidateNotNullOrEmpty()]
        [datetime]
        $StartDate,

        [ValidateNotNullOrEmpty()]
        [datetime]
        $EndDate,

        [Alias("Reason", "RequestReason")]
        [string]
        $Description,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        # Validation - Basic checks
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning

        # Parameter Type
        if ($Type) {
            Write-PSFMessage -Level System -Message "Processing Type '$($Type)'"

            switch ($Type) {
                { $_ -like "Urlaub" } { $planningType = "VACATION" }
                { $_ -like "Krankheit" } { $planningType = "ILLNESS" }
                { $_ -like "Abwesenheit*" } { $planningType = "ABSENCE" }
                { $_ -like "Bereitschaft" } { $planningType = "STAND_BY" }
                { $_ -like "Überstunden abfeiern" } { $planningType = "OVERTIME" }
                { $_ -in ("VACATION", "ILLNESS", "ABSENCE", "STAND_BY", "OVERTIME") } { $planningType = $_ }
                default {
                    Stop-PSFFunction -Message "Unhandled Type '$($Type)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "VacationType", "SwitchException"
                }
            }
            Write-PSFMessage -Level System -Message "Using VacationRequestType '$($planningType)'"
        }
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)" -Tag "VacationRequest"

        #region Validation
        # Check dates
        if (($StartDate -and $EndDate) -and ($StartDate -gt $EndDate)) {
            Stop-PSFFunction -Message "Specified dates are not valid! Please check dates, StartDate '$($StartDate)' is greater then EndDate '$($EndDate))'" -EnableException $true -Cmdlet $pscmdlet
        }

        # Find additional AbsenceSubType from specified name
        if ($parameterSetName -like "*WithSubTypeName") {
            Write-PSFMessage -Level System -Message "Gathering TANSS absence type '$($AbsenceSubTypeName)'" -Tag "VacationRequest", "AbsenceSubType", "Lookup"

            $tmpWhatIfPreference = $WhatIfPreference
            $WhatIfPreference = $fals
            $AbsenceSubType = Get-TANSSVacationAbsenceSubType -Name $AbsenceSubTypeName -Token $Token -ErrorAction Ignore
            $WhatIfPreference = $tmpWhatIfPreference
            Remove-Variable tmpWhatIfPreference -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false

            if ($AbsenceSubType) {
                Write-PSFMessage -Level Verbose -Message "Found AbsenceSubTypeId '$($AbsenceSubType.Id)' for '$($AbsenceSubTypeName)'"
            } else {
                Stop-PSFFunction -Message "Unable to find AbsenceSubType '$($AbsenceSubTypeName)'" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "AbsenceSubType"
            }
        }

        #endregion Validation - checking parameters


        # If Id is piped in, query vacationRequests from TANSS
        if ($parameterSetName -like "ById*") {
            $InputObject = foreach ($requesterId in $Id) {
                Write-PSFMessage -Level Verbose -Message "Query VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"

                # Query VacationRequest by ID
                $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($requesterId)"
                $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token -Confirm:$false

                # Output result
                Write-PSFMessage -Level Verbose -Message "$($response.meta.text): VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"
                [TANSS.Vacation.Request]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }
            }
        }



        foreach ($vacationRequest in $InputObject) {
            Write-PSFMessage -Level Verbose -Message "Working on '$($vacationRequest.TypeName)' VacationRequest '$($vacationRequest.Id)' ($($vacationRequest.EmployeeName)) for range '$($vacationRequest.StartDate) - $($vacationRequest.EndDate)'" -Tag "VacationRequest", "Set"

            # earlier Startdate should be set --> VacationDay objects have to be added to days property
            if ($StartDate -and ($StartDate -lt $vacationRequest.StartDate)) {
                Write-PSFMessage -Level System -Message "StartDate found, need to add $(($vacationRequest.EndDate - $StartDate).Days) days to VacationRequest" -Tag "VacationRequest", "Set", "AddDays"

                $plannedVactionRequest = Request-TANSSVacationRequestObject -EmployeeId $vacationRequest.BaseObject.requesterId -Type $vacationRequest.BaseObject.planningType -StartDate $StartDate -EndDate $vacationRequest.EndDate -Token $Token
                if(-not $plannedVactionRequest) { continue }

                # add days new days to vacation request
                $vacationRequest.Days = $plannedVactionRequest.Days
                Write-PSFMessage -Level System -Message "VacationRequest '$($vacationRequest.Id)' is modified with ne Startdate, new range '$($vacationRequest.StartDate) - $($vacationRequest.EndDate)'" -Tag "VacationRequest", "Set", "AddDays"

                Remove-Variable -Name apiPath, _startDate, body, plannedVactionRequest -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction:Ignore -WarningAction:Ignore -InformationAction:Ignore
            }

            # Later Enddate should be set --> VacationDay objects have to be added to days property
            if ($EndDate -and ($EndDate -gt $vacationRequest.EndDate)) {
                Write-PSFMessage -Level System -Message "EndDate found, need to add $(($EndDate - $vacationRequest.EndDate).Days) days to VacationRequest" -Tag "VacationRequest", "Set", "AddDays"

                # Query addiontial days
                $plannedVactionRequest = Request-TANSSVacationRequestObject -EmployeeId $vacationRequest.BaseObject.requesterId -Type $vacationRequest.BaseObject.planningType -StartDate $vacationRequest.StartDate -EndDate $EndDate -Token $Token
                if(-not $plannedVactionRequest) { continue }

                # add days new days to vacation request
                $vacationRequest.Days = $plannedVactionRequest.Days
                Write-PSFMessage -Level System -Message "VacationRequest '$($vacationRequest.Id)' is modified with ne Startdate, new range '$($vacationRequest.StartDate) - $($vacationRequest.EndDate)'" -Tag "VacationRequest", "Set", "AddDays"

                Remove-Variable -Name apiPath, _endDate, body, plannedVactionRequest -Force -WhatIf:$false -Confirm:$false -Verbose:$false -Debug:$false -ErrorAction:Ignore -WarningAction:Ignore -InformationAction:Ignore
            }

            # Later StartDate should be set --> have to be remove days from VacationRequest objects
            if ($StartDate -and ($StartDate -ne $vacationRequest.StartDate)) {
                Write-PSFMessage -Level System -Message "StartDate found, need to remove $(($StartDate - $vacationRequest.StartDate).Days) day(s) from VacationRequest '$($vacationRequest.Id)'" -Tag "VacationRequest", "Set", "RemoveDays"

                $_startDate = [int][double]::Parse((Get-Date -Date $StartDate.Date.ToUniversalTime() -UFormat %s))
                $vacationRequest.BaseObject.startDate = $_startDate
                $vacationRequest.Days = $vacationRequest.Days | Where-Object date -ge $StartDate
            }

            # Earlier EndDate should be set --> have to be remove days from VacationRequest objects
            if ($EndDate -and ($EndDate -ne $vacationRequest.EndDate)) {
                Write-PSFMessage -Level System -Message "EndDate found, need to remove $(($EndDate - $vacationRequest.EndDate).Days * -1) day(s) from VacationRequest '$($vacationRequest.Id)'" -Tag "VacationRequest", "Set", "RemoveDays"

                $_endDate = [int][double]::Parse((Get-Date -Date $EndDate.Date.ToUniversalTime() -UFormat %s))
                $vacationRequest.BaseObject.endDate = $_endDate
                $vacationRequest.Days = $vacationRequest.Days | Where-Object date -le $EndDate
            }

            # Set Description
            if ($Description) {
                Write-PSFMessage -Level System -Message "Description found, going to change description from '$($vacationRequest.Description)' to '$($Description)'" -Tag "VacationRequest", "Set", "RequestDate"
                $vacationRequest.BaseObject.requestReason = "$($Description)"
            }

            # Set type
            if ($planningType) {
                Write-PSFMessage -Level System -Message "Type found, going to change Type from '$($vacationRequest.Type)' to '$($planningType)'" -Tag "VacationRequest", "Set", "Type"
                $vacationRequest.BaseObject.planningType = $planningType

                # remove additional planning type when change from absence so something else
                if (($vacationRequest.BaseObject.planningType -notlike "ABSENCE") -and ($vacationRequest.BaseObject.planningAdditionalId -gt 0)) {
                    $vacationRequest.BaseObject.planningAdditionalId = 0
                }
            }

            # Set AbsenceSubType
            if ($AbsenceSubType -or $AbsenceSubTypeName) {
                if (($planningType -like "ABSENCE") -and $AbsenceSubTypeName) {
                    $AbsenceSubType = Get-TANSSVacationAbsenceSubType -Name $AbsenceSubTypeName

                    Write-PSFMessage -Level Verbose -Message "Set additionalAbsenceSubType '$($AbsenceSubType.Name)' to VacationRequest object" -Tag "VacationRequest", "Set", "AbsenceSubType"
                    $vacationRequest.BaseObject.planningAdditionalId = $AbsenceSubType.Id
                } elseif (($planningType -notlike "ABSENCE") -and ($AbsenceSubType -or $AbsenceSubTypeName)) {
                    Write-PSFMessage -Level Important -Message "AbsenceSubType specified, but not valid '$(if($planningType){"for '$($planningType)'"} else { "without Type"})'! AbsenceSubTypes are only possible with Type 'Abwesenheit'/'ABSENCE'. Setting will be ignored" -Tag "VacationRequest", "AbsenceSubType"
                }
            }

            # Make the change effective within TANSS
            $body = $vacationRequest.BaseObject | ConvertTo-PSFHashtable
            $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($vacationRequest.Id)"

            if ($pscmdlet.ShouldProcess("VacationRequest for employeeId '$($vacationRequest.EmployeeId)' with $($vacationRequest.days | Measure-Object | Select-Object -ExpandProperty Count) days on planningType '$($vacationRequest.Type)' on dates '$(Get-Date -Date $vacationRequest.StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $vacationRequest.EndDate -Format 'yyyy-MM-dd')'", "Set")) {
                Write-PSFMessage -Level Verbose -Message "Set VacationRequest for employeeId '$($vacationRequest.EmployeeId)' with $($vacationRequest.days | Measure-Object | Select-Object -ExpandProperty Count) days on planningType '$($vacationRequest.Type)' on dates '$(Get-Date -Date $vacationRequest.StartDate -Format 'yyyy-MM-dd')'-'$(Get-Date -Date $vacationRequest.EndDate -Format 'yyyy-MM-dd')'" -Tag "VacationRequest", "Set"

                # Create the object within TANSS
                $result = Invoke-TANSSRequest -Type PUT -ApiPath $apiPath -Body $body -Token $Token
                Write-PSFMessage -Level Verbose -Message "$($result.meta.text) - RequestId '$($result.content.id)' with status '$($result.content.status)'" -Tag "VacationRequest", "VactionRequestObject", "VacationRequestResult"

                # output the result
                if ($result -and $PassThru) {
                    [TANSS.Vacation.Request]@{
                        BaseObject = $result.content
                        Id         = $result.content.id
                    }
                }
            }
        }
    }

    end {
    }
}


function Set-TANSSVacationRequestStatus {
    <#
    .Synopsis
        Set-TANSSVacationRequestStatus
 
    .DESCRIPTION
        Approve or decline a vacation request within TANSS
 
    .PARAMETER InputObject
        TANSS.Vacation.Request object to modify
 
    .PARAMETER Id
        The id of the vacation request to modify
 
    .PARAMETER Status
        Status to set for the request
 
        Available values are: "Approve", "Decline"
        Values can be tabcompleted, so you don't have to type
 
    .PARAMETER Token
        The TANSS.Connection token to access api
 
        If not specified, the registered default token from within the module is going to be used
 
    .PARAMETER PassThru
        Outputs the result to the console
 
    .PARAMETER WhatIf
        If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .PARAMETER Confirm
        If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .EXAMPLE
        PS C:\> Get-TANSSVacationRequest -Id 10 | Set-TANSSVacationRequestStatus -Status "Approve"
 
        Approve the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> Set-TANSSVacationRequestStatus -Id 10 -Status "Decline"
 
        Decline the VacationRequest Id 10
 
    .EXAMPLE
        PS C:\> $vacationRequests | Set-TANSSVacationRequestStatus -Status "Approve" -PassThru
 
        Approve all requests in variable '$vacationrequests' and output the (approved) VacationRequests on the console
 
        Assuming, the variable is build on something like:
        PS C:\>$vacationrequests = Get-TANSSVacationRequest -Year 2022 -Month 8
 
    .NOTES
        Author: Andreas Bellstedt
 
    .LINK
        https://github.com/AndiBellstedt/PSTANSS
    #>

    [CmdletBinding(
        DefaultParameterSetName = "ById",
        SupportsShouldProcess = $true,
        PositionalBinding = $true,
        ConfirmImpact = 'Medium'
    )]
    [OutputType([TANSS.Vacation.Request])]
    Param(
        [Parameter(
            ParameterSetName = "ByInputObject",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [TANSS.Vacation.Request[]]
        $InputObject,

        [Parameter(
            ParameterSetName = "ById",
            Mandatory = $true,
            ValueFromPipeline = $true
        )]
        [Alias("RequestId", "VacationRequestId")]
        [int[]]
        $Id,

        [Parameter(Mandatory = $true)]
        [ValidateSet("Approve", "Decline")]
        [string]
        $Status,

        [switch]
        $PassThru,

        [TANSS.Connection]
        $Token
    )

    begin {
        if (-not $Token) { $Token = Get-TANSSRegisteredAccessToken }
        Assert-CacheRunspaceRunning
    }

    process {
        $parameterSetName = $pscmdlet.ParameterSetName
        Write-PSFMessage -Level Debug -Message "ParameterNameSet: $($parameterSetName)"

        # If Id is piped in, query vacationRequests from TANSS
        if ($parameterSetName -like "ById") {
            $InputObject = foreach ($requesterId in $Id) {
                Write-PSFMessage -Level Verbose -Message "Query VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"

                # Query VacationRequest by ID
                $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($requesterId)"
                $response = Invoke-TANSSRequest -Type "GET" -ApiPath $apiPath -Token $Token

                # Output result
                Write-PSFMessage -Level Verbose -Message "$($response.meta.text): VacationRequestId $($requesterId)" -Tag "VacationRequest", "Query"
                [TANSS.Vacation.Request]@{
                    BaseObject = $response.content
                    Id         = $response.content.id
                }
            }
        }

        if (-not $InputObject) {
            Write-PSFMessage -Level Significant -Message "No VacationRequests found to set status on" -Tag "VacationRequest", "Set", "NoData"
        } else {
            switch ($Status) {
                "Approve" {
                    $Status = "Approve" # Just to be sure with the spelling
                    $body = @{
                        "status" = "APPROVED"
                    }
                }

                "Decline" {
                    $Status = "Decline" # Just to be sure with the spelling
                    $body = @{
                        "status" = "DECLINED"
                    }
                }

                Default {
                    Stop-PSFFunction -Message "Unhandled status '$($Status)', developers mistake" -EnableException $true -Cmdlet $pscmdlet -Tag "VacationRequest", "SwitchException", "ParameterSet"
                }
            }

            foreach ($vacationRequest in $InputObject) {

                if ($pscmdlet.ShouldProcess("VacationRequestId '$($vacationRequest.Id)' from '$($vacationRequest.EmployeeName)' on '$($vacationRequest.StartDate)-$($vacationRequest.EndDate)'", $Status)) {
                    Write-PSFMessage -Level Verbose -Message "$($Status) VacationRequestId '$($vacationRequest.Id)' from '$($vacationRequest.EmployeeName)' on '$($vacationRequest.StartDate)-$($vacationRequest.EndDate)'" -Tag "VacationRequest", "Set", $Status

                    # Set status on VacationRequest
                    $apiPath = Format-ApiPath -Path "api/v1/vacationRequests/$($vacationRequest.Id)"
                    $response = Invoke-TANSSRequest -Type "PUT" -ApiPath $apiPath -Body $body -Token $Token
                    Write-PSFMessage -Level Verbose -Message "VacationRequestId '$($vacationRequest.Id)' - $($response.meta.text)" -Tag "VacationRequest", "Set"

                    # Output if Passthrough is set
                    if($PassThru) {
                        [TANSS.Vacation.Request]@{
                            BaseObject = $response.content
                            Id         = $response.content.id
                        }
                    }
                }
            }
        }
    }

    end {
    }
}


<#
This is an example configuration file
 
By default, it is enough to have a single one of them,
however if you have enough configuration settings to justify having multiple copies of it,
feel totally free to split them into multiple files.
 
# Example Configuration
Set-PSFConfig -Module 'PSTANSS' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'"
#>




#region Module configurations
Set-PSFConfig -Module 'PSTANSS' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging."
Set-PSFConfig -Module 'PSTANSS' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments."

Set-PSFConfig -Module 'PSTANSS' -Name 'API.RestPathPrefix' -Value "backend/" -Initialize -Validation 'string' -Description "Individual URI path for API webservices on TANSS server. HuckIT specifies the rest calls on 'https://api-doc.tanss.de/', but on prod-installations for TANSS, there maybe a prefix in the path of the api rest calls."

#endregion Module configurations



#region Module variables
New-Variable -Name TANSSToken -Scope Script -Visibility Public -Description "Variable for registered token. This is for convinience use with the commands in the module" -Force

#endregion Module variables



#region Manual Lookup definitions
[TANSS.Lookup]::LinkTypes = @{
    "0"  = "Keine Zuweisung"
    "1"  = "PC"
    "2"  = "Kunde (generell)"
    "3"  = "Mitarbeiter"
    "4"  = "Peripherie"
    "5"  = "Komponente"
    "6"  = "Lizenz"
    "24" = "Domain"
}

[TANSS.Lookup]::VacationTypesPredefinedApi = [ordered]@{
    "VACATION" = "Urlaub"
    "ILLNESS"  = "Krankheit"
    "ABSENCE"  = "Abwesenheit"
    "STAND_BY" = "Bereitschaft"
    "OVERTIME" = "Überstunden abfeiern"
}

[TANSS.Lookup]::VacationAbsenceSubTypes = @{}


#endregion Manual Lookup definitions


<#
Stored scriptblocks are available in [PsfValidateScript()] attributes.
This makes it easier to centrally provide the same scriptblock multiple times,
without having to maintain it in separate locations.
 
It also prevents lengthy validation scriptblocks from making your parameter block
hard to read.
 
Set-PSFScriptblock -Name 'PSTANSS.ScriptBlockName' -Scriptblock {
     
}
#>


Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Companies" -ScriptBlock { [TANSS.Lookup]::Companies.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Client" -ScriptBlock { [TANSS.Lookup]::Employees.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Contracts" -ScriptBlock { [TANSS.Lookup]::Contracts.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.CostCenters" -ScriptBlock { [TANSS.Lookup]::CostCenters.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Departments" -ScriptBlock { [TANSS.Lookup]::Departments.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Employees" -ScriptBlock { [TANSS.Lookup]::Employees.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.OrderBys" -ScriptBlock { [TANSS.Lookup]::OrderBys.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Phases" -ScriptBlock { [TANSS.Lookup]::Phases.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Tags" -ScriptBlock { [TANSS.Lookup]::Tags.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.Tickets" -ScriptBlock { [TANSS.Lookup]::Tickets.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.TicketStates" -ScriptBlock { [TANSS.Lookup]::TicketStates.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.TicketTypes" -ScriptBlock { [TANSS.Lookup]::TicketTypes.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.VacationAbsenceSubTypes" -ScriptBlock { [TANSS.Lookup]::VacationAbsenceSubTypes.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.CarNumberPlate" -ScriptBlock { [TANSS.Lookup]::CarNumberplate.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.SupportTypes" -ScriptBlock { [TANSS.Lookup]::SupportTypes.Values }
Register-PSFTeppScriptblock -Name "PSTANSS.CacheLookup.NotChargedReasons" -ScriptBlock { [TANSS.Lookup]::NotChargedReasons.Values }


<#
# Example:
Register-PSFTeppScriptblock -Name "PSTANSS.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' }
#>


# VacationRequestTypes
Register-PSFTeppScriptblock -Name "PSTANSS.Parameter.GetVacationRequest.Type" -ScriptBlock { @([TANSS.Lookup]::VacationTypesPredefinedApi.Values, [TANSS.Lookup]::VacationTypesPredefinedApi.Keys) }


<#
# Example:
Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name PSTANSS.alcohol
#>



#region Ticket
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Company -Name "PSTANSS.CacheLookup.Companies"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Client -Name "PSTANSS.CacheLookup.Client"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Department -Name "PSTANSS.CacheLookup.Departments"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter EmployeeAssigned -Name "PSTANSS.CacheLookup.Employees"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter EmployeeTicketAdmin -Name "PSTANSS.CacheLookup.Employees"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Phase -Name "PSTANSS.CacheLookup.Phases"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Status -Name "PSTANSS.CacheLookup.TicketStates"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter Type -Name "PSTANSS.CacheLookup.TicketTypes"
Register-PSFTeppArgumentCompleter -Command New-TANSSTicket -Parameter OrderBy -Name "PSTANSS.CacheLookup.OrderBys"

Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Company -Name "PSTANSS.CacheLookup.Companies"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Client -Name "PSTANSS.CacheLookup.Client"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Department -Name "PSTANSS.CacheLookup.Departments"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter EmployeeAssigned -Name "PSTANSS.CacheLookup.Employees"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter EmployeeTicketAdmin -Name "PSTANSS.CacheLookup.Employees"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Phase -Name "PSTANSS.CacheLookup.Phases"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Status -Name "PSTANSS.CacheLookup.TicketStates"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter Type -Name "PSTANSS.CacheLookup.TicketTypes"
Register-PSFTeppArgumentCompleter -Command Set-TANSSTicket -Parameter OrderBy -Name "PSTANSS.CacheLookup.OrderBys"
#endregion Ticket


#region Core
Register-PSFTeppArgumentCompleter -Command Find-TANSSObject -Parameter CompanyName -Name "PSTANSS.CacheLookup.Companies"
#endregion Core


#region Employee
Register-PSFTeppArgumentCompleter -Command New-TANSSEmployee -Parameter Department -Name "PSTANSS.CacheLookup.Departments"
Register-PSFTeppArgumentCompleter -Command New-TANSSEmployee -Parameter CompanyName -Name "PSTANSS.CacheLookup.Companies"
#endregion Employee


#region Vacation
Register-PSFTeppArgumentCompleter -Command Get-TANSSVacationType -Parameter Name -Name "PSTANSS.CacheLookup.VacationAbsenceSubTypes"

Register-PSFTeppArgumentCompleter -Command New-TANSSVacationRequest -Parameter AbsenceSubTypeName -Name "PSTANSS.CacheLookup.VacationAbsenceSubTypes"

Register-PSFTeppArgumentCompleter -Command Get-TANSSVacationRequest -Parameter EmployeeName -Name "PSTANSS.CacheLookup.Employees"
Register-PSFTeppArgumentCompleter -Command Get-TANSSVacationRequest -Parameter DepartmentName -Name "PSTANSS.CacheLookup.Departments"
Register-PSFTeppArgumentCompleter -Command Get-TANSSVacationRequest -Parameter Type -Name "PSTANSS.Parameter.GetVacationRequest.Type"
Register-PSFTeppArgumentCompleter -Command Get-TANSSVacationRequest -Parameter AbsenceSubTypeName -Name "PSTANSS.CacheLookup.VacationAbsenceSubTypes"

Register-PSFTeppArgumentCompleter -Command Set-TANSSVacationRequest -Parameter Type -Name "PSTANSS.Parameter.GetVacationRequest.Type"
Register-PSFTeppArgumentCompleter -Command Set-TANSSVacationRequest -Parameter AbsenceSubTypeName -Name "PSTANSS.CacheLookup.VacationAbsenceSubTypes"

Register-PSFTeppArgumentCompleter -Command Request-TANSSVacationRequestObject -Parameter Type -Name "PSTANSS.Parameter.GetVacationRequest.Type"
Register-PSFTeppArgumentCompleter -Command Request-TANSSVacationRequestObject -Parameter EmployeeName -Name "PSTANSS.CacheLookup.Employees"

Register-PSFTeppArgumentCompleter -Command Set-TANSSVacationEntitlement -Parameter EmployeeName -Name "PSTANSS.CacheLookup.Employees"
#endregion Vacation


#region TimeStamp
Register-PSFTeppArgumentCompleter -Command Get-TANSSTimeStamp -Parameter EmployeeName -Name "PSTANSS.CacheLookup.Employees"

Register-PSFTeppArgumentCompleter -Command New-TANSSTimeStamp -Parameter EmployeeName -Name "PSTANSS.CacheLookup.Employees"
#endregion TimeStamp


#region ProjektPhase
Register-PSFTeppArgumentCompleter -Command Set-TANSSProjectPhase -Parameter PhaseName -Name "PSTANSS.CacheLookup.Phases"
#endregion ProjektPhase


New-PSFLicense -Product 'PSTANSS' -Manufacturer 'Andreas Bellstedt' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2022-07-29") -Text @"
Copyright (c) 2022 Andreas Bellstedt
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@


$runspaceName = "TANSS.LookupValidation"
$ScriptBlock = [System.Management.Automation.ScriptBlock]::Create( (Get-Content "$($script:ModuleRoot)\internal\scripts\Expand-TANSSCacheData.ps1" -Raw) )

if (Get-PSFRunspace -Name $runspaceName) {
    [TANSS.Cache]::StopValidationRunspace = $true
    Get-PSFRunspace -Name $runspaceName | Stop-PSFRunspace
}

Register-PSFRunspace -Name $runspaceName -ScriptBlock $ScriptBlock

[TANSS.Cache]::StopValidationRunspace = $false
Start-PSFRunspace -Name $runspaceName

#endregion Load compiled code