Skip to content

MDR

The first tab of Master Doc. Register: the register grid, its toolbar, and the buttons that move documents (Import MDR, Upload, Upload Transmittal, Generate Transmittal).

The Civil group opened: one row per MDR entry, with the template columns of the project
The Civil group opened: one row per MDR entry, with the template columns of the project · click to enlarge

One row per ts_documents_master entry, grouped by discipline by default. The columns are the project's MDR template (getProjectSelectedColumn, maintained under Project Register), which is why two instances show different column sets. The KTP project shows ID, Document Number, Title, Vendor Codes, Last Revision, Project Code, Discipline, Document Type, Area Number, Route Type, Document Size, Contract / PO Number, Weight Factor, Document Category, Previous Document Number, Remarks, Document Sub-Type and Drawing Category.

ButtonWhat it opensWhere it is described
Uploadthe Upload File dialog: pick files, then the upload option step matches each file to an MDR row and creates the library revision and the routingUpload)
Deleteremoves the selected MDR rows, their matrix rows and their ts_mdr_progress rowadmin only
Import MDRthe Import MDR from Excel Template dialog: download the template, upload the filled oneImport MDR)
Replace MDRthe same dialog in replace mode: rows that exist are updated, not duplicatedImport MDR)
Upload Transmittalthe return flow of the audit-based instancesUpload Transmittal)
Searchthe Search MDR Data dialog, basic and advancedbelow
Sorta sort dialog on the template columns
Export Excelcmd=exportXLS, the grid as a workbook
Printthe Select Column dialog, then the grid printed with the ticked columns (cmd=printgrid)
Ungroup, Regroup Bycmd=regroup: change the groupingbelow
Upload: files are dropped here first; the matching to MDR rows happens on the next step
Upload: files are dropped here first; the matching to MDR rows happens on the next step · click to enlarge
Import MDR from Excel Template: download the template, fill it, upload it
Import MDR from Excel Template: download the template, fill it, upload it · click to enlarge

cmd=search opens documents_master_search.htm with two tabs. Basic Search is one text box matched against the document number and title of the current folder, or of every folder when Search in all folders is ticked. Advance Search is the usual field / operator / value rows on the template columns, plus a sort. The result replaces the grid until Refresh.

Search, basic tab
Search, basic tab · click to enlarge
Search, advanced tab: field, operator, value, and a sort
Search, advanced tab: field, operator, value, and a sort · click to enlarge
Print: tick the columns, then the grid prints as a table
Print: tick the columns, then the grid prints as a table · click to enlarge
Regroup By: the grouping column, the sort column and the order
Regroup By: the grouping column, the sort column and the order · click to enlarge

The Regroup By list is Discipline, Document Type and the columns the instance has switched on: enable_doc_category, enable_system_code, enable_doc_classification, enable_func_code and enable_contract_po_number each add one (cmd=getRegroupOption). The choice is saved per user and project in ts_grouping_parameter, so the grid opens the same way next time.

Import MDR

Overview

Import MDR bulk-creates the MDR planning grid (ts_documents_master) and the routing matrix (ts_matrix_rout) from an Excel (.xlsx) file. It is a two-phase staged import with a red-cell preview, on $pdo, and in most forks it supports several matrix types.

Where it sits in the workflow

Import MDR (this)  → ts_documents_master (grid) + ts_matrix_rout (matrix)

Upload (file)      → ts_documents (register) + ts_routing            ← see Upload

Upload Transmittal → stamps submission no. onto register / routing   ← see Upload Transmittal

User flow

  1. Documents Master, Import MDR (import_act, edit privilege).
  2. Choose MDR or VDR (dm_is_vdr), pick the .xlsx, Upload.
  3. A preview grid shows. Invalid cells are highlighted red and block the commit until fixed.
  4. Confirm to commit the rows into the MDR.

