ARCHITECTURE.md — one journal, two books
How the new system unifies the two existing softwares without losing what they were built for.
1. The two worlds today (and why they duplicate)
| Job work module | Trading module | |
|---|---|---|
| Material | Party's yarn/material (or bought on party's account) | Own raw material |
| Income | Conversion charges (₹/m, ₹/kg) for work done | Fabric sale margin |
| Tracking | Production given to units on job work | Full material → fabric flow |
| Profit view | Profit centre per job-work business | Profit centre per trading business |
| Cost view | Cost centre = the manufacturing units | Cost centre = the units (same physical units!) |
Today even in-house production is entered once in the trading module and again as "job work given to own unit" in the job work module. The two systems don't talk to each other, so:
- the same physical event (a beam woven, mtrs checked, fabric stocked) is typed twice;
- stock and WIP are reconciled by hand between the two screens;
- profitability of each line of business requires cross-referencing two systems.
What must be preserved: the separation — job-work income vs trading margin, and per-operation profitability. That is a valuation/viewing requirement, not a data-entry requirement. The next section is how we get both.
2. Target model: one event journal, two valuation books
Three layers. Physical facts are entered once; money views are computed, never re-entered.
┌─ PHYSICAL LAYER (single source of truth, entered at creation) ─────────┐
│ - Inventory lots with OWNERSHIP dimension: own | party (party yarn │
│ received/held for job work) │
│ - Beams (warp/sized/weaving), machines/units (each unit = cost centre) │
│ - Typed events: purchase receipt, yarn issue, sized-beam return, │
│ weaving production (mtrs), checking/grading, dispatch, sample, … │
│ every event: who, when, where (unit), device/source, QR/batch │
└─────────────────────────────────────────────────────────────────────────┘
│ one journal of events
▼
┌─ VALUATION LAYER (computed profiles, no double entry) ──────────────────┐
│ Profile A (trading): own material cost + conversion + overhead │
│ Profile B (job work): conversion income; party material on party acct │
│ Internal transfers: in-house unit (cost centre) books a conversion │
│ charge against the trading company (profit centre) — the "job work │
│ given to own unit" fiction becomes an automatic internal transfer │
│ generated from the SAME event, not a second entry. │
└─────────────────────────────────────────────────────────────────────────┘
│
▼
┌─ REPORTING LAYER ───────────────────────────────────────────────────────┐
│ Per profit centre P&L (trading vs job-work), per unit cost & │
│ efficiency, open POs/SOs, stock & apportionment sheets │
└─────────────────────────────────────────────────────────────────────────┘
The rule that kills the duplication: an event is recorded once; both profit centres see it because they are views over the same journal. "Job work given to own unit" is not a second record — it is the automatic internal-transfer leg of a production event whose unit is in-house. Out-house units are the same event type, with the party's/own material ownership riding on the lot.
What the current demo already gives us
- SvelteKit runes + daisyUI → role-based mobile-first screens (Phase: floor entry apps).
- Better Auth (email/password) → one login, role-based screens: godown, warper, sizer, weaver, checker, dispatcher, planner, owner.
- Drizzle/libSQL (Turso shared DB; local file fallback) → tables grow per
phase;
drizzle/migrations are the schema history. - POST edges are just fetch calls → an IoT ingestion endpoint is the same mechanism (machine counters pushing meters) with the same audit trail as a human entry.
Ownership dimension (job-work correctness)
Every lot/carton carries own | party. Party material is never valued into our stock; it is tracked (given, WIP, returned) on the party's account. This is what job-work plants in this trade rely on (yarn received / issue to sizing / weave / return fabric + wastage recovery). The screenshots will confirm the exact field names and the wastage (off-cut, selvedge, rejects) conventions.
3. Entry at the point of creation (mobile-first + IoT)
- Roles, not big menus: each staff profile opens directly on their task screen (weaver: "log today's meters" with the assigned job + beam preselect; checker: "check piece" flow with grade buttons; godown: receipt over PO / issue over challan).
- Physical tokens: every lot, beam, job, and piece gets a QR label; entry = scan → pre-filled form → 2 taps. No typing codes on the floor.
- IoT-ready: loom pick counters / machine sensors / connected weigh
scales emit the same "production event" the app does — a thin
/api/events/ingestaccepts both. Whatever is automated shows up in the journal alongside human entries, tagged by source. Offline-tolerant: mobile app queues events when the network is down and syncs (libSQL can mirror locally) — on a weaving floor, a dead WiFi must not stop entries. - post-hoc backfill still allowed but flagged ("entered on 21st for 20th") so reports can distinguish real-time vs corrected data.
4. Field inventory & terminology mapping (WIP — fed from screenshots)
As the existing softwares' screens are shared, this table is populated with every field → where it lives in the new model → local terminology notes. Presumed anchors (correct as screenshots arrive):
| Old-software concept | New model home | Notes |
|---|---|---|
| Party yarn receipt / challan | Lot (ownership=party) + event party-material-in |
verify field names |
| Job-work order (party + construction + mtrs + rate) | Order (kind=job-work) + job work ledger | conversion ₹/m |
| Trading sale order | Order (kind=trading) + SO ledger | fabric ₹/m |
| Beam issue (warp/sized) | Beam + issue event (own/party) | ends×length, kgs computed |
| Weaving production slip | Unit production events (mtrs) | source: mobile / IoT / ERP import |
| Checking slip & grading | Checking events + gradings | A/B/C/seconds, points/100m |
| Dispatch challan | Dispatch events by grade | vehicle/challan/party-challan |
| Godown stock registers | Live inventory per lot | kgs + mtrs, own vs party |
| Profit & loss per module | Computed P&L per profit centre | internal transfer auto-post |
5. Migration & cutover (both old systems retire together)
- Parallel run: new system live next to both old ones; staff enter in new system only for new movements, old systems keep historical data.
- Opening balances: yarn stock (own + party), open POs, open SOs, in-flight beams/jobs, WIP, unchecked stock — one-time cutover entries, printed + signed.
- Reconcile & cutover: ledger totals vs each old module's closing
balances; when staff trust the screens, old systems go read-only and
then archive. The web is on the same Pi infrastructure (Caddy/DSH), so
deployment is the existing
deploy.shloop. - Slice by slice, bridge by file: cutover is never whole-system-at-once. Each slice is decided as S (switch) / B (bridge) / H (hold) at the time (ROADMAP.md "How each phase is delivered"), and the ONLY bridge to the old software is its periodic file push which we import — contracts, idempotency, reconciliation and retirement dates live in BRIDGE.md. The parallel run above is sliced too: per domain, one owner at a time.
6. Principles (recap — the checklist every feature must pass)
- An event is entered once, at the point of creation, by the person who did the work (mobile-first).
- Ownership (own/party) is carried by the lot, not by the module.
- Profit centres are computed, with internal transfers generated — never typed twice.
- All kg↔mtrs conversions come from the construction engine (BUSINESS.md §3), never from hand calculations.
- Allocations of yarn to orders stay soft & auditable (BUSINESS.md §2).
- Printing (challans, checking reports) is first-class.
- Every event has source + actor + time, ready for IoT and audit.
- The system is the company platform: every table is tenant-scoped (company × setup), every fact enters through the event backbone, and the runtime picture is real-time — see PLATFORM.md.
- The system IS the work (owner, 2026-09-01): entry happens inside the user's real work, at the moment of work — if the real step cannot complete without the system, adherence is automatic. Never require separate before/after recording; auto-capture where the work is outside the system (leads, machine data).
- Append-only operations: no edit/delete UIs anywhere; every update and correction is an APPENDAGE (new event/append form); audit log is the only source of truth for history; backend may collapse to one table, front-end never exposes edit/delete.
- Tap-first entry: number fields show suggestive TAP-TAGS (common values) before a number pad; typing is the last resort everywhere (office screens included).
7. Open items the screenshots must settle
- Exact field names/terminology per screen (the map in §4 gets filled).
- Job-work rate basis: ₹/meter woven? ₹/pick? beam basis? size charge?
- Wastage conventions on party material (allowed %, who bears it).
- Whether party yarn sometimes arrives already sized beams and similar.
- The billing flow: is it invoiced from this system or elsewhere?
- Which of the two modules currently holds "grey fabric stock" — trading, or a separate register that must be reconciled first.
- What the weavers' own ERP exports (supercedes BUSINESS.md §6 q.4).
8. The ERP interdependency map (C159)
The whole system is one spine: every step is connected to the step before and
the step after, so a fact is typed once and carried, never re-keyed. This
section is the single place that says, for every step: its previous step,
its next step, exactly which fields it hands over, and both entry points.
tests/c159-erp-interdependency-map.test.ts pins it against the real routes,
so a hand-over claimed here that the code does not do fails the suite.
8.1 The rule — the flow is the default, never a compulsion
- Every step may be entered on its own. A booking can be made with no order and attached to one later; an order can exist with no booking yet; a beam can be entered before its tippan is printed.
- What is forbidden is re-keying data the previous step already holds: you type only what the previous step did not record and what is new to your form. Everything else arrives prefilled and the user confirms it.
- So a hand-over is never a lock. "Connected by default" means the button that walks you forward and the prefill that saves the typing — not a rule that blocks the desk.
8.2 The map
| Step | Screen | Previous step (back button goes to) | Next step (forward button) | Hands over | Entry point A: from the previous step | Entry point B: entered directly |
|---|---|---|---|---|---|---|
| 1. Yarn inward | /inward |
— (godown receipt; may start here) | /lots (the lot it creates) |
lot no, yarn, kgs received, party, receipt date | inward slip / gatepass reference | yes — a receipt with no order is normal |
| 2. Yarn stock | /lots |
/inward |
/bookings/new?yarnId= |
lot no, kgs available, party/own | the inward receipt | yes — stocktake, opening balance |
| 3. Yarn booking | /bookings/new |
/lots or the order record's Book yarn |
/gatepasses/new (issue against it) |
booking no, lot/yarn, ordered kgs, order id, delivery date | the lot, or an order | yes — booked before the order exists |
| 4. Order (sales) | /orders/new/trading, /orders/new/job-work, /orders/new/production-order |
/quote/edit (quote → order), /leads (lead → order), /orders |
/bookings/new, /tippans/new, /production/*?orderId=, /dispatch?orderId= |
party, quality, qty + unit, rate, dates, payment dhara, brokerage | a quote, a lead, a capture draft | yes — entered at the desk with nothing behind it |
| 5. Sizing / tippan | /tippans/new |
/orders/[id] (Tippan), /bookings/new |
/sizing?tippanId=, /beams |
tippan no, warp qty + count, weaver/unit, quality | the order, or the booking's yarn | yes — a tippan printed straight from the master's quantity |
| 6. Beams | /beams |
/tippans/new |
(weaving — see gap G2) | beam no, ends, mtrs, warp/weft yarn issued | the tippan | yes — a beam entered from a handwritten slip |
| 7. Weaving / production | /production/trading, /production/job-details, /production/job-commercial, /production/production-order |
/orders/[id] (Open in … production, ?orderId=), /beams |
/checking (see gap G3) |
mtrs woven, beam no, wastage, rolls, per-piece mtrs, dispatch city | the order record | yes — a unit's own slip keyed in |
| 8. Checking / grading | /checking |
/production/* |
/dispatch?orderId= |
checked mtrs, grade A/B/C, pieces, rejects, points | the woven lot | yes — a buyer's own check |
| 9. Dispatch | /dispatch |
/checking, /orders/[id] |
/hishob (job-work print unlock) |
PS/challan no, rolls, mtrs, grade, vehicle, transport | the checked stock | yes — a returned/consigned dispatch |
| 10. Job-work hishob | /hishob |
/dispatch |
/bookings (party yarn reconciled back) |
job-work mtrs, conversion rate, hishob legs | the dispatch | yes — a leg recorded from a printed slip |
| 11. Invoice / payment | the order record's print (/orders/[id]/print) + the payment dhara terms on the order |
/dispatch |
— | invoice no, mtrs, rate, GST, dhara due date | the dispatch | yes — see gap G4 |
Job-work / production counterparts of the same spine: steps 5–10 have a
job-work face (/production/job-details, /production/job-commercial,
/hishob) and a trading/production face (/production/trading,
/production/production-order) — the same hand-over, valued in a different
book (§2). An order of kind job-work walks steps 5–10 through the job-work
face; a trading or production-order walks them through the trading face.
8.3 Today's real gaps (recorded, not papered over)
- G1 — the order record's "Book yarn" button loses the order. It links to
/bookings/newwith no?orderId=, so the booking opens empty even though/bookings/newalready reads?orderId=and can prefill from an order. (The fix is a queued correction; until then the booking is re-keyed — the one thing §8.1 forbids.) - G2 —
/beamshas no forward link to the production forms; weaving is reached from the order record. - G3 —
/production/*has no forward link to/checking; checking is reached from the order record / dispatch. - G4 — there is no invoice/payment screen. Invoicing is the order record's print; payment is the dhara/terms on the order. Payment receipting is not in the app yet, so step 11 has no "next" step and no screen of its own.
- G5 —
/gatepasseslinks to/movements, not forward to a tippan; the issue-then-size walk is done from the order record.
8.4 Machine-readable hand-over pairs (pinned by the test)
Each from -> to pair below is a real link in the routes: the from route
references the to path. Adding a step-to-step link without naming it here (or
naming one that does not exist) fails c159-erp-interdependency-map.test.ts.
/inward -> /lots
/inward -> /gatepasses
/bookings -> /bookings/new
/bookings -> /gatepasses/new
/orders/[id] -> /bookings/new
/orders/[id] -> /tippans/new
/orders/[id] -> /production/trading
/orders/[id] -> /production/job-details
/orders/[id] -> /production/job-commercial
/orders/[id] -> /production/production-order
/orders/[id] -> /dispatch
/gatepasses -> /movements
/tippans -> /sizing
/tippans -> /beams
/tippans -> /bookings/new
/sizing -> /reconciliations
/checking -> /dispatch
/dispatch -> /hishob
/lots -> /reconciliations
9. The production order as a cost bucket (owner re-walk, 2026-10-30)
The canonical reading of the production-order lane. Rows C364–C373 implement it; a change that contradicts a line here is wrong even if its own test passes. PLANNING §650 is the session transcript.
9.1 Two ledgers. Physical inventory (kg, mtrs, location) and cost are separate books. A gatepass moves quantity and never creates cost. Cost enters a PO only when a human earmarks a cost document to it — procurement and PPC apportion each booking and bill as they land. Nothing auto-attaches; the system offers what is unallocated and shows the drift.
9.2 The PO is a program and a bucket. It is the instruction to supply chain (spec, parties, rates) and the bucket the costs fill. One quality ideally, but several picks, tippans and weavers. Its cost is therefore per lot, pick-wise — the fabric pool key is quality+pick — never one ₹/m for the whole order. An SO-linked PO carries the accepted quote's planned rates over its own planned quantities as a budget; a stock PO has none. A quote is frozen once an SO exists.
9.3 Yarn. Earmarked purchases never enter the free pool's WAC: they carry their own landed rate (rate × received kg + freight + purchase brokerage, short receipt absorbed) direct to the PO. Unearmarked yarn pools by count+type+grade. The split is fixed at booking; the physical kg stay liquid across orders — quantity reconciles, cost never moves with the physical draw. An issue consumes its own earmark first, then free stock at WAC.
9.4 Commitment timing. Cost commits at consumption — yarn when it leaves, each service at its booking — and never at fabric-in. Fabric-in only converts WIP to output and fixes the divisor. A PO's unit cost is provisional until the last bill, and any screen showing it must say so.
9.5 Weaving has no ₹/m. The booking fixes ₹/pick. Per-metre weaving is
₹/pick × actual picks/m ÷ 100 of the fabric actually dispatched. A sale
order carries the rate of the PO it was apportioned against and the
pick of the fabric delivered (SO-A carries rate of PO-1 but pick of PO-2). A substitution (higher pick delivered for a lower-pick SO) is an
additional cost that must show in the SO's P&L; the SO's committed rate never
moves. Market or stock fabric apportioned as such has no weaving leg at all.
9.6 Tippan and beams. A tippan is a routing instruction to the sizer — warp spec, yarn issued, destination weaver, N empty beams collected from that weaver, approximate mtrs. Its only costing touch is the warping and sizing bill, pro rata when a beam is partly used. A beam (or part) can move to another PO, even of another quality: the receiving PO books it at market value typed by the user, composed leg by leg (warp kg × yarn rate + warping
- sizing) with hints from the latest procurement rate, the day's rate sheet and the yarn master's sizing rate. The differential — gain or loss — stays on the cancelled PO, and the SO apportioned to it is re-apportioned.
9.7 Grades. Within a lot A/B/C/R carry the same ₹/m; the grade difference is entirely in the selling price. X is zero cost and its share of yarn, weaving and checking is pushed onto the lot's sellable mtrs. Checking is charged per checked metre including X.
9.8 Cancellation. Produced fabric joins the free pool at its lot cost; yarn returns to free stock at its carried value (never zero); unstarted sizing or weaving is not charged.
| Row | Rule it enforces |
|---|---|
| C364 | 9.3 — free pool vs per-PO earmark ledger |
| C365 | 9.1/9.4 — the yarn leg comes from the earmark, not the gatepass |
| C366 | 9.2 — po_segment (pick × weaver × tippan) as the cost unit |
| C367 | 9.2/9.4 — lot-level cost with the delivered pick |
| C368 | 9.7 — grade-blind divisor, X at zero |
| C369 | 9.6 — beam transfer at typed market value |
| C370 | 9.2 — budget snapshot from the quote |
| C371 | 9.5 — SO cost per leg at its PO's rates × delivered pick |
| C372 | 9.8 — cancellation release at value |
| C373 | 9.1 — manual bill apportionment |