The situation
A make-to-order furniture manufacturer planned its purchasing from one saved search of burn rates and reorder points, one location at a time. Planners could see what they had used. They could not see what was coming in, what was already committed to orders, or what a booked order for an assembly meant for its components three levels down. Buying was reactive, and shortages showed up on the shop floor.
What we built
A React and TypeScript application that runs inside NetSuite from a single Suitelet, with a SuiteScript back end organised as services.
- Item view. For any item, a day-by-day ledger of supply (purchase orders, transfers, work orders) against demand (sales orders, work order components, transfers), across every location or a chosen set, with the running balance.
- Assemblies. Bills of material are exploded to four levels, with duplicate lines added together and phantom assemblies passed through, so a booked assembly shows its effect on every component.
- Forecast. Assembly sales and booked orders are projected forward, and the projection feeds component demand.
- Insights. A background Map/Reduce computes account-wide consumption and shortages and caches the result, because a Suitelet request cannot do that work within its limits.
- Purchasing. Shortages are grouped into proposed purchase orders by preferred vendor, rounded to order multiples. A buyer who holds purchase order permission in NetSuite can create the order from the proposal.
- Work orders. Proposed builds are listed for planning. The app shows them and does not create them, and the page says so.
What it does now
The application is live in production, and we are still extending it with the planners. Before release, we reconciled its item list row by row against the planners' original saved search, using a comparison script that fails on any row it cannot explain. We run that check again whenever the logic changes.
Creating a purchase order is the only write the application makes. Everything else is a read, so a planner can explore without changing anything in the account.
Under the hood
- Account-wide work runs as a Map/Reduce that streams a summary search in pages. Other search methods stop silently at 4,000 or 5,000 rows.
- The one write checks the calling user's own purchase order permission, because the Suitelet runs with elevated rights.
- Type contracts cover the SuiteScript back end and the React front end. A check script runs the type checker, the Vitest suite and the linter before every commit.
- The build enforces a size budget on the front-end bundle.
- Deployment uses SDF, and a production deploy requires a typed confirmation.
- The bill of materials walk stops at four levels, a depth measured against real data, which also guards against cycles.