A womenswear seller with its own sewing factory: four seller accounts across two major e-commerce marketplaces, five production workshops, fabrics with lead times up to 38 days. The system computes the whole production order: which items, in which batches, in which weeks to cut and sew, which fabrics to order and when, and what to ship to which marketplace warehouse.
The client owns a sewing factory and sells through four storefronts on two marketplaces. There is one factory and four sources of demand. Every week somebody sat down and worked out by hand what to move and where to squeeze it in.
The same item sells from four storefronts. To know how much to sew you must add up the orders of all four, then send the finished batch back in proportion to their sales. In spreadsheets this was done by hand and drifted apart.
Fabric lead times reach 38 days, while the cut-to-shelf cycle fits in three weeks. So the fabric order is decided a month and a half before the item reaches the shelf. Miss the ordering point and you miss the season.
How many hours a week cutting, sewing, ironing, quality control and packing actually deliver, nobody knew. The plan was set by feel, and the bottleneck surfaced mid-week, when moving anything is already too late.
Fabric consumption norms and operation roubles sat in personal spreadsheets. The same item cost different amounts in different files, and every profit calculation started with an argument about the numbers.
Every night Celery pulls four seller accounts from two marketplaces into PostgreSQL. Production reference data and the planning engine turn orders into weekly batches. People work in a React portal, managers see the same truth in a Google Sheet, urgent things arrive in Telegram. External systems are read only: the system writes nothing back.
Demand per item is assembled from the actual orders of all four accounts, not from plans and not from wishes. It is then spread across the weeks of the horizon with the seasonal coefficient of the category and a manual correction when a manager knows something the history does not.
The pace is counted on orders: a buyout arrives later and shifts the picture two weeks back. For the decision "launch a batch or not" what matters is the speed of demand today.
A category has its own monthly profile, and a single item can override it. The coefficient sits in the table as its own column: a person sees exactly what lifted the plan.
A manager can set the pace and a multiplier by hand, with a comment. The correction does not dissolve into the calculation: in the "Why" block it stands as its own line with the author.
Day boundaries follow Moscow time. The server lives in UTC while the business counts a day in Moscow. Until that was pinned down, evening orders fell into the next day and the sales pace jumped for no reason. Every analytics period is now cut by the Moscow day.
The engine places batches across weeks so that the profit of the horizon is the highest and the workshops are not overloaded. Every batch has three weeks: cutting, sewing, delivery. A batch is a multiple of the minimum launch: a lay smaller than that does not pay off the cutting.
A search over launch options with the obviously worse branches cut off, then a rebalance across workshops. The result is a version of the calculation: it can be compared with the current plan and only then made current.
Every run is saved as its own version with its profit and batch count. The previous working plan does not vanish: unlaunched batches are closed, launched ones live on through their own life cycle.
A fabric with no lead time, an item with no bill of materials, a workshop rate still under review: the engine says so in a list at the top of the screen. A silent default would be unacceptable here: the plan would look honest while being made up.
What will not be sewn before the shelf runs dry is shown in roubles next to the horizon profit. That is the only way to compare "launch now" and "wait" in the same units.
Five workshops, each with its own capacity and its own rate. The load is computed on the fly on every request: the resulting hours and percentages are stored nowhere, otherwise the load would have two versions of the truth.
Example: 3 people, 8 hours, 5 shifts, a 12% reserve and no outsourced hours give 3 × 8 × 5 × 0.88 = 105.6 hours. Sewing a skirt at 495 ₽ with a sewing rate of 2,202 ₽ per hour is 0.225 hours, that is 13.5 minutes per piece; a batch of 240 takes 54 hours.
Empty is more honest than zero. If a workshop has no headcount, it has no capacity: the cell is grey with the reason "the ironing workshop has no headcount", not a zero and not 100%. If outsourced hours ate the whole capacity, the batch hours are still counted but there is no percentage, and the workshop header keeps the number with an explanation. There can be several reasons, and the cell puts them into one sentence.
Fabric travels between 9 and 38 days depending on the supplier. The system takes the cutting week, subtracts the lead time and the time buffer and gets the date after which ordering is too late. Rows whose ordering point has passed stand in red at the top.
Two buttons on a calendar row: the first creates an order with a planned arrival date, the second confirms the arrival with the actual date and metres. The gap between plan and fact is visible and feeds the next recalculation.
On top of the lead time sits a buffer for a supplier slipping and for defects. It is a setting, not a constant in the code: a supplier's lead time changes with a phone call, while a code release takes time.
When the ordering point is due, the manager gets a message. Waiting for someone to open the tab is not an option here: a missed fabric order costs a month.
There is one working plan. A batch goes through six states, and at every transition the system takes the fact: a date and a quantity. Any gap against the plan is visible at once and takes part in the next calculation.
Where to ship is computed, but edited by hand. By default a finished batch is split between accounts in proportion to their orders over the period. A manager can rewrite that split: they have reasons the data does not carry, for example a warehouse that stopped accepting supplies.
Underneath it is ordinary theory of constraints. None of its vocabulary reaches the screen: people on the shop floor use different words, and there is no reason to teach them a glossary for the sake of an interface.
| In the code and the spec | In the interface |
|---|---|
| item_key | Item |
| bill of materials | What we sew it from |
| consumption norm | Fabric per piece, m |
| logistics lead time | Fabric in transit, days |
| production cycle | From cutting to the shelf, weeks |
| buffer | Time buffer |
| reorder point | Order by |
| workshop capacity | Hours per week |
| branch and bound | the "Run the plan" button |
| margin per unit | Earned on one piece |
Every computed number carries a "how this was counted" tooltip: the formula in words and the numbers substituted into it. A number that cannot be explained in ten seconds counts as unfinished in this interface.
| Before | After |
|---|---|
| Demand was counted per account, while the factory is one. | Orders of four accounts are added up by item key, and the finished batch is sent back in proportion to their sales. |
| "What to launch" was decided by a person with a spreadsheet. | The engine proposes batches across weeks and names the horizon profit and the lost sales in roubles. The decision stays with a human, but now it comes with a number. |
| Workshop capacity was a feeling. | Five workshops with separate capacity and weekly load, and a red cell is visible before the week begins. |
| Fabric was ordered whenever somebody remembered. | An ordering calendar counted backwards from the cutting week, with an "order by" date and a Telegram signal. |
| Cost lived in personal files. | Bills of materials are uploaded as a template and become the single source of norms and operation roubles for every calculation. |
| Nobody could explain where a number came from. | Every computed number carries the formula in words with the substituted values, and every batch carries a "Why" block. |
Business analyst and systems builder. I start with the business process and the data contract, then automate. Here the manual guesswork of "what to move and where to squeeze it in" is replaced by a calculation that can explain itself.