Spec v0.3 — ฉบับวิศวกรรม + โมเดลบริการ (เคาะรอบดึก 12 มิ.ย.)

ConsultDesk v0.3 — โมเดลบริการ 4 รูปแบบ · schema เต็ม · เครื่องยนต์งานประจำ · วงจรรับ-ทำ-ส่งกลับ · แผน 5 เฟส

ต่อยอดจาก spec v0.1 + mockup Workstation — ฉบับนี้สังเคราะห์จากการออกแบบ 6 มุมที่ไล่โค้ดจริงใน repo homeoffice ทุกจุด (migrations 32 ไฟล์ · operator-worker · workers · web) ของที่อ้างถึงตรวจแล้วว่ามีอยู่จริง จุดที่ของจริงขัดกับ mockup ชี้ไว้หมดแล้ว · ชื่อเคาะแล้ว: ConsultDesk (12 มิ.ย. — ตัวเลือกที่ตกรอบ: HQ/Desk, OpsDesk, Moster Desk, AgencyOS)

ส่วนที่ 1

โจทย์ v0.2 ทวนสั้นๆ — แล้วฉบับนี้เพิ่มอะไร

v0.2 = ConsultDesk ต้องยืนเองได้ไม่ต้องมี OS (ลูกค้า 2 โหมด: CONNECTED ดึงสด / MANUAL เราเปิดงานเอง · งานเข้า 3 ทาง: 🔗 auto · 🔁 ประจำตามสัญญา · ✍️ เปิดเอง · เมนู 8 ตัว) — ดูภาพรวม+จอใน Workstation · ฉบับนี้คือชั้นถัดไป: ทำให้ทุกอย่างข้างบนสร้างได้จริงโดยไม่ต้องเดา

เพิ่ม 1

Schema เต็ม

5 ตารางใน mockup กางออกเป็นของจริง 14 ตาราง + ALTER 1 — พร้อมกฎ "จุดที่ตั้งใจไม่เก็บ" กันแหกกฎเหล็กระดับ DB

เพิ่ม 2

เครื่องยนต์ + ชั้นรวมงาน

แคตตาล็อกงานสำนักงานบัญชีไทย 11 ชนิด · กติกาผลิตงาน idempotent · สูตร merge ปฏิทิน 3 แหล่ง · เงาใบงาน auto แบบ lazy

เพิ่ม 3

แผนเฟสใหม่ + ความเสี่ยง

กลับด้านจาก v0.1: standalone มาก่อน connected · kill points 9 ข้อ · คำถามที่ต้องเคาะก่อนเริ่ม แยก "บล็อกเฟส 1" ชัด

ประโยคยึดเดิมไม่เปลี่ยน: ConsultDesk คือ practice management ของสำนักงานที่ปรึกษา ที่สมบูรณ์ในตัวเอง — การเชื่อม OS คือท่อป้อนงานเสริมที่เสียบเพิ่มได้ทีละลูกค้า และลูกค้า manual คือ funnel ขาย OS ในอนาคต
ส่วนที่ 2

บริการ & โมเดลธุรกิจ — เคาะแล้ว (v0.3)

HomeOffice = ซอฟต์แวร์ราคาเบา ใครก็ใช้เองได้ · บริการสำนักงาน = ชั้นเสริมที่เลือกจ้างได้ทีละเรื่อง — สองอย่างแยกขาดจากกัน ไม่บังคับพ่วง และไม่ล็อกว่าต้องใช้สำนักงานเรา (ลูกค้าเชิญสำนักบัญชีเจ้าไหนก็ได้ผ่านกลไกเชิญสมาชิกปกติ) · ค่า HomeOffice ลูกค้าจ่ายตรงกับแพลตฟอร์ม ไม่รวมในบิลบริการ (บิลสองโลกแยกกันอยู่แล้วใน schema: subscription ฝั่งแพลตฟอร์ม vs สัญญาบริการฝั่ง Desk)

บันไดลูกค้า — ขึ้นลงได้ตลอด ข้อมูลอยู่ที่เดิม

ขั้นลูกค้าระบบรองรับด้วย
1ใช้ HomeOffice เองล้วน ไม่จ้างใครHomeOffice สมบูรณ์ในตัว (เลขา AI + เครื่องคำนวณภาษี/เงินเดือน) — ดีไซน์เดิมคือ "ระบบเตรียมให้ ผู้ใช้ยื่นเอง"
2ใช้ระบบ + จ้างเราบางเรื่อง (แค่ภาษี / แค่เอกสาร HR)สัญญาบริการเลือกขอบเขตเป็นรายการ — จ้างแค่ไหน งานเข้าโต๊ะแค่นั้น
3ใช้ระบบ + จ้างเราเต็ม (เราเป็น สนง.บช.+HR ของเขา)โหมด "เชื่อมระบบ" เต็มรูป
4จ้างก่อน → เราวางระบบส่ง HomeOffice ให้งานวางระบบ (ด้านล่าง) — ส่งมอบเป็นระบบที่รันจริง
5ใช้ระบบ + จ้างสำนักบัญชีอื่นเขาเชิญสำนักบัญชีของเขาเป็น "บัญชีภายนอก" ได้ปกติ — ไม่มีอะไร hard-lock กับสำนักงานเรา

จุดแข็งเชิงโครงสร้าง: เปลี่ยนผู้ให้บริการง่าย (สิทธิ์เชิญ-ถอนได้) แต่ย้ายออกจากระบบยาก — switching cost อยู่ฝั่งที่เป็นประโยชน์กับเรา

รูปแบบคิดเงิน 4 แบบ ใต้ "สัญญาบริการ" ตัวเดียว

รูปแบบตัวอย่างระบบทำยังไง
เหมาโปรเจกต์ (จบเป็นครั้ง)วางระบบ HR ฿35,000สัญญาแบบครั้งเดียว + ชุดงานเรียงขั้น (ด้านล่าง)
เหมารายเดือน (retainer)สนง.บช. ฿8,500/เดือนสัญญารายเดือน → งานประจำเปิดเองทุกงวด (ส่วนที่ 5)
ตามเวลา — รายชั่วโมง/รายวันเข้าปรึกษา ฿1,500/ชม. · ประจำหน้างาน ฿8,000/วันสัญญาระบุเรท+หน่วย · ทุกครั้งที่เข้าปรึกษา = งาน 1 ใบ ปิดงานแล้วใส่จำนวนหน่วย (ช่องเดียว — ไม่มี timer ไม่มี timesheet) · สิ้นเดือนรายงานรวม "7 ชม. × ฿1,500 = ฿10,500" เป็นใบแนบเรียกเก็บ + หลักฐานว่าเข้าทำอะไรวันไหน · ไม่ขายแพ็กชั่วโมงล่วงหน้า (เคาะแล้ว)
ตามชิ้นงานชุดเอกสาร ฿2,500/งานสัญญาผูกชิ้นงาน/เอกสารที่ออก — นับจากของจริง

ลูกค้าหนึ่งรายผสมได้หลายแบบพร้อมกัน (เช่น retainer เงินเดือน + ปรึกษารายชั่วโมงเป็นครั้งๆ = สองสัญญาใต้ลูกค้าเดียว — schema รองรับอยู่แล้ว)

งานวางระบบ HR (Engagement) — "สัญญา = ตัวโปรเจกต์ · งานใต้สัญญา = ขั้นตอน"

หลักคิดเดียวกับคลังแม่แบบเอกสาร: แม่แบบเอกสารมีหัวข้อ optional ติ๊กเลือก → แม่แบบชุดงานก็มีขั้น optional ติ๊กเลือก — เปิดสัญญาจากแม่แบบ "วางระบบ HR" → ติ๊กขั้นที่รายนี้ใช้ → ระบบเปิดงานทั้งชุดเรียงตามสัปดาห์ · progress โปรเจกต์ = นับงานเสร็จ/ทั้งหมดใต้สัญญา (ไม่ต้องมีตารางโปรเจกต์ใหม่) · "แต่ละรายต่างกันเล็กน้อย" = ติ๊กขั้น + แก้งานรายใบหลังเปิดได้อิสระ

ขั้นงานผูกอะไรสัปดาห์
1เก็บข้อมูล: สัมภาษณ์เจ้าของ + ทะเบียนพนักงาน + นโยบายปัจจุบันไฟล์แนบในงาน1
2ออกแบบโครงสร้าง: ผังองค์กร/ตำแหน่ง + โครงสร้างเงินเดือน + เกณฑ์ ลา/OT/สวัสดิการ1-2
3ชุดเอกสาร: ข้อบังคับการทำงาน + สัญญาจ้าง×N + กฎระเบียบคลังแม่แบบ (1 งาน : N เอกสาร — เห็น "ลงนาม 5/8" ในตัว)2-3
4วางระบบเงินเดือน: รอบจ่าย เงินเพิ่ม-หัก ปกส./ภาษี + ทำเดือนแรกคู่ขนานchecklist ในงาน3-4
5ขึ้นทะเบียน/นำส่งราชการ: นายจ้าง สปส. + แจ้งข้อบังคับ (≥10 คน)หน้ากฎหมายแรงงานเตือนเกณฑ์4
6ตั้งระบบจริงบน HomeOffice: ลงทะเบียนพนักงาน เปิดเช็คอิน ตั้งเงินเดือน — เป็นขั้น default ของลูกค้าใหม่ (ตัดได้เฉพาะรายที่ไม่เอาจริงๆ)→ ลูกค้ากลายเป็น "เชื่อมระบบ" ตั้งแต่วันส่งมอบ4-5
7ส่งมอบ + ชี้แจงพนักงาน + เล่ม HR Blueprint (โครงสร้าง+กติกา+เอกสารรวมเล่มเดียว — pattern Wizard+Blueprint)เอกสารส่งมอบ5
จุดจบที่สวยที่สุด — implement คือประตูหน้าของ operate: ปิดโปรเจกต์แล้วระบบชงต่อ 2 ทาง: ① ต่อสัญญาดูแลรายเดือน → งานประจำเดินทันที ② ลูกค้าอยู่บน HomeOffice แล้ว → ได้ทั้งค่าดูแล + ค่า subscription — วางระบบครั้งเดียว ได้ recurring สองทาง · รายได้ต่อลูกค้า = ค่าวางระบบ (ครั้งเดียว) + ค่าดูแล (รายเดือน) + ค่าระบบ (subscription)
กฎกันบาน (K5 ยังศักดิ์สิทธิ์): ไม่มี Gantt · ไม่มี subtask · ไม่มี dependency · ไม่มี timer — ทุกขั้นคืองานธรรมดาผูกสัญญาเดียวกัน progress นับจากงาน แค่นั้น
ส่วนที่ 3

