Marketing — Process flows
สถาปัตยกรรม & วงจรชีวิต สร้างอัตโนมัติจากโค้ด (dispatch, CHECK status enums, SET status writes) ส่วน sequence & หมายเหตุ เขียนกำกับเอง
สถาปัตยกรรม (Architecture)
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","secondaryColor":"#3a3d41","secondaryBorderColor":"#dcdcaa","tertiaryColor":"#252526","tertiaryBorderColor":"#569cd6","clusterBkg":"#252526","clusterBorder":"#569cd6","titleColor":"#569cd6","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa"},"flowchart":{"useMaxWidth":false},"state":{"useMaxWidth":false}}}%%
flowchart LR
C["Client"] --> A["Pages Fn marketing<br/>bolt_session auth · company_id scope"]
A --> D["dispatch<br/>roi · create · add · link · convert · multi_touch · record · post"]
D --> K["Kernel<br/>erp_document + journal<br/>atomic GL / stock"]
K --> DB[("D1 tables")]
D --> DB
D --> EV["events<br/>append audit trail"]
EV --> DB
DB -.-> T["marketing_campaign<br/>marketing_campaign_member<br/>marketing_segment<br/>marketing_segment_list_member<br/>marketing_segment_snapshot<br/>marketing_automation_journey<br/>marketing_journey_enrollment<br/>marketing_lead"]
วงจรชีวิต (Lifecycle)
วงจรชีวิตเอกสารระดับ Kernel (erp_document.docstatus)
เอกสารทุกใบที่ขยับเงิน/สต็อกในโมดูลนี้ จะโพสต์ผ่าน kernel ที่ใช้ร่วมกัน โดยเดินตามลำดับ docstatus ที่ตายตัว:
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","secondaryColor":"#3a3d41","secondaryBorderColor":"#dcdcaa","tertiaryColor":"#252526","tertiaryBorderColor":"#569cd6","clusterBkg":"#252526","clusterBorder":"#569cd6","titleColor":"#569cd6","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa"},"flowchart":{"useMaxWidth":false},"state":{"useMaxWidth":false}}}%%
stateDiagram-v2
[*] --> Draft: createDocument (docstatus 0)
Draft --> Submitted: submitDocument (docstatus 1, journal posted)
Submitted --> Cancelled: cancelDocument (docstatus 2, reversed)
Submitted --> [*]
Cancelled --> [*]
marketing_campaign.status
สถานะ: draft (เริ่มต้น) · scheduled · active · paused · completed · archived
marketing_campaign_member.member_status
สถานะ: active (เริ่มต้น) · suppressed · converted · removed
marketing_campaign_cost.status
สถานะ: recorded (เริ่มต้น) · posted
Flow สำคัญ: บันทึกและโพสต์ค่าใช้จ่ายแคมเปญเข้า GL
cost.record จะเขียนเฉพาะแถวต้นทุนสถานะ recorded เท่านั้น ส่วน spend.post (หรือ post_spend: true) จะขับ kernel เพิ่มเติมด้วย: มันจะ resolve บัญชี marketing_expense และบัญชี offset, สร้างเอกสาร MARKETING-SPEND แล้ว submit journal ที่ balanced (Dr marketing_expense / Cr cash) ค่า idempotency_key ช่วยให้การ retry ทำ dedupe ที่ kernel เพื่อให้ต้นทุนของ ROI เท่ากับค่าใช้จ่ายใน GL เสมอ
%%{init: {"theme":"base","themeVariables":{"darkMode":true,"background":"#1e1e1e","primaryColor":"#2d2d30","primaryBorderColor":"#569cd6","primaryTextColor":"#d4d4d4","lineColor":"#6796c6","secondaryColor":"#3a3d41","secondaryBorderColor":"#dcdcaa","secondaryTextColor":"#d4d4d4","tertiaryColor":"#252526","tertiaryBorderColor":"#569cd6","noteBkgColor":"#3a3d41","noteTextColor":"#dcdcaa","clusterBkg":"#252526","clusterBorder":"#569cd6","titleColor":"#569cd6","actorBkg":"#2d2d30","actorBorder":"#569cd6","actorTextColor":"#d4d4d4","signalColor":"#9cdcfe","signalTextColor":"#d4d4d4","noteBkgColor":"#3a3d41","labelBoxBkgColor":"#3a3d41","labelBoxBorderColor":"#dcdcaa"},"flowchart":{"useMaxWidth":false},"sequence":{"useMaxWidth":false},"state":{"useMaxWidth":false},"er":{"useMaxWidth":false}}}%%
sequenceDiagram
participant C as Client
participant F as Pages Fn (recordCost)
participant K as Kernel / GL
participant D as D1 marketing_campaign_cost
participant V as ROI projection view
C->>F: POST spend.post (campaign_id, amount, idempotency_key)
F->>F: mustCampaign + validate amount > 0
F->>K: resolveAccount(marketing_expense) + offset(cash)
F->>K: createDocument(MARKETING-SPEND, origin_ref campaign:spend)
F->>K: submitDocument(intent: Dr expense / Cr offset)
alt idempotent (key seen before)
K-->>F: idempotent = true, prior journal_entry_id
F->>D: SELECT existing cost by kernel_journal_entry_id
D-->>F: prior cost row
F-->>C: return prior row (no duplicate)
else first post
K-->>F: journal entry id
F->>D: INSERT cost (status=posted, kernel ids)
D-->>F: new cost row
F-->>C: return posted cost row
end
C->>F: GET campaign/{id}/roi
F->>V: SELECT roi_projection
V-->>C: revenue / cost, roi_projection
Notes
- จุดประสงค์: จัดการ campaign → audience (segment / member) → touch & attribution → ROI ครบวงจร; feed
qualified lead และ attributed conversion ต่อให้ CRM/Sales (
attributed_doctype= opportunity / sale / sales_order / invoice; lead ใช้handoff_ref). - Money-safety: มีทางเดียวที่แตะ GL คือ
recordCostเมื่อpost_spend— โพสต์ผ่าน kernel (createDocument+submitDocument) เป็น journal balanced Drmarketing_expense/ Cr offset. ถ้าไม่ post แถวเป็นสถานะrecorded(kernel ids = NULL); schema CHECK บังคับว่าpostedต้องมีkernel_document_id. - Idempotency: หลายจุดกัน duplicate จริง —
addMembersดีดูปด้วย (campaign_id, party_id);linkAttributionด้วย (member, doctype, doc_id);scoreLeadด้วยerp_marketing_lead_score_event(lead_id + rule_id); และ spend ดีดูปด้วยidempotency_keyที่ kernel — ถ้าซ้ำจะคืน cost row เดิม ไม่เขียนใหม่ เพื่อให้ ROI cost == GL expense. - ROI เป็น view ล้วน:
erp_marketing_campaign_roi_projection= SUM(attribution_link.attributed_revenue) หารด้วย SUM(campaign_cost.amount); ถ้า cost = 0 จะคืนroi_projection = NULL(กันหารด้วยศูนย์). - Attribution มี 2 กลไก:
linkAttribution(single link, model last/first/manual → ป้อน ROI revenue) และattributeConversion(multi-touch: เลือก touches ตาม model first/last/linear แล้วsplitCreditแตกเครดิต ลงattribution_credit). - Gotchas: (1) ไม่มี endpoint เปลี่ยน
statuscampaign /journey— ตั้งตอน create เท่านั้น (มี CHECK enum draft→archived / draft→retired);enrollJourneyต้อง journeystatus = activeไม่งั้น 409. (2)routeGetมี generic list/get ต่อทุกตาราง marketing ผ่าน whitelist_RRT(scoped ด้วยcompany_id). (3) capability gate ยอมรับerpหรือmanageหรือmarketing.