Pax8API/Public/Get-Pax8PortalProductRatePlan.ps1

function Get-Pax8PortalProductRatePlan {
    <#
    .SYNOPSIS
    Read Pax8 portal display rate plans for one or more products.

    .DESCRIPTION
    Uses the read-only portal pricing endpoint used by the app.pax8.com pricing
    panel. The endpoint expects legacy numeric product IDs, so this command can
    resolve OpenAPI product UUIDs through Get-Pax8PortalCatalogProduct first.

    This path is useful for products whose published OpenAPI pricing endpoint
    returns 404 while the portal still shows bills-in-arrears and child arrears
    charge rate plans.

    .PARAMETER ProductId
    One or more Pax8 product UUIDs to resolve and price.

    .PARAMETER ProductLegacyId
    One or more Pax8 legacy numeric product IDs.

    .PARAMETER CurrencyId
    Pax8 portal currency ID. 9 is AUD, 1 is USD.

    .PARAMETER UseChildProductsForArrears
    Include child products used by bills-in-arrears products.

    .PARAMETER Unique
    Deduplicate rows by rate plan/range/rate values.

    .PARAMETER Raw
    Return raw portal rate plan objects instead of flattened rate rows.
    #>

    [CmdletBinding(DefaultParameterSetName = 'ProductId')]
    param (
        [Parameter(Mandatory, ParameterSetName = 'ProductId', ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [Alias('Id', 'Uuid')]
        [guid[]]$ProductId,

        [Parameter(Mandatory, ParameterSetName = 'LegacyId')]
        [ValidateRange(1, [int]::MaxValue)]
        [int[]]$ProductLegacyId,

        [ValidateRange(1, [int]::MaxValue)]
        [int]$CurrencyId = 9,

        [switch]$UseChildProductsForArrears,

        [switch]$Unique,

        [switch]$Raw
    )

    begin {
        $sourceProducts = [System.Collections.Generic.List[object]]::new()
    }

    process {
        if ($PSCmdlet.ParameterSetName -eq 'ProductId') {
            foreach ($id in $ProductId) {
                if ($id -eq [guid]::Empty) {
                    continue
                }

                $portalProduct = Get-Pax8PortalCatalogProduct -ProductId $id | Select-Object -First 1
                if (-not $portalProduct -or -not $portalProduct.legacyId) {
                    Write-Warning "Pax8 portal catalogue did not return a legacyId for product '$id'."
                    continue
                }

                $sourceProducts.Add([pscustomobject]@{
                    SourceProductId = [string]$id
                    SourceLegacyId = [int]$portalProduct.legacyId
                    SourceSku = $portalProduct.sku
                    SourceName = $portalProduct.name
                    BillsInArrears = [bool]$portalProduct.billsInArrears
                })
            }
        } else {
            foreach ($legacyId in $ProductLegacyId) {
                $sourceProducts.Add([pscustomobject]@{
                    SourceProductId = $null
                    SourceLegacyId = [int]$legacyId
                    SourceSku = $null
                    SourceName = $null
                    BillsInArrears = $false
                })
            }
        }
    }

    end {
        Assert-Pax8Session -Audience 'https://api.pax8.com'

        $rows = [System.Collections.Generic.List[object]]::new()
        foreach ($source in $sourceProducts) {
            $body = [ordered]@{
                productIds = @([int]$source.SourceLegacyId)
                currencyId = $CurrencyId
            }

            if ($UseChildProductsForArrears -or $source.BillsInArrears) {
                $body.useChildProductsForArrears = $true
            }

            $uri = [uri]'https://app.pax8.com/p8p/api/v3/pricing/ratePlans'
            $ratePlans = @(Invoke-Pax8RestMethod -Uri $uri -Method POST -Body $body)

            if ($Raw) {
                foreach ($ratePlan in $ratePlans) {
                    $rows.Add($ratePlan)
                }
                continue
            }

            foreach ($ratePlan in $ratePlans) {
                foreach ($rate in @($ratePlan.rates)) {
                    $rows.Add([pscustomobject]@{
                        SourceProductId = $source.SourceProductId
                        SourceLegacyId = $source.SourceLegacyId
                        SourceSku = $source.SourceSku
                        SourceName = $source.SourceName
                        RatePlanId = $ratePlan.id
                        RatePlanUuid = $ratePlan.uuid
                        ProductLegacyId = $ratePlan.productId
                        ProductName = $ratePlan.productName
                        VendorName = $ratePlan.vendorName
                        BillingTerm = $ratePlan.billingTerm
                        CurrencyId = $ratePlan.currencyId
                        Type = $ratePlan.type
                        StartDate = $ratePlan.startDate
                        SuggestedRetailPrice = $rate.suggestedRetailPrice
                        PartnerBuyRate = $rate.partnerBuyRate
                        WholesaleBuyRate = $rate.wholesaleBuyRate
                        RangeStart = $rate.rangeStart
                        RangeEnd = $rate.rangeEnd
                        SkuBand = $rate.skuBand
                        VendorSkuBand = $rate.vendorSkuBand
                        IsFlat = $rate.isFlat
                        PricingSource = 'Pax8PortalRatePlans'
                    })
                }
            }
        }

        if ($Unique -and -not $Raw) {
            return $rows | Sort-Object RatePlanId, ProductLegacyId, ProductName, BillingTerm, RangeStart, RangeEnd, SuggestedRetailPrice, PartnerBuyRate -Unique
        }

        $rows
    }
}