209 lines
7.2 KiB
Markdown
209 lines
7.2 KiB
Markdown
# POS sign-in by mobile number, and shift assignment — handover to the terminal team
|
|
|
|
Backend is done and builds clean. **Nothing about this breaks the current app** —
|
|
username sign-in keeps working exactly as it does today. Read §5 before you ship
|
|
anything, because the switch has one ordering rule that will lock out every
|
|
cashier if it is done in the wrong order.
|
|
|
|
---
|
|
|
|
## 1. What changed, in one line
|
|
|
|
`POST /pos/login` now accepts a **mobile number** as well as a username, only
|
|
till accounts are candidates, and a till account can carry a **shift**.
|
|
|
|
---
|
|
|
|
## 2. Endpoints — what was edited and how
|
|
|
|
### `POST /live/api/v1/pos/login` — CHANGED (backwards compatible)
|
|
|
|
The request body already had both fields. **Nothing in the contract changed.**
|
|
What changed is behaviour behind it.
|
|
|
|
```jsonc
|
|
// Sign in by mobile — the new way
|
|
{ "contactno": "9876543210", "password": "xHegDaH55ccWic" }
|
|
|
|
// Sign in by username — still works, unchanged
|
|
{ "authname": "cashier.1135@pos.nearle.in", "password": "xHegDaH55ccWic" }
|
|
```
|
|
|
|
`authname` wins if both are sent. The response is unchanged.
|
|
|
|
**Behavioural change:** the account lookup is now restricted to till roles
|
|
(Supervisor `7`, Cashier `8`).
|
|
|
|
*Why it matters to you:* previously a number shared with a back-office account
|
|
returned two candidates and the login was refused outright. On live data **34
|
|
numbers are shared by 104 accounts** — one by eleven — so without this,
|
|
sign-in by phone would simply have failed for a large number of people. Now a
|
|
number only has to be unique among till accounts.
|
|
|
|
A back-office user who types their own password at a till still gets the
|
|
specific `403 "this account is not set up for the till"` rather than a vague
|
|
rejection. That did not change.
|
|
|
|
### `POST /live/api/v1/web/tenants/createposuser` — CHANGED (two new fields)
|
|
|
|
```jsonc
|
|
{
|
|
"tenantid": 1087,
|
|
"locationid": 1135,
|
|
"full_name": "Priya Raman",
|
|
"role": "cashier",
|
|
"pin": "4731",
|
|
"contactno": "9876543210", // NEW — the sign-in number
|
|
"shift_id": 3 // NEW — optional, 0/omitted = unassigned
|
|
}
|
|
```
|
|
|
|
The response gains `shift_id`, and `contactno` now comes back **normalised to
|
|
ten digits**.
|
|
|
|
### `PUT /live/api/v1/web/tenants/updateposuser` — CHANGED (two new fields)
|
|
|
|
Accepts `contactno` and `shift_id`. Every field stays optional; only what is
|
|
sent is written.
|
|
|
|
### `GET /live/api/v1/web/tenants/getposusers` — CHANGED (four new fields)
|
|
|
|
Each user in `details.users[]` now also carries:
|
|
|
|
```jsonc
|
|
{
|
|
"contactno": "9876543210",
|
|
"shift_id": 3,
|
|
"shift_name": "Morning",
|
|
"shift_start": "07:00",
|
|
"shift_end": "15:00"
|
|
}
|
|
```
|
|
|
|
Blank on accounts created before shifts existed. **`shift_*` also appears on
|
|
`staff[]` in the `/pos/login` session**, so the terminal gets it for free.
|
|
|
|
### `GET/POST/PUT /live/api/v1/web/tenants/{getstaffshifts,createstaffshift,updatestaffshift}` — NEW
|
|
|
|
Console-only. The terminal does not need to call these; shifts arrive with the
|
|
session. Documented for completeness:
|
|
|
|
```jsonc
|
|
// POST createstaffshift
|
|
{ "tenantid": 1087, "locationid": 1135,
|
|
"name": "Morning", "start_time": "07:00", "end_time": "15:00",
|
|
"weekdays": "1111100" } // 7 chars from Monday; empty = every day
|
|
```
|
|
|
|
Also mirrored under `/v1/mob/tenants/*`.
|
|
|
|
---
|
|
|
|
## 3. Number format — the one thing to get exactly right
|
|
|
|
The server reduces every number to **ten digits** before storing or matching:
|
|
non-digits are stripped, then a leading `91` or `0` is dropped once. Anything
|
|
that is not ten digits afterwards is **rejected**, not stored.
|
|
|
|
So all of these are the same account:
|
|
|
|
```
|
|
"+91 98765 43210" → 9876543210
|
|
"098765-43210" → 9876543210
|
|
"9876543210" → 9876543210
|
|
```
|
|
|
|
**What you should send:** the ten digits, or anything in the list above — the
|
|
server normalises either way. **Do not** send a country code the user did not
|
|
type, and do not reject `+91` locally; let it through and let the server reduce
|
|
it. What matters is that you never send something that normalises to a
|
|
*different* number than what the console stored.
|
|
|
|
---
|
|
|
|
## 4. Shift is informational
|
|
|
|
`shift_id` / `shift_name` / `shift_start` / `shift_end` are for **display**.
|
|
Nothing on the server refuses a bill rung outside a shift window, and you should
|
|
not add that check on the device either. A cashier locked out mid-queue by a
|
|
clock is a worse failure than a bill filed against the wrong window. Show whose
|
|
shift it is; do not gate on it.
|
|
|
|
Times are 24-hour `HH:MM`. A shift may legitimately wrap midnight (`22:00`
|
|
→ `06:00`) — do not assume `end > start`.
|
|
|
|
---
|
|
|
|
## 5. 🔴 Sequencing — read this before shipping
|
|
|
|
**Every existing POS account has no mobile number.** All 12 live accounts have
|
|
`contactno = ""`:
|
|
|
|
```
|
|
supervisor.1135@pos.nearle.in phone=""
|
|
cashier.1135@pos.nearle.in phone=""
|
|
… 12 of 12
|
|
```
|
|
|
|
If the app ships sign-in-by-phone **only**, every cashier in every shop is
|
|
locked out on the next app update.
|
|
|
|
**Required order:**
|
|
|
|
1. Backend deploys. *(Nothing changes for the app — username login is untouched.)*
|
|
2. Back office adds a mobile number to every existing till account through the
|
|
console. New accounts already require one.
|
|
3. **Only then** the app makes mobile the primary sign-in field.
|
|
|
|
**Recommendation for the app:** keep both. One field labelled *"Mobile number or
|
|
username"* — if the value is all digits send it as `contactno`, otherwise as
|
|
`authname`. That is a handful of lines, works before and after the backfill, and
|
|
means a shop with one un-backfilled account is not stranded.
|
|
|
|
---
|
|
|
|
## 6. Errors you should handle
|
|
|
|
| Message | Meaning | What the app should do |
|
|
|---|---|---|
|
|
| generic 401 rejection | wrong number/username or wrong password | "Check your details" — do **not** say which was wrong |
|
|
| `this account is not set up for the till…` | a back-office login was used | Show it verbatim; it names the fix |
|
|
| `more than one account uses these sign-in details…` | ambiguous match | Show verbatim; it needs the back office |
|
|
| `this account has no password set…` | provisioned without a password | Show verbatim |
|
|
| `another till account in this business already signs in with 9876543210` | console-side only | Not seen by the app |
|
|
|
|
---
|
|
|
|
## 7. What did **not** change
|
|
|
|
- The response shape of `/pos/login`, including `token`, `expires_at`,
|
|
`can_manage_staff`, `locations[]` and `staff[]`
|
|
- `POST /pos/login/pin`
|
|
- Token format, TTL (30 days) and the `PosAuth` guard
|
|
- `POST /pos/orders`, `/pos/customers`, `/pos/health`, `GET /pos/catalogue`
|
|
- `POS_AUTH_REQUIRED` still defaults to off
|
|
|
|
---
|
|
|
|
## 8. Verify after deploy
|
|
|
|
```bash
|
|
B=https://fiesta.nearle.app/live/api/v1
|
|
|
|
# username sign-in still works (regression check — run this first)
|
|
curl -s -X POST "$B/pos/login" -H 'Content-Type: application/json' \
|
|
-d '{"authname":"supervisor.1135@pos.nearle.in","password":"…"}' \
|
|
| grep -o '"can_manage_staff":[a-z]*'
|
|
# expect: "can_manage_staff":true
|
|
|
|
# after a number is set on that account, the same account by phone
|
|
curl -s -X POST "$B/pos/login" -H 'Content-Type: application/json' \
|
|
-d '{"contactno":"9876543210","password":"…"}' \
|
|
| grep -o '"can_manage_staff":[a-z]*'
|
|
|
|
# a back-office account is still refused with the specific message
|
|
curl -s -X POST "$B/pos/login" -H 'Content-Type: application/json' \
|
|
-d '{"authname":"rmart@gmail.com","password":"rmart@123"}'
|
|
# expect: 403 "this account is not set up for the till"
|
|
```
|