Spec-ul listează invalid-argument: outlet missing ca eroare frecventă pentru submitOrder(). Designul îl elimină la nivel de UX:
1. Outletul implicit e pre-selectat (configurat de owner, vine din outlets where isDefault). 2. Banner sticky sus, vizibil mereu, niciodată ascuns în sheet. 3. Submit dezactivat cu copy inline dacă outletul lipsește, în loc de toast pop-up după click.
Layer 2 — salvare zero outlets (Frame 4). Retailerii vechi (claim făcut înainte ca tranzacția să creeze stub) sau cei cu toate locațiile arhivate pot ajunge totuși în coș fără outlet. ChangeOutletSheet livrează atunci un EmptyHero in-sheet pentru owner/admin (CTA „Adaugă locație" — modal-ul de creare e ridicat la CartScreen pentru a evita Modal nested), iar pentru worker rămâne copy-ul existent „Cere proprietarului…" (worker-ii nu mai ajung aici post-parallel-work, dar variant-ul rămâne defensiv).
Totalurile folosesc exact aritmetica computeOrderTotals: pure-integer (bani × milliQty), TVA per rată, fără floating point.
setDraftLineQuantity({retailerIdno, draftId, productId, quantityMilli}) (scriere directă în orderDrafts/{id}/lines/{productId}, cantitate absolută) ·
Linie − / 0 → removeDraftLine({retailerIdno, draftId, productId}) ·
Schimbă outlet → setDraftOutlet({draftId, outletId}) ·
Trimite → submitOrder({draftId}) · tranzacție unică · rezervă stoc + creează orders/{id} + șterge draft + scrie primul events/ ·
Plafon stoc (UX): + dezactivat la stockOnHandMilli − stockReservedMilli; linia peste plafon (stoc scăzut între timp) folosește tratamentul .line.err proactiv. submitOrder rămâne garda autoritativă.
Banner outlet sus (ink — atrage ochiul). Linii cu stepper. Totaluri pe signature card. CTA "Trimite comanda" mare jos, cu suma vizibilă.
Worker poate trimite, dar nu poate schimba outletul (rolul organization.edit aparține doar proprietarului). Banner-ul devine surface-2 cu copy explicativ, nu un toast frustrant.
Eroarea apare pe linia afectată, nu ca toast. Submit-ul devine gri cu helper explicativ. Banner sus dacă link-ul e între timp pus pe pauză de distribuitor.
Banner outlet în stare „nesetată" (border accent + CTA „Schimbă"). User tap → sheet se deschide → 0 locații active. În loc de mesaj-dead-end („adaugă din meniu"), un EmptyHero in-sheet oferă o singură acțiune: „Adaugă locație". Modal-ul de creare e mountat la nivel de CartScreen, deci sheet-ul curent se închide înainte ca CreateOutletSheet să se deschidă — fără Modal nested.
Adaugă o adresă de livrare ca să poți trimite comanda către distribuitor.
Plafonul apare în coș, nu la trimitere. Linia care a atins stocul are + dezactivat cu motiv inline. Dacă stocul a scăzut sub cantitatea deja din coș, linia trece în tratamentul roșu existent cu acțiunea de reducere — toate înainte de a apăsa Trimite.
stockOnHandMilli − stockReservedMilli din useProducts (deja încărcat în CartScreen)
+ off când: quantityMilli + step > available
Over-cap: quantityMilli > available → tratament .line.err proactiv
Submit: rămâne garda autoritativă submitOrder (Phone 3 = varianta eroare la trimitere)