Booking lifecycle notifications and status updates

This commit is contained in:
2026-02-28 15:06:35 +03:00
parent db36551211
commit ca2a6b58b6
15 changed files with 613 additions and 5 deletions
+104
View File
@@ -0,0 +1,104 @@
# Booking Lifecycle Notifications (SMS/WhatsApp)
This ExecPlan is a living document. The sections `Progress`, `Surprises & Discoveries`, `Decision Log`, and `Outcomes & Retrospective` must be kept up to date as work proceeds.
The requirements for ExecPlans live in `PLANS.md` at the repository root. This document must be maintained in accordance with that file.
## Purpose / Big Picture
After this change, a booking will automatically notify the customer and the assigned staff member when it is created, confirmed, or cancelled. You can see it working by creating a booking and observing two notification records (customer + staff), then changing the booking status to confirmed or cancelled and seeing two more notification records for that event. In the console provider, the messages are logged, giving an immediate, user-visible trace of the booking lifecycle.
## Progress
- [x] (2026-02-28 17:05Z) Created ExecPlan for booking lifecycle notifications and reviewed bookings + notifications gaps.
- [x] (2026-02-28 17:30Z) Implemented notifications app with audit-friendly model, providers, and booking message templates.
- [x] (2026-02-28 17:40Z) Connected booking create/update flows to notification dispatch with idempotent event handling.
- [x] (2026-02-28 17:55Z) Allowed booking status updates with role checks to enable confirmation/cancellation.
- [x] (2026-02-28 18:05Z) Added tests for booking notifications (create, status change, no duplicate sends).
- [x] (2026-02-28 18:10Z) Updated `docs/risks.md` and validated tests (`python3 -m pytest`).
## Surprises & Discoveries
- Observation: Booking status updates were blocked because `status` was read-only on the default booking serializer.
Evidence: `PATCH /api/bookings/<id>` returned HTTP 400 when attempting to confirm.
## Decision Log
- Decision: Store every booking notification in a dedicated `Notification` model for auditability, even when skipped.
Rationale: Lifecycle messages are user-facing and must be traceable for support and compliance.
Date/Author: 2026-02-28, Codex
- Decision: Reuse existing OTP provider adapters for SMS/WhatsApp delivery, with a new `NOTIFICATION_PROVIDER` setting.
Rationale: Avoid duplicate integration code while still allowing independent provider configuration.
Date/Author: 2026-02-28, Codex
- Decision: Default to SMS for booking notifications and use the recipients preferred language when formatting messages.
Rationale: SMS is the most reliable baseline in KSA, and language preference is already captured on the user.
Date/Author: 2026-02-28, Codex
- Decision: Allow booking status changes via `BookingSerializer` with role-based validation.
Rationale: Confirmation/cancellation must be reachable through the existing API, but should still respect basic role boundaries.
Date/Author: 2026-02-28, Codex
## Outcomes & Retrospective
Booking lifecycle notifications are now implemented with audit-friendly records and idempotent sending. Booking creation and status changes (confirmed/cancelled) trigger SMS/WhatsApp notifications for both customer and staff, and role-based validation now governs status updates. Provider adapters remain scaffolds, so production delivery still requires real SMS/WhatsApp wiring.
## Context and Orientation
Booking creation and updates are handled in `backend/apps/bookings/views.py` via a DRF `ModelViewSet`. The booking model is in `backend/apps/bookings/models.py`, with `status` indicating lifecycle state. There is currently no notification system beyond OTP scaffolding in `backend/apps/accounts/services/otp.py`. This plan adds a new Django app at `backend/apps/notifications/` to store notification records, format booking lifecycle messages, and dispatch them via SMS or WhatsApp providers.
A “notification” in this repository means a user-facing message (SMS or WhatsApp) that is stored for auditability in a `Notification` database row. A “lifecycle event” is a booking change that should inform the customer and staff: booking created, confirmed, or cancelled.
## Plan of Work
First, create a `notifications` Django app with models and admin registration. Define `Notification`, `NotificationEvent`, `NotificationStatus`, and `NotificationChannel` in `backend/apps/notifications/models.py`. The model must capture booking, recipient, phone number, event, channel, status, provider, message, and send timestamps, and it must be idempotent by preventing duplicates for the same booking + recipient + event + channel. Register the model in `backend/apps/notifications/admin.py` and add `apps.notifications` to `INSTALLED_APPS` in `backend/salon_api/settings.py`.
Next, implement notification dispatch in `backend/apps/notifications/services.py`. Reuse OTP provider adapters from `apps.accounts.services.otp` with a new `NOTIFICATION_PROVIDER` setting (default to `OTP_PROVIDER`). Add a `NOTIFICATION_DEFAULT_CHANNEL` setting (default `sms`). Implement `send_booking_notification(booking, recipient, event)` to build localized message text using the recipients preferred language, send via the provider, and update the notification status. Implement `notify_booking_lifecycle(booking, event)` for initial sends and `notify_on_status_change(booking, previous_status)` to trigger only on status transitions. If the recipient lacks a phone number, record the notification as `skipped` with a reason.
Then, wire booking lifecycle events in `backend/apps/bookings/views.py`. On `perform_create`, call `notify_booking_lifecycle(..., booking_created)` so both customer and staff receive a message. On `perform_update`, compare the previous status to the new status and call `notify_on_status_change` for confirmed or cancelled transitions. Avoid sending notifications if the status does not change.
Finally, add tests in `backend/apps/notifications/tests/test_booking_notifications.py`. Cover booking creation (two notifications), status change to confirmed (two notifications), and a repeat status update that should not create duplicates. Ensure tests use phone numbers on users to avoid skipped notifications. Update `docs/risks.md` to mark “No notifications (email/SMS) beyond OTP scaffolding” as addressed once tests pass.
## Concrete Steps
Run these commands from the repository root (`/home/m7md/kshkool/Salon`).
1. Add notifications app code and migrations.
- Create `backend/apps/notifications/` with `apps.py`, `models.py`, `services.py`, `admin.py`, and a migration `0001_initial.py`.
- Update `backend/salon_api/settings.py` to include `apps.notifications` and notification settings.
2. Wire booking lifecycle events.
- Update `backend/apps/bookings/views.py` to call notification services on create and status changes.
3. Add tests.
- Create `backend/apps/notifications/tests/test_booking_notifications.py`.
4. Run backend tests.
- From `backend/` with the venv active:
python3 -m pytest
## Validation and Acceptance
- Creating a booking returns HTTP 201 and creates two notification records (customer + staff) with event `booking_created`.
- Updating a bookings status to `confirmed` creates two notification records with event `booking_confirmed`.
- Repeating the same status update does not create duplicate notifications (records remain at two for that event).
- `python3 -m pytest` passes, and the new tests fail before the change and pass after.
## Idempotence and Recovery
Notification creation is idempotent by a uniqueness constraint on booking + recipient + event + channel. Re-running the send logic will update a pending or failed notification rather than creating duplicates. If a migration needs to be reverted, use standard Django migration rollback and re-apply. If a notification provider is misconfigured, notifications will be marked failed and can be retried after fixing settings.
## Artifacts and Notes
Expected console-provider log example when creating a booking:
INFO OTP SMS to 0500000002: Your booking request is received for Haircut at Main Salon on 2026-03-01 10:00.
INFO OTP SMS to 0500000003: Your booking request is received for Haircut at Main Salon on 2026-03-01 10:00.
## Interfaces and Dependencies
- `backend/apps/notifications/models.py` must define `Notification`, `NotificationEvent`, `NotificationStatus`, `NotificationChannel`.
- `backend/apps/notifications/services.py` must expose `send_booking_notification`, `notify_booking_lifecycle`, and `notify_on_status_change`.
- `backend/apps/bookings/views.py` must call notification services in `perform_create` and `perform_update`.
- `backend/salon_api/settings.py` must define `NOTIFICATION_PROVIDER` and `NOTIFICATION_DEFAULT_CHANNEL` settings.
Plan Maintenance Note: Created on 2026-02-28 to implement booking lifecycle notifications as the next Phase 1 reliability milestone.
Plan Maintenance Note (Update): Marked milestones complete, recorded the booking status update discovery, and documented role-based status validation after implementing notifications and tests on 2026-02-28.
+1 -1
View File
@@ -21,7 +21,7 @@ This file tracks known gaps and risks to address in future iterations.
## Data And UX
- Ratings are not recalculated from reviews.
- No image upload or storage strategy for photos.
- No notifications (email/SMS) beyond OTP scaffolding.
- Booking lifecycle notifications are implemented (SMS/WhatsApp via provider scaffolds); production delivery still needs real provider adapters.
- Localization foundations are in progress; full Arabic translation coverage and RTL QA are still pending.
## Ops And Compliance