How it works

  • Phase 1, stage and parse: act=importexcel in documents_master_handler_post.php calls proceedTempMDRImport(), then saveTemporaryDocs() in documents_master.inc.php. It reads the file with PhpSpreadsheet, takes the first data row from ts_projects.mdr_row_as_value_start, resolves the column-to-field mapping from ts_mdr_template_setting joined with ts_mdr_column, and stages each row plus its matrix as JSON into ts_docmaster_metadata_temp.
  • Phase 2, validate (preview): duplicate document numbers, date format (dd-Mon-yy), plan date format (dd-mm-yyyy), originator in ts_company, sub-discipline and discipline combination.
  • Phase 3, commit: proceedSaveTempdata() calls insertMetadataToDocumentsMaster(), which writes ts_documents_master through the Mdr model (model/mdr.php), and insertMatrixData() writes ts_matrix_rout.

Both staging functions and the Mdr model exist in all five forks.

Data and template contract

Column layout is data-driven per project, not hard-coded: mdr_row_as_value_start plus ts_mdr_template_setting and ts_mdr_column define which Excel column maps to which ts_documents_master field, and where the user-matrix block begins. Matrix action codes map to sequences in insertMatrixData(): R → sequence 1, L → sequence 2, A → sequence 3, anything else → 4.

Sheet order is metadata columns, then the User Matrix block (doc_number type only), then Plan Date columns.

Matrix types

getProjectMdrMatrixType() reads the project's matrix type: doc_number, issued_status or transmission_type. Master, Jadestone, Medco and JOTRE have it. Timas does not; it knows a single matrix type.

Plan Date columns (INA01-809, master only)

The template carries one Plan Date <ISSUED_CODE> column per row in ts_issued_status for the project, second header line DD-MM-YYYY, written after the User Matrix block by mdrSettingDownload(). Import stores the value in ts_mdr_progress.progress_content at ['two'][<issued_code>]['plan_date'] as Y-m-d, the same field Edit Progress writes. Written and read for every matrix type: the plan date is per Issued Status and has nothing to do with the matrix type.

  • Located by header text, never by counting columns. saveTemporaryDocs() matches row 1 against Plan Date <code> and accepts only codes the project actually has, then stages each value under a plan_date_<ISSUED_CODE> key in temp_data. Metadata stays keyed by column letter. Both the preview grid and the commit read them back by code via extractPlanDatesFromTempData().
  • Why not a column count. Counting back from the last column breaks when the file was downloaded before an Issued Status was added or removed. Counting forward with count(get_doc_column_master2()) - 1 breaks because that function filters ts_mdr_template_setting.is_import = 1 while the template writer (mdrSettingList()) does not. On the INA01 dev data that is a two-column error.
  • An empty cell imports fine and stores an empty plan date. A file with no Plan Date columns leaves anything already stored untouched, so older template files still import unchanged.
  • The format check happens at cmd=temporaryMetadataValidation (d-m-Y), reported in the invalidDateCellList2 bucket. That bucket's branch order in templates/tracking_v2/documents_master_browse.htm must match the branch order that picks $data["msg"], or the red cells and the message disagree.

Only master has this

extractPlanDatesFromTempData() exists in master alone. The other four forks import no plan dates from the MDR template.

Database tables affected

TableOperationNotes
ts_docmaster_metadata_tempinsert / select / deletestaging (row plus matrix as JSON)
ts_documents_masterinsert (via Mdr)one row per spreadsheet row; dm_is_vdr from the option
ts_matrix_routinsertreviewer matrix per document
ts_mdr_progressinsert / updateweight factor plus plan date per Issued Status (plan date: master only)
ts_projects, ts_mdr_template_setting, ts_mdr_columnselectrow start plus column mapping
ts_issued_status, ts_progress_gateselectPlan Date columns plus progress skeleton
ts_company, ts_sub_discipline, ts_docdiscipline, ts_org_personselectvalidation plus reviewer lookup

Gotchas and known issues

  • Layout is data-driven. A wrong mdr_row_as_value_start or mapping silently reads headers as data. Start from the project-specific template.
  • Reviewer-name lookups fail silently. An unresolved matrix name yields an empty person_id and that matrix entry is skipped, leaving routing gaps that only Upload) surfaces later.
  • One staged import per user and project. Starting a new import overwrites an unconfirmed one.
  • The User Matrix block must end at its merged header, not at getHighestColumn(), because the sheet no longer ends with the matrix. saveTemporaryDocs() reads the end of the merge for User Matrix and keeps only the column letter. Using the raw cell address (CE1) leaves the row digit in the per-row range, so CE1 . 4 becomes CE14 and the read spans thousands of rows instead of one. That is INA27-236, a 504 on a 2,600-row file. A small file looks fine either way, so only a large file catches it.

