Commit Graph

12 Commits

Author SHA1 Message Date
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 cb962466b3 Merge pull request 'Pilot readiness: server-side roles, stock ledger and transfers, offline-safe sync' (#1) from fix/pilot-readiness into main
Reviewed-on: #1
2026-10-12 06:14:50 +00:00
denis_ireri 8fe50af341 Show Offline when the server is unreachable, and document the pilot changes
The terminal's badge only looked at navigator.onLine, so it said Online
while the server was down. It now also reflects whether the last sync or
health check reached the server. The cart badge no longer covers the
product name.

docs/PILOT_READINESS.md records what changed against the PRD review, the
decisions taken (offline sales may take stock negative, transfers may
not, server price wins but differences are flagged), rollout notes, and
what is still open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:54:24 +03:00
denis_ireri 472573a1a8 Make the terminal work offline end to end, and add stock screens
Terminal (PRD 5.3/5.5):
- Opens with no network: a service worker caches the app shell and build
  assets, and the last confirmed session and product list are kept on the
  phone. Only a definitive 401/403 sends the cashier to the login page.
- The offline queue is keyed by cashier and branch, sends only the
  signed-in cashier's sales, chunks to the server's batch limit, and acts
  on the per-sale result: synced, or rejected with the reason shown and
  never retried. Rows from the old queue are adopted on first sync.
- Several products per sale (tap to add), optional customer details, and
  a printable receipt. The banner shows sales waiting to sync and any
  that need attention; sign-out warns about unsynced sales.
- M-Pesa only appears when FEATURE_MPESA is on. A timed-out prompt now
  tells the cashier not to take cash until the payment is checked,
  instead of reporting it as failed.

Admin:
- New Stock page: stock per branch with low/out flags, physical counts
  and adjustments, inter-branch transfers, morning dispatch and evening
  count entry, and recent movements.
- Dashboard shows stock on hand per branch and links to Stock.
- Staff page can deactivate and reactivate accounts.
- Products page is read-only for branch managers and edits the low-stock
  alert level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:46:23 +03:00
denis_ireri 8817c0f52f Add a stock ledger, inter-branch transfers and a sync protocol that cannot jam
Stock (PRD 5.2): a branch's stock of a product is now the sum of its
StockMovement rows. Every movement names the document that caused it and
(type, refId) is unique, so replaying a sale, retrying a transfer or a
second M-Pesa callback never moves the same stock twice.

- Cash sales and confirmed M-Pesa payments deduct stock; purchases of a
  catalogue product add it.
- POST /api/stock/transfers moves stock between two branches (owner: any
  two; manager: out of their own) and refuses to send more than the
  sending branch holds. Double submits are applied once.
- POST /api/stock/adjustments records opening stock, breakage or a
  physical count (the ledger is corrected to the counted number).
- GET /api/stock returns every branch x product with LOW/OUT flags
  (per-product lowStockThreshold, default 10); GET /api/stock/movements
  is the history.

Sync (PRD 5.3/5.5): POST /api/transactions/cash now returns a result per
sale - saved, duplicate or rejected - instead of silently skipping
replays. The old client never learned a replayed sale had synced, so
rows piled up until the batch passed the 100-sale limit and every flush
failed with 400. Also:
- A sale can hold several products (one Transaction per line, grouped by
  saleId); the old single-product payload is still accepted.
- One bad sale is rejected on its own instead of failing the batch.
- A queued sale is only accepted from the cashier who recorded it, and
  stays with the branch where it happened.
- A product deactivated while offline is still recorded at its price; an
  invalid phone is dropped rather than losing the sale; the phone is now
  optional for cash sales.
- The price the device showed is kept and a difference is flagged
  (priceMismatch) instead of being silently overwritten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 18:39:11 +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
denis_ireri 2fbf7e6bce Add REVIEW.md 2026-10-11 14:15:11 +00:00
denis_ireri 79f7d22433 Add PRD.md 2026-10-11 14:14:01 +00:00
EOH Developer fa375bde91 feat: subscription tiers, branch-with-manager flow, platform command center 2026-10-05 10:20:04 -07: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 3b22ddf6fb Fix Render build: load Google Fonts via link instead of next/font 2026-10-03 00:02:07 -07:00
EOH Developer 3dc6eef18d Initial commit: NetsKey ERP (copper & cream redesign) 2026-10-02 23:48:11 -07:00