Stock
Global or per-warehouse stock, physical, reserved and available quantities, reservation and deduction by order status with automatic restock on cancellation and return, the movement ledger, alerts, inventories, availability-only display and overrides.
This area controls commercial availability (cahier §3, §4.1, §4.3, §8, §11, §12): how much stock HOCO has, how orders reserve and consume it, how cancellations and returns put it back, how every variation is journaled, how the stock team is alerted and counts, and what resellers may see — availability only, never a quantity (decision of 17 Oct 2026). The server's inventory module owns Warehouse, StockLevel, StockMovement, InventorySession and InventoryLine; the orders module triggers reservations and deductions through the order lifecycle. Back-office operations are on API › Stock; stock effects of order statuses on Lifecycles › Stock effects.
| ID | Requirement | Release | Priority | Source | Apps |
|---|---|---|---|---|---|
STK-01 | Global stock or stock per warehouse | V1 | MUST | §4.3 | API · BO |
STK-02 | Physical, reserved and available stock (available = physical − reserved) with alert thresholds | V1 | MUST | §4.3, §8 | API · BO |
STK-03 | Reservation and deduction triggered by configurable order statuses; cancellations and returns put quantities back automatically | V1 | MUST | §8, §12 | API |
STK-04 | Every stock variation is journaled (movement ledger) | V1 | MUST | §4.3 | API · BO |
STK-05 | Low-stock and out-of-stock alerts | V1 | MUST | §4.1, §4.3 | API · BO |
STK-06 | Inventory sessions and adjustments with reasons | V1 | SHOULD | §3 | API · BO |
STK-07 | Resellers and guests only ever see availability (in stock / out of stock) — never quantities | V1 | MUST | Team decision (17 Oct 2026), §4.3 | API · RA · RW |
STK-08 | No order validated beyond available stock unless an authorised override is recorded | V1 | MUST | §4.3 | API · BO |
STK-09 | Synchronisation with the client's existing stock system (V1 import, V2 API) | V2 | SHOULD | §11 | API |
Settings used in this area
| Setting | Default | Meaning |
|---|---|---|
stock.multiWarehouse | false | false: one sellable warehouse (Dépôt principal); true: stock per warehouse (D-02). |
stock.reserveOn | VALIDATED | Order status at which quantities are reserved: SUBMITTED or VALIDATED (D-22). |
stock.deductOn | SHIPPED | Order status at which quantities leave physical stock: READY, SHIPPED or DELIVERED. |
stock.lowThresholdDefault | 10 | Alert threshold of a variant without its own threshold. |
Requirements
STK-01 — Global stock or stock per warehouse
Statement — The system shall keep stock per variant and per warehouse, and work either with one sellable warehouse (global stock) or with several.
Rules
- A
Warehousehas a code (WH-+ 2–10 capitals or digits), a name, a commune, an active flag, a sellable flag and a default flag; exactly one warehouse is the default. The seed creates Dépôt principal (WH-MAIN). StockLevelholds, per variant and warehouse,physical,reservedand an optional alert threshold; available = physical − reserved (STK-02). A new variant gets rows at 0 in every active warehouse.- With
stock.multiWarehouse = false(default) only one warehouse may be sellable (422 MULTI_WAREHOUSE_DISABLEDotherwise) and the warehouse picker is hidden in the back-office. Withtrue, reseller availability is computed over all sellable warehouses and an order is fulfilled from one warehouse chosen at confirmation (the default one unless changed). - Warehouses are managed in BO-37 (
stock.administer); deactivating one with physical stock, reservations or an open inventory session, or removing the only default, answers409 RESOURCE_IN_USE. - Transfers between warehouses (
TRANSFER_OUT/TRANSFER_IN) exist in the ledger but have no V1 operation; they come with multi-warehouse use if HOCO needs it.
Acceptance criteria
- After the seed, BO-33 shows one warehouse and no warehouse picker.
- Making a second warehouse sellable with
stock.multiWarehouse = falsereturns422 MULTI_WAREHOUSE_DISABLED. - With two sellable warehouses holding 4 and 6 available units of a variant, the reseller sees En stock and an order of 8 is fulfilled from the warehouse chosen at confirmation (stock control per that warehouse).
- Deactivating a warehouse that still holds stock returns
409 RESOURCE_IN_USE.
Data — Warehouse, StockLevel.
Interfaces
- Screens: BO-33 Stock overview · BO-37 Warehouses
- API:
admin.stock.levelsGET /admin/stock·admin.warehouses.crudGET · POST · PATCH /admin/warehouses[/{warehouseId}]
Notes / open points — One depot or several is D-02 (default: one).
STK-02 — Physical, reserved and available stock with alert thresholds
Statement — The system shall track physical, reserved and available stock (available = physical − reserved) for every variant and warehouse, and classify each level against an alert threshold.
Rules
physicalis what is on the shelf;reservedis what confirmed orders hold before deduction (STK-03);available = physical − reservedis what can still be sold. Every control — cart, checkout, confirmation — usesavailable(cahier §8).- Alert level per level:
OUTwhen available ≤ 0,LOWwhen 0 < available ≤ threshold,OKabove. The threshold is the level's own value, else the variant's, elsestock.lowThresholdDefault. Thresholds are set in bulk (admin.stock.thresholds, at most 1 000 pairs per call,stock.update); they move no stock. - Available may become negative only through an authorised over-stock confirmation (STK-08) or an inventory count below reservations; BO-33 then lists the shortage and the orders to arbitrate. Physical never becomes negative through an adjustment (
422 STOCK_BELOW_RESERVEDwhen a manual adjustment would put physical under reserved). - BO-33 shows per variant and warehouse: physical, reserved, available, threshold, alert level and — with
pricing.view— stock value at purchase price; filters by brand, category, alert level and warehouse. - These quantities are back-office data only (STK-07).
Acceptance criteria
- With physical 100 and 20 reserved, BO-33 shows available 80; confirming an order of 30 shows reserved 50 and available 50.
- With threshold 10, available 8 shows
LOW; available 0 showsOUT. - An adjustment to physical 40 while 50 are reserved returns
422 STOCK_BELOW_RESERVED. - A stock manager without
pricing.viewsees no stock value column.
Data — StockLevel, ProductVariant (default threshold), Warehouse.
Interfaces
- Screens: BO-33 Stock overview
- API:
admin.stock.levelsGET /admin/stock·admin.stock.thresholdsPUT /admin/stock/thresholds
STK-03 — Reservation and deduction by order status; automatic restock on cancellation and return
Statement — The system shall reserve stock when an order reaches the configured reservation status, deduct it when the order reaches the configured deduction status, and put quantities back automatically when an order is refused, cancelled, expired or returned.
Rules
-
Defaults: reserve at
VALIDATED— the back-office confirmation (ORD-14) — and deduct atSHIPPED.stock.reserveOnacceptsSUBMITTEDorVALIDATED;stock.deductOnacceptsREADY,SHIPPEDorDELIVERED, always later than the reservation. -
Crossing rule: a movement happens when an order reaches or passes its status, exactly once — a pickup order going from
READYtoDELIVEREDstill gets its deduction. -
Movements (each one a
StockMovementwith the order as reference, in the transaction of the order change):Event Movement Effect Order reaches reserveOnRESERVATIONper linereserved + q Line modified on a reserved order RESERVATIONorRESERVATION_RELEASEfor the differencereserved ± Δ Order reaches deductOnSHIPMENTper linephysical − q, reserved − q (the reservation is consumed) Order refused, cancelled or assisted order expired, before deduction RESERVATION_RELEASEfor what is reservedreserved − q Order cancelled after a deduction (possible only with deductOn = READY)RETURNfor what was deductedphysical + q Order refused at the door ( SHIPPED → RETURNED)a return in transit is created; its reception posts RETURNphysical + accepted q Return reception (ORD-13) RETURNper item for the restockable quantityphysical + restockQty -
Automatic means automatic: nobody re-enters stock by hand after a cancellation or a return; defective or damaged returned units are recorded on the return but not re-entered as sellable stock.
-
Concurrency: a reservation runs under a row lock on the
StockLevel(or an atomic conditional update) so two confirmations cannot both take the last units; the loser gets422 STOCK_INSUFFICIENTunless an override is recorded (STK-08). -
Changing
stock.reserveOnorstock.deductOnapplies to orders that reach a status after the change; existing reservations and deductions are not replayed. -
Every crossing of the zero line recomputes product availability and status (
OUT_OF_STOCK↔ACTIVE, CAT-04) and the alert level (STK-05).
Example with the defaults — HOCO X89 câble USB-C, physical 100, reserved 20, available 80. An order of 30 is confirmed → reserved 50, available 50. It ships → physical 70, reserved 20, available 50. Had it been cancelled while READY → reserved 20, available 80. If 10 cables come back later and 8 are accepted as sellable at reception → physical 78, available 58.
Acceptance criteria
- With the defaults,
cart.checkoutcreates no stock movement;admin.orders.validatecreates oneRESERVATIONper line. - Moving the order to
SHIPPEDcreatesSHIPMENTmovements and lowers physical and reserved by the line quantities. - Cancelling a
READYorder releases its reservations in the same transaction, with no manual step; the movements reference the order number. - Receiving a return with 8 sellable units out of 10 creates one
RETURNmovement of +8; the 2 damaged units do not change stock. - Two users confirming two orders for the last 5 units at the same second: one succeeds, the other gets
422 STOCK_INSUFFICIENT. - With
stock.reserveOn = SUBMITTED, checkout reserves and a refusal releases.
Data — StockLevel, StockMovement, Order, OrderItem, ReturnRequest, ReturnItem.
Interfaces
- Screens: BO-69 Returns & after-sales claims
- API:
cart.checkoutPOST /cart/checkout·admin.returns.receivePOST /admin/returns/{returnId}/reception
Notes / open points — Reservation and deduction statuses are D-22 (default: reserve at confirmation, deduct at shipping).
STK-04 — Movement ledger
Statement — The system shall record every variation of physical or reserved stock as an immutable movement, so that every stock level can be explained by its history.
Rules
- A
StockMovementhas: time, type (StockMovementType), variant, warehouse, signed physical change, signed reserved change, the physical, reserved and available quantities after the movement, a reference (order, receipt, adjustment, inventory, return or import, with its number), a reason when required, the unit cost for receipts (visible withpricing.view), the actor (or system) and the client app. - A
StockLevelis never written without its movement, in the same transaction; movements are append-only — a correction is a new movement. - Manual movements: goods receipts (
admin.stock.receipt,stock.create: warehouse, lines, supplier reference, reception date, optional unit cost) →RECEIPT; adjustments (admin.stock.adjust,stock.update, reason 3–500 characters required) →ADJUSTMENT; inventory validation →INVENTORY(STK-06). Every other movement is posted automatically (STK-03). - A nightly check compares every level with the sum of its movements and alerts the stock manager on any drift.
- The journal (BO-34,
admin.stock.movements) filters by variant, product, warehouse, type, date range, actor and reference number, and shows each row's balances after the movement.
| Value | UI label (fr) | Meaning |
|---|---|---|
RECEIPT | Réception | |
ADJUSTMENT | Ajustement | |
INVENTORY | Inventaire | |
RESERVATION | Réservation | |
RESERVATION_RELEASE | Libération de réservation | |
SHIPMENT | Sortie (expédition) | |
RETURN | Retour | |
TRANSFER_OUT | Transfert sortant | |
TRANSFER_IN | Transfert entrant |
Acceptance criteria
- After a receipt of 200 X14 cables with supplier reference FRN-8812, BO-34 shows one
RECEIPTof +200 with physical after, the actor and the reference. - An adjustment without reason returns
422 REASON_REQUIRED. - Filtering BO-34 on
CMD-2026-000158shows its reservation, shipment and any release. - The nightly check reports no drift on a database where levels equal the sum of movements, and raises an alert after a level is altered directly in the database.
Data — StockMovement, StockLevel, AuditLog.
Interfaces
- Screens: BO-34 Stock movements journal
- API:
admin.stock.movementsGET /admin/stock/movements·admin.stock.receiptPOST /admin/stock/receipts
STK-05 — Low-stock and out-of-stock alerts
Statement — The system shall alert the stock team when a variant's available stock falls to or under its threshold and when it runs out, and show the alerts on the stock dashboard.
Rules
- An alert is raised when a level's alert level changes to
LOWorOUT, and cleared when it goes back toOK; stayingLOWraises nothing new, so a variant never alerts twice for the same state. - Channels: live event
stock.alertto back-office users holdingstock.view(BO-07, BO-33 badges); in-app notificationsstock.lowandstock.outto stock managers; a daily e-mail digest of current alerts (notifications matrix). - BO-07 (stock dashboard) and the general dashboard (ANA-01) show the counts of
LOWandOUTvariants and the shortages created by overrides; BO-33 filters by alert level. - Out-of-stock also changes what resellers see — En rupture — and may set the product
OUT_OF_STOCK(CAT-04); it never sends anything to resellers by itself.
Acceptance criteria
- When a confirmation brings a variant from 12 to 8 available with threshold 10, stock managers get one
stock.lownotification and BO-33 shows it under Stock faible; a further confirmation to 5 raises nothing new. - At 0 available,
stock.outis raised and the product shows En rupture to resellers. - A receipt bringing it back above 10 clears the alert from the dashboard.
Data — StockLevel (alert level), Notification, OutboxEvent.
Interfaces
- Screens: BO-07 Stock dashboard · BO-33 Stock overview
- API:
admin.stock.levelsGET /admin/stock
STK-06 — Inventory sessions and adjustments with reasons
Statement — The system should let the stock team count stock in inventory sessions, review the differences and post them as journaled corrections, and adjust stock outside inventories with a reason.
Rules
- A session (
InventorySession) targets one warehouse with a scope: all variants, some categories or brands, or a list of variants. TwoOPENorIN_REVIEWsessions of a warehouse cannot overlap (409 INVENTORY_SESSION_CONFLICT); a variant in an open session cannot be adjusted outside it (same code). - Counting (
OPEN): counters enter quantities per variant or by barcode scan (InventoryLine); a variant outside the scope answers422 VARIANT_NOT_IN_SCOPE. Orders keep shipping during the count. - Review (
IN_REVIEW): each line shows expected, counted and difference, where the difference is computed ascounted − (expected at opening + movements since opening), so shipments during the count are not counted twice. Recounts need a reason. - Validation (
admin.inventory.validate,stock.validate): posts oneINVENTORYmovement per non-zero difference, all or nothing; uncounted lines are either rejected (422 INVENTORY_LINES_INCOMPLETE) or treated as unchanged, as chosen at validation.VALIDATEDandCANCELLEDare final (409 INVENTORY_TRANSITION_NOT_ALLOWED); a wrong count is corrected by a new session. A count below reservations is accepted and shown as a shortage. - Adjustments outside sessions follow STK-04 R3 (reason required, free text in V1).
- Validation recomputes alert levels and product statuses and is journaled (
VALIDATE) with a summary of differences.
Acceptance criteria
- A session opened with 166 expected, 6 shipped during the count and 157 counted posts an
INVENTORYmovement of −3 at validation (physical 157). - Opening a second session on the same warehouse and category returns
409 INVENTORY_SESSION_CONFLICT. - Validating with 4 uncounted lines and the reject option returns
422 INVENTORY_LINES_INCOMPLETEwithuncounted: 4. - A stock manager without
stock.validatecannot validate (403 PERMISSION_DENIED).
Data — InventorySession, InventoryLine, StockMovement, StockLevel, AuditLog.
Interfaces
- Screens: BO-35 Stock receipt / adjustment · BO-36 Inventory sessions
- API:
admin.stock.adjustPOST /admin/stock/adjustments·admin.inventory.crudGET · POST · PUT /admin/inventory-sessions[/{sessionId}[/lines]]·admin.inventory.validatePOST /admin/inventory-sessions/{sessionId}/validate
STK-07 — Availability only for resellers and guests
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V1 | MUST | Team decision (17 Oct 2026), §4.3 | API · RA · RW | SRV-13 · MRA-02 |
Statement — The system shall show resellers, guests and delegates only whether a product is available — En stock or En rupture — and never a stock quantity, a threshold or a remaining-units hint.
Rules
- A variant is
IN_STOCKwhen its available stock over the sellable warehouses is above 0, otherwiseOUT_OF_STOCK(StockAvailability); a product isOUT_OF_STOCKonly when every visible variant is. - No reseller-, guest- or delegate-facing response carries
physical,reserved,available, a threshold oravailableQty; the « Plus que N pièces » hint of the reference mock-up is not implemented; there is no stock display setting. - When a requested quantity exceeds what is available, the cart refuses it with
422 STOCK_INSUFFICIENT(details.availabilityonly) and never suggests the available quantity, which would reveal it (ORD-02). - Availability is recomputed on every read (no cross-caller cache) and filters on it (
availability=IN_STOCK) are available to everyone (CAT-12). - Exact quantities exist only in the back-office, for users holding
stock.view.
| Value | UI label (fr) | Meaning |
|---|---|---|
IN_STOCK | En stock | Available quantity greater than 0. |
OUT_OF_STOCK | En rupture | Available quantity is 0. |
Acceptance criteria
catalog.products.get,cart.get,delegate.assisted.updateandcatalog.facetsresponses contain no stock number (contract test on every reseller and delegate schema).- With 3 units available, adding 5 to the cart returns
422 STOCK_INSUFFICIENTwhose body contains no3. - A product with one variant in stock and one out of stock shows En stock on its card and En rupture on the out-of-stock variant.
Data — StockLevel (read), ProductVariant.
Interfaces
- Screens: RA-12 Catalog · RA-14 Product detail
- API:
catalog.products.listGET /catalog/products·catalog.products.getGET /catalog/products/{productId}
STK-08 — No confirmation beyond available stock without an authorised override
Statement — The system shall refuse to confirm an order whose lines exceed available stock unless a user holding stock.validate records an explicit override with a reason.
Rules
- The stock control of an order compares each line with available stock (excluding the order's own reservation) in its fulfilment warehouse; it is re-run on every read of an order waiting for confirmation and inside the confirmation transaction (ORD-05).
admin.orders.validateon an order with an insufficient line answers422 STOCK_INSUFFICIENTwith the lines (requested, available, short by) unless the request carriesoverride.stock = trueand a reason (3–500 characters,422 REASON_REQUIREDotherwise) and the caller holdsstock.validate(403 PERMISSION_DENIEDwithdetails.requiredotherwise).- With an override the reservation is made anyway: available becomes negative, a
stock.alertfollows and the shortage appears on the stock dashboard until goods arrive. - The override is stored on the order (kind
STOCK, reason, user, time) and journaled as anOVERRIDEentry next to theVALIDATEentry (AUD-01). - The alternative to an override is a controlled modification of the order (reduce the line — ORD-05) or a refusal.
Acceptance criteria
- Confirming an order of 10 units with 6 available returns
422 STOCK_INSUFFICIENTwithshortBy: 4. - The same confirmation by a commercial administrator without
stock.validateandoverride.stock = truereturns403 PERMISSION_DENIED. - A stock manager confirming with
override.stock = trueand a reason succeeds; reserved rises by 10, available shows −4 in BO-33 and the activity log has anOVERRIDEentry with the reason.
Data — Order (controls, overrides), StockLevel, StockMovement, AuditLog.
Interfaces
- Screens: BO-39 Order detail (controls, actions, history)
- API:
admin.orders.validatePOST /admin/orders/{orderId}/validate
STK-09 — Synchronisation with HOCO's stock system
| Release | Priority | Source | Apps | Owner tasks |
|---|---|---|---|---|
| V2 | SHOULD | cahier §11 | API | SRV-37 |
Statement — The system should synchronise stock with HOCO's existing stock system — by file import in V1 and by API in V2.
Rules
- V1: stock levels are loaded and corrected through the stock import of ADM-06 — mode
SET(postsADJUSTMENTmovements to reach the file's quantity) orADD(postsRECEIPTmovements) — with a dry run and a report. - V2: an API synchronisation agreed with HOCO's system — which side is master of physical stock, how often, how conflicts are resolved; reservations always stay in the HOCO platform.
- Every synchronised change is a journaled movement with reference
IMPORT(or the V2 integration), never a direct write.
Acceptance criteria
- A V1 stock file setting X14 to 250 in
SETmode posts the movement that brings physical to 250 and shows it in BO-34 with referenceIMPORT.
Data — ImportJob, StockMovement, StockLevel.
Interfaces
- Screens: none dedicated
- API: no dedicated operation
Notes / open points — Which system is master at go-live is D-34. V2 work is SRV-37.
Error codes
| Code | HTTP | Raised when |
|---|---|---|
STOCK_INSUFFICIENT | 422 | A cart line, checkout or confirmation exceeds available stock (no quantity in the body for resellers) |
STOCK_BELOW_RESERVED | 422 | A manual adjustment would put physical under reserved |
REASON_REQUIRED | 422 | Adjustment, recount or override without reason |
MULTI_WAREHOUSE_DISABLED | 422 | A second sellable warehouse while stock.multiWarehouse = false |
WAREHOUSE_INACTIVE | 422 | Receipt, adjustment or inventory on an inactive warehouse |
VARIANT_NOT_IN_SCOPE · INVENTORY_LINES_INCOMPLETE | 422 | Counting outside the session scope; validating with uncounted lines |
INVENTORY_SESSION_CONFLICT · INVENTORY_TRANSITION_NOT_ALLOWED | 409 | Overlapping sessions; move not allowed |
RESOURCE_IN_USE | 409 | Deactivating a warehouse in use |
PERMISSION_DENIED | 403 | Override without stock.validate, validation without stock.validate |
Pricing
Three selling prices per variant chosen by the reseller's customer type, purchase and recommended prices with their history, VAT and totals, re-validation at cart and checkout, and the V2 per-reseller agreements.
Cart & orders
The server-side cart and its rules, checkout, the eleven-status order lifecycle with back-office confirmation of every order, processing, history, live tracking, assisted orders, first orders, cancellation, phone orders, documents and returns.