ข้อค้นพบจากโค้ดจริง — 9 ข้อที่เปลี่ยนแผน (ตรวจแล้ว ไม่ใช่เดา)

ก่อนออกแบบ ทีมออกแบบไล่ migrations + worker ทั้งสองตัว + web จริง — เจอของที่ mockup คิดว่ามีแต่ไม่มี และช่องโหว่ที่ต้องอุดก่อนเริ่ม:

#ข้อค้นพบผลต่อแผน
1 🔴requireAdmin ไม่เช็ค role เลยoperator-worker/src/middleware/admin.js ตรวจแค่ JWT ถูกต้อง แล้วเกือบทุก route ของ Console (tenants/billing/impersonate) ใช้ตัวเดียวกันหมดถ้าสร้างบัญชี SERVICE ก่อนแก้ = SERVICE ทะลุ Console ทั้งใบ ผิดกฎเหล็ก "Console/Desk แยกเส้น" ตั้งแต่วันแรก → แยก requireConsole / requireDesk เป็นงานแรกของเฟส 1 + test case "SERVICE ยิงทุกเส้น /admin/* ต้อง 403"
2audit_logs ฝั่ง tenant บังคับ tenant_id NOT NULL และฝั่ง platform ยังไม่มีตาราง audit เลยlog ชั้น Desk (กฎ audit สองชั้น) ใช้ของเดิมไม่ได้ → สร้าง desk_logs ใหม่ (metadata เท่านั้น)
3กลไก deep link มีจริงเกือบครบ: signCustomerSession() มินต์ JWT ฝั่ง tenant ได้ (ใช้กับ impersonate อยู่) + web รับ token ทาง #lt=… อยู่แล้ว — แต่ JWT สองโลกคนละ secret และ web ล้าง hash ก่อนอ่านหน้าปลายทางdeep link = เพิ่ม endpoint แลกเซสชัน + patch web boot ~1-3 บรรทัด (อ่าน param lp ก่อนล้าง hash) — ไม่ใช่ "ใช้ของเดิมเฉยๆ" อย่างที่ v0.1 เขียน แต่ก็เล็กกว่าที่กลัว
4ตารางต้นทางใบงาน auto มีครบ 7 จาก 8 ชนิด (expenses · approvals · monthly_closes · tax_calendar+doc_submissions · payroll_runs · wht_certs · bank_txns) — ขาด expense_claim เพราะ HomeOffice ไม่มีตารางตั้งเบิกฝั่ง tenant เลยเฟส connected เปิดได้ 7 ชนิดทันที · ตั้งเบิก = โมดูลฝั่ง Desk เอง (เฟส 4 รอ flow พี่กี้)
5เกณฑ์ "ยื่นภาษีแล้ว" ของจริงคือ doc_submissions (มี flow เขียนจริง) — ส่วน tax_filings มีตารางแต่ไม่มีโค้ดเขียน (อ่านอย่างเดียว)ใบงาน tax_due ใช้ doc_submissions เป็นเกณฑ์หาย — อย่าอ้าง tax_filings
6cron ใช้ไป 3/5 slot ของ account (workers 2 + operator-worker 1 ตัวคือ 0 2 * * * = 09:00 ไทย)เครื่องยนต์งานประจำเกาะ slot เดิมของ operator-worker — ไม่กิน slot ใหม่
7operator-worker ไม่มี KV binding (มีแต่ R2) และ pattern กันซ้ำทั้ง repo เป็น DB ไม่ใช่ KVกันเตือนซ้ำ/กันผลิตงานซ้ำใช้ unique index + insert ก่อนทำ (แข็งกว่า KV) · ถ้าต้องการ cache ใบงาน ค่อย bind KV เมื่อลูกค้าเกิน ~15 ราย
8tax_calendar มีแค่ 4 ฟอร์ม (ภพ.30, ภงด.1/3/53) ไม่มี e-filing / สปส. / ภงด.1ก / ภงด.50/51 — และ parser เดิมเจอ rule ที่ไม่รู้จักจะ "ข้ามเงียบ"extend แบบ additive ได้ปลอดภัย: เพิ่มคอลัมน์ efiling_due_rule + แถวใหม่ — cron ฝั่ง tenant เดิมไม่พัง
9แฟ้มเอกสาร HR ฝั่ง tenant ไม่มีตารางจริง — mockup เขียน "เก็บเข้าแฟ้ม HR ใน HomeOffice ของเขา" เหมือนมีอยู่แล้ว · และ expenses ไม่มี flag "รอตรวจ" (bill_review = status='recorded' + category is null; ร่างที่ลูกค้ายังไม่ยืนยันอยู่ใน KV — Desk มองไม่เห็น by design)ต้องสร้าง hr_documents ฝั่ง tenant ในเฟสเอกสาร (โคลนโครง vendor_documents) · นิยามใบงาน bill_review ยึดตามของจริง
ส่วนที่ 4

Schema — "5 ตาราง" ใน mockup กางเป็นของจริง 14 ตาราง + ALTER 1

ทุกตารางใหม่ใช้ prefix desk_ (เคาะแล้ว — กันสับสนกับ customers/tasks ฝั่ง tenant ใน Supabase เดียวกัน) · ทุกตัวเป็น platform table: เปิด RLS แบบ deny-all (ไม่มี policy) เข้าได้เฉพาะ operator-worker service role — pattern เดียวกับ platform_users เป๊ะ · migration เดียวจบ 20260612000033_consultdesk_core.sql

โลก tenant (มีอยู่แล้ว — ไม่แตะ)          identities ──< tenant_members >── tenants
                                              ▲ สิทธิ์เห็นข้อมูลจริงมาจากตรงนี้เสมอ │ (เฉพาะ connected)
──────────────────────────────────────────────┼──────────────────────────────────┼──────────
โลก Desk (ใหม่ · RLS deny-all)                │ identity_id (ALTER ใหม่)          │ tenant_id
  platform_users(+SERVICE) ──< desk_assignments >── desk_clients ── 2 โหมด ──────┘
        │                       (primary/backup)        ├──< desk_agreements (สัญญาบริการ)
        │                                               │        └──< desk_agreement_services ──> desk_task_catalog ──> tax_calendar
        │                                               ├──< desk_jobs (งาน 3 ทาง) ──< desk_job_comments · desk_job_files
        │                                               └──< desk_documents ──< desk_document_events
        │                          desk_doc_templates ──< desk_template_versions ──┘
        └──< desk_logs (audit ชั้น Desk) · desk_notifications (กันเตือนซ้ำ)

desk_jobs(origin='auto') = "เงา" pointer 4 ฟิลด์: source + tenant_id + kind + ref_key
  → เนื้อหา (หัวเรื่อง/จำนวน/ยอด/กำหนด) คำนวณสดทุกครั้งจาก expenses · approvals ·
    monthly_closes · tax_calendar+doc_submissions · payroll_runs · wht_certs · bank_txns
