POS Point — ระบบสะสมแต้ม POS และ Sale บน Odoo 17 Enterprise
โมดูล Odoo 17 Enterprise สำหรับจัดการ Point (แต้ม): รวมออเดอร์จาก Sale และ POS เป็น view เดียว คำนวณแต้มจาก Air Type, หมวดหมู่+ช่วงราคา และยอดสั่ง พร้อม export log เป็น Excel. ใช้ Python (ORM, SQL view) + res.config.settings.
Problem
ต้องการระบบสะสมแต้มที่รวมทั้งออเดอร์จาก Sale และ POS เข้าด้วยกัน โดยคำนวณแต้มตามกฎหลายแบบ (ประเภทสินค้า, หมวดหมู่+ช่วงราคา, ยอดสั่ง) และกรองเฉพาะออเดอร์ที่จ่ายครบ ผ่านช่องทางที่กำหนด และไม่ overdue — โฟลว์มาตรฐานของ Odoo ไม่มี unified view และกฎคำนวณแต้มแบบนี้
Solution
พัฒนาโมดูลขยาย Odoo 17 (point_of_sale, sale, account) สร้าง SQL view รวม sale_order, pos_order และ out_refund; เพิ่มฟิลด์ our_point_code และ point ที่ res.partner, ขยาย product.air_type ด้วย point; สร้าง model ช่วงราคา–แต้ม และ res.config.settings สำหรับกฎคำนวณ (payment method, special price, overdue, air type, main category, ยอดทุก X บาทได้ Y แต้ม, exclude หมวดสินค้า) ทั้ง Sale และ POS; Wizard export log เป็น Excel ตามช่วงวันที่
Impact
ดูรายการออเดอร์ที่ได้แต้มจาก Sale และ POS ในที่เดียว, คำนวณแต้มตรงตามกฎที่ตั้งค่าได้, export log ใช้ตรวจสอบ/ส่งต่อได้ทันที
ทำไมต้องมีโมดูลนี้ (Executive Summary)
ปัญหาที่พบบ่อยใน Odoo คือแต้มสมาชิกแยกกันระหว่างหน้าร้าน (POS) กับหลังบ้าน (Sale Order) ทำให้ลูกค้าสับสนว่าแต้มมาจากช่องทางไหน และแอดมินต้องตรวจหรือรวบรวมข้อมูลจากหลายที่ ทำงานซ้ำซ้อน
POS Point ช่วยรวมศูนย์ข้อมูลแต้มจากทุกช่องทาง (Sale, POS, การคืนเงิน) ให้เห็นในหน้าเดียว และคำนวณแต้มตามเงื่อนไขธุรกิจที่ซับซ้อนได้โดยอัตโนมัติ — เช่น แต้มตามประเภทสินค้า ตามช่วงราคา หรือตามยอดสั่ง (ทุก X บาทได้ Y แต้ม) — โดยกรองเฉพาะออเดอร์ที่จ่ายครบและผ่านวิธีชำระที่กำหนด
ผลลัพธ์ที่ได้: แอดมินตรวจสอบแต้มได้ในหน้าเดียว และออกรายงาน Excel ตามช่วงวันที่ เพื่อใช้ทำ Marketing หรือส่งต่อทีมอื่นได้ทันที
เหมาะกับ: ธุรกิจที่มีทั้งโชว์รูม/หน้าร้าน (POS) และขายผ่านออเดอร์หลังบ้าน (Sale) และต้องการโปรแกรมสะสมแต้มที่ยืดหยุ่นตามกฎของบริษัท
System Architecture
Data Flow (Flowchart)
ข้อมูลออเดอร์จาก Sale, POS และใบลดหนี้ไหลมารวมที่ SQL view เดียว จากนั้นใช้แสดงใน Tree View หรือส่งออกผ่าน Export Wizard:
flowchart LR
subgraph sources["แหล่งข้อมูล"]
SO[sale.order]
PO[pos.order]
AM[account_move<br/>out_refund]
end
subgraph filter["กรอง"]
RC[res.partner<br/>our_point_code]
end
subgraph unified["Unified View"]
PSU[(pos.sale.unified<br/>SQL View)]
end
subgraph output["ผลลัพธ์"]
TV[Tree View<br/>POS and Sale]
WIZ[Export Wizard]
XLSX[Excel]
end
SO --> RC
PO --> RC
AM --> RC
RC --> PSU
PSU --> TV
PSU --> WIZ
WIZ --> XLSX
- เฉพาะลูกค้าที่มี รหัสสมาชิก (Our Point Code) จะถูกนำมาคำนวณแต้ม
- View คำนวณให้ว่าแต่ละออเดอร์ ผ่านเงื่อนไขหรือไม่ (validated) และ ได้กี่แต้ม ตามกฎใน Settings
การเปิดดูรายการ (Sequence)
เมื่อผู้ใช้เปิดเมนู “POS and Sale” ระบบดึงข้อมูลจาก view แล้วคำนวณ validated และ point ทาง backend:
sequenceDiagram
participant User
participant OdooUI as Odoo Web UI
participant ORM as ORM / Backend
participant DB as PostgreSQL
User->>OdooUI: เปิดเมนู POS and Sale
OdooUI->>ORM: search_read(pos.sale.unified)
ORM->>DB: SELECT จาก view pos_sale_unified
DB-->>ORM: rows (order_id, partner, order_type, ...)
ORM->>ORM: _compute_validated_order_and_point (แต่ละ record)
Note over ORM: อ่าน res.config.settings<br/>กรอง payment / overdue<br/>_calculate_point (air type, category, amount)
ORM-->>OdooUI: records พร้อม validated_order, point
OdooUI-->>User: แสดง Tree View
การ Export Log (Sequence)
เมื่อผู้ใช้รัน Export Wizard กำหนดช่วงวันที่แล้วกด Export:
sequenceDiagram
participant User
participant Wizard as Export Wizard
participant ORM as ORM
participant SO as sale.order
participant PO as pos.order
participant XLSX as XlsxWriter
User->>Wizard: กำหนด date_from, date_to → Export
Wizard->>ORM: _get_paid_sale_order(domain)
Wizard->>ORM: _get_paid_pos_order(domain)
ORM->>SO: search + กรองจ่ายครบ / payment method
ORM->>PO: search + กรอง our_point_code
SO-->>Wizard: paid sale orders
PO-->>Wizard: paid pos orders
Wizard->>Wizard: _combine_and_sort, กรอง our_point_code
Wizard->>Wizard: _calculate_point แต่ละ order
Wizard->>XLSX: เขียนหัวคอลัมน์ + แถว
Wizard->>ORM: สร้าง ir.attachment (binary)
Wizard-->>User: redirect download URL
Implementation Details (Deep Dive)
POS Point เป็นโมดูลส่วนขยายของ Odoo 17 Enterprise สำหรับจัดการ Point (แต้ม) โดยรวมออเดอร์จาก Sale และ POS เข้าด้วยกัน คำนวณแต้มจากหลายกฎที่กำหนดใน Settings และแสดงใน view เดียว รองรับการ export รายการออเดอร์ที่ได้แต้มเป็นไฟล์ Excel
สถาปัตยกรรมเชิงเทคนิค
- Backend (Python) — inherit
res.partner,res.config.settings, ขยายproduct.air_type(จาก sale_report_extension); สร้าง modelpos.point.config,pos.point.product.price.rangeและ SQL viewpos.sale.unifiedรวมข้อมูลจาก sale_order, pos_order และ account_move (out_refund) - กฎคำนวณแต้ม — อยู่ที่ Settings (company-scoped): วิธีชำระเงินที่อนุญาต, ออเดอร์ราคาพิเศษ/overdue นับหรือไม่, แต้มตาม Air Type, แต้มตาม Main POS Category + ช่วงราคา (price range → points), แต้มตามยอด (ทุก X บาทได้ Y แต้ม), และการยกเว้นหมวดสินค้า
- Export — TransientModel wizard เลือกช่วงวันที่ แล้วดึง Sale/POS orders ที่จ่ายครบและผ่านกฎ จากนั้นคำนวณ point และ export เป็น XLSX (XlsxWriter)
โมดูลและ Models
โมดูลอยู่ในโครงสร้างมาตรฐาน Odoo (__manifest__.py, models, views, wizard, security):
- res.partner — เพิ่ม
our_point_code,pointสำหรับลูกค้าที่ร่วมรายการสะสมแต้ม - product.air_type (inherit จาก sale_report_extension) — เพิ่มฟิลด์
point(company_dependent) ใช้คำนวณแต้มตามประเภทสินค้า - pos.point.product.price.range — ช่วงราคา (price_from, price_to) และ points; ใช้ร่วมกับ Main Point Category ในการคำนวณแต้มตามราคาขาย
- res.config.settings — ตั้งค่ากฎคำนวณแยก Sale / POS: payment method ที่อนุญาต, special price, overdue, air type point, main category + price range, point by order amount (every THB / get point), exclude product categories; เก็บใน
ir.config_parameterแยกตาม company - pos.sale.unified — SQL view (
_auto = False) รวมออเดอร์จาก Sale, POS และ Refund เฉพาะ partner ที่มีour_point_code; มี computed fieldsvalidated_orderและpointตามกฎใน settings
# models/pos_sale_unified.py (สรุป)
class PosSaleUnified(models.Model):
_name = "pos.sale.unified"
_auto = False
_order = "last_payment_date DESC, name DESC"
order_id = fields.Char("Order ID")
name = fields.Char("Order Ref")
order_type = fields.Selection([("sale", "Sale"), ("pos", "POS"), ("refund", "Sale Refund")])
partner_id = fields.Many2one("res.partner", string="Customer")
our_point_code = fields.Char("Our Point Code")
validated_order = fields.Boolean(compute="_compute_validated_order_and_point", ...)
point = fields.Float(compute="_compute_validated_order_and_point", ...)
# ... sale_order_id, pos_order_id (computed from order_type + order_id)
การคำนวณ validated_order และ point ใช้เมธอดเช่น _get_paid_sale_order, _get_paid_pos_order (กรอง payment method, down payment, overdue ตาม settings) และ _calculate_point (รวมจาก air type, main category + price range, และยอดสั่ง)
การคำนวณแต้ม
ลำดับใน _calculate_point: กรองบรรทัดออเดอร์ (ยกเว้น down payment, โอนไป POS ตามกฎ) → กรอง special price ตาม setting → คำนวณแต้มจาก Air Type → จาก Main Category + ช่วงราคา (pos.point.product.price.range) → จากยอดสั่ง (every THB / get point) รวมทั้ง refund ที่หักแต้ม
Export Log (Wizard)
Wizard pos.point.log.export.wizard กำหนดช่วงวันที่ (From/To), เลือก Partner และ Source (POS/Sale) จากนั้นเรียก export_query_excel: ดึง sale orders และ pos orders ที่จ่ายครบและผ่านกฎใน domain, เรียงตาม create_date, กรองเฉพาะที่มี our_point_code, คำนวณ point ต่อออเดอร์ แล้วเขียน Excel (หัวคอลัมน์: No., Company Name, Customer Name, Our point Code, Create Date, Mobile, Source, Order Ref., Point) และสร้าง ir.attachment แล้วส่งกลับ action download
Deployment
รันบน instance Odoo 17 Enterprise; โมดูล depend บน base, sale, point_of_sale, account, sale_report_extension, purchase, mrp. ติดตั้งผ่าน Apps แล้วอัปเดต module list ใช้ PostgreSQL ของ Odoo โดยตรง ไม่เพิ่ม service อื่น
สรุปผลลัพธ์เชิงเทคนิค
- รายการออเดอร์จาก Sale และ POS แสดงใน view เดียว (pos.sale.unified) พร้อมสถานะ validated และจำนวนแต้ม
- กฎคำนวณแต้มกำหนดได้แยก Sale / POS ผ่าน Settings (payment, special price, overdue, air type, main category + price range, amount-based, exclude category)
- Export log เป็น Excel ตามช่วงวันที่และตัวกรอง สำหรับตรวจสอบหรือส่งต่อได้ทันที