Public/New-Transfer.ps1

function New-Transfer {
    <#
    .SYNOPSIS
        Creates a new transfer between accounts.
 
    .DESCRIPTION
        Creates a recurring transfer between two accounts. Transfers appear as
        negative transactions in the source account and positive transactions
        in the destination account when generating projected transactions.
 
    .PARAMETER Name
        The name of the transfer (e.g., "Savings Contribution").
 
    .PARAMETER StartDate
        The date when the transfer schedule starts.
 
    .PARAMETER Frequency
        How often the transfer occurs (Daily, Weekly, BiWeekly, Monthly, Quarterly, Yearly).
 
    .PARAMETER Amount
        The amount to transfer.
 
    .PARAMETER FromAccount
        The source account name where money is withdrawn from.
 
    .PARAMETER ToAccount
        The destination account name where money is deposited to.
 
    .PARAMETER Budget
        Optional budget name to target. Uses active budget if not specified.
 
    .PARAMETER DataPath
        Optional custom path for data storage. Overrides budget-based paths.
 
    .EXAMPLE
        New-Transfer -Name "Weekly Savings" -StartDate "2025-01-01" -Frequency Weekly -Amount 100 -FromAccount "Checking" -ToAccount "Savings"
 
    .EXAMPLE
        New-Transfer -Name "Rent Fund" -StartDate "2025-01-01" -Frequency BiWeekly -Amount 500 -FromAccount "My Billing" -ToAccount "Joint Billing"
 
    .OUTPUTS
        Transfer object
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Name,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [datetime]$StartDate,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [ValidateSet('Daily', 'Weekly', 'BiWeekly', 'Monthly', 'Bimonthly', 'Quarterly', 'Yearly')]
        [string]$Frequency,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [decimal]$Amount,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$FromAccount,

        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$ToAccount,

        [Parameter()]
        [string]$Budget,

        [Parameter()]
        [string]$DataPath
    )

    process {
        $resolvedPath = Resolve-DataPath -DataPath $DataPath -Budget $Budget
        if (-not $resolvedPath) { return }

        # Resolve source account
        $accounts = Read-EntityData -EntityType 'Account' -DataPath $resolvedPath
        $fromAccountObj = $accounts | Where-Object { $_.Name -eq $FromAccount }
        
        if (-not $fromAccountObj) {
            Write-Error "Source account '$FromAccount' not found. Please create it first with New-Account."
            return
        }

        # Resolve destination account
        $toAccountObj = $accounts | Where-Object { $_.Name -eq $ToAccount }
        
        if (-not $toAccountObj) {
            Write-Error "Destination account '$ToAccount' not found. Please create it first with New-Account."
            return
        }

        # Validate accounts are different
        if ($fromAccountObj.Id -eq $toAccountObj.Id) {
            Write-Error "Source and destination accounts must be different."
            return
        }

        $transfer = [Transfer]::new($Name, $StartDate, $Frequency, $Amount, $fromAccountObj.Id, $toAccountObj.Id)

        $existingTransfers = Read-EntityData -EntityType 'Transfer' -DataPath $resolvedPath
        $transfersList = [System.Collections.ArrayList]@($existingTransfers)
        $transfersList.Add($transfer.ToHashtable()) | Out-Null

        if (Write-EntityData -EntityType 'Transfer' -Data $transfersList -DataPath $resolvedPath) {
            Write-Verbose "Created transfer: $Name ($FromAccount -> $ToAccount)"
            return $transfer
        }
    }
}