Skip to content

Cropto end-to-end trading flow ​

Scope. Single source of truth for the full trading lifecycle. Begins at the user's Add Product Button entry-point (faithful to your sketch), extends through sell + buy lead posting, e-mandi bid/offer negotiation, acceptance, Order creation, shipment, delivery, and loops back to the buyer's Confirmed Deal Section where the goods become a new batch — at which point the buyer chooses to relist as-is, process into a new product, or sell off-platform. Covers cancellation paths, packing-mode split (Bulk vs Retail), provenance / visibility rules (with role-aware source disclosure), basic accounting, GST display, the printable-document deliverables per lifecycle event, and the dispute-resolution framework deferred to Phase 5+.

Phasing. All design + delivery sits under Phase 1 (1, 1a, 1b, 1c). Phase 2 bundles payments, shipment tracking, full accounting, and tax-invoicing — deliberately scoped outside the Phase 1 family.

Audience. Cropto engineering + product + ops.

Render. All diagrams are Mermaid. Renders inline in GitHub, VS Code, Obsidian, mermaid.live. Export to SVG / PNG by pasting any block into mermaid.live. PDF the whole document via VS Code's "Markdown PDF" extension.

Closed-loop insight. Every accepted Cropto deal that reaches DELIVERED state creates a new batch in the buyer's inventory — which THAT buyer can then (a) relist as-is, (b) transform into a new product, or (c) sell off-platform. Yesterday's buy becomes today's sell — possibly in a different form (raw → roasted) or through a different channel (direct mandi sale outside Cropto). The flow is a graph, not just a circle.


1. Legend ​

   Phase 1   (orders-and-order-ids)              plain box
   Phase 1a  (batch-level-inventory)             yellow tone
   Phase 1b  (lifecycle + receipt + transform)   blue tone
   Phase 1c  (basic accounting + GST view)       green tone
   Phase 2   (payments + shipment + full
              accounting + tax invoicing)        dashed box, ┊P2┊ marker
   Phase 5+  (dispute resolution)                grey, italic

   System action       verb in lowercase, no avatar       "creates"
   User action         role → verb                        Seller→"accepts"
   External trigger    ⏱ timer, ✉ SMS/in-app,            ⏱2h, 📸 evidence
                       📸 evidence, 📄 PDF generated
   Decision diamond    <choice?>
   Closed-loop arrow   dashed feedback                    -.->

2. Master end-to-end flow — the closed loop with three exit ramps ​

Three exit ramps after add-to-inventory. The dashed arrow back to A1 represents the relisting path. The two other exits — Transform and OffSell — are equally first-class: a processor turning raw makhana into roasted is just as valid an outcome as relisting, and a trader selling outside Cropto needs an inventory-decrement path so their stock figures stay honest.


3. Your decisions — captured (canonical) ​

