using General_Ledger_Service.Exceptions; using General_Ledger_Service.Interfaces.Repositories; using General_Ledger_Service.Models; namespace General_Ledger_Service.Services { // Extracted from AccountService (2026-07-31) so BankService's auto-created "Bank"/"Cash" GL // hierarchy (see BankService.CreateBankAccountAsync/CreateCashAccountAsync) can reuse the same // Rule 8.1 code-generation scheme instead of duplicating it - same shared-static-helper shape // as RecursiveTreeQueries/DepthGuard (CLAUDE.md Rule 11), not a base class. public static class AccountCodeGenerator { private static readonly Dictionary AccountTypeBlockStart = new() { [1] = 1000, // Asset [2] = 2000, // Liability [3] = 3000, // Equity [4] = 4000, // Income [5] = 5000 // Expense }; public static async Task GenerateAsync(IAccountRepository accountRepository, short accountTypeId, Account? parent) { if (parent != null) { for (var suffix = 1; suffix <= 99; suffix++) { var code = $"{parent.AccountCode}-{suffix:D2}"; if (code.Length > 20) { throw new BadRequestException("Cannot generate an account code: nesting is too deep for the 20-character account code column."); } if (await accountRepository.GetByCodeAsync(code) is null) { return code; } } throw new BadRequestException("Maximum number of child accounts under this parent has been reached."); } if (!AccountTypeBlockStart.TryGetValue(accountTypeId, out var blockStart)) { throw new BadRequestException( $"Invalid accountTypeId '{accountTypeId}'. Must be one of: 1 (Asset), 2 (Liability), 3 (Equity), 4 (Income), 5 (Expense)."); } // Probes each of the 10 designed slots directly rather than trusting a root count, // since pre-existing accounts created before this generation scheme existed (manually // coded, e.g. "5100") don't necessarily occupy slots in count order - a count-based // guess can collide with one of those and crash on the DB's unique constraint instead // of failing cleanly. for (var slot = 0; slot < 10; slot++) { var code = (blockStart + slot * 100).ToString(); if (await accountRepository.GetByCodeAsync(code) is null) { return code; } } throw new BadRequestException("Maximum number of root-level accounts for this account type has been reached."); } } }