Audit
Audit lists every sent routing of the project, whoever sent it, grouped by project, state and date. It is shown to the admin, Document Controller and Procurement Controll groups.

Toolbar: View, Edit, Delete, Refresh, Search, Print, Download, Group Routing, Return, Clear Return Status.
Return
Overview
Return closes the loop on a reviewed document: an auditor produces an outgoing Transmittal Out (TSO) package and marks the routing returned. The business semantics are the ones INACT has always had (same auditor roles, same status write, same correspondence record, same validation). v3 rebuilt the generation engine as an OOP Transmittal class that renders via HTML2PDF, added a config gate, and modernised the download flow.
Prerequisites and where it sits
Upload → routing created → reviewers act → routing CLOSED
↓
Return (this) → builds TSO + sets ts_routing.rout_return_status='Y', rout_return_date=todayA routing can be returned when its rout_status is closed, it is not already returned (rout_return_status != 'Y'), and the selection shares one project and one originator. And enable_return_on_audit must be '1' for the button to appear at all.
User flow
- Open Document Routing, Audit tab. Auditor groups only; the tab is hidden otherwise.
- Select one or more closed routing rows, same project and originator.
- Click Return (
downloadrout). Present only whenenable_return_on_audit=1. - The system validates, reserves a transmittal number, builds the TSO, marks the routing returned, and streams the packages to the browser.
How it works
Gating and button
The Audit tab is shown only to admin, Document Controller and Procurement Controll (routing_header.inc.php). The same three names appear in all five forks. The Return button is created only when the flag is on, in routing_browse.htm:
if("{enable_return_on_audit}" === "1") {
procBarAudit.addButton('downloadrout', 9,"Return","download.svg");
}JOTRE and Timas have no Return button at all
In those two forks the downloadrout line is commented out on the Audit toolbar, on the Outbox toolbar and in the overflow menu, and the enable_return_on_audit key does not exist. The AJAX handler is still in the code, so the feature is reachable only by a direct request.
Front-end
Downloadrout() in js/routing.js confirms, then calls ajax=transmittaloutgoingdownload&ids=<mapids> and downloads each returned archive client-side via blob and XMLHttpRequest. This supports batch returns, several archives in one action.
Back-end
routing_handler_ajax.php routes transmittaloutgoingdownload to generateTransmittalOutSantos($ids) in routing.inc.php, which uses the Transmittal class:
$t = new Transmittal("return", $html2pdf);
$t->setRoutId($routids);
$t->setBatchRouting(true, $routids);
$t->saveReturnAuditTransmittalNumber(); // reserve number in ts_transmittal_data
$t->generateTransmittal(); // build PDF via HTML2PDF
$t->storeFile(); // write to diskThen the core writes:
- Mark returned:
UPDATE ts_routing SET rout_return_date=<now>, rout_return_status='Y' WHERE rout_id IN (...), via$pdo->prepare()->execute(). - Outgoing correspondence:
handleProcessTransmittalCorrespondence()insertsts_correspondencewithcorrkind_id='2'and links the file.
Numbering
saveReturnAuditTransmittalNumber() reserves the next number in ts_transmittal_data, keyed by year, company, trans_type='out', project and owner. A more granular scheme than v2's simple increment.
Generation engine: HTML2PDF, with the legacy Excel path still present
The active Return path renders the transmittal as HTML to PDF via HTML2PDF inside the Transmittal class (routing_handler_transmittal.class.php, writeHTML() / Output()). generateTransmittalOutSantos() calls new Transmittal in all five forks.
Hybrid, not a clean replacement
The legacy per-company Excel mechanism is still in the code: writeExcelSantosTransmittalOut() and the ts_company_transmittal / comp_file_name lookups (docUpload/routing/) remain in routing.inc.php in every fork. Confirm which path your build wires up before assuming Excel templates are unused. Both exist.
Database tables affected
| Table | Operation | Notes |
|---|---|---|
ts_routing | update | rout_return_status='Y', rout_return_date=today |
ts_correspondence | insert | outgoing header (corrkind_id='2') |
ts_file_explorer, ts_map_corr_file | insert | stored transmittal file plus link |
ts_transmittal_data | insert / update | numbering (year / company / project / owner, out) |
ts_company_transmittal | select | per-company template, legacy path, if used |
Validation and error handling
The checks and messages in routing.inc.php: "You cannot return open document(s)", "Document(s) already returned", "You cannot return document(s) with different contractor/originator", and a different-project guard.
The "Ready to Return" tab
Master, Jadestone and Medco have an Audit-adjacent tab backed by routing_handler_ready_to_return.php, listing rout_status='close' AND rout_return_status='N'. JOTRE and Timas do not have the file.
Instance differences (Return)
Checked on 2026-09-06.
| Instance | Difference |
|---|---|
| JOTRE | No Return button: downloadrout is commented out everywhere in routing_browse.htm. No enable_return_on_audit key. No Ready to Return tab. |
| Timas | Same as JOTRE. |
Master, Jadestone and Medco match on the gate, the button, the auditor roles, the Transmittal class and the Ready to Return tab.
Clear Return Status
Dormant. The toolbar button is commented out.
The feature is not reachable from the UI in any of the big five. The still-broken handler remains in the code. In master, Jadestone, Medco and Timas it would fatal if act=clearreturn were posted directly, because it calls the decommissioned iw_mysql_query(). In JOTRE that helper still works, so there the handler would actually run, with the bug below. Checked 2026-09-06.
Overview
In v2, Clear Return Status was an active Audit-tab button with a well-known naming bug: despite "Clear", it set rout_return_status='Y' (mark returned) rather than clearing it. The v2 doc flagged this for a v3 fix.
What actually happened in v3: the fix did not land. Instead, the button was commented out. The feature is gone from the UI, but the broken handler is unchanged.
What the field means
rout_return_status on ts_routing: 'Y' = returned to originator, 'N' = not returned. 'N' is written only by internal approve and sign-off paths, never by a user-facing button.
Current state of the button
// procBarAudit.addButton('aud_clear', 10,"Clear Return Status","bar_channel_trace_all.svg");The line is commented out, and so is the overflow-menu entry further down. The button is never created, so there is no UI path to invoke act=clearreturn. This is the same in all five forks.
The handler, unchanged and still broken
} elseif ($_POST["act"] == "clearreturn") {
if ($_LOGIN_GROUP_ID != "admin" && !havePrivilegesResource($_LOGIN_GROUP_RESOURCE, $mod_code, 'edit')) {
echo "denied"; exit;
}
$sumId = explode(",", $_POST['id']);
for ($k = 0; $k < count($sumId); $k++) {
if (iw_mysql_query("update ts_routing set rout_return_status='Y' where rout_id='$sumId[$k]'", "...")) {
echo "Row Updated.";
} else {
echo "Error clear return status.";
}
}
// br_logs($_LOGIN_ID,'clear',$qry);
exit;
}Every v2 defect is still here:
- Still sets
'Y', not'N', the same inverted behaviour the name promised to fix. - Still does not set
rout_return_date. A "cleared" row shows Returned = Yes with an empty return date. - Audit log still commented out (
// br_logs(...)). - Still
iw_mysql_querywith unescaped$sumId[$k]: SQL-injection-prone, and on the legacy API rather than$pdo. In four of the five forks that legacy API throws, which is the only reason the handler cannot do damage there.
The only v3 change to the handler itself is the privilege check: v2 used havePrivileges($_LOGIN_GROUP_ID, ...); v3 uses resource-based havePrivilegesResource($_LOGIN_GROUP_RESOURCE, ...) with an admin bypass. The new authorisation model, applied here but not fixing the core bug.
Database tables affected
| Table | Operation | Notes |
|---|---|---|
ts_routing | update | rout_return_status='Y' per rout_id. Does not set rout_return_date. |
What a real fix would need
Write 'N' and clear the date, or rename the action to "Mark as Returned" and set the date. Restore logging. Escape the input or move to $pdo. Until then, leave the button commented out.
Instance differences (Clear Return Status)
Checked on 2026-09-06.
| Instance | Difference |
|---|---|
| JOTRE | iw_mysql_query() is still live, so a direct act=clearreturn POST runs and sets 'Y' without a date. Everywhere else the same POST fatals on entry. |
Button state and handler text are identical in all five forks.
Related
- Document Routing: the overview.
- Action Indicated: the routing action codes.