Commit Graph

104 Commits

Author SHA1 Message Date
7d9e628fd0 app sbucategories 2026-09-08 11:49:34 +05:30
e87f09c4f2 console categories 2026-09-07 17:53:35 +05:30
c699a400c3 product categories 2026-09-07 17:28:38 +05:30
f1b3a5eba4 deliveries 2026-09-05 15:02:23 +05:30
593b11f1b8 rider creation 2026-09-04 16:02:47 +05:30
e51ad615ed assign 2026-09-04 11:32:12 +05:30
fb5d7da81f deliveries 2026-09-03 19:12:03 +05:30
18a0f9b34d pricing 2026-09-03 16:23:29 +05:30
0d17f46fcf pricing in app 2026-09-03 15:44:09 +05:30
7ed821e8b0 pricing 2026-09-03 11:21:12 +05:30
da0e9d987e variants as a single product 2026-09-03 10:43:05 +05:30
2401158190 bulk request and approve 2026-09-02 16:50:37 +05:30
e45da9f7df variants units 2026-09-02 16:01:24 +05:30
09efc5403f orders 2026-09-02 15:37:59 +05:30
68871cb043 product variant id 2026-09-02 13:11:01 +05:30
9f9b05ed1c tenant profile 2026-09-02 12:30:05 +05:30
c0d39e550d changes 2026-09-02 10:49:46 +05:30
1e3fe88e87 user accounts 2026-09-01 13:53:22 +05:30
6b27448ff9 role id 2026-09-01 13:43:05 +05:30
ab74e70ba5 login bug 2026-09-01 13:17:52 +05:30
485f31239d guide changes 2026-09-01 12:01:43 +05:30
ae6162a632 changes 2026-08-31 19:00:35 +05:30
d8cfe2e87b store filter 2026-08-31 16:39:20 +05:30
bc10b589cd fix on stocks on store catalogue 2026-08-31 16:26:06 +05:30
d55f101834 fix on shelf 2026-08-31 15:51:36 +05:30
4bce5ac854 product on shelf 2026-08-31 14:59:48 +05:30
ffc66462fc changes on agent 2026-08-31 12:33:10 +05:30
76554d26e5 bugs on variant id 2026-08-29 16:45:47 +05:30
c5109bf216 catalogue images 2026-08-29 11:29:15 +05:30
fb4fcaee66 catalogue brands 2026-08-29 10:58:01 +05:30
7c5be9b5cf bugs fixed 2026-08-28 18:19:52 +05:30
40500f936a variant and price 2026-08-28 16:54:55 +05:30
6c1ad472f2 changes according to the test 2026-08-28 13:19:13 +05:30
c31f3aca27 changes with app variant 2026-08-28 13:02:03 +05:30
aef105979a changes with app product stock 2026-08-28 12:43:07 +05:30
46da26c452 changes 2026-08-28 11:56:57 +05:30
edb9c2803f lat and long 2026-08-27 13:59:16 +05:30
dc9c049bb9 live stock update 2026-08-27 11:17:17 +05:30
Suriya
ca846f9cd6 require a mobile number when creating a till account
A till signs in with a mobile number and a PIN, but createposuser still
accepted an account without a number. Such an account cannot reach the
sign-in screen at all, and the failure surfaces at a counter in front of
a queue rather than at the point of creation. Enforced in CreatePosUser,
so both doors are covered: POST /pos/users from the terminal and
createposuser from the console share that path.

Two checks, not one. normalisePosPhone answers ("", nil) rather than an
error for a value holding no digits, so "abc" would have passed an
emptiness check, then been written as a blank and skipped the uniqueness
check below it — which is the hole this closes.

Scope is new accounts only. The column stays nullable and UpdatePosUser
still reads an empty contactno as "leave alone", so the accounts that
predate the number keep working through the backfill and cannot have
theirs cleared. The PIN stays optional at creation.

Also in this change:

- docs: correct both phone-login handovers, which claimed creation
  already required a number. The sequencing note in the PIN handover
  said step 2 was a backfill that could never be finished; it now is
  one, and POS_LOGIN.md says which half of the pair creation enforces.

