v2.12.0
v2.12.0 — Product lifecycle enforced
Minor release. One additive migration (backfill runs before enforcement starts). ## Operator-facing **DRAFT, ACTIVE, and DISCONTINUED now mean something.** A DRAFT product cannot be sold
Minor release. One additive migration (backfill runs before enforcement starts).
Operator-facing
DRAFT, ACTIVE, and DISCONTINUED now mean something. A DRAFT product cannot be sold — sales order lines refuse with a named reason, and its channel ATP composes 0 with its own named reason (never a fake stockout). DISCONTINUED products sell down remaining stock but refuse new replenishment (PO/ASN lines), while in-flight receipts stay open. Product status is editable on the form with an activation checklist (base UOM + a scannable code), and the catalog gains a status filter so every DRAFT is one view away.
Frozen reasons: PRODUCT_NOT_ACTIVE — {sku} is {status}; activate the SKU before selling · PRODUCT_DISCONTINUED_NO_REPLENISH · PRODUCT_ACTIVATE_INCOMPLETE.
Migration
Backfill: DRAFT products with operational evidence (movements or physical stock) become ACTIVE before enforcement starts — reviewed pre-merge (1 product in prod). Statuses are never demoted.
Rollback
Re-deploy v2.11.1 (old code ignores status entirely; backfilled ACTIVE persists, harmless).