SurePhone ERD — Domain 4: Device Catalog (สินค้า/แคตตาล็อก · AD12) 🚧
building blocks ของสินค้า (ต่อกิจการ): ยี่ห้อ, ประเภทอุปกรณ์, ความจุ, เรทชำระ 🚧 ตาราง “สินค้า/รุ่น” (product) ที่รวมทุกอย่าง + ราคา → รอฟอร์มสร้างสินค้า schema =
device· audit อยู่ 00-shared · ทุก catalog ผูก business_id (ต่อกิจการ)
DBML
//// audit_c, audit_cu นิยามใน 00-shared.dbml · org.businesses ใน 01-org-access.md
Enum device.device_type {
"SMARTPHONE"
"TABLET"
"LAPTOP"
}
//// ───────── ยี่ห้อ (custom ต่อกิจการ) ─────────
Table device.brands {
id smallserial [pk]
business_id uuid [not null]
name varchar(255) [not null, note: 'ชื่อยี่ห้อ — ตั้งชื่ออย่างเดียว']
~audit_cu
indexes {
(business_id, name) [name: 'idx_brand_business_name', unique]
}
Note: 'ยี่ห้อ (custom) — ลบได้ถ้าไม่มีสินค้าใช้ (FK products.brand_id = restrict) · ไม่มี soft delete'
}
Ref fk_brand_business: device.brands.business_id > org.businesses.id [delete: cascade, update: no action]
//// ───────── ความจุ (config ต่อกิจการ) ─────────
Table device.capacities {
id smallserial [pk]
capacity_gb integer [not null, note: 'ความจุ (GB)']
~audit_cu
indexes {
capacity_gb [name: 'idx_capacity_gb', unique]
}
checks {
`capacity_gb > 0`
}
Note: 'ความจุเครื่อง (GB) — ระดับระบบ (shared ทุกกิจการ) ไม่ผูกกิจการ'
}
//// ───────── เรทชำระ (custom ต่อกิจการ) — header + items ─────────
Table device.payment_rates {
id smallserial [pk]
business_id uuid [not null]
name varchar(255) [not null, note: 'ชื่อเรทชำระ — ห้ามซ้ำ (ต่อกิจการ)']
document_fee integer [not null, note: 'ค่าเอกสาร (สตางค์) — ฟอร์มกรอกเป็นบาท']
~audit_cu
indexes {
(business_id, name) [name: 'idx_rate_business_name', unique]
}
checks {
`document_fee >= 0`
}
Note: 'เรทชำระ (custom) — มีรายการ (จำนวนเดือน/ตัวคูณ/ค่าคอม) ใน payment_rate_items'
}
Ref fk_rate_business: device.payment_rates.business_id > org.businesses.id [delete: cascade, update: no action]
Table device.payment_rate_items {
id serial [pk]
rate_id smallint [not null]
months smallint [not null, note: 'จำนวนเดือน — จำนวนเต็ม ≥ 1']
multiplier numeric(10,4) [not null, note: 'ตัวคูณ — > 0']
commission_pct numeric(5,2) [not null, note: 'ค่าคอมฯ (%) — > 0']
indexes {
(rate_id, months) [name: 'idx_rate_item_months', unique, note: 'เดือนห้ามซ้ำในเรทเดียวกัน']
}
checks {
`months >= 1`
`multiplier > 0`
`commission_pct > 0`
}
Note: 'รายการอัตราในเรทชำระ (อย่างน้อย 1 รายการ บังคับที่ app layer)'
}
Ref fk_rate_item_rate: device.payment_rate_items.rate_id > device.payment_rates.id [delete: cascade, update: no action]
//// ───────── สินค้า/รุ่น (product) + ราคาต่อความจุ ─────────
Table device.products {
id serial [pk]
business_id uuid [not null]
brand_id smallint [not null, note: 'FK → device.brands']
device_type device.device_type [not null]
name varchar(255) [not null, note: 'ชื่อรุ่น — ห้ามซ้ำ (ต่อกิจการ)']
colors text[] [not null, note: 'สี (ชื่ออะไรก็ได้ ไม่มี config) — text array']
payment_rate_id smallint [not null, note: 'เรทชำระเดียว ใช้กับทุกความจุของรุ่น']
~audit_cu
indexes {
(business_id, name) [name: 'idx_product_business_name', unique]
}
checks {
`cardinality(colors) >= 1`
}
Note: 'รุ่นสินค้า — brand + type + ชื่อ + สี(array) + เรทชำระ(1) · ราคา/ความจุ อยู่ product_capacities · ค่าที่ใช้คำนวณเงินถูก snapshot ตอนทำสัญญา (Domain 5)'
}
Ref fk_product_business: device.products.business_id > org.businesses.id [delete: cascade, update: no action]
Ref fk_product_brand: device.products.brand_id > device.brands.id [delete: restrict, update: no action]
Ref fk_product_rate: device.products.payment_rate_id > device.payment_rates.id [delete: restrict, update: no action]
Table device.product_capacities {
id serial [pk]
product_id integer [not null]
capacity_id smallint [not null]
price_new integer [note: 'ราคามือหนึ่งสูงสุด (สตางค์) — nullable']
price_used integer [note: 'ราคามือสองสูงสุด (สตางค์) — nullable']
price_foreign integer [note: 'ราคาเครื่องนอก (สตางค์) — nullable']
down_payment_percents integer[] [not null, note: 'อัตรายอดชำระก่อนรับสินค้า (เงินดาวน์) เป็น % หลายอัตรา เช่น {10,20,30}']
indexes {
(product_id, capacity_id) [name: 'idx_product_capacity', unique]
}
checks {
`price_new IS NOT NULL OR price_used IS NOT NULL`
`price_new IS NULL OR price_new > 0`
`price_used IS NULL OR price_used > 0`
`price_foreign IS NULL OR price_foreign > 0`
`cardinality(down_payment_percents) >= 1`
}
Note: 'ราคาต่อความจุ — มือหนึ่ง/มือสอง/เครื่องนอก + เงินดาวน์ (% array) · บังคับกรอกมือหนึ่งหรือมือสองอย่างน้อย 1 · child ของ products (ไม่มี audit แยก)'
}
Ref fk_product_capacity_product: device.product_capacities.product_id > device.products.id [delete: cascade, update: no action]
Ref fk_product_capacity_capacity: device.product_capacities.capacity_id > device.capacities.id [delete: restrict, update: no action]Mermaid ER
erDiagram businesses ||--o{ brands : "ยี่ห้อ" businesses ||--o{ payment_rates : "เรทชำระ" businesses ||--o{ products : "สินค้า/รุ่น" payment_rates ||--o{ payment_rate_items : "รายการอัตรา" brands ||--o{ products : "ยี่ห้อ" payment_rates ||--o{ products : "เรทชำระ" products ||--o{ product_capacities : "ราคา/ความจุ" capacities ||--o{ product_capacities : "ความจุ" brands { smallserial id PK uuid business_id FK varchar name varchar created_by timestamptz created_at } capacities { smallserial id PK integer capacity_gb } payment_rates { smallserial id PK uuid business_id FK varchar name integer document_fee } payment_rate_items { serial id PK smallint rate_id FK smallint months numeric multiplier numeric commission_pct } products { serial id PK uuid business_id FK smallint brand_id FK enum device_type varchar name text_array colors smallint payment_rate_id FK } product_capacities { serial id PK integer product_id FK smallint capacity_id FK integer price_new integer price_used integer price_foreign int_array down_payment_percents }
✅ Resolved
-
ยี่ห้อ
device.brands(custom, ชื่ออย่างเดียว, unique/กิจการ, ลบได้ถ้าไม่ถูกใช้ผ่าน FK restrict) -
ประเภท
Enum device.device_type(SMARTPHONE/TABLET/LAPTOP) -
ความจุ
device.capacities(config, GB) — ระดับระบบ (ไม่ผูกกิจการ) -
เรทชำระ
device.payment_rates(ชื่อ+ค่าเอกสาร) +device.payment_rate_items(เดือน/ตัวคูณ/ค่าคอม, เดือนห้ามซ้ำ, ทุกค่ามี check) -
สินค้า/รุ่น
device.products(brand+type+ชื่อ unique+สี text[]+เรทชำระ 1 ตัว) +device.product_capacities(ราคา มือหนึ่ง/มือสอง/เครื่องนอก + เงินดาวน์ % array · ความจุ M:N) — ฟอร์ม node 1095:158862 ✓ (รีไฟแนนซ์ใน UI = ทำเกิน ไม่มีจริง)
❓ ยังค้าง
- contract snapshot — ตอนทำสัญญาต้อง snapshot ราคา/เรท/เงินดาวน์ ที่เลือก (Domain 5) เพื่อกันราคาเปลี่ยนกระทบสัญญาเก่า