- docs: remove credentials from the examples. POS_PHONE_LOGIN_HANDOVER
  carried a real-looking back-office pair and a generated till password,
  and POS_LOGIN.md a second one.

- posController.Staff: the comment justified scoping by token because
  "the answer carries PINs". It has not since the PIN left the wire. The
  scoping is still right for a different reason, which the comment now
  gives.

- scratch/posstaffsetup: takes both mobile numbers as arguments and
  refuses to run without them. Generating stand-ins would have produced
  exactly what this change prevents. Validated before the database is
  opened, in plan mode too, so a dry run cannot print a plan that apply
  would reject halfway through and leave half a shop set up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-12 17:26:41 +05:30
d566ca5591 pos login with the ph number and pin 2026-08-12 17:10:27 +05:30
ff72af9a8a changes fixed in the catalogue 2026-08-11 17:10:21 +05:30
fba42259ea fix in the product import 2026-08-11 15:49:04 +05:30
c179e4d642 product images added to the catalogue 2026-08-11 15:05:27 +05:30
d35caf34d3 pos login edited with phone number 2026-08-11 11:08:43 +05:30
3531c656d4 fix in the catlogue 2026-08-10 15:36:39 +05:30
Suriya
5c04d40f0e Scope /health/terminal to the caller's tenant, and backfill the blank codes
The outlet check could not go in the middleware with the others: it scopes a
request by the location it names, and this route names only a terminal code —
free text minted at the till. The outlet is not known until after the lookup,
so the check happens in the handler, reading the outlet off the heartbeat
itself rather than off the request.

Guessing "T4A9" now answers 403 instead of another shop's pending bills,
takings so far today, and app version. Silent when no token is presented, in
step with middleware.PosAuth: while POS_AUTH_REQUIRED is off, real tills are
still calling this unauthenticated and refusing them would blank the fleet
board for exactly the terminals it watches.

scratch/termbackfill repairs the rows left behind by the posTerminalFor bug.
The code was never lost — it is the third segment of the invoice number the
till printed in the same transaction — so this derives rather than guesses,
and refuses to write a code that outlet has never filed a bill under. 39 of
40 recovered; the holdout is a synthetic proof bill whose only sibling is
itself, and unverifiable codes stay blank rather than becoming plausible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 11:22:15 +05:30
Suriya
f7895d3ccf Correct what /posroles tells a console about each role
The supervisor blurb still said "Also signs into the app", which was true when it
was written and is now the opposite of true: a till account has no Nearle Daily
login at all. A console showing that text would be telling a store admin the one
thing about these roles they most need not to believe.

The cashier blurb said "Billing only", which understated it in the other
direction — a cashier now has their own username and password, so a shop can
open without a supervisor standing there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 12:14:10 +05:30
Suriya
9e9401215d Give a cashier their own till login, not just a PIN behind a supervisor
A PIN cannot open a closed terminal. The PIN route needs a session that already
exists, so a PIN-only account works only while somebody else is standing there
to unlock the till first. For a supervisor that was an outright deadlock and was
fixed last commit. For a cashier it is subtler and just as wrong: the shop
cannot open until two people have arrived, and whoever gets in at seven is as
often the cashier as the supervisor.

So every till account now gets a username and a password, and the role decides
the shell rather than the credential deciding it. A cashier signs in exactly the
way a supervisor does and is still held to billing only, because that comes from
roleid 8 and not from how they got in.

The earlier reasoning — that a second password is one more credential to leak
for no capability gained — was measuring the wrong thing. It counted the cost of
the credential and not the cost of the shop that cannot open without one.

CreatePosUser generates both when the request omits them, so provisioning is one
call per person and nobody has to invent a naming scheme. An explicit value
always wins. A generated name that collides walks to the next free one, because
a second cashier at one counter is ordinary rather than an error; a name the
caller supplied is refused instead, because silently signing somebody in as
another person's address is worse than a message. Uniqueness is checked against
authname and email together, since the insert writes the same value to both and
app_users_email_unique would otherwise fail the transaction rather than return
something anyone can act on.

