Public/Employees/Invoke-UKGEmployeeBulk.ps1

function Invoke-UKGEmployeeBulk {
    <#
    .SYNOPSIS
        Performs bulk operations on employees in the UKG HR Service Delivery system.

    .DESCRIPTION
        Creates, updates, or patches multiple employees in a single API call.
        Returns a request ID that can be used to check the status of the operation.

    .PARAMETER Create
        Create new employees.

    .PARAMETER Update
        Update existing employees (PUT - full replacement).

    .PARAMETER Patch
        Patch existing employees (PATCH - partial update).

    .PARAMETER Employees
        Array of employee objects to process.

    .PARAMETER WaitForCompletion
        Wait for the bulk operation to complete before returning.

    .PARAMETER TimeoutSeconds
        Maximum time to wait for completion (default: 300 seconds).

    .EXAMPLE
        $employees = @(
            @{ email = "emp1@company.com"; first_name = "John"; last_name = "Doe" },
            @{ email = "emp2@company.com"; first_name = "Jane"; last_name = "Smith" }
        )
        Invoke-UKGEmployeeBulk -Create -Employees $employees

    .EXAMPLE
        $updates = @(
            @{ id = "emp123"; title = "Senior Developer" },
            @{ id = "emp456"; title = "Tech Lead" }
        )
        Invoke-UKGEmployeeBulk -Patch -Employees $updates -WaitForCompletion

    .OUTPUTS
        UKG.BulkRequest object with the request ID and status.
    #>

    [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'Create')]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ParameterSetName = 'Create')]
        [switch]$Create,

        [Parameter(Mandatory, ParameterSetName = 'Update')]
        [switch]$Update,

        [Parameter(Mandatory, ParameterSetName = 'Patch')]
        [switch]$Patch,

        [Parameter(Mandatory, ValueFromPipeline)]
        [object[]]$Employees,

        [Parameter()]
        [switch]$WaitForCompletion,

        [Parameter()]
        [ValidateRange(30, 3600)]
        [int]$TimeoutSeconds = 300
    )

    begin {
        $allEmployees = @()
    }

    process {
        foreach ($emp in $Employees) {
            if ($emp -is [hashtable]) {
                $allEmployees += $emp
            }
            else {
                # Convert PSCustomObject to hashtable
                $ht = @{}
                foreach ($prop in $emp.PSObject.Properties) {
                    if ($prop.Name -notin @('PSTypeName')) {
                        $ht[$prop.Name] = $prop.Value
                    }
                }
                $allEmployees += $ht
            }
        }
    }

    end {
        if ($allEmployees.Count -eq 0) {
            Write-Warning "No employees provided for bulk operation."
            return
        }

        # Determine HTTP method
        $method = switch ($PSCmdlet.ParameterSetName) {
            'Create' { 'POST' }
            'Update' { 'PUT' }
            'Patch' { 'PATCH' }
        }

        $operationName = switch ($PSCmdlet.ParameterSetName) {
            'Create' { 'Create' }
            'Update' { 'Update (PUT)' }
            'Patch' { 'Patch' }
        }

        if ($PSCmdlet.ShouldProcess("$($allEmployees.Count) employees", "Bulk $operationName")) {
            $body = @{
                employees = $allEmployees
            }

            $response = Invoke-UKGRequest -Endpoint '/employees/bulk' -Method $method -Body $body

            if ($response) {
                $response.PSObject.TypeNames.Insert(0, 'UKG.BulkRequest')

                if ($WaitForCompletion -and $response.request_id) {
                    $startTime = Get-Date
                    $completed = $false

                    Write-Verbose "Waiting for bulk operation to complete..."

                    while (-not $completed) {
                        Start-Sleep -Seconds 2

                        $status = Get-UKGEmployeeBulkStatus -RequestId $response.request_id

                        if ($status.status -eq 'completed' -or $status.status -eq 'failed') {
                            $completed = $true
                            return $status
                        }

                        $elapsed = ((Get-Date) - $startTime).TotalSeconds
                        if ($elapsed -ge $TimeoutSeconds) {
                            Write-Warning "Timeout waiting for bulk operation to complete. Use Get-UKGEmployeeBulkStatus to check status."
                            return $status
                        }

                        Write-Verbose "Status: $($status.status), Progress: $($status.processed_count)/$($status.total_count)"
                    }
                }
            }

            return $response
        }
    }
}

function Get-UKGEmployeeBulkStatus {
    <#
    .SYNOPSIS
        Gets the status of a bulk employee operation.

    .DESCRIPTION
        Retrieves the current status and results of a previously submitted bulk operation.

    .PARAMETER RequestId
        The request ID returned from Invoke-UKGEmployeeBulk.

    .EXAMPLE
        Get-UKGEmployeeBulkStatus -RequestId "req123"

    .OUTPUTS
        UKG.BulkRequest object with status and results.
    #>

    [CmdletBinding()]
    [OutputType([PSCustomObject])]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateNotNullOrEmpty()]
        [Alias('request_id')]
        [string]$RequestId
    )

    process {
        $response = Invoke-UKGRequest -Endpoint "/employees/bulk/$RequestId" -Method GET

        if ($response) {
            $response.PSObject.TypeNames.Insert(0, 'UKG.BulkRequest')
        }

        return $response
    }
}