ตารางบทบาทจุดออกแบบสำคัญ
desk_clientsทะเบียนลูกค้าสำนักงาน 2 โหมดCHECK บังคับ: connected ต้องมีปลายทาง (tenant_id สำหรับ HomeOffice / external_ref สำหรับ SiteOS เฟส 5) · manual ต้องสะอาด · unique tenant_id (1 tenant = 1 ลูกค้า desk) · ไม่มีฟิลด์ตัวเลขธุรกิจ (งานค้าง/ยอดเงิน = คำนวณสด)
desk_agreementsสัญญาบริการที่ปรึกษาขอบเขต (บัญชี/ภาษี/เงินเดือน/HR/เอกสาร) · fee numeric(15,2) · starts/ends/auto_renew · คนละเรื่องกับ subscriptions (ค่า SaaS) ห้ามปน · ผูกตัวเอกสารสัญญาที่ลงนาม (desk_documents) · v0.3 รูปแบบคิดเงิน 4 แบบ: เหมาโปรเจกต์ / รายเดือน / ตามเวลา (เรท+หน่วย ชม./วัน) / ตามชิ้น — ไม่มีแพ็กชั่วโมง
desk_agreement_servicesงานประจำรายชนิดใน 1 สัญญา1 แถว = งานประจำ 1 ชนิด (ภงด.1 + ภพ.30 + เงินเดือน = 3 แถว) อ้าง catalog + override ได้: due_day (เงินเดือน 25/28) · lead · ใช้ e-filing ไหม · unique (agreement, code)
desk_task_catalogแคตตาล็อกงานมาตรฐานไทย (global seed 11 ชนิด)SoT ของ "วิธีทำงานทีม" (lead/checklist/เตือน) · FK ชี้กลับ tax_calendar = SoT กำหนดกฎหมาย — แก้กำหนดกฎหมายที่เดียว ทั้ง tenant และ Desk ขยับพร้อมกัน
desk_jobsงาน 3 ทาง — หัวใจ migrationCHECK 3 ชุดบังคับตาม origin · เงา auto = pointer ล้วน (title/detail/due/checklist ต้องว่าง — DB ไม่ให้เก็บเนื้อหาแม้โค้ดพลาด) · กันซ้ำ 2 ชั้น: recurring unique (client, kind, period_key) + เงา unique (source, tenant, kind, ref_key) · v0.3 เพิ่ม billable_qty + billable_unit (nullable — ใส่ตอนปิดงานของสัญญาแบบตามเวลา ไม่มี timer)
desk_job_comments · desk_job_filesคอมเมนต์ + ไฟล์แนบงานคอมเมนต์ใช้ได้ทุก origin (บทสนทนาทีมเรา) · ไฟล์แนบห้ามผูกงาน auto — กันคนเซฟสำเนาบิลจาก OS มาฝั่งเรา (เอกสารลูกค้า connected อยู่ใน tenant เขา)
desk_doc_templates · desk_template_versionsคลังแม่แบบ + เวอร์ชันเวอร์ชัน append-only: draft → published (แก้ไม่ได้อีก) → retired · เอกสารที่ออกแล้วผูกเวอร์ชันเดิมตลอดชีพ (หลักเดียวกับ 50 ทวิ) · เนื้อหาเป็น sections + ตัวแปร (ดูส่วนที่ 7)
desk_documents · desk_document_eventsเอกสารที่ออกให้ลูกค้า + ประวัติสถานะ draft→…→filed (ส่วนที่ 7) · snapshot เนื้อหาเต็ม · share_token ลิงก์อ่าน · pointer ไป hr_documents ฝั่ง tenant (connected) · ทุกจุดเปลี่ยนสถานะ = 1 event
desk_assignmentsใครดูแลลูกค้ารายไหน (ระดับ client)จาก v0.1 — role primary/backup · partial unique: primary ได้คนเดียวต่อลูกค้า · assignment = routing งานเท่านั้น ไม่ใช่สิทธิ์เห็นข้อมูล (สิทธิ์มาจาก tenant_members เสมอ)
desk_logsaudit ชั้น Desk (กฎเหล็กข้อ 3)actor + action + client/job/tenant + detail metadata เท่านั้น ห้ามใส่เนื้อหา/ตัวเลขธุรกิจ (กฎเดียวกับ usage_events)
desk_notificationsกันเตือนซ้ำunique (คน, ชนิด, อ้างอิง, วันไทย) — insert ก่อน push ชนแล้วข้าม (atomic, ไม่ใช้ KV)
desk_work_snapshots เฟส 3สถิติใบงาน auto สำหรับรายงานทางออก tension "ใบงาน auto คำนวณสดแล้วหายไป แต่รายงานเดือนหน้าต้องนับได้" — เก็บแค่ key + first/last_seen + resolved_at + count (int) = telemetry การทำงานของทีม ไม่ใช่ข้อมูลธุรกิจ
desk_engagement_templates v0.3 · เฟส 2แม่แบบชุดงาน (งานวางระบบ)รายการขั้นมาตรฐาน (ลำดับ + optional + สัปดาห์สัมพัทธ์ + ผูกแม่แบบเอกสาร) — เปิดสัญญาเหมาโปรเจกต์จากแม่แบบ → generate งานทั้งชุดใต้สัญญา · progress = นับงาน done/ทั้งหมดใต้สัญญา ไม่มีตารางโปรเจกต์แยก
hr_documents เฟสเอกสาร · ฝั่ง tenantแฟ้มเอกสาร HR ของลูกค้า connectedตารางใหม่ฝั่ง tenant (RLS isolation ปกติ ไม่ใช่ deny-all) — ปลายทาง "เก็บเข้าแฟ้ม HR ให้พนักงานเขาเปิดดูเอง" ที่ mockup อ้างแต่ยังไม่มีจริง
ALTER platform_usersเชื่อมสองโลก + บทบาท SERVICEidentity_id (nullable + unique partial + FK identities) · เพิ่ม 'SERVICE' ใน role check · status active/suspended · identity_id เป็น nullable จริงจัง — คนดูแลลูกค้า manual ล้วน (แบบ "คุณนภา" ใน mockup) ไม่ต้องมี ใช้ Desk ได้เต็ม ขาดแค่ deep link

DDL ไฮไลต์ — desk_jobs: กฎเหล็กข้อ 1 บังคับระดับ DB

create table desk_jobs (
  id uuid primary key default gen_random_uuid(),
  client_id uuid not null references desk_clients(id) on delete cascade,
  origin text not null check (origin in ('auto','recurring','manual')),
  -- pointer เงางาน auto (สร้าง lazy เมื่อมีคน assign/comment ครั้งแรก)
  source text, source_tenant_id uuid, kind text, ref_key text,
  -- งานประจำ
  agreement_service_id uuid, period_key text,        -- '2026-06' / '2026-Q2' / '2026'
  -- เนื้องาน (recurring/manual เท่านั้น)
  title text, detail text, due_date date,            -- date ล้วน คิดตามวันไทย
  status text not null default 'open'
    check (status in ('open','in_progress','waiting_client','done','skipped','cancelled')),
  assignee_id uuid, checklist jsonb not null default '[]',
  done_at timestamptz, done_note text, …
  -- ★ กฎข้อ 1 ระดับ DB: เงา auto = pointer ล้วน ห้ามถือเนื้อหา/กำหนด/เช็คลิสต์
  constraint jobs_auto_pointer_only check (origin <> 'auto' or (
    source is not null and source_tenant_id is not null and kind is not null and ref_key is not null
    and title is null and detail is null and due_date is null and checklist = '[]'::jsonb )),
  constraint jobs_nonauto_no_pointer check (origin = 'auto' or (
    title is not null and source is null and ref_key is null ))
);
-- กันซ้ำที่ DB ไม่ใช่ที่โค้ด — cron รันซ้ำกี่รอบ / สัญญาซ้อนกี่ฉบับ ก็ไม่เกิดงานคู่
create unique index uq_jobs_recurring on desk_jobs (client_id, kind, period_key) where origin='recurring';
create unique index uq_jobs_auto      on desk_jobs (source, source_tenant_id, kind, ref_key) where origin='auto';

ทำไม unique recurring ผูก client_id ไม่ใช่ agreement: ต่อสัญญาแบบ "สร้างใหม่+ปิดเก่า" หรือสัญญาซ้อนที่มีงานชนิดเดียวกัน 2 ฉบับ — งวดเดียวกันต้องได้งานใบเดียวเสมอ กันที่ระดับ ลูกค้า × ชนิด × งวด จบทุกกรณี

จุดที่ตั้งใจ "ไม่เก็บ" (กันละเมิดกฎเหล็กเชิงโครงสร้าง)

ห้ามเก็บ

ฝั่ง Desk ไม่มีที่ให้เก็บข้อมูลธุรกิจ

  • เงา auto: CHECK บังคับ title/detail/due ว่าง — ไม่มีคอลัมน์ amount/snapshot ใดๆ
  • ไม่มีตาราง mirror ของ expenses/invoices/payroll แม้แต่ตัวเดียว — จอประกอบจาก query สด
  • desk_clients ไม่มีฟิลด์ตัวเลขธุรกิจ — การ์ด "งานค้าง 4" คำนวณสด
  • ไฟล์แนบห้ามผูกงาน auto (บังคับใน worker)
  • desk_logs.detail = metadata เท่านั้น
เก็บได้ (เขียนชัดกันตีความเกิน)

ทรัพย์สิน/ของรับฝากของสำนักงาน

  • ข้อมูลติดต่อลูกค้า + เลขผู้เสียภาษี (ของที่ลูกค้าให้เราเพื่อให้บริการตามสัญญา)
  • สัญญา/ค่าบริการของเราเอง
  • เอกสารที่เราร่าง (work product ของสำนักงาน) + ค่าตัวแปรที่กรอกตอนสร้าง
  • ไฟล์ที่ลูกค้า manual ส่งมาให้ทำงาน (เราเป็นผู้รับฝากตามสัญญาบริการ)
  • สถิติการทำงานของทีม (key+เวลา+จำนวนนับ — ไม่มีเนื้อหา)

R2 — bucket เดิม homeoffice-docs เพิ่ม prefix ใหม่ desk/ แยกโลกชัด

ของpath
ไฟล์แนบงานdesk/clients/{client_id}/jobs/{job_id}/{uuid}.{ext}
เอกสารออกให้ลูกค้า (ร่าง/ส่ง · ฉบับลงนามสแกนกลับ)desk/clients/{client_id}/documents/{doc_id}/…
ไฟล์แม่แบบต้นทางdesk/templates/{template_id}/v{n}/…
ส่วนที่ 5

เครื่องยนต์งานประจำ — เซ็นสัญญาแล้ว ระบบเปิดงานให้เองทุกงวด

เครื่องยนต์หลักของลูกค้า manual (และคนคุมจังหวะของ connected ด้วย) — หลักคิด: tax_calendar = ความจริงตามกฎหมาย · desk_task_catalog = วิธีทำงานของทีม สองชั้นนี้ไม่ยุบรวมกัน

แคตตาล็อกงานมาตรฐาน 11 ชนิด (seed พร้อมระบบ)

codeงานรอบกำหนด (กระดาษ)e-filingchecklist ตั้งต้น
pnd1ภงด.1 หัก ณ ที่จ่ายเงินเดือนรายเดือนวันที่ 7 เดือนถัดไปวันที่ 15รับข้อมูล→คำนวณ→ยื่น→แนบใบเสร็จ
pnd3 / pnd53ภงด.3 / ภงด.53 หัก ณ ที่จ่ายรายเดือนวันที่ 7วันที่ 15รวบรวม 50ทวิ→ยื่น→แนบใบเสร็จ
vat30ภพ.30 ภาษีมูลค่าเพิ่มรายเดือนวันที่ 15วันที่ 23กระทบยอดซื้อ-ขาย→ยื่น→ชำระ/ขอคืน
ssoเงินสมทบ สปส. (สปส.1-10)รายเดือนวันที่ 15~วันที่ 22 (แก้ได้ในตาราง — ประกาศต่ออายุเป็นช่วง)ยอดค่าจ้าง→นำส่ง→ใบเสร็จ
payrollทำเงินเดือนรายเดือนกำหนดเองต่อสัญญา (25/28)รับข้อมูล→คำนวณ→ส่งสลิป→เก็บหลักฐาน
month_closeปิดเดือนรายเดือนวันที่ 15 (ปรับได้)ตาม checklist ปิดเดือนของระบบ
pnd1a / wht_annualภงด.1ก + ออก 50ทวิ ประจำปีรายปี28 ก.พ. / 15 ก.พ.8 มี.ค. / —กระทบยอด 12 เดือน→ยื่น / ออกครบ→ส่งมอบ
pnd50 / pnd51ภงด.50 ประจำปี / ภงด.51 ครึ่งปีรายปี / ครึ่งปี150 / 60 วันหลังสิ้น(ครึ่ง)รอบบัญชี — อ่านรอบบัญชีจริง ไม่ hardcode มกราคม+8 วันงบเสร็จ→ผู้สอบลงนาม→ยื่น

