# Task F.4: Notification Framework Foundation ## Status Planned ## Objective Build a reusable Notification Framework for CRM events, starting with Approval events. This task introduces a centralized notification foundation so Approval, Lead, Opportunity, Quotation, Follow-up, and future modules can publish events without each feature directly sending email or UI notifications. The goal is to support: * event-driven notification creation * in-app notification inbox / bell foundation * notification recipient resolution * notification templates * read/unread state * approval event integration * future channel support for email, LINE, webhook, and reminder/escalation This task does not implement full email delivery, LINE integration, or escalation scheduling yet. --- ## Mandatory Review Review before implementation: * `AGENTS.md` * `docs/standards/project-foundations.md` * `docs/standards/task-catalog.md` * `docs/implementation/task-f2-approval-matrix-foundation.md` * `docs/implementation/task-f3-approval-workflow-builder-foundation.md` * existing Approval submit/approve/reject/cancel flow * `src/db/schema.ts` * `src/features/crm/approval/**` * `src/features/foundation/approval/**` * `src/features/foundation/audit-log/**` * `src/lib/auth/rbac.ts` * `docs/security/crm-authorization-boundaries.md` --- ## Current Foundation Already available: ```txt Approval Matrix Approval Workflow Builder Approval Requests Approval Actions Audit Log Foundation CRM Authorization Foundation ``` Current limitation: ```txt Approval actions do not yet publish reusable notifications. ``` --- ## Business Requirement When approval activity happens, related users should be notified. Examples: ```txt Quotation submitted for approval → notify current approver(s) Quotation approved → notify requester and next approver if any Quotation rejected → notify requester Quotation returned / cancelled → notify requester or related actors ``` The notification system should be generic enough for future events: ```txt Lead Assigned Opportunity Assigned Follow-up Due Quotation Expiring Opportunity Won Opportunity Lost PO Received ``` --- ## Scope F.4.1 Notification Schema Add tables: ```txt app_notifications app_notification_events app_notification_templates app_notification_deliveries ``` ### app_notification_events Stores source events published by modules. Recommended columns: ```txt id organization_id event_type entity_type entity_id actor_user_id payload jsonb dedupe_key status published_at processed_at created_at ``` Purpose: ```txt Event source stream ``` ### app_notifications Stores user-facing in-app notifications. Recommended columns: ```txt id organization_id recipient_user_id event_id title body link_url severity status read_at archived_at metadata jsonb created_at updated_at ``` Purpose: ```txt Notification inbox / bell ``` ### app_notification_templates Stores reusable message templates. Recommended columns: ```txt id organization_id event_type channel title_template body_template link_template is_active metadata jsonb created_at updated_at deleted_at created_by updated_by ``` Purpose: ```txt Event message rendering ``` ### app_notification_deliveries Tracks channel delivery attempts. Recommended columns: ```txt id organization_id notification_id channel recipient_address status attempt_count last_error sent_at created_at updated_at ``` Purpose: ```txt Future email / LINE / webhook delivery tracking ``` Rules: * In-app notification is implemented first * Email/LINE delivery can remain queued or pending * Use organization scope everywhere * Use soft delete where appropriate --- ## Scope F.4.2 Notification Event Service Create: ```txt src/features/foundation/notifications/server/event-service.ts ``` Functions: ```ts publishNotificationEvent(input) processNotificationEvent(eventId) processPendingNotificationEvents() ``` Input example: ```ts { organizationId: string; eventType: string; entityType: string; entityId: string; actorUserId?: string | null; payload?: Record; dedupeKey?: string; } ``` Rules: * publishing an event does not send email directly * event creates one or more in-app notifications * duplicate event with same `dedupeKey` should be ignored or safely reused * processing should be idempotent * failed processing should leave inspectable state --- ## Scope F.4.3 Recipient Resolver Create: ```txt src/features/foundation/notifications/server/recipient-resolver.ts ``` Support recipient types: ```txt explicit_user approval_current_step_approvers approval_requester approval_previous_actor entity_owner sales_owner ``` For F.4 required recipient resolvers: ```txt approval_current_step_approvers approval_requester explicit_user ``` Rules: * return user IDs only * never notify deleted/inactive users if such state exists * avoid notifying the actor if event rules say actor should be excluded * de-duplicate recipients --- ## Scope F.4.4 Template Renderer Create: ```txt src/features/foundation/notifications/server/template-renderer.ts ``` Functions: ```ts renderNotificationTemplate(template, payload) ``` Supported syntax can be simple: ```txt {{quotationCode}} {{workflowName}} {{actorName}} {{stepName}} {{entityLink}} ``` Rules: * missing variables render as `-` or empty string * no arbitrary code execution * no unsafe HTML injection * plain text first --- ## Scope F.4.5 Notification Inbox APIs Create: ```txt GET /api/notifications GET /api/notifications/unread-count POST /api/notifications/[id]/read POST /api/notifications/read-all POST /api/notifications/[id]/archive ``` Rules: * users can only see their own notifications * organization scope must apply * unread count must be efficient * support pagination * default ordering: newest first --- ## Scope F.4.6 Notification UI Foundation Add or integrate notification bell. Required UI: ```txt Notification Bell Unread Count Badge Notification Dropdown / Sheet Mark as Read Mark All as Read Open Link ``` Recommended placement: ```txt top nav / header ``` Rules: * in-app only in F.4 * no real-time requirement yet * polling or manual refetch is acceptable * empty state required * error state required --- ## Scope F.4.7 Approval Event Integration Publish notification events from approval lifecycle. Required events: ```txt approval.requested approval.step.approved approval.step.rejected approval.completed approval.cancelled approval.returned ``` Required recipients: ### approval.requested Notify: ```txt current step approvers ``` ### approval.step.approved Notify: ```txt requester next step approvers if any ``` ### approval.step.rejected Notify: ```txt requester ``` ### approval.completed Notify: ```txt requester ``` ### approval.cancelled Notify: ```txt requester current approvers if needed ``` ### approval.returned Notify: ```txt requester ``` Rules: * publish events from approval service layer, not route handlers * do not block approval transaction on notification delivery failure if possible * notification failure must be logged/inspectable * approval action should still succeed if notification creation fails unless strict mode is explicitly required --- ## Scope F.4.8 Default Notification Templates Seed default templates for approval events. Example: ```txt approval.requested Title: Approval Required: {{quotationCode}} Body: {{actorName}} submitted {{quotationCode}} for approval. Link: /dashboard/crm/quotations/{{quotationId}} ``` ```txt approval.step.rejected Title: Approval Rejected: {{quotationCode}} Body: {{actorName}} rejected {{quotationCode}}. Reason: {{remark}} Link: /dashboard/crm/quotations/{{quotationId}} ``` Rules: * templates should be organization-scoped * seed should be idempotent * no customer-sensitive data beyond document code/title in default body --- ## Scope F.4.9 Delivery Channel Foundation Support channel enum: ```txt in_app email line webhook ``` F.4 implementation: ```txt in_app = active email = delivery record only / pending line = future webhook = future ``` Do not implement real SMTP/LINE/Webhook sending yet. --- ## Scope F.4.10 Audit / Observability Audit or log: ```txt notification_event_published notification_event_processed notification_created notification_read notification_archived ``` Rules: * user read/archive actions should be auditable if current audit policy requires it * processing failures must store error detail safely * do not expose stack traces to users --- ## Scope F.4.11 Permissions Add or verify permissions: ```txt notifications.read notifications.update notifications.admin ``` Rules: * normal users can read/update only their own notifications * admin can inspect templates/events only if admin API is added later * notification bell should not require CRM module-specific permission --- ## Scope F.4.12 Documentation Create: ```txt docs/implementation/task-f4-notification-framework-foundation.md ``` Document: ```txt schema event publishing flow recipient resolver template syntax approval event integration delivery channel limitations known limitations ``` --- ## Event Flow Recommended architecture: ```txt Approval Service ↓ publishNotificationEvent() ↓ app_notification_events ↓ processNotificationEvent() ↓ resolveRecipients() ↓ renderTemplate() ↓ app_notifications ↓ Notification Bell / Inbox ``` --- ## Explicit Non-Scope Do not implement: * SMTP email sending * LINE OA integration * webhook delivery * real-time websocket / SSE * reminder scheduling * escalation rules * notification preference center * admin template editor UI * full notification analytics * mobile push notifications These belong to later F tasks. --- ## Verification Run: ```txt npm exec tsc --noEmit npm run build ``` Manual verification: ```txt Submit quotation for approval Current approver receives notification Approve step Requester receives notification Next approver receives notification if next step exists Reject step Requester receives notification Unread count updates Mark one notification as read Mark all notifications as read Archive notification Open notification link Approval still succeeds if notification processing fails gracefully ``` --- ## Definition of Done Task is complete when: * Notification event tables exist * Notification event service exists * Recipient resolver exists * Template renderer exists * In-app notification records are created from approval events * Notification inbox APIs exist * Notification bell UI exists * Approval submit/approve/reject events publish notifications * Notification read/unread workflow works * Existing approval flow remains operational Result: ```txt Notification Framework Foundation = Established Approval Notifications = Operational In-App Ready for F.5 Reminder / Escalation ```