Flow: สมัคร & พิจารณาร้านค้าพาร์ทเนอร์ (Shop Partner Onboarding)

กระบวนการตั้งแต่ร้านกดสมัคร จนพนักงานบ้านอนุมัติและผูก LINE กลุ่ม อ้างอิงตาราง: ดู 03-shop.md · ข้อตกลง: org.terms_and_conditions วันที่: 16 มิ.ย. 2026


Actors

Actorบทบาท
เจ้าของร้านผู้สมัคร — กรอกฟอร์ม, รับข้อตกลง, ยืนยัน OTP, ส่งรหัสเชื่อมในกลุ่ม LINE
พนักงานบ้าน (ADMIN)พิจารณาคำขอ (อนุมัติ/ตีกลับ/ไม่อนุมัติ), สร้างรหัสเชื่อม LINE
ระบบ (Backend)เช็คเบอร์ซ้ำ, จัดการ OTP (Redis), gen เอกสารสัญญา, บันทึก DB
LINE Webhookดักข้อความรหัสในกลุ่ม → จับ groupId → ผูกกับร้าน

Flow หลัก

flowchart TD
  A([เจ้าของร้าน: หน้า Login]) --> B[กด สมัครเลย]
  B --> C[รับข้อตกลงล่าสุด<br/>org.terms_and_conditions type=TERMS_OF_SERVICE]
  C --> D[กรอกเบอร์โทร]
  D --> E{เบอร์ซ้ำในบ้านนี้?}
  E -- ซ้ำ --> D
  E -- ไม่ซ้ำ --> F[ส่ง OTP - Redis ephemeral]
  F --> G{ยืนยัน OTP}
  G -- ผิด --> F
  G -- ผ่าน --> H[กรอกฟอร์ม 7 ขั้น<br/>ผู้สมัคร→ร้าน→ที่อยู่→บัญชี→account→ลายเซ็น→รีวิว]
  H --> I[[Submit ใบสมัคร]]
  I --> J[ระบบสร้าง:<br/>shops status=PENDING<br/>shop_users OWNER<br/>shop_consents<br/>gen contract_file]
  J --> K([เข้าคิวพิจารณา - รออนุมัติ])

  K --> L[พนักงานเปิดดูรายละเอียด<br/>บุคคล/ร้าน/บัญชี + ดูไฟล์สัญญา]
  L --> M[สร้างรหัสเชื่อม LINE - Redis<br/>+ กรอกชื่อไลน์กลุ่ม]
  M --> N{ผลพิจารณา}

  N -- อนุมัติ --> O[status=ACTIVE + log]
  N -- ตีกลับแก้ไข --> P[status=RETURNED + log<br/>เลือกหัวข้อแก้ไข + หมายเหตุ]
  N -- ไม่อนุมัติ --> Q[status=REJECTED + log<br/>เลือกเหตุผล + หมายเหตุ]

  O --> R[เจ้าของส่งรหัสในกลุ่ม LINE]
  R --> S[Webhook จับ groupId<br/>set line_group_id + line_connected_at]
  S --> T([ร้านใช้งานได้])

  P --> U[แจ้งเจ้าของผ่าน LINE<br/>เด้งไปฟอร์มที่ต้องแก้]
  U --> H
  Q --> V([จบ - ไม่อนุมัติ])

State ของสถานะพาร์ทเนอร์ (shop.partner_status)

stateDiagram-v2
  [*] --> PENDING: submit ใบสมัคร
  PENDING --> RETURNED: ตีกลับแก้ไข
  RETURNED --> PENDING: แก้แล้วส่งใหม่ (badge "แก้ไข")
  RETURNED --> CANCELED: ไม่แก้ใน 14 วัน (auto)
  PENDING --> ACTIVE: อนุมัติ
  PENDING --> REJECTED: ไม่อนุมัติ
  ACTIVE --> INACTIVE: ยกเลิกพาร์ทเนอร์
  INACTIVE --> ACTIVE: คืนสถานะ
  REJECTED --> [*]: ลบประวัติ (ปล่อยเบอร์)
  CANCELED --> [*]: ลบประวัติ (ปล่อยเบอร์)

badge “ใหม่” vs “แก้ไข” ในรายการ → derive จาก partner_status_logs (เคยมี RETURNED = “แก้ไข”)


