feat: implement business pricing service and money utilities
- Added `businessPricing.service.js` to handle business customer pricing logic. - Introduced `money.js` for money parsing, formatting, and calculations. - Created `pricing.service.js` to manage variant quoting and promotion application. - Updated validation schemas in `catalogue.schemas.js` and `inventoryMerchandising.schemas.js` for new pricing and inventory features. - Implemented cron job for inventory reservation expiry in `inventoryReservationExpiry.cron.js`. - Created database migrations for catalogue and inventory structures. - Added unit tests for catalogue localization, validation, and inventory merchandising functionalities.
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
# ZUMRI Phase 5 Inventory and Merchandising
|
||||
|
||||
## Objective
|
||||
Provide authoritative multi-warehouse stock, reservation primitives, wholesale pricing, price quotes, promotions, coupons, and scheduled localized banners for Phase 6 consumers.
|
||||
|
||||
## Existing Components Reused
|
||||
ProductVariant, BusinessCustomer, Upload, authorization middleware, audit queue, cron bootstrap, Zod, Sequelize, and catalogue localization are reused.
|
||||
|
||||
## Inventory Architecture
|
||||
Catalogue never stores stock. `InventoryBalance` is authoritative per warehouse/variant; availability is `onHand - reserved`. All writes pass through one service and append an immutable ledger event.
|
||||
|
||||
## Warehouses
|
||||
Multiple active/inactive warehouses and a transactionally selected default are supported. Default warehouse selection is deterministic.
|
||||
|
||||
## Inventory Balance
|
||||
Quantities are integers. Service invariants prevent negative stock and `reserved > onHand`.
|
||||
|
||||
## Inventory Ledger
|
||||
Every adjustment, reservation lifecycle event, and transfer has a unique event ID. There is no ledger update API.
|
||||
|
||||
## Stock Adjustments
|
||||
Adjustments lock balances, enforce invariants, append a ledger row, support HTTP idempotency keys, and emit audit events.
|
||||
|
||||
## Transfers
|
||||
Synchronous transfers lock warehouse IDs in sorted order, then create paired OUT/IN ledger rows.
|
||||
|
||||
## Reservation Architecture
|
||||
Unique reservation keys make reserve/release/consume idempotent. Generic references avoid premature cart/order coupling.
|
||||
|
||||
## Reservation Concurrency
|
||||
MySQL transactions and row-level `FOR UPDATE` locks serialize competitors for final units. Real multi-connection InnoDB validation remains a staging prerequisite.
|
||||
|
||||
## Reservation Expiry
|
||||
A no-overlap minute cron reconciles up to 100 expired ACTIVE records per run; each record is locked and the database state is authoritative.
|
||||
|
||||
## Availability Projection
|
||||
Public responses expose status and boolean sale availability only. Out-of-stock catalogue items remain visible.
|
||||
|
||||
## Low Stock
|
||||
Low stock is centrally defined as available quantity less than or equal to the configured balance threshold.
|
||||
|
||||
## Business Pricing
|
||||
Wholesale rules remain separate from retail base price and are limited to approved ACTIVE business accounts.
|
||||
|
||||
## Business Tiers
|
||||
Business customers may reference an active tier; customer-specific negotiated rules are also supported.
|
||||
|
||||
## MOQ
|
||||
MOQ belongs to a wholesale rule and never applies to retail fallback.
|
||||
|
||||
## Volume Pricing
|
||||
Integer, non-overlapping ranges select the greatest eligible minimum quantity; final maximum may be null.
|
||||
|
||||
## Pricing Precedence
|
||||
Retail -> eligible customer override (preferred over tier) -> volume tier -> highest-priority automatic promotion -> permitted coupon. Effective price floors at zero.
|
||||
|
||||
## Promotions
|
||||
Percentage, fixed amount, and fixed price campaigns support dates, priority, minimum quantity, audiences, stacking, and normalized targets.
|
||||
|
||||
## Coupons
|
||||
Codes are uppercase and validated against coupon/promotion state and schedule. Redemption counters are not fabricated before orders.
|
||||
|
||||
## Banners
|
||||
Placements are string-configurable. Status, schedule, sort order, audience, and optional business tier drive projection.
|
||||
|
||||
## Localization
|
||||
Banner translations use `en`, `si`, and `ta`, with requested locale then English fallback.
|
||||
|
||||
## Permissions
|
||||
Inventory read/adjust/transfer/reservation/warehouse, business pricing read/manage, promotion read/manage, and banner manage permissions were added.
|
||||
|
||||
## Audit Events
|
||||
Warehouse, adjustment, transfer, business-price, promotion, coupon, and banner mutations enqueue sanitized Phase 2 audit activities.
|
||||
|
||||
## Database Changes
|
||||
The forward-only `20260903050000` migration adds 13 domain tables and the BusinessCustomer tier reference. Earlier migrations are unchanged.
|
||||
|
||||
## Tests
|
||||
Unit coverage verifies decimal precision, zero floor, availability states, strict input, coupon normalization, and permission boundaries. Existing regression tests remain green.
|
||||
|
||||
## Remaining Known Issues
|
||||
Patch endpoints for promotion/coupon/banner/business pricing and real infrastructure integration tests remain follow-up hardening. No production migration was run.
|
||||
|
||||
## Phase 6 Prerequisites
|
||||
Run legacy-data prechecks and migration on staging; validate constraints and concurrent reservations using two real InnoDB connections; verify cron in a multi-instance deployment; seed permissions and warehouse data.
|
||||
Reference in New Issue
Block a user