Skip to content

Document Routing

Module: routing, menu entry Document Routing under DMS. A routing is one document revision sent to a list of people in sequence, each with an action to perform. This screen is where those routings are created, received, answered and audited. Each tab has its own page (see the table below). The routing action codes are documented in Action Indicated on this page.

How this page was verified

Written from the master code on 2026-09-06, with the screens opened on inact-ktp.eris.place (KTP production snapshot, project VDRL, user Pangesti Risang Ardyanseto, a Document Controller). KTP's template is older and has no Ready to Return or Transmittal Out tab; the other six match master. Opening the New form created draft routing 7351 in that snapshot; it was deleted again from the Drafts tab.

Document Routing, Inbox tab: the routings waiting for this user, grouped by the action they have taken
Document Routing, Inbox tab: the routings waiting for this user, grouped by the action they have taken · click to enlarge

The tabs

routing_browse.htm declares eight tabs. Three are shown to everybody; the rest depend on the user's group, a privilege or a setting (routing_header.inc.php and the template's hideTab / showTab calls):

TabWhat it listsQuery, from the handlerWho sees it
Inboxroutings where the user is a recipient and the routing is on their inboxrouting_handler_inbox.php: rout_have_send = 1 AND rout_is_oninbox = 1, rt.resource_id in the user's resources, current project. Grouped by Action Taken: Open, re-Route, Return, Sign Off, Closed Without Actioneverybody
Sent Itemsroutings the user created and sentrouting_handler_outbox_audit_drafts.php, outbox: rout_author = me AND rout_have_send = 1. Grouped Open / Closeeverybody
Auditevery sent routing of the project, whoever sent itsame file, audit: rout_have_send = 1 AND rout_is_oninbox = 1, project filter. Grouped by project, Open / Close, then by dategroups admin, Document Controller, Procurement Controll ($group_routing_auditor)
Ready to Returnroutings whose approver has answered and that wait for the return transmittalrouting_handler_ready_to_return.phpenable_return_on_audit = 1 and the ready_to_return privilege on routing
Draftsroutings created but not yet submitteddrafts: rout_author = me AND rout_have_send = 0everybody
Comment Sheetcommented files attached to routingsrouting_handler_commentsheet.php: ts_map_routing_file rows with rout_commented_bythe same three groups as Audit
Completedroutings that reached the endrouting_handler_completed.php. Grouped Approved / Rejected by the approver's responseeverybody
Transmittal Outfiles sent out in batch transmittalsrouting_handler_transmittal_out.php: ts_transmittal_out_fileswhen batch_routing_enabled is on, or the consolidated routing view is on for the user

The grid columns are the same across tabs, with small differences: Inbox shows Doc Number, Title, Rev, Rev Date, Issued Status, Originator, Author, Action, Due Date, Sign Off Date, Routing Status; Sent Items, Audit and Drafts show Author, Doc Number, Title, Rev, Rev Date, Issued Status, Originator, Return Code, Routing Status; Completed adds Closed By. Routing Status is the text of the current step ("Reviewer to Action", "Completed").

The Routing Slip

Every routing opens as a Routing Slip page in the same tab, in three sections. View (from Sent Items, Audit, Completed) is read-only; Edit and Response unlock the parts the user may change.

Routing Slip (View): Routing Detail and the Routing To grid
Routing Slip (View): Routing Detail and the Routing To grid · click to enlarge
The Attachment section: the document revision and its file, with Compare, Publish and Unpublish
The Attachment section: the document revision and its file, with Compare, Publish and Unpublish · click to enlarge
SectionContentTable
Routing DetailNo, Author, Title, Date, Originating Company, Routing Type, Remarks, Transmittal Ref; the invoicing block (Invoice No, Amount, Currency, dates, Status) when the routing type is an invoicets_routing
Routing Toone row per recipient: Resource, User ID, Action Indicated, Sequence#, Start Date, Due Date, Action Taken, Response Date, Response, Comments, Remarks, Inbox Statusts_map_routing_to
Attachmentthe routed revision (rout_type = 1) and any extra files: Number, File, Title, Rev, Remarks, Upload By, Size; Compare, Publish, Unpublishts_map_routing_file, ts_file_explorer

Inbox Status is the sequence engine made visible: Auto sent means the row's sequence has been reached and the routing sits in that person's inbox; Not yet sent means an earlier sequence is still open. When the last person of a sequence signs off, the next sequence is sent (getRoutingToNextSequence), and the approver's sign-off closes the routing (rout_status = 'close').

Creating a routing

