One person could not work for two businesses, and signup and staff
creation told anyone whether an email or phone was registered anywhere
on the platform.
- User email and phone are now unique per business: indexes on
(businessId, email) and (businessId, phoneNumber). Duplicate checks in
signup and branch-manager creation are scoped to the business, so they
no longer reveal other businesses' users.
- Login finds the email in every business and checks the password
against each account. One match signs straight in; several return 300
with the businesses to choose from, and the login page shows a picker.
A wrong password returns the same 401 as before and reveals nothing;
failures count against each candidate account, and a correct password
only resets the accounts it opens.
- Platform-operator access now also needs a platformAdmin flag on the
account. With per-business emails, an email match alone would have let
any owner create a staff account with the operator's email and open the
cross-tenant console. The flag is only set by the seed and the
migration; removing the email from PLATFORM_ADMIN_EMAILS still revokes.
- npm run migrate:identity flags today's operators (while each listed
email still matches one account) and syncs the User indexes, dropping
the old unique email_1 and phoneNumber_1. Checked against a database in
the old shape; safe to re-run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Branch price overrides were already applied at the till, in cash sales
and in M-Pesa, but nothing could set them.
- Products -> Branch prices (/admin/prices): pick a branch and set its
price for any product; setting it back to the default removes the
override, and "Use default" resets it. Branch managers see their own
branch's prices read-only.
- /api/branch-prices: GET (owner: any branch; manager: own), PUT and
DELETE (owner only). Branch and product must belong to the business;
prices must be above zero; who changed a price is recorded.
- Branch pricing is a plan feature, as the Growth plan already
advertised: Growth and Enterprise, plus any business on its free
trial. A paid Starter business keeps any prices set during its trial
and can reset them, but cannot set new ones.
- The catalogue view on the Products page always shows default prices;
the till's view shows what the branch charges.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>