105 lines
5.6 KiB
Markdown
105 lines
5.6 KiB
Markdown
# booking-requests — the money-free pre-payment request
|
|
|
|
> Client seam `client/src/services/bookingRequests/` · `USE_BOOKING_REQUESTS_MOCK = false` (**real**) · 7 server ops
|
|
> Last verified: 2026-07-30 against commit `d3ec723` and swagger.v1.json (2026-07-29).
|
|
|
|
Stage one of the booking flow: the customer asks, the nurse answers, and **no money exists yet**. A
|
|
`booking_requests` row becomes a `bookings` row only on payment capture — see
|
|
[bookings.md](bookings.md) and [payment.md](payment.md).
|
|
|
|
> `Features/Booking` (singular, this domain) and `Features/Bookings` (plural, the post-payment engine) are
|
|
> **different server areas, not a rename.**
|
|
|
|
## Endpoints
|
|
|
|
| Method | Path | Caller | Verdict |
|
|
| --- | --- | --- | --- |
|
|
| POST | `/api/v1/booking_requests/create` | customer | wired |
|
|
| GET | `/api/v1/booking_requests/list` | both | wired · paginated · role-scoped |
|
|
| GET | `/api/v1/booking_requests/get/{id}` | both | wired |
|
|
| POST | `/api/v1/booking_requests/accept/{id}` | nurse | wired |
|
|
| POST | `/api/v1/booking_requests/reject/{id}` | nurse | wired |
|
|
| POST | `/api/v1/booking_requests/cancel/{id}` | customer | wired |
|
|
| GET | `/api/v1/booking_requests/checkout_summary/{id}` | customer | wired — but read by the **[payment](payment.md)** domain, not this one |
|
|
|
|
All `[Authorize]`. No phantoms.
|
|
|
|
`checkout_summary` is the C6 money read (gross / commission / VAT breakdown, REQ-016 delivered). It lives
|
|
on this controller because the request is what gets paid for, and is documented in
|
|
[payment.md](payment.md) where it is consumed.
|
|
|
|
## The lifecycle
|
|
|
|
```
|
|
pending_nurse_response ──accept──▸ accepted_awaiting_payment ──capture──▸ converted
|
|
│ │
|
|
├──reject──▸ rejected_by_nurse └──window lapses──▸ payment_deadline_expired
|
|
├──deadline──▸ expired_no_response
|
|
└──customer──▸ cancelled_by_customer
|
|
```
|
|
|
|
**Forward-only.** A backward or sideways transition is a clean `409`, never a 500. Two deadlines are
|
|
server-owned and config-driven (`booking_request_response_deadline_minutes`,
|
|
`payment_window_minutes` in [admin.md](admin.md)):
|
|
|
|
- the nurse's response window → `expired_no_response`
|
|
- the customer's payment window after acceptance → `payment_deadline_expired`
|
|
|
|
`POST /api/v1/admin_booking_requests/expire` is the ops one-shot for both; the in-process scheduler runs
|
|
it unattended. See [admin.md](admin.md).
|
|
|
|
## Shape rules the JSON does not express
|
|
|
|
- **Stage-one disclosure is deliberately partial.** Before payment the nurse sees only unencrypted
|
|
`customerNotes` and a **city/district-coarse masked address**. Encrypted care instructions and the full
|
|
address are unreadable until the booking is confirmed. This is a hard server rule, not a UI choice.
|
|
- **The countdown is server-frozen.** `CountdownTimer` renders a deadline the server sent; the client
|
|
never computes an expiry from a local clock.
|
|
- **`variantPrice` is on `BookingRequestDto`** (REQ-013, delivered) so the customer sees the price they are
|
|
committing to without a second variant fetch. It is a **digit string**.
|
|
- **The inbox list item carries `variantLabel` + `patientAge`** (REQ-014, delivered).
|
|
|
|
### The two list shapes, exactly
|
|
|
|
Confirmed field-by-field against the live swagger, because three in-repo comments disagree about this:
|
|
|
|
| | `BookingRequestListItemDto` (list) | `BookingRequestDto` (detail) |
|
|
| --- | --- | --- |
|
|
| `variantLabel` | **yes** | yes |
|
|
| `patientAge` | **yes** | — (`patientName` instead) |
|
|
| `variantPrice` · `variantPriceUnit` | **no** | yes |
|
|
| address / notes | `customerNotes` only | full masked address block |
|
|
| `nurseRejectionReason` | — | yes, **free text** |
|
|
|
|
Two consequences:
|
|
|
|
- **`client/src/services/bookingRequests/types.ts` is behind the wire.** It marks `variantLabel` as
|
|
"client-augmented … `undefined` on the real path", but the server serves it. Widening the client type is
|
|
safe and would let the real inbox card render its decision-first headline today.
|
|
- **REQ-050 is partly stale.** It states the list DTO carries neither field, "confirmed against
|
|
`services/bookingRequests/types.ts`" — i.e. confirmed against the client type, not the wire. What the
|
|
wire genuinely lacks is `variantPrice`/`variantPriceUnit` on the *list* row and the `status=answered`
|
|
group filter.
|
|
- **`requiredCaregiverGender` is never defaulted or dropped.** `any` is an explicit choice, distinct from
|
|
absent.
|
|
- The address the request references is snapshotted at create time; later edits to the saved address do
|
|
not rewrite it.
|
|
|
|
## Enums
|
|
|
|
| Vocabulary | Values |
|
|
| --- | --- |
|
|
| `BookingRequestStatus` | `pending_nurse_response` `accepted_awaiting_payment` `converted` `rejected_by_nurse` `expired_no_response` `payment_deadline_expired` `cancelled_by_customer` |
|
|
| `RequiredCaregiverGender` | `male` `female` `any` |
|
|
| `RequestRole` *(client-side list filter)* | `customer` `nurse` |
|
|
|
|
Verified identical to `Baya.Domain/Entities/Booking/BookingRequestStatus.cs`. REQ-015 confirmed these
|
|
serialise as the exact snake_case codes.
|
|
|
|
## Open REQs
|
|
|
|
| REQ | Status | Effect |
|
|
| --- | --- | --- |
|
|
| REQ-044 | open | No structured `nurseRejectionReasonCode` — the wire carries free-text `nurseRejectionReason`. The client runs a keyword heuristic over it, documented in-code as a known approximation |
|
|
| REQ-050 | open, **narrower than filed** | The list row already has `variantLabel`; it lacks `variantPrice`/`variantPriceUnit`. The `status=answered` group filter is genuinely absent, so the «پاسخداده» tab fires three page-1 queries and concatenates — an unpaged workaround a nurse with many answered requests will hit |
|