top of page
ค้นหา

เมื่อส่วนลดติดลบในใบลดหนี้

รูปภาพนักเขียน: Sathit Jittanupat
Sathit Jittanupat
11 นาทีที่ผ่านมา
ยาว 2 นาที

โปรแกรมคำนวณถูก แต่ทำไมคนถึงบอกว่าผิด?



มีเรื่องเล็ก ๆ จากการทำ ERP ที่ผมเพิ่งนึกขึ้นมาได้ว่า

บางทีคนเขียนโปรแกรมกับคนทำธุรกิจ อาจกำลัง “อ่านตัวเลข” คนละแบบกัน


เป็นเรื่องของใบลดหนี้ครับ


"ลดหนี้ หมายถึง ลดมูลค่าเงินที่เป็นหนี้ต่อกันทางบัญชี 


แต่สาเหตุของการลดหนี้ โดยทั่วไปเป็นไปได้สองกรณี 


“ราคาสินค้าลดลง” เป็นการคืนเงินจากส่วนต่างมูลค่าจากราคาที่เปลี่ยน 

“จำนวนสินค้าลดลง” หรือ “คืนสินค้า” เป็นการคืนเงินจากส่วนต่างมูลค่าจากจำนวนที่เปลี่ยน แล้วกระทบไปสู่สินค้าคงเหลือที่เปลี่ยนด้วย


ลองสมมติว่ามีการขายสินค้า


ราคาชิ้นละ 1,000 บาท จำนวน 2 ชิ้น ส่วนลด 45 บาท


ดังนั้น

1,000 × 2 − 45 = 1,955 บาท

ทุกคนเข้าใจตรงกัน

โปรแกรมเมอร์ก็เข้าใจ นักบัญชีก็เข้าใจ คนขายก็เข้าใจ

ไม่มีอะไรซับซ้อน


ทีนี้ลูกค้าคืนสินค้าทั้งบิล

ERP ก็ต้องออกใบลดหนี้

ในระบบ เราใช้วิธีบันทึกจำนวนสินค้าเป็นค่าลบ


จากเดิม

จำนวน = 2

กลายเป็น

จำนวน = -2

วิธีนี้มีเหตุผลอยู่แล้ว เพราะมันทำให้ transaction ของสต็อกเดินตามธรรมชาติ


ขายไป 2

+2

คืนกลับ 2

-2

สุดท้ายเหลือศูนย์


เราไม่จำเป็นต้องสร้าง flag พิเศษขึ้นมาอีกว่า “รายการนี้เป็นการคืนสินค้า” เพราะตัวเลข -2 เองก็บอกเรื่องราวของมันได้


ผมชอบแนวคิดนี้นะครับ

เพราะ ERP เป็นระบบที่ตัวเลขสามารถเชื่อมโยงกันไปทั้งระบบได้


แต่ปัญหาเกิดขึ้นตรงส่วนลด 45 บาท


เมื่อจำนวนกลายเป็น -2 โปรแกรมก็ยังใช้สูตรเดิม

1,000 × -2 − 45

คำตอบคือ

-2,045 บาท

ตอนเห็นครั้งแรกก็ต้องหยุดนิดหนึ่ง


เพราะถ้าถามคนทั่วไปว่า

“บิลเดิม 1,955 บาท แล้วคืนสินค้าทั้งบิล ใบลดหนี้ควรเป็นเท่าไหร่?”


คำตอบน่าจะเป็น

-1,955 บาท

ไม่มีใครน่าจะตอบว่า

-2,045 บาท

ทั้งที่โปรแกรมก็ไม่ได้คำนวณผิด



ความแปลกอยู่ตรงไหน?


ถ้ามองเฉพาะคณิตศาสตร์ สูตรนี้ถูกต้อง

1,000 × -2 − 45 = -2,045

แต่ถ้ามองว่าใบลดหนี้กำลัง “ย้อนกลับ” บิลเดิม เราอาจกำลังคิดอีกแบบ


บิลเดิมคือ

(1,000 × 2) − 45= 1,955

ถ้าย้อนกลับทั้งรายการ ก็เป็น

