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 budgetSeeded 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:
- A row with
modules_parent = NULLnever appears, whatever its installed flag. That is how the 47 legacy rows stay hidden. - Rows are grouped by
modules_parent. The seedDefaultTsModuleSidebarTypeParent.phpdefines the groups for thereporttype:dms,procurement,budgetandmaster_stamp. These are the DMS, Procurement and Cost and Budget headings in the sidebar. - The old
report_parentnesting 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,budgetandprocurement, are leftovers of that nesting: they are installed, but theirreport_urlis 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 segment | Opens | Example |
|---|---|---|
window | a dhtmlx window inside the app | window|main.php?page=member&cid=<module md5>|&show=report&print=due&view=custom |
direct_download_report | a new browser tab | used for standalone downloads |
Behind the window form there are three targets:
| Target | Query | Handled by |
|---|---|---|
| A module's print mode | print=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 mode | show=report&print=<code>&view=custom | the module's show=report branch, for example routing (print=due) and budget (print=bvcm). |
| A direct-download report | page=member&direct_download_report=<name>&get_form_filter=1 | member.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
| Group | Code | Label | Target |
|---|---|---|---|
| DMS | documents | Documents Library | documents module, cmd=print_costume |
| DMS | routing_01 | Routing Overdue | routing module, show=report&print=due |
| DMS | mdr_report_2 | MDR Report | documents_master print mode, type=mdr_report_2, rendered by documents_master_handler_print_mdr_report_2.php as an HTML template |
| DMS | mdr_report_summary | MDR Report Summary | same, rendered by documents_mater_handler_print_mdr_report_summary.php. Note the filename typo, mater. |
| Procurement | procurement_status_report | Procurement Status Report | direct download, .xlsx via PhpSpreadsheet |
| Budget | cost_summary_report | Cost Report Summary (Accrual) | procurement module, cmd=print&print=cost_summary_report |
| Budget | cost_summary_report_actual | Cost Report Summary (Actual) | same handler, actual figures |
| Budget | budget_03 | Budget versus Current Month | budget module, show=report&print=bvcm |
| Budget | procurement_05 | Contract Payment Summary | procurement module report mode |
| Budget | procurement_05_1 | Contract Payment Summary (WBS) | same |
| Budget | procurement_06 | Contract Invoices Balance Report | same |
| Budget | procurement_07 | AFE Accrual Report | same |
| Budget | procurement_14 | Finance Accrual Report | same |
| Budget | procurement_16 | Contract, Commitment and Expenditure | same |
| Budget | procurement_17 | Contract Balance Report, Expenditure | same |
| Budget | budget, procurement | legacy group headers | no 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.
Related tables
| Table | Used by | Holds |
|---|---|---|
ts_modules_report | menu, privileges, Report Register | the registry |
ts_module_parent, ts_module_type | menu | the groups and the report type |
ts_privileges_resource (priv_type='report') | menu, group privileges | who may view which report |
ts_report_settings | procurement, expedite | saved per-project, per-person report settings (type, name) |
ts_report_snapshot, ts_map_report_snapshot | procurement | AFE accrual snapshots: CBS sums for previous, current and next year, with comments |
ts_cm_report | procurement | one column, report_title |
TODO: what
ts_cm_reportandts_cm_report_commentsare 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. Checkreport_is_installed,modules_parentand the resource group'sreportprivileges before reading code. report_codeis the privilege key andmodules_codeis reused for it. A report and a module with the same code would collide ints_privileges_resource;priv_typeis 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 thereportprivilege and the login protect them.
Instance differences
Checked on 2026-09-06.
| Instance | Difference |
|---|---|
| Medco | Adds routing_04, Comment Summary, installed under DMS, pointing at routing with print=commentsummary. 18 installed. |
| Timas | Adds overdue_report, Overdue, installed under DMS, pointing at documents_master print mode. 18 installed. |
| JOTRE | Same 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. |
| Jadestone | Identical to master. |
Related
- Users and Privileges: the resource-group model the
reportprivilege type sits on. - Import MDR and Upload: the data the MDR reports read.