#DecisionResolution
1Order ID formatGlobal opaque alphanumeric. ORD- + 14-char Crockford base32 (excludes I, L, 0, O). Example: ORD-J3F7K9M2P4Q5RT. ~70 bits of entropy → collision-safe; exposes no identity / volume / date.
2Order creation triggerCreated on owner's Accept click (after any negotiation rounds), status = PLACED. Phase 2 inserts PAYMENT_PENDING → PAID states BEFORE PLACED.
3Cancellation windowWithin 2h of acceptance AND before SHIPPED. Either party. Reason required (10+ chars). Stringent; relax later if data shows users genuinely need more.
4Zero-cost batches (own-farm)Default cost basis = ₹0. Optional manual "production cost per kg" field per batch (Phase 1c).
5Provenance chain depthSingle hop only. GI metadata (status + region) follows separately; does NOT include source identity.
6Batch metadata on bid reviewFull metadata to lead owner: Batch ID, source party (role-aware — Decision 20), quality metrics, GI status + region, packing mode + spec.
7One batch backing multiple leadsOne-to-many allowed. Stock-overcommit guard.
8GST mathCalculate-and-display only in Phase 1c. Phase 2 wires full GST collection + remittance.
9PhasingPhase 1 family (1, 1a, 1b, 1c). Phase 2 = payments + shipment + full accounting + tax invoicing.
10Order architectureOption C — Order materialises on acceptance as a new entity. Negotiation domain (Offer / BuyBid) unchanged. Buy Now also links via orderId (Decision 23 below).
11Receipt-confirmation patternConfirmed Deal Section. Per-deal options: Add to Inventory (merge surface) or Cancel (📸 evidence). ⏱14-day timeline.
12Packing modesBulk + Retail both ship in Phase 1a. Same leaf + different packing = separate Product rows.
13Closed-loop inventoryGoods received → buyer's Confirmed Deal Section → buyer chooses relist / transform / off-platform (Decision 21).
14Merge match rule (REVISED — see product-merge-on-create.md for the AS-BUILT rule, which differs: the create-path lookup does NOT filter packingMode)Two batches merge into the same Product row when all three match: same leaf, same packingMode, same giStatus, same qualityMetricsFingerprint. sourceParty is no longer part of the match rule — merging across source parties is correct as long as the product itself is identical. Any one of the three downstream fields (packing, GI, metrics) different → new Product row under the same leaf. Leaf change → automatic separate row.
15Buyer "not received" cancellation📸 photo evidence + documented proofs + reason mandatory. Stored on Order.cancelEvidence (Decision 24).
16Bulk vs Retail mergeSame leaf + different packing mode → separate Product rows.
17Negotiation before Order materializationOwner CAN negotiate (one counter round per CHG-009). Order materialises only on the final Accept — after any counter exchange settles. Negotiation history is preserved on the Order as a transcript (counterPricePerKg / counterQtyKg + who-when timestamps) so disputes can reconstruct what was agreed.
18Receipt-expiry flexibilityAt ⏱14-day expiry, seller chooses: (a) auto-reverse stock, or (b) mark as Settled Off-platform (order → COMPLETED, no buyer batch auto-created). If seller doesn't choose within +7d secondary grace, defaults to auto-reverse. Reflects that the seller knows whether settlement happened outside the platform.
19Auto-generated printable documentsIndustry-standard PDF generated on each lifecycle transition: SOC (PLACED), DC (SHIPPED), GRN (DELIVERED), CC (COMPLETED), CR (CANCELLED). All include Order ID, Batch ID, datetime, parties + GST, negotiation transcript when present. See §15. Tax Invoice (TI) deferred to Phase 2 since GST collection is Phase 2.
20Role-aware source disclosure (REVISED)Source party shown only when seller's role is FARMER — they're the original grower and the farm name has trust value. For TRADER / PROCESSOR / MANUFACTURER / EXPORTER / IMPORTER roles, source party is NOT shown — it's understood they sourced their stock from elsewhere; surfacing identities would leak supply chains commercially. GI certificate carries no source identity either — it's just status + region (regulatory provenance).
21Post-DELIVERED batch optionsBuyer's batch, once in inventory, supports three per-batch actions: Relist (post sell lead), Transform (process into a different-leaf product with new qualityMetrics), Record off-platform sale (decrement only, no Order). Each is a first-class flow (§6.8, §6.9, §6.10).
22Off-platform inventory adjustmentDecrement batch.currentQty without creating an Order. InventoryEvent reason = OFF_PLATFORM_SELL. Optional fields: counterparty name, price (for P&L), invoice photo. Already exists for Products today (CHG-014 §7); extends to batches in Phase 1a.
23Buy Now Order shapeBuy Now creates an Order from Day 1 — Order's sourceType is BUY_NOW, no sourceOfferId / sourceBidId. The synthesised acceptance offer (used today for the DealThread) gets its orderId linked back so the unified Deal Detail UI works seamlessly.
24Evidence storage + visibilityOrder.cancelEvidence retained indefinitely for audit. Visible to BOTH parties (and admin) — evidence can be shared symmetrically, since either party may need to file a counter-claim. R2 keys + metadata stored alongside cancelledBy, cancelReason, cancelledAt.
25Pre-filled new-product editorWhen the buyer's incoming order doesn't match an existing Product (per Decision 14), the new-product editor pre-fills from sellerBatch.qualityMetrics + GI + photos. Buyer CAN edit these pre-fills before saving — but every edit is audit-logged on the Order as productMetadataOverrides: json so disputes can show "buyer changed claimed moisture from 9% to 11% at receipt".
26Idempotency on AcceptUse the offer/bid id as the natural idempotency key. If a second Accept arrives for an already-ACCEPTED offer/bid, the route returns the existing orderId instead of creating a new Order. Cheap, leverages the existing state machine, no extra storage.
27Backfill scopeCutoff-forward only. No retroactive Order materialization for historical accepted offers/bids. Admin tool ships in Phase 1 for on-demand backfill of specific past deals if support / audit requires it.
28Dispute resolutionDeferred to Phase 5+. Framework + UI sketched in §14 so Phase 1b's evidence-capture model and Phase 1b's PDF formats anticipate the dispute consumer. Industry-standard 7-day reporting window from DELIVERED, admin-mediated, replace/refund/credit/dismiss outcomes.
29Document delivery channelsIn-app download + SMS short link in Phase 1b. Email deferred (Cropto doesn't store email today). WhatsApp deferred to Phase 2+.
30Document languageBilingual (English + Hindi) from Phase 1b. Two-column layout per row of the PDF, English left / Hindi right for each label. Uses the existing en.json + hi.json infrastructure. Matches DeHaat / AgriBazaar invoicing posture.
31Transform — processing-cost entryOptional for everyone, stronger nudge when activeRole === 'PROCESSOR'. Default ₹0 if skipped. Non-processors get a one-liner tooltip; processors get an above-the-field banner: "Recommended — without this, your processed batch's cost basis won't reflect processing work." Required-by-role rejected as too rigid (users juggle roles).
32Buyer-side inventory creation from purchases (Phase 1b)(a) Manual confirm-receipt — buyer's inventory is NOT auto-minted at Accept time. Buyer goes to a "Confirm Receipt" surface (§6.1a) and explicitly converts the purchase into stock. Reason: physical goods may be delayed/lost in transit; buyer should affirm physical possession. (b) Merge ONLY at confirm-receipt — when the buyer confirms, the system checks the 4-tuple (leaf, packing, GI, qualityMetricsFingerprint per Decision 14). On exact match the buyer confirms merge into the existing Product; on no match it falls through to the pre-filled new-product editor (Decision 25). (c) NO source-Order/Offer backlink on the buyer's batch. Buyer's inventory is audit-detached from the source — Batch.sourceOrderId and any sourceOfferId stay NULL for purchases. Privacy + simplicity: the buyer's stock list doesn't reveal where they sourced from; their inventory looks identical regardless of acquisition path (Buy Now, regular accept, manual add). (d) GI status, GI region, and qualityMetrics still INHERIT from the source Product — they describe the physical goods, which don't change at the receipt boundary. Cost basis = order.pricePerKg. Applies symmetrically to both Buy Now (instant-settled) and regular accept-then-confirm paths.

4. Data shape — Batch + Product + Order ​

Why qualityMetrics lives on Product (and fingerprinted) ​

Match rule per Decision 14 is (leafId, packingMode, giStatus, qualityMetricsFingerprint). JSON equality is fragile (9 vs 9.0, key ordering). Canonical-form fingerprint:

ts
import { createHash } from 'node:crypto';
function fingerprintMetrics(m: Record<string, unknown>): string {
  // Canonical form: sorted keys, normalised numbers, trimmed strings
  const canonical = JSON.stringify(m, Object.keys(m).sort());
  return createHash('sha256').update(canonical).digest('hex').slice(0, 16);
}

Stored on Product as a column with a composite index (userId, leafId, packingMode, giStatus, qualityMetricsFingerprint) — single B-tree lookup at merge time.

Bulk vs Retail packing — field shapes (both ship in Phase 1a) ​

Bulk Loose Packing

{ packWeight: number, packType: 'CARTON'|'BAG'|'JUTE_SACK'|'PP_BAG',
  minimumOrderQty: number, pricePerKg: number }

Retail Packing

{ packWeight: number /* grams */, packsPerMaster: number,
  masterNetWeight: number, masterGrossWeight: number,
  mrp: number, discountPct: number,
  shippingPerMaster: number, netSellingForPrice: number }

Same leaf + different packing mode → separate Product rows.


5. Provenance & visibility — role-aware (Decision 20) ​

Single-hop rule ​

  • Bidder/buyer sees immediate source party of any batch shown — only if the seller's role is FARMER.
  • For non-farmer roles (TRADER, PROCESSOR, MANUFACTURER, EXPORTER, IMPORTER), source party is NOT displayed. It's understood they sourced their stock; surfacing identities would leak supply chains commercially.
  • GI metadata (status + region) follows up regardless — that's regulatory provenance, no source identity.

Visibility matrix — buyer viewing a sell lead ​

FieldFarmer sellerNon-farmer seller
Seller name / firm✅✅
Seller location✅✅
Pinned batch ID✅✅
Source party✅ (Farm: Sirniya)❌ (hidden)
Acquisition date✅✅
Quality metrics✅✅
GI status + region✅✅
Price, qty, days, packing✅✅
Where the source party got it❌❌
Seller's cost basis❌❌
Seller's phone❌ until acceptance❌ until acceptance

Worked example — Anand reviewing two bids ​

Buyer Anand posts: "Need 100kg of 7 Suta (Rasgulla) Makhana ≤ ₹1300/kg."

Ramesh (FARMER role) bids using BCH-2026-0042 (his own-farm batch). Suresh (TRADER role) bids using BCH-2026-0118 (sourced from Mukesh Devi).

   ┌─────────────────────────────────────────────────────────────────────┐
   │ Bids on your lead "7 Suta (Rasgulla) Makhana · 100kg"              │
   ├─────────────────────────────────────────────────────────────────────┤
   │  Bid from Ramesh — FARMER · Darbhanga, Bihar                       │
   │    Lot BCH-2026-0042 · Farm: Sirniya     ← source SHOWN (farmer)   │
   │    80 kg · ₹1280/kg · ₹1,02,400 total                              │
   │    9% moisture · handpicked · 7-suta+ grade                        │
   │    🌿 GI verified — Mithila                                        │
   │    Bulk packing: 25-kg jute sacks · MOQ 80 kg                      │
   │    Ready in 5 days · [Accept] [Counter] [Reject]                    │
   ├─────────────────────────────────────────────────────────────────────┤
   │  Bid from Suresh Traders — TRADER · Patna, Bihar                   │
   │    Lot BCH-2026-0118                       ← NO source line shown  │
   │    100 kg · ₹1310/kg · ₹1,31,000 total                             │
   │    11% moisture · machine-graded · 7-suta+ grade                   │
   │    ⚠ Not GI-tagged                                                  │
   │    Bulk packing: 50-kg PP bags · MOQ 100 kg                        │
   │    Ready in 3 days · [Accept] [Counter] [Reject]                    │
   └─────────────────────────────────────────────────────────────────────┘

Anand sees Ramesh's farm name because that's commercially honest disclosure for a grower. He does NOT see "Mukesh Devi" anywhere on Suresh's bid — it's understood Suresh, as a trader, sourced his stock from somewhere, and that supply chain stays private.

The GI certificate, when present, carries only status + region (e.g., "GI verified — Mithila"). It does NOT carry "issued because acquired from Ramesh K's farm" or any source identity. It's a regulatory token, not a chain-of-custody disclosure.


6. Sub-flows — drill-downs ​

6.1a Product creation — "Add From Product Purchased from Platform" (Phase 1b) ​

Key calls reflected:

  • Match rule = 4-tuple (leaf, packing, GI, qualityMetricsFingerprint) — Decision 14.
  • Merge ONLY at confirm-receipt time — no auto-mint at accept time — Decision 32.
  • Buyer's batch has NO source-Order/Offer backlink — buyer inventory is "clean" (audit-detached from the source). Privacy + simplicity: buyer's stock list doesn't reveal where they sourced from. GI/qualityMetrics still inherit because they describe physical reality — Decision 32.
  • Buyer cancel requires photo evidence + documented proofs (visible to BOTH parties + admin per Decision 24) — Decisions 15 + 24.
  • Pre-fill editor allows buyer edits, but each edit is audit-logged on Order — Decision 25.
  • ⏱14-day expiry routes to seller choice between auto-reverse and settled-off-platform — Decision 18.

6.1b Product creation — "Add a New Product" (Phase 1a) ​

6.2 Sell-lead post (Phase 1a) ​

6.3 Buy-lead post + bid placement (Phase 1a) ​

6.4 Negotiation + Acceptance → Order (Phase 1 + Phase 2 hook) ​

6.5 Order lifecycle + buyer inventory addition (Phase 1b — the closed loop) ​

6.6 Cancellation paths — state machine (Phase 1b) ​

6.7 Phase 2 payment hook (where it slots in) ​

6.8 Transform — process a batch into a new product (Phase 1a, Decision 21) ​

GI on transform — a deliberate call. Whether GI status transfers from raw to processed depends on the target leaf's rules (per CHG-010 — only specific processed leaves carry GI). Cropto policy: defer to the leaf's GI eligibility config. A processor handling GI Mithila raw → GI-eligible "Mithila Roasted Makhana" preserves it; raw → "Generic Flavoured Snack" does not.

6.9 Off-platform sale — inventory adjustment (Phase 1a, Decision 22) ​

Why this matters. A trader who has a Cropto inventory but sells half their stock at a physical mandi outside the platform needs to keep their Cropto numbers honest. Without this flow, their Cropto stock figures stay inflated and their sell leads would over-commit (Decision 7's stock-overcommit guard would mis-fire).


7. Accounting walkthroughs (Phase 1c) ​

7.1 Cost basis & realised P&L — single batch ​

   Day 1   Seller buys 200 kg from Ramesh K at ₹1100/kg
           → BCH-2026-0042 created (cost basis ₹2,20,000)

   Day 7   Buyer accepts. Order ORD-XXX created (₹1300/kg × 100 = ₹1,30,000)
           Negotiation history: [{round 1: counter ₹1300/kg by seller; accept by buyer}]

   Day 10  Seller marks SHIPPED. 📄 DC PDF generated.

   Day 14  Buyer adds to inventory. Order → DELIVERED. 📄 GRN PDF.

   Day 15  Order COMPLETED. 📄 CC PDF.
           Realised P&L = ₹1,30,000 − (₹1100 × 100) = ₹20,000

7.2 Own-farm batch — ₹0 cost default ​

   BCH-2026-0073 created with productionCostPerKg = ₹0 (own-farm default)
   Sale of 100 kg @ ₹1200/kg → Realised P&L = ₹1,20,000 (100% margin)
   Phase 1c tooltip: "Enter production cost to see actual margin"
   Farmer enters productionCostPerKg = ₹400 → P&L recomputes to ₹80,000

7.3 Transform — cost flow ​

   BCH-2026-0042  Raw Makhana  100 kg  @ ₹1100/kg  =  ₹1,10,000 cost basis

   Transform: 100 kg raw → 75 kg Roasted Makhana
   Processing cost: ₹150/kg over 75 kg = ₹11,250

   New batch BCH-2026-0099 (Roasted Makhana):
     qty: 75 kg
     cost basis = (100 × ₹1100 + ₹11,250) / 75
                = ₹1,21,250 / 75
                = ₹1616.67/kg

   Sale @ ₹2200/kg × 75 kg = ₹1,65,000
   Realised P&L = ₹1,65,000 − ₹1,21,250 = ₹43,750

7.4 GST display (Phase 1c — calculate-only) ​

   ┌─────────────────────────────────────────────────────────┐
   │  Order ORD-J3F7K9M2P4Q5RT                              │
   │  Seller: Ramesh Farms  GST: 10ABCDE9999X1Z2            │
   │  Buyer:  Anand Traders GST: 06FGHIJ1234K1L5            │
   │                                                         │
   │  7 Suta (Rasgulla) Makhana                              │
   │  100 kg × ₹1300/kg                       ₹1,30,000     │
   │  GST (5% — agri produce)                 ₹    6,500    │
   │  ─────────────────────────────────────────────────      │
   │  Total (incl. GST)                       ₹1,36,500     │
   │  ⓘ Calculated for your records. Phase 1 does not       │
   │     collect or remit GST. (See Phase 2 Tax Invoice.)   │
   └─────────────────────────────────────────────────────────┘

8. Order ID format ​

ORD- + 14-char Crockford base32 (no I, L, 0, O). Example ORD-J3F7K9M2P4Q5RT. Matches Stripe / Razorpay / Amazon — opaque, identity-safe, typo-safe, 70 bits of entropy.


9. Cancellation comparison (agri portals) ​

PlatformRule
AgriBazaar2h from placement only
DeHaat"Before packed" — no clock
Ninjacart B2B24h before delivery
BigHaatBefore shipped — no clock
Cropto2h AND before SHIPPED (combined, with reason)

10. Phasing ​

Updated 2026-05-28: Phase 2 split into 2a/2b/2c, Phase 3 named (3a/3b/3c), Phase 4 promoted from implicit to explicit. See critique-and-known-gaps.md §9 for the rationale.

PhaseChangeScopeKey documents
1orders-and-order-idsOrder entity, IDs, materialised on Accept (with negotiation history embedded), idempotency, cutoff-forward (no backfill), Buy Now linkage via orderId. Admin backfill tool.SOC PDF on PLACED
1abatch-level-inventory-and-transformsBatch + Product split with qualityMetricsFingerprint (spec: quality-metrics-fingerprint-spec.md). Bulk + Retail packing. Pinned-batch sell leads + bids. Single-hop role-aware provenance. One-to-many batch→leads. Transform flow. Off-platform sale adjustment. Meilisearch index for batch search (plan: batch-search-index-plan.md).—
1border-lifecycle-receipt-and-docsFull Order states. Confirmed Deal Section. 2h cancel. 📸 evidence (both-party visible, retained forever). Merge surface. Pre-filled editor with audit-logged overrides. Seller-pick at 14d expiry (auto-reverse vs settled-off-platform). Buyer batch on DELIVERED. Admin orders dashboard. Email collection at profile completion.DC on SHIPPED, GRN on DELIVERED, CC on COMPLETED, CR on CANCELLED
1cbasic-accounting-and-gst-displayCost basis. Realised P&L. GST calculate-and-display. Per-user financial dashboard. Optional production-cost entry.Statement of Account (monthly, emailed)
2apayments-escrow-refund-and-kycRazorpay/PG integration. PAYMENT_PENDING → PAID states before PLACED. Escrow default-on for high-value (≥ ₹50K) orders. Refund flow on cancellation. Basic KYC enforcement (Aadhaar + PAN). 1.5% transaction commission introduced (per monetisation-brainstorm.md).—
2bshipment-tracking-and-logisticsTracking number on Order. Carrier integration (Delhivery, Bluedart, India Post adapters). Real shipment status webhooks. DC PDF gets tracking embedded.Updated DC with tracking
2ctax-invoice-and-gst-collectionFull Tax Invoice PDF with invoice-number sequence per seller. CGST/SGST/IGST split. GST remittance hooks. DPDP Act data export endpoint. Monthly tax reports.Tax Invoice on PAID
2dplatform-completeness-bundleCatch-all for previously-pure-backlog items now owned: goods-in-transit insurance (carrier-bundled + optional add-on for high-value), unit conversions (kg ↔ quintal ↔ maund, configurable per leaf), holiday / business-day arithmetic for all timers (14d delivery, 2h cancel, 48h counter — Indian holiday calendar), mobile app (React Native + offline mode for intermittent rural connectivity), voice / WhatsApp channel (MSG91 WhatsApp + voice templates), reputation chain for processed products (full provenance chain visible post-Transform, breaks the single-hop rule for processed-product trust signals only).—
3areturns-and-partial-fulfilmentNo-fault returns within configurable window per leaf. Shipment sub-entity (Order 1:N Shipment). Split shipments + partial GRN. Partial cancellations.—
3bnetx-terms-and-financingNET-30 / NET-60 terms for Verified buyers. Financing margin revenue model (Cropto fronts seller payout, charges buyer service fee). Credit limit per trust tier. Automated dunning.—
3creviews-and-reputationPer-transaction buyer + seller reviews (1-5 + text). Reputation aggregation alongside calculated trust tier. Dispute history visible (anonymised).—
4analytics-and-biBigQuery pipeline. Per-user financial dashboards. Admin BI. GMV / volume / cancellation-rate reports. Boost / sponsored placement revenue model. Data products (Bihar Makhana price index, etc.). Multi-currency for exporters.Monthly Statement richer
5+dispute-resolution7-day buyer-report window. Admin-mediated. Replace/refund/credit/dismiss outcomes. Consumes evidence captured in 1b.Resolution Memo on close

11. State machines ​

Order ​

[Phase 2 prepend:]  PAYMENT_PENDING → PAID → ...

PLACED ─┬─→ SHIPPED ──→ DELIVERED ──→ COMPLETED
        │                  ▲
        │                  └─ buyer-confirm or ⏱14d → seller picks
        │
        ├─→ CANCELLED  (≤2h, before SHIPPED, either party + reason)
        ├─→ CANCELLED  (⏱14d, never SHIPPED, system)
        ├─→ CANCELLED  (buyer 'not received' + 📸 evidence)
        └─→ COMPLETED_OFF_PLATFORM (seller picks at ⏱14d expiry)

[Phase 5+:]  SHIPPED → DISPUTE

Batch ​

CREATED ──┬─→ partially RESERVED (in active sell leads)
          ├─→ partially CONSUMED (decremented on Order DELIVERED)
          ├─→ partially CONSUMED (TRANSFORM_OUT → new batch)
          └─→ partially CONSUMED (OFF_PLATFORM_SELL)

12. The three things you asked me to explain ​

12.1 Backfill scope — explained ​

The problem. Cropto has many accepted Offers and BuyBids in production today, from before the Order entity exists. When Phase 1 ships, what happens to them?

Two choices:

OptionWhat it doesProsCons
(A) Migrate everythingOne-shot migration at deploy: for each historical accepted Offer / BuyBid, create a matching Order row retroactively with status: COMPLETED (or the appropriate state), preserving the deal date.Every accepted deal has an Order ID for support / audit lookup. Uniform interface.Risky migration over a large table. Ambiguous timestamps (what status do you assign?). Old data quirks surface in reports.
(B) Cutoff-forward only (your pick, Decision 27)Only NEW accepts from Phase-1-release-date onwards create Orders. Historical accepted Offers / BuyBids stay as-is — no Order linkage. Support team handles two systems for ~6 months.Clean break. Safe deploy. No migration risk. Old deals remain in their original shape (no rewrite of history).Support has to know "anything before this date has no Order ID — use the Offer ID instead." Statement-of-account UIs need to handle both shapes for a year or so.

Mitigation for (B): Ship an admin migration tool in Phase 1 that can backfill specific historical accepted deals on-demand. When a support ticket needs an Order ID for an old deal, admin runs the tool and that deal gets a fresh Order. No bulk migration, no risk.

12.2 Idempotency on Accept — explained ​

The problem. A user clicks "Accept" on a bid. The network is slow. They click again. Without protection, the server processes the click twice and creates two Orders against the same offer/bid — which is impossible to reconcile.

Industry-standard solutions:

SolutionHow it worksCropto fit
Idempotency-Key header (Stripe, Square pattern)Client generates a UUID per request. Server stores {key → response} for 24h. Second request with same key returns the same response without re-executing.Best for unique-each-time operations (refund, payment). Overkill here.
Natural-key idempotency (your pick, Decision 26)The offer/bid id IS the natural dedup key — each can only be accepted once. Server checks: if offer.status === 'ACCEPTED', return its existing orderId instead of creating a new Order.Perfect for the Accept route. Leverages the existing state machine. Zero extra storage.

Pseudocode:

ts
async function acceptOffer(offerId, ownerId, idempotencyKey?) {
  const offer = await tx.offer.findUnique({ where: { id: offerId } });
  if (offer.status === 'ACCEPTED' && offer.orderId) {
    // Already accepted — return the existing Order, don't create a new one.
    return { orderId: offer.orderId, alreadyAccepted: true };
  }
  // Proceed with normal acceptance...
}

12.3 Pre-filled editors — explained ​

The problem. When the buyer's incoming order doesn't match any existing Product (per Decision 14's 4-tuple match), the system creates a new Product. Asking the buyer to type leaf / metrics / GI from scratch is wasteful — all that data is already on the seller's pinned batch.

The solution. Pre-fill the new-product editor with data inherited from order.sellerBatches[0].product:

  • Taxonomy leaf (auto)
  • packingMode (inherited)
  • giStatus + giRegion (inherited)
  • qualityMetrics (inherited as JSON)
  • Photos (carried over from seller batch)
  • Source party (the seller's name)

The open question we just settled (Decision 25): can the buyer EDIT these pre-fills before saving?

  • (A) Strictly add-as-is — data is authoritative from the seller's batch. No edits. Disputes file later via Phase 5+ flow.
  • (B) Allow edits, no tracking — buyer can change anything. Risk: silent drift, no audit trail.
  • (C) Allow edits, audit-log them (your pick) — buyer can edit (e.g., update moisture after their own quality check), but each edit is recorded on the Order as productMetadataOverrides: json. Disputes can show "buyer changed claimed moisture from 9% to 11% at receipt time."

(C) is the realistic and accountable answer. It acknowledges that buyers do quality checks at receipt and may legitimately adjust, while preserving full traceability for disputes.


13. Decisions previously open — now closed ​

#QuestionStatus
Merge strictnessLeaf + packing + GI + qualityMetricsFingerprint (Decision 14)✅ closed
Source party in matchDropped from match rule (qualityMetrics is more precise)✅ closed
Buyer cancel evidencePhotos + documents + reason, both-party visible (Decisions 15, 24)✅ closed
Cancel window2h, before SHIPPED (Decision 3)✅ closed
Bulk vs Retail mergeSeparate Product rows (Decision 16)✅ closed
Backfill scopeCutoff-forward, admin tool for on-demand (Decision 27)✅ closed
Buy Now linkageorderId from Day 1, sourceType = BUY_NOW (Decision 23)✅ closed
IdempotencyNatural-key on offer/bid id (Decision 26)✅ closed
Pre-filled editorsAllow edits + audit-log overrides (Decision 25)✅ closed
Evidence retentionForever for audit (Decision 24)✅ closed
Evidence visibilityBoth parties (Decision 24)✅ closed
Source disclosureRole-aware — farmer only (Decision 20)✅ closed
Receipt-expiry behaviourSeller picks auto-reverse vs settled-off-platform (Decision 18)✅ closed
Post-DELIVERED optionsRelist / Transform / Off-platform (Decision 21)✅ closed
Negotiation handlingHistory preserved on Order (Decision 17)✅ closed

All decisions from earlier rounds are now closed (see §3 Decisions 1–31). No remaining design-level questions — the Phase 1 OpenSpec change openspec/changes/orders-and-order-ids (drafted on 2026-05-27) implements the first slice; subsequent phases (1a, 1b, 1c, 2, 5+) get their own change folders when each kicks off.


14. Dispute resolution (Phase 5+ — framework only) ​

Standard pattern adopted across agri / B2B portals (IndiaMART, DeHaat, Alibaba Trade Assurance, Amazon B2B A-to-z):

Why this matters for Phase 1b decisions: the evidence model captured in 1b (Decision 24 — both-party visible, retained forever) and the PDF formats in §15 are designed to be directly consumable by this dispute flow. We're laying the foundation in 1b so Phase 5+ doesn't need new evidence plumbing.

Standards reflected:

  • 7-day buyer-report window (matches IndiaMART, AgriBazaar)
  • 48h seller response window (matches Alibaba Trade Assurance)
  • Admin mediation with documented outcome (matches Amazon B2B A-to-z)
  • Industry arbitration escalation for high-value (matches NCDEX dispute board)
  • Standardised resolution PDF (matches Razorpay dispute resolutions)

15. Auto-generated printable documents — per lifecycle event ​

TriggerDocPhaseIncludes
Order PLACEDSale Order Confirmation (SOC)1Order ID, datetime, both parties (name, role, GST, address), batch ID, leaf chain, qty × price = total, GST line (display-only), packing mode + spec, terms incl. negotiation transcript when present, expected delivery, cancel-window note
Order SHIPPEDDispatch Note / Delivery Challan (DC)1bOrder ID, batch ID, packing details (master cartons / weight), pack list, deliver-to address, dispatch date, tracking number when available (Phase 2), seller signature (digital)
Order DELIVEREDGoods Received Note (GRN)1bOrder ID, batch ID, received qty, quality remarks, deviations (with reference to productMetadataOverrides if any), buyer signature (digital)
Order COMPLETEDCompletion Certificate (CC)1bOrder ID, final reconciliation, all line items, both parties' signatures (digital), realised P&L summary (seller-only PDF variant)
Order CANCELLEDCancellation Record (CR)1bOrder ID, who cancelled, reason, embedded evidence references (URLs to R2-hosted photos / docs), refund status (Phase 2 hook), both parties' signatures
Order COMPLETED_OFF_PLATFORMCC variant noting settlement-off-platform1bSame as CC + explicit "Settled outside Cropto on YYYY-MM-DD per seller declaration"
Phase 2 PAIDTax Invoice (TI)2Order ID, invoice number (separate sequence), both GSTINs, batch ID, HSN code, payment ref, CGST/SGST/IGST breakdown
MonthlyStatement of Account1cPer-user roll-up: orders placed/received, realised P&L, inventory valuation, GST liability summary (display)
Dispute closed (Phase 5+)Resolution Memo5+Original Order ID, dispute reason, evidence summaries, admin decision, executed resolution, both signatures

Common header on every PDF: Cropto branding, document type, generation datetime, Order ID prominently. Common footer: terms-of-service link, audit hash (sha256 of the document body → tamper detection).

Delivery channels:

  • In-app: downloadable PDF from the Order Detail page.
  • Email: PDF attached on every state transition (when email exists on the user record).
  • SMS: short link to the PDF for both parties (uses MSG91 templates, same pattern as other notifications).
  • WhatsApp: Phase 2+ optional.

Appendix — pseudocode references ​

ts
// Order ID (Decision 1)
import { customAlphabet } from 'nanoid';
const CROCKFORD = '23456789ABCDEFGHJKMNPQRSTVWXYZ';
const orderId = customAlphabet(CROCKFORD, 14);
const generateOrderId = () => `ORD-${orderId()}`;

// Quality metrics fingerprint (Decision 14)
import { createHash } from 'node:crypto';
function fingerprintMetrics(m: Record<string, unknown>): string {
  const canonical = JSON.stringify(m, Object.keys(m).sort());
  return createHash('sha256').update(canonical).digest('hex').slice(0, 16);
}

// Idempotency on Accept (Decision 26)
async function acceptOffer(offerId: string, ownerId: string) {
  const offer = await prisma.offer.findUnique({ where: { id: offerId } });
  if (offer.status === 'ACCEPTED' && offer.orderId) {
    return { orderId: offer.orderId, alreadyAccepted: true };
  }
  // ... proceed with normal acceptance ...
}

// Role-aware source disclosure (Decision 20)
function sourcePartyForDisplay(batch: Batch, sellerRole: string): string | null {
  if (sellerRole === 'farmer') return batch.sourceParty;
  return null;  // Non-farmer roles: hide source identity.
}

// Transform flow (Decision 21, §6.8)
async function transformBatch(input: {
  sourceBatchId: string;
  targetLeafId: string;          // must differ from source
  outputQtyKg: number;
  processingCostPerKg?: number;
  newQualityMetrics: Record<string, unknown>;
  newPackingMode: 'BULK' | 'RETAIL';
}) { /* ... §6.8 logic ... */ }

// Off-platform sale (Decision 22, §6.9)
async function recordOffPlatformSale(batchId: string, input: {
  qtyKg: number;
  saleDate: Date;
  counterpartyName?: string;
  pricePerKg?: number;
  invoicePhotoR2Key?: string;
}) { /* ... §6.9 logic ... */ }

16. Known limitations + Phase 0 backlog ​

Permanent record from the design audit on 2026-05-28. Items in this section are NOT in any of the phased changes. They're either deferred, deliberately scoped out, or unnamed entirely. The team should revisit this list quarterly + before each phase kicks off so nothing here is silently forgotten. Full critique with competitive analysis lives in the companion document docs/flows/critique-and-known-gaps.md.

16.1 Scaling risks in the current design ​

#AreaConcernBites when
1PDF generationEvery state transition fires a pdfkit render (SOC, DC, GRN, CC, CR). No backpressure design. No PDF versioning — branding/translation changes can't be re-applied to old docs consistently.~500 orders/day or first rebrand
2SMS fan-outORDER_PLACED template ADDS to existing acceptance SMS rather than replacing. No notification-preference toggles. MSG91 rate limits + cost.~500 orders/day, or first spam complaint
3qualityMetricsFingerprint canonicalization"Sort keys + normalize numbers + trim strings" isn't a full spec. 9 vs 9.0, casing, missing vs null vs undefined, Prisma reorderings can fragment the match graph.First time two clearly-same products fail to merge
4negotiationHistory as JSONFine at small scale; slow to query at scale. CHG-009 caps counter rounds at 1 today — if multi-round bargaining ever ships, the JSON grows.When negotiation history needs cross-order queryability
5Stock-overcommit guard fan-outavailableQty = batch.currentQty − Σ activeLeads.reservedQty walks active leads per check.50+ active leads per power user
614-day timeline state machineCombined with the +7-day secondary grace, an order can sit "pending receipt" for 21 days. Cron worker wake-ups at D-1/3/7/13, D+14, D+21 × every order. No reminder cadence spec'd.Operational complexity hurts immediately
7Bilingual PDF renderingpdfkit + Devanagari needs font embedding. Mixing scripts is fiddly. Not addressed.First test render of a Hindi SOC
8Audit-hash tamper detectionFooter hash of body-bytes is theatre unless tooling re-renders + verifies. Real solution = certificate-based digital signatures (separate project).First dispute that hinges on PDF integrity
9Single-shipment assumptionLifecycle assumes one shipment per order. Real sellers ship in tranches; real buyers want to confirm receipt of each.First trader who ships in two batches
10Search across qualityMetricsFingerprint enables exact-match merge but buyers want range queries (moisture 9-12%, handpicked, 7-suta+). Postgres JSONB GIN doesn't scale gracefully. Phase 1a should plan for Elasticsearch / Meilisearch index.When buyer search becomes a primary discovery channel

16.2 Deliberately deferred — will bite at some scale ​

  • Phase 2 = payments + shipment + tax invoicing as one bundle — ~3 months of work in one change. Will slip; pieces will defer. Recommendation: split into Phase 2a (payments + escrow + refund), 2b (shipment tracking), 2c (tax invoice + GST collection + KYC) before kickoff.
  • Phase 5+ vagueness — Phase 3 and 4 unnamed. Users in months 4-6 post-launch will hit flows we have no plan for.
  • No escrow planned — Most successful agri B2B platforms ship escrow with payments. Without it, buyers won't pay upfront for relative strangers; the negotiation model degrades for first-time counterparties.
  • No KYC enforcement — RBI requires KYC for transactions ≥ ₹1L. When Phase 2 payments ship, this becomes a hard gate. CHG-008 deferred KYC; bringing it back is a 3-week sub-project.
  • Email channel deferred — Phase 1c proposes monthly Statement of Account. SMS short-link or in-app is awkward for a statement.
  • No support tooling beyond backfill admin endpoint — First wave of support tickets will hit a wall: no admin UI for "show me all orders for user X," no order-state inspector, no manual force-cancel override.
  • No returns flow (separate from disputes) — Buyer gets goods, perfectly fine, but doesn't want them — has to file a fake "not received" dispute. Should land before Phase 5+ dispute resolution.

16.3 Missing entirely — Phase 0 backlog ​

These were not even named in the phased plan. Cross-reference quarterly:

   ┌────────────────────────────────────────────────────────────────────┐
   │  MONETISATION MODEL                                                │
   │  No revenue plan attached to any phase. Commission per             │
   │  transaction? Subscription tiers? Listing fees? Marketplaces       │
   │  without revenue plans die.                                        │
   ├────────────────────────────────────────────────────────────────────┤
   │  PARTIAL FULFILLMENT / SPLIT SHIPMENTS                             │
   │  Order = single shipment is baked into the lifecycle. Mandi        │
   │  reality is tranches.                                              │
   ├────────────────────────────────────────────────────────────────────┤
   │  NET-X PAYMENT TERMS                                               │
   │  Faire (NET-60), Alibaba (NET-30). Cropto's pay-on-acceptance      │
   │  is the strictest possible term. Loses cash-strapped buyers.       │
   ├────────────────────────────────────────────────────────────────────┤
   │  LOGISTICS INTEGRATION                                             │
   │  Self-arranged today. No Delhivery / Bluedart / India Post         │
   │  integration planned. Tracking number field exists but no spec.    │
   ├────────────────────────────────────────────────────────────────────┤
   │  GOODS-IN-TRANSIT INSURANCE                                        │
   │  Who's liable for damage in transit? Alibaba bundles insurance;    │
   │  Cropto doesn't mention it.                                        │
   ├────────────────────────────────────────────────────────────────────┤
   │  UNIT CONVERSIONS                                                  │
   │  Everything in ₹/kg. Mandis trade in quintal (100kg), some in      │
   │  maund (37.32kg). Not addressed.                                   │
   ├────────────────────────────────────────────────────────────────────┤
   │  HOLIDAY / BUSINESS-DAY ARITHMETIC                                 │
   │  14-day timeline includes weekends, Diwali, Holi, harvest          │
   │  seasons. Need business-day calendars.                             │
   ├────────────────────────────────────────────────────────────────────┤
   │  ANALYTICS DATABASE                                                │
   │  Real-time analytics on operational Postgres = pain at scale.      │
   │  Need a separate BigQuery / Redshift pipeline before Phase 4       │
   │  reports.                                                          │
   ├────────────────────────────────────────────────────────────────────┤
   │  DPDP ACT 2023 COMPLIANCE                                          │
   │  Data export + right-to-be-forgotten. Orders have embedded PII     │
   │  (counterparty contact shared on acceptance). Hard to retrofit.    │
   ├────────────────────────────────────────────────────────────────────┤
   │  RATINGS / REVIEWS                                                 │
   │  Beyond calculated trust tier, no per-transaction review.          │
   │  Alibaba, Faire, Etsy all have these.                              │
   ├────────────────────────────────────────────────────────────────────┤
   │  MULTI-CURRENCY FOR EXPORTERS                                      │
   │  Decision 20 names EXPORTER / IMPORTER as roles but pricing        │
   │  is ₹-only. USD/AED/EUR for export trades not addressed.           │
   ├────────────────────────────────────────────────────────────────────┤
   │  MOBILE APP                                                        │
   │  Phase 2 mentions React Native; flow assumes web. Offline mode     │
   │  for intermittent rural connectivity not addressed.                │
   ├────────────────────────────────────────────────────────────────────┤
   │  VOICE / WHATSAPP CHANNEL                                          │
   │  Many farmers prefer voice / WhatsApp over web. Not in plan        │
   │  through Phase 2.                                                  │
   ├────────────────────────────────────────────────────────────────────┤
   │  REPUTATION DECAY FOR PROCESSED PRODUCTS                           │
   │  Transform (§6.8) preserves source-batch chain, but single-hop     │
   │  visibility means buyer of processed product can't see the         │
   │  original farmer's reputation.                                     │
   └────────────────────────────────────────────────────────────────────┘

16.4 Technical specs that should land in Phase 1a's design.md ​

Status update 2026-05-28: both specs now drafted ahead of Phase 1a kickoff:

  1. ✅ qualityMetricsFingerprint canonicalization spec — see quality-metrics-fingerprint-spec.md. Per-type normalization rules (NUMBER, ENUM, TEXT, YEAR, DATE, BOOLEAN), null/missing semantics, versioned algorithm with migration story, reference implementation, test fixtures.
  2. ✅ Batch search index plan — see batch-search-index-plan.md. Meilisearch chosen (over Elasticsearch / OpenSearch / Typesense / Postgres pg_trgm), Prisma-middleware sync pipeline, document schema with role-aware source disclosure, query API + facets, operational notes (cost, residency, reindex window, failure modes).

Both fold into Phase 1a's design.md when that change is scaffolded; until then they live in docs/flows/.

16.6 Monetisation brainstorm — also drafted ​

Status update 2026-05-28: revenue model is no longer invisible — see monetisation-brainstorm.md for the nine-candidate evaluation, competitive scan (DeHaat / AgriBazaar / Faire / IndiaMART / Alibaba etc.), and recommended phased rollout: free in Phase 1 → 1.5% transaction commission in Phase 2a → subscription tiers in Phase 3a → financing margin (NET-X, Faire-style) in Phase 3b → data + boost in Phase 4. Settles before Phase 2a's design.md is written.

16.5 Competitive position — one-screen executive summary ​

   AHEAD vs marketplaces            BEHIND vs peers              DEFENSIBLY DIFFERENT
   ─────────────────────            ──────────────              ────────────────────
   • Closed-loop inventory          • No escrow planned         • Calculate-only GST
     with full provenance           • No NET-X terms              in Phase 1c
   • GI as first-class              • No partial fulfilment     • Bulk + Retail as
     taxonomy concept               • No returns flow             separate Products
   • Role-aware source              • No commission model       • Negotiation history
     disclosure                     • 2h cancel is unusually     as JSON (vs
   • Negotiation + Buy Now            strict                     separate table)
     coexisting                     • Seller-decides-at-expiry  • Single batch per
   • Transform flow with              is uncommon                 sell lead
     source-batch chain             • No reviews/ratings
   • Evidence-anchored                beyond trust tier
     disputes from Phase 1b         • Single-hop limits
   • Bilingual docs                   downstream signal
     from Phase 1b                  • Logistics integration
   • Cutoff-forward backfill          undesigned
     strategy

See critique-and-known-gaps.md for the full competitive matrix, item-by-item ratings, and source platforms compared (DeHaat, AgriBazaar, Ninjacart, IndiaMART, Alibaba, Faire, AgriDigital, NCDEX, Otipy, Udaan).

16.7 Status update 2026-05-28 (PM round) — all backlog items now owned ​

The previously-pure-backlog items have been assigned to Phase 2d (platform-completeness-bundle):

  • Goods-in-transit insurance
  • Unit conversions (kg / quintal / maund)
  • Holiday / business-day arithmetic
  • Mobile app + offline mode
  • Voice / WhatsApp channel
  • Reputation chain for processed products

Phase 2d is a deliberately heterogeneous bundle — six medium-priority items grouped to give them an owner. Likely needs sub-splitting (2d.1 / 2d.2 / 2d.3) when its design.md is drafted, because the work spans foundational utilities (unit conversions, business-day arithmetic), large surface additions (mobile + offline), new channel integration (voice / WhatsApp), and a data-model amendment (reputation chain — breaks Decision 5's single-hop rule for processed products only). The 45d Gantt estimate assumes parallel work-streams across these distinct sub-domains.


End of flowchart document.

Last updated 2026-05-28 · Updates on confirmed design changes go in the change folders' design.md, with a note here.

Last updated:

Internal technical documentation — Cropto