Instance differences (Import MDR)

Checked on 2026-09-06.

InstanceDifference
TimasNo getProjectMdrMatrixType(); single matrix type only.
Jadestone, Medco, JOTRE, TimasNo Plan Date columns in the template and no extractPlanDatesFromTempData().

Upload

Overview

Upload ingests document files, creates the register row (ts_documents) for a PDF, stores the file, and kicks off routing from the project's matrix. The engine is on $pdo, with feature-flag gating and DMS-reference auto-mapping. The UI is simple: the old options dialog (Convert / Modify Routing / Force Upload) is gone in master, and PDF watermarking is disabled. It also carries two known bugs forward, see Gotchas.

Prerequisites

1. Import MDR / Add   → ts_documents_master (planning grid)     ← Import MDR
2. Upload (this)      → ts_documents (register) + ts_routing    ← creates + routes
3. Upload Transmittal → stamps submission no. onto those rows

The document number must exist in ts_documents_master, and the routing matrix ts_matrix_rout must be populated. Upload rejects the file otherwise ("User matrix empty").

User flow (master)

  1. Documents Master, Upload (upload_act, the upload privilege).
  2. The Upload File window opens directly. Pick an Issued Status (hidden if the project's filename scheme has no issued_code), add files in the multi-file uploader, Start Upload.
  3. A confirmation prompt asks "Whether originator of document is correct?" before the file goes up.
  4. Each file is validated, stored, and, for PDFs, registered and routed. Results return as JSON per file.

Filename matters. Document number, revision and title are parsed from the filename per ts_file_name_setting. An unmatched number or a malformed name is rejected.

What was simplified or removed

In master's documents_master_browse.htm the upload_act button calls multiuploadOptionProcess() straight away; the line that opened the old dialog, // multiuploadOption();, is commented out. So the three v2 choices are no longer offered:

Old optionWhat it didStatus in master
Convert Document(s) (valConvert)Ran pdf_converter.php to detect oversize PDF pages, queued them in ts_routconvert_queue, and held the doc out of inboxes until converted.Not selectable. The handler still has if ($_GET['valConvert']==1) include "pdf_converter.php", but the parameter is never sent, so Convert is dead code.
Modify Routing(s) (valModify)Staged the new routing into ts_routmaster_temp so the uploader could review it before notification emails went out.Not selectable. The upload_routing and modify_routing_process toolbar buttons inside showUpload are commented out. Routing follows the "send now" path.
Force Upload (forceStatus)Skipped the next-expected-submission and previous-revision-returned guards for out-of-sequence uploads.Not selectable as a toggle. The next-expected guard is governed by the enable_next_issue_code config flag instead.

Also removed: watermarking of SPRD / VOID PDFs. The routine that stamped superseded or void PDFs with their issued description on upload has no live line left in the handler in any of the five forks. An enable_watermark flag exists in the seed of master, Jadestone, Medco and JOTRE, default 0, and the upload handler does not read it.

Medco brought the dialog back, with one option

INA28-229 (2026-07-12) re-activated the Upload Option dialog in Medco with Force Upload only. upload_act there runs checkPerms('upload', multiuploadOption), and multiuploadOptionProcess() calls showUpload(0, 0, 0, valForce). Convert and Modify Routing stay unavailable.

Jadestone asks no originator question

Its upload form sets showOriginator: false with the note "originator is not asked per file anymore, it is always the logged in user". The other four forks show the prompt.

How it works

The handler documents_master_multiupload_upload.php validates the filename, looks up the MDR row, checks the matrix, inserts ts_documents, stores the file (ts_file_explorer, ts_map_documents_library, ts_map_doc_file), and creates routing (ts_routing, ts_map_routing_file, ts_map_routing_to) with due dates. Issued Status drives review durations, the next-expected-submission check, and the issued metadata stored on the records. DMS reference values are copied into ts_map_routing_file through MODULE_REFERENCE_TABLE_CONFIG and getAllDmsRefDataByRelatedTable() in tracking_function.inc.php, in all five forks.

Library folder: Deliverables only at upload

In v2, Upload auto-derived a nested library folder from the document's MDR metadata via getLibCodeAutoGenerateFromDocMaster(), shaped by ts_projects.project_east_java (Medco / Ophir / East Java hierarchies).

Upload does not do that any more. The function is no longer referenced from the upload handler at all, in any of the five forks. Instead the uploaded document is filed into a single fixed folder, the project's Deliverables folder, via getLibCodeDeliverablesByProjectCode(). The MDR-metadata nested placement happens later, at routing auto-publish (approver sign-off), not at upload. See Auto Folder Creation and Sign Off.

Timas files differently

Timas's upload handler has no getLibCodeDeliverablesByProjectCode() and no enable_autopublish read. It resolves the project folder from ts_doc_library directly. Read the handler before assuming the Deliverables behaviour there.

The D placement rule

When the matrix contains a D (Transmit / DocCon) row, the handler enforces its placement and defers auto-publish. Details under Action Indicated. Timas has no docconFound check.

Database tables affected

TableOperation
ts_documents_masterselect, update (dm_revision)
ts_documentsinsert (PDF), update (doc_due_date)
ts_file_explorer, ts_map_documents_library, ts_map_doc_fileinsert
ts_matrix_routselect (matrix)
ts_routing, ts_map_routing_file, ts_map_routing_toinsert

Gotchas and known issues

  • Hard-coded matrix_doc_no='4'. Still present in all five forks. The action-count query filters WHERE tmr.matrix_doc_no='4', a literal, so it always counts document number 4's matrix actions, not the uploaded document's.
  • Hard-coded is_trash='y'. Still present in all five forks. The ts_map_routing_file inserts bind $is_trash="y", flagging every routing-file link as trash.
  • Matrix must exist or upload fails. Run Import MDR) first.
  • Rollback is manual, not transactional. A mid-way error can leave orphans.
  • Dead code that looks like features. Convert and Modify Routing still have backend code but nothing sends their parameters.
  • cron/cron_issue_staging_oversize.php exists but is a file-size notification job. It is not a replacement for the removed Convert step.

