Public/Get-UKGProEmploymentDetails.ps1

function Get-UKGProEmploymentDetails {
    <#
    .SYNOPSIS
        Retrieves employment record details from UKG Pro.

    .DESCRIPTION
        Wraps GET /personnel/v1/employment-details. Returns employment records
        including status, job, work location, supervisor, and key dates
        (hire, termination, retirement) — the fields most useful for offboarding
        and IAM workflows.

        All filters are optional and applied server-side. Results are paginated
        automatically (page/per_Page) unless -MaxResults caps them.

        Termination date is filtered via one of four intent-named parameters
        (mutually exclusive, enforced by parameter sets):
          -TerminatedOn <date> terminated on that exact date
          -TerminatedSince <date> terminated on or after that date
          -TerminatedBefore <date> terminated on or before that date
          -TerminatedBetweenStart / -TerminatedBetweenEnd range

    .PARAMETER CompanyId
        Filter by company identifier.

    .PARAMETER EmployeeId
        Filter by employee identifier.

    .PARAMETER EmailAddress
        Filter by the employee's UKG-registered email address. The email is
        resolved to an employee ID via GET /personnel/v1/person-details (a
        read-only endpoint requiring only the View role) and the resolved ID
        is then used for the employment-details query. Mutually exclusive
        with -EmployeeId.

    .PARAMETER EmployeeNumber
        Filter by employee number.

    .PARAMETER EmployeeStatusCode
        Filter by employee status code (e.g. active/terminated codes as defined
        in your tenant).

    .PARAMETER EmployeeTypeCode
        Filter by employee type code.

    .PARAMETER SupervisorId
        Filter by supervisor ID.

    .PARAMETER JobTitle
        Filter by job title.

    .PARAMETER PrimaryJobCode
        Filter by primary job code.

    .PARAMETER PrimaryWorkLocationCode
        Filter by primary work location code.

    .PARAMETER TerminatedOn
        Return only records with dateOfTermination equal to this date.

    .PARAMETER TerminatedSince
        Return records with dateOfTermination greater than this date — the
        "terminated in the last N days" pattern.

    .PARAMETER TerminatedBefore
        Return records with dateOfTermination less than this date.

    .PARAMETER TerminatedBetweenStart / -TerminatedBetweenEnd
        Return records with dateOfTermination within an inclusive date range.

    .PARAMETER ChangedSince
        Return records whose dateTimeChanged is greater than this date/time
        (useful for incremental syncs).

    .PARAMETER MaxResults
        Cap total records across all pages. 0 = all.

    .PARAMETER PageSize
        Rows per page to request (default 100).

    .EXAMPLE
        Get-UKGProEmploymentDetails -EmployeeId '000123'

        Retrieves employment details for one employee.

    .EXAMPLE
        Get-UKGProEmploymentDetails -EmailAddress 'alex.doe@example.com'

        Looks up the employee by email (via person-details) and returns their
        employment details. Equivalent to -EmployeeId but avoids needing to
        know the ID up front.

    .EXAMPLE
        Get-UKGProEmploymentDetails -TerminatedOn '8/28/26'

        Employees terminated on exactly 2026-08-28.

    .EXAMPLE
        Get-UKGProEmploymentDetails -TerminatedSince (Get-Date).AddDays(-30)

        Employees terminated in the last 30 days (offboarding candidates).

    .EXAMPLE
        Get-UKGProEmploymentDetails -TerminatedBefore '2026-01-01'

        Employees terminated before the start of 2026.

    .EXAMPLE
        Get-UKGProEmploymentDetails -TerminatedBetweenStart '2026-01-01' -TerminatedBetweenEnd '2026-03-31'

        Employees terminated in Q1 2026.

    .EXAMPLE
        Get-UKGProEmploymentDetails -ChangedSince (Get-Date).AddHours(-24)

        Records changed in the last day, for an incremental sync.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseSingularNouns', '',
        Justification = 'Public API name matches the underlying UKG response schema (EmpEmploymentDetails). Renaming to singular would break every existing caller and diverge from UKG''s own naming.')]
    [CmdletBinding(DefaultParameterSetName = 'Standard')]
    [OutputType([pscustomobject])]
    param (
        [Parameter()] [string]$CompanyId,
        [Parameter()] [string]$EmployeeId,
        [Parameter()] [string]$EmailAddress,
        [Parameter()] [string]$EmployeeNumber,
        [Parameter()] [string]$EmployeeStatusCode,
        [Parameter()] [string]$EmployeeTypeCode,
        [Parameter()] [string]$SupervisorId,
        [Parameter()] [string]$JobTitle,
        [Parameter()] [string]$PrimaryJobCode,
        [Parameter()] [string]$PrimaryWorkLocationCode,

        # --- Termination date: intent-named, mutually exclusive via parameter sets ---
        [Parameter(Mandatory, ParameterSetName = 'TerminatedOn')]
        [datetime]$TerminatedOn,

        [Parameter(Mandatory, ParameterSetName = 'TerminatedSince')]
        [datetime]$TerminatedSince,

        [Parameter(Mandatory, ParameterSetName = 'TerminatedBefore')]
        [datetime]$TerminatedBefore,

        [Parameter(Mandatory, ParameterSetName = 'TerminatedRange')]
        [datetime]$TerminatedBetweenStart,

        [Parameter(Mandatory, ParameterSetName = 'TerminatedRange')]
        [datetime]$TerminatedBetweenEnd,

        # --- Incremental sync helper ---
        [Parameter()]
        [datetime]$ChangedSince,

        [Parameter()] [int]$MaxResults = 0,
        [Parameter()] [int]$PageSize   = 100
    )

    if ($EmailAddress -and $EmployeeId) {
        throw "-EmailAddress and -EmployeeId cannot be used together. Choose one."
    }

    # Resolve email -> employeeId up front so the rest of the query flow is
    # identical to the -EmployeeId path. Uses a View-only GET resolver.
    if ($EmailAddress) {
        $resolved = Resolve-UKGProEmployeeIdByEmail -EmailAddress $EmailAddress
        $EmployeeId = $resolved.EmployeeId
    }

    $q = @{}
    if ($CompanyId)               { $q['companyId']               = $CompanyId }
    if ($EmployeeId)              { $q['employeeId']              = $EmployeeId }
    if ($EmployeeNumber)          { $q['employeeNumber']          = $EmployeeNumber }
    if ($EmployeeStatusCode)      { $q['employeeStatusCode']      = $EmployeeStatusCode }
    if ($EmployeeTypeCode)        { $q['employeeTypeCode']        = $EmployeeTypeCode }
    if ($SupervisorId)            { $q['supervisorID']            = $SupervisorId }
    if ($JobTitle)                { $q['jobTitle']                = $JobTitle }
    if ($PrimaryJobCode)          { $q['primaryJobCode']          = $PrimaryJobCode }
    if ($PrimaryWorkLocationCode) { $q['primaryWorkLocationCode'] = $PrimaryWorkLocationCode }

    switch ($PSCmdlet.ParameterSetName) {
        'TerminatedOn' {
            $q['dateOfTermination'] = ConvertTo-UKGProDateFilter -Operator EqualTo -Date $TerminatedOn
        }
        'TerminatedSince' {
            $q['dateOfTermination'] = ConvertTo-UKGProDateFilter -Operator GreaterThan -Date $TerminatedSince
        }
        'TerminatedBefore' {
            $q['dateOfTermination'] = ConvertTo-UKGProDateFilter -Operator LessThan -Date $TerminatedBefore
        }
        'TerminatedRange' {
            $q['dateOfTermination'] = ConvertTo-UKGProDateFilter -Operator Between `
                -RangeStart $TerminatedBetweenStart -RangeEnd $TerminatedBetweenEnd
        }
    }

    if ($PSBoundParameters.ContainsKey('ChangedSince')) {
        $q['dateTimeChanged'] = ConvertTo-UKGProDateFilter -Operator GreaterThan -Date $ChangedSince
    }

    Invoke-UKGProRequest -Method Get -Path '/personnel/v1/employment-details' `
        -Query $q -PageSize $PageSize -MaxResults $MaxResults
}