create mvp path
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
# 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 |
|
||||
Reference in New Issue
Block a user