# 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.