Skip to content

NorteAR

Own product

Under construction · Next.js · TypeScript

Management system for service SMEs that measures real margin, not revenue.

Margin today: $40,100, 89%, with the line that explains it: $45,000 invoiced minus $4,900 in supplies.
Margin of the day$45,000 invoiced − $4,900 supplies = $40,100
See the full panelFull NorteAR panel: today's till, monthly summary, sales, appointments, stock alerts and recent activity.

The case, in six beats

  1. 01

    Problem

    A service business invoices well and by month's end there is nothing left. The system shows sales; what it does not show is how much stock was consumed to produce each of those sales — the tube of colour that yields a few applications, the product that was thrown out, the appointment that cost more than it charged.

    Almost every management system measures what comes in. Very few measure what was spent so it could come in. And without that, "we sold more" and "we earned more" are the same sentence — until they stop being.

  2. 02

    Decision

    Margin is the headline metric, not one more report in a menu: it is the figure the system returns by default. If the main number is revenue, the user keeps looking at what they already looked at.

    Services, not retail. A shop that resells knows its unit cost. A service business does not: stock is consumed in fractions and nobody writes them down. That is the gap.

    Multi-tenant from the data model, not as a layer on top: every table filters by company. The alternative — one database per client — scales worse the moment you need to touch all of them at once, and here the business is precisely measuring and comparing, not isolating.

  3. 03

    Mechanism

    The hard problem is not accounting, it is modelling: how to know how much stock a service consumed if nobody weighs it. The answer was to tie each service to its supplies and draw down against real stock.

    1. Appointment
    2. Supplies
    3. Stock
    4. Margin
    // Every service carries its supplies (serviceSupplies): what it consumes and
    // how much of each. Selling it draws down real stock, not an estimate.
    const consumptions = serviceItems.flatMap((item) => {
      const service = services.find((s) => s.id === item.serviceId)!;
    
      // quantityNeeded counts service uses; convert to base-unit stock consumption.
      return service.serviceSupplies.map((ss) => ({
        supplyId: ss.supplyId,
        consumed:
          unitsPerUse(ss.packageContent, ss.servingsPerPackage) *
          Number(ss.quantityNeeded) *
          item.quantity,
      }));
    });
    
    const stockDelta = new Map<string, number>();
    for (const { supplyId, consumed } of consumptions) {
      stockDelta.set(supplyId, (stockDelta.get(supplyId) ?? 0) + consumed);
    }
  4. 04

    Trade-off

    Loading the supplies is the user's work, and that work is the product's barrier to entry: without supplies loaded there is no margin, and with an empty system the value is invisible. Everything else is subordinate to lowering that initial cost.

    What was left out on purpose: electronic invoicing (AFIP/ARCA does not exist in the schema), multi-branch and a native app. Narrow scope by decision, not by technical limit — the product is built for one kind of business, not for everyone.

  5. 05

    Result

    Active businesses
    2
    Trial → paid
    2
    3-month retention
    2/2
    In production since
    Junio 2026
  6. 06

    Afterwards

    If I started over, I would not stretch the scope from day one. I kept adding complexity — multi-branch, roles, per-professional reports — for what, at this first stage, is a handful of known clients. That complexity costs development time that does not come back, and the product did not need it yet to prove it worked.