diff --git a/en/docs/reference/maintainer/activiti.md b/en/docs/reference/maintainer/activiti.md index c252c6a..d4861df 100644 --- a/en/docs/reference/maintainer/activiti.md +++ b/en/docs/reference/maintainer/activiti.md @@ -156,6 +156,19 @@ the instance. | Currently active | Yes, on every 审核 click | Yes, in every multi-document business flow | No — `bCheckflowCheck = false` in code | | Tooling | Just a stored proc | Stored procs + module-tree configuration | BPMN modeler at `/modeler/*` | +### A real Path-1 customisation example + +[Slice 5](../../slices/05-customer-sql-override.md#worked-example-2-builds-a-multi-level-approval-workflow) +walks through 万昌's `领班驳回.sql` — a customer-side multi-level +approval rejection. It's the canonical example of how customers +extend Path 1 when the single-`bCheck` flag isn't enough: they +`ALTER TABLE` to add multiple approval flags (`bManager`, `bIPQC`, +`bDeputy`, …), write transition procs following the +`Sp__check_` naming convention, and +emit audit entries via a custom `sp_add_flow_log`. This is the +empirically-observed customisation channel — Activiti deployment +is not seen in any `script/客户/` directory. + ### Why this design works for xly's audience The printing-industry ERP customers run rule-driven business diff --git a/en/docs/slices/05-customer-sql-override.md b/en/docs/slices/05-customer-sql-override.md index 23ad3bc..e54ec63 100644 --- a/en/docs/slices/05-customer-sql-override.md +++ b/en/docs/slices/05-customer-sql-override.md @@ -143,6 +143,140 @@ mysql --defaults-file=$HOME/.my.cnf xlyweberp_saas_ai \ diff /tmp/std.sql script/客户/重庆展印/Sp_SalSalesCheck.sql | head -200 ``` +## Worked-example 2: 万昌 builds a multi-level approval workflow + +The 重庆展印 example above replaces *one* proc body. The +**`script/客户/万昌/`** directory shows a more ambitious pattern: the +customer extends the schema and builds a multi-level approval +workflow that the standard framework doesn't ship. + +The customisation tree (excerpt): + +``` +script/客户/万昌/ +├── 计件工资/ +│ ├── 日报审核/ +│ │ └── 领班驳回.sql ← this slice's anchor +│ ├── 报表/ +│ │ ├── 包装补时.sql +│ │ ├── 员工大废品.sql +│ │ ├── 班组大废品率查询报表.sql +│ │ ├── 手工质检组返工.sql +│ │ └── Sp_Manual_quality_inspection_rework.sql +│ └── 计件工资核算/ +│ ├── 计件工资/ +│ │ ├── sp_piece_rate_j.sql +│ │ ├── sp_piece_rate_JZ.sql +│ │ ├── sp_piece_rate_other.sql +│ │ └── sp_piece_rate_w.sql +│ ├── 员工工资汇总查询/员工工资汇总查询.sql +│ ├── Sp_BtnEven_CalcJsHs.sql +│ └── sp_btn_WorkOrderAssessmentPassRate.sql +├── Sp_getworkorder_calc_cb.sql +└── … +``` + +The Chinese-named subdirectories (`计件工资`/`日报审核` = "piece-rate +wages / daily-report approval") encode the customer's organisational +flow into the file system itself. A maintainer reading `ls` knows +which business process each script belongs to. + +### What the rejection script actually does + +`领班驳回.sql` ("Foreman Rejection") is 185 lines defining +`Sp_mftproductionreportmaster_check1_0`. The naming is xly's +state-transition convention: `Sp_
_check_`, +so `check1_0` means "transition from state 1 (approved) back to +state 0 (draft)" — i.e., a rejection. + +Trimmed body of the central UPDATE: + +```sql +SET p_setSql = CONCAT('bManager = 0, + bIPQC = 0, + bDeputy = 0, + bSubmit = 0, + bWorkshopManager = 0, + bCheck = 0, + sRejectMemo = ''', p_sRejectMemo, ''', + sMReserve1 = ', p_textareaValue); + +Set @sSqlStmt = CONCAT('Update mftproductionreportmaster + Set ', p_setSql, ' + Where sId = ''', p_sTmpId, ''' + AND sBrandsId = ''', sBrId, ''' + AND sSubsidiaryId = ''', sSuId, ''''); +PREPARE sSqlStmt FROM @sSqlStmt; +EXECUTE sSqlStmt; + +CALL sp_add_flow_log(p_sTmpId, p_sTmpId, '驳回', '驳回', '驳回', + sMakePerson, p_sRejectMemo, @sReturn, @sCode); +``` + +So one button click resets **six** approval flags simultaneously, +appends to a per-row rejection-reason history, and writes to a +custom audit log. + +### What's customer-side and not in standard + +Verified against the dev DB recon target (`xlyweberp_saas_ai`): + +| Customisation | Standard schema? | 万昌 needs to add it | +|---|---|---| +| 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. | +| `sRejectMemo` rejection-reason history column | **No** | Yes — `ALTER TABLE` to add a longtext. | +| `sp_add_flow_log` audit-log proc | **No** — does not exist in standard. | Yes — wholly customer-defined. | +| Naming convention `Sp_
_check_` | **No** — no procs in DB use this pattern. | Yes — 万昌's convention. | +| Hook into the framework's button machinery | Yes — `gdsconfigformslave.sButtonParam` points at the proc name. | (configuration only) | + +So 万昌's "Foreman Rejection" workflow is **a customer-built +state-machine atop xly's button primitive**: schema extension + +custom procs + custom audit log. The framework provides only the +button-press dispatch (via `/business/genericProcedureCall*` or the +button-param hook on the form-slave). Everything else — what state +the document is in, what flags toggle, what audit text gets logged — +is customer-side. + +This is fundamentally different from how Activiti would solve the +same problem (BPMN graph + assignee model + Activiti's task table). +xly's framework lets the customer choose either model: +- **Activiti style**: deploy a BPMN, link via `gdsmoduleflow`, + flip `ConstantUtils.bCheckflowCheck = true` (see + [activiti.md](../reference/maintainer/activiti.md#path-3-activiti-bpmn-workflow-gated-currently-disabled-in-code)). +- **万昌 style**: extend the schema, write transition procs, drop + them under `script/客户///`, apply manually. + +The 万昌 style is what the codebase actually shows in production- +adjacent customisations — Activiti is wired but no customer +directory under `script/客户/` deploys a BPMN, while 万昌-style +schema-extending workflows DO show up. That's the empirical answer +to "how is workflow customised in this repo?": **schema-extending +stored procs delivered via per-customer override scripts**. + +### Customer customisation patterns at a glance + +Of the 18 customer override directories, most don't customise +*workflow* per se — they customise **calculations and reports**. +The breakdown of what each directory contains: + +- `万昌` (14 files): includes the `领班驳回.sql` workflow extension, + plus piece-rate wage calculation procs. +- `千彩` (50 files): the most heavily customised customer. Mostly + per-tenant calculation overrides (`Sp_Calc_*`, `Sp_Inventory_*`, + `Sp_Manufacture_*`) and one workflow-list view + (`viw_NoSalSalesChecking`). +- `重庆展印` (2 files): replacement of one sales-check proc + a + companion view, as documented above. +- `朝阳` (8), `金宣发` (8), `无锡中江` (8), `亚明威` (6), `福雅` (5), + `金九` (5), `快马` (4), and others: smaller calc / report + overrides. + +So the workflow customisation pattern (schema extension + transition +procs + custom audit) is **rare** — it's worth doing only when the +customer's process genuinely doesn't fit a single-step approval and +the standard framework's `bCheck` toggle isn't enough. Most customer +divergence is calculation logic, not workflow shape. + The companion view `viw_salsaleschecking_pro.sql` exists for the same reason — when the override needs a join shape the standard doesn't provide, the engineer authors a customer-specific view, drops it into