-(1,955)= -1,955

หรือถ้าแยกเป็นรายการ

ราคาสินค้า      +2,000
ส่วนลด             -45
-----------------------
สุทธิ             1,955

เมื่อ reverse

ราคาสินค้า      -2,000
ส่วนลด             +45
-----------------------
สุทธิ            -1,955

จุดที่น่าสนใจจึงไม่ได้อยู่ที่เครื่องหมาย -


แต่เป็นคำถามว่า

เราเอาเครื่องหมายลบไปใส่ตรงไหน และกำลังบอกอะไรด้วยเครื่องหมายนั้น?



เพราะ “ส่วนลด” อาจไม่ได้หมายถึงสิ่งเดียวกับที่โปรแกรมคิด


เวลาผมเขียนโปรแกรม ผมอาจมองว่า

ส่วนลด 45 บาท = ตัวเลข 45 ที่ต้องนำไปลบ

มันจึงเป็นเรื่องธรรมดามากที่จะเขียน

net = price * qty - discount

แต่คนทำธุรกิจอาจไม่ได้มองส่วนลดแบบนั้น

เขาอาจมองว่า

บิลนี้เคยให้ส่วนลดไป 45 บาท

เมื่อมีการยกเลิกหรือคืนรายการเดิม สิ่งที่กำลังทำจึงไม่ใช่ “ลดอีก 45 บาท”

แต่เป็น

“ย้อนกลับสิ่งที่เคยทำไป”

ดังนั้นส่วนลดเดิมที่เคยทำให้ยอดลดลง 45 บาท เมื่อ reverse กลับต้องกลายเป็นการเพิ่ม 45 บาท

ตรงนี้เป็นเรื่องของความหมาย มากกว่าคณิตศาสตร์



ที่น่าสนใจคือ มันไม่ได้เกิดขึ้นบ่อย


ถ้าเป็นส่วนลด 10%

อาจไม่เกิดปัญหาแบบนี้


ถ้าเป็นการลดราคาต่อหน่วย ก็อาจไม่เกิดปัญหาแบบนี้


แต่กรณีนี้เป็นส่วนลดแบบจำนวนเงินที่ใส่ หลังจากคำนวณราคาคูณจำนวนแล้ว

เช่น

2,000 − 45

แล้วบังเอิญรายการนี้ถูกนำไปทำใบลดหนี้โดยเปลี่ยนจำนวนเป็น -2

จึงเกิดกรณีประหลาดขึ้นมา


มันเป็น edge case ที่ค่อนข้างเฉพาะตัว


ใบลดหนี้ก็ไม่ได้เกิดทุกวันเหมือนใบขาย

คืนสินค้าทั้งบิลก็ไม่ได้เกิดทุกวัน

และต้องบังเอิญมีส่วนลดแบบจำนวนเงินหลังคูณจำนวนอีก


ระบบจึงอาจทำงานมาเป็นเวลานานโดยไม่มีใครพบเรื่องนี้

ผมก็ไม่เคยเจอ

จนกระทั่งวันหนึ่ง มีข้อมูลชุดนี้เข้ามา

แล้วตัวเลขก็ฟ้องขึ้นมาว่า

“เดี๋ยวนะ… ทำไมคืนของ 1,955 บาท กลายเป็นลดหนี้ 2,045 บาท?”


ผมว่าโปรแกรมเมอร์ไม่ได้ผิด


จริง ๆ แล้วผมไม่ค่อยอยากเรียกเรื่องนี้ว่า bug ของโปรแกรมเมอร์ด้วยซ้ำ


เพราะถ้าเอาโจทย์มาให้ตั้งแต่แรกว่า

ราคา 1,000 จำนวน -2 ส่วนลด 45 สูตรคือ ราคา × จำนวน − ส่วนลด

โปรแกรมเมอร์ก็คงตอบ

-2,045

ไม่มีเหตุผลที่จะตอบอย่างอื่น


สิ่งที่หายไปในโจทย์ ไม่ใช่ตัวเลข

แต่คือ ความหมายของรายการ


-2 อาจไม่ได้หมายถึงเพียง “จำนวนติดลบ 2”

