Case Study
Building Wardrobe PLM: How I Architected an Apparel ERP from Tech Pack to Shipment
Building Wardrobe PLM: How I Architected an Apparel ERP from Tech Pack to Shipment
A garments factory is a machine made of people, and most of them were running on spreadsheets. That was the starting point for Wardrobe PLM, an ERP I architected for a vertically integrated apparel group that runs everything from yarn to finished shipment under one roof.
This is the story of why we built it, how it's put together, and what I'd do differently if I started again today.
The problem: everyone had their own version of the truth
Before Wardrobe PLM, a single order would travel through the company as five or six different documents. Merchandising kept the tech pack and BOM in one spreadsheet. The sample room kept its own comments in another. Costing had a third version that was rarely updated once bulk production started. By the time an order reached the floor, nobody could say for certain which BOM was the real one.
A few things made this especially painful:
- Re-typing the same data, over and over. The same style, colour and quantity got keyed in again for the knitting program, the dyeing batch card, the cutting plan and the packing list. Every retype was a chance to introduce an error — and some of them did.
- Costing that was always a month late. Pre-costing assumed planned consumption of yarn, chemicals and trims. Nobody found out the real consumption — and whether the order actually made money — until the month-end numbers came in.
- Stock you couldn't trust. Across four sites and six different store types (yarn, grey fabric, finished fabric, chemical, finished goods, general), stock counts were reported on different days, in different formats, by different people.
- Access with no shape. The group has two separate legal companies plus a head office, and some departments — accounts, HR, IT, store — serve both. The old tools had one flat list of roles, so either people saw far more than they should (payroll across both companies, for instance), or an admin created a slightly different role for every possible combination of person and department.
- Compliance that depended on memory. Buyer audits, bond and UD paperwork, VAT invoice numbering — all of it needed records that couldn't be quietly edited after the fact, and nothing enforced that.
The solution: one style record, one access model, no exceptions
The core idea behind Wardrobe PLM is simple to say and took real discipline to enforce: a style is created once, and that same record drives everything downstream — the order, the BOM, the knitting program, the dyeing batch card, the cutting plan, the packing list, the shipment. No department gets its own copy.
That covers the product side: tech pack, sampling, BOM, pre-costing, order booking, time-and-action, then the textile chain (knitting, dyeing, printing, embroidery), garments (cutting, sewing, finishing), quality, and finally stores and shipment. HR, payroll, compliance and accounts sit alongside, consuming the same data rather than keeping their own.
The part I'm most proud of, though, is underneath all of that — the access model.
Role answers "what can you do." Scope answers "where can you do it." Those had to be two separate things. "HR Manager" is a role. Whether that HR Manager can see one factory or both companies is a scope, attached to the specific assignment. That one separation meant we never had to invent a new role every time someone's job touched a new part of the business — a role like Internal Auditor can be scoped to "everything, read-only" for one person and to "Knitwear garments only" for another, without creating two roles.
Underneath that:
- A generic organization tree, not a fixed set of tables for company, division, department. One
org_unitstable with aparent_idand a type column scales from a 3-level branch to a 6-level one without a schema change — which mattered, because our real org chart turned out to be exactly that uneven. - A separate location tree for the physical world — site, building, floor, line, warehouse — because a department and a physical floor are not the same axis. A quality manager's role scope might be "Garments Division"; their location scope might be "Factory 2 only."
- Approvers as positions, not people. "Department Head of the requestor unit," resolved at runtime, so a resignation or promotion never breaks a pending approval chain.
- One shared authorization service, called by every module. No module — not production, not accounts, not HR — was allowed to write its own access check. That rule was harder to hold the line on than it sounds, but it's the reason the system stayed consistent as it grew to around 20 modules.
Architecturally, it's a modular monolith: one Laravel application, MySQL 8 underneath, with clear boundaries between domain modules but one shared core for authorization, approvals, document numbering, notifications and audit — all sitting behind a single service layer that every module calls into, rather than re-implementing.
What changed
(Numbers below are placeholders — swap in your verified figures before publishing. A couple of solid, real numbers will carry this post further than a long list of estimates.)
- Daily production reports went from [before] to [after].
- Pre-cost vs. actual cost variance per order dropped from [before] to [after].
- Sample rounds to buyer approval went from [before] to [after] per style.
- Purchase requisition to approved PO went from [before] to [after].
- Stock accuracy at physical count improved from [before] to [after].
- Monthly payroll processing time went from [before] to [after].
What I'd do differently
The hardest call was choosing one generic organization tree over separate hard-coded tables for company, division and department. The fixed-table version would have been faster to build and easier for a new engineer to read on day one. I went with the generic tree anyway, because the business kept changing shape under us — new profit-center divisions appeared, departments didn't sit at a uniform depth, shared services crossed company lines — and none of that needed a migration once the tree was in place.
A few things I'd change if I started over:
- Model the physical location tree from day one. We modelled factories, floors and lines as organizational units at first, and untangling that later meant re-tagging a lot of records.
- Freeze the shared columns before building modules, not after. Every business table needed an entity, business unit, org unit, location, data class and owner. Adding those after modules already existed was the most expensive rework of the whole project.
- Write the access test suite before writing the access engine. A short list of plain-English scenarios — "the Knitwear HR Manager cannot see WOVEN payroll" — caught more real bugs than code review ever did, and should have been the spec from day one, not an afterthought.
If there's one thing the project taught me, it's that access control is a data-modelling problem before it's ever a coding problem. Get the organization's real shape into the schema, and the permissions almost take care of themselves.