The password comes back exactly once, in the creation response. Listing till
users still reports only has_password, so an admin who loses it reissues rather
than looks it up — the right shape even while the column behind it is plaintext.

The domain is deliberately unroutable. These are till credentials, never a
mailbox, and an address that looks deliverable invites somebody to try sending a
reset to it.

Verified against live rows by scratch/posseparation, which now checks the
cashier path too: cashier.1185@pos.nearle.in opens a closed terminal alone and
comes back can_manage_staff=false. All five outlets that stock products have
both accounts, each proved by an actual sign-in.

Also drops a stray `print(queryBuilder.String())` from GetAllUsers, which was
writing the whole SQL statement to stderr on every call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 12:04:24 +05:30
Suriya
c0a7fbc1b1 Stop the till and Nearle Daily from sharing accounts
app_users is the only thing the two products have in common, and the code was
treating it as though it were the whole relationship. Both directions leaked.

Back-office roles were leaking into the till. PosRoleCanManageStaff returned
true for roleid 1 to 6, on the reasoning that somebody who already administers a
shop from a browser is not made less privileged by standing at the counter. That
sounds fine and is wrong: measured against live data it handed till-supervisor
powers to 68 accounts, 59 of them Nearle Daily Super admins, not one of whom is
the administrator of anybody's POS. Meanwhile the actual shop accounts carry
roleid 0 and were refused, so the mapping was backwards from intent in both
halves at once.

Till accounts were leaking into the application. GetStaffs is WHERE tenantid
with no role filter, so a Counter Cashier appeared in the tenant staff list
beside the delivery riders — a row every action on that page would fail against,
since a cashier has no app login, no rider shift and no back-office screen.

So: eligibility for a till is now granted explicitly by provisioning a
Supervisor or a Cashier, never inherited from a back-office role, and roles 7
and 8 are excluded from every Nearle Daily lookup. The exclusion lives in the
queries rather than in a check after them, because a check bolted on afterwards
has to be repeated at six call sites and is one edit away from being forgotten
at one of them — and that one would be the hole. A till account is not rejected
by the app login; it is not found.

Two things this surfaced that were not visible before.

A Supervisor could not open a till. PIN sign-in needs a session that already
exists, so once back-office roles were refused, an outlet whose only POS
accounts were PIN-only had no way in at all. Supervisors are now provisioned
with a username and password as well as a PIN; cashiers deliberately get neither,
because they sign on at a counter somebody has already opened and a second
password would be one more credential to leak for no capability gained.

UpdatePosUser silently dropped authname. It wrote the password, reported
success, and left the account unreachable by either lookup — the failure
surfaced at a counter as "not recognised" rather than on the screen that caused
it. Contactno had the same gap.

Verified against live rows rather than asserted, by scratch/posseparation: a
provisioned supervisor signs in and gets the supervisor shell; five real
back-office accounts including Super admins are refused; the supervisor is
invisible to applogin, tenant weblogin and the password-setup lookup; and no
till account appears in getallusers, while asking for role 7 by name still
returns them so the console can read its own people.

All five outlets that stock products now have a Supervisor and a Cashier.

Also moves the loose markdown into docs/, which was already staged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:52:23 +05:30
Suriya
f5e16b54cc Add a read-only report of who can actually open a till at an outlet
Answers the question a shop asks on day one and nothing in the product could:
what do I type into the terminal. It separates the two credentials because they
are not interchangeable — a password opens a closed terminal and the account
decides which shell it opens, a PIN only switches operator on one already open —
and it prints the shell each account would land in rather than the raw roleid,
since roleid 0 is not in app_roles and reads as nothing at all.

`top` ranks outlets by products actually stocked *and* filters to ones somebody
can sign in to. That filter is the point: the best-stocked outlets on the
platform — Dilse at 471 products, Ninhao at 286 — have no account with a
password, so a demo pointed at either opens a till nobody can unlock.

Counts products through productlocations rather than per tenant, because a
tenant with a full catalogue can still have an outlet stocking none of it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 11:26:10 +05:30