Multiple Sign Off
Module: Document Routing, Inbox. Sign off several routings with one answer instead of opening each one. Master got this back with INA01-866; before that the handler ran on the decommissioned iw_mysql_query() and every attempt ended in a fatal error.
How this page was verified
Read on master on 2026-09-16 at commits e230a25be and c63dd6658 (INA01-866), which were local and not yet on origin/main. The four other big five forks were grepped on their origin/main on the same day.
One answer, one project, one Action Indicated. Each selected routing is then processed on its own and follows the same rules as the single Sign Off for that role. A routing that cannot be signed off is reported by name and the rest still go through.
User flow
In the Inbox, tick rows and click Sign Off (
inb_multisignoff_act). The button needs the sameeditaccess as the other Inbox actions, or theadmingroup.One row ticked opens the normal single Sign Off window instead. With no row ticked the grid asks you to select one.
Three guards run before the popup, all server-side:
Guard Message when it fails Same project ( cmd=checkSimilarProject)You cannot signoff document with different Project. You are a performer on every row, or the routing's author ( cmd=checkRoutingForSignOff)You have no access. Same Action Indicated ( cmd=checkSimilarActionIndicated)You cannot signoff document with different action indicated. The Multiple Sign Off (<role>) popup opens (
cmd=multisignoff). It shows the fields of that role plus a shared Comments box. The role is read from the selected rows on the server, not sent by the browser.Answer and click the button. The browser posts
act=multiplesignoffwith the selectedmap_idlist.A green toast means every routing was signed off. Otherwise a list appears: each refused document with its reason, under a title such as "3 document(s) signed off, 1 could not be".
Fields by role
The role is ts_map_routing_to.rout_indicate. The fields are the same ones the single Sign Off shows.
| Role | rout_indicate | Fields in the popup | Button |
|---|---|---|---|
| R Review | 8 | Comments / No Comments | Yes, sign off these documents |
| I Information | 5 | Comments / No Comments | Yes, sign off these documents |
| L Leader | 11 | Return Code | Yes, sign off these documents |
| C Checking | 15 | Return Code | Yes, sign off these documents |
| S Responsible | 16 | Return Code | Yes, sign off these documents |
| A Approval | 9 | Return Code, plus Next Expected Submission when enable_next_issue_code = 1 and the field is visible for those performers | Yes, sign off these documents |
| D Transmit | 12 | none | two buttons, see below |
| anything else | — | none | "This Action Indicated has no sign off." |
- The Return Code list is scoped to the Action Indicated, the same filter as the single Sign Off: only codes whose
doc_res_organizationcontains the performer'saction_desc. - Next Expected Submission follows
isNextExpectedVisibleForSignoff(). It is hidden when any selected performer sits on the project non-owner side or the routing is procurement. The list holds the project'sexternalissued statuses.
D - Transmit answers with two buttons
D - Transmit has no dropdown. It gets the same two answers as its single Sign Off:
| Button | Return Code used |
|---|---|
| Yes - Proceed routing | the first approved code of that Action Indicated, by doc_res_sort |
| No - Return to Contractor DC | the first rejected code, by doc_res_sort |
A side with no matching code gets no button.
A rejected Return Code
A Return Code whose doc_res_action is rejected is a Return, exactly as in the single Sign Off:
- the performer row gets
rout_status_resp = '3'(Return), not'4'; - the routing is closed and every row still open is set to Closed Without Action (
closeRoutingWithSweep()), for A, for C / S, and for a performer inside the Document Controller part of the routing; - the routing is not handed to the next sequence;
- D - Transmit does not publish the document to the Document Library.
A rejection by A, or by C / S outside the Document Controller part, is still queued for the cron, so the _R document and the return Transmittal are produced, just as a single Sign Off Return does. Rejections by D, by L, and by performers inside the Document Controller part are not queued.
Partial results
Every routing is checked on its own, and a failure is reported instead of stopping the batch:
| Reason shown | Cause |
|---|---|
| Sorry, you can't response. | The row belongs to a resource you do not hold |
| You have responded to this routing! | Already answered |
| This route have closed!. | The routing is closed |
| You cannot signoff document with different Project or action indicated. | The row does not match the first one that passed |
| Procurement routings must be signed off one at a time. | Procurement routings go through their own flow |
| The selected Return Code is not available for <action>. | The code is out of scope for that Action Indicated |
| Please select Comments or No Comments. / Please select Next Expected Submission. | A required field was empty |
| You didn't reroute this routing, so you can not sign off this one! | The row was re-routed but has no child rows |
| Sign off failed. Please try again or sign off this document on its own. | An exception; the detail goes to the PHP error log |
What runs in the background
The sign off itself is written straight away. The slow parts are queued in ts_multiple_signoff_list, one row per performer (rout_id, map_id), and done by cron/cron_multiple_signoff_v2.php:
- D - Transmit: the Document Library record, the outgoing Transmittal and the library folder, in one database transaction.
- A, rejecting C / S, and the last I - Information: the approved
_Rdocument throughgetPDFAfterApprove.php, then the return Transmittal.
Without that cron line nothing of this happens. Setting it up is a manual step on every server, see Cron jobs.
Database tables affected
| Table | Operation | When |
|---|---|---|
ts_map_routing_to | update (rout_status_resp, rout_response, rout_result_code, rout_comments, rout_resp_date) | every sign off |
ts_routing | update (rout_status='close', rout_return_status='N', approve date) | final or rejected sign off |
ts_documents | update (doc_res_id, next_expected_id, doc_appdate) | A - Approval |
ts_multiple_signoff_list | insert or update (multi_status, cron_ongoing, map_id) | queued rows |
ts_documents, ts_map_documents_library, ts_map_doc_file, ts_map_routing_file | insert | the cron, D - Transmit publish |
The map_id column was added by the migration 20260915090000_alter_ts_multiple_signoff_list_add_map_id. Queue rows written before it have no map_id and the cron skips them.
Gotchas and known issues
- The popup reads the role from the first selected row. The guards make every row share it, so a selection that slips past them would be answered with the wrong role's fields.
- A group with "action as group" stamps every member of the routing group and stores no Return Code, the same as the single Sign Off.
- Non-MDR routings ("New" routings) are signed off and closed, but nothing is queued: master has no auto publish for them.
Instance differences
Checked on 2026-09-16 on each fork's origin/main.
| Instance | Difference |
|---|---|
| Jadestone | Its own implementation, not master's. Adds rules master does not have: a sign off is refused while reviewers are still reviewing or when the due date has passed, and the cron prepends the Routing Slip to the approved document. Has an Internal Document publish path for non-MDR routings. |
| Medco | Its own handler and its own cron_multiple_signoff_v2.php, which also asks for the Routing Slip (with_slip=1). |
| JOTRE | Still the legacy handler on iw_mysql_query(), which works there. The Inbox Sign Off button is commented out in routing_browse.htm, so the feature is not reachable from the toolbar. |
| Prima Energy | Legacy handler, and iw_mysql_query() throws, so it would fatal. The Inbox Sign Off button is commented out there too. |
Related
- Sign Off: the single sign off this copies, and the field rules in full.
- Cron jobs: the background job and how to set it up.
- Action Indicated: the routing action codes.