task-d.5.3

This commit is contained in:
phaichayon
2026-06-24 14:23:15 +07:00
parent 350239b307
commit 0c28735c90
61 changed files with 11019 additions and 173 deletions

537
plans/task-d.5.3.md Normal file
View File

@@ -0,0 +1,537 @@
# Task D.5.3: Lead Assignment → Create Enquiry Foundation
## Status
Planned
## Objective
Implement the first production transition from Lead into Enquiry.
This task establishes the business boundary between:
```txt
Marketing Domain
Lead
Sales Domain
Enquiry
```
A Lead remains the marketing-owned source record.
An Enquiry becomes the sales execution workspace.
This task introduces:
* Lead assignment workflow
* Enquiry creation from Lead
* Lead ↔ Enquiry relationship
* Initial ownership transfer
* Enquiry bootstrap generation
without changing quotation, dashboard, reporting, or approval behavior.
---
# Mandatory Review
Review:
* AGENTS.md
* Project Foundations Registry
* Task Catalog
* ADR-0018 Lead / Enquiry Domain Separation
* Task D.5
* Task D.5.1
* Task D.5.2
* Customer Ownership Foundation
* CRM Security Foundation
* Audit Foundation
* Document Sequence Foundation
---
# Existing Foundation
Verified available:
```txt
crm_leads
crm_enquiries
crm_enquiries.lead_id
crm_lead sequence
crm_enquiry sequence
Lead Service
Lead API
```
Do not recreate:
```txt
tables
migrations
seed data
master-option categories
```
---
# Business Model
## Before Assignment
Owner:
```txt
Marketing
```
Workspace:
```txt
Lead
```
---
## After Assignment
Owner:
```txt
Sales
```
Workspace:
```txt
Enquiry
```
---
## Relationship
```txt
1 Lead
0..N Enquiries
```
Lead remains immutable historical source.
Enquiries become sales execution records.
---
# Scope D5.3.1 Lead Assignment Service
Create:
```txt
src/features/crm/leads/server/assignment.service.ts
```
Required operation:
```txt
assignLead()
```
---
Input:
```ts
AssignLeadInput
```
Fields:
```ts
leadId
salesOwnerId
assignmentRemark
```
---
Validation:
```txt
lead exists
lead active
salesOwnerId valid
same organization
```
---
# Scope D5.3.2 Create Enquiry From Lead
Create:
```txt
createEnquiryFromLead()
```
inside assignment service.
---
Rules
Assignment automatically creates:
```txt
crm_enquiry
```
linked to:
```txt
crm_leads.id
```
through:
```txt
crm_enquiries.lead_id
```
---
Reuse:
```txt
generateNextDocumentCode()
```
with:
```txt
crm_enquiry
```
---
Expected:
```txt
EN2606-001
```
or existing enquiry format.
No custom numbering logic.
---
# Scope D5.3.3 Enquiry Bootstrap Mapping
When enquiry is created:
Copy:
```txt
customerId
contactId
projectName
projectLocation
productType
description
estimatedValue
priority
```
from Lead.
---
Do NOT copy:
```txt
marketing-only notes
internal marketing activity history
audit events
```
---
Lead remains unchanged.
---
# Scope D5.3.4 Lead Assignment API
Create:
```txt
POST /api/crm/leads/[id]/assign
```
---
Handler must call:
```txt
Lead Assignment Service
```
No direct DB access.
---
Response:
```ts
{
leadId: string;
enquiryId: string;
enquiryCode: string;
}
```
---
# Scope D5.3.5 Ownership Transfer
After assignment:
Lead:
```txt
status = assigned
```
---
Lead:
```txt
assigned_sales_owner_id
assigned_at
assigned_by
```
updated.
---
Enquiry:
```txt
owner_user_id
```
set to assigned salesperson.
---
Marketing remains able to:
```txt
view lead
view assignment result
```
but not manage enquiry.
---
# Scope D5.3.6 CRM Security
Reuse:
```txt
resolveCrmMembershipAccess()
```
Assignment permission:
```txt
lead.assign
```
Only:
```txt
Marketing Manager
CRM Manager
Admin
```
or equivalent resolved permission.
---
Do not check role strings directly.
---
# Scope D5.3.7 Duplicate Protection
Prevent:
```txt
multiple enquiry creation
```
for the same assignment action.
---
Rules:
If assignment already produced an active enquiry:
```txt
return existing enquiry
```
instead of creating a second one.
---
# Scope D5.3.8 Audit Logging
Reuse audit foundation.
Required actions:
```txt
assign_lead
create_enquiry_from_lead
```
---
Audit must capture:
```txt
leadId
enquiryId
salesOwnerId
```
---
# Scope D5.3.9 Legacy Compatibility
Do not modify:
```txt
quotation flow
approval flow
dashboard
reports
won/lost process
```
---
Current enquiry APIs must continue functioning.
---
# Deliverables
New:
```txt
src/features/crm/leads/server/assignment.service.ts
```
---
New API:
```txt
POST /api/crm/leads/[id]/assign
```
---
Documentation:
```txt
docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md
```
---
# Verification
Run:
```txt
npm exec tsc --noEmit
```
---
Verify:
```txt
Assign Lead
Create Enquiry
Reuse Existing Enquiry
Lead Status Update
Audit Logging
Security Enforcement
```
---
Verify:
```txt
Quotation APIs still work
Dashboard still works
Reports still work
```
---
# Explicit Non-Scope
Do not:
* create Lead UI
* create Opportunity UI
* create quotation conversion
* change approval workflow
* change reports
* change dashboard
* migrate historical data
* implement won/lost synchronization
These belong to later D.5 phases.
---
# Definition of Done
Task is complete when:
* Lead assignment API exists
* Assignment creates or reuses enquiry
* crm_enquiries.lead_id is populated
* Enquiry owner is assigned salesperson
* Lead assignment is audited
* Existing CRM functionality remains unchanged
Result:
```txt
Lead Assignment = Established
Lead → Enquiry Transition = Established
Ready for Opportunity Workspace Separation
```

