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

11 KiB

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:

Approval Matrix
Approval Workflow Builder
Approval Requests
Approval Actions
Audit Log Foundation
CRM Authorization Foundation

Current limitation:

Approval actions do not yet publish reusable notifications.

Business Requirement

When approval activity happens, related users should be notified.

Examples:

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:

Lead Assigned
Opportunity Assigned
Follow-up Due
Quotation Expiring
Opportunity Won
Opportunity Lost
PO Received

Scope F.4.1 Notification Schema

Add tables:

app_notifications
app_notification_events
app_notification_templates
app_notification_deliveries

app_notification_events

Stores source events published by modules.

Recommended columns:

id
organization_id
event_type
entity_type
entity_id
actor_user_id
payload jsonb
dedupe_key
status
published_at
processed_at
created_at

Purpose:

Event source stream

app_notifications

Stores user-facing in-app notifications.

Recommended columns:

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:

Notification inbox / bell

app_notification_templates

Stores reusable message templates.

Recommended columns:

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:

Event message rendering

app_notification_deliveries

Tracks channel delivery attempts.

Recommended columns:

id
organization_id
notification_id
channel
recipient_address
status
attempt_count
last_error
sent_at
created_at
updated_at

Purpose:

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:

src/features/foundation/notifications/server/event-service.ts

Functions:

publishNotificationEvent(input)
processNotificationEvent(eventId)
processPendingNotificationEvents()

Input example:

{
  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:

src/features/foundation/notifications/server/recipient-resolver.ts

Support recipient types:

explicit_user
approval_current_step_approvers
approval_requester
approval_previous_actor
entity_owner
sales_owner

For F.4 required recipient resolvers:

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:

src/features/foundation/notifications/server/template-renderer.ts

Functions:

renderNotificationTemplate(template, payload)

Supported syntax can be simple:

{{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:

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:

Notification Bell
Unread Count Badge
Notification Dropdown / Sheet
Mark as Read
Mark All as Read
Open Link

Recommended placement:

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:

approval.requested
approval.step.approved
approval.step.rejected
approval.completed
approval.cancelled
approval.returned

Required recipients:

approval.requested

Notify:

current step approvers

approval.step.approved

Notify:

requester
next step approvers if any

approval.step.rejected

Notify:

requester

approval.completed

Notify:

requester

approval.cancelled

Notify:

requester
current approvers if needed

approval.returned

Notify:

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:

approval.requested
Title: Approval Required: {{quotationCode}}
Body: {{actorName}} submitted {{quotationCode}} for approval.
Link: /dashboard/crm/quotations/{{quotationId}}
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:

in_app
email
line
webhook

F.4 implementation:

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:

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:

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:

docs/implementation/task-f4-notification-framework-foundation.md

Document:

schema
event publishing flow
recipient resolver
template syntax
approval event integration
delivery channel limitations
known limitations

Event Flow

Recommended architecture:

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:

npm exec tsc --noEmit
npm run build

Manual verification:

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:

Notification Framework Foundation = Established
Approval Notifications = Operational In-App
Ready for F.5 Reminder / Escalation