ข้ามไปยังเนื้อหา

เขตเวลาและวันธุรกิจ

วันของร้านอาหารไม่ได้จบตอนเที่ยงคืน ScanFood จึงบันทึกวันที่ไว้สองแบบบนบิลทุกใบ และการหยิบผิดตัวคือสาเหตุอันดับหนึ่งที่ข้อมูลส่งออกไม่ตรงกับรายงานของร้านเอง

ร้านแต่ละร้านมีเขตเวลาของตัวเอง ส่งกลับมาเป็นชื่อมาตรฐาน IANA ที่ GET /v1/shops

{ "shopId": "aK3mP9xQ2bR7cT4dV6wY", "timezone": "Asia/Bangkok" }

ร้านที่ไม่เคยตั้งค่านี้จะได้ null ซึ่งเป็นเรื่องปกติของร้านเก่า ให้ถือว่า null คือ Asia/Bangkok เพราะนั่นคือค่าที่ ScanFood ใช้จัดรูปวันที่ของร้านนั้นเอง อย่าส่งค่าดิบเข้าตัวแปลงเขตเวลาโดยไม่มีค่าสำรองนี้ ไม่อย่างนั้นจะพังทันทีที่เจอสาขาเก่าสาขาแรก

เวลาแต่ละช่องถูกจัดรูปให้เหมาะกับคนที่ต้องใช้มัน

ช่อง รูปแบบ เขตเวลา
timestamp และ updatedAt บนบิล ISO-8601 พร้อมค่าชดเชย เช่น 2026-09-01T13:30:00.000+07:00 เขตเวลาของร้าน
createdDate และ coupons[].expireAt บนสมาชิก ISO-8601 พร้อมค่าชดเชย เขตเวลาของแบรนด์สมาชิก
businessDate billDate lastVisit YYYY-MM-DD วันตามนาฬิกาบนผนังในเขตเวลาเดียวกันนั้น ไม่มีเวลา ไม่มีค่าชดเชย
serverTime ในทุกคำตอบ ISO-8601 ลงท้ายด้วย Z UTC
from และ to ในคำขอ YYYYMMDD ไม่มีขีด วันตามปฏิทินในเขตเวลาของร้าน
updatedSince ในคำขอ ISO-8601 เขตเวลาไหนก็ได้ ส่งเป็น Z แล้วจบเรื่อง

เพราะค่าชดเชยเดินทางมากับเวลาอยู่แล้ว คุณจึงไม่จำเป็นต้องรู้เขตเวลาของร้านเพื่อเรียงหรือเทียบบิล อ่านเป็นจุดเวลาได้เลย คุณต้องใช้เขตเวลาก็ต่อเมื่ออยากตอบว่า “บิลนี้เป็นของวันไหน” ซึ่ง API ทำให้เรียบร้อยแล้ว ให้อ่าน billDate หรือ businessDate แทนการคำนวณวันจากเวลาเอง

ช่อง ตอบคำถามว่า
billDate บิลนี้จ่ายในวันไหนตามปฏิทิน โดยดูนาฬิกาบนผนังของร้าน
businessDate บิลนี้นับเป็นยอดของวันทำการวันไหน

ร้านที่ปิดก่อนเที่ยงคืน สองค่านี้จะตรงกันเสมอ ส่วนบาร์ที่ขายถึงตีสาม สองค่านี้ต่างกันทุกคืน และนั่นคือเหตุผลทั้งหมดที่ต้องมีสองช่อง

ทุกร้านตั้งเวลาปิดรอบไว้ คือเวลาที่วันทำการหมุนไปวันใหม่ ร้านที่ตั้งเวลาปิดรอบ 06:00 น. จะถือว่าทุกบิลตั้งแต่ 06:00 น. ของวันนี้ ถึง 05:59 น. ของวันพรุ่งนี้ เป็นวันทำการเดียวกัน ซึ่งตรงกับรายงานปิดวันของร้าน ตรงกับการนับเงินในลิ้นชัก และตรงกับที่พนักงานเข้าใจ

businessDate คำนวณกติกานี้ให้คุณแล้ว คุณไม่ต้องรู้เวลาปิดรอบ ไม่ต้องไปดึงมา และไม่ต้องเขียนสูตรซ้ำเอง

สองกติกานี้ครอบคลุมทุกกรณี

  1. กรองด้วย from/to ซึ่งตรงกับ billDate ไม่มีวิธีกรองด้วยวันธุรกิจ พารามิเตอร์ช่วงวันดูที่วันตามปฏิทินเท่านั้น
  2. จัดกลุ่มด้วย businessDate ทุกครั้งที่ตัวเลขต้องตรงกับของร้าน ยอดขาย VAT ยอดปิดวัน ค่าคอมมิชชัน ทั้งหมดนี้เป็นตัวเลขระดับวันทำการ

การจับคู่แบบนี้มีผลอย่างหนึ่งที่ต้องออกแบบเผื่อไว้ คือถ้าอยากมั่นใจว่าได้บิลของวันทำการหนึ่งครบ คุณต้องดึงวันตามปฏิทินนั้น และ วันถัดไปด้วย แล้วค่อยจัดกลุ่มด้วย businessDate เพราะบิลที่จ่ายตอนตีหนึ่งของวันที่ 2 กันยายน มี billDate เป็น "2026-09-02" ทั้งที่เป็นของวันทำการ 1 กันยายน

รูปแบบที่ปลอดภัยสำหรับงานส่งออกรายคืน

  • ดึงโดยให้ from เป็นวันที่จะรายงาน และ to เป็นวันถัดไป
  • เก็บเฉพาะแถวที่ businessDate ตรงกับวันที่จะรายงาน
  • รันวันนั้นซ้ำอีกครั้งหลังเลยเวลาปิดรอบของร้านแล้ว เพราะการยกเลิกบิลย้อนหลัง และการออกใบกำกับภาษีใหม่ ทำให้บิลเปลี่ยนหลังจากนั้นได้ ซึ่งโหมดดึงเฉพาะส่วนที่เปลี่ยนใน การแบ่งหน้าและการซิงก์ จะจับให้เองอยู่แล้ว

ร้านหนึ่งอยู่ในเขตเวลา Asia/Bangkok ตั้งเวลาปิดรอบไว้ 06:00 น. มีสองบิล

จ่ายเมื่อ timestamp billDate businessDate
1 ก.ย. 13:30 น. 2026-09-01T13:30:00.000+07:00 2026-09-01 2026-09-01
2 ก.ย. 01:40 น. 2026-09-02T01:40:00.000+07:00 2026-09-02 2026-09-01

รายงานของร้านสำหรับวันที่ 1 กันยายน มีทั้งสองบิล ดังนั้น

  • เรียก from=20260901&to=20260901 จะได้ เฉพาะบิลแรก เพราะบิลที่สองมีวันตามปฏิทิน เป็นวันที่ 2 กันยายน ยอดของคุณจะขาดไป
  • เรียก from=20260901&to=20260902 จะได้ทั้งสองบิล บวกกับทุกบิลที่เป็นของวันที่ 2 กันยายนจริง ๆ แล้วกรองผลลัพธ์นั้นด้วย businessDate === "2026-09-01" จะได้ตัวเลขตรงกับของร้านพอดี

ถ้าคุณเทียบกับรายงานปิดวันของร้านแล้วขาดไปไม่กี่บิล สาเหตุมักเป็นเรื่องนี้เกือบทุกครั้ง