Cross-Border ERP
A working ERP for a company selling equipment across the Canada/US border, where the customs paperwork is the part that costs money when it is wrong.
This is a working ERP for a company selling equipment across the Canada/US border. Quotes, orders, inventory, purchasing and CRM all feed one document that matters more than the rest: the customs declaration, which is the part that costs real money when it is wrong.
The declaration builder assigns each line to an inventory source, splits a line across sources when one lot cannot cover it, and rolls the result into an addendum broken out by country of origin, with weights and values per side of the border. FIFO auto-assignment handles the common case and a manual override handles the case the algorithm gets wrong. Five validation gates count what is still wrong rather than accepting the form: missing origin, missing weight, missing value, unassigned lines, and purchase-order shortage, each a live filter you can click into.
What we measured
Around it sits the rest of the system. A margin tool that doubles as the quote builder, pulling a part from the catalogue with its real average cost so the margin is computed rather than typed, in CAD or USD at a live rate. One-click conversion from a saved quote into a sales order, with the originating quote recorded on it. A parts catalogue with cost rollup, bills of material, purchase orders, suppliers, a production log, and a CRM. And an audit log whose entries can be rolled back, because the expensive mistakes in an ERP are the ones nobody notices for a month.
Why it holds
An ERP is not hard because the features are hard. It is hard because the data is shared: a quote line, a stock allocation and a customs line are the same object seen three ways, and if they can disagree, they will. Most of the design effort went there rather than into screens.
One habit came out of this codebase and stayed. A defect class in it had the whole design token layer silently producing invalid CSS, so the application rendered almost right for months without a single error in any log. Measuring rendered output, rather than trusting that a build succeeded, is now the default.
SCOPE AND LIMITS
This was built for one company’s trade lane. The declaration logic reflects their commodity mix and what both sides of that border ask for, so it is a worked system rather than a general customs product, and a different lane would need the rules revisited. It is self-hosted and portable: it builds to a folder that runs on a Windows machine on site, with scheduled backups, a monthly backup parity check, and a startup health gate that refuses to launch on a bad database.
STACK
- Next.js
- TypeScript
- React
- Prisma
- SQLite
- Tailwind CSS