แต่มันอาจหมายถึง

“คืนสินค้าที่เคยขายไป 2 ชิ้น”

และเมื่อมองอย่างนั้น

ใบลดหนี้ก็อาจไม่ได้เป็น transaction ใหม่ที่เอาค่าลบไปใส่ในสูตรเดิมทั้งหมด


แต่มันคือการ

ย้อนกลับผลของ transaction เดิม

ซึ่งเป็นคนละวิธีคิดกัน



แล้วผมก็ลองแก้ปัญหาแบบไม่เพิ่ม flag


หลังจากเจอเคสนี้ ผมปรับวิธีคำนวณส่วนลดใหม่

ไม่ได้เพิ่ม flag ว่า

ถ้าเป็นใบลดหนี้ ให้คำนวณแบบนี้

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


ผมลองแยก “เครื่องหมาย” ออกจาก “มูลค่าที่จะคำนวณ”

ขั้นตอนจึงประมาณนี้


  1. ดูก่อนว่าค่าตั้งต้นเป็นบวกหรือลบ

  2. เอาค่าสัมบูรณ์มาใช้คำนวณส่วนลด

  3. คำนวณด้วยตรรกะเดิม

  4. ถ้าค่าตั้งต้นเป็นลบ ก็กลับเครื่องหมายของผลลัพธ์กลับไป


กรณีเดิมจึงกลายเป็น

ค่าตั้งต้น = 1,000 × -2
           = -2,000

เอาเฉพาะมูลค่ามาคำนวณ

abs(-2,000) − 45
= 2,000 − 45
= 1,955

แล้วคืนเครื่องหมายเดิม

-1,955

ผลลัพธ์จึงตรงกับสิ่งที่เราต้องการ


และที่ผมชอบคือ เราไม่ได้บอกโปรแกรมว่า

“นี่คือใบลดหนี้นะ”

เราเพียงบอกมันว่า

“ค่าตั้งต้นนี้มีทิศทางเป็นบวกหรือลบ”

จากนั้นคำนวณ “มูลค่า” แยกออกจาก “ทิศทาง” แล้วค่อยนำกลับมาประกอบกัน



เรื่องเล็ก ๆ แต่ทำให้คิดเรื่อง ERP ใหม่อีกครั้ง


ผมชอบปัญหาแบบนี้ตรงที่มันเล็กมาก

เวลาพูดถึงการออกแบบ ERP เรามักคุยเรื่องใหญ่ ๆ


Accounting

Inventory

Document Flow

Tax

Costing

Integration


แต่บางครั้งปัญหาที่ทำให้เราต้องกลับไปคิดเรื่อง design ใหม่ กลับเป็นตัวเลขเล็ก ๆ แค่ 45 บาท


และที่น่าสนใจกว่านั้นคือ ไม่มีใครผิดเลย

โปรแกรมเมอร์ไม่ได้คำนวณผิด

คนทำธุรกิจก็ไม่ได้เข้าใจเลขผิด

แต่ทั้งสองคนกำลังมองคำว่า “ส่วนลด” และ “คืนสินค้า” จากคนละมุม


โปรแกรมเมอร์เห็น

-2

คนทำธุรกิจเห็น

“คืนของ 2 ชิ้น”

โปรแกรมเมอร์เห็น

discount = 45

คนทำธุรกิจเห็น

“ส่วนลดที่เคยให้ไป 45 บาท”

พอเอาสองความหมายนี้มาเจอกันในสูตรเดียวกัน จึงเกิดเรื่องที่ดูเหมือนขัดแย้งขึ้นมา


บางทีนี่อาจเป็นอีกเหตุผลหนึ่งที่ทำให้การทำ ERP ไม่เหมือนการเขียนโปรแกรมคำนวณทั่วไป


เพราะสุดท้ายแล้ว

ตัวเลขใน ERP ไม่ได้มีแค่ค่า แต่มีความหมายของเหตุการณ์ที่เกิดขึ้นอยู่ข้างหลังตัวเลขเหล่านั้นด้วย


