Public/Remove-Account.ps1

function Remove-Account {
    <#
    .SYNOPSIS
        Removes an account entry.
 
    .DESCRIPTION
        Deletes an account from the data store by ID. By default, prevents deletion
        if the account is referenced by billers, earnings, or transfers. Use -Force
        to override and orphan dependent entities, or -Cascade to delete dependencies.
 
    .PARAMETER Id
        The ID of the account to remove.
 
    .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.
 
    .PARAMETER Force
        Bypasses dependency check and deletes the account even if it's referenced by
        other entities. Warning: This will orphan dependent billers, earnings, and transfers.
 
    .PARAMETER Cascade
        Deletes the account AND all dependent billers, earnings, and transfers.
        Requires confirmation due to high impact.
 
    .EXAMPLE
        Remove-Account -Id "abc123"
         
        Attempts to remove account, fails if dependencies exist
 
    .EXAMPLE
        Remove-Account -Id "abc123" -Force
         
        Removes account and orphans any dependent entities
 
    .EXAMPLE
        Remove-Account -Id "abc123" -Cascade
         
        Removes account and all dependent billers, earnings, and transfers
 
    .OUTPUTS
        None
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory, ValueFromPipelineByPropertyName)]
        [string]$Id,

        [Parameter()]
        [string]$Budget,

        [Parameter()]
        [string]$DataPath,

        [Parameter()]
        [switch]$Force,

        [Parameter()]
        [switch]$Cascade
    )

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

        $accounts = Read-EntityData -EntityType 'Account' -DataPath $resolvedPath
        $account = $accounts | Where-Object { $_.Id -eq $Id }

        if (-not $account) {
            Write-Error "Account with ID '$Id' not found."
            return
        }

        # Check for dependencies unless Force or Cascade is specified
        if (-not $Force -and -not $Cascade) {
            $dependencies = Get-AccountDependencies -Id $Id -DataPath $resolvedPath

            if ($dependencies.HasDependencies) {
                $message = @"
Cannot delete account '$($account.Name)' - it is referenced by:
  • $($dependencies.BillerCount) Biller(s)
  • $($dependencies.EarningCount) Earning(s)
  • $($dependencies.TransferCount) Transfer(s)
 
Use -Force to delete and orphan these entities, or -Cascade to delete them as well.
"@

                Write-Error $message
                return
            }
        }

        # If Cascade, delete all dependencies first
        if ($Cascade) {
            $dependencies = Get-AccountDependencies -Id $Id -DataPath $resolvedPath

            if ($dependencies.HasDependencies) {
                $cascadeMessage = @"
Deleting account '$($account.Name)' will also delete:
  • $($dependencies.BillerCount) Biller(s)
  • $($dependencies.EarningCount) Earning(s)
  • $($dependencies.TransferCount) Transfer(s)
Total: $($dependencies.TotalCount) dependent entities
"@

                Write-Warning $cascadeMessage

                if ($PSCmdlet.ShouldProcess("$($dependencies.TotalCount) dependent entities", "Delete cascade")) {
                    # Delete billers
                    if ($dependencies.BillerCount -gt 0) {
                        $billers = Read-EntityData -EntityType 'Biller' -DataPath $resolvedPath
                        $updatedBillers = @($billers | Where-Object { $_.AccountId -ne $Id })
                        Write-EntityData -EntityType 'Biller' -Data $updatedBillers -DataPath $resolvedPath | Out-Null
                        Write-Verbose "Deleted $($dependencies.BillerCount) biller(s)"
                    }

                    # Delete earnings
                    if ($dependencies.EarningCount -gt 0) {
                        $earnings = Read-EntityData -EntityType 'Earning' -DataPath $resolvedPath
                        $updatedEarnings = @($earnings | Where-Object { $_.AccountId -ne $Id })
                        Write-EntityData -EntityType 'Earning' -Data $updatedEarnings -DataPath $resolvedPath | Out-Null
                        Write-Verbose "Deleted $($dependencies.EarningCount) earning(s)"
                    }

                    # Delete transfers
                    if ($dependencies.TransferCount -gt 0) {
                        $transfers = Read-EntityData -EntityType 'Transfer' -DataPath $resolvedPath
                        $updatedTransfers = @($transfers | Where-Object { 
                            $_.FromAccountId -ne $Id -and $_.ToAccountId -ne $Id 
                        })
                        Write-EntityData -EntityType 'Transfer' -Data $updatedTransfers -DataPath $resolvedPath | Out-Null
                        Write-Verbose "Deleted $($dependencies.TransferCount) transfer(s)"
                    }
                }
                else {
                    Write-Verbose "Cascade delete cancelled"
                    return
                }
            }
        }

        # Delete the account
        if ($PSCmdlet.ShouldProcess($account.Name, "Remove account")) {
            $updatedAccounts = @($accounts | Where-Object { $_.Id -ne $Id })
            if (Write-EntityData -EntityType 'Account' -Data $updatedAccounts -DataPath $resolvedPath) {
                Write-Host "Removed account: $($account.Name)" -ForegroundColor Green
                
                if ($Force -and -not $Cascade) {
                    Write-Warning "Account deleted with -Force. Dependent entities may now be orphaned."
                }
            }
        }
    }
}