โมเดลข้อมูลการตั้งราคาหลายสกุลเงินสำหรับภาษีและใบแจ้งหนี้
เรียนรู้โมเดลข้อมูลการตั้งราคาหลายสกุลเงินที่จัดการอัตราแลกเปลี่ยน การปัดเศษ ภาษี และการแสดงใบแจ้งหนี้ตามท้องถิ่นโดยไม่ให้เกิดความคลาดเคลื่อน

ปัญหาที่มักเกิดกับใบแจ้งหนี้หลายสกุลเงิน
ใบแจ้งหนี้หลายสกุลเงินมักพังในทางที่น่าเบื่อและราคาแพง ตัวเลขอาจดูถูกต้องใน UI แต่พอคนส่งออก PDF ฝ่ายบัญชีนำเข้า ยอดรวมอาจไม่ตรงกับบรรทัดรายการ
สาเหตุหลักไม่ซับซ้อน: คณิตศาสตร์การเงินไม่ใช่แค่การคูณด้วยอัตราแลกเปลี่ยน ภาษี การปัดเศษ และช่วงเวลาที่คุณบันทึกอัตราทำให้ผลต่างกัน หากโครงข้อมูลราคาของคุณไม่ชี้ชัดถึงตัวเลือกเหล่านี้ ส่วนต่าง ๆ ของระบบจะ "ช่วยคำนวณ" ใหม่และได้ผลลัพธ์ไม่เหมือนกัน
ต้องมีมุมมองสามแบบที่ต้องตรงกัน แม้จะแสดงสกุลเงินต่างกัน:
- มุมมองลูกค้า: ราคาชัดเจนในสกุลเงินลูกค้า และยอดรวมบวกกันตรง
- มุมมองบัญชี: จำนวนฐานที่สอดคล้องสำหรับรายงานและการกระทบยอด
- มุมมองตรวจสอบ: ร่องรอยที่แสดงว่าใช้อัตราและกฎการปัดเศษใดในการออกใบแจ้งหนี้
ความไม่ตรงกันมักมาจากการตัดสินใจเล็ก ๆ ที่ทำในที่ต่าง ๆ ทีมหนึ่งปัดเศษแต่ละบรรทัด รายอื่นปัดเฉพาะยอดรวม หน้าหนึ่งใช้ค่าอัตราปัจจุบัน อีกหน้าหนึ่งใช้ค่าอัตราตามวันที่ใบแจ้งหนี้ ภาษีบางอย่างคำนวณก่อนส่วนลด บางอย่างหลัง ส่วนภาษีบางตัวรวมอยู่ในราคา บางตัวเพิ่มขึ้นไปด้านบน
ตัวอย่างชัดเจน: คุณขายสินาราคา 19.99 EUR แต่ส่งใบแจ้งหนี้เป็น GBP และรายงานเป็น USD ถ้าคุณแปลงต่อบรรทัดแล้วปัดเป็นทศนิยม 2 ตำแหน่ง คุณอาจได้ยอดภาษีต่างจากการรวมก่อนแล้วแปลงทีเดียว ทั้งสองวิธีเป็นไปได้ แต่ต้องเลือกวิธีใดวิธีหนึ่งเป็นกฎ
เป้าหมายคือการคำนวณที่คาดเดาได้และค่าที่เก็บไว้ชัดเจน ทุกใบแจ้งหนี้ควรตอบได้โดยไม่เดาว่า: จำนวนใดถูกป้อน สกุลเงินใดที่ป้อน อัตราใดถูกใช้ (และเมื่อใด) อะไรถูกปัด (และอย่างไร) และใช้กฎภาษีใด ความชัดเจนนี้ทำให้ยอดรวมคงที่ระหว่าง UI, PDF, การส่งออก และการตรวจสอบ
คำศัพท์สำคัญที่ควรตกลงก่อนออกแบบสคีมา
ก่อนวาดตาราง ให้แน่ใจว่าทุกคนใช้คำเดียวกัน บั๊กส่วนใหญ่ของระบบหลายสกุลเงินไม่ใช่ปัญหาทางเทคนิค แต่เป็นบั๊กแบบ "เราเข้าใจกันต่างกัน" สคีมาที่ดีเริ่มจากนิยามที่ฝ่ายผลิตภัณฑ์ การเงิน และวิศวกรรมยอมรับร่วมกัน
คำเกี่ยวกับสกุลเงินที่ส่งผลต่อฐานข้อมูล
สำหรับแต่ละการไหลของเงิน ให้ตกลงสกุลเงินสามแบบ:
- Transactional currency: สกุลที่ลูกค้าเห็นและยอมรับ (รายการราคา ตะกร้า การแสดงบนใบแจ้งหนี้)
- Settlement currency: สกุลที่คุณได้รับการชำระจริง (ที่ผู้ให้บริการชำระเงินหรือธนาคารทำการเคลียร์)
- Reporting currency: สกุลที่ใช้สำหรับแดชบอร์ดและสรุปรายการบัญชี
กำหนดด้วยว่ามี หน่วยย่อย (minor units) เท่าไร USD มี 2 (cents), JPY มี 0, KWD มี 3 สิ่งนี้สำคัญเพราะการเก็บ "12.34" เป็นจำนวนลอยตัวจะเกิดการเบี่ยงเบน ในขณะที่การเก็บเป็นจำนวนเต็มในหน่วยย่อย (เช่น 1234 เซนต์) จะคงที่และทำให้การปัดเศษเป็นไปได้คาดเดาได้
คำเกี่ยวกับภาษีที่เปลี่ยนยอดรวม
ภาษีต้องการการตกลงในระดับเดียวกัน ตัดสินใจว่าราคาเป็น รวมภาษี (tax-inclusive) หรือ ไม่รวมภาษี (tax-exclusive) และเลือกว่า ภาษีคำนวณ ต่อบรรทัด หรือ ต่อใบแจ้งหนี้ การเลือกเหล่านี้มีผลต่อการปัดเศษและอาจเปลี่ยนจำนวนที่ต้องจ่ายได้ไม่กี่หน่วยย่อย
สุดท้าย ตัดสินใจว่าสิ่งใดต้องเก็บไว้และสิ่งใดคำนวณใหม่ได้:
- เก็บสิ่งที่สำคัญทางกฎหมายและการเงิน: ราคาที่ตกลง อัตราภาษีที่ใช้ ยอดรวมหลังปัด และสกุลเงินที่ใช้
- คำนวณสิ่งที่ปลอดภัย: สตริงที่ฟอร์แมตแล้ว การแปลงเพื่อการแสดงผล และค่ากลางส่วนใหญ่
ฟิลด์เงินสดหลัก: ควรเก็บอะไรและอย่างไร
เริ่มจากการตัดสินใจว่าส่วนใดเป็นข้อเท็จจริงที่ต้องเก็บ และส่วนใดเป็นผลลัพธ์ที่คำนวณซ้ำได้ การผสมทั้งสองทำให้ใบแจ้งหนี้แสดงยอดหนึ่งในหน้าจอและยอดต่างออกไปในการส่งออก
เก็บเงินในรูปจำนวนเต็มในหน่วยย่อย (เช่น เซนต์) และเก็บรหัสสกุลเงินไว้ข้าง ๆ จำนวนเสมอ จำนวนที่ไม่มีสกุลเป็นข้อมูลไม่สมบูรณ์ จำนวนเต็มยังช่วยหลีกเลี่ยงความผิดพลาดจากทศนิยมเมื่อต้องบวกหลายบรรทัด
รูปแบบปฏิบัติได้คือเก็บทั้งค่าป้อนเข้าดิบและผลลัพธ์ที่คำนวณ ค่าป้อนเข้าอธิบายสิ่งที่ผู้ใช้กรอก ผลลัพธ์อธิบายสิ่งที่คุณเรียกเก็บ เมื่อมีข้อพิพาทต่อใบแจ้งหนี้เดือนต่อมา คุณต้องมีทั้งสอง
สำหรับบรรทัดใบแจ้งหนี้ ชุดฟิลด์ที่ชัดเจนและทนทานมีลักษณะดังนี้:
unit_price_minor+unit_currencyquantity(และuomหากจำเป็น)line_subtotal_minor(ก่อนภาษี/ส่วนลด)line_discount_minorline_tax_minor(หรือแยกตามประเภทภาษี)line_total_minor(ยอดสุดท้ายของบรรทัด)
การปัดเศษไม่ใช่แค่รายละเอียดของ UI ควรเก็บวิธีการปัดและความละเอียดที่ใช้สำหรับการคำนวณ โดยเฉพาะหากรองรับสกุลที่มีหน่วยย่อยต่างกัน (JPY vs USD) หรือกฎการปัดสำหรับเงินสด บันทึก "บริบทการคำนวณ" เล็ก ๆ เช่น calc_precision, rounding_mode, และว่าปัดเศษต่อบรรทัดหรือเฉพาะยอดรวมใบแจ้งหนี้
แยกการฟอร์แมตสำหรับการแสดงผลออกจากค่าที่เก็บ ค่าที่เก็บควรเป็นตัวเลขและรหัสล้วน ๆ; การฟอร์แมต (สัญลักษณ์สกุลเงิน ตัวย่อท้องถิ่น รูปแบบตัวเลข) อยู่ในเลเยอร์การนำเสนอ เช่น เก็บ 12345 + EUR แล้วให้ UI ตัดสินใจจะแสดงเป็น "€123.45" หรือ "123,45 €"
อัตราแลกเปลี่ยน: ตาราง เวลา และร่องรอยการตรวจสอบ
ปฏิบัติต่ออัตราแลกเปลี่ยนเป็นข้อมูลตามเวลาและมีแหล่งที่มา "อัตราของวันนี้" ไม่ใช่สิ่งที่คุณจะคำนวณใหม่ได้อย่างปลอดภัยในภายหลัง
ตารางอัตราที่ปฏิบัติได้มักรวมถึง:
base_currency(แปลงจาก เช่น USD)quote_currency(แปลงเป็น เช่น EUR)rate(อัตราต่อ 1 base เก็บเป็นทศนิยมความละเอียดสูง)effective_at(เวลาเริ่มมีผลของอัตรา)source(ผู้ให้บริการ) และsource_ref(รหัสจากผู้ให้บริการหรือแฮช payload)
ข้อมูลแหล่งที่มานี้สำคัญในการตรวจสอบ หากลูกค้าโต้แย้งจำนวน คุณสามารถชี้ให้เห็นแหล่งที่มาของตัวเลขได้อย่างชัดเจน
จากนั้น ให้เลือกกฎเดียวสำหรับอัตราที่ใช้กับใบแจ้งหนี้ แล้วยึดตามนั้น ตัวเลือกทั่วไปคืออัตราตามเวลาสั่งซื้อ เวลาจัดส่ง หรือเวลาที่ออกใบแจ้งหนี้ ทางที่ดีที่สุดขึ้นกับธุรกิจของคุณ สิ่งสำคัญคือความสม่ำเสมอและการจัดทำเอกสาร
ไม่ว่าจะเลือกกฎใด ให้เก็บอัตราที่ใช้บนใบแจ้งหนี้ (และมักบนแต่ละบรรทัดของใบแจ้งหนี้) อย่าพึ่งพาการค้นหาใหม่ในภายหลัง เพิ่มฟิลด์เช่น fx_rate, fx_rate_effective_at, และ fx_rate_source เพื่อให้สามารถทำซ้ำใบแจ้งหนี้ได้อย่างแม่นยำ
สำหรับกรณีอัตราหายไป (วันหยุดสุดสัปดาห์ วันหยุดราชการ หรือผู้ให้บริการล่ม) ให้กำหนดพฤติกรรม fallback ชัดเจน แนวทางทั่วไปเช่น: ใช้อัตราล่าสุดก่อนหน้า หยุดการออกใบแจ้งหนี้จนกว่าอัตราจะพร้อม หรืออนุญาตให้อัตราแบบแมนนวลพร้อมธงอนุมัติ
ตัวอย่าง: คำสั่งซื้อเกิดขึ้นในวันเสาร์ จัดส่งวันจันทร์ ออกใบแจ้งหนี้วันจันทร์ ถ้ากฎของคุณคือเวลาที่ออกใบแจ้งหนี้แต่ผู้ให้บริการไม่เผยแพร่อัตราช่วงสุดสัปดาห์ คุณอาจใช้ค่าอัตราของวันศุกร์และบันทึก effective_at = Friday 23:59 พร้อม source_ref เพื่อการตรวจสอบ
การแปลงสกุลเงินและกฎการปัดเศษที่ต้องคงที่
ปัญหาการปัดเศษมักไม่ชัดเจนในรูปแบบบั๊ก แต่แสดงเป็นช่องว่าง 1 เซนต์ระหว่างยอดรวมใบแจ้งหนี้กับผลรวมบรรทัด หรือต่างกันเล็กน้อยระหว่างที่แสดงกับที่ผู้ให้บริการรับชำระคาดหวัง แบบจำลองที่ดีทำให้การปัดเป็นกฎที่อธิบายได้ ไม่ใช่เรื่องประหลาดที่ค่อยแก้ทีหลัง
ตัดสินใจอย่างชัดเจนว่าปัดเศษที่จุดไหน
เลือกจุดที่อนุญาตให้ปัดเศษ และเก็บค่าที่เหลือไว้ความละเอียดสูง จุดปัดเศษทั่วไปได้แก่:
- การคูณราคาต่อหน่วยด้วยจำนวน (line extension) หลังหักส่วนลด
- จำนวนภาษีแต่ละรายการ (ต่อบรรทัดหรือต่อใบแจ้งหนี้ ขึ้นกับข้อบังคับ)
- ยอดรวมสุดท้ายของใบแจ้งหนี้
ถ้าไม่กำหนดจุดเหล่านี้ ส่วนต่าง ๆ ของระบบจะปัดเมื่อสะดวก และยอดจะเบี่ยงเบน
ใช้โหมดการปัดเดียว โดยมีข้อยกเว้นชัดเจนสำหรับกฎภาษี
เลือกโหมดการปัด (half-up หรือ bankers rounding) แล้วใช้ให้สอดคล้อง Half-up เข้าใจง่ายสำหรับลูกค้า Bankers rounding ลดอคติเมื่อมีปริมาณมาก ทั้งสองใช้ได้ แต่ API, UI, การส่งออก และรายงานบัญชีต้องใช้โหมดเดียวกัน
เก็บความละเอียดเพิ่มตอนแปลงและขั้นกลาง (เช่น เก็บอัตรา FX หลายตำแหน่งทศนิยม) แล้วปัดเฉพาะที่จุดที่กำหนด
ส่วนลดก็ต้องมีกฎเดียว: หักส่วนลดก่อนภาษี (ทั่วไปสำหรับคูปอง) หรือหลังภาษี (บางค่าธรรมเนียมต้องเป็นแบบนั้น) เขียนกฎและเข้ารหัสครั้งเดียว
บางเขตอำนาจต้องปัดภาษีต่อบรรทัด ต่อภาษี หรือยอดรวมใบแจ้งหนี้ แทนที่จะกระจายเงื่อนไขพิเศษทั่วฐานโค้ด ให้เก็บการตั้งค่า "นโยบายการปัด" (ตามประเทศ/รัฐ/ระบบภาษี) แล้วให้การคำนวณปฏิบัติตามนโยบาย
การตรวจสอบง่าย ๆ: ถ้าคุณสร้างซ้ำใบแจ้งหนี้เดิมพรุ่งนี้ด้วยอัตราและนโยบายที่เก็บไว้ คุณควรได้จำนวนเซนต์เดียวกันทุกครั้ง
ฟิลด์ภาษี: แบบแผนสำหรับ VAT, sales tax และภาษีหลายรายการ
ภาษีซับซ้อนเร็วเพราะขึ้นกับที่อยู่ผู้ซื้อ สินค้าที่ขาย และการแสดงราคาว่าเป็น net หรือ gross แบบจำลองที่ดีทำให้ภาษีชัดเจน ไม่ใช่สิ่งที่ซ่อนอยู่
ทำให้ฐานภาษีไม่กำกวม เก็บว่าราคาที่คำนวณภาษีนั้นเป็น net (บวกภาษีด้านบน) หรือ gross (รวมภาษี) แล้วเก็บทั้งอัตราที่ใช้และจำนวนภาษีที่คำนวณเป็น snapshot เพื่อให้การเปลี่ยนแปลงกฎภายหลังไม่เขียนทับประวัติ
ในแต่ละบรรทัดใบแจ้งหนี้ ชุดขั้นต่ำที่ชัดเจนสำหรับใช้งานระยะยาว:
tax_basis(NET หรือ GROSS)tax_rate(ทศนิยม เช่น 0.20)taxable_amount_minor(ฐานที่คำนวณภาษี)tax_amount_minortax_method(PER_LINE หรือ ON_SUBTOTAL)
ถ้ามีภาษีมากกว่าหนึ่งตัว ให้เพิ่มตารางแจกแจง เช่น InvoiceLineTax มีหนึ่งแถวต่อภาษีที่ใช้ แต่ละแถวควรรวม tax_code, tax_rate, taxable_amount_minor, tax_amount_minor, สกุลเงิน และเบาะแสเขตอำนาจที่ใช้ขณะคำนวณ (ประเทศ ภูมิภาค รหัสไปรษณีย์เมื่อจำเป็น)
เก็บ snapshot ของรายละเอียดกฎที่ใช้บนใบแจ้งหนี้หรือบรรทัด เช่น rule_version หรือ JSON blob ของข้อมูลตัดสินใจ (สถานะภาษีลูกค้า การโอนภาระภาษี การยกเว้น) ถ้ากฎ VAT เปลี่ยนในปีหน้า ใบแจ้งหนี้เก่า ๆ ควรยังตรงกับสิ่งที่เรียกเก็บจริง
ตัวอย่าง: การสมัครใช้งาน SaaS ขายให้ลูกค้าในเยอรมนีอาจมี VAT 19% บนราคาสุทธิของบรรทัด บวกภาษีท้องถิ่น 1% เก็บยอดบรรทัดตามที่เรียกเก็บและเก็บแถวแจกแจงสำหรับแต่ละภาษีเพื่อการแสดงและการตรวจสอบ
วิธีออกแบบตารางทีละขั้นตอน
นี่ไม่ใช่เรื่องคณิตศาสตร์ชาญฉลาด แต่เป็นการแช่แข็งข้อเท็จจริงที่ถูกต้องในเวลาที่เหมาะสม เป้าหมายคือสามารถเปิดใบแจ้งหนี้เดือนต่อมาแล้วยังเห็นตัวเลขเดิม
เริ่มจากตัดสินใจว่าจุดใดเป็นแหล่งความจริงสำหรับราคาสินค้า หลายทีมเก็บราคาพื้นฐานในสกุลฐานของสินค้า และอาจมี override แยกตามตลาด (เช่น แถวราคาต่างกันสำหรับ USD และ EUR) ไม่ว่าจะเลือกวิธีใด ให้ระบุชัดเจนในสคีมาเพื่อไม่สับสนระหว่าง "ราคาจากแคตาล็อก" กับ "ราคาที่ถูกแปลง"
ลำดับง่าย ๆ ที่ทำให้ตารางเข้าใจได้:
- Products and pricing:
product_id,price_amount_minor,price_currency,effective_from(เมื่อราคามีการเปลี่ยน) - Order and invoice headers:
document_currency,customer_locale,billing_country, และ timestamps (issued_at,tax_point_at) - Line items:
unit_price_amount_minor,quantity,discount_amount_minor,tax_amount_minor,line_total_amount_minor, และสกุลเงินสำหรับทุกฟิลด์ที่เก็บเป็นเงิน - Exchange rate snapshot: อัตราที่แน่นอนที่ใช้ (
rate_value,rate_provider,rate_timestamp) อ้างอิงจากคำสั่งซื้อหรือใบแจ้งหนี้ - Tax breakdown records: แถวต่อภาษี (
tax_type,rate_percent,taxable_base_minor,tax_amount_minor) พร้อมแฟลกcalculation_method
อย่าพึ่งพาการคำนวณใหม่ในภายหลัง เมื่อคุณสร้างใบแจ้งหนี้ ให้คัดลอกราคาสุทธิ ส่วนลด และยอดสุดท้ายลงในบรรทัดของใบแจ้งหนี้แม้มาจากคำสั่งซื้อ
เพื่อการตรวจสอบ ให้เพิ่ม calculation_version (หรือ calc_hash) บนใบแจ้งหนี้ และตาราง calculation_log เล็ก ๆ ที่บันทึกผู้ที่ทริกเกอร์การคำนวณใหม่และเหตุผล (เช่น "อัปเดตอัตราก่อนออกใบแจ้งหนี้")
การแสดงใบแจ้งหนี้แบบท้องถิ่นโดยไม่ทำให้ตัวเลขเปลี่ยน
การท้องถิ่น (localization) ควรเปลี่ยนแค่รูปลักษณ์ของใบแจ้งหนี้ ไม่ใช่ความหมาย ให้ทำการคำนวณทั้งหมดโดยใช้ค่าตัวเลขที่เก็บไว้ (หน่วยย่อยหรือทศนิยมความแม่นยำคงที่) แล้วค่อยฟอร์แมตตาม locale ที่สุดท้าย
เก็บการตั้งค่าการแสดงผลบนใบแจ้งหนี้เอง ไม่ใช่แค่ในโปรไฟล์ลูกค้า ลูกค้าเปลี่ยนประเทศ ผู้ติดต่อ และการตั้งค่าไปตามเวลา ใบแจ้งหนี้เป็นสแนปชอตทางกฎหมาย เก็บเช่น invoice_language, invoice_locale, และแฟลกการฟอร์แมต (เช่น จะแสดงทศนิยมตามท้ายหรือไม่) กับเอกสาร เพื่อให้การพิมพ์ใหม่ในหกเดือนตรงกับของเดิม
สัญลักษณ์สกุลเงินเป็นเรื่องการแสดง บาง locale วางสัญลักษณ์หน้าจำนวน บางที่วางข้างหลัง บางที่เว้นวรรค จัดการตำแหน่งสัญลักษณ์ ช่องว่าง ตัวคั่นทศนิยม และการจัดกลุ่มพันตามเวลาที่เรนเดอร์ อย่าฝังสัญลักษณ์ลงในฟิลด์เงินที่เก็บ และอย่าแปลงสตริงที่ฟอร์แมตกลับเป็นตัวเลข
ถ้าต้องการรายงานเป็นสกุลที่สอง (มักเป็นสกุลฐานอย่าง USD หรือ EUR) ให้แสดงเป็นยอดรองที่ระบุแหล่งที่มาและเวลา อย่าแทนที่สกุลเอกสาร สกุลเอกสารคือต้นทุนทางกฎหมาย
การตั้งค่าปฏิบัติสำหรับเอาท์พุตใบแจ้งหนี้:
- แสดงบรรทัดและยอดรวมในสกุลเงินเอกสาร โดยใช้การฟอร์แมตตาม
invoice-locale - แสดงยอดรองสำหรับการรายงานโดยระบุแหล่งอัตราและเวลา (ถ้าจำเป็น)
- แสดงแจกแจงภาษีเป็นบรรทัดแยก (ฐานที่ต้องเสียภาษี แต่ละภาษี ยอดรวมภาษี) ไม่ใช่ยอดผสมเดียว
- สร้าง PDF และอีเมลจากยอดที่เก็บไว้เดียวกันเพื่อป้องกันการเบี่ยง
ตัวอย่าง: ลูกค้าชาวฝรั่งเศสถูกเรียกเก็บเป็น CHF locale ใบแจ้งหนี้ใช้จุดทศนิยมเป็นจุลภาคและวางสกุลเงินข้างหลัง แต่การคำนวณยังใช้จำนวน CHF ที่เก็บและยอดภาษีที่เก็บ ผลลัพธ์ที่ฟอร์แมตเปลี่ยนได้ แต่วงจรตัวเลขไม่เปลี่ยน
ข้อผิดพลาดและกับดักที่พบบ่อย
วิธีที่เร็วที่สุดในการทำให้ใบแจ้งหนี้หลายสกุลเงินพังคือถือว่าเงินเป็นตัวเลขปกติ การใช้ float สำหรับราคา ภาษี และยอดรวมสร้างความคลาดเคลื่อนเล็ก ๆ ที่ปรากฏเป็นปัญหา "ผิดพลาด $0.01" เก็บจำนวนเป็นจำนวนเต็มในหน่วยย่อย (เซนต์) หรือใช้ทศนิยมคงที่ที่มีสเกลชัดเจน แล้วใช้ให้สอดคล้อง
กับดักคลาสสิกอีกอย่างคือการเปลี่ยนประวัติผิดพลาด ถ้าคุณคำนวณใบแจ้งหนี้เก่าด้วยอัตราแบบวันนี้หรือด้วยกฎภาษีที่อัปเดต คุณจะไม่เหลือเอกสารที่ลูกค้าเห็นและจ่ายจริง ใบแจ้งหนี้ควรไม่เปลี่ยน: เมื่อออกแล้ว ให้เก็บอัตราแลกเปลี่ยน กฎการปัด และวิธีภาษีที่ใช้ และอย่าคำนวณยอดที่เก็บใหม่
การผสมสกุลเงินภายในบรรทัดเดียวก็เป็นบั๊กสคีมาเงียบ ๆ ถ้าราคาต่อหน่วยเป็น EUR ส่วนลดเป็น USD และภาษีคำนวณเป็น GBP คุณไม่สามารถอธิบายคณิตศาสตร์ได้ในภายหลัง เลือกสกุลเอกสารหนึ่งสำหรับการแสดงและการชำระ และอาจมีสกุลฐานหนึ่งสำหรับการรายงานภายใน ทุกจำนวนที่เก็บควรมีสกุลที่ชัดเจนเสมอ
ความผิดพลาดเกี่ยวกับการปัดมักมาจากการปัดบ่อยเกินไป ถ้าคุณปัดที่ราคาต่อหน่วย แล้วอีกทีที่ยอดบรรทัด แล้วอีกทีที่ภาษีต่อบรรทัด แล้วรวมอีกครั้ง ยอดอาจไม่ตรงกับผลรวมบรรทัด
กับดักที่ควรระวัง:
- ใช้ float สำหรับเงินหรืออัตราแลกเปลี่ยนโดยไม่กำหนดความละเอียดคงที่
- คำนวณใบแจ้งหนี้เก่าใหม่ด้วยอัตราปัจจุบันแทนการใช้ค่าอัตราที่เก็บไว้
- อนุญาตให้บรรทัดเดียวมีจำนวนในหลายสกุลเงิน
- ปัดเศษในหลายขั้นตอนแทนปัดที่จุดที่กำหนด
- ไม่เก็บเวลาอัตรา โหมดการปัด และวิธีภาษีต่อเอกสาร
ตัวอย่าง: คุณสร้างใบแจ้งหนี้เป็น CAD แปลงบริการที่ตั้งราคาตาม EUR แล้วอัปเดตตารางอัตรา ถ้าคุณเก็บเพียงจำนวน EUR แล้วแปลงตอนแสดง ยอด CAD จะเปลี่ยนในสัปดาห์หน้า เก็บจำนวน EUR จำนวนอัตรา FX ที่ใช้และเวลาที่ใช้ และยอด CAD สุดท้ายที่ใช้ในใบแจ้งหนี้
เช็คลิสต์ด่วนก่อนส่งมอบ
ก่อนจะบอกว่าใบแจ้งหนี้หลายสกุลเงิน "เรียบร้อย" ให้ตรวจความสอดคล้องอีกครั้ง บั๊กส่วนใหญ่ไม่ซับซ้อน มาจากความไม่ตรงกันระหว่างสิ่งที่เก็บ สิ่งที่แสดง และสิ่งที่รวม
ใช้รายการนี้เป็นเกตการปล่อย:
- แต่ละใบแจ้งหนี้มีสกุลเงินเอกสารเพียงหนึ่งค่าในเฮดเดอร์ และยอดรวมที่เก็บทั้งหมดบนใบแจ้งหนี้เป็นสกุลนั้น
- ทุกค่าทางการเงินที่เก็บเป็นจำนวนเต็มในหน่วยย่อย รวมยอดบรรทัด ยอดภาษี ส่วนลด และค่าจัดส่ง
- ใบแจ้งหนี้เก็บอัตราแลกเปลี่ยนที่ใช้จริง (เป็นทศนิยมความละเอียดสูง) พร้อมเวลาและแหล่งที่มา
- กฎการปัดถูกระบุและนำไปใช้จากที่เดียวร่วมกัน
- ถ้ามีภาษีมากกว่าหนึ่งตัว คุณเก็บแจกแจงภาษีต่อบรรทัด (และถ้าต้องการต่อเขตอำนาจ) ไม่ใช่แค่ยอดภาษีรวมบนเฮดเดอร์
หลังสคีมาตรวจสอบแล้ว ให้ตรวจคณิตศาสตร์แบบผู้ตรวจสอบ ยอดรวมใบแจ้งหนี้ควรเท่ากับผลรวมของยอดบรรทัดที่เก็บและยอดภาษีที่เก็บ อย่าคำนวณยอดรวมจากค่าที่แสดงหรือสตริงที่ฟอร์แมตแล้ว
การทดสอบปฏิบัติ: เลือกใบแจ้งหนี้หนึ่งใบมีอย่างน้อยสามบรรทัด ใส่ส่วนลด และมีสองภาษีในบรรทัดเดียว แล้วพิมพ์ใน locale อื่น (ตัวคั่นต่างกันและสัญลักษณ์สกุลเงินต่างกัน) ยืนยันว่าตัวเลขที่เก็บไม่เปลี่ยน
ตัวอย่างสถานการณ์: คำสั่งเดียว สามสกุลเงิน และภาษี
ลูกค้าสหรัฐฯ ถูกเรียกเก็บเป็น USD ซัพพลายเออร์ใน EU คิดราคาเป็น EUR และทีมการเงินของคุณรายงานเป็น GBP นี่คือจุดที่แบบจำลองจะสงบหรือกลายเป็นกองปัญหา 1 เซนต์
คำสั่งซื้อ: 3 หน่วยของสินค้า
- ราคาลูกค้า: $19.99 ต่อหน่วย (USD)
- ส่วนลด: 10% ต่อบรรทัด
- ภาษีการขายของสหรัฐฯ: 8.25% (คิดหลังหักส่วนลด)
- ต้นทุนซัพพลายเออร์: EUR 12.40 ต่อหน่วย (EUR)
- สกุลเงินรายงาน: GBP
ลำดับเหตุการณ์: เกิดอะไรขึ้นและเมื่อไหร่ที่คุณแปลง
เลือกเวลาการแปลงครั้งเดียวแล้วยึดตามมัน ในระบบออกใบแจ้งหนี้หลายแห่ง การเลือกที่ปลอดภัยคือแปลงเมื่อออกใบแจ้งหนี้ แล้วเก็บอัตราที่ใช้
เมื่อสร้างใบแจ้งหนี้:
- คำนวณยอดย่อยบรรทัดเป็น USD: 3 x 19.99 = 59.97 USD.
- หักส่วนลด: 59.97 x 10% = 5.997 ปัดเป็น 6.00 USD.
- ยอดสุทธิของบรรทัด: 59.97 - 6.00 = 53.97 USD.
- ภาษี: 53.97 x 8.25% = 4.452525 ปัดเป็น 4.45 USD.
- ยอดรวม: 53.97 + 4.45 = 58.42 USD.
การปัดเกิดขึ้นเฉพาะที่จุดที่กำหนด (ส่วนลด จำนวนภาษีแต่ละรายการ ยอดบรรทัด) เก็บผลเหล่านั้นที่ปัดแล้ว และรวมค่าที่เก็บ วิธีนี้ป้องกันปัญหาคลาสสิกที่ PDF แสดง 58.42 แต่การส่งออกคำนวณเป็น 58.43
สิ่งที่ควรเก็บเพื่อสร้างใบแจ้งหนี้ซ้ำได้
บนใบแจ้งหนี้ (และบรรทัด) ให้เก็บรหัสสกุลเงิน (USD) จำนวนในหน่วยย่อย (เซนต์) แจกแจงภาษีต่อภาษี และ ID ระเบียนอัตราแลกเปลี่ยนที่ใช้แปลง USD เป็น GBP สำหรับการรายงาน สำหรับต้นทุนของซัพพลายเออร์ ให้เก็บต้นทุน EUR และระเบียนอัตราของมันถ้าคุณแปลงต้นทุนเป็น GBP ด้วยเช่นกัน
ลูกค้าเห็นใบแจ้งหนี้ USD ที่ชัดเจน (ราคา ส่วนลด ภาษี ยอดรวม) ฝ่ายการเงินส่งออกจำนวน USD พร้อมเทียบเท่า GBP ที่ถูกล็อกและเวลาอัตรา เพื่อให้ตัวเลขสิ้นเดือนตรงแม้อัตราจะเปลี่ยนพรุ่งนี้
ขั้นตอนถัดไป: นำไปใช้ ทดสอบ และรักษาความสามารถในการดูแล
เขียนสคีมาขั้นต่ำของคุณเป็นสัญญาสั้น ๆ: จำนวนใดเก็บ (ต้นฉบับ แปลง ภาษี) สกุลเงินที่แต่ละจำนวนอยู่ใน กฎการปัดที่ใช้ และเวลาที่ล็อกอัตราสำหรับใบแจ้งหนี้ ทำให้มันน่าเบื่อและเฉพาะเจาะจง
ก่อนสร้างหน้าจอ UI ให้สร้างเทสต์ อย่าทดสอบเฉพาะใบแจ้งหนี้ปกติ เพิ่มกรณีขอบที่เล็กพอจะเปิดเผยเสียงรบกวนของการปัดและใหญ่พอจะเปิดเผยปัญหาการรวม
ชุดกรณีทดสอบเริ่มต้น:
- ราคาต่อหน่วยเล็กมาก (เช่น 0.01) คูณด้วยจำนวนมาก
- ส่วนลดที่ทำให้เกิดทศนิยมซ้ำหลังการแปลง
- การเปลี่ยนแปลงอัตราแลกเปลี่ยนระหว่างวันที่สั่งซื้อและวันที่ออกใบแจ้งหนี้
- กฎภาษีผสม (รวมภาษีกับไม่รวมภาษี) ในประเภทใบแจ้งหนี้เดียวกัน
- การคืนเงินและบันทึกเครดิตที่ต้องตรงกับการปัดเดิม
เพื่อลดเวลาตอบคำถามจากลูกค้า ให้เพิ่มมุมมอง audit ที่อธิบายทุกตัวเลขบนใบแจ้งหนี้: จำนวนที่เก็บ รหัสสกุลเงิน ID อัตราและเวลา และวิธีการปัด เมื่อมีคนถามว่า "ทำไมยอดนี้ต่างกัน?" คุณสามารถตอบจากข้อเท็จจริงที่เก็บไว้
ถ้าคุณกำลังสร้างเครื่องมือเรียกเก็บเงินภายใน แพลตฟอร์ม no-code อย่าง AppMaster (appmaster.io) สามารถช่วยให้รักษาความสอดคล้องได้โดยการเก็บสคีมาในที่เดียวและตรรกะการคำนวณไว้ในเวิร์กโฟลว์ที่นำกลับมาใช้ได้ เพื่อให้หน้าบนเว็บและมือถือไม่ต้องทำคำนวณแยกกัน
สุดท้าย กำหนดความเป็นเจ้าของ ตัดสินใจว่าใครอัปเดตอัตราใครอัปเดตกฎภาษี และใครอนุมัติการเปลี่ยนที่มีผลต่อใบแจ้งหนี้ที่ออก ความเสถียรเป็นกระบวนการ ไม่ใช่แค่สคีมา


