Commit 7a8b9c734bb994e6515d2e58512e1b460f0cd1ba

Authored by zichun
1 parent 4a3d6a28

docs: en wiki — document 万昌's schema-extending workflow customisation

User asked: are there examples of customising workflow other than
the standard hardcoded one? Investigated and found a substantial
customer example.

Findings:
- script/客户/万昌/计件工资/日报审核/领班驳回.sql is a 185-line
  customer-side workflow customisation defining
  Sp_mftproductionreportmaster_check1_0 ("Foreman Rejection",
  state 1 → state 0 transition).
- Naming convention: Sp_<table>_check<currentState>_<nextState>.
  No procs in standard DB use this pattern; it's 万昌's own
  convention.
- The proc resets SIX approval flags simultaneously (bManager,
  bIPQC, bDeputy, bSubmit, bWorkshopManager, bCheck) — 万昌 has
  ALTER TABLEd mftproductionreportmaster to add 5 columns that
  don't exist in the standard schema.
- Adds sRejectMemo rejection-reason history (also custom column),
  appends each rejection's reason + timestamp + foreman name.
- Calls sp_add_flow_log — a custom audit-log proc that doesn't
  exist in the standard DB.
- Includes customer-specific business rules (e.g., block rejection
  if any slave row has bSAPCheck=1 — SAP-sync guard).

So the answer to "any examples of customising workflow":
- Yes — 万昌 has built their OWN multi-level approval workflow on
  top of xly's button-dispatch primitive: schema extension +
  custom transition procs + custom audit log. The framework
  provides only the button-press dispatch (via /business/
  genericProcedureCall* or sButtonParam); everything else is
  customer-defined.
- This is fundamentally different from Activiti — no BPMN, no FSM
  library, no engine. Just SQL + ALTER TABLE.
- Other customer dirs (千彩, 重庆展印, 朝阳, etc.) mostly customise
  *calculations and reports*, not workflow. 万昌's pattern is
  rare but real.

Slice 5 gets a "Worked-example 2" section walking through
领班驳回.sql in detail, plus a customisation-patterns-at-a-glance
breakdown of what each of the 18 customer dirs actually contains.

