delivery slot updated in orders and deliveries
This commit is contained in:
218
docs/DELIVERY_SLOTS_APP.md
Normal file
218
docs/DELIVERY_SLOTS_APP.md
Normal file
@@ -0,0 +1,218 @@
|
||||
# Delivery windows — the app contract
|
||||
|
||||
Shoppers now choose when their order arrives: one of three windows a day —
|
||||
morning, afternoon, evening — set per branch by the shop.
|
||||
|
||||
Two things to build: show the choice at checkout, and send it with the order.
|
||||
Everything else is done.
|
||||
|
||||
---
|
||||
|
||||
## 1. What a shopper may pick
|
||||
|
||||
```
|
||||
GET https://fiesta.nearle.app/live/api/v1/mob/deliveryslots/available?tenantid=&locationid=
|
||||
```
|
||||
|
||||
No authentication. Call it at checkout, once the branch is known.
|
||||
|
||||
**Real response** (branch 1179, taken at 19:02 IST):
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"message": "Success",
|
||||
"status": true,
|
||||
"details": [
|
||||
{ "deliveryslotid": 3, "slotkey": "evening", "name": "Evening",
|
||||
"starttime": "17:00", "endtime": "20:00",
|
||||
"slotdate": "2026-10-06", "istomorrow": false },
|
||||
{ "deliveryslotid": 1, "slotkey": "morning", "name": "Morning",
|
||||
"starttime": "08:00", "endtime": "10:00",
|
||||
"slotdate": "2026-10-07", "istomorrow": true },
|
||||
{ "deliveryslotid": 2, "slotkey": "afternoon", "name": "Afternoon",
|
||||
"starttime": "12:00", "endtime": "15:00",
|
||||
"slotdate": "2026-10-07", "istomorrow": true },
|
||||
{ "deliveryslotid": 3, "slotkey": "evening", "name": "Evening",
|
||||
"starttime": "17:00", "endtime": "20:00",
|
||||
"slotdate": "2026-10-07", "istomorrow": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Morning and afternoon are absent from today because both had ended by 19:02.
|
||||
**That filtering is already done — render the list as given.**
|
||||
|
||||
### Do no time arithmetic
|
||||
|
||||
Do not compare `starttime`/`endtime` against the device clock to decide what to
|
||||
show. The server owns that rule, and the device's clock, timezone and locale are
|
||||
all things we do not control. If the app re-derives it, the two will disagree
|
||||
and the shopper will be offered a window the server then rejects.
|
||||
|
||||
The fields are there to display — "Evening, 5–8pm" — not to filter on.
|
||||
|
||||
### Ordering
|
||||
|
||||
Already sorted: today's remaining windows first, then tomorrow's, each by start
|
||||
time. Render in the order given.
|
||||
|
||||
`istomorrow` is there so you can put "Tomorrow" beside a name without comparing
|
||||
dates yourself.
|
||||
|
||||
---
|
||||
|
||||
## 2. An empty list is normal
|
||||
|
||||
```json
|
||||
{ "code": 200, "message": "Success", "status": true, "details": [] }
|
||||
```
|
||||
|
||||
**This is not an error, and it is the common case today.** Most branches have
|
||||
not set windows yet, and they are trading normally right now.
|
||||
|
||||
When `details` is empty:
|
||||
|
||||
- Do not show the window picker
|
||||
- Do not show an error, a retry, or "this shop is closed"
|
||||
- **Let the order go through with no window**, exactly as before this feature
|
||||
|
||||
The whole rollout depends on this. A branch with no windows is an ordinary
|
||||
branch, and treating it as broken would take every shop on the platform offline.
|
||||
|
||||
It is always `[]`, never `null`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Sending the choice
|
||||
|
||||
```
|
||||
POST https://fiesta.nearle.app/live/api/v1/mob/orders/createorder
|
||||
```
|
||||
|
||||
Two new **optional** fields on the existing body:
|
||||
|
||||
```json
|
||||
{
|
||||
"deliveryslotid": 3,
|
||||
"deliveryslotdate": "2026-10-06"
|
||||
}
|
||||
```
|
||||
|
||||
Send both or neither. Copy them straight from the chosen entry — do not
|
||||
recompute the date.
|
||||
|
||||
Omitting them creates an order with no window, which is valid and unchanged
|
||||
from today's behaviour.
|
||||
|
||||
---
|
||||
|
||||
## 4. The one error to handle
|
||||
|
||||
A window takes orders **right up until it ends**, then stops. So a shopper who
|
||||
opens checkout at 09:58 and pays at 10:02 has chosen a window that closed while
|
||||
they were deciding.
|
||||
|
||||
The server re-checks on every order and answers:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 409,
|
||||
"status": false,
|
||||
"message": "the morning window has closed for today — please choose another"
|
||||
}
|
||||
```
|
||||
|
||||
**On 409:** re-fetch `available`, show the fresh list, ask again. The `message`
|
||||
is written to be shown to the shopper as-is.
|
||||
|
||||
Other 409 messages from the same check, all safe to display:
|
||||
|
||||
- `that delivery window is not one this shop offers`
|
||||
- `the evening window is not currently available` — the shop switched it off
|
||||
- `that delivery window has already passed`
|
||||
- `a delivery date is required with a delivery window`
|
||||
|
||||
This is worth handling properly rather than as a generic failure. It is the one
|
||||
case that will happen to real people in normal use.
|
||||
|
||||
---
|
||||
|
||||
## 5. Reading it back
|
||||
|
||||
Orders carry what was chosen:
|
||||
|
||||
```json
|
||||
{ "deliveryslotid": 3, "deliveryslotdate": "2026-10-06" }
|
||||
```
|
||||
|
||||
Both absent or `0`/empty on orders placed without a window. Show the window on
|
||||
the confirmation screen and in order history; treat absence as "no window was
|
||||
asked for", never as missing data.
|
||||
|
||||
---
|
||||
|
||||
## 6. What the window means
|
||||
|
||||
**A preference, not a promise.**
|
||||
|
||||
- Every order is accepted. A window never fills up and never blocks a sale.
|
||||
- There is no capacity limit, and no "slots remaining".
|
||||
- It tells the shop when to group the drop, and the shopper roughly when to
|
||||
expect it.
|
||||
|
||||
Please do not word it in the app as a guaranteed delivery time. "Arrives
|
||||
between 5 and 8pm" is right; "Guaranteed by 8pm" is not something the backend
|
||||
can honour.
|
||||
|
||||
---
|
||||
|
||||
## 7. Done on our side
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `deliveryslots` table, per branch | ✅ live |
|
||||
| `GET /v1/mob/deliveryslots/available` | ✅ live, filtering and dating already applied |
|
||||
| `orders.deliveryslotid` + `deliveryslotdate` | ✅ live |
|
||||
| `createorder` accepts and validates both | ✅ live, 409 on a closed window |
|
||||
| Server timezone (IST) | ✅ fixed — windows close on the shop's clock |
|
||||
| Shops set their windows at onboarding | ✅ live in both consoles |
|
||||
| Shops edit them later (Store profile → Settings) | ✅ live |
|
||||
| Window shown on the order in the console | ✅ live |
|
||||
|
||||
Verified end to end on live data: saved through the console, served to the app
|
||||
already filtered, with today's closed windows correctly absent.
|
||||
|
||||
**Not done:** grouping the dispatch queue by window. That is a console concern
|
||||
and does not affect anything above.
|
||||
|
||||
---
|
||||
|
||||
## 8. A branch you can test against
|
||||
|
||||
**Tenant `1141`, branch `1179`** — three windows configured:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Morning | 08:00–10:00 |
|
||||
| Afternoon | 12:00–15:00 |
|
||||
| Evening | 17:00–20:00 |
|
||||
|
||||
```
|
||||
GET /live/api/v1/mob/deliveryslots/available?tenantid=1141&locationid=1179
|
||||
```
|
||||
|
||||
Call it at different times of day and the list shortens as windows close —
|
||||
that is the easiest way to see the rule working.
|
||||
|
||||
For the empty-list path, use any other branch: most have no windows set, which
|
||||
is exactly the case you need to handle.
|
||||
|
||||
---
|
||||
|
||||
## Questions
|
||||
|
||||
The rule lives in one place server-side (`services/deliverySlotService.go`), so
|
||||
if anything about open/closed looks wrong, it is one function and not a
|
||||
disagreement between us. Ask rather than working around it in the app — a
|
||||
workaround on the device is how the two clocks drift apart.
|
||||
Reference in New Issue
Block a user