# Sales Module Plan ## Summary This sales module is split into two phases: - **Phase 1**: basic, standard sales features that fit the current ERP architecture - **Phase 2**: enterprise extensions that can be added after the core flow is stable The design stays aligned with the existing backend patterns: - controller thinness - service-based business rules - repository + unit of work - ETag concurrency - audit logging - stock FIFO and ledger posting Returns, credit notes, and sales returns are **out of scope for Phase 1**. ### Current Implementation Status The Phase 1 core is implemented and wired across the backend and frontend for: - sales invoices - sales slips - free issues as a slip alias - bundle sales - sales posting to stock/FIFO Shared backend services now centralize the repeated sales logic: - sales validation and pricing - sales posting checks and FIFO outbound posting - invoice/slip mapping and totals - shared draft edit/load workflow checks Still intentionally separate: - bundle pricing and bundle margin behavior - production and GRN as upstream stock/cost sources - reservation/backorder flow - sales reports visibility in the frontend UI, which is currently hidden from the navigation but still implemented in the backend --- ## Phase 1 - Basic Standard Sales Module ### Goal Implement the minimum sales flow needed for both B2B and B2C: - maintain customers - create sales invoices - create sales slips - support fixed sale price fallback and GRN-based cost fallback - support discounts by percentage and value - support free issue lines - post stock movement and ledger entries - generate basic sales reports ### In Scope - Customer master - Sales invoice - Sales invoice lines - Sales slip - Sales slip lines - Pricing resolver - Discount calculation - Free issue handling - Stock posting - Basic sales reports ### Implemented Shared Services - `ISalesDomainService` - header validation - line validation - price resolution - line financial computation - stock-item classification - `ISalesPostingService` - invoice/slip/bundle posting checks - shared FIFO posting for stocked items - `ISalesMappingService` - invoice/slip totals mapping - invoice/slip DTO mapping - `ISalesDocumentWorkflowService` - shared editable-document load and concurrency checks for invoice/slip draft updates ### Not in Scope for Phase 1 - customer groups - price lists - promotions - reservations - sales payments allocation - approval workflow - returns and credit notes - advanced customer segmentation - fully unified sales provenance tracing across production, GRN, and sales documents ### Phase 1 Entity Design #### `Customer` Basic customer master used for both B2B and B2C. Fields: - `CustomerId` - `CustomerCode` - `CustomerType` (`B2B`, `B2C`, `WalkIn`) - `Name` - `DisplayName` - `Phone` - `Email` - `AddressLine1` - `AddressLine2` - `City` - `Country` - `TaxRegistrationNo` - `CreditLimit` - `CreditDays` - `DefaultWarehouseId` - `Status` - `CreatedAt` - `UpdatedAt` - `RowVersion` #### `SalesInvoice` Primary posted sales document. Fields: - `SalesInvoiceId` - `InvoiceNo` - `InvoiceDate` - `CustomerId` - `CustomerSnapshotName` - `CustomerSnapshotTaxNo` - `WarehouseId` - `InvoiceType` (`B2B`, `B2C`, `Cash`, `Credit`) - `Status` (`Draft`, `Posted`, `Cancelled`) - `Subtotal` - `DiscountTotal` - `TaxTotal` - `GrandTotal` - `RoundOff` - `NetPayable` - `PaidAmount` - `BalanceAmount` - `CreatedBy` - `CreatedAt` - `UpdatedAt` - `RowVersion` #### `SalesInvoiceLine` Invoice line with pricing, discount, and free issue support. Fields: - `SalesInvoiceLineId` - `SalesInvoiceId` - `ItemId` - `Description` - `Qty` - `FreeQty` - `UomId` - `WarehouseId` - `UnitPrice` - `BaseCost` - `PriceSource` - `DiscountPct` - `DiscountAmount` - `NetUnitPrice` - `LineTotal` - `TaxPct` - `TaxAmount` - `IsFreeIssue` - `ParentLineId` - `RowVersion` #### `SalesSlip` Fast retail or counter-sale document. Fields: - `SalesSlipId` - `SlipNo` - `SlipDate` - `CustomerId` - `CustomerSnapshotName` - `WarehouseId` - `CashierUserId` - `Status` - `Subtotal` - `DiscountTotal` - `TaxTotal` - `GrandTotal` - `PaidAmount` - `BalanceAmount` - `CreatedAt` - `UpdatedAt` - `RowVersion` #### `SalesSlipLine` Slip line with the same sales calculation rules as invoices. Fields: - `SalesSlipLineId` - `SalesSlipId` - `ItemId` - `Description` - `Qty` - `FreeQty` - `UomId` - `WarehouseId` - `UnitPrice` - `BaseCost` - `PriceSource` - `DiscountPct` - `DiscountAmount` - `NetUnitPrice` - `LineTotal` - `TaxPct` - `TaxAmount` - `IsFreeIssue` - `ParentLineId` - `RowVersion` ### Phase 1 Pricing Rule Use the following order: 1. fixed `Item.SalePrice` 2. GRN-derived stock cost fallback 3. FIFO valuation fallback Important: - use a weighted average when deriving from multiple GRNs - keep the resolved source in `PriceSource` - allow manual override only if permitted by business rule ### Phase 1 Discount Rule Support: - percentage discount - fixed value discount Discount must be computed server-side and stored in line and document totals. ### Phase 1 Free Issue Rule Support free issue lines in the same invoice/slip document. Rules: - free quantity must be separate from paid quantity - free issue still reduces stock - free issue must be visible in reports - free issue should not be merged into discount ### Phase 1 Stock Posting Rule When an invoice or slip is posted: - reduce stock from the selected warehouse - consume FIFO layers - write `StockLedger` rows - maintain source document traceability - update totals in the same transaction Sales document provenance is stored by document family: - `SalesInvoice` / `SalesInvoiceLine` - `SalesSlip` / `SalesSlipLine` - `BundleSale` / `BundleSaleLine` Inventory movement provenance is stored in: - `StockLayer` - `StockLedger` via `SourceDocType` / `SourceDocId` - `JournalEntryStub` via `SourceDocType` / `SourceDocId` Upstream cost/availability sources remain: - `Grn` / `GrnLine` for inbound purchasing cost - `ProductionRun` and stage tables for finished-goods production cost ### Phase 1 API Route List - `GET /api/v1/customers` - `GET /api/v1/customers/{id}` - `POST /api/v1/customers` - `PUT /api/v1/customers/{id}` - `PATCH /api/v1/customers/{id}/status` - `GET /api/v1/sales-invoices` - `GET /api/v1/sales-invoices/{id}` - `POST /api/v1/sales-invoices` - `PUT /api/v1/sales-invoices/{id}` - `POST /api/v1/sales-invoices/{id}/post` - `POST /api/v1/sales-invoices/{id}/cancel` - `GET /api/v1/sales-invoices/{id}/print-preview` - `GET /api/v1/sales-slips` - `GET /api/v1/sales-slips/{id}` - `POST /api/v1/sales-slips` - `POST /api/v1/sales-slips/{id}/post` - `POST /api/v1/sales-slips/{id}/cancel` - `GET /api/v1/free-issues` - `GET /api/v1/free-issues/{id}` - `POST /api/v1/free-issues` - `PUT /api/v1/free-issues/{id}` - `POST /api/v1/free-issues/{id}/post` - `POST /api/v1/free-issues/{id}/cancel` - `GET /api/v1/reports/sales` - `GET /api/v1/reports/sales/{reportId}` - `POST /api/v1/reports/sales/query` Note: - the sales report backend routes remain implemented - the frontend report entry points are currently hidden from navigation, but the screens and API contracts still exist ### Phase 1 Folder / Module Plan - `Domain/Entities` - add `Customer`, `SalesInvoice`, `SalesInvoiceLine`, `SalesSlip`, `SalesSlipLine` - `Domain/Enums` - add sales status enums and invoice/slip type enums - `Dtos/Sales` - add request and response DTOs for customer, invoice, slip, and reports - `Services/Interfaces` - add `ICustomerService`, `ISalesInvoiceService`, `ISalesSlipService`, `ISalesPricingService`, `ISalesReportService` - `Services` - implement the sales services with transaction-safe logic - `Controllers` - add `CustomersController`, `SalesInvoicesController`, `SalesSlipsController`, `SalesReportsController` - `Infra/Persistence/Configurations` - add EF Core mappings for all sales entities - `Infra/Persistence/ErpDbContext.cs` - register sales `DbSet`s - `Infra/Persistence/Migrations` - add the sales schema migration after the model is defined ### Phase 1 Implementation Order 1. Customer master 2. Sales invoice entity and DTOs 3. Sales slip entity and DTOs 4. Pricing resolver 5. Discount computation 6. Free issue handling 7. Stock posting 8. Basic sales reports 9. Controllers and swagger wiring --- ## Phase 2 - Enterprise Extensions ### Goal Add richer commercial features after Phase 1 is stable and tested. ### In Scope - customer groups - price lists - promotions - free issue schemes - bundle sales templates - bundle sales documents - reservations - payment allocation - approval flow - advanced reporting dimensions ### Phase 2 Entity Additions #### `CustomerGroup` Used only if group-level pricing or segmentation is needed. #### `PriceList` Customer, group, warehouse, or global price policies. #### `PriceListItem` Per-item pricing rows inside a price list. #### `Promotion` Promotional header. #### `PromotionRule` Buy-X-get-Y, discount, or reward rules. #### `BundleSaleTemplate` Fixed bundle composition definition. #### `BundleSaleTemplateLine` Component stock items and quantities inside a bundle template. #### `BundleSale` Posted or draft bundle sale header. #### `BundleSaleLine` Component lines expanded from a bundle template. #### `FreeIssueScheme` Separate free issue header. #### `FreeIssueSchemeLine` Rule lines for free issue behavior. #### `SalesReservation` Stock reservation header for B2B order fulfillment. #### `SalesReservationLine` Reserved item quantities. #### `SalesPayment` Payment header for cash or credit settlement. #### `SalesPaymentAllocation` Allocation of a payment across invoices. ### Phase 2 API Routes - `GET /api/v1/customer-groups` - `POST /api/v1/customer-groups` - `PUT /api/v1/customer-groups/{id}` - `PATCH /api/v1/customer-groups/{id}/status` - `GET /api/v1/price-lists` - `POST /api/v1/price-lists` - `PUT /api/v1/price-lists/{id}` - `PATCH /api/v1/price-lists/{id}/status` - `GET /api/v1/price-lists/{id}/items` - `PUT /api/v1/price-lists/{id}/items` - `GET /api/v1/pricing/resolve` - `GET /api/v1/promotions` - `POST /api/v1/promotions` - `PUT /api/v1/promotions/{id}` - `PATCH /api/v1/promotions/{id}/status` - `GET /api/v1/free-issue-schemes` - `POST /api/v1/free-issue-schemes` - `PUT /api/v1/free-issue-schemes/{id}` - `PATCH /api/v1/free-issue-schemes/{id}/status` - `POST /api/v1/sales-orders` - `POST /api/v1/sales-orders/{id}/reserve` - `POST /api/v1/sales-orders/{id}/confirm` - `POST /api/v1/sales-payments` - `POST /api/v1/sales-payments/{id}/allocate` - extended reporting routes for channel, cashier, tax, and credit views ### Phase 2 Folder / Module Plan - extend the same sales folders rather than creating a separate module - add new entities and DTOs under the same sales namespace - add new service interfaces and service implementations next to Phase 1 sales services - add new controllers only for the advanced routes - add migrations incrementally so Phase 1 tables remain stable ### Phase 2 Implementation Order 1. customer groups 2. price lists 3. promotions and free issue schemes 4. sales orders and reservations 5. payments and allocations 6. advanced reports 7. permissions and approval workflow --- ## Test Plan - Verify customer CRUD with ETag concurrency and status changes. - Verify invoice and slip create/update/post flows. - Verify fixed sale price fallback works. - Verify GRN-derived fallback uses weighted average. - Verify discounts calculate correctly by percentage and fixed value. - Verify free issue lines post stock and appear in reports. - Verify stock ledger entries are created once per posted document. - Verify the frontend sales hub and sidebar only expose invoice, slip, and free-issue entry points while report pages remain reachable directly. - Verify Phase 1 routes remain stable before Phase 2 is added. ## Assumptions - Phase 1 is intentionally minimal and should not include customer groups or price lists. - `SalesInvoice` is the primary posted sales document. - `SalesSlip` is a simplified retail document. - Returns are deferred to a later step. - Existing ERP patterns must be preserved: repository, unit of work, audit, ETag, and FIFO stock posting.