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.

"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.

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.

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.

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.

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.

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".

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.

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.