A womenswear seller with its own sewing factory: four seller accounts on Wildberries and Ozon, two major e-commerce marketplaces, its own finished goods warehouse and a sewing floor of two teams under their foremen. The system answers the three questions of every week: what to ship from the own warehouse to the marketplaces, what to sew and on which sewing line, and how the week is going in fact. Next to that: money that matches the client's accounting system to the kopeck, seasonality from the seller's own sales history, and an item card with two cost prices.
The client has its own sewing factory, its own finished goods warehouse and four storefronts on two marketplaces. Every week the planner put the answers together by hand: what to ship to the marketplaces, what to put into sewing, whether there are enough people. Money was counted in two places and did not match.
Twice a week for every account: the marketplace catalog, the 1C stock, the marketplace stock, the seller warehouse and the order feed. The order feed has no barcode, so the key was glued from article and size, and lookup errors were replaced with zeros by hand. The reserve between accounts was zeroed, and the same unit could go to two accounts.
One article sells from four storefronts. How much of it to sew shows only in the sum of orders across all accounts. And apparel buyout is far from a hundred percent: part of the orders come back to the warehouse and sell again.
Hours per garment and how many people a foreman has. Piece rates in roubles existed, but turning them into hours through an average rate is too rough. The client's requirement was blunt: the plan needs hours, not money.
The totals matched to the rouble, but the base of display was different. The accounting system counts revenue by buyer receipts, while the portal divided margin by the seller price, which is 1.6 to 2.4 times higher than the receipts. The portal margin came out several times lower than in 1C.
The factory price list for reports and the technologist's costing sheets, where coefficients and a hand-typed number follow the total. Which figure was right depended on who was asking.
Seasonality was not set up either in the accounting system or in Excel. Every article has its own season: suit trousers and linen trousers of one category sell in different months. And the marketplace gives no more than a year of history.
At night Celery pulls four accounts on two marketplaces into PostgreSQL: daily orders, the sales funnel, the marketplace size report, stock, accruals and advertising. Warehouse stock, hours from the work-norming department and bills of materials arrive as files. The modules compute restock, the plan, demand and money on the fly from the latest snapshots. People work in a React portal, managers see the same truth in a Google Sheet, and deviations gather on the Signals page.
Restock became the first tab of production: it is a daily question, not a monthly table. A row is one barcode of one size in one account. The table is not stored anywhere: it is computed on the fly from the latest stock and order snapshots and repeats the planner's Excel calculation formula for formula.
In words: ship enough to cover a week of sales with three more units left on the shelf, but no more than the warehouse holds. Every number on the screen has its substitution: 6 orders in 3 days, 2.0 a day × 7 days = forecast 14; stock 6 - 14 = shortfall -8; safety 3 - (-8) = 11; warehouse 53 - reserve 0 = 53; restock min(11, 53) = 11.
The calculation file is not an export of numbers: eight sheets match the planner's file sheet for sheet, row formulas match character for character, reconciliation columns against the portal sit on the right, plus a legend sheet. A test evaluates the workbook formulas as Excel does and demands a match with the portal. A person checks the system in a familiar layout and spots an error either way.
The funnel matches the account but knows no sizes. The order list knows sizes but is smaller than the funnel. The answer: the marketplace size report day by day with a daily reconciliation against the funnel. On a day without a report each size takes its own listed orders, and only the difference is split by shares. A test on 300 random cases holds the invariants.
The Create a list for the warehouse button issues a document that is open at once. An open document reserves its units from other accounts and closes itself when a 1C snapshot newer than its date arrives. One unit no longer goes to two accounts.
A background job with a status and a percentage, a lock against a double click, and a build stuck for 10 minutes reads as an error with a Retry button. Fresh marketplace stock is taken before the build; a marketplace refusal does not break it, the file is built from the last snapshot and the reason is said in words. 5,500 rows build in 6.6 seconds.
Before applying, the window says what will happen: this many sizes will be applied, this many sizes of the previous snapshot will become zero. Replacing it whole is deliberate: the accounting export lists only what is there. A file of another warehouse is refused, an empty upload is not applied.
Archived articles are included in restock by default. Archived means no longer sewn, while the stock is still being sold and has to be shipped: almost half of the orders fall on models already out of production.
What the calculation does not see is written right on the screen: supplies to marketplace warehouses, goods on their way to a buyer, the second account of the same marketplace. Without a marketplace snapshot the stock shows as a dash with a note, while the arithmetic takes it as zero and says accepted as 0: otherwise such a row could never be shipped. A row with a shortfall and an empty warehouse gets the nothing to cover note. It is a candidate for sewing and the bridge to the plan.
Whether to sew is decided by a person, by money and by the traits of an article. The plan's job is to lay out a decision already taken across weeks and sewing lines, size the batches, set the dates, count the fabric shortage and go to the factory as a file.
A batch sewn in the last week of a three-week horizon reaches the marketplace past the edge of the run, and the engine saw zero demand after arrival. So run weeks = horizon + arrival lag + cover window = 3 + 2 + 6 = 11, and batches are placed only in the first three. The exact search sped up on the way: 1,141 variants per item instead of 20,489.
The exact search needs a criterion to compare variants, so the engine still counts profit, while the answer goes out as a copy without money keys at any depth. The why texts are rewritten in pieces, hours and dates. The workbook has a second lock: a test walks every cell and lets no words about roubles, profit or margin through.
A ticked article is always placed, even when by its stock it is too early to sew: then it goes to the first week of the horizon with the words sewn by a human decision. The only refusal is a batch that does not fit the horizon capacity.
Every run keeps the whole engine input, and an edit is recomputed from that same input without a trip to the database. Two people editing one run get two branches: nobody overwrites anybody.
The Python engine is checked against the prototype engine on six items: the same layout, the same batches, the same number of search nodes. Even JavaScript rounding is reproduced as floor(x + 0.5).
In the workbook the total is computed from exact hours rather than by adding rounded rows. Otherwise the file would argue with the screen over a tenth of an hour.
Honest limits. The run does not yet see batches in progress either as arrivals or as busy hours, so their ticks are cleared and the In progress column warns about a repeated tick. There is no fabric order calendar with statuses yet: the fabric shortage and the order-by date are computed and shown as diamonds on the gantt, while the order itself is placed by people.
After a meeting with the factory only sewing is planned, in two lines under their foremen. Cutting, quality control, ironing and packing are switched off the load, while their roubles still go into the cost price. Scheduling every workshop rigidly does harm: the factory loses room to manoeuvre.
An example on made-up numbers: 20 people at 8 hours, 5 shifts and a 3% reserve give 20 × 8 × 5 × 0.97 = 776 hours a week. A garment with a norm of 2.03 hours in a batch of 180 takes 365 hours, almost half the line's week.
Hours per piece come from one function for the engine, the advisor, the dashboard, the item card, the BOMs and the catalog. There is no second way to count hours in the system, and every figure names its source: from the BOM or converted by the rate.
The week layout keeps pinned batches in place and sends the rest, by descending hours, to the line that will have more free hours left after it. The advisor runs the same function: a batch that does not fit is trimmed by the step with the reason written out; when even the minimum batch does not fit, it moves to the next week.
Otherwise a new batch of the week would move one already started to the other line and rewrite the history of actual hours. A line can also be pinned with a button or by making the run the working plan.
The sheet is found by its header, the column order is free, hours come with a decimal comma, a model without a color applies to every color in the catalog. A novelty without hours goes to the without hours list, not to errors. The pay column is not read at all.
Empty is more honest than zero. If a line has no headcount, it has no capacity: the cell is grey with the reason, not zero and not 100%, and the line stays out of the Sewing total, which is said in words. A workshop whose rate is under review is counted, but the figure is grey and not used in decisions.
A plan has to be followed: what is cut and sewn, what is late, how many hours actually went. It also works the other way: the factory is already sewing something the plan did not have, and that is entered too, as a list pasted straight from Excel.
It is not stored separately: one truth. A stage can have as many marks as needed, cutting can go in parts. More pieces than the stage has left or a date in the future is refused in words.
Every batch says where it is now and how far behind: sewing planned by 13.09, 40 of 100 done. A click on a week cell opens the batches its hours came from.
The size split is edited in the expanded batch, the sum 230 of 240 sits next to it, and Save stays disabled until the sum matches the batch.
Demand is assembled from the actual orders of all four accounts by item key. Three multipliers act on it: buyout, size shares and season. Each comes from a ladder: manual beats automatic, the item beats the category, and when there is no data, the system says exactly that.
The marketplace funnel keeps adding buyouts for about two more weeks, so the last 14 days stay out of the window: otherwise the freshest days would always look like a slump. Zero buyouts with live orders means no data, not 0%: otherwise the engine would call every batch of the article a loss.
Units that were not bought come back to the warehouse after three weeks and sell again. On a textbook batch of 240 at a 50% buyout this correction flips the sign of the batch profit: without it the advisor would place loss-making batches for everyone.
Shares come from a manual setting, the article's own orders over 180 days, its own sales, the category profile or an even split. A batch is split by size: first the shortage of each size in the month of arrival is covered, the rest goes by shares in multiples of 10, and the sum always equals the batch. A shortage of size 48 is never covered by a surplus of size 54.
The port of the monthly demand calculation is checked on the inputs and expectations of the workbook itself: 13,786 size-month pairs in a full run. Rounding happens once and only half up, as ROUND in Excel. The monthly screen later gave way to the daily restock, and the engine lives on in the plan, the batch size split and the item card.
History collects itself every night: a year of marketplace analytics arrives as one report in seconds instead of hours of requests, and old months are never deleted, so a second round of the season appears after a year. A month coefficient is the daily pace of the month over the average daily pace of full months: by totals, February would lose three days to March. Fewer than six full months means too little history, not an invented profile. A novelty never takes its own history: the category gives the shape of the season, not the level of demand.
Ozon categories find their Wildberries pair by a rule on word stems: doubled letters collapse, endings are stripped, and a candidate must be shorter than the original name. A qualifier can be dropped but never invented: a top does not become a sports top. 23 categories of 29 find a pair, a person picks the donor for the rest.
Articles sit on the right axis as a percentage of their own average: in pieces an article line would lie flat near zero next to its category, and it is the shape of the season that people compare. Year over year runs on a twelve-month axis: January against January.
Twelve ones would block the way to the category history, so a deliberate ignore the season is a separate button. Coefficients are never written for all categories at once: noisy categories need a human look.
The display was rebuilt the way the client's accounting system counts: one ladder for both marketplaces on the dashboard, in analytics and in the pulse board. Profit and the money paid out did not change by a kopeck: the base of the percentages changed, not the money.
A summary row, a category or a pie slice opens the marketplace articles the amount is made of. A new article is marked with a dated chip and raises a signal. An administrator assigns an article to the right row, and the move applies to the whole history.
Both figures come from one column of the showcase, and the frontend compares them in whole kopecks: in floating point a one-kopeck difference would simply vanish.
Ten totals and the total-to-pay formula are checked in whole kopecks, and a mismatch is a red signal naming the field. The paid-out amount is collected by the operation day of the report, so a week's sum equals the marketplace total to the kopeck.
A queue with 120 seconds of quiet, each move pushes the deadline, and the recount runs through the same functions as the nightly build: no second profit formula appears in the system.
Even and proportional splits run in whole kopecks: minus 5 kopecks in three parts is 1, 2 and 2 kopecks, and the parts always add up to the whole.
On September 7 the comparison is with August 1-7, not with the whole of August: otherwise at the start of a month every card would show minus 75%.
Learning about one article used to take five tabs. The card stores nothing: it gathers figures from the source modules on every request. Cards, the cost chain, labour, batch and advice, season, 12 weeks of movement, sizes and batches.
Only an explicit button on one article writes into the current cost. The two figures serve their own purposes, and the gap between them is highlighted.
The first cost version applies to the whole history, later ones only forward. A zero or a jump of more than 10 times is held for review: a zero would turn a loss-making item into a star. A new price recomputes the showcases by itself, and the header shows the progress.
An archived article leaves planning, while its stock still counts in the economics and is shipped by restock. Who archived it, when and from where is kept in the journal.
Underneath runs ordinary theory of constraints and honest combinatorics. None of their terms appear on the screen: people on the factory floor speak differently, and there is no point in teaching them a vocabulary for the sake of an interface.
| In the code and the spec | On the screen |
|---|---|
| item key, item_key | Article |
| bill of materials, BOM | What we sew from |
| consumption norm | Fabric per piece, m |
| logistics lead time | Fabric in transit, days |
| reorder point | Order by |
| workshop capacity | Hours per week |
| launch decision | the sew tick |
| greedy layout by urgency | the Build the plan with the advisor button |
| branch and bound | the Exact calculation button |
| uncovered shortfall | the nothing to cover note |
A figure with a dotted underline opens the formula in words and the substitution line. A figure that cannot be explained in ten seconds counts as unfinished in this interface.
A funnel in the header of every column, and for paged tables the selection runs on the server. A guard test breaks the build if a table without filters appears in the system.
Articles everywhere follow the factory order: dresses, sundresses, skirts, trousers, suits, then model number and color. One function on the frontend and the server. Tabs became addresses, the first column is sticky, and on a phone tables turn into cards.
| Before | After |
|---|---|
| Restock for every account from five reports and lookups, the reserve between accounts zeroed. | Restock on the fly for every barcode, a workbook with the planner's live formulas and a reconciliation against the portal, the reserve between accounts held by a document. |
| Demand counted per account, buyout as one constant, season in someone's head. | Orders of four accounts by item key, buyout, sizes and season from a ladder of sources, history collected automatically. |
| What to sew and when was decided with a spreadsheet. | A person ticks what to sew, the advisor lays it out by week and line, and the plan goes to the factory as a money-free file. |
| Factory load lived in someone's head. | Two sewing lines with capacity in hours, a batch goes whole to one, and plan against fact by week on the dashboard. |
| Portal margin several times lower than in 1C. | A money ladder as in 1C: revenue by receipts, expense articles with a breakdown, every marketplace report reconciled to the kopeck. |
| Cost price in two places and arguments about the figures. | Two cost prices by purpose in one item card, the gap highlighted. |
| Nobody could explain where a figure came from. | Every figure carries how it is counted with a substitution, and the plan has a Why block and a list of what did not fit. |
Business analyst and systems builder. I start from the business process and the data contract, then automate. Here the weekly assembly from five spreadsheets was replaced by a system that computes restock, the plan and the money and can explain every figure.