taks-d.2.1

This commit is contained in:
phaichayon
2026-06-17 14:46:52 +07:00
parent 0a484e0b45
commit 5be6c54272
39 changed files with 6254 additions and 316 deletions

370
plans/task-d.2.1.md Normal file
View File

@@ -0,0 +1,370 @@
# Task D.2.1: Revenue Attribution & Party Governance
## Goal
Freeze Revenue Attribution Rules และ Project Party Reporting Rules สำหรับ ALLA OS CRM
หลัง Task D.2.1 ผ่านแล้ว:
* Dashboard KPI
* Report Center
* Export
* Sales Analytics
ต้องใช้กติกาชุดเดียวกัน
ห้ามแต่ละ module ตีความ Revenue Owner เอง
---
# Business Problem
ตัวอย่าง:
Billing Customer:
```txt
Italian Thai
```
Project Parties:
```txt
PTT -> End Customer
Italian Thai -> Contractor
AEC -> Consultant
ALLA -> Billing Customer
```
Quotation:
```txt
5,000,000 THB
```
คำถาม:
```txt
Revenue นี้เป็นของใคร?
```
ถ้าไม่ Freeze ตอนนี้
Dashboard และ Report จะให้คำตอบไม่ตรงกัน
---
# Scope D2.1.1 Revenue Attribution Rule
## Revenue Owner
Freeze:
```txt
Revenue Owner = End Customer
```
Rules:
* ใช้ Project Party role = `end_customer`
* ถ้าไม่มี end_customer ให้ใช้ billing_customer fallback
* Dashboard ต้องใช้ Revenue Owner ในการจัดอันดับลูกค้า
Example:
```txt
Quotation = 5,000,000
Billing Customer:
Italian Thai
End Customer:
PTT
```
Revenue Attribution:
```txt
PTT = 5,000,000
```
---
# Scope D2.1.2 Billing Customer Revenue
Freeze:
```txt
Billing Revenue
```
Definition:
```txt
Sum quotation value
where party role = billing_customer
```
Purpose:
```txt
Commercial relationship reporting
```
Example:
```txt
Italian Thai
```
ได้รับ Billing Revenue
---
# Scope D2.1.3 Contractor Revenue
Freeze:
```txt
Contractor Revenue
```
Definition:
```txt
Sum quotation value
where party role = contractor
```
Purpose:
```txt
Contractor relationship reporting
```
---
# Scope D2.1.4 Consultant Revenue
Freeze:
```txt
Consultant Revenue
```
Definition:
```txt
Sum quotation value
where party role = consultant
```
Purpose:
```txt
Consultant relationship reporting
```
---
# Scope D2.1.5 Multiple Party Rule
Example:
```txt
Contractor A
Contractor B
```
Rules:
```txt
Both receive full attribution
```
Meaning:
```txt
Revenue = 5,000,000
Contractor A = 5,000,000
Contractor B = 5,000,000
```
Reason:
```txt
CRM relationship analytics
Not accounting allocation
```
Do NOT prorate.
---
# Scope D2.1.6 Dashboard Reporting Rule
Dashboard must use:
```txt
crm_project_parties
crm_enquiry_customers
crm_quotation_customers
```
Dashboard must NOT use:
```txt
crm_customers only
```
as revenue source.
Project Party role is the reporting authority.
---
# Scope D2.1.7 KPI Extension
Freeze new KPI dimensions:
## End Customer
```txt
Top End Customers
Revenue by End Customer
Won Revenue by End Customer
Lost Revenue by End Customer
```
---
## Contractor
```txt
Top Contractors
Revenue by Contractor
Won Revenue by Contractor
```
---
## Consultant
```txt
Top Consultants
Revenue by Consultant
Won Revenue by Consultant
```
---
## Billing Customer
```txt
Top Billing Customers
Revenue by Billing Customer
```
---
# Scope D2.1.8 Service Layer
Create reporting helpers:
```ts
getRevenueByEndCustomer()
getRevenueByBillingCustomer()
getRevenueByContractor()
getRevenueByConsultant()
```
Rules:
* organization scoped
* respect active filters
* reusable by Dashboard and Reports
No UI required.
---
# Scope D2.1.9 Governance Documentation
Create ADR:
```txt
Revenue Attribution Governance
```
Document:
```txt
Revenue Owner Rule
Project Party Reporting Rule
Fallback Rule
Multiple Party Rule
```
Update:
```txt
docs/implementation/technical-debt.md
```
if any unresolved reporting edge cases are discovered.
---
# Explicit Non-Scope
Do NOT implement:
```txt
Dashboard UI
Charts
Export
Forecasting
Commission calculation
Accounting allocation
Profitability reporting
```
This task is governance only.
---
# Output
1. Revenue Attribution Rules Frozen
2. Revenue Owner Frozen
3. Project Party Reporting Rules Frozen
4. KPI Dimensions Frozen
5. Service Layer Added
6. ADR Added
7. Remaining Risks
8. Task J Readiness
---
# Definition of Done
✔ Revenue Owner frozen
✔ End Customer attribution frozen
✔ Billing attribution frozen
✔ Contractor attribution frozen
✔ Consultant attribution frozen
✔ Multiple party rule frozen
✔ Dashboard reporting rule frozen
✔ KPI dimensions frozen
✔ ADR created
✔ Ready for Task J Dashboard KPI

