Skip to content

Reporting

System: cross-cutting. Reports are not a module. They are a registry of menu entries that point into the modules, plus a privilege type of their own. The report code itself is spread over eleven module folders. This page is the map.

How this page was verified

Written from master on 2026-09-06 and compared with the big five the same day. No earlier page existed.

What a report is in INACT

A report is a row in ts_modules_report. The row gives it a code, a label, a menu group and a URL. The URL points at an existing module screen in print mode, or at a standalone download script. There is no report engine: each report is whatever the target screen renders.

ts_modules_report
├── report_id            (PK)
├── report_code          e.g. mdr_report_2, used as the privilege key
├── report_desc          the menu label
├── report_parent        legacy nesting, no longer used by the menu
├── report_url           "window|..." or "direct_download_report|...", see below
├── report_sort          order inside its group
├── report_is_installed  1 = shown in the menu
└── modules_parent       menu group: dms, procurement or budget

Seeded by DefaultTsModulesReport.php. Master seeds 64 rows, of which 17 are installed. The other 47 are legacy reports (Activity, Tender, Hazop, Commission, Shipping Mark, and so on) that stay in the registry switched off.

How the menu is built

_menu.php renders a Report module type next to the ordinary modules. Its entries come from getModuleReportList() in reference_function.inc.php, which joins ts_modules_report to ts_module_parent on modules_parent. Three things follow:

  1. A row with modules_parent = NULL never appears, whatever its installed flag. That is how the 47 legacy rows stay hidden.
  2. Rows are grouped by modules_parent. The seed DefaultTsModuleSidebarTypeParent.php defines the groups for the report type: dms, procurement, budget and master_stamp. These are the DMS, Procurement and Cost and Budget headings in the sidebar.
  3. The old report_parent nesting is dead. The code that walked child reports under a parent is commented out. Every installed row renders flat under its group. Two seeded rows, budget and procurement, are leftovers of that nesting: they are installed, but their report_url is a bare word (reportBudget, reportProc) rather than a real URL, so they render as empty list items.

Each entry is shown only when report_is_installed = 1 and the user passes the privilege check, see Privileges. The admin group bypasses the check. Before rendering, the menu appends &report_code=<md5 of report_code> to the URL; the Base class in src/basic/Base.php reads that back to know which report the page is serving.

The URL grammar

report_url is a pipe-separated string. The first segment says how to open it; the rest is joined into the query string by onButtonClick() in master.htm.

First segmentOpensExample
windowa dhtmlx window inside the appwindow|main.php?page=member&cid=<module md5>|&show=report&print=due&view=custom
direct_download_reporta new browser tabused for standalone downloads

Behind the window form there are three targets:

TargetQueryHandled by
A module's print modeprint=browse&print_browse=all&view=custom&type=<code>the module's *_handler_print_browse.inc.php. Seven modules have one: documents_master, expedite, package_order, packing_list, purchase_order, sales_order, and a second one in sales_order.
A module's report modeshow=report&print=<code>&view=customthe module's show=report branch, for example routing (print=due) and budget (print=bvcm).
A direct-download reportpage=member&direct_download_report=<name>&get_form_filter=1member.php includes modules/member/direct_download_report/<name>.inc.php and <name>.php, then exits.

cid is the md5 of the target module code, which is why the URLs look opaque.

The installed reports in master

GroupCodeLabelTarget
DMSdocumentsDocuments Librarydocuments module, cmd=print_costume
DMSrouting_01Routing Overduerouting module, show=report&print=due
DMSmdr_report_2MDR Reportdocuments_master print mode, type=mdr_report_2, rendered by documents_master_handler_print_mdr_report_2.php as an HTML template
DMSmdr_report_summaryMDR Report Summarysame, rendered by documents_mater_handler_print_mdr_report_summary.php. Note the filename typo, mater.
Procurementprocurement_status_reportProcurement Status Reportdirect download, .xlsx via PhpSpreadsheet
Budgetcost_summary_reportCost Report Summary (Accrual)procurement module, cmd=print&print=cost_summary_report
Budgetcost_summary_report_actualCost Report Summary (Actual)same handler, actual figures
Budgetbudget_03Budget versus Current Monthbudget module, show=report&print=bvcm
Budgetprocurement_05Contract Payment Summaryprocurement module report mode
Budgetprocurement_05_1Contract Payment Summary (WBS)same
Budgetprocurement_06Contract Invoices Balance Reportsame
Budgetprocurement_07AFE Accrual Reportsame
Budgetprocurement_14Finance Accrual Reportsame
Budgetprocurement_16Contract, Commitment and Expendituresame
Budgetprocurement_17Contract Balance Report, Expendituresame
Budgetbudget, procurementlegacy group headersno URL, render empty

The two direct-download scripts under modules/member/direct_download_report/ are procurement_status_report and cost_summary_report. Both build an .xlsx with PhpSpreadsheet, take the project from the user's selected project, and read their filter from the form the get_form_filter=1 step shows first.

Privileges

Reports have their own privilege type. havePrivilegesReportResource($groupid, $reportCode, 'view') in tracking_function.inc.php queries ts_privileges_resource with priv_type = 'report' and modules_code = <report_code>. The same table and the same resource-group model as module privileges, see Users and Privileges, only the priv_type differs.

Admins grant it in Reference, Group Privileges, tab Report (group_privileges.php?show=report): a grid of resource groups against every installed report, one checkbox each. Ticking inserts a view row with priv_type='report'; unticking deletes it. The first-run wizard has the same grid in setup_privileges_report.php.

Turning a report on or off

Reference, Report Register (report_register.php) is the generic reference editor bound to ts_modules_report through MODULE_REFERENCE_TABLE_CONFIG["report_register"]. It edits code, label and the report_is_installed flag. Turning on one of the 47 legacy rows makes it appear in the menu only if its modules_parent is set; the legacy rows seed NULL there, so a database edit is needed as well. Treat re-enabling a legacy report as a code change: the target screen may no longer exist.

TableUsed byHolds
ts_modules_reportmenu, privileges, Report Registerthe registry
ts_module_parent, ts_module_typemenuthe groups and the report type
ts_privileges_resource (priv_type='report')menu, group privilegeswho may view which report
ts_report_settingsprocurement, expeditesaved per-project, per-person report settings (type, name)
ts_report_snapshot, ts_map_report_snapshotprocurementAFE accrual snapshots: CBS sums for previous, current and next year, with comments
ts_cm_reportprocurementone column, report_title

TODO: what ts_cm_report and ts_cm_report_comments are for. They are seeded (TsCmReport.php) but the grep found no reader outside procurement.

Gotchas

  • A missing report is usually a privilege or a modules_parent, not a bug. Check report_is_installed, modules_parent and the resource group's report privileges before reading code.
  • report_code is the privilege key and modules_code is reused for it. A report and a module with the same code would collide in ts_privileges_resource; priv_type is the only thing keeping them apart.
  • The MDR report handlers render HTML, not Excel. Excel export of the MDR is a separate Documents Master function.
  • Direct-download scripts run outside the module privilege check. They are reached through page=member, so only the report privilege and the login protect them.

Instance differences

Checked on 2026-09-06.

InstanceDifference
MedcoAdds routing_04, Comment Summary, installed under DMS, pointing at routing with print=commentsummary. 18 installed.
TimasAdds overdue_report, Overdue, installed under DMS, pointing at documents_master print mode. 18 installed.
JOTRESame seed as master, but its _menu.php still uses the older hand-built tree with a $_REPORT array and hard-coded group lists. It adds a jotre_procurement_status_report direct-download script and lists it by name in the Procurement group.
JadestoneIdentical to master.