Commit Graph

5 Commits

Author SHA1 Message Date
denis_ireri ff75cf2fa7 Cost sales first-in-first-out per branch instead of at an average
Cost of goods sold used each product's average buying price across the
whole business. Ten jerrycans bought at 100 and ten at 140, with 15 sold,
cost 1,800 instead of the 1,700 actually paid for those units; a branch
that bought dearer was costed like one that bought cheaper; and units
moved between branches lost their cost.

src/lib/server/costing.ts replays the stock ledger in order with cost
layers per branch and product:
- purchases add stock at their unit cost; transfers carry the sending
  branch's layers to the receiving branch; opening stock and upward counts
  are valued at the product's purchase price at the time;
- sales take the oldest layers first, and that is cost of goods sold;
- stock written off by counts or adjustments is a separate "stock losses"
  figure, now deducted in net profit;
- a sale recorded before its stock (stock gone negative) is costed by the
  next stock to arrive at that branch, even after the period ended; until
  then it is reported as units with no purchase cost on record.

The report shows the method and a Stock losses card. Sales recorded
before the stock ledger existed are counted as uncosted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:08:15 +03:00
denis_ireri f865837b27 Address the 11 October test report findings
The initial test report (run before the pilot-readiness changes) raised
seven findings and a retest plan. This closes the ones still open.

- F-01: a business on its free trial may run 2 active branches whatever
  its tier, so an owner can try transfers and per-branch stock. Only
  active branches count towards any limit.
- F-06: owners can rename branches and deactivate/reactivate them from
  the Branches page. A branch can only be deactivated once it holds no
  stock and no active staff; its history stays in every report, and it
  leaves every picker.
- F-04: the Products page lists deactivated products under a toggle with
  Reactivate. Deactivating warns about stock still on hand, which stays
  on the Stock page so it can be transferred or counted out.
- F-05: products carry a unit (L, ml, kg, g, pcs, tray, pack) instead of
  everything being litres.
- CAT-12: product prices must be above zero and names are unique per
  business, ignoring case.
- F-03: a reversed report period is refused by the page and the API, and
  the page labels which period the cards show. Report dates are Nairobi
  business days.
- F-07: cost of goods sold is units sold x average buying price. Stock
  bought in the period is reported separately, with a cash surplus line,
  so unsold stock no longer shows as a loss.
- STA-04: staff and new branch managers can be invited with a one-time,
  7-day link; the person sets their own password. Managers can issue a
  new link for their own cashiers.
- OFF-02 / POS-02: the till shows each product's stock and deducts every
  sale immediately, online or offline; it warns before selling more than
  the recorded stock, and has a Clear sale button.
- npm run backfill:stock turns purchases recorded before the stock ledger
  into stock, so existing tenants (and the report's ZERRO fixtures) start
  from what they bought.

tests/retest-plan.test.ts runs the report's completion and retest plan
and checks every balance and total it predicts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 10:29:21 +03:00
denis_ireri d4808b3e46 Enforce roles and tenant status on the server, and use the Kenyan business day
The admin pages redirected cashiers away, but the API itself let them read
profit, expenses and purchases and record expenses and purchases. Branch
managers could also change product prices for every branch. Every gated
route now goes through authorize(), which answers 401 when signed out and
403 for the wrong role.

- Owners and managers only: profit report, expenses, purchases, users,
  morning dispatch and evening count. Owner only: create and edit products,
  rename branches.
- PATCH /api/users/:id deactivates or reactivates staff (managers: their own
  cashiers only) and revokes the user's device sessions. PATCH
  /api/branches/:id renames a branch.
- Login and token refresh refuse inactive or SUSPENDED businesses.
- Public signup refuses PLATFORM_ADMIN_EMAILS addresses, so an unregistered
  operator email can no longer be claimed.
- M-Pesa sits behind FEATURE_MPESA (off by default, out of scope for the
  pilot): its routes return 404 and the Daraja variables are optional.
  Turning it on in production requires the callback IP allowlist.
- "Today" and the default month now follow Africa/Nairobi instead of the
  server's UTC clock, so the business day no longer starts at 03:00 EAT.
- Malformed ids in query strings and bodies return 400 instead of a 500,
  the inventory routes check the product belongs to the tenant, and the
  reconciliation lookups are tenant-scoped.
- Branch-scoped staff see their branch's override price from GET /api/products.
- Adds a vitest suite against an in-memory MongoDB (npm test).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:34:35 +03:00
EOH Developer 04e3051eb6 Add multi-tenant SaaS: business signup, owner/branch-manager/staff hierarchy, full tenant isolation 2026-10-04 01:22:42 -07:00
EOH Developer 3dc6eef18d Initial commit: NetsKey ERP (copper & cream redesign) 2026-10-02 23:48:11 -07:00