SDIApp Phase 7 is fundamentally two-sided commerce. A retailer's action becomes a distributor's notification. A distributor's confirmation becomes a retailer's timeline entry. If the two halves don't feel like one system, the design fails — no matter how polished individual screens look.
This page reads left-to-right across one complete transaction. The distributor's phone is the top lane; the retailer's phone is the bottom lane. Each step calls out the Cloud Function that fires and the data that crosses the boundary. The downstream surface mocks (catalog, cart, order detail, invoice) are deep dives into individual frames you see here.
Story-ul de mai jos începe cu un retailer și un distribuitor deja legați. În realitatea B2B FMCG moldovenească, această legătură nu apare prin retailer pulling — apare aproape întotdeauna prin agentul distribuitorului care vizitează magazinul și apelează inviteExistingRetailerToLink (dacă magazinul are deja cont) sau inviteRetailerToOnboard (dacă magazinul e nou — atomic creează cont + link prin claim-ul retailerului). Vezi vizite.html pentru fluxul agentului și link-inbox.html pentru ce vede fiecare parte după acel moment fizic.
Phase 3 a livrat două callable-uri distincte ce acoperă această premisă: inviteExistingRetailerToLink (retailerul are deja cont) și pereche inviteRetailerToOnboard + claimRetailerOnboardingInvitation (retailer nou — bootstrap atomic org + link). UI-ul le ascunde sub un singur flux de agent — vezi vizite.html.
Tip: scroll horizontally below. The strip is 11 steps wide.
inventoryLedger.orderDrafts). Distribuitorul nu vede draft.shipOrder(): rezervarea → realizare (stoc-pe-mână scade, stoc-rezervat scade). O singură tranzacție.invoiced în settled în aceeași tranzacție.| Pas | Cloud Function | Stare comandă | Date scrise / efect |
|---|---|---|---|
| 1 | createProduct() | — | Produs + ledger inițial (reason: restock). |
| 2 | — (listener) | — | Snapshot Firestore — retailerul primește produsul fără refresh. |
| 3 | addDraftLine() | — | Draft retailer-privat. Distribuitorul nu vede. |
| 4 | submitOrder() | [no doc] → submitted | Snapshot linii + totaluri; stoc rezervat per linie; draft șters; primul events/. |
| 5 | confirmOrder() | submitted → confirmed | Eveniment "confirmed" + actor. |
| 6 | packOrder() · shipOrder() | confirmed → packed → shipped | La shipOrder: rezervare → realizare (stockOnHand −= qty). |
| 7 | deliverOrder() | shipped → delivered | Anularea retailerului devine indisponibilă (vizibil în UI prin lipsa CTA). |
| 8 | issueInvoice() + renderInvoicePdf() | delivered → invoiced | Counter atomic per (distribuitor, serie); doc fiscal imutabil; PDF render queued. |
| 9 | recordPayment() | invoiced → settled | Plată în sub-colecție; dacă cumulul ≥ total, comanda trece la settled. |