# Canonical Data Contract

## Decimals
- Money: `DECIMAL(19,6)`.
- Quantity: `DECIMAL(18,4)`.
- Tax percentages: `DECIMAL(9,6)`.
- API sends decimals as JSON **strings**, e.g. `"12.500"`, not binary floats.
- `currency_precision` controls UI/report rounding; KWD defaults to 3.

## Time
- Store timestamps in UTC.
- API uses ISO-8601 UTC.
- Business-day reports convert using business/outlet timezone.
- Date-only values such as expiry dates remain date-only.

## Transaction invariants
A posted sale atomically creates/updates sale header/items, payments, inventory movement/balance, batch allocation, FIFO/COGS, customer ledger if needed, cash/financial-account movement and audit log. Any failure rolls everything back.

PO/GRN/purchase posting atomically updates PO receipt progress, stock, batch data, FIFO cost layers, supplier ledger and payment/account effects.

Returns never delete original sales. They create return/reversal records and compensating ledger/stock/cost effects.

## Inventory
`inventory_movements` is the permanent stock ledger. `stock_balances` and `batch_stock_balances` are materialized current balances updated in the same transaction as each movement. Application code must never modify a stock balance without a matching movement.

## FIFO
Incoming inventory creates `inventory_cost_layers`. Sales consume open layers oldest-first under row locks; `sale_cost_allocations` permanently records the layer allocation. This prevents two concurrent online sales from consuming the same layer quantity.

## Optimistic concurrency
Products, customers and suppliers carry `version_no`. Update requests include the version read by the client. A stale update returns HTTP 409 `VERSION_CONFLICT`.

## Deletion
Historically referenced master records are soft-deleted/deactivated. Posted transactional documents are voided/canceled/reversed, never physically deleted.

## Tenant rule
Every referenced outlet, location, product, batch, customer, supplier, role and staff member must resolve to the active business. Valid numeric foreign keys alone are never sufficient authorization.

## Default document formats
Generated under `SELECT ... FOR UPDATE`:
- `SAL-YYYYMM-######`
- `RET-YYYYMM-######`
- `PUR-YYYYMM-######`
- `PO-YYYYMM-######`
- `GRN-YYYYMM-######`
- `STR-YYYYMM-######`
- `ADJ-YYYYMM-######`
- `STK-YYYYMM-######`
- SaaS billing: `SUB-YYYYMM-######`

The sequence implementation is configurable later without changing resource IDs.
