Public/Get-ProjectedTransactions.ps1
|
function Get-ProjectedTransactions { <# .SYNOPSIS Generates projected transactions based on billers, earnings, and transfers. .DESCRIPTION Creates a list of projected transactions for a specified date range based on configured billers, earnings, and transfers. Calculates running balance for each transaction. Transactions use the AccountId from their source entity. Transfers create two transactions: outflow from source and inflow to destination. .PARAMETER StartDate The start date for the projection period. .PARAMETER EndDate The end date for the projection period. .PARAMETER Account Optional account name to filter transactions. Only returns transactions for the specified account. .PARAMETER InitialBalance The starting balance for the projection. Defaults to 0. .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 Get-ProjectedTransactions -StartDate "2025-01-01" -EndDate "2025-03-31" .EXAMPLE Get-ProjectedTransactions -StartDate (Get-Date) -EndDate (Get-Date).AddMonths(6) -InitialBalance 1000 .EXAMPLE Get-ProjectedTransactions -StartDate "2025-01-01" -EndDate "2025-12-31" -Account "Joint Billing" .EXAMPLE Get-ProjectedTransactions -StartDate "2025-01-01" -EndDate "2025-12-31" -Budget "japan-holiday-2026" .OUTPUTS Array of Transaction objects #> [CmdletBinding()] param( [Parameter(Mandatory)] [datetime]$StartDate, [Parameter(Mandatory)] [datetime]$EndDate, [Parameter()] [string]$Account, [Parameter()] [decimal]$InitialBalance = 0, [Parameter()] [string]$Budget, [Parameter()] [string]$DataPath ) $resolvedPath = Resolve-DataPath -DataPath $DataPath -Budget $Budget if (-not $resolvedPath) { return @() } # Get all entities $billers = Read-EntityData -EntityType 'Biller' -DataPath $resolvedPath $earnings = Read-EntityData -EntityType 'Earning' -DataPath $resolvedPath $transfers = Read-EntityData -EntityType 'Transfer' -DataPath $resolvedPath $accounts = Read-EntityData -EntityType 'Account' -DataPath $resolvedPath # Resolve account filter if specified $filterAccountId = $null if ($Account) { $matchingAccount = $accounts | Where-Object { $_.Name -eq $Account } if (-not $matchingAccount) { Write-Error "Account '$Account' not found." return @() } $filterAccountId = $matchingAccount.Id } # Create transaction list $transactions = [System.Collections.ArrayList]::new() # Helper function to add next occurrence function Get-NextDate { param([datetime]$CurrentDate, [string]$Frequency) switch ($Frequency) { 'Daily' { return $CurrentDate.AddDays(1) } 'Weekly' { return $CurrentDate.AddDays(7) } 'BiWeekly' { return $CurrentDate.AddDays(14) } 'Monthly' { return $CurrentDate.AddMonths(1) } 'Bimonthly' { return $CurrentDate.AddMonths(2) } 'Quarterly' { return $CurrentDate.AddMonths(3) } 'Yearly' { return $CurrentDate.AddYears(1) } } } # Process earnings (positive transactions) foreach ($earning in $earnings) { # Skip if filtering by account and this earning doesn't match if ($filterAccountId -and $earning.AccountId -ne $filterAccountId) { continue } $currentDate = Get-NextOccurrence -StartDate $earning.StartDate -Frequency $earning.Frequency -FromDate $StartDate while ($currentDate -le $EndDate) { $transaction = [Transaction]@{ Date = $currentDate Name = $earning.Name Amount = $earning.Amount Balance = 0 # Will calculate later Type = 'Earning' AccountId = $earning.AccountId } $transactions.Add($transaction) | Out-Null $currentDate = Get-NextDate -CurrentDate $currentDate -Frequency $earning.Frequency } } # Process billers (negative transactions) foreach ($biller in $billers) { # Skip if filtering by account and this biller doesn't match if ($filterAccountId -and $biller.AccountId -ne $filterAccountId) { continue } $currentDate = Get-NextOccurrence -StartDate $biller.StartDate -Frequency $biller.Frequency -FromDate $StartDate while ($currentDate -le $EndDate) { $transaction = [Transaction]@{ Date = $currentDate Name = $biller.Name Amount = -$biller.Amount # Negative for expenses Balance = 0 # Will calculate later Type = 'Biller' AccountId = $biller.AccountId } $transactions.Add($transaction) | Out-Null $currentDate = Get-NextDate -CurrentDate $currentDate -Frequency $biller.Frequency } } # Process transfers (creates two transactions: outflow and inflow) foreach ($transfer in $transfers) { $currentDate = Get-NextOccurrence -StartDate $transfer.StartDate -Frequency $transfer.Frequency -FromDate $StartDate while ($currentDate -le $EndDate) { # Outflow transaction (from source account) if (-not $filterAccountId -or $transfer.FromAccountId -eq $filterAccountId) { $outflowTransaction = [Transaction]@{ Date = $currentDate Name = "$($transfer.Name) (Transfer Out)" Amount = -$transfer.Amount # Negative for outflow Balance = 0 Type = 'Transfer' AccountId = $transfer.FromAccountId } $transactions.Add($outflowTransaction) | Out-Null } # Inflow transaction (to destination account) if (-not $filterAccountId -or $transfer.ToAccountId -eq $filterAccountId) { $inflowTransaction = [Transaction]@{ Date = $currentDate Name = "$($transfer.Name) (Transfer In)" Amount = $transfer.Amount # Positive for inflow Balance = 0 Type = 'Transfer' AccountId = $transfer.ToAccountId } $transactions.Add($inflowTransaction) | Out-Null } $currentDate = Get-NextDate -CurrentDate $currentDate -Frequency $transfer.Frequency } } # Sort transactions by date $sortedTransactions = $transactions | Sort-Object -Property Date # Add initial balance as the first entry $initialTransaction = [Transaction]@{ Date = $StartDate Name = 'Initial Balance' Amount = 0 Balance = $InitialBalance Type = 'Balance' AccountId = $filterAccountId } # Create final list with initial balance first $finalTransactions = [System.Collections.ArrayList]::new() $finalTransactions.Add($initialTransaction) | Out-Null # Calculate running balance for remaining transactions $runningBalance = $InitialBalance foreach ($transaction in $sortedTransactions) { $runningBalance += $transaction.Amount $transaction.Balance = $runningBalance $finalTransactions.Add($transaction) | Out-Null } return $finalTransactions } |