การแมปข้อมูลการขายของ Smaregi กับข้อมูลการขายของ Receipt Roller

สมาเรจิ การเชื่อมต่อ POS การแมปข้อมูล รายละเอียดทางเทคนิค ข้อมูลการขาย API

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/transactionsSmaregiTransaction)กับ PosTransactionDto ของ Receipt Roller มีดังนี้

Smaregi Receipt Roller หมายเหตุ
transactionHeadIdTransactionId / ExternalTransactionIdบันทึกค่าเดียวกันไว้ใน 2 ฟิลด์ เป็นคีย์สำหรับการ upsert แบบคงเส้นคงวา
transactionDateTimeTransactionDateTimeแปลงสตริง JST เป็น UTC แล้วบันทึก
(สกุลเงิน)Currencyบันทึกเป็น JPY คงที่
storeId(ไม่บันทึกโดยตรง)บันทึกไว้แล้วเป็น PosTerminal.SmaregiStoreId ตอนเชื่อมต่อ ฝั่งรายการขายจะแทนที่ด้วย PosTerminal.StoreId(ID สาขาของ Receipt Roller)
terminalId(ไม่บันทึกโดยตรง)บันทึกไว้แล้วเป็น PosTerminal.SmaregiTerminalId ตอนเชื่อมต่อ ฝั่งรายการขายจะแทนที่ด้วย PosTerminal.TerminalId(ID เครื่อง POS ของ Receipt Roller)
customerId / customerCodeExternalIds.ProviderCustomerId(และส่งต่อไปยังการระบุ CRM)ส่งต่อไปยังตรรกะการระบุ CRM ในฐานะคีย์จับคู่ลูกค้า(อธิบายด้านล่าง)รวมถึงเก็บไว้ในรายการขายเป็น ID สำหรับอ้างอิงข้ามผู้ให้บริการด้วย
staffIdStaffIdบันทึกตามเดิม
staffNameStaffNameหากเป็นค่าว่างจะดึงจาก /pos/staffs/{staffId} มาเติมเต็ม
totalTotalAmountSmaregi ส่งค่าตัวเลขเป็นสตริงด้วย จึงใช้ decimal.TryParse
subtotalSubtotalAmountหากเป็น 0 จะคำนวณจาก total - tax
taxTaxAmountแปลงสตริงเช่นเดียวกับข้างต้น
discountDiscountAmountเช่นเดียวกับข้างต้น
status / cancelDivisionStatusดูตารางแปลงสถานะด้านล่าง
receiptNumber / terminalTransactionIdReceiptNumberหมายเลขใบเสร็จที่ออกโดยเครื่อง
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 SmaregiTransactionDetailReceipt Roller PosTransactionLineItemDtoหมายเหตุ
productNameProductNameคัดลอกตามเดิม
quantityQuantityปัดเศษสตริงเป็นจำนวนเต็ม หากน้อยกว่าหรือเท่ากับ 0 จะปรับเป็น 1
priceUnitPriceแปลงสตริง
subtotalSubtotalแปลงสตริง
taxRateTaxRateแปลงสตริง
taxTaxAmountจำนวนภาษีระดับรายละเอียด เก็บเป็นจำนวนเงินไม่ใช่แค่อัตราภาษี
productIdCatalogObjectId / ProductIdID แคตตาล็อกสำหรับเชื่อมโยงระหว่างระบบ
categoryNameCategoryคัดลอกตามเดิม

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 — เส้นทางที่ถูกพุชแบบเรียลไทม์จาก Smaregi
  • Sync — เส้นทางการซิงค์ด้วยตนเองที่ผู้ดูแลร้านค้ากดปุ่ม "ซิงค์กับ 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(เวลาที่เกิดรายการขาย)จะไม่เปลี่ยนแปลง

ภาพรวมของไปป์ไลน์การนำเข้า

  1. เกิดรายการขายในฝั่ง Smaregi → Webhook ถูกส่งจาก Smaregi Platform ไปยัง Receipt Roller
  2. Endpoint รับ Webhook ของ Receipt Roller ดำเนินการยืนยันตัวตนด้วย custom header
  3. ดึง transactionHeadIds(อาจมีหลายรายการ)จาก payload
  4. สำหรับแต่ละ ID จะดึงส่วนหัวรายการขาย + รายละเอียด + ข้อมูลพนักงานจาก Platform API
  5. แปลงเป็น PosTransactionDto ด้วย SmaregiTransactionMapper
  6. ส่งคีย์จับคู่ลูกค้าไปยัง IPosCustomerMatchService เพื่อดำเนินการระบุตัวตนกับ CRM
  7. บันทึกด้วย PosTransactionService.UpsertAsync(รายการซ้ำจะถูกเขียนทับ)
  8. อัปเดต PosTerminal.LastTransactionAt(สำหรับใช้แสดงรายการขายล่าสุดผ่านการสแกน QR)
  9. หากระบุลูกค้าได้ จะส่งต่อไปยังบริการกระจายอัตโนมัติ(การส่งใบเสร็จอิเล็กทรอนิกส์)

บทความที่เกี่ยวข้อง

เผยแพร่เมื่อ: 2569-05-28 อัปเดตเมื่อ: 2569-07-02
บทความที่เกี่ยวข้อง