และบางครั้ง เราอาจต้องเข้าใจเหตุการณ์นั้นให้ได้เสียก่อน

แล้วค่อยเขียนสูตรครับ


ที่จริงแล้ว ผมคิดว่าบทเรียนจากส่วนลด 45 บาทครั้งนี้ ไม่ได้อยู่ที่การใช้ abs() ด้วยซ้ำ เพราะถ้าเราแก้ปัญหาว่า “ถ้าติดลบก็ให้คำนวณอีกแบบ” วันหนึ่งก็คงมีกรณีใหม่ให้ต้องเพิ่มเงื่อนไขอีก


สิ่งที่น่าสนใจกว่าคือ

เราเพิ่งเห็นว่า เครื่องหมายของตัวเลขใน ERP อาจกำลังบอกเหตุการณ์บางอย่างอยู่


-2 ไม่ได้บอกเพียงว่า “จำนวนติดลบ”


แต่มันกำลังบอกว่า

มีการคืนสินค้าจำนวน 2 ชิ้น

-1,955 ก็ไม่ได้เป็นเพียง “ยอดติดลบ”

แต่มันกำลังบอกว่า

กำลังย้อนกลับมูลค่า 1,955 บาทของรายการเดิม

เมื่อคิดแบบนี้ ปัญหาที่เกิดขึ้นจึงอาจไม่ใช่เรื่องสูตรผิด

แต่เป็นเพราะเราเอา เครื่องหมายของรายการ ไปปนกับ วิธีคำนวณมูลค่า


ตอนแรกโปรแกรมเห็น

-2,000 - 45

แต่คนใช้ไม่ได้กำลังบอกว่า

“เอา -2,000 ไปหักด้วย 45”

เขากำลังบอกว่า

“ช่วยย้อนกลับรายการเดิมที่มีมูลค่า 1,955 บาท”

สองประโยคนี้ให้ความรู้สึกเหมือนกันมาก เมื่อมองจากตัวเลข แต่กลับเป็นคนละเรื่อง เมื่อมองจากเหตุการณ์


แล้วผมก็เริ่มสงสัยว่า ใน ERP ที่เราเขียนกันอยู่ทุกวัน มีตัวเลขอีกกี่ตัวที่เราเห็นเป็นเพียง “ค่า” แต่คนใช้เห็นเป็น “เหตุการณ์”


ยอดติดลบ อาจหมายถึงการคืนเงิน

จำนวนติดลบ อาจหมายถึงการคืนสินค้า

ยอดคงค้าง อาจหมายถึงสิ่งที่ยังไม่ได้เกิดขึ้นครบ


ตัวเลขเหล่านี้อาจดูเหมือนเป็นข้อมูลชนิดเดียวกันในโปรแกรม

แต่ในโลกจริง มันอาจกำลังเล่าเรื่องคนละเรื่องกัน


นี่ทำให้ผมกลับมาคิดว่า เวลาที่เราออกแบบข้อมูลใน ERP เราควรถามเพียงว่า

“ตัวเลขนี้มีค่าเท่าไร?”

หรือเราควรถามอีกคำถามหนึ่งด้วยว่า

“ตัวเลขนี้กำลังบอกว่าเกิดอะไรขึ้น?”

และบางทีคำถามที่สำคัญกว่านั้นอาจเป็น

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

Transaction สต็อคออกที่ติดลบ อาจหมายถึงการรับคืนสินค้าฝั่งขาย และถ้าเป็นสต็อคเข้าติดลบเป็นการส่งคืนสินค้าฝั่งซื้อ


เพราะถ้าคำตอบคือ “ไม่เหลืออะไรเลย”

นั่นอาจหมายความว่า เราไม่ได้เก็บความหมายของเหตุการณ์ไว้ในข้อมูล

เราเพียงเก็บผลลัพธ์ของการคำนวณไว้เท่านั้น


และสำหรับ ERP ผมไม่แน่ใจว่าสองอย่างนี้ควรเป็นเรื่องเดียวกันหรือเปล่าครับ

 
 
 

ความคิดเห็น


  • Facebook

©2020 by Scraft On Cloud. Proudly created with Wix.com

bottom of page