676 lines
11 KiB
Markdown
676 lines
11 KiB
Markdown
# 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<string, unknown>;
|
|
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
|
|
```
|