ไวยากรณ์กำหนด (superset ของ day:N เดิม): day:N · md:MM-DD (วันตายตัวรายปี) · fy_end+N / fy_half+N (อิงรอบบัญชี) · custom (บังคับใส่ due_day ต่อสัญญา) — parser ใหม่อยู่ฝั่ง operator-worker ไม่แตะของเดิมฝั่ง tenant (แถวใหม่ใน tax_calendar ถูก cron เดิมข้ามเงียบ = ปลอดภัย)

กติกาผลิตงาน (สรุปการตัดสินใจสำคัญ)

กุญแจงวด

period_key = งวดข้อมูล ไม่ใช่เดือนยื่น

ภงด.1 ยื่น 7 ก.ค. = งวด 2026-06 — ตรงกับ period ของ payroll_runs / monthly_closes / doc_submissions ฝั่ง tenant ที่ใช้ "งวดข้อมูล" ทั้งหมด → join ตรงไม่ต้องแปลง และเป็นกุญแจกันซ้ำ

อายุสัญญา

เทียบกับงวดข้อมูล ไม่ใช่วันยื่น

สัญญาจบ 30 มิ.ย. → ภงด.1 งวด มิ.ย. (ยื่น 7 ก.ค.) ยังเป็นงานของเรา — ธรรมเนียมจริงของสำนักงานบัญชี เดือนสุดท้ายของสัญญายังต้องยื่นให้ · เงื่อนไขนี้ทำให้ "สัญญาหมดอายุ" ไม่ต้องมี logic พิเศษ — เครื่องผลิตจนงวดสุดท้ายแล้วหยุดเอง

จังหวะ

ผลิตทีละ 1 งวดถัดไป เมื่อเข้าช่วง lead

ไม่ pre-generate ทั้งปี — แก้สัญญากลางงวดง่าย ตารางไม่รกด้วยงานอนาคต · เดือนสั้น: due_day 31 → clamp วันสุดท้ายจริง · ตรงวันหยุด → เลื่อนวันทำการถัดไป (แนวสรรพากร — ทีมอยากเผื่อก่อนวันหยุดให้เพิ่ม lead แทน)

ปิดงาน

ห้ามลบ — ปิดด้วย skipped + เหตุผล

เดือนที่ไม่มียอด (ภงด.3 ไม่มีจ่าย): งานเกิดตามสัญญาเสมอ — สำนักงานต้อง "ยืนยันว่าไม่มี" ไม่ใช่ "ไม่รู้" → ทีมติ๊ก skipped "งวดนี้ไม่มียอด" · ประวัติคือหลักฐานเวลาลูกค้าถาม "เดือนนั้นทำไมไม่ยื่น"

★ ปฏิทินงวดรวม — กฎ merge 3 แหล่ง (design decision สำคัญสุดของระบบ)

โจทย์ชน: ลูกค้า connected ที่มีสัญญา "ยื่นภาษีรายเดือน" — งวดเดียวกันโผล่ทั้งจากข้อมูลจริง (live) และจากสัญญา (recurring job) ใครชนะ?

เคาะ: งานจากสัญญา (job) คือแถวหลัก · live คือตัวเติมสถานะ — เพราะปฏิทินของทีมบริการคือ "ภาระความรับผิดชอบ" ไม่ใช่ "ข้อเท็จจริงของโลก": ความรับผิดชอบเกิดจากสัญญา ไม่ใช่จากการที่ข้อมูลมีอยู่ในระบบ (ลูกค้า connected ที่จ้างแค่ HR consult — ภาษีเขายื่นเอง ไม่ควรเกิดงานภาษีในคิวทีม) · แถวที่มอบหมาย/ติดตามได้ต้องมีตัวตนใน DB ส่วน live ไม่มี id ให้มอบหมาย · ทิศข้อมูลทางเดียว ดีบักง่าย
กรณีผลในปฏิทิน/คิวงาน
มี recurring job + live ตรงงวด (connected)แถวเดียว = job · live เติม badge สด: "เตรียม 80%" / "checklist 3/4" / "ยื่นแล้วในระบบ ✓" + deep link · auto-done: ข้อมูลจริงยืนยันงวดแล้ว (monthly_closes closed / payroll_runs approved / doc_submissions submitted) → cron ปิดงานเอง "อัตโนมัติ: ระบบลูกค้ายืนยันงวดนี้แล้ว"
มี recurring job · ลูกค้า manualแถว job ล้วน — สถานะทีมติ๊กเอง · มาตรฐานการเตือนเดียวกับ connected ทุกอย่าง
live มี แต่ไม่มีสัญญาครอบชนิดนั้นแถวจาง (ghost) badge "นอกสัญญา" — ไม่นับ workload ปิดได้ด้วย filter · default เปิดเพื่อกันพลาด + เป็นโอกาสชวนเซ็นเพิ่ม รอพี่ปุ้ยเคาะ
connected แต่ทีมยังไม่ถูกเชิญเข้า tenantไม่มี live (ไม่มีสิทธิ์ = ไม่เห็นแม้ชื่อใบงาน) — เห็นเฉพาะแถว job + badge "ยังไม่ได้รับเชิญเข้าระบบลูกค้า"

เตือนทีมใน LINE + จังหวะ cron

เตือนใคร เมื่อไหร่

Digest เช้า 1 ข้อความ/คน/วัน + การ์ดเดี่ยวเฉพาะจุดวิกฤต

  • ผู้รับไล่ลำดับ: ผู้รับผิดชอบงาน → ผู้ดูแลหลักของลูกค้า → หัวหน้าทีม
  • Digest 09:00 ไทย: "เกินกำหนด n · ครบวันนี้ n · ครบใน 3 วัน n" — งานเกินกำหนดโผล่ทุกวันจนกว่าจะปิด (ตั้งใจให้รำคาญ — มันคือค่าปรับของลูกค้า)
  • การ์ดเดี่ยว: D-1 และวันแรกที่เกินกำหนด
  • ช่องทาง: OA กลางของ platform (pushPlatformText ที่มีอยู่) · line_user_id ของทีมได้จาก identity ที่ผูก — ไม่แตะ OA HomeOffice ที่ภาระเต็มแล้ว
  • กันซ้ำ: insert desk_notifications ก่อน push — ชน unique = ส่งวันนี้แล้ว ข้าม
cron

เกาะ slot เดิม 09:00 ไทย — ไม่กิน slot ใหม่ (เหลือ 3/5 เท่าเดิม)

  • ลำดับใน slot: ① ผลิตงานงวดใหม่ + sync งานที่กติกาเปลี่ยน → ② reconcile ปิดงาน connected ที่ข้อมูลจริงยืนยัน + ปิดเงาที่ใบงานจริงหาย → ③ digest + การ์ดเตือน
  • ทุกขั้น idempotent — รันซ้ำ/retry ไม่เกิดงานคู่ ไม่เตือนซ้ำ
  • เพิ่มปุ่มสั่งรันมือ (ทดสอบ/เริ่มกลางงวด) ตาม pattern เดิมของ dunning
  • ปริมาณ: ลูกค้าหลักสิบ × ~5 บริการ = หลักร้อยแถว/วัน — จบใน slot เดียวสบาย
ส่วนที่ 6

ชั้นรวมงาน 3 ทาง — เอกลักษณ์ใบงาน · เงา · จอเดียวไม่งง

หัวใจเมนู "งาน" + "หน้าแรก Today" — ใบงาน auto ต้องมี key เสถียร (ไม่แตกใบใหม่ทุกครั้งที่ query) เงาถึงผูกได้ และจอรวมถึงเรียงได้

เอกลักษณ์ใบงาน: (source, tenant, kind, ref_key) — key ห้ามมีส่วนผันแปร

ชนิดref_keyเหตุผล (อ้างตารางจริง)
บิลรอตรวจ · 50ทวิค้างออก · กระทบยอดค้าง · ปิดเดือน · เงินเดือนงวดเดือน yyyy-mmงานสะสม "เคลียร์ของงวดนี้ให้หมด" — ถ้าใช้ id รายใบ บิลเข้าจาก LINE วันละหลายใบ ใบงานจะเกิด/หายถี่ เงาบานเป็นร้อย และไม่ตรงวิธีมอบงานจริง ("ไปเคลียร์บิลเดือนนี้ของเจ้านี้") · เพดานธรรมชาติ 12 ใบ/ปี/ชนิด
รออนุมัติapprovals.id (uuid)เอกสารเดี่ยว ตัดสินรายใบ — status pending→approved/rejected ชัด
ภาษีใกล้ครบ{form}:{yyyy-mm} เช่น ภพ.30:2026-05ต้องมีงวดใน key (ไม่งั้นเดือนถัดไปชนใบเดิม) · ใช้ชื่อฟอร์มไม่ใช่ uuid — อ่านรู้เรื่อง + SiteOS ใช้ตามได้โดยไม่ต้องมีตารางร่วม
เบิกรออนุมัติuuid ใบตั้งเบิกเฟส 4 (ตารางฝั่ง Desk เอง) · เฟส 5 รับจาก SiteOS ผ่าน /work-items

เงา (shadow job) — lazy เท่านั้น

เกิดเมื่อ

การกระทำแรกของมนุษย์

มอบหมาย · คอมเมนต์ · snooze ครั้งแรก → ค่อย insert แถวเงา · ใบงาน auto ที่ไม่มีใครแตะ = ไม่มีแถวเลย (คำนวณสดล้วนเหมือน v0.1) — eager ต้องมี sync engine คอยเปิด/ปิดแถว = ครึ่งทางไปสู่การ sync สำเนา

เก็บได้

pointer + ชั้นงานของทีม

