Skip to content

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

  1. In the Inbox, tick rows and click Sign Off (inb_multisignoff_act). The button needs the same edit access as the other Inbox actions, or the admin group.

  2. One row ticked opens the normal single Sign Off window instead. With no row ticked the grid asks you to select one.

  3. Three guards run before the popup, all server-side:

    GuardMessage 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.
  4. 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.

  5. Answer and click the button. The browser posts act=multiplesignoff with the selected map_id list.

  6. 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.

Rolerout_indicateFields in the popupButton
R Review8Comments / No CommentsYes, sign off these documents
I Information5Comments / No CommentsYes, sign off these documents
L Leader11Return CodeYes, sign off these documents
C Checking15Return CodeYes, sign off these documents
S Responsible16Return CodeYes, sign off these documents
A Approval9Return Code, plus Next Expected Submission when enable_next_issue_code = 1 and the field is visible for those performersYes, sign off these documents
D Transmit12nonetwo buttons, see below
anything elsenone"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_organization contains the performer's action_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's external issued statuses.

D - Transmit answers with two buttons

D - Transmit has no dropdown. It gets the same two answers as its single Sign Off:

ButtonReturn Code used
Yes - Proceed routingthe first approved code of that Action Indicated, by doc_res_sort
No - Return to Contractor DCthe 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 shownCause
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 _R document through getPDFAfterApprove.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

TableOperationWhen
ts_map_routing_toupdate (rout_status_resp, rout_response, rout_result_code, rout_comments, rout_resp_date)every sign off
ts_routingupdate (rout_status='close', rout_return_status='N', approve date)final or rejected sign off
ts_documentsupdate (doc_res_id, next_expected_id, doc_appdate)A - Approval
ts_multiple_signoff_listinsert or update (multi_status, cron_ongoing, map_id)queued rows
ts_documents, ts_map_documents_library, ts_map_doc_file, ts_map_routing_fileinsertthe 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.

InstanceDifference
JadestoneIts 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.
MedcoIts own handler and its own cron_multiple_signoff_v2.php, which also asks for the Routing Slip (with_slip=1).
JOTREStill 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 EnergyLegacy handler, and iw_mysql_query() throws, so it would fatal. The Inbox Sign Off button is commented out there too.
  • 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.