Routing Slip (Add): the draft exists as soon as the form opens; Submit Routing sends sequence 1
Routing Slip (Add): the draft exists as soon as the form opens; Submit Routing sends sequence 1 · click to enlarge

New on Inbox, Sent Items or Drafts opens routing_add.htm and immediately inserts a draft (rout_have_send = 0) with the next routing number. Filling the detail, adding recipients under Routing To (New, Delete, Refresh) and attachments, then Submit Routing sets rout_have_send = 1, marks the sequence 1 rows rout_is_oninbox = 1, and mails them. Close without submitting leaves the draft in the Drafts tab, where it can be edited or deleted.

Most routings are not created here. The Upload step of the MDR builds them from the Distribution Matrix, which is why a manual routing is the exception.

Action Indicated

What "Action Indicated" is

The Action Indicated is the role a recipient holds on a routing: what that person is asked to do with the document (Review, Approve, be informed, lead, and so on). It is the single most important per-recipient attribute. It drives which response fields show at sign-off, how the routing sequences and ends, which PDF annotations and stamps are allowed, and which notifications fire.

Every action has three identifiers, used in different places:

IdentifierExampleWhere used
action_id (number)9ts_map_routing_to.rout_indicate, the recipient's action on a live routing
action_matrix_type (one letter)Ats_matrix_rout.matrix_rout_action, the action assigned in the MDR matrix
action_desc (label)Approvalshown in the UI

So the matrix stores the letter (for example A), while a recipient row stores the numeric action_id (for example '9'). Both resolve to the same ts_routing_action_indicated row.

Supported actions

getRoutingActionIndicatedList() in reference_function.inc.php only returns actions whose action_matrix_type is in $ACTION_CODE_FOR_ROUTING, a code constant in additional_global_variables.inc.php. In master, Jadestone, JOTRE and Timas it reads:

php
$ACTION_CODE_FOR_ROUTING = ["C", "S", "D", "R", "I", "A", "N"];

Medco adds two codes

Medco's list is ["C", "S", "D", "R", "I", "A", "N", "L", "W"]. L (Leader) and W (Information Owner, action_id 17) are offered for routing there and nowhere else. See Instance differences.

The allowlist resolves to these supported values, in two families:

  • Approval-type: A, D, C, S, and in Medco L. These can set a Result Code at sign-off, and because a Result Code can be an approve or a reject code, Approval-type actions can reject the document. That is the defining trait.
  • Non-approval: R, I, N. These cannot set a Result Code, so they cannot reject. They record comments (R, I) or are only notified (N).
action_idaction_descmatrix typeFamilyrout_daysreviewResponse field shown at sign-off
9ApprovalAApproval-type0Return Code + Next Expected Submission
12TransmitDApproval-typeReturn Code²
15CheckingCApproval-typeReturn Code
16ResponsibleSApproval-typeReturn Code
11LeaderLApproval-type1Return Code¹. Medco only in the allowlist.
8ReviewRNon-approval5Comments / No Comments
5InformationINon-approval0Comments / No Comments
14NotifyNNon-approval0no inbox response, see rules_action below

Next Expected Submission is shown only for A (Approval). No other row lists it: D/C/S/L only ever get the Return Code. Within A there is one more gate: it is hidden for the non-owner side, so in practice it surfaces for the project owner's Approver. The full logic is under Sign Off, Next Expected bottom line).

¹ Leader uses the standard Return Code. In master and Medco L is folded into both approval blocks of the response form (routing_resp.htm), so a Leader sees the same Return Code field as the other Approval-type actions. There is no separate "Leader Return Code". The old #return_code_leader row wrote the same ts_map_routing_to.rout_result_code column with the identical option list. In Jadestone, JOTRE and Timas the orphaned return_code_leader row still sits in the template as dead markup.

² D (Transmit) is not just an approval row. It is the Document Consolidation (DocCon) checkpoint and behaves differently from the other Approval-type actions. See the D section.

The field-gating logic by matrix type is documented under Sign Off, Response fields by role).

Not supported, or legacy

These rows exist in ts_routing_action_indicated but are not in $ACTION_CODE_FOR_ROUTING, so they are not offered for routing:

  • O, Originator (id 13): has a matrix type but is excluded from the current allowlist. It was in the previous, commented-out list.
  • L, Leader (id 11), everywhere except Medco. The sign-off code still handles rout_indicate = 11, and the matrix import writes whatever letter the template holds, so a Leader row can still appear on a routing. It is just not offered by the list function.
  • No matrix type at all, legacy non-routing dispositions: Action (1), File (2), See Me (3), Comments (4), Note And Return (6), Copy (7), Endorse (10).