219
plans/task-d.2.md Normal file
View File

@@ -0,0 +1,219 @@
# Addendum for Task D.2: Customer Role Selection UI + Separate Project Party Field
## Context
ตอนนี้การเลือก Customer ใน Lead / Opportunity / Quotation ใช้งานได้แล้ว แต่ยังไม่ชัดว่า Customer ที่เลือกมีบทบาทอะไรในโครงการ
Business requirement ใหม่:
* Customer หลักในฟอร์มยังใช้เป็น Billing Customer ได้
* แต่ต้องมี Project Party แยกอีกชุดหนึ่ง
* Project Party ใช้บอกว่าในโครงการนี้มีบริษัทไหนทำ role อะไร
* บริษัทเดียวกันอาจมี role ต่างกันในแต่ละโครงการ
---
# UI Requirement 1: Billing Customer Field
ใน Lead / Opportunity / Quotation form ให้คง field หลัก:
```txt
Billing Customer
```
Data source:
```txt
crm_customers
```
Meaning:
```txt
ลูกค้าที่ใช้เป็นผู้ติดต่อ/ผู้ออกเอกสาร/ผู้วางบิลหลัก
```
Do not rename this field to generic Customer.
---
# UI Requirement 2: Project Party Section
เพิ่ม section แยกชื่อ:
```txt
Project Parties
```
หรือภาษาไทย:
```txt
บริษัทที่เกี่ยวข้องในโครงการ
```
ใน form/detail ของ:
```txt
Lead / Opportunity
Quotation
```
Project Party row ต้องมี field:
```txt
Customer
Role
Remark
```
Role ดึงจาก master option:
```txt
crm_project_party_role
```
Role values:
```txt
billing_customer
customer
consultant
contractor
end_customer
other
```
---
# UI Requirement 3: Add Party Flow
ใน Project Parties section ต้องมีปุ่ม:
```txt
Add Party
```
เมื่อกดให้เลือก:
```txt
Customer
Role
Remark optional
```
ถ้า customer ยังไม่มีใน master ให้ยังไม่ต้องสร้าง inline customer ใน Task นี้
ให้ใช้ customer master ที่มีอยู่ก่อน
---
# UI Requirement 4: Display Role Clearly
ทุกที่ที่แสดง Project Party ต้องแสดง role ชัดเจน เช่น:
```txt
PTT Public Company Limited
Role: End Customer
Italian Thai
Role: Contractor
AEC Consultant
Role: Consultant
```
ห้ามแสดงแค่ชื่อบริษัทโดยไม่มี role
---
# UI Requirement 5: Billing Customer Auto Add Option
เมื่อสร้าง Lead / Opportunity / Quotation แล้วเลือก Billing Customer
ให้ระบบสามารถ auto-add ลง Project Parties เป็น role:
```txt
billing_customer
```
ถ้าทำได้โดยไม่ซับซ้อน
Rules:
* ห้าม duplicate customerId + roleCode ใน entity เดียวกัน
* ถ้า billing customer มีอยู่แล้วใน Project Parties ไม่ต้องเพิ่มซ้ำ
* ถ้า user เปลี่ยน Billing Customer ให้ถาม/หรือ update ตาม business-safe behavior
ถ้า logic นี้ใหญ่เกินไป ให้ทำเป็น TODO และไม่ block Task D.2
---
# UI Requirement 6: Detail Page
ใน detail page ให้แสดงแยกชัดเจน:
```txt
Billing Customer
```
และ
```txt
Project Parties
```
ตัวอย่าง:
```txt
Billing Customer:
ABC Engineering Co., Ltd.
Project Parties:
- ABC Engineering Co., Ltd. / Billing Customer
- PTT / End Customer
- Italian Thai / Contractor
- AEC / Consultant
```
---
# UI Requirement 7: Quotation Compatibility
Quotation เดิมมี:
```txt
crm_quotation_customers
```
ให้ refactor/align กับ Project Parties
แต่ UI ต้องแสดงแบบเดียวกัน:
```txt
Billing Customer
Project Parties
```
อย่าใช้คำว่า Owner ถ้า business ไม่ใช้แล้ว
Mapping เก่า:
```txt
owner -> customer
billing -> billing_customer
consultant -> consultant
contractor -> contractor
```
---
# Definition of Done Addendum
Task D.2 จะถือว่าครบเมื่อ:
* Billing Customer เป็น field หลักแยกจาก Project Parties
* Project Parties เป็น section/table แยก
* แต่ละ Project Party มี Customer + Role + Remark
* Role ดึงจาก master option
* Detail page แสดง Billing Customer และ Project Parties แยกกัน
* Quotation compatibility mapping ไม่ทำให้ข้อมูลเก่าหาย
* ไม่มี UI ที่แสดงบริษัทเกี่ยวข้องโดยไม่บอก role

