Seed and self-heal Gift Certificate Liability account 2500 (audit O5)
Account 2500 is resolved by number as the GC liability (GiftCertificatesController),
but the per-tenant seeder never created it — so tenants onboarded after the
AccountingGapsPhase2 migration had no GC liability account and gift-certificate GL
postings silently no-op'd. The default-company seeder also created 2500 as
"Long-Term Loan", mislabeling that company's GC obligations.
- SeedDataService.Accounts: seed 2500 "Gift Certificate Liability" (IsSystem)
- SeedData: seed 2500 as GC liability; move long-term loan to 2900
- EnsureSystemAccountsAsync: self-heal — rename a 2500 still named "Long-Term Loan"
(preserving user renames) and ensure a 2500 exists
- migration FixGiftCertificateLiabilityAccount: move long-term loan to 2900 where a
2500="Long-Term Loan" exists without a 2900, relabel the mislabeled 2500, and
safety-net insert a 2500 for any company lacking one
Non-destructive: no account Id/number/balance is changed (same pattern as O1).
Verified on dev: existing GC-liability rows preserved, no spurious accounts added.
All audit findings O1-O5 resolved. Build clean; 291 unit tests pass; migration applied.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -66,6 +66,9 @@ public partial class SeedDataService
|
||||
new Account { AccountNumber = "2300", Name = "Customer Deposits", AccountType = AccountType.Liability, AccountSubType = AccountSubType.OtherCurrentLiability, IsSystem = true, IsActive = true, Description = "Deposits received from customers before an invoice is created; cleared when applied to an invoice", CompanyId = company.Id, CreatedAt = now },
|
||||
new Account { AccountNumber = "2350", Name = "Customer Credits", AccountType = AccountType.Liability, AccountSubType = AccountSubType.OtherCurrentLiability, IsSystem = true, IsActive = true, Description = "Store credit owed to customers (credit memos not yet applied)", CompanyId = company.Id, CreatedAt = now },
|
||||
new Account { AccountNumber = "2400", Name = "Payroll Liabilities", AccountType = AccountType.Liability, AccountSubType = AccountSubType.OtherCurrentLiability, IsSystem = false, IsActive = true, Description = "Payroll taxes and withholdings owed", CompanyId = company.Id, CreatedAt = now },
|
||||
// 2500 Gift Certificate Liability — credited when a GC is issued, debited when redeemed/voided
|
||||
// (resolved by number in GiftCertificatesController). IsSystem because the GL posting depends on it.
|
||||
new Account { AccountNumber = "2500", Name = "Gift Certificate Liability", AccountType = AccountType.Liability, AccountSubType = AccountSubType.OtherCurrentLiability, IsSystem = true, IsActive = true, Description = "Outstanding gift certificate obligations owed to certificate holders", CompanyId = company.Id, CreatedAt = now },
|
||||
new Account { AccountNumber = "2900", Name = "Business Loan", AccountType = AccountType.Liability, AccountSubType = AccountSubType.LongTermLiability, IsSystem = false, IsActive = true, Description = "Long-term equipment or business loan", CompanyId = company.Id, CreatedAt = now },
|
||||
|
||||
// ── EQUITY ────────────────────────────────────────────────────────
|
||||
@@ -235,6 +238,46 @@ public partial class SeedDataService
|
||||
added++;
|
||||
}
|
||||
|
||||
// 2500 has always been resolved by number as the Gift Certificate Liability (GiftCertificatesController),
|
||||
// but the default-company seed created it as "Long-Term Loan" — so GC obligations were mislabeled there.
|
||||
// Rename it (only where it still carries the old default name) and mark it system.
|
||||
var legacyGcAcct = await _context.Set<Account>()
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(a => a.CompanyId == company.Id && a.AccountNumber == "2500"
|
||||
&& !a.IsDeleted && a.Name == "Long-Term Loan");
|
||||
if (legacyGcAcct != null)
|
||||
{
|
||||
legacyGcAcct.Name = "Gift Certificate Liability";
|
||||
legacyGcAcct.Description = "Outstanding gift certificate obligations owed to certificate holders";
|
||||
legacyGcAcct.IsSystem = true;
|
||||
legacyGcAcct.UpdatedAt = now;
|
||||
await _context.SaveChangesAsync();
|
||||
}
|
||||
|
||||
// 2500 Gift Certificate Liability — ensure it exists for companies that never got one (e.g. tenants
|
||||
// onboarded after the AccountingGapsPhase2 migration ran). Without it, GC GL postings silently no-op.
|
||||
var has2500 = await _context.Set<Account>()
|
||||
.IgnoreQueryFilters()
|
||||
.AnyAsync(a => a.CompanyId == company.Id && a.AccountNumber == "2500" && !a.IsDeleted);
|
||||
|
||||
if (!has2500)
|
||||
{
|
||||
_context.Set<Account>().Add(new Account
|
||||
{
|
||||
AccountNumber = "2500",
|
||||
Name = "Gift Certificate Liability",
|
||||
AccountType = AccountType.Liability,
|
||||
AccountSubType = AccountSubType.OtherCurrentLiability,
|
||||
IsSystem = true,
|
||||
IsActive = true,
|
||||
Description = "Outstanding gift certificate obligations owed to certificate holders",
|
||||
CompanyId = company.Id,
|
||||
CreatedAt = now
|
||||
});
|
||||
await _context.SaveChangesAsync();
|
||||
added++;
|
||||
}
|
||||
|
||||
return added;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user