การซิงก์ฝั่งแอป POS
SyncEngine ดูแลการคุยกับ BC Ai Account ทั้งหมดของแอปแคชเชียร์: ส่งบิล/รูปใบเสร็จ/รายการเงินขึ้น และ ดึงสินค้า สมาชิก โปรโมชั่น คูปอง ค่าเครื่องลง ฝั่งเซิร์ฟเวอร์อธิบายใน POS ซิงก์ข้อมูลเครื่องแคชเชียร์ ผูกเครื่อง/ดึงส่วนต่าง/กู้คืน/รูปภาพ (POS Device Sync) POS Sync API: เครื่อง POS เชื่อมกับ Account (POS device sync) และ API จุดขาย POS (POS endpoints /api/v1/pos) หน้านี้อธิบายเฉพาะพฤติกรรมของแอป ภาพรวม: ระบบ POS ขายหน้าร้าน ภาพรวม การเปิดใช้ และแผนที่บันทึก (sys-pos-overview)
ซิงก์เมื่อไร
| จังหวะ | พฤติกรรม |
|---|---|
| เปิดแอป | ซิงก์ทันทีแบบข้ามเวลารอ (force) (pos_provider.dart:228-229) |
| กลับเข้าแอป (resume) | ซิงก์แบบ force (pos_provider.dart:233-235) |
| ตัวจับเวลา | ทุก 60 วินาที (pos_provider.dart:228) |
| หลังขายสำเร็จ | สั่งซิงก์เบื้องหลัง ไม่ต้องรอ (pos_provider.dart:1146) |
| กดป้ายสถานะ Online/Offline บนแถบบน หรือปุ่มซิงก์ | ซิงก์ทันที (cashier_screen.dart:104,278, pos_provider.dart:290-298) |
ล็อกเดียวต่อแอป (_running): ถ้ามีรอบซิงก์ทำงานอยู่ จะตั้งธง _again ให้วนอีกรอบหลังจบ แทนการเริ่มซ้อน (sync_engine.dart:21,55-56,158-183) ถ้ายังไม่เชื่อมเครื่อง หรือสถานะ 401 จะไม่ซิงก์เลย
ลำดับในหนึ่งรอบ (sync_engine.dart:185-195)
- ส่งบิลที่รออยู่ เรียงตามเวลาที่ขาย
- ส่งรูปใบเสร็จ (ใบเสร็จ POS การพิมพ์ซ้ำ และการอัปโหลดรูปสลิป (sys-pos-receipt-printing))
- ส่งรายการรับ/ส่งเงินและยกเลิกบิลที่ค้างคิว (กะแคชเชียร์ รับ-ส่งเงิน ยกเลิกบิล และกู้คืนข้อมูลเครื่อง POS (sys-pos-shift-cash))
- ส่งลูกค้าชั่วคราว (ครั้งเดียวต่อรหัส)
- ดึงข้อมูลลงเครื่อง (delta)
- กู้คืนอัตโนมัติ ครั้งเดียวถ้าเครื่องว่างจริง (การเชื่อมเครื่อง POS ด้วยรหัสเชื่อมเครื่อง ไม่มีหน้าล็อกอิน (sys-pos-device-pairing))
ขั้นไหนพบว่าเครือข่ายขัดข้องหรือ 401 จะหยุดทั้งรอบ ขั้นที่เหลือไม่ถูกลองต่อ (sync_engine.dart:196-211)
ส่งบิลขึ้น (POST /api/pos/bills)
- เนื้อหาบิลถูก "freeze" ตั้งแต่ตอนขายและเก็บในคิว
outboxพร้อมสถานะpending→sentหรือattentionต่อbill_guid(outbox_entry.dart:3,pos_provider.dart:1137-1142) - ส่งซ้ำด้วย
bill_guidเดิมได้เสมอ เซิร์ฟเวอร์ไม่สร้างบิลซ้ำ (api_service.dart:95) จึงปลอดภัยเมื่อเครือข่ายหลุดระหว่างตอบกลับ - ผลตอบกลับ (
sync_engine.dart:213-253,api_service.dart:15-19):- สำเร็จ → สถานะ
sentเก็บสถานะที่เซิร์ฟเวอร์ตอบ ล้างเนื้อหาที่ freeze วาดรูปใบเสร็จเข้าคิว ย้ายบิลไปกลุ่มซิงก์แล้ว - 400 / 409 / 422 (เซิร์ฟเวอร์ปฏิเสธ) → สถานะ
attentionส่งซ้ำก็ไม่ช่วย ต้องให้คนดู - 5xx หรือเครือข่ายขัดข้อง → รอแล้วลองใหม่
- 401 → หยุดและแจ้งรหัสถูกยกเลิก
- สำเร็จ → สถานะ
- ระยะรอก่อนลองใหม่ (backoff): 15 วินาที แล้วทวีคูณ 30, 60 ... สูงสุด 300 วินาที (5 นาที) ต่อบิล; ตัวจับเวลา 60 วินาทีจะหยิบบิลที่ถึงเวลาแล้วเอง; การกดซิงก์/เปิดแอป/กลับเข้าแอป (
force) ข้ามการรอ (sync_engine.dart:255-265) - บิลที่เซิร์ฟเวอร์รับแล้วแต่ประมวลผลผิดพลาด (
sentที่สถานะเป็นerror) ก็นับเป็น "ต้องตรวจสอบ" (outbox_entry.dart:31-32)
หน้าจอแสดงสถานะ
- ป้ายบนแถบบน: Online / Offline / ยังไม่เชื่อมเครื่อง พร้อมข้อความ "รอส่ง N" บนจอแคบ (โครงสร้างแอปแคชเชียร์ BC Ai POS (Flutter) และหน้าจอขาย (sys-pos-app-structure))
- แท็บ "เครื่อง POS & ค่าจาก BC Ai Account" แสดงข้อความล่าสุด และรายการ "ต้องตรวจสอบ" (บิลและรูปใบเสร็จ) พร้อมสาเหตุ และปุ่ม "รับทราบ" เพื่อเอาออกจากรายการ (
settings_dialog.dart:520-526,612-640,pos_provider.dart:386-403) - ประวัติบิลในเครื่องแสดงป้าย "รอส่ง" / "ต้องตรวจสอบ" ต่อบิล (
pos_provider.dart:362-384)
ดึงข้อมูลลง (GET /api/pos/sync)
- ส่ง
cursorที่เก็บไว้ (ว่าง = ดึงทั้งหมด) จำนวนสูงสุดหน้าละ 500 แถว พร้อมหัวX-Pos-Pending-Bills(จำนวนบิลรอส่ง) และX-Pos-App-Version(sync_engine.dart:25,401-411,api_service.dart:75-92) - ผลตอบกลับเป็นรายการเปลี่ยนแปลงต่อ entity:
product,member,promotion,coupon,config; แถวที่ว่างของ key หนึ่ง = ลบรายการนั้น (sync_engine.dart:438-467,objectbox_native.dart:195-225) configถูกนำไปอัปเดตค่าเครื่อง (ค่าเครื่อง POS ที่ซิงก์มา และการตั้งค่าในเครื่อง (sys-pos-app-config)) ส่วนอื่นเขียนลงฐานข้อมูลหนึ่งหน้า = หนึ่งทรานแซกชัน และลบไฟล์แคชรูปของรายการที่เปลี่ยน/ถูกลบ- ดึงต่อจนเซิร์ฟเวอร์บอก
has_moreเป็นเท็จ แล้วจึง บันทึก cursor ครั้งเดียวตอนจบ ถ้าหลุดกลางทาง cursor เดิมยังอยู่ รอบหน้าดึงซ้ำได้อย่างปลอดภัย (ถ้า cursor ที่ได้กลับมาซ้ำหรือว่างขณะยังมีหน้าถัดไป ถือว่าข้อมูลผิดปกติและหยุดรอบนั้น) (sync_engine.dart:420-436) - เมื่อแค็ตตาล็อกเปลี่ยน แอปโหลดสินค้า/สมาชิก/โปรโมชั่นเข้าหน่วยความจำใหม่และคำนวณโปรโมชั่นของบิลปัจจุบันซ้ำ (
pos_provider.dart:250-260)
ข้อควรรู้
- เวลาออฟไลน์ ขายต่อได้ตามปกติ บิลสะสมในคิว พอกลับมาออนไลน์จะถูกส่งตามลำดับเวลา
- ล้างเครื่อง/เปลี่ยนรหัสเชื่อมเครื่องไม่ได้ตราบที่มีบิลหรือรูปค้างส่ง เพื่อไม่ให้ข้อมูลขายหาย (การเชื่อมเครื่อง POS ด้วยรหัสเชื่อมเครื่อง ไม่มีหน้าล็อกอิน (sys-pos-device-pairing))
- ไม่มีการซิงก์บิลพัก ค่าเครื่องแบบสองทาง หรือรายการสต็อกกลับมาที่แอป: ขาลงมีเพียงห้า entity ข้างต้น
ที่มาในโค้ด
frontend/pos/lib/services/sync_engine.dart:21-467ตัวซิงก์ทั้งหมดfrontend/pos/lib/services/api_service.dart:7-124ผลตอบกลับและเส้นทาง APIfrontend/pos/lib/providers/pos_provider.dart:207-260,290-298,362-403trigger และสถานะ- ดูเพิ่ม: ระบบ POS ขายหน้าร้าน ภาพรวม การเปิดใช้ และแผนที่บันทึก (sys-pos-overview) · ที่เก็บข้อมูลในเครื่อง POS ออฟไลน์ ObjectBox และ SharedPreferences (sys-pos-offline-storage) · POS ซิงก์ข้อมูลเครื่องแคชเชียร์ ผูกเครื่อง/ดึงส่วนต่าง/กู้คืน/รูปภาพ (POS Device Sync) · POS รับบิลขายจากเครื่องแคชเชียร์ กล่องรับบิล/ประมวลผล/ยกเลิก/ตัดสต็อกเป็นชุด (POS Bill Inbox) · POS Sync API: เครื่อง POS เชื่อมกับ Account (POS device sync) · API จุดขาย POS (POS endpoints /api/v1/pos)