การแมปข้อมูลการขายของ Smaregi กับข้อมูลการขายของ Receipt Roller
Receipt Roller เชื่อมต่อกับผู้ให้บริการ POS หลายรายรวมถึง Smaregi ไม่ว่าจะนำเข้าข้อมูลการขายจากผู้ให้บริการรายใด ภายในระบบจะถูกจัดการด้วยโมเดลข้อมูลการขายร่วม(PosTransactionDto)โดยมีการปรับมาตรฐานฟิลด์เฉพาะของแต่ละผู้ให้บริการ บทความนี้อธิบายทางเทคนิคว่าข้อมูลการขายของ Smaregi Platform API ถูกจับคู่เข้ากับโมเดลภายในของ Receipt Roller อย่างไร
บทความนี้เป็นเอกสารอ้างอิงสำหรับนักพัฒนาและผู้ดูแลระบบที่ต้องการทำความเข้าใจข้อกำหนดภายในของการเชื่อมต่อ POS สำหรับขั้นตอนการตั้งค่าเชื่อมต่อทั่วไป ดูได้ที่ การตั้งค่าเชื่อมต่อ POS ของ Smaregi สำหรับข้อกำหนดโมเดลมาตรฐานแยกตามฟิลด์ ดูได้ที่ ข้อกำหนด PosTransactionDto (ฟิลด์อ้างอิง)
การเทียบโครงสร้างแบบลำดับชั้น
Smaregi Platform API มีโครงสร้างลำดับชั้น 4 ระดับ ได้แก่ สัญญา・สาขา・เครื่องขาย・รายการขาย ฝั่ง Receipt Roller มีลำดับชั้น 4 ระดับเช่นกัน คือ บัญชีธุรกิจ・สาขา・เครื่อง POS・รายการขาย ซึ่งออกแบบให้ตรงกันแบบหนึ่งต่อหนึ่ง
- สัญญา(contractId) ↔ บัญชีธุรกิจของ Receipt Roller เมื่อติดตั้งแอป "Receipt Roller Link" ใน Smaregi สัญญานั้นจะถูกเชื่อมโยงกับบัญชีธุรกิจ 1 บัญชีของ Receipt Roller(
SmaregiContract.OrganizationId) - สาขาของ Smaregi(storeId) ↔ สาขาของ Receipt Roller เชื่อมโยงกับเครื่อง POS ฝั่ง Receipt Roller ผ่าน
PosTerminal.SmaregiStoreId - เครื่องขายของ Smaregi(terminalId) ↔ เครื่อง POS ของ Receipt Roller เก็บไว้ใน
PosTerminal.SmaregiTerminalIdโดย 1 เครื่อง POS ของ Receipt Roller ตรงกับ 1 เครื่องขายของ Smaregi(แคชเชียร์) - รายการขาย(transactionHeadId) ↔ รายการขายของ Receipt Roller เก็บ ID เดียวกันไว้เป็นทั้ง
PosTransaction.TransactionIdและExternalTransactionId
การแมปฟิลด์ของส่วนหัวรายการขาย
ความสัมพันธ์ระหว่าง response ของ Smaregi /pos/transactions(SmaregiTransaction)กับ PosTransactionDto ของ Receipt Roller มีดังนี้
| Smaregi | Receipt Roller | หมายเหตุ |
|---|---|---|
transactionHeadId | TransactionId / ExternalTransactionId | บันทึกค่าเดียวกันไว้ใน 2 ฟิลด์ เป็นคีย์สำหรับการ upsert แบบคงเส้นคงวา |
transactionDateTime | TransactionDateTime | แปลงสตริง JST เป็น UTC แล้วบันทึก |
| (สกุลเงิน) | Currency | บันทึกเป็น JPY คงที่ |
storeId | (ไม่บันทึกโดยตรง) | บันทึกไว้แล้วเป็น PosTerminal.SmaregiStoreId ตอนเชื่อมต่อ ฝั่งรายการขายจะแทนที่ด้วย PosTerminal.StoreId(ID สาขาของ Receipt Roller) |
terminalId | (ไม่บันทึกโดยตรง) | บันทึกไว้แล้วเป็น PosTerminal.SmaregiTerminalId ตอนเชื่อมต่อ ฝั่งรายการขายจะแทนที่ด้วย PosTerminal.TerminalId(ID เครื่อง POS ของ Receipt Roller) |
customerId / customerCode | ExternalIds.ProviderCustomerId(และส่งต่อไปยังการระบุ CRM) | ส่งต่อไปยังตรรกะการระบุ CRM ในฐานะคีย์จับคู่ลูกค้า(อธิบายด้านล่าง)รวมถึงเก็บไว้ในรายการขายเป็น ID สำหรับอ้างอิงข้ามผู้ให้บริการด้วย |
staffId | StaffId | บันทึกตามเดิม |
staffName | StaffName | หากเป็นค่าว่างจะดึงจาก /pos/staffs/{staffId} มาเติมเต็ม |
total | TotalAmount | Smaregi ส่งค่าตัวเลขเป็นสตริงด้วย จึงใช้ decimal.TryParse |
subtotal | SubtotalAmount | หากเป็น 0 จะคำนวณจาก total - tax |
tax | TaxAmount | แปลงสตริงเช่นเดียวกับข้างต้น |
discount | DiscountAmount | เช่นเดียวกับข้างต้น |
status / cancelDivision | Status | ดูตารางแปลงสถานะด้านล่าง |
receiptNumber / terminalTransactionId | ReceiptNumber | หมายเลขใบเสร็จที่ออกโดยเครื่อง |
paymentMethods[] | Payments[] / PaymentMethod | เก็บผู้ให้บริการชำระเงินแต่ละรายไว้ครบถ้วนใน Payments[](รองรับการชำระแบบแยก)ส่วน PaymentMethod เก็บวิธีการหลักไว้(อธิบายด้านล่าง) |
การแปลงรหัสสถานะ
ค่าสถานะของ Smaregi ส่งกลับมาเป็นสตริงตัวเลข ภายใน Receipt Roller จะปรับให้เป็นป้ายกำกับแบบสตริง
| ค่าของ Smaregi | ความหมาย | Receipt Roller Status |
|---|---|---|
"1" | เสร็จสมบูรณ์ | Completed |
"2" | ยกเลิก | Voided |
"3" | คืนเงิน | Refunded |
| ค่าไม่ทราบ・สตริงว่าง | — | Pending(สำรอง) |
มีบางกรณีที่ต้องใช้ทั้ง status และ cancelDivision ร่วมกันขึ้นอยู่กับข้อกำหนด ระบบจะตรวจสอบค่าทั้งสองเพื่อจำแนกให้ถูกต้อง หากตรวจพบค่าใดค่าหนึ่ง
การปรับมาตรฐานวิธีการชำระเงิน
paymentMethods ของ Smaregi ส่งกลับมาเป็นอาร์เรย์ โดยแต่ละองค์ประกอบมีชื่อวิธีการชำระเงินและจำนวนเงิน(หากเป็นการชำระแบบแยกจะมีหลายองค์ประกอบ)Receipt Roller เก็บผู้ให้บริการชำระเงินทุกรายไว้ใน Payments[] และยังเก็บ "วิธีการชำระเงินหลัก" เพียง 1 รายการไว้ใน PaymentMethod เพื่อความเข้ากันได้ย้อนหลังด้วย วิธีการหลักจะเลือกองค์ประกอบที่มีจำนวนเงินมากที่สุดจากอาร์เรย์ แล้วปรับมาตรฐานข้อความภาษาญี่ปุ่นให้เป็นรหัสภายใน
| ข้อความของ Smaregi | รหัสภายในของ Receipt Roller |
|---|---|
| มีคำว่า "เงินสด" | Cash |
| มีคำว่า "บัตรเครดิต" | CreditCard |
| มีคำว่า "QR" | QR |
| มีคำว่า "เงินอิเล็กทรอนิกส์" หรือ "IC" | IC |
| อาร์เรย์ว่างหรือไม่มีรายการที่ตรงกัน | Other |
บาง API รายการขายของ Smaregi อาจไม่มีข้อมูลวิธีการชำระเงินรวมอยู่ด้วย ในกรณีนั้นระบบจะดึงข้อมูลเพิ่มเติมจาก endpoint แยกต่างหาก สถานะล่าสุดของการพัฒนาจริงดูได้จาก source code(SmaregiTransactionMapper)
การแมปข้อมูลรายละเอียด (line item)
นอกเหนือจากส่วนหัวรายการขาย ระบบจะดึงข้อมูลรายละเอียดจาก /pos/transactions/{id}/details แล้วแปลงเป็นอาร์เรย์ของ PosTransactionLineItemDto ก่อนบันทึกแบบ serialize ลงใน LineItemsJson
Smaregi SmaregiTransactionDetail | Receipt Roller PosTransactionLineItemDto | หมายเหตุ |
|---|---|---|
productName | ProductName | คัดลอกตามเดิม |
quantity | Quantity | ปัดเศษสตริงเป็นจำนวนเต็ม หากน้อยกว่าหรือเท่ากับ 0 จะปรับเป็น 1 |
price | UnitPrice | แปลงสตริง |
subtotal | Subtotal | แปลงสตริง |
taxRate | TaxRate | แปลงสตริง |
tax | TaxAmount | จำนวนภาษีระดับรายละเอียด เก็บเป็นจำนวนเงินไม่ใช่แค่อัตราภาษี |
productId | CatalogObjectId / ProductId | ID แคตตาล็อกสำหรับเชื่อมโยงระหว่างระบบ |
categoryName | Category | คัดลอกตามเดิม |
ItemCount(จำนวนแต้มซื้อทั้งหมดของรายการขาย)คำนวณจากผลรวมของ Quantity ในรายละเอียด
สำหรับส่วนลดระดับรายละเอียด มีแผนจะแมปหลังจากยืนยันชื่อฟิลด์ที่เกี่ยวข้องใน transactionDetails ของ Smaregi ก่อนจนกว่าจะยืนยันได้ DiscountAmount จะเป็น null และเก็บไว้ใน payload ดิบ(RawJson)เพื่อป้องกันการสูญหายของข้อมูล
การเก็บข้อมูลดิบ (RawJson)
เมื่อนำเข้าข้อมูล ระบบจะบันทึก payload ดิบต้นฉบับของส่วนหัวรายการขาย + รายละเอียดไว้ใน RawJson เป็นมาตรการความปลอดภัยเพื่อให้สามารถกู้คืนฟิลด์ที่ไม่ได้แปลงเป็นโมเดลได้ในภายหลัง ใช้งานภายในเซิร์ฟเวอร์เท่านั้น(ไม่ส่งคืนผ่าน API)
การระบุข้อมูลลูกค้า (การเชื่อมต่อ CRM)
customerId / customerCode ที่ดึงมาจาก Smaregi จะถูกส่งไปยังบริการระบุตัวตน CRM(IPosCustomerMatchService)ในฐานะคีย์จับคู่ลูกค้า รวมถึงเก็บไว้ใน ExternalIds.ProviderCustomerId เป็น ID สำหรับอ้างอิงข้ามผู้ให้บริการด้วย หากจับคู่สำเร็จ รายการขายจะถูกเชื่อมโยงเข้ากับโปรไฟล์ลูกค้า CRM ของ Receipt Roller โดยอัตโนมัติ และนำไปใช้ในการคำนวณประวัติการซื้อรายบุคคลหรือคะแนนความเสี่ยงในการเลิกใช้บริการ
ประเภทหลักของคีย์จับคู่มีดังนี้(อ้างอิงจาก PosCustomerMatchDto ในการพัฒนาจริง)
- หมายเลขสมาชิก(รหัสสมาชิก)—
customerCodeของ Smaregi เป็นต้น - ID ลูกค้าภายนอก —
customerIdของ Smaregi - ที่อยู่อีเมล/หมายเลขโทรศัพท์ — เฉพาะกรณีที่ดึงได้จาก Smaregi
รายการขายที่จับคู่ไม่สำเร็จ สามารถให้ลูกค้าเชื่อมโยงด้วยตนเองในภายหลังผ่านการสแกน QR ที่หน้าร้านได้เช่นกัน
การเติมเต็มข้อมูลพนักงาน
มีบางกรณีที่ส่วนหัวรายการขายมีเพียง staffId เท่านั้น ในกรณีนั้นระบบจะเรียก /pos/staffs/{staffId} แยกต่างหากเพื่อดึง displayName แล้วบันทึกลงใน StaffName แม้การดึงชื่อพนักงานจะล้มเหลว การนำเข้ารายการขายเองจะไม่ล้มเหลวไปด้วย แต่จะบันทึกเป็นค่าว่างไว้(เพื่อป้องกันข้อมูลรายการขายสูญหาย)
การแยกแยะเส้นทางการดึงข้อมูล (Source)
แม้เป็นรายการขายเดียวกัน ระบบจะแยกแยะด้วยฟิลด์ Source ว่าถูกนำเข้าผ่านเส้นทางใด
Webhook— เส้นทางที่ถูกพุชแบบเรียลไทม์จาก SmaregiSync— เส้นทางการซิงค์ด้วยตนเองที่ผู้ดูแลร้านค้ากดปุ่ม "ซิงค์กับ Smaregi"API— การดึงข้อมูลผ่าน Platform API เส้นทางอื่นๆ(เช่น การดึงข้อมูลทันทีตอนสแกน QR)
แม้ transactionHeadId เดียวกันจะมาถึงผ่านหลายเส้นทาง จะไม่มีรายการขายซ้ำเกิดขึ้น เนื่องจากใช้ ID เดียวกันเป็นคีย์ในการ upsert ค่า Source จะสะท้อนเส้นทางที่นำเข้าล่าสุด
การจัดการโซนเวลา
Smaregi Platform API ส่งคืนวันเวลาในรูปแบบ ISO 8601 ของ JST(+09:00)ฝั่ง Receipt Roller จะบันทึกทั้งหมดเป็น UTC และแปลงเป็น JST เมื่อแสดงผล เมื่อส่งฟิลเตอร์วันเวลาไปยัง Platform API ก็จะแปลงจาก UTC เป็น JST แล้วเข้ารหัส URL ก่อนส่ง(เพื่อไม่ให้เครื่องหมาย + ของ +09:00 ถูกตีความเป็นช่องว่าง)
ความคงเส้นคงวา (Idempotency)
การ upsert รายการขายใช้ transactionHeadId เป็นคีย์ แม้รายการขายเดียวกันจะมาถึงทั้งผ่าน Webhook และการซิงค์ด้วยตนเอง ผลลัพธ์จะรวมเป็นเรคคอร์ดเดียว UpdatedAt จะถูกอัปเดตตามเวลาที่นำเข้าล่าสุด แต่ TransactionDateTime(เวลาที่เกิดรายการขาย)จะไม่เปลี่ยนแปลง
ภาพรวมของไปป์ไลน์การนำเข้า
- เกิดรายการขายในฝั่ง Smaregi → Webhook ถูกส่งจาก Smaregi Platform ไปยัง Receipt Roller
- Endpoint รับ Webhook ของ Receipt Roller ดำเนินการยืนยันตัวตนด้วย custom header
- ดึง
transactionHeadIds(อาจมีหลายรายการ)จาก payload - สำหรับแต่ละ ID จะดึงส่วนหัวรายการขาย + รายละเอียด + ข้อมูลพนักงานจาก Platform API
- แปลงเป็น
PosTransactionDtoด้วยSmaregiTransactionMapper - ส่งคีย์จับคู่ลูกค้าไปยัง
IPosCustomerMatchServiceเพื่อดำเนินการระบุตัวตนกับ CRM - บันทึกด้วย
PosTransactionService.UpsertAsync(รายการซ้ำจะถูกเขียนทับ) - อัปเดต
PosTerminal.LastTransactionAt(สำหรับใช้แสดงรายการขายล่าสุดผ่านการสแกน QR) - หากระบุลูกค้าได้ จะส่งต่อไปยังบริการกระจายอัตโนมัติ(การส่งใบเสร็จอิเล็กทรอนิกส์)
บทความที่เกี่ยวข้อง
- ข้อกำหนด PosTransactionDto (ฟิลด์อ้างอิง) — ข้อกำหนดโดยละเอียดของแต่ละฟิลด์
- การแมปข้อมูลการขายของ Square — เวอร์ชัน Square
- การตั้งค่าเชื่อมต่อ POS ของ Smaregi — ขั้นตอนการเชื่อมต่อ
- การเชื่อมต่อกับ POS
- การตั้งค่าเชื่อมต่อ POS ของ Square
- การซิงค์ข้อมูลสมาชิกแบบสองทิศทางกับ Smaregi / Square
- ประวัติการซื้อรายบุคคล (การเชื่อมต่อ POS)
-
การแมปข้อมูลสินค้า Smaregi กับข้อมูลสินค้าของ Receipt Rollerอธิบายทีละฟิลด์ว่าข้อมูลหลักสินค้าของ Smaregi ถูกแปลงเป็นข้อมูลสินค้าของ Receipt Roller (ProductDto) อย่างไร ครอบคลุมความสัมพันธ์ของรหัสสินค้า ชื่อสินค้า หมวดหมู่ ราคา ต้นทุน และอัตราภาษี รวมถึงพฤติกรรมเฉพาะของ Smaregi
-
การแมปข้อมูลพนักงาน Smaregi กับข้อมูลพนักงานของ Receipt Rollerอธิบายทีละฟิลด์ว่าข้อมูลพนักงานหลักของ Smaregi ถูกแปลงเป็นข้อมูลพนักงานของ Receipt Roller (StaffDto) อย่างไร ครอบคลุมตรรกะการแยกชื่อ-นามสกุลจาก staffName, การแมป staffCode ไปยัง EmployeeNumber และการข้ามพนักงานที่ถูกซ่อนด้วย displayFlag
-
การแมปข้อมูลสมาชิก Smaregi กับข้อมูลลูกค้าของ Receipt Rollerอธิบายทีละฟิลด์ว่าข้อมูลหลักสมาชิกของ Smaregi ถูกแปลงเป็นข้อมูลลูกค้าของ Receipt Roller (CrmCustomerDto) อย่างไร ครอบคลุมความสัมพันธ์ของหมายเลขสมาชิก ชื่อ-นามสกุล ข้อมูลติดต่อ ลำดับความสำคัญของหมายเลขโทรศัพท์ และจังหวะเวลาการซิงค์ผ่าน Webhook