Activiti.md gets a back-reference: "A real Path-1 customisation
example" pointing at the new slice 5 worked example, with the
empirical observation that Activiti deployment is NOT seen in any
script/客户/ directory.
en/docs/reference/maintainer/activiti.md
@@ -156,6 +156,19 @@ the instance. @@ -156,6 +156,19 @@ the instance.
156 | Currently active | Yes, on every 审核 click | Yes, in every multi-document business flow | No — `bCheckflowCheck = false` in code | 156 | Currently active | Yes, on every 审核 click | Yes, in every multi-document business flow | No — `bCheckflowCheck = false` in code |
157 | Tooling | Just a stored proc | Stored procs + module-tree configuration | BPMN modeler at `/modeler/*` | 157 | Tooling | Just a stored proc | Stored procs + module-tree configuration | BPMN modeler at `/modeler/*` |
158 158
  159 +### A real Path-1 customisation example
  160 +
  161 +[Slice 5](../../slices/05-customer-sql-override.md#worked-example-2-builds-a-multi-level-approval-workflow)
  162 +walks through 万昌's `领班驳回.sql` — a customer-side multi-level
  163 +approval rejection. It's the canonical example of how customers
  164 +extend Path 1 when the single-`bCheck` flag isn't enough: they
  165 +`ALTER TABLE` to add multiple approval flags (`bManager`, `bIPQC`,
  166 +`bDeputy`, …), write transition procs following the
  167 +`Sp_<table>_check<currentState>_<nextState>` naming convention, and
  168 +emit audit entries via a custom `sp_add_flow_log`. This is the
  169 +empirically-observed customisation channel — Activiti deployment
  170 +is not seen in any `script/客户/` directory.
  171 +
159 ### Why this design works for xly's audience 172 ### Why this design works for xly's audience
160 173
161 The printing-industry ERP customers run rule-driven business 174 The printing-industry ERP customers run rule-driven business
en/docs/slices/05-customer-sql-override.md
@@ -143,6 +143,140 @@ mysql --defaults-file=$HOME/.my.cnf xlyweberp_saas_ai \ @@ -143,6 +143,140 @@ mysql --defaults-file=$HOME/.my.cnf xlyweberp_saas_ai \
143 diff /tmp/std.sql script/客户/重庆展印/Sp_SalSalesCheck.sql | head -200 143 diff /tmp/std.sql script/客户/重庆展印/Sp_SalSalesCheck.sql | head -200
144 ``` 144 ```
145 145
  146 +## Worked-example 2: 万昌 builds a multi-level approval workflow
  147 +
  148 +The 重庆展印 example above replaces *one* proc body. The
  149 +**`script/客户/万昌/`** directory shows a more ambitious pattern: the
  150 +customer extends the schema and builds a multi-level approval
  151 +workflow that the standard framework doesn't ship.
  152 +
  153 +The customisation tree (excerpt):
  154 +
  155 +```
  156 +script/客户/万昌/
  157 +├── 计件工资/
  158 +│ ├── 日报审核/
  159 +│ │ └── 领班驳回.sql ← this slice's anchor
  160 +│ ├── 报表/
  161 +│ │ ├── 包装补时.sql
  162 +│ │ ├── 员工大废品.sql
  163 +│ │ ├── 班组大废品率查询报表.sql
  164 +│ │ ├── 手工质检组返工.sql
  165 +│ │ └── Sp_Manual_quality_inspection_rework.sql
  166 +│ └── 计件工资核算/
  167 +│ ├── 计件工资/
  168 +│ │ ├── sp_piece_rate_j.sql
  169 +│ │ ├── sp_piece_rate_JZ.sql
  170 +│ │ ├── sp_piece_rate_other.sql
  171 +│ │ └── sp_piece_rate_w.sql
  172 +│ ├── 员工工资汇总查询/员工工资汇总查询.sql
  173 +│ ├── Sp_BtnEven_CalcJsHs.sql
  174 +│ └── sp_btn_WorkOrderAssessmentPassRate.sql
  175 +├── Sp_getworkorder_calc_cb.sql
  176 +└── …
  177 +```
  178 +
  179 +The Chinese-named subdirectories (`计件工资`/`日报审核` = "piece-rate
  180 +wages / daily-report approval") encode the customer's organisational
  181 +flow into the file system itself. A maintainer reading `ls` knows
  182 +which business process each script belongs to.
  183 +
  184 +### What the rejection script actually does
  185 +
  186 +`领班驳回.sql` ("Foreman Rejection") is 185 lines defining
  187 +`Sp_mftproductionreportmaster_check1_0`. The naming is xly's
  188 +state-transition convention: `Sp_<table>_check<currentState>_<nextState>`,
  189 +so `check1_0` means "transition from state 1 (approved) back to
  190 +state 0 (draft)" — i.e., a rejection.
  191 +
  192 +Trimmed body of the central UPDATE:
  193 +
  194 +```sql
  195 +SET p_setSql = CONCAT('bManager = 0,
  196 + bIPQC = 0,
  197 + bDeputy = 0,
  198 + bSubmit = 0,
  199 + bWorkshopManager = 0,
  200 + bCheck = 0,
  201 + sRejectMemo = ''', p_sRejectMemo, ''',
  202 + sMReserve1 = ', p_textareaValue);
  203 +
  204 +Set @sSqlStmt = CONCAT('Update mftproductionreportmaster
  205 + Set ', p_setSql, '
  206 + Where sId = ''', p_sTmpId, '''
  207 + AND sBrandsId = ''', sBrId, '''
  208 + AND sSubsidiaryId = ''', sSuId, '''');
  209 +PREPARE sSqlStmt FROM @sSqlStmt;
  210 +EXECUTE sSqlStmt;
  211 +
  212 +CALL sp_add_flow_log(p_sTmpId, p_sTmpId, '驳回', '驳回', '驳回',
  213 + sMakePerson, p_sRejectMemo, @sReturn, @sCode);
  214 +```
  215 +
  216 +So one button click resets **six** approval flags simultaneously,
  217 +appends to a per-row rejection-reason history, and writes to a
  218 +custom audit log.
  219 +
  220 +### What's customer-side and not in standard
  221 +
  222 +Verified against the dev DB recon target (`xlyweberp_saas_ai`):
  223 +
  224 +| Customisation | Standard schema? | 万昌 needs to add it |
  225 +|---|---|---|
  226 +| Multi-level approval columns: `bManager`, `bIPQC`, `bDeputy`, `bSubmit`, `bWorkshopManager` on `mftproductionreportmaster` | **No** — only `bCheck`, `sCheckPerson`, `tCheckDate` exist. | Yes — `ALTER TABLE` to add 5 boolean columns. |
  227 +| `sRejectMemo` rejection-reason history column | **No** | Yes — `ALTER TABLE` to add a longtext. |
  228 +| `sp_add_flow_log` audit-log proc | **No** — does not exist in standard. | Yes — wholly customer-defined. |
  229 +| Naming convention `Sp_<table>_check<n>_<m>` | **No** — no procs in DB use this pattern. | Yes — 万昌's convention. |
  230 +| Hook into the framework's button machinery | Yes — `gdsconfigformslave.sButtonParam` points at the proc name. | (configuration only) |
  231 +
  232 +So 万昌's "Foreman Rejection" workflow is **a customer-built
  233 +state-machine atop xly's button primitive**: schema extension +
  234 +custom procs + custom audit log. The framework provides only the
  235 +button-press dispatch (via `/business/genericProcedureCall*` or the
  236 +button-param hook on the form-slave). Everything else — what state
  237 +the document is in, what flags toggle, what audit text gets logged —
  238 +is customer-side.
  239 +
  240 +This is fundamentally different from how Activiti would solve the
  241 +same problem (BPMN graph + assignee model + Activiti's task table).
  242 +xly's framework lets the customer choose either model:
  243 +- **Activiti style**: deploy a BPMN, link via `gdsmoduleflow`,
  244 + flip `ConstantUtils.bCheckflowCheck = true` (see
  245 + [activiti.md](../reference/maintainer/activiti.md#path-3-activiti-bpmn-workflow-gated-currently-disabled-in-code)).
  246 +- **万昌 style**: extend the schema, write transition procs, drop
  247 + them under `script/客户/<name>/<flow-name>/`, apply manually.
  248 +
  249 +The 万昌 style is what the codebase actually shows in production-
  250 +adjacent customisations — Activiti is wired but no customer
  251 +directory under `script/客户/` deploys a BPMN, while 万昌-style
  252 +schema-extending workflows DO show up. That's the empirical answer
  253 +to "how is workflow customised in this repo?": **schema-extending
  254 +stored procs delivered via per-customer override scripts**.
  255 +
  256 +### Customer customisation patterns at a glance
  257 +
  258 +Of the 18 customer override directories, most don't customise
  259 +*workflow* per se — they customise **calculations and reports**.
  260 +The breakdown of what each directory contains:
  261 +
  262 +- `万昌` (14 files): includes the `领班驳回.sql` workflow extension,
  263 + plus piece-rate wage calculation procs.
  264 +- `千彩` (50 files): the most heavily customised customer. Mostly
  265 + per-tenant calculation overrides (`Sp_Calc_*`, `Sp_Inventory_*`,
  266 + `Sp_Manufacture_*`) and one workflow-list view
  267 + (`viw_NoSalSalesChecking`).
  268 +- `重庆展印` (2 files): replacement of one sales-check proc + a
  269 + companion view, as documented above.
  270 +- `朝阳` (8), `金宣发` (8), `无锡中江` (8), `亚明威` (6), `福雅` (5),
  271 + `金九` (5), `快马` (4), and others: smaller calc / report
  272 + overrides.
  273 +
  274 +So the workflow customisation pattern (schema extension + transition
  275 +procs + custom audit) is **rare** — it's worth doing only when the
  276 +customer's process genuinely doesn't fit a single-step approval and
  277 +the standard framework's `bCheck` toggle isn't enough. Most customer
  278 +divergence is calculation logic, not workflow shape.
  279 +
146 The companion view `viw_salsaleschecking_pro.sql` exists for the same 280 The companion view `viw_salsaleschecking_pro.sql` exists for the same
147 reason — when the override needs a join shape the standard doesn't 281 reason — when the override needs a join shape the standard doesn't
148 provide, the engineer authors a customer-specific view, drops it into 282 provide, the engineer authors a customer-specific view, drops it into