จอรายงานกลาง (Universal Report Screen)
รายงานเกือบทุกเมนูใช้ component เดียวคือ UniversalReportScreen ที่เป็น thin client: หน้าจอส่งเงื่อนไขไป POST /api/process/report/run แล้ว backend คืนคอลัมน์ แถว ยอดรวม และ SQL ที่รันกลับมา หน้าจอไม่คำนวณยอดเอง
เมนูที่ไม่ใช่รายงานมาตรฐานมีแค่ไม่กี่จอ (ตารางด้านล่าง)
ทำงานอย่างไร
screenRegistry.tsxแยกเมนูรายงานเป็นmode(11 โหมดรายงาน +check_price+barcode_print+sql_utility) ตามกลุ่มเมนูบนสุด (topBranchName) แล้วห่อด้วยcreateReportScreen(screenRegistry.tsx:173) ->UniversalReportScreen config={menuKey,menuCode,title,mode}(typeWorkbenchModeที่UniversalReportScreen.tsx:79-92)- ตอนเปิดจอ (หรือ
menuKey/modeเปลี่ยน)useEffectที่UniversalReportScreen.tsx:568-585ล้างเงื่อนไขเดิมแล้วเรียกrunReportทันทีรอบแรก ไม่ต้องกดปุ่ม ถ้า backend ตอบdetailDefault(เมนูที่ต้นฉบับ C# ติ๊ก "แสดงรายละเอียด" ไว้) จะรันซ้ำแบบviewDetail: trueอัตโนมัติ buildCurrentPayload(:268) ประกอบReportRunPayload(query-client.ts:940) จากค่าบนหน้าจอทั้งหมด:menuKey,mode,dateFrom/dateTo,fromCode/toCode,orderBy,keyword,viewDetail,options(ค่าเงื่อนไขเพิ่มเติม), ค่าตั้งหน้ากระดาษ (paperSize,orientation,printTheme,reportLangฯลฯ),groupByColumn/subGroupByColumn,customSql,customColumnsและฟิลด์template*executeReportRequest(:401) เรียกrunBackendReport(query-client.ts:1020->apiPost("/api/process/report/run")) แล้วเอาผลใส่ state:columns,rows,totals,summaryCards,orderByOptions,conditions,hierarchyLevels/groupNodes,defaultSql/executedSql/executionMs,templates- เบราว์เซอร์แสดงแถวทีละ 80 แถว (
INITIAL_VISIBLE_ROWS,:200) แล้วเลื่อนใกล้ท้ายตาราง (เหลือ <= 320px) จึงเพิ่มทีละ 150 แถว (handleTableScroll:1067-1079); โหมดจัดกลุ่มหลายระดับเริ่ม 40 กลุ่ม เพิ่มทีละ 60
เงื่อนไขรายงาน
- เงื่อนไขหลักบนหน้าจอ: ช่วงวันที่, จากรหัส-ถึงรหัส (ป้ายชื่อ
codeRangeLabelมาจาก backend), เรียงตาม (orderByOptions), คำค้น, สลับสรุป/รายละเอียด (ปุ่มแสดงเมื่อsupportsDetail) - เงื่อนไขเพิ่มเติม backend เป็นคนประกาศมาใน
conditions(ReportCondition,query-client.ts:922-930) ชนิดcheckbox | number | text | date | select | ranges | multi;ReportConditionFields.tsxเรนเดอร์แต่ละชนิด (:121-196) และส่งค่ากลับในoptions(map key -> string) - ชนิด
rangesเก็บเป็นสตริง"A..B,C"(หลายแถวจากรหัส-ถึงรหัส) แปลงด้วยparseRanges/joinRangesและcleanCodeตัด..,,,:ออกจากรหัสเพราะเป็นตัวคั่น (ReportConditionFields.tsx:20-30) - กด Enter ในช่องเงื่อนไขใดก็รันรายงานใหม่ (
onKeyDown ... runReport(),:1242,:1259)
รูปแบบรายงาน (template) และแผงออกแบบ
- เปิดจาก toolbar: คอลัมน์, SQL, รูปแบบรายงาน (
handleOpenStudioTab,:589) ผ่านGlobalReportStudioPanel(:1389) ซึ่งเป็น component ในGlobalReportPrintModal.tsx(ดู พิมพ์รายงาน แบบฟอร์มเอกสาร และตัวออกแบบ (Report Print & Designers)) - แก้ความกว้าง/ซ่อนคอลัมน์ได้ (
columnOverrides) แล้ว "ใช้" ด้วยการรันรายงานใหม่พร้อมcustomColumns - Template เก็บใน DB ผ่านการรัน
report/runอีกรอบพร้อมtemplateAction: "save" | "delete" | "set_default"(:779,:796,:805) ไม่มี endpoint แยก; backend ส่งรายการ template กลับมาในdata.templatesและsavedTemplateId - ถ้าเลือก "รูปแบบมาตรฐานระบบ" (template = null) จะส่ง
customSql: ""และcustomColumns: [](:748-757) - ชุดคอลัมน์ "สรุป" กับ "รายละเอียด" แยกกัน เก็บใน
colsBySetRef(:196,:551-557) สลับโหมดแล้วการปรับของแต่ละชุดไม่ปนกัน - โหมด
sql_utility: เปิดแผงออกแบบ tabsqlทันที (ยกเว้น menuKey ที่มีdatabase_struct) (:186-193) ผู้ใช้แก้ SQL แล้วกด "ทดสอบ" ->handleTestQueryOnTheFly(:598) ส่ง SQL เป็นcustomSqlไปให้ backend รัน; ขอบเขตสิทธิ์/การจำกัดเฉพาะ SELECT ฝั่ง backend (ยังไม่ยืนยัน)
ส่งออก / พิมพ์ / วิเคราะห์
| ปุ่ม | ทำอะไร | ที่มา |
|---|---|---|
| ส่งออก CSV | สร้างไฟล์ในเบราว์เซอร์จาก rows ของคอลัมน์ที่เห็น ใส่ BOM ชื่อไฟล์ <menuKey>_<วันนี้>.csv |
UniversalReportScreen.tsx:1042-1060 |
| พิมพ์ / PDF | เปิด GlobalReportPrintModal พร้อม basePayload ที่รวมคอลัมน์ล่าสุด; PDF เรียก fetchBackendReportPdf -> POST /api/process/report/pdf |
:812-815, :2355, query-client.ts:1029 |
| วิเคราะห์รายงานด้วย AI | POST /api/process/workflow/ai-report-analyze ส่ง menuKey, title, rows, totals, summaryCards ได้ headline, highlights, anomalies, metrics มาแสดงเป็นแผง ai-report-insight-panel |
:229-250, :1158, :1482 |
ฝั่ง proxy (Next.js)
src/app/api/process/report/run/route.tsมีทั้งPOST(ใช้จากหน้าจอ) และGET(รับ query string หลายชื่อ เช่นmenuKey/menu_key/reportKey/report_code,dateFrom/date_from/doc_date_fromแล้วที่เหลือยัดใส่options)REPORT_KEY_ALIASES(route.ts:12-30) แปลงชื่อ key เก่า/ชื่อสำรองเป็น key จริงของ backend ก่อนส่ง เช่นrp_so_quotation->menu_so_report_quotation,menu_ap_detail->menu_ap_report_detail_ap,menu_ic_report_stock_balance->menu_ic_info_stk_balance- หลังได้ผล ถ้า
data.total_vat_amount/total_base_amountไม่มี จะเติมจากtotalsตามชื่อคอลัมน์ไทย (จำนวนเงินภาษี,ยอดภาษี,ภาษีมูลค่าเพิ่ม/มูลค่าสินค้า/บริการ,ฐานภาษี) หรือ"0.00"(route.ts:60-76) report/pdf/route.tsส่งต่อPOST /api/v1/process/report/pdfและคืนContent-Dispositionของ backend (ค่าสำรองinline; filename="sml_report.pdf",:77)- ภาพรวม proxy ทั้งหมดดู เส้นทาง API ฝั่ง Next.js ที่ส่งต่อไป Go backend (API proxy routes)
ตารางโหมด: เมนูไหนไปจอไหน
กฎคัดแยกอยู่ใน buildDynamicScreen (screenRegistry.tsx:1262-1380):
| เงื่อนไข (เรียงตามลำดับ) | จอ/โหมด | ที่มา |
|---|---|---|
ชื่อเมนูมี query, qeury, database_struct, verify_database |
UniversalReportScreen mode sql_utility |
screenRegistry.tsx:1265-1280 |
client_design, menu_pos_design_screen |
PosSetupAndControlScreen tab machines (ดู จอประมวลผลระบบและ POS (System Process Workbench & POS Setup)) |
:1285-1292 |
ไม่มี report แต่มี check_price, price_formula, price_list, saleprice, purchase_price, all_price |
mode check_price |
:1297-1314 |
มี barcode และ print |
mode barcode_print |
:1318-1328 |
topBranchName = menu_po |
report_po |
:1334 |
menu_so, menu_so_pos |
report_so |
:1335-1336 |
menu_ap / menu_ar |
report_ap / report_ar |
:1337-1338 |
menu_cash_and_bank (ชื่อมี chq, cheque, credit_card -> report_chq ที่เหลือ report_cb) |
report_chq / report_cb |
:1339-1345 |
menu_asset |
report_fa |
:1346 |
menu_general_ledger_main |
report_gl |
:1347 |
menu_wh, menu_singha_warehouse |
report_wh_pp |
:1348-1352 |
menu_singha_report (เดาจากชื่อ: _gl_/tax -> gl, _cb_/bank -> cb, _ar_/debt -> ar, _so_/sale -> so, อื่น ๆ -> ic) |
report_gl/cb/ar/so/ic |
:1353-1358 |
menu_setup |
SystemProcessWorkbenchScreen mode system_tool |
:1359-1365 |
| อื่น ๆ (ค่าเริ่มต้น) | report_ic |
:1333, :1371-1379 |
หมายเหตุ: mode ใช้เป็นแค่ตัวส่งให้ backend (payload.mode) กับตัดสินใจ tab เริ่มต้นของแผงออกแบบ; ฝั่งหน้าจอไม่มีโค้ดแยกพฤติกรรมของ check_price / barcode_print (ค้นแล้วพบ config.mode ใช้แค่ :188, :192, :283, :337, :585) ดังนั้นผลลัพธ์ของสองโหมดนี้ขึ้นกับ backend ทั้งหมด
จอที่ลงทะเบียนมือ (ไม่ใช่ mode ข้างบน)
| ชื่อจอ (ไทย) | ไฟล์/component | endpoint ที่เรียก |
|---|---|---|
รายงานพนักงานเรียงตามกลุ่ม (menu_report_employee_group, 1.1.8) |
UniversalReportScreen mode report_ic ผ่าน createReportScreen |
POST /api/process/report/run (screenRegistry.tsx:415-424) |
รายละเอียดการซื้อ (menu_po_buy_information, 3.8.7) |
account/report/BuySaleInformationScreen.tsx mode="buy" |
/api/query/list (queryList) (screenRegistry.tsx:741-752) |
รายละเอียดการขาย (menu_so_sale_information, 4.10.22) |
BuySaleInformationScreen mode="sale" |
/api/query/list (screenRegistry.tsx:754-765) |
ตรวจสอบราคาสินค้า (check_price, 2.1.21) |
CheckPriceScreen ใน account/master/IcExtraScreens.tsx:573 |
(ยังไม่ได้อ่านข้างใน) (screenRegistry.tsx:767-778) |
BuySaleInformationScreen (ข้อมูลซื้อ/ขายรายคู่ค้า)
- จอเดียวสองโหมด:
isSaleใช้ตารางar_customerและtrans_flag44, 46, 36; โหมดซื้อใช้ap_supplierและ 12, 14, 310 (BuySaleInformationScreen.tsx:38-40) - คิวรีผ่าน
queryList+escapeSqlตรง ๆ จากเบราว์เซอร์ (ไม่ผ่าน/api/process) จากic_trans,ic_trans_detail(JOINic_transด้วยdoc_no+trans_flag, LEFT JOINic_inventory),cb_chq_listและสรุปรายเดือนด้วยSUM(CASE WHEN EXTRACT(MONTH FROM doc_date)=n ...)ต่อปี (:71-150,:189-190) - โครงจอ: ตารางคู่ค้าซ้าย (กรอง "เฉพาะที่มีหนี้" + คำค้น), ตารางสินค้า/เอกสารขวา, สรุปหนี้, ตารางรายเดือน (
:42-61,:264-349) - เป็นข้อยกเว้นของกฎ "รายงานต้องผ่าน backend" เพราะเขียน SQL ในหน้าจอเอง ถ้าแก้สูตรต้องแก้ที่ไฟล์นี้ ไม่ใช่ backend
ข้อควรระวัง
- รายงานใหม่ไม่ต้องเขียนหน้าจอ: เมนูที่ชื่ออยู่ใน
smlMenuจะถูกลงทะเบียนเองโดยregisterAllMenus/buildDynamicScreen; ที่ต้องทำคือให้ backend รู้จักmenuKey(และเพิ่ม alias ในREPORT_KEY_ALIASESถ้าชื่อใน menu ต่างจากฝั่ง backend) - ลำดับกฎใน
buildDynamicScreenสำคัญ: เมนูcheck_priceที่มีคำว่าreportในชื่อจะไม่เข้าโหมดcheck_price(:1297-1306) และไปตกกฎtopBranchName report_chqตัดสินจาก substring ของชื่อเมนูเท่านั้น (chq,cheque,credit_card) เมนูใหม่ที่ตั้งชื่อต่างไปจะไปเป็นreport_cb- ตัวเลขจำนวนเงินในผลรายงานเป็น string จาก backend (
totals: Record<string,string>) จึงจัดรูปแบบผ่านformatMoneyDisplayไม่แปลงเป็น number ดู ตัวเลข เงิน ภาษี และปี พ.ศ. ฝั่ง frontend (numeric.ts)
ที่มาในโค้ด
frontend/account/report/UniversalReportScreen.tsx:79-107typeWorkbenchMode,UniversalWorkbenchConfig, ชื่อคอมโพเนนต์frontend/account/report/UniversalReportScreen.tsx:268-340buildCurrentPayload;:401-545executeReportRequest;:548-565runReportfrontend/account/report/UniversalReportScreen.tsx:568-585รันอัตโนมัติตอนเปิดจอ;:1042-1060ส่งออก CSV;:229-262AI วิเคราะห์frontend/account/report/ReportConditionFields.tsx:20-30, 121-196ชนิดเงื่อนไขranges/multi/select/date/number/text/checkboxfrontend/src/lib/query-client.ts:922-1050typeReportCondition,ReportRunPayload,ReportRunResult,runBackendReport,fetchBackendReportPdffrontend/src/app/api/process/report/run/route.ts:12-148alias และ proxy;report/pdf/route.ts:28-87frontend/account/tabs/screenRegistry.tsx:173-181createReportScreen;:1262-1380คัดแยก modefrontend/account/report/BuySaleInformationScreen.tsx:27-150จอรายละเอียดซื้อ/ขาย
ดูเพิ่ม
พิมพ์รายงาน แบบฟอร์มเอกสาร และตัวออกแบบ (Report Print & Designers) | ทะเบียนหน้าจอและโครงเมนู (Screen registry & smlMenu) | เส้นทาง API ฝั่ง Next.js ที่ส่งต่อไป Go backend (API proxy routes) | ตัวเรียก API และตัวประกอบ SQL ฝั่งเบราว์เซอร์ (api-client, query-client, sml-query-builder) | ตัวเลข เงิน ภาษี และปี พ.ศ. ฝั่ง frontend (numeric.ts) | จอประมวลผลระบบและ POS (System Process Workbench & POS Setup) | ภาพรวม Frontend ของ SML Ai Account (Frontend overview) | ภาพรวมกลุ่ม process ของ Backend (Process overview) | ภาพรวมระบบบัญชี SML AI Account (Accounting Overview)