#process#report#engine#big-rat#multi-tenant
เครื่องมือรายงานฝั่ง Backend (Report engine / RunReport)
Service.RunReport คือจุดเดียวที่ทุกรายงานผ่าน: รับเงื่อนไขจากหน้าจอ (RunReportInput) -> เลือกตัวสร้างรายงานตาม menuKey -> รวมยอดด้วย big.Rat -> ส่งผล (RunReportResult) กลับให้ frontend แสดงเป็นตาราง หัวเอกสารของ package ระบุว่าการจัดประเภท/SQL/ยอดสะสม/ยอดรวมทำที่ backend ทั้งหมด (Thin Client)
ทำงานอย่างไร
ทางเข้า
POST /process/report/runและ/api/v1/process/report/run->Handler.RunReportอ่าน JSON (จำกัด 16 MB) แล้ว เขียนทับProviderCode,DatabaseName,Usernameจาก claims (ฟิลด์เหล่านี้ tagjson:"-"ผู้เรียกส่งเองไม่ได้)- สิทธิ์:
readปกติ แต่ถ้า body มีcustomSqlต้องadmin(การตรวจสิทธิ์เมนูของ route process (Process route rights))
ลำดับใน RunReport (ตามโค้ด)
normalizeRunReportInput: ถ้าไม่มีmenuKeyใช้reportCode/report_code; แปลง alias เก่าผ่านreportCodeAliases(เช่นreport_vat_sale->menu_report_vat_sale,ic_stock_balance->menu_ic_info_stk_balance); รับชื่อฟิลด์ snake_casedoc_date_from/to,doc_no_from/toแทนdateFrom/dateTo/fromCode/toCode- เปิด pool ของบริษัท (
companyPool) และตั้ง timeout 45 วินาทีต่อคำขอ - ถ้ามี
templateAction(save/delete/set_default) รันexecuteTemplateMutationก่อน แล้วโหลดเทมเพลตของเมนู (listReportTemplates) ดู SQL กำหนดเองและเทมเพลตรายงาน (Custom SQL & report templates) - วันที่เริ่มต้น:
dateFrom= ย้อนหลัง 1 ปี,dateTo= วันนี้ (ถ้าไม่ส่ง) withSQLCaptureเก็บ SQL ที่รันจริงไว้ในDefaultSQL/ExecutedSQL(ถ้าจับไม่ได้ใช้fallbackDefaultSQL)- เลือกตัวสร้างรายงาน:
menuReports[lowerKey](ลงทะเบียนไว้ ดู ทะเบียนรายงานรายเมนูและเงื่อนไขกรอง (Menu report registry & conditions)) ถ้าไม่มี และไม่ใช่เมนูตัวออกแบบ (fast_report_designer,menu_form_edit,menu_import_formdesign) ไปที่runLegacyReportซึ่งรองรับเฉพาะmode == "sql_utility"(ที่เหลือคืนerrNoLegacyReport-> ข้อความ "ไม่พบรายงาน <menuKey>"KindValidation) - กฎต้นทุน (fail closed): ถ้ารายงานตั้ง
costGuardedและitemCostHiddenเป็นจริง -> ตัวสร้างที่ไม่ได้ลงทะเบียนจะถูกปฏิเสธ "ไม่มีสิทธิ์ดูต้นทุนสินค้า" หรือตัดเฉพาะคอลัมน์ในcostCols(dropCostColumns); รายงาน IC ที่ลงทะเบียนใช้กฎนี้ในicRunเอง - ถ้ามี
customSql-> ตรวจcustomSQLRefusal(ต้นทุนซ่อน หรือpermGuardedคือรายงานที่กรองคลัง/ที่เก็บตามสิทธิ์ผู้ใช้ => ปฏิเสธ) แล้วรันrunCustomSQLReportแทนผลเดิม - ปี: อ่าน
erp_option.year_type(readErpYearType; ค่า "1" = พ.ศ., อื่น ๆ = ค.ศ.) แล้วapplyReportYearTypeแปลงวันที่ในConditionTextและในเซลล์ข้อความ; ถ้า ค.ศ. เปลี่ยนคำ "ปี พ.ศ. " เป็น "ปี ค.ศ. " finalizeReportConditionsAndOrder: เติมCodeRangeLabel,OrderByOptions(ถ้าว่าง ใช้ทุกคอลัมน์), กรองช่วงรหัสจาก/ถึงในหน่วยความจำเมื่อ SQL ยังไม่ได้กรอง (codeRangeInSQL=false) แล้วสร้างConditionTextapplyReportColumnOverridesAndAggregates: ปรับความกว้าง %, ซ่อน/แสดง, ชื่อคอลัมน์, ลำดับ, การจัดชิด, ฟังก์ชันรวม (sum/avg/count/min/max)- ยอดรวม:
computeExactColumnTotalsรวมเฉพาะคอลัมน์IsSummableด้วย*big.Rat(ParseDecimalRat) แล้วฟอร์แมตทศนิยม 2 ตำแหน่งเป็นสตริง; สร้างSummaryCardsถ้ายังว่างหรือผู้ใช้แก้คอลัมน์/ใช้ SQL เอง buildReportMultiLevelHierarchyสร้างHierarchyLevels/GroupNodes(การจัดกลุ่มหลายระดับและการพิมพ์ PDF รายงาน (Report hierarchy & PDF))- คืน
ExecutionMs,Templates,SavedTemplateID
ฟิลด์สำคัญ
- Input:
menuKey,mode,title,dateFrom,dateTo,fromCode,toCode,orderBy,keyword,viewDetail,options(map ค่าเงื่อนไขเพิ่ม),customSql,customColumns,groupByColumn,subGroupByColumn, ตัวเลือกพิมพ์ (paperSize,orientation,fontSizePt,printTheme,reportLangฯลฯ) - Output:
columns(key/label/align/isNumeric/isSummable/widthPercent/visible/aggregate),rows,totals(สตริงทศนิยม),conditions(ตัวควบคุมเพิ่มเติมที่หน้าจอต้องวาด),summaryCards,defaultSql,executedSql,templates - ตัวเลขเงินเป็นสตริงทศนิยมตลอด (ไม่ใช้ float) ตามหัวเอกสารที่อ้าง Rule 20
จุดที่ต้องระวัง
- ไม่ลงทะเบียน
menuKeyและmodeไม่ใช่sql_utility=> รายงานไม่มี (ไม่ใช่ว่างเปล่า) - ส่ง
customSqlพร้อมเมนูที่ซ่อนต้นทุน => 403 ทั้งที่ผู้ใช้เป็น Admin ก็ยังถูกcustomSQLRefusalเช็ก (ตรวจที่ระดับ result ไม่ใช่ระดับสิทธิ์) - ถ้า error แต่มี
customSqlในคำขอ ตัวรายงานมาตรฐานที่พังจะถูกข้าม (err != nil && CustomSQL == ""จึงจะคืน error) และตั้งcostGuardedกันรั่ว
ที่มาในโค้ด
backend/internal/application/process/report.go:20-70-RunReportInputbackend/internal/application/process/report.go:88-135-RunReportResult(ฟิลด์ privatecodeRangeInSQL,costGuarded,permGuarded,costCols)backend/internal/application/process/report.go:185-readErpYearTypebackend/internal/application/process/report.go:209-reportCodeAliasesbackend/internal/application/process/report.go:225-normalizeRunReportInputbackend/internal/application/process/report.go:246-377-RunReportbackend/internal/application/process/report.go:380-customSQLRefusalbackend/internal/application/process/report.go:427-runLegacyReportbackend/internal/application/process/report.go:436-finalizeReportConditionsAndOrderbackend/internal/application/process/report.go:650-applyReportYearTypebackend/internal/application/process/report.go:695-computeExactColumnTotalsbackend/internal/application/process/report_options.go:124-itemCostHiddenbackend/internal/adapter/inbound/http/process/handler.go:223- handlerRunReport
ดูเพิ่ม
- ทะเบียนรายงานรายเมนูและเงื่อนไขกรอง (Menu report registry & conditions)
- SQL กำหนดเองและเทมเพลตรายงาน (Custom SQL & report templates)
- การจัดกลุ่มหลายระดับและการพิมพ์ PDF รายงาน (Report hierarchy & PDF)
- เครื่องมือสต็อก กลุ่ม route stocktools (Stock Tools)
- การตรวจสิทธิ์เมนูของ route process (Process route rights)
- ภาพรวมกลุ่ม process ของ Backend (Process overview)