key 4 ฟิลด์ + ผู้รับผิดชอบ + snooze + คอมเมนต์ (ข้อความทีมพิมพ์เอง) — ห้าม: หัวเรื่อง/ยอดเงิน/ชื่อคู่ค้า/กำหนด ทั้งหมดคำนวณสดตอน render

จบยังไง

ปิดเอง auto_resolved

ใบงานจริงหายจากผลคิวรีสด (งานเสร็จในระบบจริง) → เงาปิดเอง status done + closed_reason='auto_resolved' · key กลับมาโผล่อีก (เช่น reopen เดือน) → สร้างเงาใหม่ได้ ประวัติเก่าไม่ลบ

คิวรีรวม + สถานะรวมในจอเดียว

ความเร็ว

batch ต่อชนิดข้าม tenant — ~9 คิวรีไม่ว่ากี่ลูกค้า

  • ห้าม fan-out ต่อ tenant (8-10 คิวรี × 50 ราย = ชนเพดาน subrequest) — ทุก resolver รับ tenantIds[] แล้วคิวรี .in() ครั้งเดียว group ใน JS
  • ที่ 9 ราย ≈ ตอบใน <500ms ไม่ต้องมี cache · เกิน ~15 ราย ค่อย bind KV cache ต่อ tenant TTL ≤2 นาที (ใช้ render จอเท่านั้น ห้ามเป็นแหล่งรายงาน + ปุ่มรีเฟรชข้าม cache)
  • ระวังเพดาน PostgREST ~1,000 แถว — ใส่ .range() loop
  • เรียง: เกินกำหนดก่อน → ครบวันนี้ → ค้างนานสุด (กติกาเดียวกับคิวหลังบ้านเดิม)
สถานะรวม

งาน auto "ติ๊กเสร็จไม่ได้" — สื่อทั้ง UI และ API

  • แถว auto ไม่มี checkbox เลย — ปุ่มเดียว "เปิดใน HomeOffice →" + บรรทัดรอง "เสร็จเองเมื่อข้อมูลจริงเรียบร้อย ไม่ต้องติ๊ก"
  • กันที่ API ด้วย: PATCH เงาที่ส่ง status มา → 409 "งานนี้เสร็จเองเมื่อข้อมูลจริงเรียบร้อย — เปิดเข้าไปทำในระบบลูกค้า"
  • อยากเอาออกจากคิวชั่วคราว → snooze (หายจาก Today ยังอยู่ใน "ทั้งหมด") — ไม่มี force-done
  • มอบหมายแล้ว = badge "มอบแล้ว · พี่กี้" — ไม่เปลี่ยน status (สถานะเป็นของข้อมูลจริงฝ่ายเดียว)

Deep link — กดใบงานแล้วหลุดเข้าหน้างานจริง

POST /desk/enter-tenant { clientId, page }
1. ลูกค้าต้อง connected · ผู้กดต้องผูก identity แล้ว
2. เช็ค tenant_members (identity × tenant, active)        ← กฎเหล็กข้อ 2 — Desk ไม่ใช่ทางลัด
3. gate เดียวกับ tenant switcher เดิมเป๊ะ: web_access (non-OWNER) + tenant ต้อง trial/active
4. มินต์ tenant JWT ด้วย signCustomerSession ที่มีอยู่ — ไม่ใส่ imp claim
   (ที่ปรึกษาเป็นสมาชิกจริง เขียนได้ตาม role ACCOUNTANT/ADMIN ที่ลูกค้าให้ — ไม่ใช่การสวมรอย)
   TTL 8 ชม. + claim via:{kind:'desk'} บอกที่มาของ session
