Guard money-account selections; derive account type from sub-type
Item 1 — server-side guard (defense in depth) on payment-source / deposit / reconcilable account selections. New AccountGuard.IsValidMoneyAccountAsync checks the submitted account is active, company-owned, and an Asset or Liability before any GL posting, at: bill RecordPayment, bill Create (payNow), bill EditPayment, BankReconciliation.Create, and deposit Record. The dropdowns already constrain normal users; this rejects tampered/stale POSTs. Per the "trust the operator" decision it still allows A/R etc. (any Asset/Liability) — it only blocks non-money types. Item 2 — account AccountType is now derived from the chosen AccountSubType on create/edit via the new AccountClassification.TypeForSubType (single source of truth, also used by the Create pre-select). The two can no longer disagree, so the sub-type-based debit/credit sign convention is always consistent with the account's type. A read-only sweep of the dev DB found 0 existing mismatches, so no repair tool was built. Audit doc updated: both backlog items marked resolved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
namespace PowderCoating.Core.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// Single source of truth mapping an <see cref="AccountSubType"/> to its parent
|
||||
/// <see cref="AccountType"/>. Each sub-type belongs to exactly one type, so the type can always
|
||||
/// be derived from the sub-type. Used on account create/edit to keep the two fields consistent
|
||||
/// (a mismatched pair would post with the wrong debit/credit sign, since the sign convention keys
|
||||
/// off the sub-type) and anywhere else that needs the canonical pairing.
|
||||
/// </summary>
|
||||
public static class AccountClassification
|
||||
{
|
||||
/// <summary>Returns the parent <see cref="AccountType"/> for a given <see cref="AccountSubType"/>.</summary>
|
||||
public static AccountType TypeForSubType(AccountSubType subType) => subType switch
|
||||
{
|
||||
AccountSubType.Cash or AccountSubType.Checking or AccountSubType.Savings
|
||||
or AccountSubType.AccountsReceivable or AccountSubType.Inventory or AccountSubType.FixedAsset
|
||||
or AccountSubType.OtherCurrentAsset or AccountSubType.OtherAsset => AccountType.Asset,
|
||||
|
||||
AccountSubType.AccountsPayable or AccountSubType.CreditCard
|
||||
or AccountSubType.OtherCurrentLiability or AccountSubType.LongTermLiability => AccountType.Liability,
|
||||
|
||||
AccountSubType.OwnersEquity or AccountSubType.RetainedEarnings => AccountType.Equity,
|
||||
|
||||
AccountSubType.Sales or AccountSubType.ServiceRevenue or AccountSubType.OtherIncome => AccountType.Revenue,
|
||||
|
||||
AccountSubType.CostOfGoodsSold => AccountType.CostOfGoods,
|
||||
|
||||
// All expense sub-types (enum values >= 50) and any future additions default to Expense.
|
||||
_ => AccountType.Expense
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user