Supported is not the same as in use

The allowlist is what the system allows. Which actions a project actually uses is data-driven by its MDR matrix template. In many installs the matrix only uses A (Approval) and L (Leader) from the Approval-type family, plus R and I. Check ts_matrix_rout.matrix_rout_action for your project.

D (Transmit) is the Document Consolidation (DocCon) checkpoint

D is the odd one out among the Approval-type actions. It is not really a reviewer role. It is a position in the routing chain: the Document Consolidation (DocCon) point that divides the internal side of the routing from the external side. Almost everything special about D follows from that. Several plausible-looking D paths are dead code and are called out below so nobody rediscovers them as features.

It defines the internal / external boundary

getPositionResourceMatrix() in routing.inc.php records the matrix index of the D row as doccon_postion and counts D rows as countDoccon. checkIsInternalApprover() then treats a responder as internal when their position is at or before doccon_postion and external when it is after. D itself sits at that position. This split drives which finalize branch a sign-off takes in routing_handler_post.php. Both functions exist in all of the big five.

It constrains matrix placement, enforced at upload

When a matrix contains a D, the live upload handler documents_master_multiupload_upload.php enforces:

  • D must be at or before A. A D sequenced after the Approver is rejected.
  • All D rows on one sequence, all A rows on one sequence.
  • The Approver is the final step. Nothing is sequenced past it.

No other action triggers a placement rule. A second copy of this rule lives in the matrix-edit-screen handlers, but those sit behind commented-out toolbar buttons. The live enforcement is the upload one.

Timas has no D rule at upload

The docconFound check does not exist in Timas's upload handler. The other four have it.

Its presence defers auto-publish

Also at upload: library consolidation and auto-publish run only if (!$docconFound). A D in the matrix means publishing waits for the consolidation step instead of happening at upload time.

On a normal sign-off it publishes the document plus an outgoing transmittal

In the live finalize path, when the D signs off and enable_autopublish = 1 and enable_transmittal = 1 and the routing is issued_matrix = 'external', the D branch in routing_handler_post.php:

  1. inserts the document into the register (ts_documents), then
  2. generates a Transmittal("out"), the original / submission transmittal, and files it to the document (ts_map_doc_file plus doc_submission_number). It is published alongside the document.

The "in / out" naming is crossed. Read carefully before trusting a label.

The transmittal D produces is type "out", the submission. The external Approver's branch produces the type "in" copy, which is actually the return. Only "in" gets the (R) suffix, in routing_handler_transmittal.class.php. The destination columns are also crossed: the return ("in") writes ts_documents.outgoing_transmittal_no, while the submission ("out") writes ts_documents.doc_submission_number. Even the submission number string ends in a literal -IN. So a transmittal that looks incoming in the data may well be D's outgoing one. In code terms: D = outgoing / submission; the Approver = the (R) return.

Batch routing changes how D responds

Everything above is non-batch behaviour, where D signs off through the normal Response dialog with its Return Code and no Next Expected. In a batch-routing project (ts_projects.batch_routing = 1) on an external routing, D is pulled out of normal sign-off into the dedicated External Transmission flow: the transmit_dc inbox button and a redirect in js/routing.js. Its rows are auto-signed by setAutoDCSignoff() in routing.inc.php. See Sign Off) for the two modes side by side.

Dead D code. Do not rediscover these as features.

  • Server-side dcc role and stamp enforcement. $rulesPersonAs = 'dcc' in routing.php and the rout_indicate == 12 stamp-mandatory check next to it are computed, but their consumers are entirely commented out. Nothing enforces them.
  • Two older transmittal blocks in routing_handler_post.php are inside if (false), so dead. The live transmittal generation is the D and external-Approver pair above. All five forks carry three if (false) blocks in this file.

Per-action behaviour rules: rules_action

Not relied upon. Effectively not working for now.

The rules_action blob is read client-side, but the server-side enforcement is not wired up. The code that would act on it (the dcc and role checks and the stamp-mandatory checks in routing.php) is commented out, and L (Leader) has rules_action = NULL entirely. Treat the table below as design intent, not guaranteed runtime behaviour.

Each supported action carries a rules_action JSON blob that declares how that action behaves. v2 had no such column. It is read by getActionRules.php and getUserLevel.php, and seeded by DefaultTsRoutingActionIndicated.php. Medco additionally carries a one-off script, migrations/scripts/MIG-013_routing_action_rules.php, that the other forks do not have.

The JSON has three areas:

  • pdf_viewer: which annotations and stamps the role may use, and create / edit / delete / update permissions in the PDF viewer.
  • notification: which emails fire (rejected, doc_con_issued, doc_return).
  • routing: the workflow rules below, plus per-outcome (approved / rejected) allowed and disallowed stamp lists.