5. audit สองชั้น: desk_logs 'enter_tenant' + audit_logs ฝั่ง tenant 'desk.enter' (actor = สมาชิกตัวจริง)
6. คืน URL: …/#lt={token}&lr={role}<n={tenant}&lp={page} → เปิดแท็บใหม่
   (ฝั่ง web patch ~1 บรรทัด: อ่าน lp ก่อนล้าง hash — กลไก #lt มีอยู่แล้ว)
ชนิดใบงานหน้าใน web ลูกค้า (page key จริง)
บิลรอตรวจ → expensesรายจ่าย & ผู้ขาย (ตั้งหมวด/แก้บิล)
รออนุมัติ → approvals · 50ทวิ → whtCerts · กระทบยอด → reconcileใบให้ตรวจ&อนุมัติ · ใบ 50 ทวิ · กระทบยอดธนาคาร
ปิดเดือน + ภาษี → monthlyClose · เงินเดือน → hrปิดเดือน (checklist+นำส่ง) · พนักงาน·เงินเดือน
สัญญากลาง /work-items (เฟส 5): ใบงาน 1 ใบ = JSON ที่มี id {source}:{tenant}:{kind}:{ref_key} + product + api_base + title/status/due/age/count + can_complete_here + deep_link — ไม่มีฟิลด์เงิน (metadata-only by design จำนวนเงินไปดูในระบบจริง) · SiteOS แค่ทำ GET /work-items ตอบตามนี้ ใส่ไว้ใน backbone ของ os-starter ทีเดียวทุก OS ได้ฟรี
ส่วนที่ 7

วงจรรับ-ทำ-ส่งกลับ — เราเป็นสำนักงานบัญชีของเขาแล้ว ทิศเอกสารกลับด้าน

HomeOffice เดิมออกแบบทิศ "รวบกองเอกสารส่งออกไปให้สำนักบัญชีข้างนอก แล้วจบ" (ตาราง doc_submissions คอมเมนต์ไว้ตรงๆ ว่า "ทะเบียนคุมการนำส่งเอกสารให้สำนักบัญชี") — ขารับจึงครบเกือบหมด แต่ขา "สำนักงานบัญชีตอบกลับเข้ามาในระบบ" ไม่เคยมีใครสร้าง เพราะตอนนั้นเรายังไม่ใช่ สนง.บช. — v0.3 เติมขานี้ให้ครบ

ขารับ — เอกสารเข้ามาทางไหน

ของลูกค้าเชื่อมระบบลูกค้าดูแลเอง
บิลซื้อ-ขายพนักงานเขาถ่ายเข้า LINE ระบบเขา → OCR → ลงบัญชี → ใบงาน "บิลรอตรวจ" ที่โต๊ะเรา ✓ ครบแล้วส่ง LINE ส่วนตัว → แนบเข้างานของงวด → เข้าแฟ้มลูกค้าเอง ✓
สัญญาณ "เอกสารครบ เริ่มทำได้"G2: ปุ่ม "รวบกองส่งสำนักบัญชี" (doc_submissions) ที่ลูกค้ามีอยู่แล้ว — เปลี่ยนความหมายเป็น "ส่งมอบงวดให้ที่ปรึกษา": ลูกค้ากด → โต๊ะเราเกิดงาน "เริ่มทำงวด" + รู้ว่ามีบิลกี่ใบงานประจำมีขั้น "รับข้อมูล" + สถานะ "รอลูกค้าส่ง" → ระบบช่วยทวง (G4)
statement ธนาคารนำเข้าในระบบเขา → ใบงานกระทบยอด ✓ลูกค้าส่งไฟล์ → แนบเข้างาน
เงินเดือน/เวลาทำงานอยู่ในระบบเขาอยู่แล้ว (เช็คอิน/ลา/OT) ✓ลูกค้าส่งไฟล์ → แนบเข้างานทำเงินเดือน
เอกสารงวดอื่น (สัญญาเช่า ฯลฯ)G5: ยังไม่มีที่ลงชัดฝั่ง tenant — "แฟ้มงวด" เป็นงานเฟสหลัง ไม่บล็อกอะไร

ขาส่งกลับ — เราอัพเดตอะไรให้ลูกค้า

สายบัญชี-ภาษี

3 ชิ้นต่องวด

  • G1 · บันทึกการยื่น + ใบเสร็จสรรพากร — ตาราง tax_filings ในระบบเขามีอยู่แล้วแต่ไม่มีโค้ดเขียน → ปุ่ม "บันทึกยื่นแล้ว" ของเราเขียนตารางนี้ + แนบใบเสร็จ → ใบงานภาษีหายเอง + ลูกค้าเปิดดูย้อนหลังได้ตลอด
  • ปิดเดือน → monthly_closes ✓ มีแล้ว ทำผ่าน deep link
  • G3 · รายงานปิดงวดจากที่ปรึกษา 1 หน้า — ตัวเลขจริง + ภาษีที่จ่าย + ข้อสังเกตจากเรา → inbox + LINE ของเขา (เสริม P&L รายเดือนที่อยู่คิว HomeOffice พอดี)
สาย HR-เงินเดือน

เชื่อมระบบ = จบโดยไม่ต้องสร้าง

  • เราทำ payroll run ในระบบเขา → OWNER กดอนุมัติ (กลไก approvals เดิม) → สลิปวิ่งหาพนักงานทาง LINE เอง ✓
  • ใบลา/เวลาทำงานอยู่ระบบเขาอยู่แล้ว ✓
  • ดูแลเอง: คำนวณนอกระบบ (เฟสแรก) → แนบสลิปเข้างาน → ส่ง LINE — และเป็นจุดชวนเข้า HomeOffice ที่แรงสุด
สายเอกสาร

ครบใน v0.2 แล้ว

  • ลงนาม → เก็บแฟ้มเรา + สำเนาเข้าแฟ้ม HR ระบบเขา (hr_documents) พนักงานเปิดดูเองได้ ✓
  • กฎหมายเปลี่ยน → เปิดงานทบทวนให้ลูกค้าเก่า ✓
โบนัสที่ได้ฟรีจากการทำงาน "ลงระบบเขา": เลขา AI ของลูกค้าตอบ "เดือนนี้ภาษีเท่าไหร่ ยื่นหรือยัง" ได้เองจากข้อมูลจริง — เราไม่ต้องนั่งตอบไลน์ลูกค้าทีละข้อความ · นี่คือเหตุผลว่าทำไมต้องอัพเดตลงระบบ ไม่ใช่ส่งไฟล์ใส่ไลน์

สรุปช่องว่างที่เติม (G1-G6) + เข้าเฟสไหน

#ช่องว่างเติมที่ไหนเฟส
G1ปุ่มบันทึกยื่นภาษี + แนบใบเสร็จ (เขียน tax_filings ที่ว่างอยู่)ฝั่ง HomeOffice เพิ่มเล็ก3
G2เปลี่ยนบทบาท doc_submissions → "ส่งมอบงวดให้ที่ปรึกษา" trigger งานที่โต๊ะเราแก้ป้าย/ความหมาย + connector3
G3รายงานปิดงวดจากที่ปรึกษา (ตัวเลข+คำแนะนำ → inbox+LINE ลูกค้า)ใหม่3-4
G4ปุ่มทวงเอกสาร: เชื่อมระบบ = เตือนผ่าน LINE ระบบเขา · ดูแลเอง = copy ข้อความทวงDesk2
G5แฟ้มงวดสำหรับเอกสารที่ไม่ใช่บิลฝั่ง tenantหลัง ไม่บล็อก
G6ปุ่ม "จ้างสำนักงานบัญชี/ที่ปรึกษา" ใน HomeOffice (funnel ทิศ product → service): ลูกค้า self-serve ที่เหนื่อยกับการยื่นเอง/ทำเอกสารเอง กดแล้วเด้งเป็น "ลูกค้าใหม่ + คำขอจ้าง" เข้าโต๊ะเรา → เสนอสัญญา → เชิญทีม (กลไกที่มีครบแล้ว ขาดแค่ปุ่มต้นทาง) — วางแบบ generic ทีมเราเป็น default ไม่ล็อกฝั่ง HomeOffice (จุดวาง: หน้า ภาษี/ปิดเดือน/เอกสาร + first-run)3-4
ส่วนที่ 8

API /desk/* + โมเดลสิทธิ์ SERVICE — ต่อจากของที่มี ไม่รื้อ

ลำดับตรวจของ requireDesk (middleware ใหม่ ข้าง requireAdmin เดิม)

1 · token
platform JWT เดิม
login /admin/login เดิมร่วมกันสองประตู — ไม่ฝัง memberships ใน token
2 · role gate
SERVICE | PLATFORM_ADMIN
role อื่น (EXPERT/SUPPORT/SALES) ไม่เข้า Desk · SERVICE ที่ /admin/* → 403
3 · ตัวจริงใน DB
re-check ทุก request
ถูกถอด/เปลี่ยน role = หลุดทันที ไม่รอ token หมดอายุ 12 ชม. (อุดช่องโหว่เดิม)
4 · memberships
tenant_members ของ identity
ได้ tenantIds[] — ทุกคิวรีข้อมูลสดผูกจาก list นี้เท่านั้น (service role เป็นแค่ transport)
ขอบเขตการเห็น

SERVICE เห็นอะไร

  • ลูกค้า: รายที่ถูก assign (desk_assignments) ∪ ลูกค้า connected ที่ identity ตัวเองมี membership รอเคาะ: หรือทีมเล็กเห็นร่วมกันหมด
  • เนื้อหาใบงานสดของ tenant: ต้องมี membership เท่านั้น — ไม่มี = tenant นั้นไม่โผล่แม้ชื่อ
  • คลังแม่แบบ: เห็นหมด (ทรัพย์สินกลางสำนักงาน)
  • :id นอก scope ตอบ NOT_FOUND ไม่ใช่ FORBIDDEN — กันเดาชื่อลูกค้า
หมวกสองใบ

PLATFORM_ADMIN ใน Desk

  • เข้าได้เสมอ (จัดการทีม/มอบหมาย/รายงาน) ไม่บังคับผูก identity
  • แต่ข้อมูลสด + enter-tenant ต้องมี membership ของ identity ตัวเองเสมอ — ไม่มีข้อยกเว้น (เห็นกว้างจาก Console ไม่ช่วยให้เห็นลึกใน Desk)
  • ทางพิเศษจริงๆ ใช้ impersonate เดิมของ Console ที่มี guardrail ครบ (เหตุผล+30 นาที+แจ้ง owner)
  • ผูก identity ต้องผ่าน flow ยืนยันที่เจ้าตัว login ฝั่ง tenant สำเร็จ — แอดมินจิ้ม email ให้ไม่ได้ (กันผูกผิดคนแล้วได้สิทธิ์คนอื่น)

เส้นทางหลักต่อเมนู (ย่อ — รายละเอียด param อยู่ในรายงานออกแบบ)

เมนูเส้นทางหลักจุดบังคับ
หน้าแรก TodayGET /desk/summary · GET /desk/work-items?scope=…endpoint เดียวกับเมนูงาน + filter ฝั่ง server — ไม่ทำตรรกะแยก
ลูกค้า + สัญญา/desk/clients CRUD + /connect (manual→connected) + /assign · /desk/agreements CRUD + /terminate + /generate-nowPOST สร้าง = idempotency · คนสร้างเป็น primary อัตโนมัติ · generate-now กันซ้ำด้วย unique index
งาน/desk/jobs CRUD + /assign + /comments + /files · POST /desk/work-items/adopt (สร้างเงา)แนบไฟล์เฉพาะงานไม่ใช่ auto · PATCH เงา+status → 409
เอกสาร & แม่แบบ/desk/templates + versions/publish · /desk/documents + เดินสถานะ/send/signed-file/file/voidpublish เวอร์ชัน = PLATFORM_ADMIN · ผู้รีวิว ≠ ผู้สร้าง · เดินสถานะหน้าเดียว ย้อนได้เฉพาะแอดมิน
ปฏิทินงวดGET /desk/calendar?month=merge 3 แหล่งตามกฎส่วนที่ 4 — อ่านอย่างเดียว ไม่มีตารางปฏิทินแยก
ทีมGET /desk/team + workload · POST /desk/team/invite-to-tenantคำขอเชิญ = ขยาย member_invites เดิม (status 'requested') → OWNER ลูกค้ากดยืนยันในเว็บเขา → ระบบ insert tenant_members — สิทธิ์มาจากลูกค้าเสมอ Desk แค่ยื่นคำขอ
รายงานGET /desk/reports/overview · /clients/:id · /export?format=csvเริ่มจากตารางดิบ export ได้ — dashboard KPI ไว้เมื่อมีข้อมูลสะสม ≥3 เดือน
Deep linkPOST /desk/enter-tenantrate limit 30 ครั้ง/10 นาที/คน · audit สองชั้นทุกครั้ง

ฝั่งหน้าจอ — แยกประตูโดยไม่แตะโครง Console

โครงไฟล์

operator/ เพิ่ม desk.html + desk.js

SPA แยกไฟล์ ใช้ styles.css + token เดิมร่วมกัน (login ครั้งเดียวเข้าได้สองประตู) — แตะ app.js ของ Console แค่ 2 จุด: login แล้ว role SERVICE เด้งไป /desk + ปุ่มสลับประตูสำหรับ PLATFORM_ADMIN · _redirects เพิ่ม /desk → desk.html

มาตรฐานเดิม

convention ที่ใช้ตาม repo

response {ok,data}/{ok,error,code} · เพิ่ม RATE_LIMITED + TENANT_SUSPENDED ใน response.js ฝั่ง operator (เดิมไม่มี) · idempotency POST สำคัญ (ต้อง bind KV ก่อน) · rate limit /admin/login (เดิมไม่มี!) · เงิน numeric(15,2) · เวลา UTC แสดงไทย · UI copy ไทยง่ายไม่ทับศัพท์

ส่วนที่ 9

เอกสาร & แม่แบบ — หัวใจงาน consult

เคาะรูปแบบ: HTML แตกเป็น section + ตัวแปร {{placeholder}} → render เป็นหน้าพิมพ์ (พิมพ์/Save as PDF) — ตรง pattern ที่ระบบใช้จริงอยู่แล้ว (printDoc + Sarabun) dependency ใหม่เป็นศูนย์ · ไม่เอา DOCX (หลุด version control + render ไทยเพี้ยน) ไม่เอา PDF lib (จัดหน้าไทยยาวหลายหน้าด้วยมือ = งานมหาศาล)

โครงแม่แบบ

sections + ตัวแปร 3 ชั้น

  • เนื้อหา = section เรียงลำดับ (หมวด 1 บททั่วไป, หมวด 2 เวลาทำงาน, …) — แก้รายย่อหน้าต่อลูกค้าได้ ไม่ใช่ก้อนเดียว
  • optional = ติ๊กเลือกตอนสร้าง (กฎระเบียบ 8 หัวข้อเลือกเฉพาะที่ใช้) · locked = ถ้อยคำตามกฎหมายบังคับ แก้รายลูกค้าไม่ได้ (แก้ที่แม่แบบ→ออกเวอร์ชันใหม่เท่านั้น)
  • ตัวแปร: {{client.*}} เติมอัตโนมัติจากทะเบียนลูกค้า · {{doc.*}} ระบบใส่ให้ (เลขที่/วันที่) · {{var.*}} {{emp.*}} กรอกตอนสร้าง — ฟอร์มกรอก generate จากนิยามตัวแปร ไม่ hardcode
  • สัญญาจ้าง 3 แบบย่อย (รายเดือน/รายวัน/ทดลองงาน) = แม่แบบ 3 แถวใต้กลุ่มเดียว — ไม่ทำ conditional ในเนื้อหา
  • หมวดที่มีขั้นตอนกฎหมายกำกับ (หนังสือเตือน/เลิกจ้าง) มี guidance เตือนผู้ใช้ก่อนสร้าง — เป็นข้อความ ไม่ใช่ logic
flow

สร้าง → รีวิว → ส่ง → ลงนาม → เก็บแฟ้ม

  • สร้างร่าง = snapshot เนื้อหาเต็ม + ผูกเวอร์ชันแม่แบบ (provenance — เทียบ diff กับแม่แบบรายหัวข้อได้ · แม่แบบ retired แล้วเอกสารยังสมบูรณ์ในตัว)
  • รีวิวภายใน: ผู้รีวิว ≠ ผู้สร้าง (กติกาใน endpoint ไม่ใช่ DB — ทีมเล็กไม่ตัน) · default = ผู้ดูแลหลักของลูกค้า
  • ส่ง = ลิงก์อ่านแบบ share token (หมดอายุ 30 วัน) — ทีม copy ส่งทาง LINE ที่คุยกับลูกค้าอยู่แล้ว ไม่ผูก OA (ภาระเต็ม + ลูกค้า manual ไม่ได้แอด) · ลูกค้าเปิดลิงก์ครั้งแรก = stamp "เปิดดูแล้ว" ฟรี
  • ลงนามเฟสแรก = พิมพ์เซ็นกระดาษ → สแกนส่งกลับ → ทีมอัปโหลด (ไม่มี public write — ลด attack surface) · จุดเสียบ e-signature อนาคตออกแบบเผื่อแล้ว ไม่เขียนตอนนี้
  • เลขที่เอกสารล็อกตอนส่ง (idempotent แบบเดียวกับเลข 50 ทวิ)

สถานะเต็ม + ใบงานติดตามอัตโนมัติ

draft ──▶ in_review ──▶ approved ──▶ sent ──▶ viewed ──▶ signed ──▶ filed
  ▲           │                      (ลูกค้าเปิดลิงก์)   (อัปสแกน)   (เก็บแฟ้ม + สำเนา tenant)
  └── ตีกลับ ──┘        ทางแยก: voided (ก่อน signed) · superseded (ถูกฉบับใหม่แทน)
ใบงานสดชนิดใหม่เกิดเมื่อหายเมื่อ
doc_unsigned ส่งแล้วไม่ลงนามส่งแล้วเกิน X วัน (ตั้งต่อแม่แบบ default 7)ลงนาม/ยกเลิก — ไม่ insert งานจริง โผล่ใน work queue แบบเดียวกับ 8 ชนิดเดิม
doc_review ถึงรอบทบทวนแม่แบบออกรุ่นใหม่ (กฎหมายเปลี่ยน) หรือครบรอบทบทวนรายปี (ข้อบังคับ = 12 เดือน)กด "ออกฉบับใหม่" (ฉบับเก่า superseded) หรือ "ยังใช้ฉบับเดิม" (stamp รับทราบถึงรุ่นนี้ — มีร่องรอยใครตัดสินเมื่อไหร่)

สองโหมด — เก็บแฟ้ม

MANUAL

เก็บฝั่งเราอย่างเดียว

R2 desk/clients/{id}/docs/… + แถว metadata — จบ · งานผูกเอกสาร 1 งาน : N ใบ (สัญญาจ้าง ×8 คน = งานเดียว เอกสาร 8 ใบ เห็น progress "ลงนามแล้ว 5/8") · งานปิดเองเมื่อเอกสารทุกใบถึง filed/voided

CONNECTED

+ ส่งสำเนาเข้าแฟ้ม HR ของ tenant (ลำดับบังคับ 5 ขั้น)

① เช็ค membership ผู้กดก่อนเสมอ (ไม่เป็นสมาชิก = ปุ่ม disabled — service role ไม่ใช่ทางลัด) ② copy ไฟล์จริงเข้า tenants/{id}/hr-docs/… ③ insert hr_documents (ตารางใหม่ฝั่ง tenant — พนักงานเขาเปิดดูเองได้ผ่าน /hr/documents) ④ audit สองชั้น: ฝั่ง tenant "บัญชีภายนอกเก็บเอกสารเข้าแฟ้ม" + event ฝั่ง Desk ⑤ stamp pointer + filed · แยก signed/filed เพราะขั้น copy อาจติดสิทธิ์ — ให้มองเห็นค้าง

การตัดเฟสของเมนูนี้ (รอเคาะ Q-เอกสาร): ดีไซน์เต็มข้างบนคือเป้าหมาย — แต่เฟส 2 อาจเริ่มจากชั้นเล็กก่อน: อัปโหลดไฟล์ master + ติดตามสถานะจนลงนาม (ไม่มี section editor) ถ้าพี่ปุ้ยใช้แล้วติดค่อยเปิด section+ตัวแปร — generator คือหลุม scope ที่ใหญ่ที่สุดของเมนูนี้
ส่วนที่ 10

แผนสร้างใหม่ 5 เฟส — กลับด้านจาก v0.1: standalone มาก่อน

เหตุผลที่กลับด้าน: พี่ปุ้ยมีลูกค้า manual จริงอยู่แล้ววันนี้ ส่วนลูกค้า connected ที่ทีมดูแลจริงยังต้องเคาะรายชื่อ · เฟส connected มีงานแฝงที่ v0.1 ประเมินต่ำ (แลกเซสชันข้าม JWT สองโลก, ผูก identity, OWNER ต้องเปิด web_access) · และโหมด manual ต้องการตาราง clients+jobs+agreements ตั้งแต่วันแรกอยู่ดี — งาน auto ค่อยมาเกาะทีหลัง

เฟส 1
Standalone Core
โต๊ะงานหมุนเองได้ไม่ต้องมี OS — migration ชุดเดียว + แยก middleware + API clients/agreements/jobs + เครื่องยนต์งานประจำ + UI 4 เมนูแรก
เฟส 2
เอกสาร & แม่แบบ
หัวใจ consult ของพี่ปุ้ย — มาเร็วเพราะไม่มี dependency ภายนอก (เริ่มจากชั้นเล็ก: ไฟล์ master + สถานะลงนาม)
เฟส 3
Connected
ใบงานคำนวณสด 7 ชนิด + เงา lazy + deep link + ผูก identity (= เฟส 1 เดิมของ v0.1 ย้ายมา)
เฟส 4
ตั้งเบิก + ทีม/รายงาน
ห้ามเริ่มก่อนนัดฟัง flow ตั้งเบิกจริงจากพี่กี้ — "จับคู่ statement" ใช้ได้เฉพาะ connected ต้องนิยาม scope ใหม่
เฟส 5
Connector OS อื่น
สัญญา /work-items ลง backbone os-starter — ปลดล็อกเมื่อมีลูกค้า SiteOS จริงที่จ้างทีมดูแล ก่อนนั้นเขียนแค่เอกสาร

เฟส 1 ละเอียด — ชิ้นงาน + ขนาด

ชิ้นงานเนื้อหาขนาด
Migration ชุดเดียวdesk_clients · desk_agreements (+services+catalog) · desk_jobs (+comments/files) · desk_assignments · desk_logs · desk_notifications · ALTER platform_users — additive ล้วน role เดิม 4 ค่าผ่านหมดM
🔴 แยก middleware สิทธิ์requireConsole ครอบ /admin/* เดิมทั้งหมด + requireDesk สำหรับ /desk/* — ต้องเสร็จก่อน insert บัญชี SERVICE คนแรกM
API กลุ่ม deskclients/agreements/jobs CRUD + มอบหมาย + แนบไฟล์ + คอมเมนต์ — ทุก endpoint เขียน desk_logsM
เครื่องยนต์งานประจำเกาะ scheduled() เดิม 09:00 ไทย — ผลิต/sync/เตือน idempotent ทุกขั้นM
UI 4 เมนูแรกToday · ลูกค้า · งาน · ปฏิทินงวด (= view จาก jobs.due_date ไม่มีตารางแยก) — desk.html/desk.js แยกไฟล์L
นิยาม "เฟส 1 เสร็จ" (วัดได้): พี่กี้ login ด้วยบัญชี SERVICE → เพิ่มลูกค้า manual จริง 1 ราย + สัญญา "ยื่น ภงด.1 ทุกเดือน" → เช้าวันถัดมาระบบเปิดงานงวด มิ.ย. เอง due ถูกต้องตามวันไทย → มอบงานลูกทีม + แนบไฟล์ที่ลูกค้าส่งทาง LINE → ติ๊กเสร็จ → ทุกขั้นมีรอยใน desk_logs · และบัญชี SERVICE เดียวกันยิง /admin/tenants, /admin/billing, impersonate ได้ 403 ทุกเส้น
อัปเดต v0.3 — ของที่เคาะเพิ่ม ลงเฟสตามนี้: เฟส 1: สถานะ "รอลูกค้าส่ง" (อยู่ในแกนแล้ว) · เฟส 2: + ปุ่มทวงเอกสาร (G4) + แม่แบบชุดงานวางระบบ + ช่องหน่วยเวลา (ชม./วัน) ตอนปิดงาน · เฟส 3: + บันทึกยื่นภาษี+ใบเสร็จ (G1) + สัญญาณส่งมอบงวด (G2) · เฟส 3-4: + รายงานปิดงวดจากที่ปรึกษา (G3) + ปุ่ม "จ้างสำนักงานบัญชี" ใน HomeOffice (G6) · หลัง: แฟ้มงวด (G5) — และทิศใหม่: ลูกค้าใหม่ทุกรายตั้งต้นเป็น "เชื่อมระบบ" ผ่านงานวางระบบ → เฟส 3 คือโหมดหลัก ไม่ใช่เผื่ออนาคต (ลำดับเฟสไม่เปลี่ยน เพราะเฟส 1-2 ใช้กับทุกโหมดและลูกค้า manual เดิมยังอยู่)
ส่วนที่ 11

ความเสี่ยง / Kill Points — 9 ข้อ เรียงตามอันตราย

K1 · ทีม 3 คนไม่ใช้ เพราะกลายเป็น double entry
งานคุยกันใน LINE อยู่แล้ว — ถ้าต้องมากรอกซ้ำ ระบบตายเงียบ
ทางกัน: เฟส 1 ต้อง"ลด"งานชัดๆ อย่างน้อย 1 อย่าง (งานประจำเปิดเอง ไม่ต้องจำ) · เปิดงาน ad-hoc จบใน <30 วิจากมือถือ · kill switch: วัด 2 สัปดาห์หลังส่งมอบ ถ้างาน active ต่อคนต่ำกว่าที่ตกลง (<5/สัปดาห์) หยุดสร้างเฟสถัดไป กลับมาแก้ workflow ก่อน
K2 · SERVICE รั่วเข้า Console (ช่องโหว่มีอยู่แล้ววันนี้)
requireAdmin ไม่เช็ค role — ใครมี platform JWT เรียกได้เกือบทุก endpoint รวม impersonate
ทางกัน: แยก middleware เป็นงานแรกของเฟส 1 + test "SERVICE เรียกทุก route /admin/* ต้อง 403" เข้า definition of done
K3 · เงา jobs drift จากใบงานจริง
ถ้าเงาเก็บสถานะเอง วันหนึ่งใบงานจริงเสร็จแล้วแต่เงายังค้าง — ทีมเลิกเชื่อจอ
ทางกัน: เงา lazy + ไม่มีคอลัมน์เนื้อหาให้เก็บ (CHECK ระดับ DB) · สถานะคำนวณสดตอน render · cron กลางคืน reconcile ปิดเงาที่ underlying หาย
K4 · jobs กลายเป็นคลังสำเนาข้อมูลธุรกิจโดยไม่ตั้งใจ
คนจะ paste ยอดเงิน/ชื่อพนักงานลง title/comment เอง แล้วกฎเหล็กพังโดยไม่มีใครตั้งใจ
ทางกัน: schema ไม่มีคอลัมน์เงิน · title งาน auto สร้างสดไม่ editable · ไฟล์แนบเฉพาะลูกค้า manual · ใส่ checklist code review ของ repo
K5 · Scope creep กลายเป็น project management เต็มรูป
subtask, kanban, custom field, time tracking → ConsultDesk กลายเป็น ClickUp ห่วยๆ
ทางกัน: jobs ฟิลด์ตายตัว ไม่มี subtask/custom field (checklist ได้เฉพาะที่มากับงานประจำ) · ฟีเจอร์ใหม่ทุกตัวต้องตอบ "ลดงานทีม 3 คนตรงไหน" ก่อนเข้า backlog
K6 · งานประจำผลิตผิดวัน (timezone / วันหยุด / เดือนสั้น)
DB เก็บ UTC แต่ "ยื่นทุกวันที่ 7" คือวันที่ 7 เวลาไทย · เดือนไม่มี 31 · due ตรงเสาร์-อาทิตย์
ทางกัน: due เป็น date ตีความวันไทยเสมอ · clamp สิ้นเดือน · วันหยุดเลื่อนวันทำการถัดไป (แนวสรรพากร) · idempotent unique — test บังคับ: ก.พ. + วันที่ 31 + วันหยุดยาว
K7 · ผูก identity ผิดคน / คนไม่มี identity
ทีมที่ดูแล manual ล้วนอาจไม่มี identity เลย และการผูกด้วย email โดยแอดมิน = เสี่ยงได้สิทธิ์ tenant ของคนอื่น
ทางกัน: identity_id nullable — ไม่มีก็ใช้ Desk ได้เต็ม (ขาดแค่ deep link) · การผูกต้องผ่าน flow ที่เจ้าตัว login ฝั่ง tenant สำเร็จ + log
K8 · ลูกค้า manual โตจนขอ portal ฝั่งลูกค้า
"ขอดูสถานะงานหน่อย" ฟังดูเล็ก แต่คือการเปิด auth โลกที่สามทั้งใบ
ทางกัน: ประกาศเป็น non-goal · ทางระบาย: สรุป PDF/LINE รายเดือน (เฟส 4) · อยากเห็นสด = จังหวะขาย HomeOffice พอดี — ตรง funnel ที่วางไว้
K9 · ความรับผิด/PII ของไฟล์ลูกค้า manual
เราเก็บไฟล์เงินเดือน/เลขบัตรพนักงานลูกค้าฝั่งเรา = เราเป็นผู้ประมวลผลข้อมูล ถ้าหลุดลูกค้าชี้มาที่เรา
ทางกัน: ข้อสัญญารับฝากข้อมูลใน desk_agreements (แม่แบบสัญญาบริการเฟส 2 ต้องมีข้อนี้) · R2 แยก path ต่อ client + เปิดไฟล์ทุกครั้งลง desk_logs · ห้ามเก็บเลขบัตรดิบเป็นคอลัมน์
ส่วนที่ 12

คำถามเปิด — แยก "บล็อกเฟส 1" ออกจาก "เคาะทีหลังได้"

🔴 บล็อกเฟส 1 — ยังไม่ได้คำตอบ = ยังไม่เขียน migration

#คำถามคนเคาะกระทบ
Q1รายชื่อลูกค้า manual จริงวันนี้ + บริการที่ทำให้แต่ละราย (ยื่นภาษี? เงินเดือน? เอกสาร?)พี่ปุ้ยชุดงานประจำรุ่นแรก + seed ข้อมูลจริง
Q2ทีมที่ได้บัญชี SERVICE มีใครบ้าง — ทุกคนสิทธิ์เท่ากันหมดได้ไหม (เสนอ: เท่ากันก่อน ไม่ทำ sub-role) และเห็นลูกค้าร่วมกันทั้งทีม หรือเฉพาะรายที่ถูก assignพี่กี้+พี่ปุ้ยโมเดลสิทธิ์ + หน้าเมนูลูกค้า
Q3งานประจำต้องมี checklist ขั้นย่อย ("4 ขั้น" ใน mockup) หรือใบเดียวติ๊กเสร็จพอ — ถ้าไม่จำเป็นจริงตัดทิ้ง (กัน K5)พี่กี้schema jobs + ความซับซ้อน UI
Q4กำหนดยื่นภาษีใช้เกณฑ์กระดาษ (7/15) หรือ e-filing (+8 วัน) ต่อลูกค้า · ค่า สปส. e-Payment ที่ทีมใช้จริงตอนนี้พี่กี้สูตรคำนวณ due ของเครื่องยนต์
Q5แจ้งเตือนทีมเฟส 1: in-app พอ หรือต้อง LINE push เลย — ถ้า push ใช้ OA กลาง platform ตามดีไซน์ใช่ไหมพี่ปุ้ยscope เฟส 1

เคาะทีหลังได้ (ไม่ขวางเฟส 1)

เคาะก่อนคำถาม
เฟส 2เมนูเอกสารเริ่มระดับไหน — ไฟล์ master + สถานะลงนาม (เสนอ) หรือ section+ตัวแปรเต็มรูปเลย · ตำแหน่งหน้าอ่านลิงก์สาธารณะ: operator-worker มีคอมเมนต์ "ห้ามรับ traffic ลูกค้า" → ยกเว้นจำกัด read-only หรือ worker เล็กตัวที่สาม (เสนอ: worker แยก สะอาดสุด)
เฟส 3ลูกค้า connected รายแรกที่ทีมดูแลจริงคือใคร + OWNER ยอมเชิญทีมเป็น ACCOUNTANT + เปิด web_access ไหม (ไม่มีรายจริง = เฟส 3 ยังไม่เริ่ม) · งวด live "นอกสัญญา" โชว์เป็นแถวจางหรือซ่อน (เสนอ: โชว์) · cron auto-done อ่านสถานะ tenant ด้วย service role แบบ existence-check 3 ตาราง — ตีความผ่านกฎเหล็กข้อ 2 ไหม หรือให้ปิดเฉพาะตอนคนที่มี membership เปิดจอ · token เข้าเว็บลูกค้า TTL 8 ชม.+localStorage หรือ in-memory ปิดแท็บแล้วหลุด
เฟส 2v0.3 ขั้นมาตรฐานจริงของงานวางระบบ HR (ร่าง 7 ขั้นในส่วนที่ 2 ถูกกี่ % — ใช้ทำ seed แม่แบบชุดงาน) · งวดเก็บเงินโปรเจกต์ (เช่น มัดจำ 50 / ส่งมอบ 50) ให้ระบบช่วยตามทวงไหม หรือคุมเองนอกระบบ
เฟส 4นัดฟัง flow ตั้งเบิกจริงทั้งวงจรจากพี่กี้ (ค้างจาก v0.1) รวม "จับคู่ statement กับลูกค้า manual ที่เราไม่มี statement นิยามยังไง" · SLA ลูกค้า manual นับจากทีมติ๊ก — ยอมรับความแม่นระดับนี้ หรือบังคับแนบหลักฐานก่อนติ๊กเสร็จ เคาะแล้ว: ฐานคิดค่าบริการสัญญาแบบเวลา = หน่วยที่ใส่ตอนปิดงาน ไม่ใช่ time tracking
นโยบายEXPERT/SUPPORT/SALES เห็น Desk ไหม (เสนอ: ไม่เห็น) · prefill รายชื่อพนักงาน tenant ตอน batch สัญญาจ้าง = อ่าน PII มาฝั่ง Desk (เฟสแรกพิมพ์มือ — เคาะนโยบายทีหลัง)

สิ่งที่จงใจไม่ทำ (Non-goals v0.2) — กันเขตก่อน scope บาน

ฝั่งลูกค้า

  • Portal ลูกค้า manual — ไม่มี login เด็ดขาด (อยากเห็นสด = ขาย HomeOffice)
  • e-signature เต็มรูป — ลงนามเกิดนอกระบบ มาอัปเดตสถานะ
  • เขียนเอกสารเข้าแฟ้ม tenant อัตโนมัติแบบไม่มีคนกด — ทุกครั้งต้องผ่าน membership + ปุ่ม

ฝั่งเครื่องมือ

  • Timer/timesheet จับเวลา — สัญญาแบบรายชั่วโมง/รายวันใช้ "ใส่จำนวนหน่วยตอนปิดงาน" พอ
  • แพ็กชั่วโมงล่วงหน้า/กระเป๋าเวลาคงเหลือ — เคาะแล้วไม่ขาย (v0.3)
  • แชต/คอมเมนต์ realtime — คุยใน LINE เหมือนเดิม
  • ⌘K global search — ลูกค้า 9 ราย filter chips เกินพอ ตัดออกจากปีนี้
  • มือถือ native / LIFF ฝั่ง Desk — responsive web พอ

ฝั่งข้อมูล

  • Sync สำเนาข้อมูลธุรกิจลูกค้า connected — ไม่มีข้อยกเว้นแม้ "เพื่อ performance"
  • Connector /work-items — เฟส 5 รอลูกค้า SiteOS จริง
  • AI ใน Desk (สรุปงาน/ร่างเอกสาร) — รอ core มีคนใช้จริงก่อน