Files
alla-allaos-fullstack/plans/task-f.4.md
phaichayon 2ace5dbb83 task-f.4
2026-06-26 15:27:05 +07:00

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
```