Editors and Submission
The Novusoft editor and submission surfaces are separate boundaries:
- Component::editor resolves a module-specific
editor.phpinclude and uses it to add form options or permission controls before rendering the modal. - Component::submit resolves feature-specific submit hooks, normalizes posted fields, persists through MainModel, optionally runs an after-submit hook, finalizes the transaction, and returns a JSON-shaped response.
An editor option or form field describes submitted input; it does not prove that persistence succeeded.
Submission stages
flowchart TD
A["Component::submit"] --> B["before-submit.php if present"]
B --> C["Start transaction"]
C --> D{"Action is batch?"}
D -- "no" --> E{"Action is delete?"}
E -- "no" --> F["Normalize submitted fields"]
E -- "yes" --> G["Set deleted payload and resolve ID(s)"]
F --> H["Feature submit.php if present"]
G --> H
H --> I["MainModel::postProcess"]
D -- "yes" --> J["Normalize each selected row"]
J --> K["batch-submit.php if present"]
K --> L["batch_update_insert"]
L --> M{"Persistence result truthy?"}
M -- "yes" --> N["Log batch"]
M -- "no" --> O["Set submission result"]
N --> P{"Collected duplicate list truthy?"}
P -- "yes" --> Q["duplicateDataBatch"]
P -- "no" --> O
Q --> O
I --> O
O --> R{"Result is truthy?"}
R -- "yes" --> S["after-submit.php if present"]
R -- "no" --> T["Skip after-submit.php"]
S --> U["Finalize transaction"]
T --> U
U --> V["Return setJSON(data)"]
Within Component::submit in imspr/novusoft/modules/Component/Controllers/Component.php, the before-submit include is resolved from the module name and prefix and occurs before $this->model->dbTrans(true). For non-batch actions whose action is not delete, the handler normalizes fields and then includes the module/prefix-specific submit.php when present. Delete actions instead set $update['deleted'] = 1, resolve one or more IDs, and include the same feature hook before MainModel::postProcess, which performs the normal insert or update.
Batch actions normalize each selected row separately and use the batch path. That path can include batch-submit.php, then calls batch_update_insert. If that result is truthy, the source logs the batch; it invokes duplicate handling only when the collected duplicate list is truthy, then continues to the common result handling. If the result is not truthy, neither of those guarded batch side effects runs.
The generic path checks the truthiness of $res before including after-submit.php. It then calls $this->model->dbTrans(false) and returns the assembled $data through setJSON.
results.success is therefore a source-defined boolean derived from $res on the generic path; no execution record retrieved for this repository proves that a submission actually succeeded or failed.
Editor sources
Component::editor constructs the module-specific path:
ROOTPATH.'/'.PROJECT.'/include/'.$moduleInfo->name.'/'.$_prefix.'editor.php'
If that file exists, it is included before the form modal is rendered. The following feature files provide independently maintained option or permission sources.
[
{
"title": "Catalog, PPMP management, and purchase request options",
"body": "`imspr/novusoft/include/catalog/editor.php`, `imspr/novusoft/include/ppmp-management/editor.php`, and `imspr/novusoft/include/pr/editor.php` provide an `option` helper. For `category`, each uses the fixed values `POLICY`, `QUALITY PROCEDURE`, `WORK INSTRUCTION`, and `FORM`. Other types read distinct values from `tbl_h_master_category`. Catalog maps `department`, `devision`, `section`, and `unit` to `DEPT`, `ABBREV`, `ABBREV1`, and `ABBREV2`; PPMP management and purchase request use those columns as option keys and map them to `DEPT`, `DIV`, `SECTION`, and `UNIT` as display values."
},
{
"title": "User-account role source",
"body": "`imspr/novusoft/include/user-account/editor.php` maps active `tbl_user_roles` records into the `user_role_id` select source, using `role_name` for labels and `user_role_id` for values."
},
{
"title": "User-role module permissions",
"body": "`imspr/novusoft/include/user-role/editor.php` joins `tbl_modules` to `tbl_role_permissions`. Non-add actions use the selected role or null permissions; add actions select null permissions. The query limits results to backend modules and orders them by `level` and `order`. For modules with a table, regular `tbl_fields` rows are selected for that table in `order` sequence and used to build the `editor_batch` field selector."
},
{
"title": "User-role stages",
"body": "The user-role editor exposes `PR`, `OBR`, `RFQ`, `TWG`, `ABSTRACT`, `BAC`, `PO`, `CMO`, `PO_RELEASE`, `GSO`, `SUP_REQ`, `ACCTG`, `CHECK`, and `TREASURER` stages. Each stage represents `update`, `comment`, `pending`, `for-checking`, `approved`, and `denied` actions through `stages_role` inputs."
}
]
The corresponding imspr/novusoft/include/user-role/submit.php override serializes the posted stages_role structure into the update payload's stages field. This is a payload transformation; the persistence result is produced later by the generic submission path.
Feature-local submission boundaries
Feature submit files alter the normalized payload or stop the generic path before persistence:
imspr/novusoft/include/new-item-catalog/submit.phpremovesnew_unit_costfrom non-delete updates when the postedyearselectvalue is greater than2024.imspr/novusoft/include/pr/submit.phpandimspr/novusoft/include/pr-backup/submit.phpadd purchase-request totals and status-related values to the update payload.imspr/novusoft/include/pr-catalog/submit.phpsets purchase-request status when a status is posted and contains the same status-related structure, but itsstatus_remarksandaccount_descassignments are line-commented and do not populate those values through those lines.
The generic submit handler includes these files only when the resolved module/prefix-specific file exists. Keep their mutations separate from MainModel::postProcess, which owns the actual normal insert or update.
Persistence and batch limits
For non-batch actions, MainModel::postProcess in imspr/novusoft/models/MainModel.php:
- adds audit fields and inserts for
add, returning the database insert ID when the insert result is truthy; - adds audit fields and updates either one primary key or a list of primary keys for other actions;
- returns the primary ID or IDs only when the database result is truthy, otherwise returning
false.
Batch persistence uses batch_update_insert, which constructs an INSERT ... ON DUPLICATE KEY UPDATE statement and sends it through the database connection. At the inspected value sink, each non-empty value is interpolated into a quoted SQL fragment such as '$dat'; no parameter binding or escaping is source-proven there. Treat batch payload values as reaching this unresolved SQL-safety boundary.
[!WARNING] A truthy
results.successvalue is not an observed completion record. It reflects the truthiness of the persistence result used by the source, and the batch path additionally relies on affected-row behavior from the assembled SQL statement.
Early exits and disabled behavior
[!CAUTION] The PPMP-management delete override looks up selected
tbl_ppmp_catalogrecords wherelock = 1. In the inspected delete branch, after that lookup it unconditionally setsresults.successtofalse, puts the lookup result in the message, and calls$_includeHelper::jsonexit($data); the source does not guard that failed response on whether the lookup returned a locked record. That branch exits before the genericpostProcess, after-submit hook, transaction finalization, and generic response continuation.
The purchase-request filter also contains a disabled creator restriction. In imspr/novusoft/include/pr/view/filter.php, the role-3 user_id_created hidden input is between /* ?> and <?php */ block-comment delimiters, so that control is not submitted by the inspected filter source. The separate office_id restriction for non-admin roles remains outside that block.
When maintaining these surfaces, verify each feature hook and editor helper independently: editor declarations shape the form, submit overrides shape the payload or control early exits, and the generic path derives results.success from its persistence result. An early-exit override can set a response independently; the PPMP-management delete branch sets a failed response and exits before generic persistence.
Updated