The Production Tally Sheet bridges WIP Tracking and Finished Goods. The floor team uses it to capture the raw piece-count coming off a machine across a shift — eight hourly slots of good, scrap and rework, plus operator initials. When the supervisor confirms the tally, the document is locked, the recorded net_qty becomes the authoritative quantity entering QC inspection, and the next-step FGRN is unlocked.
This document mirrors the Sage X3 "Production Tracking", Syspro "Operation Time and Quantity Posting", SAP "Production Order Confirmation" (CO11N), and NetSuite "Work Order Completion" partial-confirmation. Where Sage uses a numeric grid in the Operation tracking screen, PalmStat captures the same data on a printable A4 with hourly rows that match the operator's clock-on / clock-off cadence.
The tally sheet is the only document in the lifecycle owned primarily by an operator (not a planner / supervisor). The 4-state lifecycle (DRAFT → SUBMITTED → CONFIRMED → REJECTED) is enforced at the API layer.
PLANNED or IN_PROGRESS — the tally tile becomes available on the Tally Sheet page.DRAFT and continued by the next shift's operator.SUBMITTED and awaits supervisor sign-off.CONFIRMED, document locks, QC inspection unlocks.REJECTED and operator must recount.The tally-sheet view (frontend/js/views/tally-sheet.js) calls API.tallySheets against the tally_sheets table introduced in the route's ensureSchema() bootstrap. Every row below is wired into a real column.
| Field on document | API endpoint | DB table.column | Example value |
|---|---|---|---|
| Tally Ref No. | auto-generated | tally_sheets.tally_ref | TALLY-260508-4218 |
| Batch ref | GET /api/production-batches | production_batches.batch_ref via FK batch_id | BATCH-2026-0481 |
| Batch status | GET /api/tally-sheets (LEFT JOIN) | production_batches.status | IN_PROGRESS |
| Shift | POST /api/tally-sheets | tally_sheets.shift | DAY / NIGHT |
| Shift date | POST /api/tally-sheets | tally_sheets.shift_date | 2026-05-08 |
| FG product code & name | GET /api/products | products.code & products.name | A4_72PG_FM · A4 72-page FM |
| Machine code & name | GET /api/machines | machines.code & machines.name | RUL_4 · Ruling 4 |
| Planned qty | denormalised | tally_sheets.planned_qty (mirror of production_batches.planned_qty) | 5 000 |
| Hourly grid (8 rows) | PATCH /api/tally-sheets/:id | tally_sheets.hourly_counts (JSON) | [{slot, count, scrap, initials} × 8] |
| Tally total (good) | computed on submit | tally_sheets.tally_total | 4 980 |
| Scrap total | computed on submit | tally_sheets.scrap_total | 40 |
| Net qty | derived | tally_sheets.net_qty (= tally_total − scrap_total) | 4 940 |
| Operator clock no. | POST /api/tally-sheets | tally_sheets.operator_clock_no | 1042 |
| Operator name | POST /api/tally-sheets | tally_sheets.operator_name | P. Mokoena |
| Assistant clock & name | POST /api/tally-sheets | tally_sheets.assistant_clock_no / assistant_name | 1108 / S. Ndlovu |
| Supervisor clock & name | POST /api/tally-sheets/:id/confirm | tally_sheets.supervisor_clock_no / supervisor_name | 0901 / N. Williams |
| Supervisor signed at | auto-stamped on confirm | tally_sheets.supervisor_signed_at | 2026-05-08 14:12 |
| Status | state machine | tally_sheets.status | DRAFT → SUBMITTED → CONFIRMED |
| Reject reason | POST /api/tally-sheets/:id/reject | tally_sheets.reject_reason | "Hour 3 missing scrap" |
| Notes (downtime, machine) | PATCH /api/tally-sheets/:id | tally_sheets.notes | "Breakdown 11:00–11:45" |
| Created by | req.user | users.email via created_by | operator@palmstat.co.za |
/tally-sheet route. View hydrates from API.tallySheets.list() + API.productionBatches.list().PLANNED or IN_PROGRESS with no CONFIRMED tally yet. Operator clicks the tile for their machine.recalc() function.POST /api/tally-sheets on first save (creates the row), PATCH /api/tally-sheets/:id on subsequent updates. Status remains DRAFT; the document is editable.POST /api/tally-sheets/:id/submit validates that at least one count row is > 0 and flips status to SUBMITTED. The document is now read-only for the operator and visible to the supervisor.POST /api/tally-sheets/:id/confirm stamps supervisor_clock_no, supervisor_name, supervisor_signed_at. Status → CONFIRMED. The document is locked. QC inspection is now unlocked, and the FGRN can be issued.POST /api/tally-sheets/:id/reject with a reject_reason. Status → REJECTED. Operator must reopen, fix and resubmit.document_links ties it to the underlying batch.| Action | Admin | Operator | Supervisor | Planner | QC |
|---|---|---|---|---|---|
| Generate / create tally | ✓ | ✓ | ✓ | ✓ | – |
| Edit hourly counts (DRAFT) | ✓ | ✓ | ✓ | – | – |
| Submit (operator) | ✓ | ✓ | ✓ | – | – |
| Confirm (supervisor sign) | ✓ | – | ✓ | – | – |
| Reject with reason | ✓ | – | ✓ | – | – |
| Reprint after CONFIRMED | ✓ | ✓ | ✓ | ✓ | ✓ |
| Delete / void | ✓ | – | – | – | – |
Permission gate enforced by requireRole('admin','supervisor','operator','planner') on POST routes; submit/confirm endpoints add stricter role checks.
Happy path: Operator captures hourly counts, totals 4 980 good + 40 scrap = 4 940 net. Submit at 14:00, supervisor confirms within 30 min. Tally ref TALLY-260508-4218 is locked.
Happy path: Breakdown 11:00–11:45. Operator records 0 count for that hour, adds note "Breakdown 11:00–11:45". Supervisor cross-checks against machine_breakdowns and confirms.
Happy path: Day operator captures rows H+1–H+6 and saves as draft. Night operator opens the same tally, picks up at H+7. Both initials are captured per row. Supervisor confirms after Night shift.
Sad path: Day operator forgets to save before clocking out. Counts are lost. Recovery: planner opens the original Job Card paper trail, manually back-fills the tally rows and supervisor signs with a "back-filled" note.
Happy path: Supervisor flags a 200-unit gap between operator tally and an interim machine output reading. Tally is REJECTED with reason "200-unit variance vs machine counter at 12:00". Operator re-counts from the FG pallets, identifies an over-recorded H+5 row, re-submits. Supervisor confirms.
production_batches in PLANNED / IN_PROGRESStally_sheets.net_qty as the source of truth)machine_breakdowns (downtime context)
| Stage | Target | Owner | Escalation |
|---|---|---|---|
| Hourly capture | Within 15 min of hour-end | Operator | Supervisor on hour |
| End-of-shift submit | Within 30 min of clock-out | Operator | Supervisor |
| Supervisor confirm | Within 60 min of submit | Supervisor | Production Manager |
| Reject → resubmit | Same shift | Operator | Supervisor + Quality |
| QC follow-on | Within 2h of CONFIRMED | QC Inspector | Quality Manager |
tally_total − scrap_total. The number that flows into the FGRN as actual_qty.reject_reason. Operator must fix and resubmit.