Map แต่ละ step → ตาราง/คอลัมน์

Stepการกระทำตาราง / คอลัมน์หมายเหตุ
1รับข้อตกลงorg.terms_and_conditions (อ่าน is_latest, type=TERMS_OF_SERVICE)บันทึกจริงตอน submit
2เช็คเบอร์ + OTP— (Redis)เบอร์ unique ต่อบ้าน เช็คกับ shop.shops(business_id, phone_number)
3กรอกฟอร์ม 1–7(buffer ฝั่ง client/Redis)ยังไม่ลง DB จน submit
4Submitshop.shops (status=PENDING) + shop.users(OWNER) + shop.consentsgen contract_file_id (เอกสารสัญญา)
5สร้างรหัสเชื่อม LINE— (Redis OTP) + shops.line_group_nameทำก่อนทุกผลพิจารณา
6aอนุมัติpartner_status_logs(to=ACTIVE) → shops.status=ACTIVE
6bตีกลับpartner_status_logs(to=RETURNED) + partner_log_reasons (reasons applicable_to=RETURNED) + log.note
6cไม่อนุมัติpartner_status_logs(to=REJECTED) + partner_log_reasons (REJECTED) + log.note
7ผูก LINE กลุ่มshops.line_group_id + line_connected_atwebhook จับ groupId

หมายเหตุสำคัญ (business logic)

  • OTP ทั้งหมดผ่าน Redis (สมัคร + รหัสเชื่อม LINE) — ephemeral ไม่เก็บใน DB
  • เอกสารสัญญา contract_file_id ระบบ gen ตอน submit และ regenerate เมื่อข้อมูลเปลี่ยน (เช่น ตีกลับแล้วแก้)
  • ผูก LINE กลุ่มผ่าน webhook: เจ้าของส่ง “รหัสเชื่อม” ในกลุ่ม → ระบบ map รหัส↔ร้าน (Redis) + จับ groupId จาก event → เขียน line_group_id (unique 1 กลุ่ม = 1 ร้านสาขา)
  • ตีกลับ: เลือกได้หลายหัวข้อ (form sections) → UI เด้งเจ้าของไปฟอร์มแรกที่ต้องแก้
  • 1 ร้านสาขา = 1 row ใน shop.shops · เจ้าของ 1 + พนักงาน ≤5 ใน shop.users

Lifecycle (สถานะ)

  • ไม่อนุมัติ (REJECTED) → ประวัติ · กดลบประวัติได้ = hard-delete row ⇒ ปล่อยเบอร์ (unique ต่อบ้าน) ให้สมัครใหม่ด้วยเบอร์เดิม
  • ตีกลับ (RETURNED) ไม่แก้ใน 14 วัน → batch job auto → CANCELED → ประวัติ (ลบได้เช่นกัน)
  • ยกเลิกพาร์ทเนอร์ (ACTIVE → INACTIVE): เมนู “ประวัติการยกเลิก” → กดเพิ่มรายการยกเลิก → Modal (ค้นหาร้านที่จะยกเลิก + เลือกเหตุผล + หมายเหตุ) → partner_status_logs(to=INACTIVE) + partner_log_reasons(reasons applicable_to=INACTIVE) + log.note · ปรับกลับ ACTIVE ได้
  • คอลัมน์ “วันที่อนุมัติ” ในหน้าประวัติ → derive จาก partner_status_logs (log ที่ to_status=ACTIVE) · “พนักงานที่ดำเนินการ” → partner_status_logs.created_by

การแก้ไขข้อมูลร้าน (หลังเป็นพาร์ทเนอร์ · SH3)

  • เปลี่ยน ธนาคาร + เลขที่บัญชี ได้ (bank_code, bank_account_number) — ชื่อบัญชี (bank_account_name) ห้ามเปลี่ยน
  • เปลี่ยน เบอร์ร้าน (shops.phone_number) ได้ — เป็นเบอร์เดียวกับ owner (shop.users role=OWNER) → ต้อง sync กัน
  • shop.users เปลี่ยน username / password / display_name / เบอร์ ได้เสมอ (username ยัง unique ต่อกิจการ) · พนักงานร้าน soft-delete ได้ (ไม่มีปิดใช้งาน)