Antonenko. Send an enquiryUA

Notes · Case study

A hair-care brand, run from one system

The owner of a Ukrainian hair-care brand opens the system in the morning and sees four numbers: orders today, revenue this month, parcels in transit, items running low. Under them, orders by status. A manager who logs in to the same system sees his own income this month, and shop money is hidden from managers on purpose.

The owner dashboard: orders today, revenue this month, parcels in transit, items running low, and orders by status
The owner’s morning screen. Demo data.

"Running low" means three or fewer free units.

The brand makes its own product: bottles, pumps and labels come in as raw materials and go out as filled bottles, sold to people through a storefront and to hairdressers and salons at partner prices. Goods ship from a central warehouse, from regional warehouses and from a salon.

Before this, orders lived in an external CRM, and money was noted by hand in three places. That CRM had no place for expenses at all, so the bank's acquiring fee was in nobody's record. When the new system went live, 378 orders and 81 contacts, March 2025 to September 2026, were imported into it. The storefront and the CRM now sit on one database.

Orders

Every new order goes into a shared list, and each admin and manager gets a Telegram message about it personally. A manager takes it. From that moment it is his. The others can still see it, but I lock it, because otherwise two people would write two waybills for one box.

The order list with statuses, payment state and the manager who took each order
Orders, with who took each one. A taken order is locked for the others. Demo data.

He confirms the order, which reserves the stock. The shipping warehouse is chosen per order, since the same product may be sitting at a regional warehouse, at the salon and at the central warehouse, and the reservation moves with that choice. If the buyer has ordered before, their Nova Poshta branch (Nova Poshta is the national courier) is prefilled from the last order.

A new order for a returning buyer: the city and Nova Poshta branch are filled in from their last order
A returning buyer: the branch comes from their last order. Demo data.

The recipient can be a different person from the buyer, and the waybill, which Nova Poshta calls a TTN, is made out to the recipient. The manager makes the waybill from the order card when the box is ready.

Every message sent to the buyer, whether SMS, e-mail or Telegram, is logged on the order card, failed ones included.

A full order card: buyer and delivery, items, the Monopay payment, the warehouse it ships from, the TTN, messages to the buyer and the history of who did what
One order, top to bottom: payment, warehouse, waybill, every message to the buyer, and the history. Demo data.

The buyer gets an SMS when the order is accepted, with a link to the receipt, and another when Nova Poshta takes the parcel. Tracking runs every 10 minutes and moves orders to shipped, delivered and completed. Free promo parcels go through the same flow at marketing's cost. A report later shows who came back to buy after one.

On prepaid orders, each admin and manager also gets the payment result in Telegram, and at 20:00 Kyiv time a digest of prepaid orders still unpaid.

Money

A card payment through Monopay, the bank's card acquiring, lands on the account net of the 1.30% fee, and the fee goes to expenses as acquiring. Nobody retypes it. Cash on delivery works the same way: once Nova Poshta hands over the parcel, the money is credited to NovaPay, the courier's payment service, minus 0.50%. Cash and bank transfers carry no fee.

The Accounts screen shows how much money is where. Each FOP (a Ukrainian sole-trader entity), NovaPay and each cash box is its own account. I store the fee rate on every entry, so when the bank changes its rate, older entries stay as they were.

The accounts page: each sole-trader entity, NovaPay and cash box as its own account; a 2,800 UAH card payment credited as 2,763.60
Money by account. A 2,800 UAH card payment arrives as 2,763.60, with the 1.30% fee booked as an expense. Demo data.

When money arrives some other way, the manager marks the payment on the order with the amount, the account and the date. Marking it books the money to that account, and the paid status follows.

The P&L counts revenue from completed orders. Cost of goods comes from the cost price saved on each order line at the time of sale. Expenses are grouped by section. SMS spending is booked when the SMS account is topped up, which is when the money leaves.

The P&L for a month: revenue, cost of goods, gross profit, expenses, net profit, money received, and expenses by section
The month’s P&L. Demo data.

Manager pay is a fixed amount per completed order, 100 UAH by default and changeable in settings. The manager sees the same figures under "My income".

Manager pay: a fixed amount per completed order, with orders, accrued, paid and balance for each manager
Manager pay per completed order. The rate here is a demo value. Demo data.

Stock and production

Every place goods ship from is a warehouse in the system: the central one, the regional ones and the salon. Each has its own stock and reservations, and I assign warehouses to managers, so a manager sees only his own. The cost of moving stock between warehouses goes to courier and taxi and stays out of the cost price of the product.

Production works in batches. On confirmation, raw materials are written off per item's kit; if any material is short, confirmation stops and shows what is missing. Defective units do not go to stock, and their cost is spread over the good units of the batch. Shared batch costs are split by weight, so a 400 ml bottle takes more of them than a 100 ml one.

Production batches: per-unit cost from production and materials, the supplier invoice, the advance paid and what is still owed
Production batches with unit cost and what is still owed to the producer. Demo data.

Not everything is made in-house. Hair turbans, for example, are bought ready-made, and since October 2026 they go through Purchases instead of Production. A purchase order is the same kind of batch, so the money follows the same rules: the debt to the supplier at purchase prices or by their invoice, payments in parts from any account, booked as purchase of finished goods. Delivery from the supplier goes into the unit cost, and on receipt the goods go to the warehouse at a weighted average cost. The order also carries the date the goods are expected. Each product is marked as made or bought, each section shows only its own, and one batch cannot mix the two. Suppliers are picked from a list and a new one is added on the spot, so each supplier exists once and carries one debt.

Supplier invoices arrive as files. A person uploads the invoice, a model reads it and fills in the receipt, the person matches each line to a raw material and confirms, and the amount becomes an expense.

A stock count has three steps: start, enter what is on the shelf, finish. Products and raw materials are counted separately. An empty field means not counted; a zero means counted, and nothing there.

Partners and Instagram

Hairdressers and salons buy at partner prices, and the price depends on a tier and on the bottle volume. Bronze, Silver and Golden start at 0, 20,000 and 50,000 UAH of paid purchases in a calendar month, and the tier applies the following month. The professional line on the storefront has no cart. A salon fills in an application form, and each application also goes to Telegram.

Every Instagram Direct conversation is kept, and its outcome is computed from what happened in it. The outreach list of hairdressers lives in the CRM. A model judges each public profile and drafts a personal message of up to 90 words. Since 24 September 2026 a bot sends the first message itself, between 9:00 and 19:00 Kyiv time, one every 20 minutes, and marks sent and replied from Direct. The bot sends partner terms as an image, and only to hairdressers and salons. I never let purchase prices go out as text.

Each user has a role and assigned warehouses. Twelve in-app guides, with steps, gotchas and screenshots, are filtered by role. I keep the guide texts in the code so they change in the same commit as the feature they describe.

If you run a brand on an external CRM and a few spreadsheets, send me how an order moves through it today and where it jams, and I will look at it.

Another note: how I killed the SaaS.