- Introduced a new column 'AccruedExpensesCode' of type varchar(20) to the 'gl_account_config' table.
- Added SQL command to update existing records with a default value of '2300' for the 'AccruedExpensesCode' where it is currently null.
- Updated StockService to compute running balance on read and include item SKU, name, category, and brand in StockLedgerRowDto.
- Modified GetLedgerAsync method to accept categoryId and brandId parameters for filtering.
- Enhanced StockController to support new query parameters for ledger retrieval.
- Updated StockDtos to include additional item information in StockLedgerRowDto.
- Adjusted frontend Stock Ledger page to display item details and implement searchable item picker.
- Added tests for StockLedger to verify running balance calculations and filtering by category and brand.
- Updated appsettings for service URLs and modified login page navigation logic.
- Implemented VendorOptionalFields component to handle optional vendor details (mobile, email, address) with validation.
- Created useCustomerConfig hook to load optional customer field settings from Sales configuration.
- Developed useProcurementConfig hook to manage Procurement configuration flags.
- Introduced accessConfigApi for managing access configuration settings.
- Added openingBalancesApi for handling opening balances operations.
- Created procurementConfigApi for managing procurement configuration settings.
- Implemented nav-routes for permission-based routing.
- Developed PDF generation for purchase orders with dynamic vendor and item details.
- Added purchase-order tax helpers for tax calculations and formatting.
- Defined types for opening balances and related operations.
The rebase onto Dev resolved five conflicts; four were wrong. In the
service files Dev's constructor parameter lists won while the branch's
field declarations and constructor bodies stayed, so the bodies assigned
parameters that no longer existed.
- AdjustmentService: restore fifo, approvals, db and serialTracking
parameters (Dev's glAccounts kept, configuration dropped).
- FifoCostingService: restore the LowStockTracker field, parameter and
assignment.
- CountService: restore _serialTracking and _adjustments, and delete
Dev's PostAdjustmentJournalAsync. Dev's call site did not survive the
rebase, so the method was orphaned dead code referencing fields this
class no longer has. The branch routes count variances through
AdjustmentService.SubmitAsync, which posts via Dev's role-keyed
glCodes, so nothing is lost by removing it.
- PurchaseInvoicePaymentService: this one compiled. paymentDay was
computed but the GL period and EntryDate still used now, so a
back-dated payment posted its journal to today's period. Use
paymentDay, and resolve "today" through BusinessClock, matching
SalesInvoicePaymentService.
- ErpDbContextModelSnapshot: the resolution dropped the whole Expense
module from the definition and relationship sections while leaving its
navigation entries behind, so the snapshot no longer loaded. Rebuilt as
the branch's snapshot plus Dev's GlAccountConfig blocks; EF now reports
no pending model changes.
Tests: BankDepositTestSupport's FakeGeneralLedger gains the
ListAccountsAsync member Dev added to IGeneralLedgerService, and
DualApprovalTests passes a StubGlAccountConfigService in place of the
removed IConfiguration.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The per-module Gl*Code appsettings keys pinned GL's account *codes* in
ERPCore's config, while GL generates codes positionally and refuses a
requested one. Nothing connected the two, so "1100 means Inventory" held
only because someone once created accounts in a particular order - and on
2026-10-07 eleven of the twelve configured codes turned out never to have
existed in GL. Every GRN, adjustment, day-end, invoice and FIFO posting had
been failing with "Account <code> not found".
The mapping is now master data in gl_account_config, keyed by accounting
role rather than by module, and GlAccountConfigService validates every
submitted code against GL's live chart of accounts before storing it. A
code GL does not have, or cannot post to, is refused with the role named -
so the mismatch is unrepresentable rather than merely detected. Correcting
a mapping no longer needs a redeploy.
Notes:
- Fails closed on save (503 if GL is unreachable), deliberately opposite to
the startup validator, which must still let the app serve.
- Non-postable accounts are rejected too: GL rejects a journal line against
one, so storing it only defers the failure. The auto-created 1000
Bank/Cash header is exactly that trap.
- The settings endpoint also serves the selectable accounts, because
GeneralLedgerController is gated on `ledgers` while this is on `settings`
- and it keeps the picker and the validation list the same data.
- Reading an unmapped role throws naming the role, instead of posting ""
and letting GL return an opaque 404 from inside a transaction.
- The appsettings keys are now seed-only: DataSeeder copies them in once so
existing deployments keep their current mapping. No runtime fallback, or
there would be two sources of truth again.
- The 32 read sites across 11 services collapsed to 12 roles, since every
module carried the same value for a given role.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The catch filter only matched DomainException/OperationCanceledException,
so anything else escaped a hosted service and took the host down. One such
path was mine: `"items": null` deserializes over the property initialiser,
so `data.Items.Select(...)` would NullReferenceException straight past the
filter.
Fixed at both ends - the reader tolerates a null or absent items array, and
the catch is now a deliberate catch-all that only lets a genuine host
shutdown through. This is a diagnostic; a diagnostic that can core-dump the
thing it diagnoses is worse than no diagnostic at all.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The check shipped in f753cae threw on a missing code, which core-dumped
erpcore.service on deploy: 11 of the 12 configured Gl*Code values turned
out never to have existed in GL's chart of accounts, so a long-standing
mismatch became an immediate outage.
Detection was the point; refusing to serve the rest of the app was not.
GL already rejects a posting to an unknown account, so the per-flow error
stays the real enforcement — this now logs the whole set at Error once at
boot instead of surfacing one flow at a time.
Hard-fail is still available behind
GeneralLedgerService:FailOnMissingAccountCodes, for once the chart of
accounts is known-good and drift should stop a deploy.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>