Commerce ERP
Multi-tenant ERP for Bangladeshi online sellers: orders, couriers, stock, purchasing, finance and fraud in one system.
559 API operations behind Postgres row-level security, covered by 3,000+ unit tests and 400+ Playwright tests.
An ERP for cash-on-delivery e-commerce in Bangladesh, where orders arrive from a website, Facebook, WhatsApp and the phone, and most of the money moves through couriers. The merchant dashboard covers the order board, courier parcels and COD settlement, multi-branch stock with transfers and stocktakes, purchasing with a supplier ledger, finance through to profit and loss, customer segments, and a fraud engine that holds risky orders with its reasons attached. A separate platform console runs merchants, plans, billing and audit. Every tenant table is isolated by Postgres row-level security, money is stored in integer minor units, and order and parcel status can only change through one transition function. Built with a spec-first, test-first AI agent workflow: the architecture specs, review gates and acceptance tests were written first, agents implemented each slice against them, and independent review rounds audited the seams between modules. Pre-launch, not yet deployed.
Problem
Bangladeshi online sellers take orders from a website, Facebook, WhatsApp and the phone, ship nearly everything cash on delivery through several couriers, and keep stock across branches. That usually means spreadsheets plus three dashboards, and nobody can say what an order actually earned once returns, courier fees and ad spend are counted.
Constraint
Many merchants share one database, where one leaked row is another business’s customers and cash. Money has to add up to the paisa across orders, courier settlements and supplier bills. Courier, SMS and fraud-check providers time out, retry, and send the same webhook twice.
Decision
Isolated tenants in the database itself: every tenant table has forced row-level security keyed to a per-transaction tenant id that fails closed when unset, and composite foreign keys block cross-tenant references. Stored money as integer minor units with the server computing every total. Made one pure transition table the only writer of order status, and routed every courier update, whether webhook, poll, scan or manual, through a single parcel mover. Courier calls run as queued jobs enqueued in the same transaction as their cause, with retries and idempotency keys.
Tradeoff
Forced RLS makes every query and background job declare its tenant, and cross-tenant platform work needs a separate privileged database role. Branch-level scoping stays in application code rather than the database. The courier, SMS and fraud-check drivers are built against provider specs but not yet proven on live accounts.
Result
One system spanning 116 dashboard screens and a 19-page platform console over 559 API operations and 164 tables. Stored counters are tested against a full recomputation after random order sequences, and 3,000+ unit tests plus 400+ Playwright tests run at phone, desktop and 4K widths in light and dark themes.