- 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.
- Added toggle functionality for user roles (activate/deactivate) in roles and users pages.
- Introduced a reset password page that validates and updates user passwords via a reset link.
- Enhanced user detail page with edit functionality for user information (display name, email, mobile number).
- Implemented sending reset link feature for users, with appropriate error handling and success notifications.
- Updated API endpoints for user management to support new functionalities.
- Improved UI components for better user experience during status updates and password changes.
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>
- Introduced GlAccountConfig entity for mapping accounting roles to GL account codes.
- Added GlAccountConfigConfiguration for database configuration of the singleton GL account mapping.
- Created GlAccountRole enum to define accounting roles used in GL postings.
- Enhanced error handling with new error codes for unconfigured and invalid GL accounts.
- Updated existing DbContext to include GlAccountConfig DbSet.
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>
Three CHECK constraints shipped with PostgreSQL identifier quoting left over
from the MySQL port:
ck_grn_config_singleton CHECK ("ConfigId" = 1)
ck_sales_config_singleton CHECK ("ConfigId" = 1)
ck_warranty_periods_months CHECK ("Months" BETWEEN 1 AND 600)
ANSI_QUOTES is not in MariaDB's default sql_mode, so a double-quoted token is a
*string literal*, not an identifier. `"ConfigId" = 1` therefore evaluates as
`'ConfigId' = 1`; MariaDB coerces the string to a number to compare it and,
under strict mode, raises `Truncated incorrect DECIMAL value: 'ConfigId'`.
The DDL creates fine and only breaks at runtime, so this got through the
deploy and then failed every INSERT into grn_config, sales_config and
warranty_periods. That includes DataSeeder's two singleton config rows, which
run immediately after MigrateAsync() in Program.cs — so the service threw
"Database migration succeeded, but startup seeding failed" on every boot and
systemd restart-looped (counter reached 110, each restart dumping core).
Fixed in the entity configurations and repaired on existing databases by
migration 20261005190000_FixCheckConstraintQuotingForMySql, which drops and
re-adds the three constraints with backticks. The drop/add is emitted as raw
SQL rather than DropCheckConstraint because MariaDB spells the removal
DROP CONSTRAINT, not MySQL's DROP CHECK; IF EXISTS keeps it re-runnable on a
database where the broken constraint was already removed by hand.
The identical trap was swept during the port itself; these three were
reintroduced by a feature migration written afterwards.
No data cleanup is needed: the failing inserts never committed, so the
affected tables are empty.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Added loginerps.png.bak (1134 KB) as a backup for the login image.
- Added whithoutbackground.png.bak (3455 KB) as a backup for the image without background.
- Implemented PurchaseInvoiceBatchPrintPage for printing purchase invoices.
- Created InvoicePrintHeader and InvoicePrintFooter components for invoice layout.
- Added invoice layout presets for different styles (Classic, Compact, Modern).
- Developed PurchaseInvoicePaymentDialog for handling payments against invoices.
- Introduced API mocks for company profile and invoice template configuration.
- Added validation functions for company profile and purchase invoices.
- Defined types for company profile, invoice template configuration, and purchase invoices.
- Created a new migration to add navigation items and permissions for the production section.
- Implemented the Produced Items page with filtering options for date range, templates, warehouses, and status.
- Integrated API calls to fetch produced items, templates, and warehouses.
- Added UI components for displaying produced items in a table format with relevant details.
- Updated `UpdateStageQuantitiesRequest` to include `reasonCodeId` for lowering planned quantities below consumed amounts.
- Modified `ProductionRunService` to allow adjustments to planned quantities for running stock inputs, enforcing reason code requirements for decreases.
- Enhanced `StageDrawer` and `StageEditorPanel` components to support reason code selection and display available stock.
- Added utility functions for stock management and improved user feedback for stock adjustments.