# 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/` 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 recipient’s 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 recipient’s 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 booking’s 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.