#process#report#registry#conditions#sql-parameters
ทะเบียนรายงานรายเมนูและเงื่อนไขกรอง (Menu report registry & conditions)
รายงานแต่ละเมนูของ SML คือฟังก์ชัน menuReportFunc ที่ลงทะเบียนไว้ใน map menuReports ด้วย key เป็น menuKey ตัวพิมพ์เล็ก ให้ 1 เมนู = 1 รายงานที่พอร์ตมาจากคลาส C# ของเมนูนั้น หน้าจอกรองเงื่อนไขไม่ได้ฮาร์ดโค้ด แต่ backend เป็นผู้ประกาศรายการ ReportCondition กลับไปให้ frontend วาด
ทำงานอย่างไร
การลงทะเบียน
registerMenuReport(fn, keys...)ทำstrings.ToLower(TrimSpace)ที่ key แล้วใส่ map; ถ้า key ซ้ำจะpanic("report menu registered twice: ...")ตอนเริ่มโปรแกรม (ลงทะเบียนในinit()ของแต่ละไฟล์)- ไฟล์ที่ลงทะเบียน (ดู
init()):report_menu_ic.go(สินค้า/คลัง),report_menu_ap_ar.go,report_menu_cb.go,report_menu_gl_fa.go,report_menu_employee.go,report_menu_autocare.go,report_menu_singha.go,report_menu_so_po.go - ผู้เรียก
RunReportตรวจmenuReports[lowerKey]ก่อนเสมอ (เครื่องมือรายงานฝั่ง Backend (Report engine / RunReport)) reportParamsส่งให้ตัวสร้าง:MenuKey,Title,DateFrom/DateTo(YYYY-MM-DD),FromCode/ToCode,OrderBy,Keyword,KwLike(%คำ%),ViewDetail,Username,Opt(ค่า options ดิบ)
รูปแบบของรายงานกลุ่มสินค้า (icRun)
- แต่ละรายงานเป็น
icSpec(code,category= คลาส C# ต้นฉบับ,subtitle,codeLabel,cols,sql,opts,cost,optCol,rangeInSQL,perm) - SQL ทุกตัวขึ้นต้นด้วย CTE
prm(icPrm):$1=d1วันเริ่ม,$2=d2วันสิ้นสุด,$3=fcจากรหัส,$4=tcถึงรหัส,$5=kw,$6=kl,$7=o(JSON ของ options) -- แต่ละ$nต้องถูกอ้างครั้งเดียว (คอมเมนต์: pgx ปฏิเสธ param ที่ไม่ได้ใช้) ส่วน SQL ที่เหลืออ่านจากprm.d1,prm.fcฯลฯ icRunรองรับรายงานสรุป + รายงานรายละเอียด (ViewDetailสลับไป spec ที่ 2 ตาม C#_displayDetail)- ฟังก์ชันช่วย SQL:
icCodeRange(col)(ช่วงรหัสไม่สนตัวพิมพ์),icKw(...)(ILIKEprm.kl) - ถ้า
cost != icCostNoneและitemCostHiddenเป็นจริง จะตัดคอลัมน์ต้นทุนผ่านicHideCosts; ถ้า spec ใช้permตั้งpermGuarded(ตัวรายงานกรองคลัง/ที่เก็บตามสิทธิ์ ดู สิทธิ์คลัง/ที่เก็บรายกลุ่มผู้ใช้ (perm_wh_shelf)) icAllSpecsเก็บทุก spec ไว้ให้ unit test ตรวจ SQL/คอลัมน์โดยไม่ต้องใช้ DB
ชนิดเงื่อนไข (ReportCondition.Type)
| Type | ความหมาย | ตัวสร้าง |
|---|---|---|
checkbox |
default "1"/"0" | condCheck |
number |
จำนวนเต็ม | condNumber |
text |
ข้อความ (มี Hint) |
condText |
date |
YYYY-MM-DD | condDate |
select |
เลือกจาก Options |
condSelect |
ranges |
แถว [จากรหัส][ถึงรหัส] รูป "A..B,C" |
condRanges |
multi |
รายการรหัสที่ติ๊ก (ว่าง = ทั้งหมด) | condMulti |
- ฝั่ง Go อ่านค่าผ่าน
p.optStr/optList/optBool/optInt/optDate/optSelect(ค่าผิดรูปแบบ = ค่า default;optIntถูกหนีบด้วย lo..hi;optStrตัดความยาวoptMaxLen=100, รายการรหัสตัดoptListMaxLen=1000ที่ขอบ token) - ฝั่ง SQL ใช้
optOn(key),optInt(key),optHas(key),optInList,optCodeSet,optCodeFilter-- key เป็นค่าคงที่ในโค้ดเท่านั้น (ตรวจด้วยoptKeyRe = ^[a-z][a-z0-9_]*$ใน unit test ตามคอมเมนต์) normalizeReportOptions(defs, in)เก็บเฉพาะ key ที่ประกาศ แล้วบังคับชนิด (checkbox: 1/true/on/y/yes -> "1"; number ตรง^-?[0-9]{1,6}$; date ต้อง parse ได้; select ต้องอยู่ใน options; ข้อความตัด control char และยาวสุด 500 rune) -- ป้องกันการยัด key/ค่าแปลกเข้า SQLreportOptionsJSONสร้างค่า$7: ทุก key ที่ประกาศมีอยู่เสมอ และรวมค่าฝั่ง server (เช่นทศนิยมของบริษัท) ที่ไม่ใช่ข้อมูลจากคำขอ
ช่วงรหัสหลายแถว (ranges / multi)
parseCodeRanges("A..B,C"): สูงสุดmaxCodeRangeRows = 50แถว, ต้องมีค่า "จาก" ไม่ว่าง, "ถึง" ว่าง = เท่ากับค่า "จาก" พอดี;cleanCodeตัด control char,,,:,..และจำกัดmaxCodeLen = 40runecodeRange.sql(col, bind):upper(col) = upper($n)หรือBETWEENแบบไม่สนตัวพิมพ์ (bind แทน placeholder)parseCodeMulti: ตัวพิมพ์ใหญ่ ไม่ซ้ำ จำกัดmaxCodeRangeRows*4; ทุกแถวถูกติ๊กหรือไม่ติ๊กเลย = ไม่กรอง (ตาม C#)- สมุดรายวัน GL:
glfaBooksผูกเป็น$8::text[](glfaBookPrm/glfaBookCond)
จุดที่ต้องระวัง
- ลงทะเบียน key ซ้ำ = โปรแกรมไม่เริ่ม (panic)
- ถ้า SQL ของ spec ไม่อ้าง
prm.dเลยUsesDateRangeจะเป็นเท็จ (คำนวณจากstrings.Contains(s.sql, "prm.d")ที่report_menu_ic.go) - ห้ามต่อ key ของ option จากข้อมูลผู้ใช้ลงใน SQL: ฟังก์ชัน
opt*รับ key คงที่เท่านั้น
ที่มาในโค้ด
backend/internal/application/process/report_menu.go:10-42-reportParams,menuReportFunc,menuReports,registerMenuReportbackend/internal/application/process/report_conditions.go:12-48-ReportConditionและตัวสร้างcond*backend/internal/application/process/report_conditions.go:51-120- ตัวอ่าน option ฝั่ง Gobackend/internal/application/process/report_options.go:23-90-normalizeReportOptions,reportOptionsJSONbackend/internal/application/process/report_options.go:91-122- ฟังก์ชันสร้างชิ้น SQLopt*backend/internal/application/process/report_code_ranges.go:22-107-codeRange,parseCodeRanges,parseCodeMultibackend/internal/application/process/report_menu_ic.go:20-icPrmbackend/internal/application/process/report_menu_ic.go:23-36-icSpecbackend/internal/application/process/report_menu_ic.go:41-77-icRun