421
plans/task-j.0.md Normal file
View File

@@ -0,0 +1,421 @@
# Task J.0: KPI Definition Freeze
## Goal
Freeze KPI Definitions และ Sales Pipeline Definitions ก่อนเริ่มสร้าง CRM Dashboard KPI
หลัง Task J.0 ผ่านแล้ว ห้ามเปลี่ยนนิยาม KPI โดยไม่มี ADR ใหม่
---
# Sales Pipeline Definition
## Lead
Definition:
```txt
ยังไม่ได้ Assign Sales
```
Rules:
```txt
assignedToUserId = null
pipelineStage = lead
```
KPI Impact:
```txt
Lead Count
Lead Aging
Lead Source
New Leads
```
---
## Opportunity
Definition:
```txt
Assign Sales แล้ว
```
Rules:
```txt
assignedToUserId != null
pipelineStage = opportunity
```
Auto Transition:
```txt
Lead
↓ Assign Sales
Opportunity
```
KPI Impact:
```txt
Opportunity Count
Opportunity Value
Hot Opportunities
Opportunity Aging
```
---
## Closed Won
Definition:
```txt
ลูกค้ายืนยันรับงาน
หรือโครงการได้รับ Award
```
Rules:
```txt
pipelineStage = closed_won
```
Recommendation:
Add future field:
```txt
closedWonAt
```
---
## Closed Lost
Definition:
```txt
แพ้คู่แข่ง
งบประมาณไม่ผ่าน
ลูกค้ายกเลิก
โครงการไม่เดินต่อ
```
Rules:
```txt
pipelineStage = closed_lost
```
Recommendation:
Add future field:
```txt
closedLostAt
lostReason
```
---
# Opportunity Lifecycle
```txt
Lead
↓ Assign Sales
Opportunity
↓ Create Quotation
Opportunity
↓ Approved Quotation
Opportunity
↓ Award
Closed Won
```
---
# Customer Party Definition
## Billing Customer
Definition:
```txt
บริษัทที่ใช้ในการออกใบเสนอราคา
หรือวางบิล
```
Examples:
```txt
ALLA
Italian Thai
SCG
```
---
## End Customer
Definition:
```txt
เจ้าของโครงการตัวจริง
```
Examples:
```txt
PTT
CP
SCG Chemicals
```
---
## Contractor
Definition:
```txt
ผู้รับเหมาก่อสร้าง
```
---
## Consultant
Definition:
```txt
ที่ปรึกษาโครงการ
```
---
## Customer
Definition:
```txt
คู่ค้าหรือผู้เกี่ยวข้องทั่วไป
```
---
# KPI Definitions
## Lead Metrics
```txt
Lead Count
New Leads
Unassigned Leads
Lead Aging
```
Lead Count:
```txt
pipelineStage = lead
```
---
## Opportunity Metrics
```txt
Opportunity Count
Open Opportunities
Hot Opportunities
```
Opportunity Count:
```txt
pipelineStage = opportunity
```
---
## Conversion Metrics
### Lead → Opportunity
Formula:
```txt
Opportunity Created
/
Total Leads
```
---
### Opportunity → Quotation
Formula:
```txt
Opportunity With Quotation
/
Total Opportunities
```
---
### Quotation → Won
Formula:
```txt
Closed Won
/
Total Opportunities With Quotation
```
---
# Revenue Metrics
## Opportunity Value
Source:
```txt
crm_enquiries.estimatedValue
```
Condition:
```txt
pipelineStage = opportunity
```
---
## Quotation Value
Source:
```txt
crm_quotations.totalAmount
```
Condition:
```txt
quotation status != cancelled
```
---
## Won Value
Source:
```txt
crm_quotations.totalAmount
```
Condition:
```txt
pipelineStage = closed_won
```
---
## Lost Value
Source:
```txt
crm_quotations.totalAmount
```
Condition:
```txt
pipelineStage = closed_lost
```
---
# Sales Ownership
Dashboard must use:
```txt
assignedToUserId
```
for:
```txt
Lead
Opportunity
```
and
```txt
salesmanId
```
for:
```txt
Quotation
```
---
# Future Fields (Not In Scope)
Do NOT implement now.
Track as technical debt:
```txt
convertedToOpportunityAt
closedWonAt
closedLostAt
lostReason
defaultPartyRole
```
---
# Output
1. KPI Definitions Frozen
2. Sales Pipeline Frozen
3. Customer Party Definitions Frozen
4. Revenue Rules Frozen
5. Dashboard Ready
---
# Definition of Done
✔ Lead definition frozen
✔ Opportunity definition frozen
✔ Won/Lost definition frozen
✔ Billing Customer definition frozen
✔ Project Party role definition frozen
✔ KPI formulas frozen
✔ Revenue formulas frozen
✔ Ready for Task J

0
plans/task-j.md Normal file
View File