506
plans/task-d.5.4.md Normal file
View File

@@ -0,0 +1,506 @@
# Task D.5.4: Lead / Enquiry Workspace Separation UI Foundation
## Status
Planned
## Objective
Separate the user-facing CRM workspace between Lead and Enquiry.
This task makes Lead and Enquiry visible as different working areas while preserving existing enquiry, quotation, dashboard, and reporting behavior.
Business boundary:
```txt
Lead = Marketing-owned source record
Enquiry = Sales execution workspace
```
This task focuses on UI routing, navigation, data-source separation, and safe compatibility wiring.
---
## Mandatory Review
Review before implementation:
* `AGENTS.md`
* `docs/standards/project-foundations.md`
* `docs/standards/task-catalog.md`
* `docs/adr/0018-lead-enquiry-domain-separation.md`
* `docs/implementation/task-d5-lead-enquiry-domain-separation.md`
* `docs/implementation/task-d51-lead-foundation-stabilization.md`
* `docs/implementation/task-d52-lead-domain-service-api-foundation.md`
* `docs/implementation/task-d53-lead-assignment-create-enquiry-foundation.md`
* `docs/security/crm-authorization-boundaries.md`
* Existing CRM navigation/sidebar configuration
* Existing enquiry list/detail/form UI
* Existing lead API routes
* Existing enquiry API routes
---
## Existing Foundation
Already available:
```txt
crm_leads
crm_enquiries
crm_enquiries.lead_id
/api/crm/leads
/api/crm/leads/[id]
/api/crm/leads/[id]/followups
/api/crm/leads/[id]/assign
existing enquiry APIs
existing quotation APIs
```
Do not recreate:
```txt
tables
migrations
seed data
service foundation
assignment service
```
---
## Workspace Rules
### Lead Workspace
Audience:
```txt
Marketing
Marketing Manager
CRM Manager
Admin
```
Purpose:
```txt
capture lead
qualify lead
record awareness/status/lost reason
assign lead to sales
view linked enquiry result
```
Source:
```txt
crm_leads
/api/crm/leads
```
---
### Enquiry Workspace
Audience:
```txt
Sales
Sales Manager
CRM Manager
Admin
```
Purpose:
```txt
work assigned opportunity
prepare for quotation
track sales execution
continue existing enquiry lifecycle
```
Source:
```txt
crm_enquiries
existing /api/crm/enquiries
```
---
## Scope D5.4.1 Navigation Separation
Update CRM navigation so Lead and Enquiry are separate menu items.
Add:
```txt
CRM > Leads
CRM > Enquiries
```
Rules:
* Lead menu uses lead permission/scope
* Enquiry menu keeps existing permission/scope
* Do not rename Enquiry menu globally if it breaks existing routes
* Do not remove old enquiry route yet
Suggested routes:
```txt
/dashboard/crm/leads
/dashboard/crm/leads/[id]
/dashboard/crm/enquiries
/dashboard/crm/enquiries/[id]
```
If project uses another route prefix, follow existing app route convention.
---
## Scope D5.4.2 Lead List Page
Create Lead list page using `/api/crm/leads`.
Required columns:
```txt
Lead Code
Customer
Project Name
Status
Awareness
Estimated Value
Suggested Sales Owner
Assigned Sales Owner
Created Date
```
Required actions:
```txt
View
Edit
Assign
Delete
```
Rules:
* Assign action appears only when user has assignment permission
* Delete action appears only when user has delete permission
* No direct enquiry mutation from Lead list
* Use existing shadcn/table/list conventions
* Use design tokens only
* Follow existing CRM page layout pattern
---
## Scope D5.4.3 Lead Detail Page
Create Lead detail page using `/api/crm/leads/[id]`.
Sections:
```txt
Lead Summary
Customer Information
Qualification Information
Follow-up History
Assignment Information
Linked Enquiries
Audit-safe Activity View
```
Actions:
```txt
Edit Lead
Assign Lead
Create Follow-up
Delete Lead
Open Linked Enquiry
```
Rules:
* Assignment creates or reuses enquiry through `/api/crm/leads/[id]/assign`
* After assignment, show linked enquiry
* Marketing can see assignment result but must not edit enquiry unless permitted
* Follow-up history uses D.5.2 audit-backed implementation until future ADR decides final persistence
---
## Scope D5.4.4 Lead Create / Edit Form
Create or adapt Lead form components.
Required fields:
```txt
Customer
Contact
Project Name
Project Location
Description
Product Type
Priority
Estimated Value
Awareness
Status
Lost Reason
Notes
```
Rules:
* Use master options for awareness, status, lost reason
* No hardcoded labels
* Product type must use existing option/source convention
* Validate with D.5.2 lead schemas
* On create, call `POST /api/crm/leads`
* On edit, call `PATCH /api/crm/leads/[id]`
---
## Scope D5.4.5 Lead Assignment UI
Create assignment dialog or sheet.
Input:
```txt
Sales Owner
Assignment Remark
```
Behavior:
```txt
POST /api/crm/leads/[id]/assign
```
After success:
```txt
show enquiry code
show Open Enquiry action
refresh lead detail
refresh lead list
```
Rules:
* Sales owner list must use assignable sales members only
* If API returns existing enquiry, show reuse message instead of duplicate-created message
* Do not create enquiry client-side
* Do not mutate `crm_enquiries` directly from UI
---
## Scope D5.4.6 Enquiry UI Compatibility
Keep current enquiry UI operational.
Add optional linked-lead display if `lead_id` exists:
```txt
Source Lead
Lead Code
Open Lead
```
Rules:
* Do not rewrite enquiry workflow
* Do not change quotation conversion behavior
* Do not change dashboard/report queries
* Do not remove `pipeline_stage`
* Existing enquiry list/detail must still work
---
## Scope D5.4.7 Permission / Visibility Handling
UI must respect backend permissions but not replace backend enforcement.
Lead UI:
```txt
crm.lead.read
crm.lead.create
crm.lead.update
crm.lead.delete
crm.lead.assign
```
Enquiry UI:
```txt
existing enquiry permissions
```
Rules:
* Hide unauthorized actions
* Handle 403 gracefully
* Do not check role strings directly if access helpers exist
* Follow existing CRM access resolver/client conventions
---
## Scope D5.4.8 Empty / Loading / Error States
Implement consistent states:
```txt
loading
empty
not authorized
not found
server error
validation error
```
Messaging:
* Lead empty state should guide Marketing to create first Lead
* Enquiry empty state should remain sales-oriented
* Assignment success should clearly identify created/reused enquiry
---
## Scope D5.4.9 Documentation
Create:
```txt
docs/implementation/task-d54-lead-enquiry-workspace-separation-ui-foundation.md
```
Document:
```txt
routes added
components added
APIs consumed
permission mapping
compatibility notes
known limitations
```
---
## Deliverables
New or updated pages:
```txt
src/app/**/crm/leads/page.tsx
src/app/**/crm/leads/[id]/page.tsx
```
New or updated components:
```txt
src/features/crm/leads/components/lead-list.tsx
src/features/crm/leads/components/lead-form.tsx
src/features/crm/leads/components/lead-detail.tsx
src/features/crm/leads/components/lead-assignment-dialog.tsx
src/features/crm/leads/components/lead-followup-panel.tsx
```
Updated:
```txt
CRM navigation/sidebar
Enquiry detail optional Source Lead display
```
Documentation:
```txt
docs/implementation/task-d54-lead-enquiry-workspace-separation-ui-foundation.md
```
---
## Explicit Non-Scope
Do not:
* create new tables
* create migrations
* change lead assignment service behavior
* change enquiry service behavior beyond read-only linked lead display
* change quotation flow
* change approval flow
* change dashboard datasets
* change reports
* implement won/lost synchronization
* replace audit-backed lead follow-up persistence
* migrate historical enquiry records into leads
These belong to later D.5 phases.
---
## Verification
Run:
```txt
npm exec tsc --noEmit
```
Optional if project supports:
```txt
npm run lint
npm run build
```
Manual verification:
```txt
Lead menu appears for permitted users
Lead menu hidden or blocked for unauthorized users
Lead list loads from /api/crm/leads
Lead create works
Lead edit works
Lead detail works
Lead assign creates or reuses enquiry
Linked enquiry opens from lead detail
Existing enquiry page still works
Existing quotation flow still works
Dashboard still works
Reports still work
```
---
## Definition of Done
Task is complete when:
* Lead and Enquiry appear as separate workspaces
* Lead UI uses Lead APIs
* Enquiry UI remains compatible
* Lead assignment UI creates or reuses enquiry through API
* Linked enquiry is visible from Lead detail
* Existing enquiry, quotation, dashboard, and reporting flows remain unchanged
Result:
```txt
Lead Workspace = Established
Enquiry Workspace = Preserved
Lead / Enquiry UI Boundary = Established
Ready for D.5.5 Opportunity / Sales Workspace Refinement
```