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