obelaw / ium-sales
Sales & Order Management System (OMS) Domain Module for Obelawium ERP
Requires
- php: ^8.2
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/events: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- illuminate/validation: ^11.0|^12.0|^13.0
- obelaw/ium: *
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^3.0|^4.0
- pestphp/pest-plugin-laravel: ^3.0|^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-28 13:41:08 UTC
README
The Sales & Order Management System (ium-sales) is a headless, domain-driven ERP module within the Obelawium ecosystem. It orchestrates the entire commercial lifecycle of customer demand: omnichannel order capture, quotation conversion, strict price-lock freeze invariants, stock allocation, multi-facility split fulfillment, and Return Merchandise Authorizations (RMA).
1. Architectural Philosophy & Fluent DSL Gateway
ium-sales operates strictly as an autonomous headless business domain. It contains no presentation layers (no HTTP controllers, routes, Blade, Livewire, or Inertia). All operations flow through the unified fluent gateway:
ium() -> sales() -> {service}() -> {method}()
Quick Example
use Obelaw\Ium\Sales\Data\CreateSalesOrderDto; use Obelaw\Ium\Sales\Data\SalesOrderLineDto; // 1. Create a commercial sales order $order = ium()->sales()->orders()->create(new CreateSalesOrderDto( lines: [ new SalesOrderLineDto( sku: 'SRV-001', name: 'Cloud Compute Node', unitPrice: 350.0, quantity: 2.0, discountAmount: 20.0, taxRate: 14.0, ), ], customerType: 'company', customerId: 101, channelCode: 'b2b-portal', currency: 'USD', shippingTotal: 25.0, )); // 2. Confirm order (Validates financial balance and permanently locks prices) $order = ium()->sales()->orders()->confirm($order); // 3. Allocate verified ATP inventory $order = ium()->sales()->fulfillment()->allocate($order, 'WH-CENTRAL'); // 4. Release fulfillment batch to WMS $batch = $order->batches()->first(); ium()->sales()->fulfillment()->releaseBatch($batch);
2. Core Domain Invariants ("The Law")
ium-sales enforces strict non-negotiable domain invariants:
Invariant 1: Order Financial Balance Rule
$$\text{Total Order Value} = \sum(\text{Line Prices} - \text{Discounts}) + \text{Taxes} + \text{Shipping Fees}$$
The order total must reconcile exactly with line amounts, discounts, taxes, and shipping fees. Rounding is calculated with high precision. Corrupted orders fail confirmation with InvalidFinancialBalanceException.
Invariant 2: "The Price-Lock Law" (Pricing Immutability)
Once an order transitions to Confirmed, its unit prices, line discounts, taxes, shipping fees, currency, and line items are permanently frozen. Direct in-place updates or deletions throw OrderPriceLockedException. Adjustments must be performed via formal compensating documents (Supplementary Orders, Credit Notes, or RMAs).
Invariant 3: Stock Allocation Rule
Hard allocation cannot occur without verified Available-To-Promise (ATP) stock at the target sourcing node. Attempting to allocate stock when requested exceeds available throws InsufficientInventoryException.
Invariant 4: Split & Cancellation Governance
Confirmed orders that have entered active warehouse fulfillment (Released, Shipped) cannot be cancelled in place. Attempting to cancel throws CannotCancelReleasedOrderException. Cancellation must follow formal goods interception and RMA/credit compensation. Pre-release cancellation is permitted.
Invariant 5: RMA Quantity Integrity
Return requests cannot exceed eligible returnable quantities:
$$\text{Max Returnable} = \text{Shipped Quantity} - \text{Previously Returned Quantity}$$
Violations throw InvalidRmaQuantityException.
3. Services Overview
| Gateway Method | Service Class | Primary Responsibilities |
|---|---|---|
ium()->sales()->orders() |
OrderService |
Omnichannel order creation, ingestion, confirmation, price-lock, and cancellation governance. |
ium()->sales()->quotations() |
QuotationService |
Drafting, sending, customer acceptance, rejection, expiration, and conversion into orders. |
ium()->sales()->fulfillment() |
FulfillmentService |
ATP allocation, multi-facility split shipment routing, WMS batch release, carrier shipping & delivery tracking. |
ium()->sales()->rmas() |
RmaService |
Return Merchandise Authorizations, goods receipt inspection disposition, and credit compensation. |
ium()->sales()->channels() |
ChannelService |
Omnichannel profiles (ecommerce, mobile, b2b, pos, marketplace). |
ium()->sales()->pricing() |
PricingEngineService |
Financial balance equation validation, discount tiers, and line-level tax calculation. |
ium()->sales()->audit() |
AuditService |
Immutable append-only forensic audit trail for state transitions, price locks, and cancellations. |
ium()->sales()->acl() |
AclService |
Anti-Corruption Layer for inbound payloads (POS/EDI/Carts) and outbound payloads (WMS/TMS/FMS). |
4. Domain Events
All domain events are emitted asynchronously across bounded contexts:
QuotationAccepted: Emitted when a customer accepts a quotation.SalesOrderConfirmed: Emitted when an order is confirmed and its commercial terms are frozen.FulfillmentBatchReleased: Emitted when a batch is released to WMS for picking/packing.OrderSplitted: Emitted when an order is partitioned across multiple fulfillment nodes.OrderCancelled: Emitted when an order is cancelled according to pre-release governance rules.ReturnAuthorized: Emitted when an RMA request is approved.
5. Testing
The module includes a comprehensive PestPHP test suite validating all domain invariants:
composer test