Instance differences (Upload)

Checked on 2026-09-06.

InstanceDifference
MedcoUpload Option dialog re-activated with Force Upload only (INA28-229). forceStatus is read twice in the handler.
JadestoneNo originator confirmation prompt. The originator is always the logged-in user.
TimasNo docconFound rule, no getLibCodeDeliverablesByProjectCode(), no enable_autopublish read in the upload handler.
JOTREMatches master on every item checked.

Upload Transmittal

Overview

Upload Transmittal imports a filled Transmittal Sheet (.xlsx) and stamps matching register documents with a submission number and a previous-document reference. The layout contract and cell positions are the ones INACT has always used. In master the importer is on $pdo, checks the file type, and no longer updates routing remarks.

It is not a generic importer: it only updates existing ts_documents rows, matched on exact doc_number plus doc_rev.

Prerequisites and order

1. Import MDR / Add   → ts_documents_master
2. Upload (file)      → ts_documents (register) + ts_routing
3. Upload Transmittal → stamps submission no. / prev-doc onto those rows

Uploading a transmittal before step 2 matches zero rows.

User flow

  1. Documents Master, Upload Transmittal (import_transmittal_act, edit privilege).
  2. Pick the filled Transmittal Sheet .xlsx, Save.
  3. On success the server responds Success.; a duplicate name responds Transmittal <name> already exist.

How it works

The act=importtransmittal handler in documents_master_handler_post.php:

  1. File type check. A non-Excel file is refused with "Invalid file type. Please upload an Excel file."
  2. Duplicate check. The filename without extension is compared with ts_correspondence.corr_doc_number.
  3. Save the file. uploadTransmittal() writes it to docUpload/correspondence/.
  4. Parse the Excel with PHPExcel, the legacy library. First sheet to array.
  5. Read the header cells. H9 transmittal number, H7 contract, B7 from-company.
  6. Insert ts_correspondence (corrkind_id=1). library_code is looked up from the incoming-transmittal default folder rather than the old hard-coded 1.1.
  7. Record the file. ts_file_explorer, ts_map_corr_file.
  8. Loop document rows from row 25, stopping at the first empty column A. For each row, update ts_documents (doc_submission_number, doc_number_prev) WHERE doc_number=<A> AND doc_rev=<F>. In master, Jadestone and Medco the old update of ts_routing.rout_remarks is commented out.
  9. Resolve the sender. ts_company.company_desc LIKE '%<from>%'.

