~/openarch/work/marquise-// case study

A single-tenant ERP for a Kolkata jewellery manufacturer. Job sheets, packaging and diamond sheets, and an append-only inventory ledger - with exports that mirror the operator's paper form field-for-field.

ShreeKunj·jewellery manufacturing · ERP·2026·in production
openarch.in/work/marquise
Marquise interface
//the brief

What we built.

ShreeKunj ran manufacturing on paper job sheets and spreadsheet rate cards. Marquise moves both into one system without asking the floor to relearn its own paperwork: the PDF and Excel it prints match the original form line for line. Underneath, every write is diffed field by field into an append-only audit log, and rates that used to be typed from memory are pulled live.

// The paper form, kept honest.

domainjewellery manufacturing
shapesingle-tenant ERP
statusin production
versionv3.2
stack
angular 17node · expresspostgrespdfkit · exceljspdfjs · bwip-js
//what we shipped
01

Job sheets

Platinum, gold and silver variants, each drawing its own jobcard series from an atomic per-material, per-fiscal-year counter.

02

Packaging sheets

Two rate streams behind one form - a platinum sales rate card and a purchase packing sheet that lands imported metal cost through duty to wastage.

03

Diamond sheets

Per-stone records with certificate and inscription capture, exported as an A3 landscape sheet built for the bench, not the browser.

04

Inventory ledger

An append-only movements table. Stock is never stored - it is aggregated as inward minus outward, so the ledger and the position can't disagree.

05

Audit by construction

Every update snapshots before and after and records a per-field diff, down to individual rows inside an item or diamond array.

06

Scan and print parity

A QR per sheet and a Code128 barcode per item, read by USB scanners on the floor. Emailed attachments are byte-identical to downloads.

//data model
schema.sql5 tables
table job_sheets
material, customer_id, jobcard_no, qr_code, status, part_a_date
table job_sheet_items
job_sheet_id, item, factory_job_no, weight_of_pt, barcode
table rate_cards
type, card_date, packaging_data, status, created_by
table inventory_movements
movement_type, source_ref, design_no, net_wt, created_at
table audit_log
user_id, action, entity_type, entity_id, details, created_at
// sheet lifecycle
  1. 01draft
  2. 02submitted
  3. 03closed
//business logic
jobcard.tsJobcard numbering
// <PREFIX><FY><NNN> - SALCO2627055 is platinum #55, FY 2026-27.
// The counter is claimed atomically per material, per fiscal year,
// so two operators saving at once can never collide.
function composeJobcardNo(prefix, fy, num) {
return prefix + fy + String(num).padStart(3, '0')
}
norm-value.tsDiff normalisation
// Dates are read in local time, never toISOString().slice(0,10)
// - east of UTC that shifts the day and fabricates a diff.
function normDate(d) {
return d.getFullYear() + '-' +
pad(d.getMonth() + 1) + '-' + pad(d.getDate())
}
// "2.000" and "2" collapse to the same value before comparing.
//on the roadmap

Honest edges.

  • →Retiring the legacy packaging sheet table once back-compat lapses
  • →Widening bulk import beyond the four July-2026 batch templates
  • →Moving remaining hardcoded duty constants behind operator-editable fields
//result
166
sheets bulk-imported in one month
1
process serving API and app
0
writes without a field-level diff

Marquise replaced paper job sheets and spreadsheet rate cards with one system whose printed output the floor still recognises. A July 2026 migration loaded 131 sales packaging sheets and 35 diamond sheets through purpose-built importers, each dry-run before commit. Prism, the assistant layer, is built on the records this system keeps.