Key routing.* flags by action, from the seed:

Actionreceived_in_inboxbypass_on_overdueend_routingreturn_code
Approval (A)yesyesyesyes
Transmit (D)yesyes
Checking (C)yesyesyes
Responsible (S)yesyes
Review (R)yesyes
Information (I)yesyes
Notify (N)no
Leader (L)rules_action is NULL

What they mean:

  • received_in_inbox: whether the action lands in the recipient's inbox. Notify (N) is false, so it notifies without creating an inbox task.
  • bypass_on_overdue: the recipient can be auto-bypassed when overdue.
  • end_routing: completing this action can finish the routing (the A/D/C/S Approval-type actions).
  • return_code: a return or result code applies. The JSON flags Approval and Review; the form renders a result-code input for all Approval-type actions but not for R or I. A small JSON-versus-form discrepancy.

Where Action Indicated is consumed

  • MDR matrix / Upload: the matrix import writes matrix_rout_action (letter) per resource. Upload turns the matrix into per-recipient ts_map_routing_to rows, ordering sequences partly by action. See Upload.
  • Sign Off: rout_indicate (action_id) drives the per-role behaviour and the response-form field gating. Full breakdown under Sign Off, Behaviour by Action Indicated).
  • PDF viewer: rules_action.pdf_viewer would control allowed annotations and stamps, but server-side enforcement is commented out. Treat as design intent.
  • Notifications: rules_action.notification declares which emails fire, but is part of the same not-enforced subsystem.

Database tables

TableColumnHolds
ts_routing_action_indicatedaction_id, action_desc, action_matrix_type, rout_daysreview, rules_actionthe reference definitions
ts_matrix_routmatrix_rout_actionthe action letter assigned in the MDR matrix
ts_map_routing_torout_indicatethe recipient's action_id on a live routing

There are _corr variants (ts_routing_action_indicated_corr, and a char(1) rout_indicate in ts_map_routing_to_corr) used by the correspondence routing flow. The main document routing uses the tables above.

Gotchas and known issues

  • Three identifiers for one action (id / letter / label). The matrix uses the letter, recipient rows use the id. Mixing them up is an easy mistake.
  • Supported (allowlist), in use (matrix data) and defined (table) are three different sets.
  • O (Originator) is defined but not in the routing allowlist. L (Leader) has no rules_action JSON, and outside Medco it is not in the allowlist either.
  • D (Transmit) is not a normal approval row. It is the Document Consolidation checkpoint with placement rules, publish deferral, transmittal generation and a batch-routing-only flow. Treating it as just another Approval-type action will mislead you.
  • The allowlist is a code constant, not a database or admin setting. Changing the supported set is a code change.

Instance differences (Action Indicated)

Checked on 2026-09-06.

InstanceDifference
Medco$ACTION_CODE_FOR_ROUTING adds L (Leader) and W (Information Owner, action_id 17, with its own rules_action). The response form has an extra Information Owner branch keyed on resp_is_terminal. Carries the migrations/scripts/MIG-013_routing_action_rules.php one-off.
JadestoneResponse form still keys field gating on the matrix type like master, but keeps the dead return_code_leader row in the template.
JOTREResponse form gates fields by the action label (`a == 'Approval'
TimasSame form as JOTRE. No docconFound rule at upload, so the D placement constraints and the publish deferral do not exist there.

Master and Jadestone match on the allowlist, the seed and the D mechanics.

Gotchas

  • Audit and Comment Sheet are group-gated by name, not by privilege: the group must be called admin, Document Controller or Procurement Controll (sic). A Document Controller group under another name never sees Audit.
  • New creates a row before anything is typed. Closing the form leaves a draft behind; users who click New to look end up with empty drafts.
  • Inbox is per resource, not per person. A person with two resources sees both; a person with no resource sees nothing.
  • Sent Items and Audit hide routings that left every inbox (rout_is_oninbox = 1 in both queries), which is the same rule the MDR Report uses.
  • Tab visibility is decided at page load. Turning enable_return_on_audit on needs a reload to show Ready to Return.

Instance differences

Checked on 2026-09-06.

InstanceDifference
KTPTemplate without the Ready to Return and Transmittal Out tabs; Comment Sheet exists but its group does not see it.
JOTRE, TimasNo Return button; the label-based response form and the approver-finalize rule described on Return).
MedcoThe Comment Summary report, the W action and the multi-process rework described on Sign Off).
JadestoneAs master.