Data and template contract

CellMeaning
H9Transmittal Number
H7Contract Number
B7From, the sender company

Document table: header row 24, data from row 25. A = Document Number, F = Rev, N = previous document (doc_number_prev), P = Remarks.

What happens to an unmatched row

In master, the per-row UPDATE is a prepared statement and the import aborts with "Failed to map transmittal file5." only when the statement fails. A row that matches zero documents passes silently. Medco adds rowCount() == 0 checks and fails explicitly on an unmatched row; whether one unmatched document aborts the whole Medco import was not pinned down.

Database tables affected

TableOperationNotes
ts_correspondenceinsert, updateheader (corrkind_id=1); company_id after lookup
ts_file_explorer, ts_map_corr_fileinsertstored file plus link
ts_documentsupdatedoc_submission_number, doc_number_prev (match doc_number plus doc_rev)
ts_routingupdate, JOTRE and Timas onlyrout_remarks from column P
ts_companyselectsender resolution

Gotchas and known issues

  • Exact revision match required. A trailing space or a different format in column F means the row is silently not updated.
  • Update-only. Documents missing from the register are not created.
  • Remarks are not imported in master. Column P is parsed but stored nowhere. JOTRE and Timas still write it to ts_routing.rout_remarks, which had a column-mismatch caveat across template versions.
  • Still PHPExcel here, while Import MDR) uses PhpSpreadsheet.
  • Mostly unescaped SQL on cell values in the older parts of the handler. Treat uploads as trusted input.

Instance differences (Upload Transmittal)

Checked on 2026-09-06.

InstanceDifference
JOTREThe ts_routing.rout_remarks update is live. Column P is stored.
TimasSame as JOTRE.
MedcoExplicit failure on an unmatched row (rowCount() == 0).

Master and Jadestone match.

Generate Transmittal

Dormant. Not reachable from the UI in any of the big five.

The toolbar button is commented out; the backend logic is intact. Checked on 2026-09-06: the commented button, the cmd=generateTransmittal handler, the removed prototype file and the Content-Type typo are identical in all five forks.

Overview

Generate Transmittal produces a filled Transmittal Sheet (.xlsx) for a selected MDR document by merging it into a per-company Excel template, then streaming it as a download. It is the intended producer of the sheet that Upload Transmittal) consumes. Do not describe it to users as available.

Why it does not appear in the UI

In documents_master_header.inc.php, note the leading //:

php
// $tpl->Assign('toolbar_generate_transmittal','procBarMDR.addButton("generate_transmittal_act", 13, "Generate Transmittal", ...);');

The template variable is never assigned, so the button is never rendered. The dispatch and the backend route exist but nothing fires them.

How it would work when enabled

cmd=generateTransmittal in documents_master_handler_form.php looks up the document in ts_documents_master, picks the originator's template from ts_company_transmittal (comp_file_name, in docUpload/routing/), loads it with PHPExcel, fills fixed cells, writes the single document row, and streams TRANSMITTAL <date>.xlsx.

Data and template contract

Per-company .xlsx, uploaded under Reference, Company. Cells: G6 project, G9 contractor (To), G16 attention, G12 address, O48 issued-by, S46 date, and the document row at row 53 (B53 item number, C53 document number, L53 title). Reads only, no writes.

Known defects in the dormant code

  • Single document only. There is no loop; one document per sheet.
  • Content-Type typo. The response header sends pplication/... instead of application/....
  • The orphaned prototype documents_master_create_transmittal.php from v2 has been removed in every fork.
  • The OOP Transmittal class (HTML2PDF) exists for the routing Return feature and is not wired to this generator.

How to re-enable

Uncomment the button, ensure each originator has a template in ts_company_transmittal and docUpload/routing/, fix the Content-Type typo, and test the round-trip through Upload Transmittal). Treat it as a real code change with a work item.

Instance differences (Generate Transmittal)

Checked on 2026-09-06. None. All five forks are identical on every item above.