gaoluyang
5 天以前 eee03bbfea8b15d1a56ab63bd09a35c6c861c4cb
feat(mes): 不合格品处置联动入库与生产订单

- 让步放行/报废:生成杂项入库单并自动提交、完成入库(处置弹窗选仓库/库区/库位,默认原材料仓/不合格品仓,批次管理物料必填批次号)
- 返工/返修:生成自行生产类型生产订单,BOM/条码自动生成,工艺路线人工补充
- 杂项入库单/行/明细补齐 type、来源单据、批次号、库位字段,并补「让步放行入库(2)/报废入库(3)」字典
- 配置导出脚本 config_export_all_20261108.sql 同步结构与字典
- 同步更新 NCR 前端联调文档与质量流程业务可视化
- 一并提交此前未提交的审批业务单据附件、客户洽谈进度、BOM 导入、员工 API、工位人员、生产任务等改动

Co-Authored-By: Claude Code <noreply@anthropic.com>
已添加8个文件
已修改30个文件
5438 ■■■■■ 文件已修改
docs/mes_qc_ncr_integration.md 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/data/flows.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/data/modules.json 1399 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/index.html 2707 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/mermaid/02-sales-flow.mmd 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/mermaid/03-purchase-flow.mmd 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/project-business/mermaid/05-quality-flow.mmd 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/config_export_all_20261108.sql 178 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerProgressServiceImpl.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/sale/dto/ErpSaleOrderCreateReqDTO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/HrmEmployeeApi.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/dto/HrmEmployeeSimpleRespDTO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/HrmEmployeeApiImpl.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/employee/HrmEmployeeMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdProductBomController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/bom/MesMdProductBomImportExcelVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/bom/MesMdProductBomImportRespVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/workstation/MesMdWorkstationWorkerController.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/workstation/vo/worker/MesMdWorkstationEmployeeRespVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/MesQcNcrController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/vo/MesQcNcrDisposeReqVO.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/vo/MesQcNcrDisposeRespVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMiscReceiptTypeEnum.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ncr/MesQcNcrService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ncr/MesQcNcrServiceImpl.java 303 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/miscreceipt/MesWmMiscReceiptServiceImpl.java 1 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mes_qc_ncr_integration.md
@@ -74,13 +74,47 @@
**PUT** `/mes/qc/ncr/dispose`
处置会联动生成业务单据:让步放行(4)/报废(2) ç”Ÿæˆæ‚项入库单并自动提交、完成入库;返工(3)/返修(6) ç”Ÿæˆè‡ªè¡Œç”Ÿäº§ç±»åž‹ç”Ÿäº§è®¢å•(草稿)。
请求参数:
| å‚æ•° | ç±»åž‹ | å¿…å¡« | è¯´æ˜Ž |
|------|------|------|------|
| id | Long | æ˜¯ | NCR编号 |
| disposition | Integer | æ˜¯ | å¤„置方式(仅 2=报废/3=返工/4=让步放行/6=返修) |
| warehouseId | Long | æ¡ä»¶å¿…å¡« | å…¥åº“仓库;让步放行/报废时必填,前端默认带出原材料仓 |
| locationId | Long | æ¡ä»¶å¿…å¡« | å…¥åº“库区;让步放行/报废时必填 |
| areaId | Long | æ¡ä»¶å¿…å¡« | å…¥åº“库位;让步放行/报废时必填 |
| batchCode | String | æ¡ä»¶å¿…å¡« | æ‰¹æ¬¡å·ï¼›ç‰©æ–™å¯ç”¨æ‰¹æ¬¡ç®¡ç†æ—¶å¿…填,批次不存在会自动创建 |
| handlerUserId | Long | å¦ | å¤„理人ID |
| handleResult | String | å¦ | å¤„理结果说明 |
响应字段(`data`):
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| disposition | Integer | å¤„置方式 |
| docType | String | ç”Ÿæˆçš„单据类型:`MISC_RECEIPT`=杂项入库单,`WORK_ORDER`=生产订单 |
| docId | Long | å•据ID |
| docCode | String | å•据编号 |
| docName | String | å•据名称 |
| message | String | å¯ç›´æŽ¥ç”¨äºŽ toast çš„æç¤ºæ–‡æ¡ˆ |
响应示例:
```json
{
  "code": 0,
  "data": {
    "disposition": 4,
    "docType": "MISC_RECEIPT",
    "docId": 1,
    "docCode": "MI_20261109001",
    "docName": "让步放行入库单-NCR20261109E2E01",
    "message": "已生成让步放行入库单-NCR20261109E2E01并完成入库,入库单号[MI_20261109001]"
  }
}
```
### å…³é—­ NCR
@@ -157,7 +191,7 @@
### çŠ¶æ€æµè½¬
```
待评审(0) â”€â”€è¯„审──▶ å·²è¯„审(1) â”€â”€å¤„置──▶ å·²å¤„ç½®(2) â”€â”€å…³é—­â”€â”€â–¶ å·²å…³é—­(3)
待评审(0) â”€â”€è¯„审(或提交BPM审批 å®¡æ‰¹ä¸­2)──▶ å·²è¯„审(6) â”€â”€å¤„置──▶ å·²å¤„ç½®(7) â”€â”€å…³é—­â”€â”€â–¶ å·²å…³é—­(8)
```
### æ“ä½œæƒé™çº¦æŸ
@@ -175,11 +209,20 @@
| å€¼ | åç§° | è¯´æ˜Ž |
|----|------|------|
| 1 | é€€è´§ | é€€å›žä¾›åº”商 |
| 2 | æŠ¥åºŸ | å†…部报废处理 |
| 3 | è¿”å·¥ | é‡æ–°åŠ å·¥ |
| 4 | è®©æ­¥æŽ¥æ”¶ | ç‰¹é‡‡æŽ¥æ”¶ |
| 5 | é™çº§ä½¿ç”¨ | é™çº§åŽä½¿ç”¨ |
| 1 | é€€è´§ | é€€å›žä¾›åº”商(历史枚举,评审不再可选) |
| 2 | æŠ¥åºŸ | æ‚项入库到不合格品仓 |
| 3 | è¿”å·¥ | ç”Ÿæˆç”Ÿäº§è®¢å• |
| 4 | è®©æ­¥æŽ¥æ”¶ | æ‚项入库到原材料仓 |
| 5 | é™çº§ä½¿ç”¨ | åŽ†å²æžšä¸¾ï¼Œè¯„å®¡ä¸å†å¯é€‰ |
| 6 | è¿”ä¿® | ç”Ÿæˆç”Ÿäº§è®¢å• |
### å¤„置联动规则
| å¤„置方式 | ç”Ÿæˆå•据 | ç›®æ ‡/规则 |
|----------|----------|-----------|
| è®©æ­¥æ”¾è¡Œ(4) | æ‚项入库单(type=2 è®©æ­¥æ”¾è¡Œå…¥åº“),自动提交并完成入库,库存增加 | é»˜è®¤åŽŸææ–™ä»“ï¼Œå¯æ”¹ä»“åº“/库区/库位 |
| æŠ¥åºŸ(2) | æ‚项入库单(type=3 æŠ¥åºŸå…¥åº“),自动提交并完成入库,库存增加 | é»˜è®¤ä¸åˆæ ¼å“ä»“,可改仓库/库区/库位 |
| è¿”å·¥(3)/返修(6) | ç”Ÿäº§è®¢å•(自行生产,草稿态) | äº§å“=NCR物料、数量=不合格数量,BOM/条码自动生成,工艺路线需人工补充后下达 |
### ç¼ºé™·ç­‰çº§æžšä¸¾
docs/project-business/data/flows.json
@@ -5,7 +5,7 @@
      "name": "销售流程",
      "mermaid": "mermaid/02-sales-flow.mmd",
      "color": "#4f9cff",
      "summary": "线索转化客户 â†’ å•†æœºæŽ¨è¿› â†’ æŠ¥ä»·(可AI识别) â†’ åˆåŒ BPM å®¡æ‰¹ â†’ ç”Ÿæˆ ERP é”€å”®è®¢å•并审批(台账含销售类型、手工合同号、产品编号与生产数量) â†’ éœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS â†’ å‘货通知(物流公司/快递费/开票状态,审批默认物流) â†’ å‘货台账按产品平铺 â†’ é”€å”®å‡ºåº“提交(需检验行转待检测) â†’ ç‹¬ç«‹å½•å…¥ OQC åŽæŒ‰ç‰©æ–™+盘号+数量关联回写 â†’ æ‹£è´§å‘运 â†’ å¼€ç¥¨(BPM) â†’ å½•入发票与款项节点 â†’ å›žæ¬¾(先开票校验,创建为未提交,显式提交 BPM ååŒå®¡æ‰¹ï¼Œå®¡æ‰¹é€šè¿‡æ‰å›žå†™å›žæ¬¾è®¡åˆ’);发货单/成品出库单可导出带电子章的 Word ä¾›æ‰‹å¡«ç­¾å­—归档;客户档案支持多收货地址。",
      "summary": "线索转化客户 â†’ å•†æœºæŽ¨è¿› â†’ æŠ¥ä»·(可AI识别) â†’ åˆåŒ BPM å®¡æ‰¹ â†’ ç”Ÿæˆ ERP é”€å”®è®¢å•(合同转来的订单提交免审批直接生效,提交后不可修改;台账含销售类型、手工合同号、产品编号与生产数量) â†’ éœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS â†’ å‘货通知(物流公司/快递费/开票状态,审批默认物流) â†’ å‘货台账按产品平铺 â†’ é”€å”®å‡ºåº“提交(需检验行转待检测) â†’ ç‹¬ç«‹å½•å…¥ OQC åŽæŒ‰ç‰©æ–™+盘号+数量关联回写 â†’ æ‹£è´§å‘运 â†’ å¼€ç¥¨(BPM) â†’ å½•入发票与款项节点 â†’ å›žæ¬¾(先开票校验,创建为未提交,显式提交 BPM ååŒå®¡æ‰¹ï¼Œå®¡æ‰¹é€šè¿‡æ‰å›žå†™å›žæ¬¾è®¡åˆ’);发货单/成品出库单可导出带电子章的 Word ä¾›æ‰‹å¡«ç­¾å­—归档;客户档案支持多收货地址。",
      "steps": [
        {
          "stage": "获客",
@@ -38,7 +38,7 @@
        },
        {
          "stage": "接单",
          "action": "合同一键生成销售订单(XSDD),销售订单再走 BPM;审批通过自动为 needProduction æ˜Žç»†åˆ›å»º MPS",
          "action": "合同一键生成销售订单(XSDD)后在销售管理提交:合同转来的订单免 BPM ç›´æŽ¥ç”Ÿæ•ˆ(20),提交后不可修改;手工/导入订单仍走 BPM。生效后自动为 needProduction æ˜Žç»†åˆ›å»º MPS æµè½¬ç”Ÿäº§ï¼Œæ— éœ€ç”Ÿäº§çš„订单可直接生成仓储发货通知单",
          "roles": [
            "销售",
            "计划员"
@@ -245,7 +245,7 @@
        },
        {
          "stage": "不合格",
          "action": "NCR: å¾…评审(0)→提交BPM审批(2)→审批通过已评审(6)/驳回回待评审→质量部处置(让步放行/返修/返工/报废)→已处置(7)→关闭(8);审批意见由 BPM æµè½¬è®°å½•承载,未发布流程定义或全局免审批时提交后自动通过",
          "action": "NCR: å¾…评审(0)→提交BPM审批(2)→审批通过已评审(6)/驳回回待评审→质量部处置(让步放行/返修/返工/报废)→已处置(7)→关闭(8);处置联动:让步放行/报废生成杂项入库单并自动提交完成入库(处置弹窗选仓库/库区/库位,让步放行默认原材料仓、报废默认不合格品仓,批次管理物料必填批次号),返工/返修生成自行生产类型生产订单(产品=NCR物料、数量=不合格数量、BOM与条码自动生成、工艺路线人工补充);审批意见由 BPM æµè½¬è®°å½•承载,未发布流程定义或全局免审批时提交后自动通过",
          "roles": [
            "质量工程师"
          ]
@@ -364,7 +364,7 @@
      "name": "会计核算流程",
      "mermaid": "mermaid/12-finance-flow.mmd",
      "color": "#3ec9a7",
      "summary": "会计核算:凭证/分录/借贷平衡 -> è¿‡è´¦ -> æ€»è´¦/明细账/报表;固定资产折旧、无形资产摊销。销售采购财务闭环:ERP é”€å”®è®¢å• + MES å·²å®Œæˆå‡ºåº“单 -> CRM å¼€ç¥¨ç”³è¯·ï¼ˆç»‘定订单+出库单)-> å®¡æ‰¹é€šè¿‡ -> å½•入发票并填写合同款/预付款/到货款/验收款/质保金收款节点 -> CRM å›žæ¬¾æŒ‰æ ¸é”€æ˜Žç»†åˆ†æ‘Šåˆ°è®¢å•/出库/开票 -> å®¢æˆ·åº”收对账、已发货未开票、未核销应收账龄、下月回款计划;来票必须同时绑定采购订单与 MES é‡‡è´­å…¥åº“单;凭证可关联来源单据(开票/回款/来票/付款)。",
      "summary": "会计核算:凭证/分录/借贷平衡 -> è¿‡è´¦ -> æ€»è´¦/明细账/报表;固定资产折旧、无形资产摊销。销售采购财务闭环:ERP é”€å”®è®¢å• + MES å·²å®Œæˆå‡ºåº“单 -> CRM å¼€ç¥¨ç”³è¯·ï¼ˆé”€å”®è®¢å•/出库单选填,累计≤合同额)-> å®¡æ‰¹é€šè¿‡ -> å½•入发票并填写合同款/预付款/到货款/验收款/质保金收款节点 -> CRM å›žæ¬¾æŒ‰æ ¸é”€æ˜Žç»†åˆ†æ‘Šåˆ°è®¢å•/出库/开票 -> å®¢æˆ·åº”收对账、已发货未开票、未核销应收账龄、下月回款计划;来票必须同时绑定采购订单与 MES é‡‡è´­å…¥åº“单;凭证可关联来源单据(开票/回款/来票/付款)。",
      "steps": [
        {
          "stage": "建账",
@@ -421,7 +421,7 @@
        },
        {
          "stage": "开票申请",
          "action": "选择合同、销售订单、该订单下已完成出库单,填写开票金额后提交 BPM å®¡æ‰¹",
          "action": "选择合同(销售订单与该订单下已完成出库单均为选填;选择合同时若该合同仅有一张已审核销售订单则自动带出),填写开票金额后提交 BPM å®¡æ‰¹",
          "roles": [
            "销售",
            "财务"
docs/project-business/data/modules.json
@@ -1,701 +1,704 @@
{
 "modules": [
  {
   "key": "mdm",
   "name": "基础主数据",
   "color": "#8a94a6",
   "tag": "MDM",
   "flow": null,
   "positioning": "全系统统一口径的主数据权威源",
   "problem": "物料、单位、仓库在 MES/ERP/CRM å¤šå¤„重复定义导致数据打架",
   "features": [
    "物料与SKU(含批次属性配置12开关)",
    "物料分类树",
    "计量单位与换算",
    "品牌",
    "仓库主数据(含type字段)",
    "MES ç‰©æ–™ç» MdmItemSyncApi åå‘同步"
   ],
   "objects": [
    "物料",
    "SKU",
    "计量单位",
    "仓库"
   ],
   "roles": [
    "主数据管理员"
   ],
   "upstream": [],
   "downstream": [
    "生产",
    "进销存",
    "销售",
    "采购"
   ],
   "coreTables": [
    "mdm_item",
    "mdm_item_sku",
    "mdm_item_category",
    "mdm_unit_measure",
    "mdm_brand",
    "mdm_warehouse"
   ]
  },
  {
   "key": "crm",
   "name": "销售管理(客户关系)",
   "color": "#4f9cff",
   "tag": "CRM",
   "flow": "sales",
   "positioning": "销售前端:从线索到回款的客户经营",
   "problem": "客户资产沉淀、商机转化、合同审批与开票回款管控",
   "features": [
    "线索转化客户",
    "公海池与领取上限",
    "联系人",
    "商机自定义阶段",
    "跟进记录",
    "报价单(AI OCR)",
    "合同(BPM审批→生成销售订单·支持Excel导入)",
    "开票申请(绑定销售订单+MES销售出库单)→审批→录入发票(发票号/日期/附件+合同款/预付款/到货款/验收款/质保金收款节点)",
    "开票(BPM审批·支持Excel导入·同一批数据不可重复导入)→回款(先开票校验·核销明细可拆分到订单/出库/开票)",
    "回款计划(收款节点回写已回款/实际时间/状态)",
    "客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
    "投标保证金台账(客户招标方+已审核销售订单·中标结果/退回状态/退回金额日期)",
    "销售目标",
    "数据权限三级(负责人/只读/读写)"
   ],
   "objects": [
    "线索",
    "客户",
    "联系人",
    "商机",
    "报价单",
    "合同",
    "发票",
    "回款",
    "回款核销明细",
    "销售目标",
    "投标保证金"
   ],
   "roles": [
    "销售内勤",
    "销售经理",
    "财务(开票回款)"
   ],
   "upstream": [
    "主数据(物料)",
    "进销存(销售订单)"
   ],
   "downstream": [
    "进销存(销售订单)",
    "售后(客户)",
    "AI(报价OCR)"
   ],
   "coreTables": [
    "crm_clue",
    "crm_customer",
    "crm_contact",
    "crm_business",
    "crm_sale_quotation",
    "crm_contract",
    "crm_invoice",
    "crm_receivable",
    "crm_receivable_plan",
    "crm_permission",
    "crm_bid_bond"
   ]
  },
  {
   "key": "srm",
   "name": "供应商管理",
   "color": "#c9a86a",
   "tag": "SRM",
   "flow": "purchase",
   "positioning": "供应商档案 + è¯¢ä»·æ¯”ä»·",
   "problem": "供应商信息多开户行/多联系人维护,采购寻源按产品横向比价并快速转为采购订单",
   "features": [
    "供应商档案与分类",
    "多联系人/多银行账户(含银行行号)",
    "询价单:新增时一并录入产品行×参与供应商报价",
    "每个产品单独选定中标供应商",
    "确认后按中标供应商拆分生成采购订单草稿"
   ],
   "objects": [
    "供应商",
    "询价比价"
   ],
   "roles": [
    "采购员",
    "供应商管理员"
   ],
   "upstream": [
    "主数据(物料)"
   ],
   "downstream": [
    "进销存(采购订单)"
   ],
   "coreTables": [
    "srm_supplier",
    "srm_supplier_contact",
    "srm_supplier_bank",
    "srm_supplier_category",
    "srm_inquiry",
    "srm_inquiry_item",
    "srm_inquiry_supplier",
    "srm_inquiry_quote",
    "srm_inquiry_order"
   ]
  },
  {
   "key": "erp",
   "name": "进销存与财务",
   "color": "#5fc79e",
   "tag": "ERP",
   "flow": "purchase",
   "positioning": "商贸与资金单据中枢,向 MES å‘起实物收发",
   "problem": "采购销售单据审批、来票付款控制、订单执行进度回写",
   "features": [
    "采购申请(BPM)→订单(自动/手动生成)",
    "确认收货→MES到货通知",
    "入库/退货数量状态回写",
    "采购来票(绑定采购订单+MES采购入库单·双层剩余额度校验)",
    "付款单(必须关联来票·BPM)",
    "供应商往来(订单/来票/付款三流水·左右展示开票金额)",
    "销售订单(BPM·审批通过自动建MPS)",
    "出库状态回写",
    "收款单(关联CRM客户)",
    "结算账户",
    "AI发票OCR"
   ],
   "objects": [
    "采购申请",
    "采购订单",
    "采购来票",
    "付款单",
    "供应商往来",
    "销售订单",
    "收款单",
    "结算账户"
   ],
   "roles": [
    "采购员",
    "销售员",
    "出纳",
    "会计"
   ],
   "upstream": [
    "CRM(合同)",
    "SRM(询价确认)",
    "主数据"
   ],
   "downstream": [
    "生产(MPS)",
    "仓储(收发)",
    "财务(来票付款入账)"
   ],
   "coreTables": [
    "erp_purchase_request",
    "erp_purchase_order",
    "erp_purchase_invoice",
    "erp_finance_payment",
    "erp_sale_order",
    "erp_finance_receipt",
    "erp_account"
   ]
  },
  {
   "key": "finance",
   "name": "财务管理(会计核算)",
   "color": "#3ec9a7",
   "tag": "FIN",
   "flow": "finance",
   "positioning": "独立核算模块:会计核算 + èµ„产核算 + è´¢åŠ¡æŠ¥è¡¨çœ‹æ¿ï¼ˆä¸Ž ERP è¿›é”€å­˜è´¢åŠ¡å•æ®åˆ†ç¦»ï¼Œyudao-module-finance)",
   "problem": "进销存只管单据不管账,缺少会计科目/凭证/账簿与资产折旧摊销沉淀,无法出账表与资产净值",
   "features": [
    "会计科目树(约68条一级科目种子·可建下级)",
    "记账凭证(字号自动生成·分录借贷平衡前后端双重校验)",
    "凭证状态机 æœªè¿‡è´¦0→过账10/作废20,已过账不可改",
    "总账按期间逐月汇总",
    "明细账逐条分录并结转余额",
    "固定资产+计提折旧",
    "无形资产+计提摊销",
    "会计核算看板(收支/资产/凭证/月度趋势)",
    "凭证来源单据关联(sourceType/sourceId/sourceNo,可回溯开票/回款/来票/付款)",
    "报表取数接入CRM客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
    "附件走系统附件中心 recordType=fin_voucher"
   ],
   "objects": [
    "会计科目",
    "记账凭证",
    "凭证分录",
    "固定资产",
    "无形资产"
   ],
   "roles": [
    "会计",
    "财务主管",
    "资产会计",
    "管理层"
   ],
   "upstream": [
    "进销存(ERP å·²å®¡æ ¸æ”¶è´§/付款/购销单据)",
    "CRM(开票/回款/核销/对账报表)"
   ],
   "downstream": [
    "财务报表看板",
    "BI ç®¡ç†é©¾é©¶èˆ±"
   ],
   "coreTables": [
    "fin_account_subject",
    "fin_voucher",
    "fin_voucher_entry",
    "fin_fixed_asset",
    "fin_intangible_asset"
   ]
  },
  {
   "key": "pro",
   "name": "生产管理",
   "color": "#e0b34f",
   "tag": "MES-PRO",
   "flow": "production",
   "positioning": "生产执行核心:计划-工单-任务-报工",
   "problem": "订单到车间执行的逐层拆解与过程可控",
   "features": [
    "MPS主生产计划(可由销售订单自动创建·合并下发)",
    "工单(按工艺路线自动生成工序任务)",
    "报工(按工序顺序控制·BPM审批)",
    "倒冲扣线边库/产出入库",
    "安灯",
    "工艺路线·工序·工单BOM",
    "研发设计项目(PD·审批归档)",
    "AI å››ç±»é¢„测"
   ],
   "objects": [
    "MPS",
    "生产工单",
    "生产任务",
    "报工单",
    "工艺路线",
    "工单BOM",
    "设计项目"
   ],
   "roles": [
    "计划员",
    "车间主任",
    "操作工",
    "工艺员"
   ],
   "upstream": [
    "进销存(销售订单)"
   ],
   "downstream": [
    "仓储(领料/入库)",
    "质量(IPQC)"
   ],
   "coreTables": [
    "mes_pro_mps",
    "mes_pro_work_order",
    "mes_pro_task",
    "mes_pro_feedback",
    "mes_pro_route",
    "mes_pro_process",
    "mes_pro_work_order_bom",
    "mes_pd_project"
   ]
  },
  {
   "key": "wm",
   "name": "仓储管理",
   "color": "#7e6bd6",
   "tag": "MES-WM",
   "flow": "warehouse",
   "positioning": "厂内 WMS:单据驱动的库存账实一致",
   "problem": "多来源出入库、批次/SN è¿½æº¯ã€åº“存精确到库位",
   "features": [
    "15种出入库单据",
    "库存台账(物料×仓库×库区×库位×批次×供应商)",
    "事务流水只读留痕",
    "调拨成对事务",
    "盘点(静态/动态·盲盘·盘盈盘亏调整)",
    "批次/SN/条码/装箱嵌套",
    "虚拟线边库(允许负库存)",
    "库存占用(5ç±»)",
    "四级冻结",
    "WMS审批配置+BPM",
    "对外 WmsStockApi/WmsTransactionApi"
   ],
   "objects": [
    "到货通知",
    "采购入库",
    "领料",
    "完工入库",
    "销售出库",
    "发货通知",
    "销售退货",
    "供应商退货",
    "调拨",
    "盘点",
    "库存台账",
    "批次",
    "SN"
   ],
   "roles": [
    "仓管员",
    "拣货员",
    "盘点员"
   ],
   "upstream": [
    "采购(收货)",
    "生产(领退料)",
    "销售(发货)",
    "售后(退货)"
   ],
   "downstream": [
    "进销存(数量回写)",
    "财务(成本基础)"
   ],
   "coreTables": [
    "mes_wm_warehouse",
    "mes_wm_material_stock",
    "mes_wm_transaction",
    "mes_wm_item_receipt",
    "mes_wm_product_issue",
    "mes_wm_product_receipt",
    "mes_wm_product_sales",
    "mes_wm_return_sales",
    "mes_wm_transfer",
    "mes_wm_stock_taking_task",
    "mes_wm_batch",
    "mes_wm_sn",
    "mes_wm_stock_reserve"
   ]
  },
  {
   "key": "qc",
   "name": "质量管理",
   "color": "#e06c6c",
   "tag": "MES-QC",
   "flow": "quality",
   "positioning": "四道检验闸口 + ä¸åˆæ ¼å“é—­çŽ¯å¤„ç½®",
   "problem": "来料/过程/出货/退货检验标准化,不合格可追溯处置",
   "features": [
    "IQC æ¥æ–™(到货/外协)",
    "IPQC è¿‡ç¨‹(联动报工)",
    "OQC å‡ºè´§(联动销售出库)",
    "RQC é€€è´§(联动退料/售后)",
    "质检方案·指标·缺陷库",
    "检测结果:合格/特采/退货/报废",
    "NCR:评审→处置(退货/报废/返工/让步/降级)→关闭",
    "AI åˆ¤å®šå»ºè®®"
   ],
   "objects": [
    "IQC",
    "IPQC",
    "OQC",
    "RQC",
    "质检方案",
    "NCR",
    "缺陷记录"
   ],
   "roles": [
    "质检员(IQC/IPQC/OQC)",
    "质量工程师"
   ],
   "upstream": [
    "仓储(到货)",
    "生产(报工)",
    "销售(出库)",
    "售后(退货)"
   ],
   "downstream": [
    "仓储(合格入库/报废出库)",
    "生产(NCR返工)"
   ],
   "coreTables": [
    "mes_qc_iqc",
    "mes_qc_ipqc",
    "mes_qc_oqc",
    "mes_qc_rqc",
    "mes_qc_template",
    "mes_qc_indicator",
    "mes_qc_defect",
    "mes_qc_ncr"
   ]
  },
  {
   "key": "qcreport",
   "name": "智能质检报告",
   "color": "#b06ce0",
   "tag": "QC-REPORT",
   "flow": "quality",
   "positioning": "可视化设计报告模板 â†’ ä¸€é”®å‡ºä»¶å¹¶å†»ç»“历史报告",
   "problem": "各行业质检报告格式各异,排版硬编码改一次发一次版;且历史报告必须可复现、不随业务数据变化",
   "features": [
    "GrapesJS å¯è§†åŒ–设计器(质检组件走注册机制,不硬编码进编辑器)",
    "模板 + å¤šç‰ˆæœ¬ï¼šè‰ç¨¿0/已发布1/已停用2,已发布版本内容不可覆盖",
    "组件数据绑定与独立判定规则引擎(禁用 eval / ScriptEngine)",
    "与前端逐字对齐的 Java æ¸²æŸ“引擎,出站 HTML å‰¥ç¦» on* äº‹ä»¶å±žæ€§ä¸Ž javascript:/vbscript:/data: åè®®",
    "出件接口:preview åªæ¸²æŸ“不落库不消耗编号 / generate æ¸²æŸ“+落库+冻结 data_snapshot / regenerate ç”¨å†»ç»“快照与同一版本重渲",
    "报告编号 QR+yyyyMMdd+4位流水,按天独立,可显式指定(撞号会报出具体编号)",
    "缺口清单只在 preview/generate/regenerate å“åº”里返回,不落库",
    "PDF(Playwright) å½’档与 MES å››ç§è´¨æ£€å•归一化对接待后续轮次"
   ],
   "objects": [
    "报告模板",
    "报告实例"
   ],
   "roles": [
    "报告设计员",
    "质检员(出件)"
   ],
   "upstream": [
    "质量(质检单上下文,本轮由调用方传入)"
   ],
   "downstream": [
    "附件中心(PDF å½’档·后续轮次)",
    "BI"
   ],
   "coreTables": [
    "qc_report_template",
    "qc_report_template_version",
    "qc_report_instance",
    "qc_report_data_source",
    "qc_report_rule",
    "qc_report_render_record"
   ]
  },
  {
   "key": "dv",
   "name": "设备与工装",
   "color": "#59a8b8",
   "tag": "MES-DV/TM",
   "flow": null,
   "positioning": "设备资产健康与生产资源保障",
   "problem": "设备点检保养维修可执行、工装状态可控",
   "features": [
    "设备台账(停机/生产中/保养中)",
    "点检·保养方案与记录",
    "维修工单(草稿→维修→待验收→已确认)",
    "计量器具检定",
    "工装夹具(在库/领用/ç»´ä¿®/报废)"
   ],
   "objects": [
    "设备",
    "点检记录",
    "维修工单",
    "工装"
   ],
   "roles": [
    "设备员",
    "维修工",
    "工装管理员"
   ],
   "upstream": [],
   "downstream": [
    "生产(AI é£Žé™©é¢„测消费设备数据)"
   ],
   "coreTables": [
    "mes_dv_machinery",
    "mes_dv_check_plan",
    "mes_dv_check_record",
    "mes_dv_repair",
    "mes_tm_tool"
   ]
  },
  {
   "key": "aftersales",
   "name": "售后服务",
   "color": "#c78bd6",
   "tag": "AFTER",
   "flow": "aftersales",
   "positioning": "客户反馈到退货/ç»´ä¿®/退款闭环",
   "problem": "售后工单流转、退货与质检入库退款的跨系统联动",
   "features": [
    "工单六态(草稿→处理中→待入库→待退款→完结→关闭)",
    "问题判定(一般/ç»´ä¿®/退货)",
    "客户诉求反馈描述+附件",
    "处理结果+附件",
    "工单流转记录",
    "ç»´ä¿®(现场/返厂/远程)",
    "退货由仓储物流发起→MES é”€å”®é€€è´§å•(原售后退货申请页面已下线)",
    "质检结论/入库/退款三重回调",
    "行级数据权限四视图"
   ],
   "objects": [
    "售后工单",
    "工单流转记录",
    "维修记录",
    "退货申请(后台保留)"
   ],
   "roles": [
    "客服",
    "维修工程师",
    "售后管理员"
   ],
   "upstream": [
    "CRM(客户)",
    "ERP(销售订单)"
   ],
   "downstream": [
    "仓储(销售退货)",
    "质量(RQC)",
    "财务(退款)"
   ],
   "coreTables": [
    "after_sale_ticket",
    "after_sale_ticket_item",
    "after_sale_ticket_flow",
    "after_sale_repair",
    "after_sale_return",
    "after_sale_permission"
   ]
  },
  {
   "key": "hrm",
   "name": "人力资源",
   "color": "#9db85a",
   "tag": "HRM",
   "flow": null,
   "positioning": "员工入转调离与考勤薪酬",
   "problem": "人事流程审批化、离职数据不丢(交接)、算薪自动化",
   "features": [
    "员工档案(入职自动建系统账号)",
    "请假/加班/调岗/离职 BPM å®¡æ‰¹",
    "离职交接同时转移员工ID/用户ID/业务数据 expandUserIds",
    "离职到期自动把员工状态改为离职并停用系统账号",
    "考勤(移动端我的考勤状态·打卡·规则·排班·补卡审批·月度汇总)",
    "薪酬(应发-社保公积金-个税-请假扣款·1.5/2/3倍加班费·发放台账)",
    "社保公积金档案按部门维护(部门下员工共用最新生效档案)",
    "培训证书(SQL先行待实现)"
   ],
   "objects": [
    "员工",
    "请假单",
    "离职申请",
    "交接映射",
    "考勤记录",
    "部门社保档案",
    "薪酬核算"
   ],
   "roles": [
    "HR",
    "部门主管",
    "员工本人"
   ],
   "upstream": [],
   "downstream": [
    "各模块(数据可见性扩展)",
    "system(账号)"
   ],
   "coreTables": [
    "hrm_employee",
    "hrm_leave_application",
    "hrm_resignation_application",
    "hrm_user_handover",
    "hrm_attendance_record",
    "hrm_salary_calculation",
    "hrm_salary_payment",
    "hrm_employee_social_security"
   ]
  },
  {
   "key": "bpm",
   "name": "协同审批",
   "color": "#e6a23c",
   "tag": "BPM",
   "flow": null,
   "positioning": "全系统单据审批中枢(Flowable)",
   "problem": "多模块单据需要灵活可配的多级审批",
   "features": [
    "BPMN+钉钉风格双设计器",
    "流程定义部署",
    "审批事件统一回调(BpmProcessInstanceStatusEvent)",
    "13个业务监听器",
    "表单·用户组·抄送·加签"
   ],
   "objects": [
    "流程模型",
    "流程实例",
    "审批任务"
   ],
   "roles": [
    "审批人(按流程配置)",
    "流程管理员"
   ],
   "upstream": [
    "各业务提交审批"
   ],
   "downstream": [
    "回调更新各业务单据状态"
   ],
   "coreTables": [
    "bpm_process_definition_info",
    "bpm_process_instance",
    "bpm_task",
    "act_*",
    "flowable_*"
   ]
  },
  {
   "key": "ai",
   "name": "AI æ™ºèƒ½",
   "color": "#56c8d8",
   "tag": "AI",
   "flow": "ai",
   "positioning": "单据录入提效与生产决策辅助",
   "problem": "人工录发票/报价慢、质检与排产依赖经验",
   "features": [
    "chat/chatWithImage åŒé€šé“",
    "OCR é¢„å¡«(发票/报价)",
    "质检判定建议",
    "缺料/时长/风险/交付四类预测",
    "RAG çŸ¥è¯†åº“(Milvus)",
    "AI不可用时 fallback äººå·¥"
   ],
   "objects": [
    "知识库",
    "AI模型配置",
    "对话记录"
   ],
   "roles": [
    "采购员",
    "销售",
    "质检员",
    "计划员"
   ],
   "upstream": [
    "各业务提供上下文数据"
   ],
   "downstream": [
    "结构化JSON建议返回业务侧"
   ],
   "coreTables": [
    "ai_chat_conversation",
    "ai_chat_message",
    "ai_model",
    "ai_knowledge",
    "ai_knowledge_document",
    "ai_knowledge_segment"
   ]
  }
 ],
 "platform": {
  "name": "系统底座",
  "capabilities": [
   "JWT+OAuth2 è®¤è¯Â·æ»‘块验证码",
   "RBAC è§’色-菜单-数据范围(全部/部门/本部门及下级/仅本人/自定义)",
   "字典",
   "附件中心(storage-blob æ–‡ä»¶ + storage-attachment æŒ‰ recordType+recordId å¤šæ€å…³è”,全模块强制使用)",
   "编码规则(各业务自实现 Redis å‰ç¼€+日期+流水)",
   "Quartz å®šæ—¶ä»»åŠ¡Â·RocketMQ/Kafka/Redis Stream MQ·WebSocket",
   "多租户已关闭",
   "实际 system_role ä»… 4 ä¸ªï¼šè¶…级管理员/普通角色/CRM管理员/租户管理员,业务分工靠数据权限而非多角色"
  "modules": [
    {
      "key": "mdm",
      "name": "基础主数据",
      "color": "#8a94a6",
      "tag": "MDM",
      "flow": null,
      "positioning": "全系统统一口径的主数据权威源",
      "problem": "物料、单位、仓库在 MES/ERP/CRM å¤šå¤„重复定义导致数据打架",
      "features": [
        "物料与SKU(含批次属性配置12开关)",
        "物料分类树",
        "物料分类含半成品/成品独立大类",
        "生产物料BOM维护与Excel批量导入(导入默认启用,可选覆盖已有用量)",
        "计量单位与换算",
        "品牌",
        "仓库主数据(含type字段)",
        "MES ç‰©æ–™ç» MdmItemSyncApi åå‘同步"
      ],
      "objects": [
        "物料",
        "SKU",
        "计量单位",
        "仓库"
      ],
      "roles": [
        "主数据管理员"
      ],
      "upstream": [],
      "downstream": [
        "生产",
        "进销存",
        "销售",
        "采购"
      ],
      "coreTables": [
        "mdm_item",
        "mdm_item_sku",
        "mdm_item_category",
        "mdm_unit_measure",
        "mdm_brand",
        "mdm_warehouse"
      ]
    },
    {
      "key": "crm",
      "name": "销售管理(客户关系)",
      "color": "#4f9cff",
      "tag": "CRM",
      "flow": "sales",
      "positioning": "销售前端:从线索到回款的客户经营",
      "problem": "客户资产沉淀、商机转化、合同审批与开票回款管控",
      "features": [
        "线索转化客户",
        "公海池与领取上限",
        "联系人",
        "商机自定义阶段",
        "跟进记录",
        "报价单(AI OCR)",
        "合同(BPM审批→生成销售订单·支持Excel导入)",
        "开票申请(销售订单/出库单选填·选合同自动带出唯一已审核订单)→审批→录入发票(发票号/日期/附件+合同款/预付款/到货款/验收款/质保金收款节点)",
        "开票(BPM审批·支持Excel导入·同一批数据不可重复导入)→回款(先开票校验·核销明细可拆分到订单/出库/开票)",
        "回款计划(收款节点回写已回款/实际时间/状态)",
        "客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
        "投标保证金台账(客户招标方+已审核销售订单·中标结果/退回状态/退回金额日期)",
        "销售目标",
        "数据权限三级(负责人/只读/读写)"
      ],
      "objects": [
        "线索",
        "客户",
        "联系人",
        "商机",
        "报价单",
        "合同",
        "发票",
        "回款",
        "回款核销明细",
        "销售目标",
        "投标保证金"
      ],
      "roles": [
        "销售内勤",
        "销售经理",
        "财务(开票回款)"
      ],
      "upstream": [
        "主数据(物料)",
        "进销存(销售订单)"
      ],
      "downstream": [
        "进销存(销售订单)",
        "售后(客户)",
        "AI(报价OCR)"
      ],
      "coreTables": [
        "crm_clue",
        "crm_customer",
        "crm_contact",
        "crm_business",
        "crm_sale_quotation",
        "crm_contract",
        "crm_invoice",
        "crm_receivable",
        "crm_receivable_plan",
        "crm_permission",
        "crm_bid_bond"
      ]
    },
    {
      "key": "srm",
      "name": "供应商管理",
      "color": "#c9a86a",
      "tag": "SRM",
      "flow": "purchase",
      "positioning": "供应商档案 + è¯¢ä»·æ¯”ä»·",
      "problem": "供应商信息多开户行/多联系人维护,采购寻源按产品横向比价并快速转为采购订单",
      "features": [
        "供应商档案与分类",
        "多联系人/多银行账户(含银行行号)",
        "询价单:新增时一并录入产品行×参与供应商报价",
        "每个产品单独选定中标供应商",
        "确认后按中标供应商拆分生成采购订单草稿"
      ],
      "objects": [
        "供应商",
        "询价比价"
      ],
      "roles": [
        "采购员",
        "供应商管理员"
      ],
      "upstream": [
        "主数据(物料)"
      ],
      "downstream": [
        "进销存(采购订单)"
      ],
      "coreTables": [
        "srm_supplier",
        "srm_supplier_contact",
        "srm_supplier_bank",
        "srm_supplier_category",
        "srm_inquiry",
        "srm_inquiry_item",
        "srm_inquiry_supplier",
        "srm_inquiry_quote",
        "srm_inquiry_order"
      ]
    },
    {
      "key": "erp",
      "name": "进销存与财务",
      "color": "#5fc79e",
      "tag": "ERP",
      "flow": "purchase",
      "positioning": "商贸与资金单据中枢,向 MES å‘起实物收发",
      "problem": "采购销售单据审批、来票付款控制、订单执行进度回写",
      "features": [
        "采购申请(BPM)→订单(自动/手动生成)",
        "确认收货→MES到货通知",
        "入库/退货数量状态回写",
        "采购来票(绑定采购订单+MES采购入库单·双层剩余额度校验)",
        "付款单(必须关联来票·BPM)",
        "供应商往来(订单/来票/付款三流水·左右展示开票金额)",
        "销售订单(BPM·审批通过自动建MPS)",
        "出库状态回写",
        "收款单(关联CRM客户)",
        "结算账户",
        "AI发票OCR"
      ],
      "objects": [
        "采购申请",
        "采购订单",
        "采购来票",
        "付款单",
        "供应商往来",
        "销售订单",
        "收款单",
        "结算账户"
      ],
      "roles": [
        "采购员",
        "销售员",
        "出纳",
        "会计"
      ],
      "upstream": [
        "CRM(合同)",
        "SRM(询价确认)",
        "主数据"
      ],
      "downstream": [
        "生产(MPS)",
        "仓储(收发)",
        "财务(来票付款入账)"
      ],
      "coreTables": [
        "erp_purchase_request",
        "erp_purchase_order",
        "erp_purchase_invoice",
        "erp_finance_payment",
        "erp_sale_order",
        "erp_finance_receipt",
        "erp_account"
      ]
    },
    {
      "key": "finance",
      "name": "财务管理(会计核算)",
      "color": "#3ec9a7",
      "tag": "FIN",
      "flow": "finance",
      "positioning": "独立核算模块:会计核算 + èµ„产核算 + è´¢åŠ¡æŠ¥è¡¨çœ‹æ¿ï¼ˆä¸Ž ERP è¿›é”€å­˜è´¢åŠ¡å•æ®åˆ†ç¦»ï¼Œyudao-module-finance)",
      "problem": "进销存只管单据不管账,缺少会计科目/凭证/账簿与资产折旧摊销沉淀,无法出账表与资产净值",
      "features": [
        "会计科目树(约68条一级科目种子·可建下级)",
        "记账凭证(字号自动生成·分录借贷平衡前后端双重校验)",
        "凭证状态机 æœªè¿‡è´¦0→过账10/作废20,已过账不可改",
        "总账按期间逐月汇总",
        "明细账逐条分录并结转余额",
        "固定资产+计提折旧",
        "无形资产+计提摊销",
        "会计核算看板(收支/资产/凭证/月度趋势)",
        "凭证来源单据关联(sourceType/sourceId/sourceNo,可回溯开票/回款/来票/付款)",
        "报表取数接入CRM客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
        "附件走系统附件中心 recordType=fin_voucher"
      ],
      "objects": [
        "会计科目",
        "记账凭证",
        "凭证分录",
        "固定资产",
        "无形资产"
      ],
      "roles": [
        "会计",
        "财务主管",
        "资产会计",
        "管理层"
      ],
      "upstream": [
        "进销存(ERP å·²å®¡æ ¸æ”¶è´§/付款/购销单据)",
        "CRM(开票/回款/核销/对账报表)"
      ],
      "downstream": [
        "财务报表看板",
        "BI ç®¡ç†é©¾é©¶èˆ±"
      ],
      "coreTables": [
        "fin_account_subject",
        "fin_voucher",
        "fin_voucher_entry",
        "fin_fixed_asset",
        "fin_intangible_asset"
      ]
    },
    {
      "key": "pro",
      "name": "生产管理",
      "color": "#e0b34f",
      "tag": "MES-PRO",
      "flow": "production",
      "positioning": "生产执行核心:计划-工单-任务-报工",
      "problem": "订单到车间执行的逐层拆解与过程可控",
      "features": [
        "MPS主生产计划(可由销售订单自动创建·合并下发)",
        "工单(按工艺路线自动生成工序任务)",
        "报工(按工序顺序控制·BPM审批)",
   "生产排产(选工作站后按岗位从「员工管理」带出报工人·不要求开通系统账号)",
        "倒冲扣线边库/产出入库",
        "安灯",
        "工艺路线·工序·工单BOM",
        "研发设计项目(PD·审批归档)",
        "AI å››ç±»é¢„测"
      ],
      "objects": [
        "MPS",
        "生产工单",
        "生产任务",
        "报工单",
        "工艺路线",
        "工单BOM",
        "设计项目"
      ],
      "roles": [
        "计划员",
        "车间主任",
        "操作工",
        "工艺员"
      ],
      "upstream": [
        "进销存(销售订单)"
      ],
      "downstream": [
        "仓储(领料/入库)",
        "质量(IPQC)"
      ],
      "coreTables": [
        "mes_pro_mps",
        "mes_pro_work_order",
        "mes_pro_task",
        "mes_pro_feedback",
        "mes_pro_route",
        "mes_pro_process",
        "mes_pro_work_order_bom",
        "mes_pd_project"
      ]
    },
    {
      "key": "wm",
      "name": "仓储管理",
      "color": "#7e6bd6",
      "tag": "MES-WM",
      "flow": "warehouse",
      "positioning": "厂内 WMS:单据驱动的库存账实一致",
      "problem": "多来源出入库、批次/SN è¿½æº¯ã€åº“存精确到库位",
      "features": [
        "15种出入库单据",
        "库存台账(物料×仓库×库区×库位×批次×供应商)",
        "事务流水只读留痕",
        "调拨成对事务",
        "盘点(静态/动态·盲盘·盘盈盘亏调整)",
        "批次/SN/条码/装箱嵌套",
        "虚拟线边库(允许负库存)",
        "库存占用(5ç±»)",
        "四级冻结",
        "WMS审批配置+BPM",
        "对外 WmsStockApi/WmsTransactionApi"
      ],
      "objects": [
        "到货通知",
        "采购入库",
        "领料",
        "完工入库",
        "销售出库",
        "发货通知",
        "销售退货",
        "供应商退货",
        "调拨",
        "盘点",
        "库存台账",
        "批次",
        "SN"
      ],
      "roles": [
        "仓管员",
        "拣货员",
        "盘点员"
      ],
      "upstream": [
        "采购(收货)",
        "生产(领退料)",
        "销售(发货)",
        "售后(退货)"
      ],
      "downstream": [
        "进销存(数量回写)",
        "财务(成本基础)"
      ],
      "coreTables": [
        "mes_wm_warehouse",
        "mes_wm_material_stock",
        "mes_wm_transaction",
        "mes_wm_item_receipt",
        "mes_wm_product_issue",
        "mes_wm_product_receipt",
        "mes_wm_product_sales",
        "mes_wm_return_sales",
        "mes_wm_transfer",
        "mes_wm_stock_taking_task",
        "mes_wm_batch",
        "mes_wm_sn",
        "mes_wm_stock_reserve"
      ]
    },
    {
      "key": "qc",
      "name": "质量管理",
      "color": "#e06c6c",
      "tag": "MES-QC",
      "flow": "quality",
      "positioning": "四道检验闸口 + ä¸åˆæ ¼å“é—­çŽ¯å¤„ç½®",
      "problem": "来料/过程/出货/退货检验标准化,不合格可追溯处置",
      "features": [
        "IQC æ¥æ–™(到货/外协)",
        "IPQC è¿‡ç¨‹(联动报工)",
        "OQC å‡ºè´§(联动销售出库)",
        "RQC é€€è´§(联动退料/售后)",
        "质检方案·指标·缺陷库",
        "检测结果:合格/特采/退货/报废",
        "NCR:评审→处置(退货/报废/返工/让步/降级)→关闭",
        "AI åˆ¤å®šå»ºè®®"
      ],
      "objects": [
        "IQC",
        "IPQC",
        "OQC",
        "RQC",
        "质检方案",
        "NCR",
        "缺陷记录"
      ],
      "roles": [
        "质检员(IQC/IPQC/OQC)",
        "质量工程师"
      ],
      "upstream": [
        "仓储(到货)",
        "生产(报工)",
        "销售(出库)",
        "售后(退货)"
      ],
      "downstream": [
        "仓储(合格入库/报废出库)",
        "生产(NCR返工)"
      ],
      "coreTables": [
        "mes_qc_iqc",
        "mes_qc_ipqc",
        "mes_qc_oqc",
        "mes_qc_rqc",
        "mes_qc_template",
        "mes_qc_indicator",
        "mes_qc_defect",
        "mes_qc_ncr"
      ]
    },
    {
      "key": "qcreport",
      "name": "智能质检报告",
      "color": "#b06ce0",
      "tag": "QC-REPORT",
      "flow": "quality",
      "positioning": "可视化设计报告模板 â†’ ä¸€é”®å‡ºä»¶å¹¶å†»ç»“历史报告",
      "problem": "各行业质检报告格式各异,排版硬编码改一次发一次版;且历史报告必须可复现、不随业务数据变化",
      "features": [
        "GrapesJS å¯è§†åŒ–设计器(质检组件走注册机制,不硬编码进编辑器)",
        "模板 + å¤šç‰ˆæœ¬ï¼šè‰ç¨¿0/已发布1/已停用2,已发布版本内容不可覆盖",
        "组件数据绑定与独立判定规则引擎(禁用 eval / ScriptEngine)",
        "与前端逐字对齐的 Java æ¸²æŸ“引擎,出站 HTML å‰¥ç¦» on* äº‹ä»¶å±žæ€§ä¸Ž javascript:/vbscript:/data: åè®®",
        "出件接口:preview åªæ¸²æŸ“不落库不消耗编号 / generate æ¸²æŸ“+落库+冻结 data_snapshot / regenerate ç”¨å†»ç»“快照与同一版本重渲",
        "报告编号 QR+yyyyMMdd+4位流水,按天独立,可显式指定(撞号会报出具体编号)",
        "缺口清单只在 preview/generate/regenerate å“åº”里返回,不落库",
        "PDF(Playwright) å½’档与 MES å››ç§è´¨æ£€å•归一化对接待后续轮次"
      ],
      "objects": [
        "报告模板",
        "报告实例"
      ],
      "roles": [
        "报告设计员",
        "质检员(出件)"
      ],
      "upstream": [
        "质量(质检单上下文,本轮由调用方传入)"
      ],
      "downstream": [
        "附件中心(PDF å½’档·后续轮次)",
        "BI"
      ],
      "coreTables": [
        "qc_report_template",
        "qc_report_template_version",
        "qc_report_instance",
        "qc_report_data_source",
        "qc_report_rule",
        "qc_report_render_record"
      ]
    },
    {
      "key": "dv",
      "name": "设备与工装",
      "color": "#59a8b8",
      "tag": "MES-DV/TM",
      "flow": null,
      "positioning": "设备资产健康与生产资源保障",
      "problem": "设备点检保养维修可执行、工装状态可控",
      "features": [
        "设备台账(停机/生产中/保养中)",
        "点检·保养方案与记录",
        "维修工单(草稿→维修→待验收→已确认)",
        "计量器具检定",
        "工装夹具(在库/领用/ç»´ä¿®/报废)"
      ],
      "objects": [
        "设备",
        "点检记录",
        "维修工单",
        "工装"
      ],
      "roles": [
        "设备员",
        "维修工",
        "工装管理员"
      ],
      "upstream": [],
      "downstream": [
        "生产(AI é£Žé™©é¢„测消费设备数据)"
      ],
      "coreTables": [
        "mes_dv_machinery",
        "mes_dv_check_plan",
        "mes_dv_check_record",
        "mes_dv_repair",
        "mes_tm_tool"
      ]
    },
    {
      "key": "aftersales",
      "name": "售后服务",
      "color": "#c78bd6",
      "tag": "AFTER",
      "flow": "aftersales",
      "positioning": "客户反馈到退货/ç»´ä¿®/退款闭环",
      "problem": "售后工单流转、退货与质检入库退款的跨系统联动",
      "features": [
        "工单六态(草稿→处理中→待入库→待退款→完结→关闭)",
        "问题判定(一般/ç»´ä¿®/退货)",
        "客户诉求反馈描述+附件",
        "处理结果+附件",
        "工单流转记录",
        "ç»´ä¿®(现场/返厂/远程)",
        "退货由仓储物流发起→MES é”€å”®é€€è´§å•(原售后退货申请页面已下线)",
        "质检结论/入库/退款三重回调",
        "行级数据权限四视图"
      ],
      "objects": [
        "售后工单",
        "工单流转记录",
        "维修记录",
        "退货申请(后台保留)"
      ],
      "roles": [
        "客服",
        "维修工程师",
        "售后管理员"
      ],
      "upstream": [
        "CRM(客户)",
        "ERP(销售订单)"
      ],
      "downstream": [
        "仓储(销售退货)",
        "质量(RQC)",
        "财务(退款)"
      ],
      "coreTables": [
        "after_sale_ticket",
        "after_sale_ticket_item",
        "after_sale_ticket_flow",
        "after_sale_repair",
        "after_sale_return",
        "after_sale_permission"
      ]
    },
    {
      "key": "hrm",
      "name": "人力资源",
      "color": "#9db85a",
      "tag": "HRM",
      "flow": null,
      "positioning": "员工入转调离与考勤薪酬",
      "problem": "人事流程审批化、离职数据不丢(交接)、算薪自动化",
      "features": [
        "员工档案(入职自动建系统账号)",
        "请假/加班/调岗/离职 BPM å®¡æ‰¹",
        "离职交接同时转移员工ID/用户ID/业务数据 expandUserIds",
        "离职到期自动把员工状态改为离职并停用系统账号",
        "考勤(移动端我的考勤状态·打卡·规则·排班·补卡审批·月度汇总)",
        "薪酬(应发-社保公积金-个税-请假扣款·1.5/2/3倍加班费·发放台账)",
        "社保公积金档案按部门维护(部门下员工共用最新生效档案)",
        "培训证书(SQL先行待实现)"
      ],
      "objects": [
        "员工",
        "请假单",
        "离职申请",
        "交接映射",
        "考勤记录",
        "部门社保档案",
        "薪酬核算"
      ],
      "roles": [
        "HR",
        "部门主管",
        "员工本人"
      ],
      "upstream": [],
      "downstream": [
        "各模块(数据可见性扩展)",
        "system(账号)"
      ],
      "coreTables": [
        "hrm_employee",
        "hrm_leave_application",
        "hrm_resignation_application",
        "hrm_user_handover",
        "hrm_attendance_record",
        "hrm_salary_calculation",
        "hrm_salary_payment",
        "hrm_employee_social_security"
      ]
    },
    {
      "key": "bpm",
      "name": "协同审批",
      "color": "#e6a23c",
      "tag": "BPM",
      "flow": null,
      "positioning": "全系统单据审批中枢(Flowable)",
      "problem": "多模块单据需要灵活可配的多级审批",
      "features": [
        "BPMN+钉钉风格双设计器",
        "流程定义部署",
        "审批事件统一回调(BpmProcessInstanceStatusEvent)",
        "13个业务监听器",
        "表单·用户组·抄送·加签"
      ],
      "objects": [
        "流程模型",
        "流程实例",
        "审批任务"
      ],
      "roles": [
        "审批人(按流程配置)",
        "流程管理员"
      ],
      "upstream": [
        "各业务提交审批"
      ],
      "downstream": [
        "回调更新各业务单据状态"
      ],
      "coreTables": [
        "bpm_process_definition_info",
        "bpm_process_instance",
        "bpm_task",
        "act_*",
        "flowable_*"
      ]
    },
    {
      "key": "ai",
      "name": "AI æ™ºèƒ½",
      "color": "#56c8d8",
      "tag": "AI",
      "flow": "ai",
      "positioning": "单据录入提效与生产决策辅助",
      "problem": "人工录发票/报价慢、质检与排产依赖经验",
      "features": [
        "chat/chatWithImage åŒé€šé“",
        "OCR é¢„å¡«(发票/报价)",
        "质检判定建议",
        "缺料/时长/风险/交付四类预测",
        "RAG çŸ¥è¯†åº“(Milvus)",
        "AI不可用时 fallback äººå·¥"
      ],
      "objects": [
        "知识库",
        "AI模型配置",
        "对话记录"
      ],
      "roles": [
        "采购员",
        "销售",
        "质检员",
        "计划员"
      ],
      "upstream": [
        "各业务提供上下文数据"
      ],
      "downstream": [
        "结构化JSON建议返回业务侧"
      ],
      "coreTables": [
        "ai_chat_conversation",
        "ai_chat_message",
        "ai_model",
        "ai_knowledge",
        "ai_knowledge_document",
        "ai_knowledge_segment"
      ]
    }
  ],
  "unused": [
   "im æ¨¡å—(IM/RTC,业务未消费)",
   "member æ¨¡å—(C端会员,遗留)"
  ]
 }
  "platform": {
    "name": "系统底座",
    "capabilities": [
      "JWT+OAuth2 è®¤è¯Â·æ»‘块验证码",
      "RBAC è§’色-菜单-数据范围(全部/部门/本部门及下级/仅本人/自定义)",
      "字典",
      "附件中心(storage-blob æ–‡ä»¶ + storage-attachment æŒ‰ recordType+recordId å¤šæ€å…³è”,全模块强制使用)",
      "编码规则(各业务自实现 Redis å‰ç¼€+日期+流水)",
      "Quartz å®šæ—¶ä»»åŠ¡Â·RocketMQ/Kafka/Redis Stream MQ·WebSocket",
      "多租户已关闭",
      "实际 system_role ä»… 4 ä¸ªï¼šè¶…级管理员/普通角色/CRM管理员/租户管理员,业务分工靠数据权限而非多角色"
    ],
    "unused": [
      "im æ¨¡å—(IM/RTC,业务未消费)",
      "member æ¨¡å—(C端会员,遗留)"
    ]
  }
}
docs/project-business/index.html
@@ -207,7 +207,7 @@
    "coreObjects": 53,
    "documentFlows": 7,
    "approvalListeners": 13,
    "aiCapabilities": 8,
    "aiCapabilities": 9,
    "wmBillTypes": 15
  },
  "businessAnswers": [
@@ -233,7 +233,7 @@
    ],
    [
      "生产如何产生库存",
      "报工审批通过后自动生成产品产出单入库(质检工序先待检、IPQC åŽæ‹†è¡Œå…¥åº“);倒冲工序按 BOM æ‰£çº¿è¾¹åº“"
      "报工审批通过后自动生成产品产出单:成品先待检、完成 IPQC åŽæ‹†è¡Œå…¥åº“;半成品直接按生产 BOM ç›®æ ‡åº“位自动入库,不等质检;工单存在生产 BOM å³è‡ªåŠ¨æ‰£çº¿è¾¹åº“"
    ],
    [
      "质量在哪里介入",
@@ -245,7 +245,7 @@
    ],
    [
      "AI åœ¨å“ªé‡Œå‚与",
      "发票/报价 OCR é¢„填、质检结果建议、缺料/时长/风险/交付四类预测;均为『业务组装 Prompt→模型→JSON→人工确认』模式"
      "发票/报价 OCR é¢„填、质检结果建议、缺料/时长/风险/交付四类预测;均为『业务组装 Prompt→模型→JSON→人工确认』模式。智能体物料导入另走规则+Java é¢„检链路,人工确认后幂等提交,只新建不同步 MES。AI å…¥å£ç»Ÿä¸€å—总开关 VITE_APP_AI_ENABLE æŽ§åˆ¶ï¼Œå…³é—­å³éšè—æ¨¡å—路由与各业务 AI æŒ‰é’®ã€‚模型管理按「类型+能力」唯一启用,启用一个自动关闭同组其他模型;对话取 text èƒ½åŠ›ã€å›¾ç‰‡è¯†åˆ«å– vision èƒ½åŠ›ï¼Œæœªå¯ç”¨å¯¹åº”èƒ½åŠ›æ¨¡åž‹ç›´æŽ¥æŠ¥é”™æç¤ºåŽ»å¯ç”¨"
    ],
    [
      "模块间数据关系",
@@ -309,6 +309,16 @@
      "name": "RAG çŸ¥è¯†åº“",
      "flow": "文档 â†’ POI/Tika ææ–‡æœ¬ â†’ åˆ‡ç‰‡(800token) â†’ å‘量化入 Milvus â†’ æ£€ç´¢æŽ¥å£(topK3,阈值0.7)【当前无业务模块消费,预留能力】",
      "callsApis": []
    },
    {
      "name": "智能体物料导入",
      "flow": "创建补录会话→上传并绑定附件→Python è§£æž CSV/XLSX å€™é€‰â†’Java åªè¯»é¢„检分类·单位·编码存在性并生成确定性摘要→人工修订确认→commandId+幂等键逐行独立事务提交→逐行回执含目标物料ID;编码已存在跳过,不更新物料、不触发 MES",
      "callsApis": [
        "SystemAgentImportBridgeService",
        "MdmItemService",
        "MdmAgentImportPreflightService",
        "MdmAgentImportCommandService"
      ]
    }
  ],
  "pendingConfirm": [
@@ -333,289 +343,2359 @@
}</script>
<script type="application/json" id="data-modules">{
  "modules": [
    {"key": "mdm", "name": "基础主数据", "color": "#8a94a6", "tag": "MDM", "flow": null,
     "positioning": "全系统统一口径的主数据权威源",
     "problem": "物料、单位、仓库在 MES/ERP/CRM å¤šå¤„重复定义导致数据打架",
     "features": ["物料与SKU(含批次属性配置12开关)", "物料分类树", "计量单位与换算", "品牌", "仓库主数据(含type字段)", "MES ç‰©æ–™ç» MdmItemSyncApi åå‘同步"],
     "objects": ["物料", "SKU", "计量单位", "仓库"],
     "roles": ["主数据管理员"],
     "upstream": [], "downstream": ["生产", "进销存", "销售", "采购"],
     "coreTables": ["mdm_item", "mdm_item_sku", "mdm_item_category", "mdm_unit_measure", "mdm_brand", "mdm_warehouse"]},
    {"key": "crm", "name": "销售管理(客户关系)", "color": "#4f9cff", "tag": "CRM", "flow": "sales",
     "positioning": "销售前端:从线索到回款的客户经营",
     "problem": "客户资产沉淀、商机转化、合同审批与开票回款管控",
     "features": ["线索转化客户", "公海池与领取上限", "联系人", "商机自定义阶段", "跟进记录", "报价单(AI OCR)", "合同(BPM审批→生成销售订单·支持Excel导入)", "开票申请(绑定销售订单+MES销售出库单)→审批→录入发票(发票号/日期/附件+合同款/预付款/到货款/验收款/质保金收款节点)", "开票(BPM审批·支持Excel导入·同一批数据不可重复导入)→回款(先开票校验·核销明细可拆分到订单/出库/开票)", "回款计划(收款节点回写已回款/实际时间/状态)", "客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势", "投标保证金台账(客户招标方+已审核销售订单·中标结果/退回状态/退回金额日期)", "销售目标", "数据权限三级(负责人/只读/读写)"],
     "objects": ["线索", "客户", "联系人", "商机", "报价单", "合同", "发票", "回款", "回款核销明细", "销售目标", "投标保证金"],
     "roles": ["销售内勤", "销售经理", "财务(开票回款)"],
     "upstream": ["主数据(物料)", "进销存(销售订单)"], "downstream": ["进销存(销售订单)", "售后(客户)", "AI(报价OCR)"],
     "coreTables": ["crm_clue", "crm_customer", "crm_contact", "crm_business", "crm_sale_quotation", "crm_contract", "crm_invoice", "crm_receivable", "crm_receivable_plan", "crm_permission", "crm_bid_bond"]},
    {"key": "srm", "name": "供应商管理", "color": "#c9a86a", "tag": "SRM", "flow": "purchase",
     "positioning": "供应商档案 + è¯¢ä»·æ¯”ä»·",
     "problem": "供应商信息多开户行/多联系人维护,采购寻源按产品横向比价并快速转为采购订单",
     "features": ["供应商档案与分类", "多联系人/多银行账户(含银行行号)", "询价单:新增时一并录入产品行×参与供应商报价", "每个产品单独选定中标供应商", "确认后按中标供应商拆分生成采购订单草稿"],
     "objects": ["供应商", "询价比价"],
     "roles": ["采购员", "供应商管理员"],
     "upstream": ["主数据(物料)"], "downstream": ["进销存(采购订单)"],
     "coreTables": ["srm_supplier", "srm_supplier_contact", "srm_supplier_bank", "srm_supplier_category", "srm_inquiry", "srm_inquiry_item", "srm_inquiry_supplier", "srm_inquiry_quote", "srm_inquiry_order"]},
    {"key": "erp", "name": "进销存与财务", "color": "#5fc79e", "tag": "ERP", "flow": "purchase",
     "positioning": "商贸与资金单据中枢,向 MES å‘起实物收发",
     "problem": "采购销售单据审批、来票付款控制、订单执行进度回写",
     "features": ["采购申请(BPM)→订单(自动/手动生成)", "确认收货→MES到货通知", "入库/退货数量状态回写", "采购来票(绑定采购订单+MES采购入库单·双层剩余额度校验)", "付款单(必须关联来票·BPM)", "销售订单(BPM·审批通过自动建MPS)", "出库状态回写", "收款单(关联CRM客户)", "结算账户", "AI发票OCR"],
     "objects": ["采购申请", "采购订单", "采购来票", "付款单", "销售订单", "收款单", "结算账户"],
     "roles": ["采购员", "销售员", "出纳", "会计"],
     "upstream": ["CRM(合同)", "SRM(定标)", "主数据"], "downstream": ["生产(MPS)", "仓储(收发)"],
     "coreTables": ["erp_purchase_request", "erp_purchase_order", "erp_purchase_invoice", "erp_finance_payment", "erp_sale_order", "erp_finance_receipt", "erp_account"]},
    {"key": "finance", "name": "财务管理(会计核算)", "color": "#3ec9a7", "tag": "FIN", "flow": "finance",
     "positioning": "独立核算模块:会计核算 + èµ„产核算 + è´¢åŠ¡æŠ¥è¡¨çœ‹æ¿ï¼ˆä¸Ž ERP è¿›é”€å­˜è´¢åŠ¡å•æ®åˆ†ç¦»ï¼Œyudao-module-finance)",
     "problem": "进销存只管单据不管账,缺少会计科目/凭证/账簿与资产折旧摊销沉淀,无法出账表与资产净值",
     "features": ["会计科目树(约68条一级科目种子·可建下级)", "记账凭证(字号自动生成·分录借贷平衡前后端双重校验)", "凭证状态机 æœªè¿‡è´¦0→过账10/作废20,已过账不可改", "总账按期间逐月汇总", "明细账逐条分录并结转余额", "固定资产+计提折旧", "无形资产+计提摊销", "会计核算看板(收支/资产/凭证/月度趋势)", "凭证来源单据关联(sourceType/sourceId/sourceNo,可回溯开票/回款/来票/付款)", "报表取数接入CRM客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势", "附件走系统附件中心 recordType=fin_voucher"],
     "objects": ["会计科目", "记账凭证", "凭证分录", "固定资产", "无形资产"],
     "roles": ["会计", "财务主管", "资产会计", "管理层"],
     "upstream": ["进销存(ERP å·²å®¡æ ¸æ”¶è´§/付款/购销单据)", "CRM(开票/回款/核销/对账报表)"], "downstream": ["财务报表看板", "BI ç®¡ç†é©¾é©¶èˆ±"],
     "coreTables": ["fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"]},
    {"key": "pro", "name": "生产管理", "color": "#e0b34f", "tag": "MES-PRO", "flow": "production",
     "positioning": "生产执行核心:计划-工单-任务-报工",
     "problem": "订单到车间执行的逐层拆解与过程可控",
     "features": ["MPS主生产计划(可由销售订单自动创建·合并下发)", "工单(按工艺路线自动生成工序任务)", "报工(按工序顺序控制·BPM审批·按计时/计件工资定额计薪)", "标准工时(转速×节距/线速度/满速百分比+辅助工时)与工时考核对比", "倒冲扣线边库/产出入库", "安灯", "工艺路线·工序·工单BOM", "研发设计项目(PD·审批归档)", "AI å››ç±»é¢„测"],
     "objects": ["MPS", "生产工单", "生产任务", "报工单", "生产工序", "辅助工时配置", "工艺路线", "工单BOM", "设计项目"],
     "roles": ["计划员", "车间主任", "操作工", "工艺员"],
     "upstream": ["进销存(销售订单)"], "downstream": ["仓储(领料/入库)", "质量(IPQC)"],
     "coreTables": ["mes_pro_mps", "mes_pro_work_order", "mes_pro_task", "mes_pro_feedback", "mes_pro_worktime_aux", "mes_pro_feedback_aux", "mes_pro_route", "mes_pro_process", "mes_pro_work_order_bom", "mes_pd_project"]},
    {"key": "wm", "name": "仓储管理", "color": "#7e6bd6", "tag": "MES-WM", "flow": "warehouse",
     "positioning": "厂内 WMS:单据驱动的库存账实一致",
     "problem": "多来源出入库、批次/SN è¿½æº¯ã€åº“存精确到库位",
     "features": ["15种出入库单据", "库存台账(物料×仓库×库区×库位×批次×供应商)", "事务流水只读留痕", "调拨成对事务", "盘点(静态/动态·盲盘·盘盈盘亏调整)", "批次/SN/条码/装箱嵌套", "虚拟线边库(允许负库存)", "库存占用(5ç±»)", "四级冻结", "WMS审批配置+BPM", "对外 WmsStockApi/WmsTransactionApi"],
     "objects": ["到货通知", "采购入库", "领料", "完工入库", "销售出库", "发货通知", "销售退货", "供应商退货", "调拨", "盘点", "库存台账", "批次", "SN"],
     "roles": ["仓管员", "拣货员", "盘点员"],
     "upstream": ["采购(收货)", "生产(领退料)", "销售(发货)", "售后(退货)"], "downstream": ["进销存(数量回写)", "财务(成本基础)"],
     "coreTables": ["mes_wm_warehouse", "mes_wm_material_stock", "mes_wm_transaction", "mes_wm_item_receipt", "mes_wm_product_issue", "mes_wm_product_receipt", "mes_wm_product_sales", "mes_wm_return_sales", "mes_wm_transfer", "mes_wm_stock_taking_task", "mes_wm_batch", "mes_wm_sn", "mes_wm_stock_reserve"]},
    {"key": "qc", "name": "质量管理", "color": "#e06c6c", "tag": "MES-QC", "flow": "quality",
     "positioning": "四道检验闸口 + ä¸åˆæ ¼å“é—­çŽ¯å¤„ç½®",
     "problem": "来料/过程/出货/退货检验标准化,不合格可追溯处置",
     "features": ["IQC æ¥æ–™(到货/外协)", "IPQC è¿‡ç¨‹(联动报工)", "OQC å‡ºè´§(联动销售出库)", "RQC é€€è´§(联动退料/售后)", "质检方案·指标·缺陷库", "检测结果:合格/特采/退货/报废", "NCR:评审→处置(退货/报废/返工/让步/降级)→关闭", "AI åˆ¤å®šå»ºè®®"],
     "objects": ["IQC", "IPQC", "OQC", "RQC", "质检方案", "NCR", "缺陷记录"],
     "roles": ["质检员(IQC/IPQC/OQC)", "质量工程师"],
     "upstream": ["仓储(到货)", "生产(报工)", "销售(出库)", "售后(退货)"], "downstream": ["仓储(合格入库/报废出库)", "生产(NCR返工)"],
     "coreTables": ["mes_qc_iqc", "mes_qc_ipqc", "mes_qc_oqc", "mes_qc_rqc", "mes_qc_template", "mes_qc_indicator", "mes_qc_defect", "mes_qc_ncr"]},
    {"key": "qcreport", "name": "智能质检报告", "color": "#b06ce0", "tag": "QC-REPORT", "flow": "quality",
     "positioning": "可视化设计报告模板 â†’ ä¸€é”®å‡ºä»¶å¹¶å†»ç»“历史报告",
     "problem": "各行业质检报告格式各异,排版硬编码改一次发一次版;且历史报告必须可复现、不随业务数据变化",
     "features": ["GrapesJS å¯è§†åŒ–设计器(质检组件走注册机制,不硬编码进编辑器)", "模板 + å¤šç‰ˆæœ¬ï¼šè‰ç¨¿0/已发布1/已停用2,已发布版本内容不可覆盖", "组件数据绑定与独立判定规则引擎(禁用 eval / ScriptEngine)", "与前端逐字对齐的 Java æ¸²æŸ“引擎,出站 HTML å‰¥ç¦» on* äº‹ä»¶å±žæ€§ä¸Ž javascript:/vbscript:/data: åè®®", "出件接口:preview åªæ¸²æŸ“不落库不消耗编号 / generate æ¸²æŸ“+落库+冻结 data_snapshot / regenerate ç”¨å†»ç»“快照与同一版本重渲", "报告编号 QR+yyyyMMdd+4位流水,按天独立,可显式指定(撞号会报出具体编号)", "缺口清单只在 preview/generate/regenerate å“åº”里返回,不落库", "PDF(Playwright) å½’档与 MES å››ç§è´¨æ£€å•归一化对接待后续轮次"],
     "objects": ["报告模板", "报告实例"],
     "roles": ["报告设计员", "质检员(出件)"],
     "upstream": ["质量(质检单上下文,本轮由调用方传入)"], "downstream": ["附件中心(PDF å½’档·后续轮次)", "BI"],
     "coreTables": ["qc_report_template", "qc_report_template_version", "qc_report_instance", "qc_report_data_source", "qc_report_rule", "qc_report_render_record"]},
    {"key": "dv", "name": "设备与工装", "color": "#59a8b8", "tag": "MES-DV/TM", "flow": null,
     "positioning": "设备资产健康与生产资源保障",
     "problem": "设备点检保养维修可执行、工装状态可控",
     "features": ["设备台账(停机/生产中/保养中)", "点检·保养方案与记录", "维修工单(草稿→维修→待验收→已确认)", "计量器具检定", "工装夹具(在库/领用/ç»´ä¿®/报废)"],
     "objects": ["设备", "点检记录", "维修工单", "工装"],
     "roles": ["设备员", "维修工", "工装管理员"],
     "upstream": [], "downstream": ["生产(AI é£Žé™©é¢„测消费设备数据)"],
     "coreTables": ["mes_dv_machinery", "mes_dv_check_plan", "mes_dv_check_record", "mes_dv_repair", "mes_tm_tool"]},
    {"key": "aftersales", "name": "售后服务", "color": "#c78bd6", "tag": "AFTER", "flow": "aftersales",
     "positioning": "客户反馈到退货/ç»´ä¿®/退款闭环",
     "problem": "售后工单流转、退货与质检入库退款的跨系统联动",
     "features": ["工单六态(草稿→处理中→待入库→待退款→完结→关闭)", "问题判定(一般/ç»´ä¿®/退货)", "客户诉求反馈描述+附件", "处理结果+附件", "工单流转记录", "ç»´ä¿®(现场/返厂/远程)", "退货由仓储物流发起→MES é”€å”®é€€è´§å•(原售后退货申请页面已下线)", "质检结论/入库/退款三重回调", "行级数据权限四视图"],
     "objects": ["售后工单", "工单流转记录", "维修记录", "退货申请(后台保留)"],
     "roles": ["客服", "维修工程师", "售后管理员"],
     "upstream": ["CRM(客户)", "ERP(销售订单)"], "downstream": ["仓储(销售退货)", "质量(RQC)", "财务(退款)"],
     "coreTables": ["after_sale_ticket", "after_sale_ticket_item", "after_sale_repair", "after_sale_return", "after_sale_permission"]},
    {"key": "hrm", "name": "人力资源", "color": "#9db85a", "tag": "HRM", "flow": null,
     "positioning": "员工入转调离与考勤薪酬",
     "problem": "人事流程审批化、离职数据不丢(交接)、算薪自动化",
     "features": ["员工档案(入职自动建系统账号)", "请假/加班/调岗/离职 BPM å®¡æ‰¹", "离职交接同时转移员工ID/用户ID/业务数据 expandUserIds", "离职到期自动把员工状态改为离职并停用系统账号", "考勤(打卡·规则·排班·补卡审批·月度汇总)", "薪酬(应发-社保公积金-个税-请假扣款·1.5/2/3倍加班费·发放台账)", "社保公积金档案按部门维护(部门下员工共用最新生效档案)", "培训证书(SQL先行待实现)"],
     "objects": ["员工", "请假单", "离职申请", "交接映射", "考勤记录", "部门社保档案", "薪酬核算"],
     "roles": ["HR", "部门主管", "员工本人"],
     "upstream": [], "downstream": ["各模块(数据可见性扩展)", "system(账号)"],
     "coreTables": ["hrm_employee", "hrm_leave_application", "hrm_resignation_application", "hrm_user_handover", "hrm_attendance_record", "hrm_salary_calculation", "hrm_salary_payment", "hrm_employee_social_security"]},
    {"key": "bpm", "name": "协同审批", "color": "#e6a23c", "tag": "BPM", "flow": null,
     "positioning": "全系统单据审批中枢(Flowable)",
     "problem": "多模块单据需要灵活可配的多级审批",
     "features": ["BPMN+钉钉风格双设计器", "流程定义部署", "审批事件统一回调(BpmProcessInstanceStatusEvent)", "13个业务监听器", "表单·用户组·抄送·加签"],
     "objects": ["流程模型", "流程实例", "审批任务"],
     "roles": ["审批人(按流程配置)", "流程管理员"],
     "upstream": ["各业务提交审批"], "downstream": ["回调更新各业务单据状态"],
     "coreTables": ["bpm_process_definition_info", "bpm_process_instance", "bpm_task", "act_*", "flowable_*"]},
    {"key": "ai", "name": "AI æ™ºèƒ½", "color": "#56c8d8", "tag": "AI", "flow": "ai",
     "positioning": "单据录入提效与生产决策辅助",
     "problem": "人工录发票/报价慢、质检与排产依赖经验",
     "features": ["chat/chatWithImage åŒé€šé“", "OCR é¢„å¡«(发票/报价)", "质检判定建议", "缺料/时长/风险/交付四类预测", "RAG çŸ¥è¯†åº“(Milvus)", "AI不可用时 fallback äººå·¥"],
     "objects": ["知识库", "AI模型配置", "对话记录"],
     "roles": ["采购员", "销售", "质检员", "计划员"],
     "upstream": ["各业务提供上下文数据"], "downstream": ["结构化JSON建议返回业务侧"],
     "coreTables": ["ai_chat_conversation", "ai_chat_message", "ai_model", "ai_knowledge", "ai_knowledge_document", "ai_knowledge_segment"]}
    {
      "key": "mdm",
      "name": "基础主数据",
      "color": "#8a94a6",
      "tag": "MDM",
      "flow": null,
      "positioning": "全系统统一口径的主数据权威源",
      "problem": "物料、单位、仓库在 MES/ERP/CRM å¤šå¤„重复定义导致数据打架",
      "features": [
        "物料与SKU(含批次属性配置12开关)",
        "物料分类树",
        "物料分类含半成品/成品独立大类",
        "生产物料BOM维护与Excel批量导入(导入默认启用,可选覆盖已有用量)",
        "计量单位与换算",
        "品牌",
        "仓库主数据(含type字段)",
        "MES ç‰©æ–™ç» MdmItemSyncApi åå‘同步"
      ],
      "objects": [
        "物料",
        "SKU",
        "计量单位",
        "仓库"
      ],
      "roles": [
        "主数据管理员"
      ],
      "upstream": [],
      "downstream": [
        "生产",
        "进销存",
        "销售",
        "采购"
      ],
      "coreTables": [
        "mdm_item",
        "mdm_item_sku",
        "mdm_item_category",
        "mdm_unit_measure",
        "mdm_brand",
        "mdm_warehouse"
      ]
    },
    {
      "key": "crm",
      "name": "销售管理(客户关系)",
      "color": "#4f9cff",
      "tag": "CRM",
      "flow": "sales",
      "positioning": "销售前端:从线索到回款的客户经营",
      "problem": "客户资产沉淀、商机转化、合同审批与开票回款管控",
      "features": [
        "线索转化客户",
        "公海池与领取上限",
        "联系人",
        "商机自定义阶段",
        "跟进记录",
        "报价单(AI OCR)",
        "合同(BPM审批→生成销售订单·支持Excel导入)",
        "开票申请(销售订单/出库单选填·选合同自动带出唯一已审核订单)→审批→录入发票(发票号/日期/附件+合同款/预付款/到货款/验收款/质保金收款节点)",
        "开票(BPM审批·支持Excel导入·同一批数据不可重复导入)→回款(先开票校验·核销明细可拆分到订单/出库/开票)",
        "回款计划(收款节点回写已回款/实际时间/状态)",
        "客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
        "投标保证金台账(客户招标方+已审核销售订单·中标结果/退回状态/退回金额日期)",
        "销售目标",
        "数据权限三级(负责人/只读/读写)"
      ],
      "objects": [
        "线索",
        "客户",
        "联系人",
        "商机",
        "报价单",
        "合同",
        "发票",
        "回款",
        "回款核销明细",
        "销售目标",
        "投标保证金"
      ],
      "roles": [
        "销售内勤",
        "销售经理",
        "财务(开票回款)"
      ],
      "upstream": [
        "主数据(物料)",
        "进销存(销售订单)"
      ],
      "downstream": [
        "进销存(销售订单)",
        "售后(客户)",
        "AI(报价OCR)"
      ],
      "coreTables": [
        "crm_clue",
        "crm_customer",
        "crm_contact",
        "crm_business",
        "crm_sale_quotation",
        "crm_contract",
        "crm_invoice",
        "crm_receivable",
        "crm_receivable_plan",
        "crm_permission",
        "crm_bid_bond"
      ]
    },
    {
      "key": "srm",
      "name": "供应商管理",
      "color": "#c9a86a",
      "tag": "SRM",
      "flow": "purchase",
      "positioning": "供应商档案 + è¯¢ä»·æ¯”ä»·",
      "problem": "供应商信息多开户行/多联系人维护,采购寻源按产品横向比价并快速转为采购订单",
      "features": [
        "供应商档案与分类",
        "多联系人/多银行账户(含银行行号)",
        "询价单:新增时一并录入产品行×参与供应商报价",
        "每个产品单独选定中标供应商",
        "确认后按中标供应商拆分生成采购订单草稿"
      ],
      "objects": [
        "供应商",
        "询价比价"
      ],
      "roles": [
        "采购员",
        "供应商管理员"
      ],
      "upstream": [
        "主数据(物料)"
      ],
      "downstream": [
        "进销存(采购订单)"
      ],
      "coreTables": [
        "srm_supplier",
        "srm_supplier_contact",
        "srm_supplier_bank",
        "srm_supplier_category",
        "srm_inquiry",
        "srm_inquiry_item",
        "srm_inquiry_supplier",
        "srm_inquiry_quote",
        "srm_inquiry_order"
      ]
    },
    {
      "key": "erp",
      "name": "进销存与财务",
      "color": "#5fc79e",
      "tag": "ERP",
      "flow": "purchase",
      "positioning": "商贸与资金单据中枢,向 MES å‘起实物收发",
      "problem": "采购销售单据审批、来票付款控制、订单执行进度回写",
      "features": [
        "采购申请(BPM)→订单(自动/手动生成)",
        "确认收货→MES到货通知",
        "入库/退货数量状态回写",
        "采购来票(绑定采购订单+MES采购入库单·双层剩余额度校验)",
        "付款单(必须关联来票·BPM)",
        "供应商往来(订单/来票/付款三流水·左右展示开票金额)",
        "销售订单(BPM·审批通过自动建MPS)",
        "出库状态回写",
        "收款单(关联CRM客户)",
        "结算账户",
        "AI发票OCR"
      ],
      "objects": [
        "采购申请",
        "采购订单",
        "采购来票",
        "付款单",
        "供应商往来",
        "销售订单",
        "收款单",
        "结算账户"
      ],
      "roles": [
        "采购员",
        "销售员",
        "出纳",
        "会计"
      ],
      "upstream": [
        "CRM(合同)",
        "SRM(询价确认)",
        "主数据"
      ],
      "downstream": [
        "生产(MPS)",
        "仓储(收发)",
        "财务(来票付款入账)"
      ],
      "coreTables": [
        "erp_purchase_request",
        "erp_purchase_order",
        "erp_purchase_invoice",
        "erp_finance_payment",
        "erp_sale_order",
        "erp_finance_receipt",
        "erp_account"
      ]
    },
    {
      "key": "finance",
      "name": "财务管理(会计核算)",
      "color": "#3ec9a7",
      "tag": "FIN",
      "flow": "finance",
      "positioning": "独立核算模块:会计核算 + èµ„产核算 + è´¢åŠ¡æŠ¥è¡¨çœ‹æ¿ï¼ˆä¸Ž ERP è¿›é”€å­˜è´¢åŠ¡å•æ®åˆ†ç¦»ï¼Œyudao-module-finance)",
      "problem": "进销存只管单据不管账,缺少会计科目/凭证/账簿与资产折旧摊销沉淀,无法出账表与资产净值",
      "features": [
        "会计科目树(约68条一级科目种子·可建下级)",
        "记账凭证(字号自动生成·分录借贷平衡前后端双重校验)",
        "凭证状态机 æœªè¿‡è´¦0→过账10/作废20,已过账不可改",
        "总账按期间逐月汇总",
        "明细账逐条分录并结转余额",
        "固定资产+计提折旧",
        "无形资产+计提摊销",
        "会计核算看板(收支/资产/凭证/月度趋势)",
        "凭证来源单据关联(sourceType/sourceId/sourceNo,可回溯开票/回款/来票/付款)",
        "报表取数接入CRM客户应收对账/已发货未开票/未核销应收账龄/月度开票回款趋势",
        "附件走系统附件中心 recordType=fin_voucher"
      ],
      "objects": [
        "会计科目",
        "记账凭证",
        "凭证分录",
        "固定资产",
        "无形资产"
      ],
      "roles": [
        "会计",
        "财务主管",
        "资产会计",
        "管理层"
      ],
      "upstream": [
        "进销存(ERP å·²å®¡æ ¸æ”¶è´§/付款/购销单据)",
        "CRM(开票/回款/核销/对账报表)"
      ],
      "downstream": [
        "财务报表看板",
        "BI ç®¡ç†é©¾é©¶èˆ±"
      ],
      "coreTables": [
        "fin_account_subject",
        "fin_voucher",
        "fin_voucher_entry",
        "fin_fixed_asset",
        "fin_intangible_asset"
      ]
    },
    {
      "key": "pro",
      "name": "生产管理",
      "color": "#e0b34f",
      "tag": "MES-PRO",
      "flow": "production",
      "positioning": "生产执行核心:计划-工单-任务-报工",
      "problem": "订单到车间执行的逐层拆解与过程可控",
      "features": [
        "MPS主生产计划(可由销售订单自动创建·合并下发)",
        "工单(按工艺路线自动生成工序任务)",
        "报工(按工序顺序控制·BPM审批)",
   "生产排产(选工作站后按岗位从「员工管理」带出报工人·不要求开通系统账号)",
        "倒冲扣线边库/产出入库",
        "安灯",
        "工艺路线·工序·工单BOM",
        "研发设计项目(PD·审批归档)",
        "AI å››ç±»é¢„测"
      ],
      "objects": [
        "MPS",
        "生产工单",
        "生产任务",
        "报工单",
        "工艺路线",
        "工单BOM",
        "设计项目"
      ],
      "roles": [
        "计划员",
        "车间主任",
        "操作工",
        "工艺员"
      ],
      "upstream": [
        "进销存(销售订单)"
      ],
      "downstream": [
        "仓储(领料/入库)",
        "质量(IPQC)"
      ],
      "coreTables": [
        "mes_pro_mps",
        "mes_pro_work_order",
        "mes_pro_task",
        "mes_pro_feedback",
        "mes_pro_route",
        "mes_pro_process",
        "mes_pro_work_order_bom",
        "mes_pd_project"
      ]
    },
    {
      "key": "wm",
      "name": "仓储管理",
      "color": "#7e6bd6",
      "tag": "MES-WM",
      "flow": "warehouse",
      "positioning": "厂内 WMS:单据驱动的库存账实一致",
      "problem": "多来源出入库、批次/SN è¿½æº¯ã€åº“存精确到库位",
      "features": [
        "15种出入库单据",
        "库存台账(物料×仓库×库区×库位×批次×供应商)",
        "事务流水只读留痕",
        "调拨成对事务",
        "盘点(静态/动态·盲盘·盘盈盘亏调整)",
        "批次/SN/条码/装箱嵌套",
        "虚拟线边库(允许负库存)",
        "库存占用(5ç±»)",
        "四级冻结",
        "WMS审批配置+BPM",
        "对外 WmsStockApi/WmsTransactionApi"
      ],
      "objects": [
        "到货通知",
        "采购入库",
        "领料",
        "完工入库",
        "销售出库",
        "发货通知",
        "销售退货",
        "供应商退货",
        "调拨",
        "盘点",
        "库存台账",
        "批次",
        "SN"
      ],
      "roles": [
        "仓管员",
        "拣货员",
        "盘点员"
      ],
      "upstream": [
        "采购(收货)",
        "生产(领退料)",
        "销售(发货)",
        "售后(退货)"
      ],
      "downstream": [
        "进销存(数量回写)",
        "财务(成本基础)"
      ],
      "coreTables": [
        "mes_wm_warehouse",
        "mes_wm_material_stock",
        "mes_wm_transaction",
        "mes_wm_item_receipt",
        "mes_wm_product_issue",
        "mes_wm_product_receipt",
        "mes_wm_product_sales",
        "mes_wm_return_sales",
        "mes_wm_transfer",
        "mes_wm_stock_taking_task",
        "mes_wm_batch",
        "mes_wm_sn",
        "mes_wm_stock_reserve"
      ]
    },
    {
      "key": "qc",
      "name": "质量管理",
      "color": "#e06c6c",
      "tag": "MES-QC",
      "flow": "quality",
      "positioning": "四道检验闸口 + ä¸åˆæ ¼å“é—­çŽ¯å¤„ç½®",
      "problem": "来料/过程/出货/退货检验标准化,不合格可追溯处置",
      "features": [
        "IQC æ¥æ–™(到货/外协)",
        "IPQC è¿‡ç¨‹(联动报工)",
        "OQC å‡ºè´§(联动销售出库)",
        "RQC é€€è´§(联动退料/售后)",
        "质检方案·指标·缺陷库",
        "检测结果:合格/特采/退货/报废",
        "NCR:评审→处置(退货/报废/返工/让步/降级)→关闭",
        "AI åˆ¤å®šå»ºè®®"
      ],
      "objects": [
        "IQC",
        "IPQC",
        "OQC",
        "RQC",
        "质检方案",
        "NCR",
        "缺陷记录"
      ],
      "roles": [
        "质检员(IQC/IPQC/OQC)",
        "质量工程师"
      ],
      "upstream": [
        "仓储(到货)",
        "生产(报工)",
        "销售(出库)",
        "售后(退货)"
      ],
      "downstream": [
        "仓储(合格入库/报废出库)",
        "生产(NCR返工)"
      ],
      "coreTables": [
        "mes_qc_iqc",
        "mes_qc_ipqc",
        "mes_qc_oqc",
        "mes_qc_rqc",
        "mes_qc_template",
        "mes_qc_indicator",
        "mes_qc_defect",
        "mes_qc_ncr"
      ]
    },
    {
      "key": "qcreport",
      "name": "智能质检报告",
      "color": "#b06ce0",
      "tag": "QC-REPORT",
      "flow": "quality",
      "positioning": "可视化设计报告模板 â†’ ä¸€é”®å‡ºä»¶å¹¶å†»ç»“历史报告",
      "problem": "各行业质检报告格式各异,排版硬编码改一次发一次版;且历史报告必须可复现、不随业务数据变化",
      "features": [
        "GrapesJS å¯è§†åŒ–设计器(质检组件走注册机制,不硬编码进编辑器)",
        "模板 + å¤šç‰ˆæœ¬ï¼šè‰ç¨¿0/已发布1/已停用2,已发布版本内容不可覆盖",
        "组件数据绑定与独立判定规则引擎(禁用 eval / ScriptEngine)",
        "与前端逐字对齐的 Java æ¸²æŸ“引擎,出站 HTML å‰¥ç¦» on* äº‹ä»¶å±žæ€§ä¸Ž javascript:/vbscript:/data: åè®®",
        "出件接口:preview åªæ¸²æŸ“不落库不消耗编号 / generate æ¸²æŸ“+落库+冻结 data_snapshot / regenerate ç”¨å†»ç»“快照与同一版本重渲",
        "报告编号 QR+yyyyMMdd+4位流水,按天独立,可显式指定(撞号会报出具体编号)",
        "缺口清单只在 preview/generate/regenerate å“åº”里返回,不落库",
        "PDF(Playwright) å½’档与 MES å››ç§è´¨æ£€å•归一化对接待后续轮次"
      ],
      "objects": [
        "报告模板",
        "报告实例"
      ],
      "roles": [
        "报告设计员",
        "质检员(出件)"
      ],
      "upstream": [
        "质量(质检单上下文,本轮由调用方传入)"
      ],
      "downstream": [
        "附件中心(PDF å½’档·后续轮次)",
        "BI"
      ],
      "coreTables": [
        "qc_report_template",
        "qc_report_template_version",
        "qc_report_instance",
        "qc_report_data_source",
        "qc_report_rule",
        "qc_report_render_record"
      ]
    },
    {
      "key": "dv",
      "name": "设备与工装",
      "color": "#59a8b8",
      "tag": "MES-DV/TM",
      "flow": null,
      "positioning": "设备资产健康与生产资源保障",
      "problem": "设备点检保养维修可执行、工装状态可控",
      "features": [
        "设备台账(停机/生产中/保养中)",
        "点检·保养方案与记录",
        "维修工单(草稿→维修→待验收→已确认)",
        "计量器具检定",
        "工装夹具(在库/领用/ç»´ä¿®/报废)"
      ],
      "objects": [
        "设备",
        "点检记录",
        "维修工单",
        "工装"
      ],
      "roles": [
        "设备员",
        "维修工",
        "工装管理员"
      ],
      "upstream": [],
      "downstream": [
        "生产(AI é£Žé™©é¢„测消费设备数据)"
      ],
      "coreTables": [
        "mes_dv_machinery",
        "mes_dv_check_plan",
        "mes_dv_check_record",
        "mes_dv_repair",
        "mes_tm_tool"
      ]
    },
    {
      "key": "aftersales",
      "name": "售后服务",
      "color": "#c78bd6",
      "tag": "AFTER",
      "flow": "aftersales",
      "positioning": "客户反馈到退货/ç»´ä¿®/退款闭环",
      "problem": "售后工单流转、退货与质检入库退款的跨系统联动",
      "features": [
        "工单六态(草稿→处理中→待入库→待退款→完结→关闭)",
        "问题判定(一般/ç»´ä¿®/退货)",
        "客户诉求反馈描述+附件",
        "处理结果+附件",
        "工单流转记录",
        "ç»´ä¿®(现场/返厂/远程)",
        "退货由仓储物流发起→MES é”€å”®é€€è´§å•(原售后退货申请页面已下线)",
        "质检结论/入库/退款三重回调",
        "行级数据权限四视图"
      ],
      "objects": [
        "售后工单",
        "工单流转记录",
        "维修记录",
        "退货申请(后台保留)"
      ],
      "roles": [
        "客服",
        "维修工程师",
        "售后管理员"
      ],
      "upstream": [
        "CRM(客户)",
        "ERP(销售订单)"
      ],
      "downstream": [
        "仓储(销售退货)",
        "质量(RQC)",
        "财务(退款)"
      ],
      "coreTables": [
        "after_sale_ticket",
        "after_sale_ticket_item",
        "after_sale_ticket_flow",
        "after_sale_repair",
        "after_sale_return",
        "after_sale_permission"
      ]
    },
    {
      "key": "hrm",
      "name": "人力资源",
      "color": "#9db85a",
      "tag": "HRM",
      "flow": null,
      "positioning": "员工入转调离与考勤薪酬",
      "problem": "人事流程审批化、离职数据不丢(交接)、算薪自动化",
      "features": [
        "员工档案(入职自动建系统账号)",
        "请假/加班/调岗/离职 BPM å®¡æ‰¹",
        "离职交接同时转移员工ID/用户ID/业务数据 expandUserIds",
        "离职到期自动把员工状态改为离职并停用系统账号",
        "考勤(移动端我的考勤状态·打卡·规则·排班·补卡审批·月度汇总)",
        "薪酬(应发-社保公积金-个税-请假扣款·1.5/2/3倍加班费·发放台账)",
        "社保公积金档案按部门维护(部门下员工共用最新生效档案)",
        "培训证书(SQL先行待实现)"
      ],
      "objects": [
        "员工",
        "请假单",
        "离职申请",
        "交接映射",
        "考勤记录",
        "部门社保档案",
        "薪酬核算"
      ],
      "roles": [
        "HR",
        "部门主管",
        "员工本人"
      ],
      "upstream": [],
      "downstream": [
        "各模块(数据可见性扩展)",
        "system(账号)"
      ],
      "coreTables": [
        "hrm_employee",
        "hrm_leave_application",
        "hrm_resignation_application",
        "hrm_user_handover",
        "hrm_attendance_record",
        "hrm_salary_calculation",
        "hrm_salary_payment",
        "hrm_employee_social_security"
      ]
    },
    {
      "key": "bpm",
      "name": "协同审批",
      "color": "#e6a23c",
      "tag": "BPM",
      "flow": null,
      "positioning": "全系统单据审批中枢(Flowable)",
      "problem": "多模块单据需要灵活可配的多级审批",
      "features": [
        "BPMN+钉钉风格双设计器",
        "流程定义部署",
        "审批事件统一回调(BpmProcessInstanceStatusEvent)",
        "13个业务监听器",
        "表单·用户组·抄送·加签"
      ],
      "objects": [
        "流程模型",
        "流程实例",
        "审批任务"
      ],
      "roles": [
        "审批人(按流程配置)",
        "流程管理员"
      ],
      "upstream": [
        "各业务提交审批"
      ],
      "downstream": [
        "回调更新各业务单据状态"
      ],
      "coreTables": [
        "bpm_process_definition_info",
        "bpm_process_instance",
        "bpm_task",
        "act_*",
        "flowable_*"
      ]
    },
    {
      "key": "ai",
      "name": "AI æ™ºèƒ½",
      "color": "#56c8d8",
      "tag": "AI",
      "flow": "ai",
      "positioning": "单据录入提效与生产决策辅助",
      "problem": "人工录发票/报价慢、质检与排产依赖经验",
      "features": [
        "chat/chatWithImage åŒé€šé“",
        "OCR é¢„å¡«(发票/报价)",
        "质检判定建议",
        "缺料/时长/风险/交付四类预测",
        "RAG çŸ¥è¯†åº“(Milvus)",
        "AI不可用时 fallback äººå·¥"
      ],
      "objects": [
        "知识库",
        "AI模型配置",
        "对话记录"
      ],
      "roles": [
        "采购员",
        "销售",
        "质检员",
        "计划员"
      ],
      "upstream": [
        "各业务提供上下文数据"
      ],
      "downstream": [
        "结构化JSON建议返回业务侧"
      ],
      "coreTables": [
        "ai_chat_conversation",
        "ai_chat_message",
        "ai_model",
        "ai_knowledge",
        "ai_knowledge_document",
        "ai_knowledge_segment"
      ]
    }
  ],
  "platform": {
    "name": "系统底座",
    "capabilities": ["JWT+OAuth2 è®¤è¯Â·æ»‘块验证码", "RBAC è§’色-菜单-数据范围(全部/部门/本部门及下级/仅本人/自定义)", "字典", "附件中心(storage-blob æ–‡ä»¶ + storage-attachment æŒ‰ recordType+recordId å¤šæ€å…³è”,全模块强制使用)", "编码规则(各业务自实现 Redis å‰ç¼€+日期+流水)", "Quartz å®šæ—¶ä»»åŠ¡Â·RocketMQ/Kafka/Redis Stream MQ·WebSocket", "多租户已关闭", "实际 system_role ä»… 4 ä¸ªï¼šè¶…级管理员/普通角色/CRM管理员/租户管理员,业务分工靠数据权限而非多角色"],
    "unused": ["im æ¨¡å—(IM/RTC,业务未消费)", "member æ¨¡å—(C端会员,遗留)"]
    "capabilities": [
      "JWT+OAuth2 è®¤è¯Â·æ»‘块验证码",
      "RBAC è§’色-菜单-数据范围(全部/部门/本部门及下级/仅本人/自定义)",
      "字典",
      "附件中心(storage-blob æ–‡ä»¶ + storage-attachment æŒ‰ recordType+recordId å¤šæ€å…³è”,全模块强制使用)",
      "编码规则(各业务自实现 Redis å‰ç¼€+日期+流水)",
      "Quartz å®šæ—¶ä»»åŠ¡Â·RocketMQ/Kafka/Redis Stream MQ·WebSocket",
      "多租户已关闭",
      "实际 system_role ä»… 4 ä¸ªï¼šè¶…级管理员/普通角色/CRM管理员/租户管理员,业务分工靠数据权限而非多角色"
    ],
    "unused": [
      "im æ¨¡å—(IM/RTC,业务未消费)",
      "member æ¨¡å—(C端会员,遗留)"
    ]
  }
}</script>
<script type="application/json" id="data-objects">{
  "objects": [
    {"name": "物料", "module": "基础主数据", "table": "mdm_item", "doc": "MDM æƒå¨ç‰©æ–™/产品/半成品档案,含 SKU、批次属性、价格、安全库存", "upstream": ["MES ç‰©æ–™åå‘同步"], "downstream": ["报价单", "销售订单", "采购订单", "工单BOM", "库存台账"], "keywords": "物料 äº§å“ åŽŸææ–™ item"},
    {"name": "仓库", "module": "基础主数据", "table": "mdm_warehouse", "doc": "仓库主数据,type: 1原料/2成品/3半成品/4线边;MES è‡ªæœ‰ mes_wm_warehouse æ‰¿è½½åº“区库位", "upstream": [], "downstream": ["库存台账", "出入库单据"], "keywords": "仓库 warehouse åŽŸæ–™ä»“ æˆå“ä»“ çº¿è¾¹ä»“"},
    {"name": "客户", "module": "销售管理", "table": "crm_customer", "doc": "客户主档,公海池/锁定/成交状态,负责人数据权限;客户列表按「审核通过」合同实时统计已签订合同份数与金额合计", "upstream": ["线索"], "downstream": ["联系人", "商机", "报价单", "合同", "售后工单", "收款单"], "keywords": "客户 customer å…¬æµ·"},
    {"name": "线索", "module": "销售管理", "table": "crm_clue", "doc": "原始销售线索,仅一次转化为客户", "upstream": [], "downstream": ["客户"], "keywords": "线索 clue"},
    {"name": "联系人", "module": "销售管理", "table": "crm_contact", "doc": "客户下联系人,与商机多对多", "upstream": ["客户"], "downstream": ["商机"], "keywords": "联系人 contact"},
    {"name": "商机", "module": "销售管理", "table": "crm_business", "doc": "销售机会,DB é…ç½®å¼é˜¶æ®µæŽ¨è¿›ï¼Œç»“束态: èµ¢/输/无效", "upstream": ["客户"], "downstream": ["报价单", "合同"], "keywords": "商机 business èµ¢å• è¾“单 é˜¶æ®µ"},
    {"name": "报价单", "module": "销售管理", "table": "crm_sale_quotation", "doc": "QT编号,选 MDM ç‰©æ–™æŠ¥ä»·ï¼ŒAI OCR å½•入,草稿→已转合同", "upstream": ["商机"], "downstream": ["合同"], "keywords": "报价 quotation QT"},
    {"name": "合同", "module": "销售管理", "table": "crm_contract", "doc": "HT编号,BPM å®¡æ‰¹(0草稿/10审批中/20通过/30驳回/40取消/50作废),通过后可生成销售订单;支持 Excel å¯¼å…¥ï¼ˆä¸€åˆåŒå¤šäº§å“æ˜Žç»†è¡Œã€åˆå¹¶å•元格自动拉平,客户与物料按名称/编码校验);产品明细带出物料名称/编码/条码/规格型号/计量单位;删除合同时连带删除产品明细,不留孤儿数据", "upstream": ["报价单"], "downstream": ["销售订单", "回款计划", "开票"], "keywords": "合同 contract å®¡æ‰¹ å¯¼å…¥"},
    {"name": "发票", "module": "销售管理", "table": "crm_invoice", "doc": "KP编号(可留空自动生成、系统内唯一),BPM å®¡æ‰¹ï¼Œç´¯è®¡å¼€ç¥¨â‰¤åˆåŒé¢ï¼Œé™„件必传;开票申请须绑定 ERP é”€å”®è®¢å•与 MES é”€å”®å‡ºåº“单,审批通过后进入录入发票阶段(entry_status 0待录入→10已录入),录入时填写发票号码/日期/附件并设置合同款/预付款/到货款/验收款/质保金收款节点;支持 Excel å¯¼å…¥ï¼ˆä»…新增不更新,合同须审核通过且本次金额≤合同剩余可开票额;同一批数据不可重复导入——同合同下发票号码相同、或发票号码留空时开票金额+发票抬头+开票日期相同即判重复,文件内重复行与库内已有单都拒该行并回显维度与实际值)", "upstream": ["合同", "销售订单", "销售出库单"], "downstream": ["回款", "回款计划", "记账凭证"], "keywords": "开票 å‘票 invoice å¯¼å…¥ åŽ»é‡ å¼€ç¥¨ç”³è¯· å½•入发票 å‡ºåº“单"},
    {"name": "回款", "module": "销售管理", "table": "crm_receivable", "doc": "HK编号,创建即审批中(手动审核),强制先开票,金额双校验;新增/编辑可带核销明细 items(20销售订单/21销售出库/23开票),明细合计必须等于回款金额,审核通过后回写回款计划已回款额/实际回款时间/状态(0未回款10部分回款20已回清)", "upstream": ["合同", "发票", "销售订单", "销售出库单"], "downstream": ["收款单(关联)", "回款核销明细", "回款计划", "记账凭证"], "keywords": "回款 æ”¶æ¬¾è®¡åˆ’ receivable æ ¸é”€"},
    {"name": "回款核销明细", "module": "销售管理", "table": "crm_receivable_item", "doc": "每行把回款金额核销到一张业务单据:biz_type 20销售订单/21销售出库/23开票,biz_no、单据金额、已核销金额由后端按 biz_id å›žå¡«ï¼›æ¯è¡Œæ ¸é”€é¢ä¸å¾—超过该单据剩余应收;不传 items ä¸”带 invoice_id æ—¶è‡ªåŠ¨ç”Ÿæˆä¸€æ¡å¼€ç¥¨æ ¸é”€è¡Œ", "upstream": ["回款", "销售订单", "销售出库单", "发票"], "downstream": ["客户应收对账", "未核销应收报表"], "keywords": "核销 æ ¸é”€æ˜Žç»† write off item receivable_item"},
    {"name": "销售目标", "module": "销售管理", "table": "crm_sales_target", "doc": "å¹´/å­£/月×个人/部门/产品维度的合同回款等目标", "upstream": [], "downstream": ["业绩统计"], "keywords": "目标 target ä¸šç»©"},
    {"name": "供应商", "module": "供应商管理", "table": "srm_supplier", "doc": "供应商档案:执照/税号;多个联系人(srm_supplier_contact)与多个银行账户(srm_supplier_bank·含银行行号),主表保留默认联系人/默认银行字段,ERP ä¸Ž MES å…±ç”¨", "upstream": ["手工建档/Excel å¯¼å…¥"], "downstream": ["询价比价", "投标保证金", "采购订单", "到货通知", "供应商退货", "供应商往来"], "keywords": "供应商 supplier vendor è”系人 å¼€æˆ·è¡Œ é“¶è¡Œè¡Œå·"},
    {"name": "询价比价", "module": "供应商管理", "table": "srm_inquiry", "doc": "XJ编号,草稿0→已转订单20→已取消30(10询价中为历史状态);新增/编辑时一并录入产品行×参与供应商报价(srm_inquiry_quote)并选定每个产品的中标供应商,无需单独的报价/比价步骤,确认后按中标供应商拆分生成采购订单草稿并记录 srm_inquiry_order", "upstream": ["供应商", "主数据(物料)"], "downstream": ["采购订单(草稿)"], "keywords": "询价 æ¯”ä»· æŠ¥ä»· ä¸­æ ‡ inquiry quote"},
    {"name": "投标保证金", "module": "销售管理", "table": "crm_bid_bond", "doc": "BZ编号台账,按客户(招标方)登记投标保证金:项目名称/招标编号/保证金金额/缴纳日期,可关联该客户已审核的销售订单(销售台账,回填订单号与订单金额);中标结果(0待开标1已中标2未中标),未中标跟踪退回状态(0未退1部分2已退3无需退)/退回金额/退回日期;选销售订单自动置为已中标且不可与未中标并存", "upstream": ["客户", "销售订单(销售台账)"], "downstream": ["客户往来/应收对账(参考)"], "keywords": "投标保证金 ä¸­æ ‡ æœªä¸­æ ‡ é€€å›ž é€€æ¬¾ æ‹›æ ‡ bid bond"},
    {"name": "采购申请", "module": "进销存", "table": "erp_purchase_request", "doc": "CGSQ编号,BPM å®¡æ‰¹ï¼Œé€šè¿‡åŽæœ‰ä¾›åº”商自动生成订单", "upstream": ["生产缺口(人工)"], "downstream": ["采购订单"], "keywords": "采购申请 è¯·è´­ request"},
    {"name": "采购订单", "module": "进销存", "table": "erp_purchase_order", "doc": "CGDD编号,BPM å®¡æ‰¹(草稿0→审批中15→已审核20),入库状态:未0部分1全部2;来源可为采购申请或询价比价确认生成的草稿(来源单号回溯);单价为含税单价,价税合计=单价×数量,税额从含税金额中反算(不再额外加税)", "upstream": ["采购申请", "询价比价(确认中标)"], "downstream": ["到货通知", "采购来票", "供应商退货(约束物料)", "供应商往来"], "keywords": "采购订单 purchase"},
    {"name": "采购来票", "module": "进销存", "table": "erp_purchase_invoice", "doc": "FPD编号,AI OCR é¢„填,累计≤订单总价,付款单的前置;必须同时绑定采购订单与 MES é‡‡è´­å…¥åº“单(item_receipt_id),按订单与入库单两层剩余可来票额度校验;供应商往来右侧展示开票金额/已付/剩余应付", "upstream": ["采购订单", "MES é‡‡è´­å…¥åº“单"], "downstream": ["付款单", "供应商往来", "记账凭证"], "keywords": "来票 å‘票 é‡‡è´­å‘票 å…¥åº“单"},
    {"name": "付款单", "module": "财务", "table": "erp_finance_payment", "doc": "FKD编号,必须关联来票+附件,累计≤来票金额,BPM å®¡æ‰¹", "upstream": ["采购来票"], "downstream": ["结算账户", "供应商往来"], "keywords": "付款 åº”付 payment"},
    {"name": "供应商往来", "module": "进销存", "table": "无独立表(聚合视图)", "doc": "按供应商+时间范围聚合采购订单/来票/付款:汇总累计采购额、累计开票额、累计付款额、未付余额;左侧订单行含已开票/未开票金额,右侧来票行含开票金额/已付/剩余应付", "upstream": ["采购订单", "采购来票", "付款单"], "downstream": [], "keywords": "供应商往来 å¯¹è´¦ å¼€ç¥¨é‡‘额 åº”付 statement"},
    {"name": "收款单", "module": "财务", "table": "erp_finance_receipt", "doc": "SKD编号,关联 CRM å®¢æˆ·æ ¡éªŒï¼Œæ—  BPM(手动审核)", "upstream": ["销售(弱关联)"], "downstream": ["结算账户"], "keywords": "收款 åº”æ”¶ receipt"},
    {"name": "会计科目", "module": "会计核算", "table": "fin_account_subject", "doc": "科目树,parent_id è‡ªå…³è”;科目类型1资产2负债3权益4成本5损益,余额方向1借2贷,状态0启用1禁用;账表与分录统一按 subject_code å–æ•°", "upstream": ["会计科目种子约68条一级科目"], "downstream": ["记账凭证分录", "总账", "明细账"], "keywords": "会计科目 ç§‘ç›® subject account èµ„产类 è´Ÿå€ºç±» æŸç›Šç±»"},
    {"name": "记账凭证", "module": "会计核算", "table": "fin_voucher", "doc": "凭证字号可留空由后端自动生成;状态 æœªè¿‡è´¦0/已过账10/已作废20;已过账不可改,未过账与已过账均可作废,未过账与已作废可删除;可通过 source_type/source_id/source_no å…³è”开票/回款/来票/付款等来源单据并支持按来源单号检索;附件按 recordType=fin_voucher æŒ‚系统附件中心", "upstream": ["会计科目", "开票", "回款", "采购来票", "付款单"], "downstream": ["总账", "明细账", "财务报表看板"], "keywords": "记账凭证 å‡­è¯ voucher è¿‡è´¦ ä½œåºŸ å€Ÿè´·å¹³è¡¡"},
    {"name": "凭证分录", "module": "会计核算", "table": "fin_voucher_entry", "doc": "凭证下逐行分录:科目编码+借/贷金额+辅助核算(1客户2供应商3部门4员工5项目);一行不可借贷同时大于0,全表借贷合计必须相等且大于0", "upstream": ["记账凭证", "会计科目"], "downstream": ["总账", "明细账"], "keywords": "凭证分录 åˆ†å½• entry å€Ÿæ–¹ è´·æ–¹ è¾…助核算"},
    {"name": "固定资产", "module": "会计核算", "table": "fin_fixed_asset", "doc": "资产卡片,状态 1在用/2闲置/3维修中/4已报废;月折旧额=(原值×(1-残值率))÷(年限×12),累计折旧与净值由计提累加,不接收前端传值", "upstream": [], "downstream": ["财务报表看板", "总账"], "keywords": "固定资产 æŠ˜æ—§ èµ„产卡片 fixed asset æ®‹å€¼çއ"},
    {"name": "无形资产", "module": "会计核算", "table": "fin_intangible_asset", "doc": "无形资产卡片(证书编号/有效期),状态 1在用/2已失效/3已摊销完;月摊销额=(原值×(1-残值率))÷(摊销年限×12),累计摊销与净值由计提累加", "upstream": [], "downstream": ["财务报表看板", "总账"], "keywords": "无形资产 æ‘Šé”€ è½¯ä»¶è‘—作权 ä¸“利 intangible æœ‰æ•ˆæœŸ"},
    {"name": "销售订单", "module": "进销存", "table": "erp_sale_order", "doc": "XSDD编号,BPM å®¡æ‰¹ï¼Œéœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS,出库状态:未0已预留1部分2全部3,生产完成事件回写", "upstream": ["合同"], "downstream": ["MPS", "发货通知", "销售出库", "售后工单"], "keywords": "销售订单 sale"},
    {"name": "主生产计划", "module": "生产", "table": "mes_pro_mps", "doc": "MPS计划,草稿0→已下发1→完成2/取消3,可合并下发", "upstream": ["销售订单"], "downstream": ["生产工单"], "keywords": "MPS ä¸»è®¡åˆ’ ç”Ÿäº§è®¡åˆ’"},
    {"name": "生产工单", "module": "生产", "table": "mes_pro_work_order", "doc": "草稿0→已确认1→完成2/取消3,确认时按工艺路线生成任务,最后一道工序报工回写已生产数量", "upstream": ["MPS"], "downstream": ["生产任务", "领料", "完工入库"], "keywords": "工单 ç”Ÿäº§å·¥å• work order"},
    {"name": "生产任务", "module": "生产", "table": "mes_pro_task", "doc": "工序粒度,预备0→生产中1→部分2→完成4,可暂停9取消5", "upstream": ["生产工单"], "downstream": ["报工"], "keywords": "任务 task å·¥åº"},
    {"name": "报工单", "module": "生产", "table": "mes_pro_feedback", "doc": "草稿0→审批中2→待检3/完成4,按工序顺序校验(上道工序报满才可报),联动倒冲消耗/产出入库/IPQC;报工时快照工序计费类型/工资定额并计算报工工资(计时=工时×定额,计件=数量×定额),工资记在报人名下,审批通过后进入薪酬核算;保存时按工序标准工时配置+生产长度+辅助项次数快照标准工时(主工时+辅助工时)与计算依据,实际工时与标准工时差异仅用于考核对比、不参与工资计算", "upstream": ["生产任务", "生产工序"], "downstream": ["产品产出", "物料消耗", "IPQC", "薪酬核算"], "keywords": "报工 feedback å·¥èµ„ è®¡ä»¶ è®¡æ—¶ å·¥æ—¶"},
    {"name": "生产工序", "module": "生产", "table": "mes_pro_process", "doc": "工序主数据,计费类型 1-计时/2-计件,工资定额(计时元/小时、计件元/ä»¶);报工时按本工序配置快照并计算报工工资,工艺路线按工序排序驱动任务。标准工时配置 std_time_mode(0未启用/1转速×节距/2线速度直取/3满速百分比×节距)、折算系数、满转速与参数编码,用于报工时计算考核用标准工时", "upstream": [], "downstream": ["工艺路线", "报工", "辅助工时配置"], "keywords": "工序 process è®¡è´¹ç±»åž‹ å·¥èµ„定额 è®¡ä»¶ è®¡æ—¶ æ ‡å‡†å·¥æ—¶ æŠ˜ç®—系数"},
    {"name": "辅助工时配置", "module": "生产", "table": "mes_pro_worktime_aux", "doc": "按工序(可选机台)维护辅助工时项目:按次/按个/按盘计量时长,或按米以折算速度计时;换色场景按材料对(材料→材料)配置。报工时操作工手工录入次数/米数,系统折算辅助工时并计入标准工时快照", "upstream": ["生产工序"], "downstream": ["报工"], "keywords": "辅助工时 æ¢è‰² æ¬¡æ•° æŠ˜ç®— worktime aux"},
    {"name": "工艺路线", "module": "生产", "table": "mes_pro_route", "doc": "工序顺序+质检/关键/倒冲标记,驱动工单任务与报工规则", "upstream": [], "downstream": ["生产工单"], "keywords": "工艺 è·¯çº¿ route å·¥åº"},
    {"name": "到货通知单", "module": "仓储", "table": "mes_wm_arrival_notice", "doc": "采购订单确认收货生成,草稿0→待质检2→待入库3→完成4", "upstream": ["采购订单"], "downstream": ["IQC", "采购入库"], "keywords": "到货 æ”¶è´§ arrival"},
    {"name": "采购入库单", "module": "仓储", "table": "mes_wm_item_receipt", "doc": "草稿0→待上架2→待执行3→完成4,完成后库存+并回写 ERP inCount/inStatus", "upstream": ["到货通知"], "downstream": ["库存台账", "采购订单(回写)"], "keywords": "采购入库 æ”¶è´§å…¥åº“ receipt"},
    {"name": "生产领料单", "module": "仓储", "table": "mes_wm_product_issue", "doc": "草稿0→待拣货2→待执行3→完成4,库存-", "upstream": ["生产工单"], "downstream": ["库存台账"], "keywords": "领料 issue"},
    {"name": "销售出库单", "module": "仓储", "table": "mes_wm_product_sales", "doc": "发货通知审核后生成,提交→OQC检测→拣货→发运10→待执行3→完成4,回写销售订单出库数与状态", "upstream": ["发货通知", "销售订单"], "downstream": ["库存台账", "OQC"], "keywords": "销售出库 å‘è´§ å‡ºåº“单 sales"},
    {"name": "销售退货单", "module": "仓储", "table": "mes_wm_return_sales", "doc": "草稿→待检验(RQC)→待上架→待入库→完成,售后与手工两来源", "upstream": ["售后退货(仓储发起)"], "downstream": ["库存台账", "RQC"], "keywords": "销售退货 é€€è´§å…¥åº“"},
    {"name": "供应商退货单", "module": "仓储", "table": "mes_wm_return_vendor", "doc": "IQC ä¸åˆæ ¼é€€è´§å‘起,待拣货→待执行→完成,库存-", "upstream": ["IQC"], "downstream": ["库存台账", "采购订单(returnCount)"], "keywords": "供应商退货 é€€æ–™ é€€è´§ vendor"},
    {"name": "调拨单", "module": "仓储", "table": "mes_wm_transfer", "doc": "内部/外部调拨,成对 MOVE_OUT+MOVE_IN äº‹åŠ¡ï¼Œåœ¨é€”æ•°é‡", "upstream": [], "downstream": ["库存台账"], "keywords": "调拨 ç§»åº“ transfer"},
    {"name": "盘点任务", "module": "仓储", "table": "mes_wm_stock_taking_task", "doc": "静态/动态盘点,行结果:正常/盘盈(ADJUST_IN)/盘亏(ADJUST_OUT),可冻结库存、盲盘", "upstream": ["库存台账"], "downstream": ["库存台账(调整)"], "keywords": "盘点 ç›˜ç›ˆ ç›˜äº stock taking"},
    {"name": "库存台账", "module": "仓储", "table": "mes_wm_material_stock", "doc": "物料×仓库×库区×库位×批次×供应商六元组,数量/冻结/占用/在途四量", "upstream": ["全部入库单"], "downstream": ["全部出库单", "事务流水"], "keywords": "库存 å°è´¦ stock å¯ç”¨é‡"},
    {"name": "批次", "module": "仓储", "table": "mes_wm_batch", "doc": "关联物料/工单/供应商/客户/质量状态;SN ç å«æ‰¹æ¬¡ uuid;条码支持18种业务对象", "upstream": ["入库单据"], "downstream": ["库存台账"], "keywords": "批次 batch SN æ¡ç  è¿½æº¯"},
    {"name": "IQC来料检验", "module": "质量", "table": "mes_qc_iqc", "doc": "到货通知/外协入库触发,草稿0→完成4,结论回写来源单据;行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆå·¥å…·æ ã€Œæ‰¹é‡å¯¼å…¥ã€ï¼Œçº¯æ–°å¢žã€å¯¼å…¥å•为草稿、编号可留空自动生成);同一批数据不可重复导入(物料+供应商+供应商批次+来料日期,文件内重复行与库内已有单都拒该行)", "upstream": ["到货通知", "外协入库", "Excel æ‰¹é‡å¯¼å…¥"], "downstream": ["采购入库", "供应商退货", "NCR", "报告实例(出报告)"], "keywords": "IQC æ¥æ–™æ£€éªŒ å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"},
    {"name": "IPQC过程检验", "module": "质量", "table": "mes_qc_ipqc", "doc": "报工质检工序产出行触发,完成回调推进报工与产出入库;行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆäº§å“ç‰©æ–™ç”±ç”Ÿäº§å·¥å•推导,模板不填物料;工废+料废+其他废品不得超过不合格品数量);同一批数据不可重复导入(生产工单+检验类型+工位+检测日期,文件内重复行与库内已有单都拒该行)", "upstream": ["报工", "Excel æ‰¹é‡å¯¼å…¥"], "downstream": ["产出入库", "NCR", "报告实例(出报告)"], "keywords": "IPQC è¿‡ç¨‹æ£€éªŒ å·¡æ£€ å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"},
    {"name": "OQC出货检验", "module": "质量", "table": "mes_qc_oqc", "doc": "销售出库提交触发,完成推进出库单待拣货;行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆå®¢æˆ·é¡»åœ¨å½“前登录用户的客户数据权限范围内);同一批数据不可重复导入(物料+客户+批次号+出货日期,文件内重复行与库内已有单都拒该行)", "upstream": ["销售出库单", "Excel æ‰¹é‡å¯¼å…¥"], "downstream": ["销售出库单(推进)", "报告实例(出报告)"], "keywords": "OQC å‡ºè´§æ£€éªŒ å‡ºåŽ‚æ£€éªŒ å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"},
    {"name": "RQC退货检验", "module": "质量", "table": "mes_qc_rqc", "doc": "生产退料/销售退货/售后退货触发,结论回调售后;行操作「出报告」仅在已完成时可见。措辞已统一为「退货检验」(原菜单父项与 mes_qc_type å­—典写「退料检验」,与后端枚举/表注释/菜单权限子项不一致;RQC ä¸‰è€…都覆盖,故取「退货检验」)", "upstream": ["退料", "退货"], "downstream": ["退货入库", "售后回调", "报告实例(出报告)"], "keywords": "RQC é€€è´§æ£€éªŒ é€€æ–™æ£€éªŒ å‡ºæŠ¥å‘Š"},
    {"name": "NCR不合格品", "module": "质量", "table": "mes_qc_ncr", "doc": "待评审0→已评审6→已处置7→已关闭8,处置:退货/报废/返工/让步接收/降级", "upstream": ["IQC/IPQC/OQC/RQC"], "downstream": ["退货/报废/返工(台账)"], "keywords": "NCR ä¸åˆæ ¼ å¤„ç½® ç‰¹é‡‡ æŠ¥åºŸ"},
    {"name": "设备", "module": "设备", "table": "mes_dv_machinery", "doc": "台账状态:停机/生产中/保养中;点检·保养·维修工单;AI é£Žé™©é¢„测消费其数据", "upstream": [], "downstream": ["点检", "ç»´ä¿®", "生产风险预测"], "keywords": "设备 ç‚¹æ£€ ä¿å…» ç»´ä¿® machinery"},
    {"name": "售后工单", "module": "售后", "table": "after_sale_ticket", "doc": "草稿0→处理中10→待入库30→待退款40→完结50→关闭60/取消99,判定:一般/ç»´ä¿®/退货;客户诉求反馈描述+附件、处理结果+附件;创建/更新/判定/完结/归档/取消/回访写入流转记录", "upstream": ["客户反馈(人工录入)"], "downstream": ["维修记录", "仓储销售退货(原退货申请页面已下线)"], "keywords": "售后 å·¥å• ticket ç»´ä¿® å®¢æˆ·è¯‰æ±‚ å¤„理结果 æµè½¬è®°å½•"},
    {"name": "工单流转记录", "module": "售后", "table": "after_sale_ticket_flow", "doc": "售后工单每次动作留痕:动作编码/名称、变更前后状态、内容、操作人、时间", "upstream": ["售后工单"], "downstream": [], "keywords": "售后 æµè½¬è®°å½• flow"},
    {"name": "退货申请", "module": "售后", "table": "after_sale_return", "doc": "后台保留,售后页面已下线;草稿0→已审核20(自动建MES退货单)→已质检40→已入库50→已退款60→关闭70;类型:退款退货/换货/仅退款", "upstream": ["售后工单"], "downstream": ["销售退货单", "退款"], "keywords": "售后退货 return"},
    {"name": "员工", "module": "人力", "table": "hrm_employee", "doc": "在职1/试用2/离职3,入职自动建系统账号;离职申请到期自动置为离职3并停用系统账号", "upstream": [], "downstream": ["交接映射", "考勤", "薪酬"], "keywords": "员工 å…¥èŒ employee ç¦»èŒ"},
    {"name": "离职申请", "module": "人力", "table": "hrm_resignation_application", "doc": "草稿0→审批中10→通过20/驳回30/撤销40;审批通过后可交接0→10→20;到期任务写实际离职日期、把员工状态改为离职3并停用系统账号", "upstream": ["员工"], "downstream": ["员工档案状态", "离职交接"], "keywords": "离职 resignation å®¡æ‰¹ äº¤æŽ¥ è‡ªåŠ¨ç¦»èŒ"},
    {"name": "离职交接", "module": "人力", "table": "hrm_user_handover", "doc": "同时记录离职/交接双方员工ID与用户ID;交接完成20 åŽ expandUserIds æ‰©å±•交接人可见数据;审批通过且到预计离职日期后自动把员工状态改为离职3并停用系统账号", "upstream": ["离职申请"], "downstream": ["各模块我的数据查询", "员工档案状态"], "keywords": "交接 ç¦»èŒ handover expandUserIds è‡ªåŠ¨ç¦»èŒ"},
    {"name": "报告模板", "module": "质检报告", "table": "qc_report_template", "doc": "智能质检报告模板,启用0/停用1;画布内容存在 qc_report_template_version(草稿0/已发布1/已停用2,已发布不可覆盖),currentVersion æŒ‡å‘当前已发布版本;源文件经 AI å¯¼å…¥è¯†åˆ«æˆè‰ç¨¿åŽç”±äººå·¥ç¡®è®¤æ‰æˆä¸ºç‰ˆæœ¬ï¼›åŒä¸€æ¨¡æ¿é‡å¤å¯¼å…¥æŒ‰ã€æ›¿æ¢ã€‘处理——先删旧附件(连带 blob è¡Œä¸Žç£ç›˜æ–‡ä»¶ï¼‰å†ç»‘新的,不留孤儿文件;删除模板会连带清附件", "upstream": ["人工设计(GrapesJS è®¾è®¡å™¨)", "AI å¯¼å…¥è‰ç¨¿(qc-report/ai-import/draft)"], "downstream": ["模板版本", "报告实例"], "keywords": "报告模板 è´¨æ£€æŠ¥å‘Š template è®¾è®¡å™¨ ç‰ˆæœ¬ AI导入 è¯†åˆ« è‰ç¨¿ æºæ–‡ä»¶æ›¿æ¢"},
    {"name": "报告实例", "module": "质检报告", "table": "qc_report_instance", "doc": "出件产物,编号 QR+yyyyMMdd+4位流水按天独立且业务唯一,可显式指定覆盖(注意 uk_report_no ä¸åŒºåˆ†è½¯åˆ é™¤ï¼Œåˆ æŽ‰çš„号不回收);两条出件入口:通用 generate(调用方自带 context)与 generate-from-qc(数据从 MES è´¨æ£€å•取,权限码 qc-report:instance:generate å·²å…¥ system_menu id=1075416,五道校验按业务优先级只报第一处:105_000 ç±»åž‹éžæ³• / 105_001 å•据不存在 / 105_002 æœªå®Œæˆ / 105_003 æœªåˆ¤å®š / 105_005 æ¨¡æ¿ç±»åž‹ä¸ç¬¦ / 105_006 æ— å¯ç”¨æ£€éªŒé¡¹ï¼›å››å¼ è´¨æ£€å•列表的行操作「出报告」仅在 status=已完成时可见,弹窗只列 reportType ä¸€è‡´ã€æ¨¡æ¿å¯ç”¨ã€ä¸” currentVersion æŒ‡å‘的版本已发布的模板(后端 /qc-report/template/selectable åˆ¤å®šâ€”—停用版本不清空 currentVersion,前端拿分页结果自己推导会漏),结果就地展示并跳本页列表);冻结 data_snapshot(判定后数据,含后端算好的 PASS/FAIL ä¸Žåˆæ ¼çŽ‡ï¼‰ä¸Ž render_html;regenerate åªé‡æ¸²æŽ’版、数据与编号不变;business_id/business_type å­˜ MES è¡¨åï¼ˆmes_qc_iqc ç­‰ï¼‰ä¸Žå•据 ID,可反查、一单可出多份;报告头另带单据原判定 qcResult(数)/qcResultText(中文,合格/特采/不合格退货/不合格报废),与引擎三态判定并存互不覆盖;响应带 undecidableCount=快照中 result ä¸ºç©ºä¸²çš„项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),两者都不落库、事后看不到", "upstream": ["报告模板版本", "质检单上下文(通用 generate ç”±è°ƒç”¨æ–¹ä¼ å…¥)", "MES è´¨æ£€å• IQC/IPQC/OQC/RQC(generate-from-qc ç» MesQcReportApi å–æ•°)"], "downstream": ["前端实例页 /qc/report-instance(列表·iframe预览·快照页签·导出PDF)", "PDF归档(system_storage_attachment recordType=qc_report_instance)"], "keywords": "报告实例 å‡ºä»¶ QR编号 å¿«ç…§ å¤çް regenerate PDF归档 æŠ¥å‘Šå®žä¾‹é¡µ å¯¼å‡ºPDF generate-from-qc è´¨æ£€å•出报告 å‡ºæŠ¥å‘Šå¼¹çª— qcResult undecidableCount warnings å¾…判定 åæŸ¥ ä¸šåŠ¡å•æ®ç­›é€‰"}
    {
      "name": "物料",
      "module": "基础主数据",
      "table": "mdm_item",
      "doc": "MDM æƒå¨ç‰©æ–™/产品/半成品档案,含 SKU、批次属性、价格、安全库存",
      "upstream": [
        "MES ç‰©æ–™åå‘同步"
      ],
      "downstream": [
        "报价单",
        "销售订单",
        "采购订单",
        "工单BOM",
        "库存台账"
      ],
      "keywords": "物料 äº§å“ åŽŸææ–™ item"
    },
    {
      "name": "仓库",
      "module": "基础主数据",
      "table": "mdm_warehouse",
      "doc": "仓库主数据,type: 1原料/2成品/3半成品/4线边;MES è‡ªæœ‰ mes_wm_warehouse æ‰¿è½½åº“区库位",
      "upstream": [],
      "downstream": [
        "库存台账",
        "出入库单据"
      ],
      "keywords": "仓库 warehouse åŽŸæ–™ä»“ æˆå“ä»“ çº¿è¾¹ä»“"
    },
    {
      "name": "客户",
      "module": "销售管理",
      "table": "crm_customer",
      "doc": "客户主档,公海池/锁定/成交状态,负责人数据权限;客户列表按「审核通过」合同实时统计已签订合同份数与金额合计",
      "upstream": [
        "线索"
      ],
      "downstream": [
        "联系人",
        "商机",
        "报价单",
        "合同",
        "售后工单",
        "收款单"
      ],
      "keywords": "客户 customer å…¬æµ·"
    },
    {
      "name": "线索",
      "module": "销售管理",
      "table": "crm_clue",
      "doc": "原始销售线索,仅一次转化为客户",
      "upstream": [],
      "downstream": [
        "客户"
      ],
      "keywords": "线索 clue"
    },
    {
      "name": "联系人",
      "module": "销售管理",
      "table": "crm_contact",
      "doc": "客户下联系人,与商机多对多",
      "upstream": [
        "客户"
      ],
      "downstream": [
        "商机"
      ],
      "keywords": "联系人 contact"
    },
    {
      "name": "商机",
      "module": "销售管理",
      "table": "crm_business",
      "doc": "销售机会,DB é…ç½®å¼é˜¶æ®µæŽ¨è¿›ï¼Œç»“束态: èµ¢/输/无效",
      "upstream": [
        "客户"
      ],
      "downstream": [
        "报价单",
        "合同"
      ],
      "keywords": "商机 business èµ¢å• è¾“单 é˜¶æ®µ"
    },
    {
      "name": "报价单",
      "module": "销售管理",
      "table": "crm_sale_quotation",
      "doc": "QT编号,选 MDM ç‰©æ–™æŠ¥ä»·ï¼ŒAI OCR å½•入,草稿→已转合同",
      "upstream": [
        "商机"
      ],
      "downstream": [
        "合同"
      ],
      "keywords": "报价 quotation QT"
    },
    {
      "name": "合同",
      "module": "销售管理",
      "table": "crm_contract",
      "doc": "HT编号,BPM å®¡æ‰¹(0草稿/10审批中/20通过/30驳回/40取消/50作废),通过后可生成销售订单;支持 Excel å¯¼å…¥ï¼ˆä¸€åˆåŒå¤šäº§å“æ˜Žç»†è¡Œã€åˆå¹¶å•元格自动拉平,客户与物料按名称/编码校验);产品明细带出物料名称/编码/条码/规格型号/计量单位;删除合同时连带删除产品明细,不留孤儿数据",
      "upstream": [
        "报价单"
      ],
      "downstream": [
        "销售订单",
        "回款计划",
        "开票"
      ],
      "keywords": "合同 contract å®¡æ‰¹ å¯¼å…¥"
    },
    {
      "name": "发票",
      "module": "销售管理",
      "table": "crm_invoice",
      "doc": "KP编号(可留空自动生成、系统内唯一),BPM å®¡æ‰¹ï¼Œç´¯è®¡å¼€ç¥¨â‰¤åˆåŒé¢ï¼Œé™„件必传;开票申请须绑定 ERP é”€å”®è®¢å•与 MES é”€å”®å‡ºåº“单,审批通过后进入录入发票阶段(entry_status 0待录入→10已录入),录入时填写发票号码/日期/附件并设置合同款/预付款/到货款/验收款/质保金收款节点;支持 Excel å¯¼å…¥ï¼ˆä»…新增不更新,合同须审核通过且本次金额≤合同剩余可开票额;同一批数据不可重复导入——同合同下发票号码相同、或发票号码留空时开票金额+发票抬头+开票日期相同即判重复,文件内重复行与库内已有单都拒该行并回显维度与实际值)",
      "upstream": [
        "合同",
        "销售订单",
        "销售出库单"
      ],
      "downstream": [
        "回款",
        "回款计划",
        "记账凭证"
      ],
      "keywords": "开票 å‘票 invoice å¯¼å…¥ åŽ»é‡ å¼€ç¥¨ç”³è¯· å½•入发票 å‡ºåº“单"
    },
    {
      "name": "回款",
      "module": "销售管理",
      "table": "crm_receivable",
      "doc": "HK编号,创建为未提交0,显式提交后走 BPM ååŒå®¡æ‰¹(10审批中→20通过/30驳回/40取消),强制先开票,提交时重新校验金额额度;新增/编辑可带核销明细 items(20销售订单/21销售出库/23开票),明细合计必须等于回款金额;审批通过才回写回款计划已回款额/实际回款时间/状态(0未回款10部分回款20已回清);审批中的回款禁止页面内手工审核/反审核",
      "upstream": [
        "合同",
        "发票",
        "销售订单",
        "销售出库单"
      ],
      "downstream": [
        "收款单(关联)",
        "回款核销明细",
        "回款计划",
        "记账凭证"
      ],
      "keywords": "回款 æ”¶æ¬¾è®¡åˆ’ receivable æ ¸é”€"
    },
    {
      "name": "回款核销明细",
      "module": "销售管理",
      "table": "crm_receivable_item",
      "doc": "每行把回款金额核销到一张业务单据:biz_type 20销售订单/21销售出库/23开票,biz_no、单据金额、已核销金额由后端按 biz_id å›žå¡«ï¼›æ¯è¡Œæ ¸é”€é¢ä¸å¾—超过该单据剩余应收;不传 items ä¸”带 invoice_id æ—¶è‡ªåŠ¨ç”Ÿæˆä¸€æ¡å¼€ç¥¨æ ¸é”€è¡Œ",
      "upstream": [
        "回款",
        "销售订单",
        "销售出库单",
        "发票"
      ],
      "downstream": [
        "客户应收对账",
        "未核销应收报表"
      ],
      "keywords": "核销 æ ¸é”€æ˜Žç»† write off item receivable_item"
    },
    {
      "name": "销售目标",
      "module": "销售管理",
      "table": "crm_sales_target",
      "doc": "å¹´/å­£/月×个人/部门/产品维度的合同回款等目标",
      "upstream": [],
      "downstream": [
        "业绩统计"
      ],
      "keywords": "目标 target ä¸šç»©"
    },
    {
      "name": "供应商",
      "module": "供应商管理",
      "table": "srm_supplier",
      "doc": "供应商档案:执照/税号;多个联系人(srm_supplier_contact)与多个银行账户(srm_supplier_bank·含银行行号),主表保留默认联系人/默认银行字段,ERP ä¸Ž MES å…±ç”¨",
      "upstream": [
        "手工建档/Excel å¯¼å…¥"
      ],
      "downstream": [
        "询价比价",
        "投标保证金",
        "采购订单",
        "到货通知",
        "供应商退货",
        "供应商往来"
      ],
      "keywords": "供应商 supplier vendor è”系人 å¼€æˆ·è¡Œ é“¶è¡Œè¡Œå·"
    },
    {
      "name": "询价比价",
      "module": "供应商管理",
      "table": "srm_inquiry",
      "doc": "XJ编号,草稿0→已转订单20→已取消30(10询价中为历史状态);新增/编辑时一并录入产品行×参与供应商报价(srm_inquiry_quote)并选定每个产品的中标供应商,无需单独的报价/比价步骤,确认后按中标供应商拆分生成采购订单草稿并记录 srm_inquiry_order",
      "upstream": [
        "供应商",
        "主数据(物料)"
      ],
      "downstream": [
        "采购订单(草稿)"
      ],
      "keywords": "询价 æ¯”ä»· æŠ¥ä»· ä¸­æ ‡ inquiry quote"
    },
    {
      "name": "投标保证金",
      "module": "销售管理",
      "table": "crm_bid_bond",
      "doc": "BZ编号台账,按客户(招标方)登记投标保证金:项目名称/招标编号/保证金金额/缴纳日期,可关联该客户已审核的销售订单(销售台账,回填订单号与订单金额);中标结果(0待开标1已中标2未中标),未中标跟踪退回状态(0未退1部分2已退3无需退)/退回金额/退回日期;选销售订单自动置为已中标且不可与未中标并存",
      "upstream": [
        "客户",
        "销售订单(销售台账)"
      ],
      "downstream": [
        "客户往来/应收对账(参考)"
      ],
      "keywords": "投标保证金 ä¸­æ ‡ æœªä¸­æ ‡ é€€å›ž é€€æ¬¾ æ‹›æ ‡ bid bond"
    },
    {
      "name": "采购申请",
      "module": "进销存",
      "table": "erp_purchase_request",
      "doc": "CGSQ编号,BPM å®¡æ‰¹ï¼Œé€šè¿‡åŽæœ‰ä¾›åº”商自动生成订单",
      "upstream": [
        "生产缺口(人工)"
      ],
      "downstream": [
        "采购订单"
      ],
      "keywords": "采购申请 è¯·è´­ request"
    },
    {
      "name": "采购订单",
      "module": "进销存",
      "table": "erp_purchase_order",
      "doc": "菜单名「采购台账」,CGDD编号,BPM å®¡æ‰¹(草稿0→审批中15→已审核20),入库状态:未0部分1全部2;表头含采购类型(生产/办公用品/其他)、合同号(手工输入不自动生成)、项目名称、付款日期(必填,超30天账期标红)、付款方式(电汇/汇票/现金/手机支付);明细产品编号快照 MDM ç‰©æ–™ç¼–码;来源可为采购申请、询价比价确认生成的草稿或销售订单/生产工单(来源单号回溯);单价为含税单价,价税合计=单价×数量,税额从含税金额中反算(不再额外加税)",
      "upstream": [
        "采购申请",
        "询价比价(确认中标)"
      ],
      "downstream": [
        "到货通知",
        "采购来票",
        "供应商退货(约束物料)",
        "供应商往来"
      ],
      "keywords": "采购订单 purchase"
    },
    {
      "name": "采购来票",
      "module": "进销存",
      "table": "erp_purchase_invoice",
      "doc": "FPD编号,AI OCR é¢„填,累计≤订单总价,付款单的前置;必须同时绑定采购订单与 MES é‡‡è´­å…¥åº“单(item_receipt_id),按订单与入库单两层剩余可来票额度校验;供应商往来右侧展示开票金额/已付/剩余应付",
      "upstream": [
        "采购订单",
        "MES é‡‡è´­å…¥åº“单"
      ],
      "downstream": [
        "付款单",
        "供应商往来",
        "记账凭证"
      ],
      "keywords": "来票 å‘票 é‡‡è´­å‘票 å…¥åº“单"
    },
    {
      "name": "付款单",
      "module": "财务",
      "table": "erp_finance_payment",
      "doc": "FKD编号,必须关联来票+附件,累计≤来票金额,BPM å®¡æ‰¹",
      "upstream": [
        "采购来票"
      ],
      "downstream": [
        "结算账户",
        "供应商往来"
      ],
      "keywords": "付款 åº”付 payment"
    },
    {
      "name": "供应商往来",
      "module": "进销存",
      "table": "无独立表(聚合视图)",
      "doc": "按供应商+时间范围聚合采购订单/来票/付款:汇总累计采购额、累计开票额、累计付款额、未付余额;左侧订单行含已开票/未开票金额,右侧来票行含开票金额/已付/剩余应付",
      "upstream": [
        "采购订单",
        "采购来票",
        "付款单"
      ],
      "downstream": [],
      "keywords": "供应商往来 å¯¹è´¦ å¼€ç¥¨é‡‘额 åº”付 statement"
    },
    {
      "name": "收款单",
      "module": "财务",
      "table": "erp_finance_receipt",
      "doc": "SKD编号,关联 CRM å®¢æˆ·æ ¡éªŒï¼Œæ—  BPM(手动审核)",
      "upstream": [
        "销售(弱关联)"
      ],
      "downstream": [
        "结算账户"
      ],
      "keywords": "收款 åº”æ”¶ receipt"
    },
    {
      "name": "会计科目",
      "module": "会计核算",
      "table": "fin_account_subject",
      "doc": "科目树,parent_id è‡ªå…³è”;科目类型1资产2负债3权益4成本5损益,余额方向1借2贷,状态0启用1禁用;账表与分录统一按 subject_code å–æ•°",
      "upstream": [
        "会计科目种子约68条一级科目"
      ],
      "downstream": [
        "记账凭证分录",
        "总账",
        "明细账"
      ],
      "keywords": "会计科目 ç§‘ç›® subject account èµ„产类 è´Ÿå€ºç±» æŸç›Šç±»"
    },
    {
      "name": "记账凭证",
      "module": "会计核算",
      "table": "fin_voucher",
      "doc": "凭证字号可留空由后端自动生成;状态 æœªè¿‡è´¦0/已过账10/已作废20;已过账不可改,未过账与已过账均可作废,未过账与已作废可删除;可通过 source_type/source_id/source_no å…³è”开票/回款/来票/付款等来源单据并支持按来源单号检索;附件按 recordType=fin_voucher æŒ‚系统附件中心",
      "upstream": [
        "会计科目",
        "开票",
        "回款",
        "采购来票",
        "付款单"
      ],
      "downstream": [
        "总账",
        "明细账",
        "财务报表看板"
      ],
      "keywords": "记账凭证 å‡­è¯ voucher è¿‡è´¦ ä½œåºŸ å€Ÿè´·å¹³è¡¡"
    },
    {
      "name": "凭证分录",
      "module": "会计核算",
      "table": "fin_voucher_entry",
      "doc": "凭证下逐行分录:科目编码+借/贷金额+辅助核算(1客户2供应商3部门4员工5项目);一行不可借贷同时大于0,全表借贷合计必须相等且大于0",
      "upstream": [
        "记账凭证",
        "会计科目"
      ],
      "downstream": [
        "总账",
        "明细账"
      ],
      "keywords": "凭证分录 åˆ†å½• entry å€Ÿæ–¹ è´·æ–¹ è¾…助核算"
    },
    {
      "name": "固定资产",
      "module": "会计核算",
      "table": "fin_fixed_asset",
      "doc": "资产卡片,状态 1在用/2闲置/3维修中/4已报废;月折旧额=(原值×(1-残值率))÷(年限×12),累计折旧与净值由计提累加,不接收前端传值",
      "upstream": [],
      "downstream": [
        "财务报表看板",
        "总账"
      ],
      "keywords": "固定资产 æŠ˜æ—§ èµ„产卡片 fixed asset æ®‹å€¼çއ"
    },
    {
      "name": "无形资产",
      "module": "会计核算",
      "table": "fin_intangible_asset",
      "doc": "无形资产卡片(证书编号/有效期),状态 1在用/2已失效/3已摊销完;月摊销额=(原值×(1-残值率))÷(摊销年限×12),累计摊销与净值由计提累加",
      "upstream": [],
      "downstream": [
        "财务报表看板",
        "总账"
      ],
      "keywords": "无形资产 æ‘Šé”€ è½¯ä»¶è‘—作权 ä¸“利 intangible æœ‰æ•ˆæœŸ"
    },
    {
      "name": "销售订单",
      "module": "进销存",
      "table": "erp_sale_order",
      "doc": "XSDD编号,BPM å®¡æ‰¹ï¼Œéœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS,出库状态:未0已预留1部分2全部3,生产完成事件回写",
      "upstream": [
        "合同"
      ],
      "downstream": [
        "MPS",
        "发货通知",
        "销售出库",
        "售后工单"
      ],
      "keywords": "销售订单 sale"
    },
    {
      "name": "主生产计划",
      "module": "生产",
      "table": "mes_pro_mps",
      "doc": "MPS计划,草稿0→已下发1→完成2/取消3,可合并下发",
      "upstream": [
        "销售订单"
      ],
      "downstream": [
        "生产工单"
      ],
      "keywords": "MPS ä¸»è®¡åˆ’ ç”Ÿäº§è®¡åˆ’"
    },
    {
      "name": "生产工单",
      "module": "生产",
      "table": "mes_pro_work_order",
      "doc": "草稿0→已确认1→完成2/取消3,确认时按工艺路线生成任务,最后一道工序报工回写已生产数量",
      "upstream": [
        "MPS"
      ],
      "downstream": [
        "生产任务",
        "领料",
        "完工入库"
      ],
      "keywords": "工单 ç”Ÿäº§å·¥å• work order"
    },
    {
      "name": "生产任务",
      "module": "生产",
      "table": "mes_pro_task",
      "doc": "工序粒度,预备0→生产中1→部分2→完成4,可暂停9取消5",
      "upstream": [
        "生产工单"
      ],
      "downstream": [
        "报工"
      ],
      "keywords": "任务 task å·¥åº"
    },
    {
      "name": "报工单",
      "module": "生产",
      "table": "mes_pro_feedback",
      "doc": "草稿0→审批中2→待检3/完成4,按工序顺序校验(上道工序报满才可报),联动倒冲消耗/产出入库/IPQC;质检工序报工审批后转待检验并生成 PENDING äº§å‡ºè¡Œï¼Œç­‰å·²å®Œæˆ IPQC æŒ‰ç‰©æ–™+盘号+数量关联后才回写完成。报工时快照工序计费类型/工资定额并计算报工工资:单价优先按工艺路线+主计价设备+生效日期匹配单价规则,未命中回退工序工资定额;工资字段(系数/加减金额/计价数量)调整主+辅工资;计件按合格数量计价,质检工序按合格率应用质量系数或不合格扣款,工资记在报工人(feedback_user_id)名下,审批通过后进入薪酬核算。质检工序提交时自动生成草稿过程检验单,审批前必须完成。保存时按工序标准工时配置+生产长度+辅助项次数快照标准工时(主工时+辅助工时)与计算依据,实际工时与标准工时的差异仅用于考核对比,不参与工资计算",
      "upstream": [
        "生产任务",
        "生产工序"
      ],
      "downstream": [
        "产品产出",
        "物料消耗",
        "IPQC",
        "薪酬核算"
      ],
      "keywords": "报工 feedback å·¥èµ„ è®¡ä»¶ è®¡æ—¶ å·¥æ—¶ å•价规则 è®¾å¤‡ åˆæ ¼çއ è¾…助工资 è¿‡ç¨‹æ£€éªŒ"
    },
    {
      "name": "生产工序",
      "module": "生产",
      "table": "mes_pro_process",
      "doc": "工序主数据,计费类型 1-计时/2-计件,工资定额(计时元/小时、计件元/件);报工时按本工序配置快照并计算报工工资;另维护自定义工资字段、按工艺路线/设备/生效日期的单价规则、质量工资规则与辅助小时工资,工艺路线按工序排序驱动任务。标准工时配置 std_time_mode(0未启用/1转速×节距/2线速度直取/3满速百分比×节距)、折算系数、满转速与参数编码,用于报工时计算考核用标准工时",
      "upstream": [],
      "downstream": [
        "工艺路线",
        "报工",
        "辅助工时配置"
      ],
      "keywords": "工序 process è®¡è´¹ç±»åž‹ å·¥èµ„定额 å·¥èµ„字段 å•价规则 è´¨é‡è§„则 è¾…助小时工资 è®¡ä»¶ è®¡æ—¶ æ ‡å‡†å·¥æ—¶ æŠ˜ç®—系数"
    },
    {
      "name": "辅助工时配置",
      "module": "生产",
      "table": "mes_pro_worktime_aux",
      "doc": "按工序(可选机台)维护辅助工时项目:按次/按个/按盘计量时长,或按米以折算速度计时;换色场景按材料对(材料→材料)配置。报工时操作工手工录入次数/米数,系统折算辅助工时并计入标准工时快照",
      "upstream": [
        "生产工序"
      ],
      "downstream": [
        "报工"
      ],
      "keywords": "辅助工时 æ¢è‰² æ¬¡æ•° æŠ˜ç®— worktime aux"
    },
    {
      "name": "工艺路线",
      "module": "生产",
      "table": "mes_pro_route",
      "doc": "工序顺序+质检/关键/倒冲标记,驱动工单任务与报工规则",
      "upstream": [],
      "downstream": [
        "生产工单"
      ],
      "keywords": "工艺 è·¯çº¿ route å·¥åº"
    },
    {
      "name": "到货通知单",
      "module": "仓储",
      "table": "mes_wm_arrival_notice",
      "doc": "采购订单确认收货生成,草稿0→待质检2→待入库3→完成4;表头带采购合同号(选择采购订单自动带入,换货到货沿用原退货单合同号),可按合同号检索",
      "upstream": [
        "采购订单",
        "供应商退货(换货)"
      ],
      "downstream": [
        "IQC",
        "采购入库"
      ],
      "keywords": "到货 æ”¶è´§ arrival åˆåŒå·"
    },
    {
      "name": "采购入库单",
      "module": "仓储",
      "table": "mes_wm_item_receipt",
      "doc": "草稿0→待上架2→待执行3→完成4,完成后库存+并回写 ERP inCount/inStatus",
      "upstream": [
        "到货通知"
      ],
      "downstream": [
        "库存台账",
        "采购订单(回写)"
      ],
      "keywords": "采购入库 æ”¶è´§å…¥åº“ receipt"
    },
    {
      "name": "生产领料单",
      "module": "仓储",
      "table": "mes_wm_product_issue",
      "doc": "草稿0→待拣货2→待执行3→完成4,库存-",
      "upstream": [
        "生产工单"
      ],
      "downstream": [
        "库存台账"
      ],
      "keywords": "领料 issue"
    },
    {
      "name": "销售出库单",
      "module": "仓储",
      "table": "mes_wm_product_sales",
      "doc": "发货通知审核后生成,提交后有 oqcCheckFlag è¡Œè½¬å¾…检测1(提交时不再自动建 OQC),需质检员独立录入 OQC å¹¶åœ¨æ£€éªŒå•详情按物料+盘号+数量关联待检行;全部待检行合格后转待拣货2→发运10→待执行3→完成4,回写销售订单出库数与状态",
      "upstream": [
        "发货通知",
        "销售订单"
      ],
      "downstream": [
        "库存台账",
        "OQC"
      ],
      "keywords": "销售出库 å‘è´§ å‡ºåº“单 sales"
    },
    {
      "name": "销售退货单",
      "module": "仓储",
      "table": "mes_wm_return_sales",
      "doc": "草稿→待检验(RQC)→待上架→待入库→完成,售后与手工两来源",
      "upstream": [
        "售后退货(仓储发起)"
      ],
      "downstream": [
        "库存台账",
        "RQC"
      ],
      "keywords": "销售退货 é€€è´§å…¥åº“"
    },
    {
      "name": "供应商退货单",
      "module": "仓储",
      "table": "mes_wm_return_vendor",
      "doc": "质量部发起退货,草稿0→审批中1(BPM:采购→质量→仓库)→待拣货2→待执行退货3→完成4,可取消5,库存-;上传附件走 storage-attachment;处理方式 1退款(完成后自动生成负数冲红付款单) / 2换货(完成后自动生成待质检到货通知单并创建 IQC,重新入库质检);合同号从采购订单快照",
      "upstream": [
        "IQC",
        "采购订单(合同号)"
      ],
      "downstream": [
        "库存台账",
        "采购订单(returnCount)",
        "付款单(冲红)",
        "到货通知(换货)"
      ],
      "keywords": "供应商退货 é€€æ–™ é€€è´§ vendor"
    },
    {
      "name": "调拨单",
      "module": "仓储",
      "table": "mes_wm_transfer",
      "doc": "内部/外部调拨,成对 MOVE_OUT+MOVE_IN äº‹åŠ¡ï¼Œåœ¨é€”æ•°é‡",
      "upstream": [],
      "downstream": [
        "库存台账"
      ],
      "keywords": "调拨 ç§»åº“ transfer"
    },
    {
      "name": "盘点任务",
      "module": "仓储",
      "table": "mes_wm_stock_taking_task",
      "doc": "静态/动态盘点,行结果:正常/盘盈(ADJUST_IN)/盘亏(ADJUST_OUT),可冻结库存、盲盘",
      "upstream": [
        "库存台账"
      ],
      "downstream": [
        "库存台账(调整)"
      ],
      "keywords": "盘点 ç›˜ç›ˆ ç›˜äº stock taking"
    },
    {
      "name": "库存台账",
      "module": "仓储",
      "table": "mes_wm_material_stock",
      "doc": "物料×仓库×库区×库位×批次×供应商六元组,数量/冻结/占用/在途四量",
      "upstream": [
        "全部入库单"
      ],
      "downstream": [
        "全部出库单",
        "事务流水"
      ],
      "keywords": "库存 å°è´¦ stock å¯ç”¨é‡"
    },
    {
      "name": "批次",
      "module": "仓储",
      "table": "mes_wm_batch",
      "doc": "关联物料/工单/供应商/客户/质量状态;SN ç å«æ‰¹æ¬¡ uuid;条码支持18种业务对象",
      "upstream": [
        "入库单据"
      ],
      "downstream": [
        "库存台账"
      ],
      "keywords": "批次 batch SN æ¡ç  è¿½æº¯"
    },
    {
      "name": "IQC来料检验",
      "module": "质量",
      "table": "mes_qc_iqc",
      "doc": "到货通知/外协入库触发,草稿0→完成4,结论回写来源单据;行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆå·¥å…·æ ã€Œæ‰¹é‡å¯¼å…¥ã€ï¼Œçº¯æ–°å¢žã€å¯¼å…¥å•为草稿、编号可留空自动生成);同一批数据不可重复导入(物料+供应商+供应商批次+来料日期,文件内重复行与库内已有单都拒该行)",
      "upstream": [
        "到货通知",
        "外协入库",
        "Excel æ‰¹é‡å¯¼å…¥"
      ],
      "downstream": [
        "采购入库",
        "供应商退货",
        "NCR",
        "报告实例(出报告)"
      ],
      "keywords": "IQC æ¥æ–™æ£€éªŒ å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"
    },
    {
      "name": "IPQC过程检验",
      "module": "质量",
      "table": "mes_qc_ipqc",
      "doc": "报工质检工序产出行待检后独立录入,完成时按合格+不合格=检测数量自动判定 1 é€šè¿‡/2 ä¸é€šè¿‡ï¼ˆéœ€å·²ä¸Šä¼ æ£€éªŒé™„件);已完成且未绑定来源的单据,在详情按物料+盘号+数量一致选择待检产出行关联,回写报工完成与产出入库;未关联不回写。行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆäº§å“ç‰©æ–™ç”±ç”Ÿäº§å·¥å•推导,模板不填物料;工废+料废+其他废品不得超过不合格品数量);同一批数据不可重复导入(生产工单+检验类型+工位+检测日期,文件内重复行与库内已有单都拒该行)",
      "upstream": [
        "报工",
        "Excel æ‰¹é‡å¯¼å…¥"
      ],
      "downstream": [
        "产出入库",
        "NCR",
        "报告实例(出报告)"
      ],
      "keywords": "IPQC è¿‡ç¨‹æ£€éªŒ å·¡æ£€ ç›˜å·å…³è” æŠ¥å·¥å…³è” å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"
    },
    {
      "name": "OQC出货检验",
      "module": "质量",
      "table": "mes_qc_oqc",
      "doc": "销售出库提交后待检测行独立录入,完成时按合格+不合格=检测数量自动判定 1 é€šè¿‡/2 ä¸é€šè¿‡ï¼ˆéœ€å·²ä¸Šä¼ æ£€éªŒé™„件);已完成且未绑定来源的单据,在详情按物料+盘号+数量一致选择待检出库行关联,回写出库行质量状态 PASS/FAIL,全部待检行合格后出库单转待拣货;不合格行留待后续处置,不自动取消出库单。行操作「出报告」仅在已完成时可见;支持 Excel æ‰¹é‡å¯¼å…¥ï¼ˆå®¢æˆ·é¡»åœ¨å½“前登录用户的客户数据权限范围内);同一批数据不可重复导入(物料+客户+批次号+出货日期,文件内重复行与库内已有单都拒该行)",
      "upstream": [
        "销售出库单",
        "Excel æ‰¹é‡å¯¼å…¥"
      ],
      "downstream": [
        "销售出库单(推进)",
        "报告实例(出报告)"
      ],
      "keywords": "OQC å‡ºè´§æ£€éªŒ å‡ºåŽ‚æ£€éªŒ ç›˜å·å…³è” å‡ºåº“关联 å‡ºæŠ¥å‘Š æ‰¹é‡å¯¼å…¥ å¯¼å…¥æ¨¡æ¿"
    },
    {
      "name": "RQC退货检验",
      "module": "质量",
      "table": "mes_qc_rqc",
      "doc": "生产退料/销售退货/售后退货触发,结论回调售后;行操作「出报告」仅在已完成时可见。措辞已统一为「退货检验」(原菜单父项与 mes_qc_type å­—典写「退料检验」,与后端枚举/表注释/菜单权限子项不一致;RQC ä¸‰è€…都覆盖,故取「退货检验」)",
      "upstream": [
        "退料",
        "退货"
      ],
      "downstream": [
        "退货入库",
        "售后回调",
        "报告实例(出报告)"
      ],
      "keywords": "RQC é€€è´§æ£€éªŒ é€€æ–™æ£€éªŒ å‡ºæŠ¥å‘Š"
    },
    {
      "name": "NCR不合格品",
      "module": "质量",
      "table": "mes_qc_ncr",
      "doc": "待评审0→提交BPM审批中2→审批通过已评审6(驳回/取消回待评审)→已处置7→已关闭8;审批意见由 BPM æµè½¬è®°å½•承载,质量部最终处置:让步放行/返修/返工/报废(退货/降级仅历史展示)",
      "upstream": [
        "IQC/IPQC/OQC/RQC"
      ],
      "downstream": [
        "退货/报废/返工(台账)"
      ],
      "keywords": "NCR ä¸åˆæ ¼ å¤„ç½® ç‰¹é‡‡ æŠ¥åºŸ"
    },
    {
      "name": "设备",
      "module": "设备",
      "table": "mes_dv_machinery",
      "doc": "台账状态:停机/生产中/保养中;点检·保养·维修工单;AI é£Žé™©é¢„测消费其数据",
      "upstream": [],
      "downstream": [
        "点检",
        "ç»´ä¿®",
        "生产风险预测"
      ],
      "keywords": "设备 ç‚¹æ£€ ä¿å…» ç»´ä¿® machinery"
    },
    {
      "name": "售后工单",
      "module": "售后",
      "table": "after_sale_ticket",
      "doc": "草稿0→处理中10→待入库30→待退款40→完结50→关闭60/取消99,判定:一般/ç»´ä¿®/退货;客户诉求反馈描述+附件、处理结果+附件;创建/更新/判定/完结/归档/取消/回访写入流转记录",
      "upstream": [
        "客户反馈(人工录入)"
      ],
      "downstream": [
        "维修记录",
        "仓储销售退货(原退货申请页面已下线)"
      ],
      "keywords": "售后 å·¥å• ticket ç»´ä¿® å®¢æˆ·è¯‰æ±‚ å¤„理结果 æµè½¬è®°å½•"
    },
    {
      "name": "工单流转记录",
      "module": "售后",
      "table": "after_sale_ticket_flow",
      "doc": "售后工单每次动作留痕:动作编码/名称、变更前后状态、内容、操作人、时间",
      "upstream": [
        "售后工单"
      ],
      "downstream": [],
      "keywords": "售后 æµè½¬è®°å½• flow"
    },
    {
      "name": "退货申请",
      "module": "售后",
      "table": "after_sale_return",
      "doc": "后台保留,售后页面已下线;草稿0→已审核20(自动建MES退货单)→已质检40→已入库50→已退款60→关闭70;类型:退款退货/换货/仅退款",
      "upstream": [
        "售后工单"
      ],
      "downstream": [
        "销售退货单",
        "退款"
      ],
      "keywords": "售后退货 return"
    },
    {
      "name": "员工",
      "module": "人力",
      "table": "hrm_employee",
      "doc": "在职1/试用2/离职3,入职自动建系统账号;离职申请到期自动置为离职3并停用系统账号",
      "upstream": [],
      "downstream": [
        "交接映射",
        "考勤",
        "薪酬"
      ],
      "keywords": "员工 å…¥èŒ employee ç¦»èŒ"
    },
    {
      "name": "离职申请",
      "module": "人力",
      "table": "hrm_resignation_application",
      "doc": "草稿0→审批中10→通过20/驳回30/撤销40;审批通过后可交接0→10→20;到期任务写实际离职日期、把员工状态改为离职3并停用系统账号",
      "upstream": [
        "员工"
      ],
      "downstream": [
        "员工档案状态",
        "离职交接"
      ],
      "keywords": "离职 resignation å®¡æ‰¹ äº¤æŽ¥ è‡ªåŠ¨ç¦»èŒ"
    },
    {
      "name": "离职交接",
      "module": "人力",
      "table": "hrm_user_handover",
      "doc": "同时记录离职/交接双方员工ID与用户ID;交接完成20 åŽ expandUserIds æ‰©å±•交接人可见数据;审批通过且到预计离职日期后自动把员工状态改为离职3并停用系统账号",
      "upstream": [
        "离职申请"
      ],
      "downstream": [
        "各模块我的数据查询",
        "员工档案状态"
      ],
      "keywords": "交接 ç¦»èŒ handover expandUserIds è‡ªåŠ¨ç¦»èŒ"
    },
    {
      "name": "报告模板",
      "module": "质检报告",
      "table": "qc_report_template",
      "doc": "智能质检报告模板,启用0/停用1;画布内容存在 qc_report_template_version(草稿0/已发布1/已停用2,已发布不可覆盖),currentVersion æŒ‡å‘当前已发布版本;源文件经 AI å¯¼å…¥è¯†åˆ«æˆè‰ç¨¿åŽç”±äººå·¥ç¡®è®¤æ‰æˆä¸ºç‰ˆæœ¬ï¼›åŒä¸€æ¨¡æ¿é‡å¤å¯¼å…¥æŒ‰ã€æ›¿æ¢ã€‘处理——先删旧附件(连带 blob è¡Œä¸Žç£ç›˜æ–‡ä»¶ï¼‰å†ç»‘新的,不留孤儿文件;删除模板会连带清附件",
      "upstream": [
        "人工设计(GrapesJS è®¾è®¡å™¨)",
        "AI å¯¼å…¥è‰ç¨¿(qc-report/ai-import/draft)"
      ],
      "downstream": [
        "模板版本",
        "报告实例"
      ],
      "keywords": "报告模板 è´¨æ£€æŠ¥å‘Š template è®¾è®¡å™¨ ç‰ˆæœ¬ AI导入 è¯†åˆ« è‰ç¨¿ æºæ–‡ä»¶æ›¿æ¢"
    },
    {
      "name": "报告实例",
      "module": "质检报告",
      "table": "qc_report_instance",
      "doc": "出件产物,编号 QR+yyyyMMdd+4位流水按天独立且业务唯一,可显式指定覆盖(注意 uk_report_no ä¸åŒºåˆ†è½¯åˆ é™¤ï¼Œåˆ æŽ‰çš„号不回收);两条出件入口:通用 generate(调用方自带 context)与 generate-from-qc(数据从 MES è´¨æ£€å•取,权限码 qc-report:instance:generate å·²å…¥ system_menu id=1075416,五道校验按业务优先级只报第一处:105_000 ç±»åž‹éžæ³• / 105_001 å•据不存在 / 105_002 æœªå®Œæˆ / 105_003 æœªåˆ¤å®š / 105_005 æ¨¡æ¿ç±»åž‹ä¸ç¬¦ / 105_006 æ— å¯ç”¨æ£€éªŒé¡¹ï¼›å››å¼ è´¨æ£€å•列表的行操作「出报告」仅在 status=已完成时可见,弹窗只列 reportType ä¸€è‡´ã€æ¨¡æ¿å¯ç”¨ã€ä¸” currentVersion æŒ‡å‘的版本已发布的模板(后端 /qc-report/template/selectable åˆ¤å®šâ€”—停用版本不清空 currentVersion,前端拿分页结果自己推导会漏),结果就地展示并跳本页列表);冻结 data_snapshot(判定后数据,含后端算好的 PASS/FAIL ä¸Žåˆæ ¼çŽ‡ï¼‰ä¸Ž render_html;regenerate åªé‡æ¸²æŽ’版、数据与编号不变;business_id/business_type å­˜ MES è¡¨åï¼ˆmes_qc_iqc ç­‰ï¼‰ä¸Žå•据 ID,可反查、一单可出多份;报告头另带单据原判定 qcResult(数)/qcResultText(中文,合格/特采/不合格退货/不合格报废),与引擎三态判定并存互不覆盖;响应带 undecidableCount=快照中 result ä¸ºç©ºä¸²çš„项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),两者都不落库、事后看不到",
      "upstream": [
        "报告模板版本",
        "质检单上下文(通用 generate ç”±è°ƒç”¨æ–¹ä¼ å…¥)",
        "MES è´¨æ£€å• IQC/IPQC/OQC/RQC(generate-from-qc ç» MesQcReportApi å–æ•°)"
      ],
      "downstream": [
        "前端实例页 /qc/report-instance(列表·iframe预览·快照页签·导出PDF)",
        "PDF归档(system_storage_attachment recordType=qc_report_instance)"
      ],
      "keywords": "报告实例 å‡ºä»¶ QR编号 å¿«ç…§ å¤çް regenerate PDF归档 æŠ¥å‘Šå®žä¾‹é¡µ å¯¼å‡ºPDF generate-from-qc è´¨æ£€å•出报告 å‡ºæŠ¥å‘Šå¼¹çª— qcResult undecidableCount warnings å¾…判定 åæŸ¥ ä¸šåŠ¡å•æ®ç­›é€‰"
    },
    {
      "name": "发货通知单",
      "module": "仓储物流",
      "table": "mes_wm_sales_notice",
      "doc": "销售订单审核后生成,记录物流公司、快递费、开票状态,经默认物流审批后出库;列表支持按产品平铺台账与带章 Word å¯¼å‡º",
      "upstream": [
        "销售订单"
      ],
      "downstream": [
        "销售出库单",
        "发货台账"
      ],
      "keywords": "发货 å‘货单 ç‰©æµ å¿«é€’è´¹ å¼€ç¥¨çŠ¶æ€ sales_notice"
    },
    {
      "name": "客户收货地址",
      "module": "客户关系",
      "table": "crm_customer_address",
      "doc": "客户档案的一户多收货地址,含收货人、手机号、地区与详细地址,可设默认地址",
      "upstream": [
        "客户"
      ],
      "downstream": [
        "发货通知单"
      ],
      "keywords": "收货地址 å¤šåœ°å€ é»˜è®¤åœ°å€"
    },
    {
      "name": "质量巡检",
      "module": "质量管理",
      "table": "mes_qc_patrol",
      "doc": "独立的巡检记录,按物料+盘号登记合格/不合格数量,不生成库存业务单据",
      "upstream": [
        "物料",
        "批次"
      ],
      "downstream": [],
      "keywords": "巡检 è´¨é‡å·¡æ£€ patrol"
    },
    {
      "name": "库存冻结来源",
      "module": "仓储物流",
      "table": "mes_wm_material_stock_freeze_claim",
      "doc": "按人工/盘点/调拨来源分别持有库存冻结,解冻只释放对应来源,避免互相覆盖",
      "upstream": [
        "库存台账"
      ],
      "downstream": [
        "盘点任务",
        "调拨单"
      ],
      "keywords": "冻结 è§£å†» å†»ç»“来源 freeze"
    }
  ]
}</script>
<script type="application/json" id="data-flows">{
  "flows": [
    {"id": "sales", "name": "销售流程", "mermaid": "mermaid/02-sales-flow.mmd", "color": "#4f9cff",
     "summary": "线索转化客户 â†’ å•†æœºæŽ¨è¿› â†’ æŠ¥ä»·(可AI识别) â†’ åˆåŒ BPM å®¡æ‰¹ â†’ ç”Ÿæˆ ERP é”€å”®è®¢å•并审批 â†’ éœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS â†’ å‘货通知 â†’ é”€å”®å‡ºåº“(OQC)回写 â†’ å¼€ç¥¨(BPM) â†’ å›žæ¬¾(先开票校验);投标保证金按客户登记中标结果与退回进度并关联已审核销售订单。",
     "steps": [
       {"stage": "获客", "action": "线索转化客户、建联系人、立项商机", "roles": ["销售内勤"]},
       {"stage": "报价", "action": "商机赢单后建报价单(AI OCR é¢„å¡«),转合同", "roles": ["销售内勤"]},
       {"stage": "投标", "action": "按客户(招标方)登记投标保证金:项目/招标编号/金额/缴纳日期,可关联该客户已审核的销售订单;开标后回填中标结果,未中标跟踪退回状态、金额与日期", "roles": ["销售内勤"]},
       {"stage": "签约", "action": "合同来源:手工新建 / æŠ¥ä»·å•转入 / Excel å¯¼å…¥(一合同多产品明细行);提交 BPM å®¡æ‰¹(可选流程定义),通过=20", "roles": ["销售", "审批人"]},
       {"stage": "接单", "action": "合同一键生成销售订单(XSDD),销售订单再走 BPM;审批通过自动为 needProduction æ˜Žç»†åˆ›å»º MPS", "roles": ["销售", "计划员"]},
       {"stage": "交付", "action": "发货通知审核(占用)→ é”€å”®å‡ºåº“ OQC â†’ å‘运 â†’ å‡ºåº“回写部分/全部出库", "roles": ["仓管", "质检"]},
       {"stage": "回款", "action": "开票 BPM(累计≤合同额) â†’ å›žæ¬¾å¼ºåˆ¶å…ˆå¼€ç¥¨(累计≤合同剩余与开票剩余)", "roles": ["财务", "销售"]}]},
    {"id": "purchase", "name": "采购流程", "mermaid": "mermaid/03-purchase-flow.mmd", "color": "#5fc79e",
     "summary": "供应商档案(多联系人/多银行·含银行行号) â†’ è¯¢ä»·æ¯”ä»·(产品×供应商横向报价,按产品选中标) â†’ ç¡®è®¤åŽæŒ‰ä¸­æ ‡ä¾›åº”商拆采购订单草稿并走现有 BPM â†’ ç¡®è®¤æ”¶è´§ç”Ÿæˆ MES åˆ°è´§é€šçŸ¥ â†’ IQC(合格/特采入库·退货/报废) â†’ é‡‡è´­å…¥åº“回写订单与申请 â†’ æ¥ç¥¨(AI OCR) â†’ ä»˜æ¬¾å• BPM â†’ ä¾›åº”商往来对账。",
     "steps": [
       {"stage": "寻源", "action": "供应商档案维护多个联系人与多个银行账户(含银行行号);新增询价单时一并录入产品×参与供应商报价并选定每个产品的中标供应商,列表直接确认后按中标供应商生成采购订单草稿", "roles": ["采购员", "供应商管理员"]},
       {"stage": "申请", "action": "采购申请(CGSQ)提交 BPM,驳回可重置回草稿;通过且有供应商自动生成订单", "roles": ["采购员", "审批人"]},
       {"stage": "下单", "action": "采购订单(CGDD)自动提交 BPM(15→20)", "roles": ["采购员"]},
       {"stage": "收货", "action": "确认收货生成到货通知(0→待质检2/待入库3→4);需检物料自动创建 IQC", "roles": ["仓管", "IQC è´¨æ£€å‘˜"]},
       {"stage": "入库", "action": "IQC åˆæ ¼/特采→采购入库(待上架→待执行→完成)库存+,回写订单 inCount/inStatus ä¸Žç”³è¯·æ˜Žç»†", "roles": ["仓管"]},
       {"stage": "退货", "action": "IQC ä¸åˆæ ¼é€€è´§â†’供应商退货出库(选料锁定到订单行);报废走 NCR", "roles": ["仓管"]},
       {"stage": "付款", "action": "采购来票(FPD, AI OCR, â‰¤è®¢å•总价) â†’ ä»˜æ¬¾å•(FKD å…³è”来票+附件, â‰¤æ¥ç¥¨é‡‘额) BPM å®¡æ‰¹", "roles": ["财务", "审批人"]},
       {"stage": "对账", "action": "供应商往来按供应商汇总:左侧采购订单流水含已开票/未开票金额,右侧来票流水含开票金额/已付/剩余应付并附付款流水,顶部汇总累计采购/开票/付款/未付余额", "roles": ["财务", "采购员"]}]},
    {"id": "production", "name": "生产流程", "mermaid": "mermaid/04-production-flow.mmd", "color": "#e0b34f",
     "summary": "销售订单驱动 MPS â†’ ä¸‹å‘工单 â†’ æŒ‰å·¥è‰ºè·¯çº¿æ‹†å·¥åºä»»åŠ¡ â†’ é¢†æ–™/线边备料 â†’ æŠ¥å·¥(工序顺序硬校验+BPM,按工序计费类型快照报工工资) â†’ å€’冲消耗/产出入库/IPQC â†’ æœ«é“工序回写进度 â†’ è‡ªåŠ¨å®Œå·¥ â†’ ProductionFinishedEvent å›žå†™é”€å”®è®¢å•;报工工资在薪酬核算时按报工人汇总,可人工调整后计入实发。报工同时按工序标准工时配置+生产长度+辅助项次数快照标准工时,实际工时与标准工时的差异进入工时考核报表(按人/工序/机台/周期),标准工时只用于考核对比、不参与工资计算。",
     "steps": [
       {"stage": "计划", "action": "MPS åˆ›å»º(手工或销售订单自动)→下发或合并下发为工单", "roles": ["计划员"]},
       {"stage": "派工", "action": "工单确认按工艺路线逐工序生成任务,可指派/暂停/取消", "roles": ["车间主任"]},
       {"stage": "备料", "action": "生产领料单出库或线边虚拟库备料", "roles": ["仓管"]},
       {"stage": "报工", "action": "提交报工:上一道工序未报满直接拦截;启用 BPM èµ°å®¡æ‰¹ï¼›æŒ‰å·¥åºè®¡è´¹ç±»åž‹å¿«ç…§å·¥èµ„(计时=工时×定额,计件=数量×定额),记到报工人名下;同时录入生产长度与辅助项次数,按工序标准工时配置计算并快照标准工时(主工时+辅助工时)与计算依据,配置缺失时不阻断,转人工填工时并记录缺失原因", "roles": ["操作工"]},
      {"stage": "工时考核", "action": "实际工时 vs æ ‡å‡†å·¥æ—¶å®žæ—¶æ¯”对,差异率超阈值(默认20%)提示;报表按人/工序/机台/周期汇总报工单数、生产长度、实际工时、标准工时、差异与差异率,供车间主任与工艺员核对", "roles": ["车间主任", "工艺员"]},
       {"stage": "联动", "action": "审批通过按工序标记: å€’冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]},
       {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS ç´¯è®¡åˆ¤æ–­å®Œæˆå¹¶å‘事件给销售订单", "roles": ["系统自动"]}]},
    {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c",
     "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + åˆ¤å®šå››æžœ(合格/特采/退货/报废) + NCR è¯„审处置关闭;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å½“前仅台账不触发返工单;检验单可由智能质检报告平台一键出件(generate-from-qc:数据从 MES å–,五道校验后渲染落库)。",
     "steps": [
       {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC è´¨æ£€å‘˜"]},
       {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC è´¨æ£€å‘˜"]},
       {"stage": "出货", "action": "销售出库提交→OQC,完成后出库单进入待拣货", "roles": ["OQC è´¨æ£€å‘˜"]},
       {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC è´¨æ£€å‘˜"]},
       {"stage": "导入", "action": "IQC/IPQC/OQC ä¸‰åˆ—表各支持 Excel æ‰¹é‡å¯¼å…¥(按钮在「新增」与「导出」之间,纯新增不做更新覆盖):一行一张单,编号留空自动生成,质检方案留空按物料+检验类型自动匹配,检测人员留空取当前登录用户;物料/供应商/客户/工单/工位按编码或名称精确匹配(IPQC ç‰©æ–™ç”±å·¥å•推导,模板不填),合格+不合格=检测/接收数量、IPQC å·¥åºŸ+料废+其他废品≤不合格、OQC å®¢æˆ·é¡»åœ¨æœ¬äººæ•°æ®æƒé™å†…;逐行导入部分成功,失败行进 failureList å¸¦è¡Œå·ä¸Žå¯æ‰§è¡ŒåŠ¨ä½œï¼ŒæˆåŠŸå•ä¸€å¾‹å¾…æ£€éªŒ0;同一批数据不可重复导入:文件内重复行与库内已存在同业务键单据(IQC ç‰©æ–™+供应商+供应商批次+来料日期、IPQC ç”Ÿäº§å·¥å•+检验类型+工位+检测日期、OQC ç‰©æ–™+客户+批次号+出货日期)均拒该行,提示含维度、双方实际值与「先删除该单据再重试」动作", "roles": ["质检员", "IQC è´¨æ£€å‘˜", "IPQC è´¨æ£€å‘˜", "OQC è´¨æ£€å‘˜"]},
       {"stage": "不合格", "action": "NCR: å¾…评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]},
       {"stage": "出件", "action": "两条出件入口:①通用 generate/preview(调用方自带上下文) æ¸²æŸ“ HTML å¹¶å†»ç»“数据快照,编号 QR+日期+4位流水按天独立,preview åªæ¸²æŸ“不落库不消耗编号,regenerate ç”¨å†»ç»“快照重渲且数据与编号不变并作废已归档 PDF,导出 PDF ç”±åŽç«¯è°ƒ Chromium æ‰“印实例已存的 HTML åŽå½’档附件中心;②generate-from-qc ç”±è´¨æ£€å•直接出件(数据经 MesQcReportApi ä»Ž MES å–,前端不拼上下文),出件前五道校验按业务优先级只报第一处:类型非法/单据不存在/未完成检验/未填判定/模板类型不符/无可用检验项,报告头另带单据原判定 qcResult+qcResultText ä¸Žå¼•擎判定并存,响应回 undecidableCount å¾…判定项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),同一张单据可反复出多份", "roles": ["质检员"]},
       {"stage": "建模板", "action": "AI å¯¼å…¥ï¼šä¸Šä¼ æ—¢æœ‰æ£€éªŒæŠ¥å‘Šæ–‡ä»¶(扫描件/照片、电子版 PDF、Word/Excel) â†’ è¯†åˆ«æˆæ¨¡æ¿è‰ç¨¿(不落库) â†’ äººå·¥åœ¨è®¾è®¡å™¨ç¡®è®¤è°ƒæ•´åŽä¿å­˜ä¸ºç‰ˆæœ¬ï¼›.doc/.xls æ—§æ ¼å¼ä¸æ”¯æŒ", "roles": ["质检员", "质量工程师"]}]},
    {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6",
     "summary": "15 ç±»å•据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN è¿½æº¯ã€åº“存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi ä¾› ERP æ”¶å‘。",
     "steps": [
       {"stage": "入库", "action": "采购入库/生产入库/产出入库/退货入库/外协入库/杂项入库 â†’ IN äº‹åŠ¡ï¼Œæ‰¹æ¬¡æ··æ”¾æ ¡éªŒã€å››çº§å†»ç»“æ ¡éªŒ", "roles": ["仓管"]},
       {"stage": "出库", "action": "销售出库/领料/消耗/外协发料/供应商退货/杂项出库 â†’ OUT äº‹åŠ¡ï¼ŒcheckFlag æŽ§åˆ¶å¯å¦è´Ÿåº“å­˜", "roles": ["仓管", "系统自动"]},
       {"stage": "移库", "action": "内部/外部调拨成对 MOVE äº‹åŠ¡ï¼Œåœ¨é€”æ•°é‡è·Ÿè¸ª", "roles": ["仓管"]},
       {"stage": "盘点", "action": "方案→任务→明细行→结果,盘盈 ADJUST_IN、盘亏 ADJUST_OUT,可冻结与盲盘", "roles": ["盘点员"]},
       {"stage": "追溯", "action": "批次/SN(uuid)/条码(18ç±»)/装箱嵌套支撑正反向追溯", "roles": ["质量", "售后"]}]},
    {"id": "aftersales", "name": "售后流程", "mermaid": "mermaid/07-after-sales-flow.mmd", "color": "#c78bd6",
     "summary": "工单(六态+问题判定) â†’ å®¢æˆ·è¯‰æ±‚/处理结果附件与流转记录留痕 â†’ ç»´ä¿®ä¸‰ç±»åž‹æˆ–由仓储物流发起销售退货 â†’ RQC è´¨æ£€/入库/退款三回调逐级推进 â†’ å·¥å•完结关闭。",
     "steps": [
       {"stage": "受理", "action": "人工录入工单(关联客户/订单/合同),填写客户诉求反馈描述并上传附件,创建时指定的处理人直接进入处理中", "roles": ["客服"]},
       {"stage": "判定", "action": "问题判定: ä¸€èˆ¬/ç»´ä¿®/退货,严重度 0-3", "roles": ["售后工程师"]},
       {"stage": "处理", "action": "填写处理结果并上传处理结果附件;创建、更新、判定、完结、归档、取消、回访均写入流转记录", "roles": ["售后工程师"]},
       {"stage": "ç»´ä¿®", "action": "现场/返厂/远程维修,费用与结果记录,完成联动工单完结", "roles": ["维修工程师"]},
       {"stage": "退货", "action": "售后不再发起退货申请,由仓储物流发起销售退货单(RQC)→质检/入库回调→退款回调→关闭", "roles": ["仓储", "质检", "财务"]}]},
    {"id": "finance", "name": "会计核算流程", "mermaid": "mermaid/12-finance-flow.mmd", "color": "#3ec9a7",
     "summary": "独立核算模块 yudao-module-finance:会计科目建账 â†’ è®°è´¦å‡­è¯å½•å…¥(借贷平衡校验) â†’ è¿‡è´¦ â†’ æ€»è´¦/明细账 â†’ èµ„产计提折旧与摊销 â†’ è´¢åŠ¡æŠ¥è¡¨çœ‹æ¿å–æ•° ERP å·²å®¡æ ¸å•据。",
     "steps": [
       {"stage": "建账", "action": "维护会计科目 tree(subjectCode å”¯ä¸€ï¼Œå¯å»ºä¸‹çº§),首次可导入约 68 æ¡ä¸€çº§ç§‘目种子", "roles": ["会计"]},
       {"stage": "è®°è´¦", "action": "录入凭证(字号留空自动生成)+分录,分录逐行选科目并填借/贷;校验至少一行、借贷都不得同时大于0、合计相等且大于0", "roles": ["会计"]},
       {"stage": "过账", "action": "未过账0 â†’ post è¿‡è´¦ â†’ å·²è¿‡è´¦10(不可再修改);未过账0 æˆ– å·²è¿‡è´¦10 å‡å¯ cancel ä½œåºŸ â†’ å·²ä½œåºŸ20", "roles": ["会计", "财务主管"]},
       {"stage": "查账", "action": "总账按期间逐月汇总、明细账逐条分录并结转余额,两者只统计已过账10的凭证,并支持导出", "roles": ["会计", "财务主管"]},
       {"stage": "资产", "action": "固定资产(1在用/2闲置/3维修中/4已报废)与无形资产(1在用/2已失效/3已摊销完)计提:传 ids æŒ‰é€‰ä¸­è®¡æï¼Œids ä¸ºç©ºå¯¹å…¨éƒ¨æœªæŠ¥åºŸ/未失效资产计提一个月", "roles": ["资产会计"]},
       {"stage": "报表", "action": "看板汇总资产与凭证时点数 + ç» ErpFinanceStatisticsApi å– ERP å·²å®¡æ ¸æ”¶è´§ä»˜æ¬¾ä¸Žè´­é”€é‡‘额,输出月度收支趋势", "roles": ["财务主管", "管理层"]}]},
    {"id": "ai", "name": "AI ä¸šåŠ¡æµç¨‹", "mermaid": "mermaid/11-ai-business-flow.mmd", "color": "#56c8d8",
     "summary": "统一模式:业务侧收集上下文(必要时调 MDM/SRM/CRM API å–名称)→组装 Prompt→chat/chatWithImage→解析 JSON→前端预填或建议;无 Function Calling;RAG çŸ¥è¯†åº“已建成但暂无业务消费。",
     "steps": [
       {"stage": "录入提效", "action": "发票 OCR(图片多模态/PDF文本)、报价单 OCR â†’ ç»“构化预填", "roles": ["采购员", "销售"]},
       {"stage": "辅助判定", "action": "质检单+指标+缺陷 â†’ å»ºè®®åˆæ ¼/特采/退货/报废+理由", "roles": ["质检员"]},
       {"stage": "决策预测", "action": "缺料/生产时长/生产风险/交付四类预测供计划排产参考", "roles": ["计划员"]},
       {"stage": "模板识别", "action": "上传既有检验报告文件(扫描件/照片、电子版 PDF、Word/Excel) â†’ åˆ†é€šé“抽取(有文本层走文本、扫描版逐页转图) â†’ å¤§æ¨¡åž‹æŒ‰å‰ç«¯ä¸Šä¼ çš„组件清单产出扁平组件草稿 â†’ äººå·¥åœ¨è®¾è®¡å™¨ç¡®è®¤åŽä¿å­˜ä¸ºæ¨¡æ¿ç‰ˆæœ¬", "roles": ["质检员", "质量工程师"]},
       {"stage": "知识检索", "action": "文档→切片→Milvus å‘量→检索接口(预留)", "roles": ["—"]}]}
    {
      "id": "sales",
      "name": "销售流程",
      "mermaid": "mermaid/02-sales-flow.mmd",
      "color": "#4f9cff",
      "summary": "线索转化客户 â†’ å•†æœºæŽ¨è¿› â†’ æŠ¥ä»·(可AI识别) â†’ åˆåŒ BPM å®¡æ‰¹ â†’ ç”Ÿæˆ ERP é”€å”®è®¢å•(合同转来的订单提交免审批直接生效,提交后不可修改;台账含销售类型、手工合同号、产品编号与生产数量) â†’ éœ€ç”Ÿäº§è¡Œè‡ªåŠ¨å»º MPS â†’ å‘货通知(物流公司/快递费/开票状态,审批默认物流) â†’ å‘货台账按产品平铺 â†’ é”€å”®å‡ºåº“提交(需检验行转待检测) â†’ ç‹¬ç«‹å½•å…¥ OQC åŽæŒ‰ç‰©æ–™+盘号+数量关联回写 â†’ æ‹£è´§å‘运 â†’ å¼€ç¥¨(BPM) â†’ å½•入发票与款项节点 â†’ å›žæ¬¾(先开票校验,创建为未提交,显式提交 BPM ååŒå®¡æ‰¹ï¼Œå®¡æ‰¹é€šè¿‡æ‰å›žå†™å›žæ¬¾è®¡åˆ’);发货单/成品出库单可导出带电子章的 Word ä¾›æ‰‹å¡«ç­¾å­—归档;客户档案支持多收货地址。",
      "steps": [
        {
          "stage": "获客",
          "action": "线索转化客户、建联系人、立项商机",
          "roles": [
            "销售内勤"
          ]
        },
        {
          "stage": "报价",
          "action": "商机赢单后建报价单(AI OCR é¢„å¡«),转合同",
          "roles": [
            "销售内勤"
          ]
        },
        {
          "stage": "投标",
          "action": "按客户(招标方)登记投标保证金:项目/招标编号/金额/缴纳日期,可关联该客户已审核的销售订单;开标后回填中标结果,未中标跟踪退回状态、金额与日期",
          "roles": [
            "销售内勤"
          ]
        },
        {
          "stage": "签约",
          "action": "合同来源:手工新建 / æŠ¥ä»·å•转入 / Excel å¯¼å…¥(一合同多产品明细行);提交 BPM å®¡æ‰¹(可选流程定义),通过=20",
          "roles": [
            "销售",
            "审批人"
          ]
        },
        {
          "stage": "接单",
          "action": "合同一键生成销售订单(XSDD)后在销售管理提交:合同转来的订单免 BPM ç›´æŽ¥ç”Ÿæ•ˆ(20),提交后不可修改;手工/导入订单仍走 BPM。生效后自动为 needProduction æ˜Žç»†åˆ›å»º MPS æµè½¬ç”Ÿäº§ï¼Œæ— éœ€ç”Ÿäº§çš„订单可直接生成仓储发货通知单",
          "roles": [
            "销售",
            "计划员"
          ]
        },
        {
          "stage": "交付",
          "action": "发货通知审核(占用)→ é”€å”®å‡ºåº“提交:有 oqcCheckFlag è¡Œè½¬å¾…检测,提交时不再自动建 OQC;质检员独立录入并完成 OQC åŽï¼Œåœ¨æ£€éªŒå•详情按物料+盘号+数量一致选择待检出库行关联,全部待检行合格后出库单转待拣货 â†’ å‘运 â†’ å‡ºåº“回写部分/全部出库",
          "roles": [
            "仓管",
            "质检"
          ]
        },
        {
          "stage": "回款",
          "action": "开票 BPM(累计≤合同额) â†’ å›žæ¬¾å¼ºåˆ¶å…ˆå¼€ç¥¨(累计≤合同剩余与开票剩余);回款创建为未提交,提交时重新校验并创建 BPM å®žä¾‹ï¼Œå®¡æ‰¹é€šè¿‡æ‰å›žå†™å›žæ¬¾è®¡åˆ’,审批中的回款禁止页面内手工审核/反审核",
          "roles": [
            "财务",
            "销售"
          ]
        }
      ]
    },
    {
      "id": "purchase",
      "name": "采购流程",
      "mermaid": "mermaid/03-purchase-flow.mmd",
      "color": "#5fc79e",
      "summary": "供应商档案(多联系人/多银行·含银行行号) â†’ è¯¢ä»·æ¯”ä»·(产品×供应商横向报价,按产品选中标) â†’ ç¡®è®¤åŽæŒ‰ä¸­æ ‡ä¾›åº”商拆采购订单草稿并走现有 BPM â†’ ç¡®è®¤æ”¶è´§ç”Ÿæˆ MES åˆ°è´§é€šçŸ¥ â†’ IQC(合格/特采入库·退货/报废) â†’ é‡‡è´­å…¥åº“回写订单与申请 â†’ æ¥ç¥¨(AI OCR) â†’ ä»˜æ¬¾å• BPM â†’ ä¾›åº”商往来对账。",
      "steps": [
        {
          "stage": "寻源",
          "action": "供应商档案维护多个联系人与多个银行账户(含银行行号);新增询价单时一并录入产品×参与供应商报价并选定每个产品的中标供应商,列表直接确认后按中标供应商生成采购订单草稿",
          "roles": [
            "采购员",
            "供应商管理员"
          ]
        },
        {
          "stage": "申请",
          "action": "采购申请(CGSQ)提交 BPM,驳回可重置回草稿;通过且有供应商自动生成订单",
          "roles": [
            "采购员",
            "审批人"
          ]
        },
        {
          "stage": "下单",
          "action": "采购订单(CGDD)自动提交 BPM(15→20)",
          "roles": [
            "采购员"
          ]
        },
        {
          "stage": "收货",
          "action": "确认收货生成到货通知(0→待质检2/待入库3→4);需检物料自动创建 IQC",
          "roles": [
            "仓管",
            "IQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "入库",
          "action": "IQC åˆæ ¼/特采→采购入库(待上架→待执行→完成)库存+,回写订单 inCount/inStatus ä¸Žç”³è¯·æ˜Žç»†",
          "roles": [
            "仓管"
          ]
        },
        {
          "stage": "退货",
          "action": "IQC ä¸åˆæ ¼é€€è´§â†’供应商退货出库(选料锁定到订单行);报废走 NCR",
          "roles": [
            "仓管"
          ]
        },
        {
          "stage": "付款",
          "action": "采购来票(FPD, AI OCR, â‰¤è®¢å•总价) â†’ ä»˜æ¬¾å•(FKD å…³è”来票+附件, â‰¤æ¥ç¥¨é‡‘额) BPM å®¡æ‰¹",
          "roles": [
            "财务",
            "审批人"
          ]
        },
        {
          "stage": "对账",
          "action": "供应商往来按供应商汇总:左侧采购订单流水含已开票/未开票金额,右侧来票流水含开票金额/已付/剩余应付并附付款流水,顶部汇总累计采购/开票/付款/未付余额",
          "roles": [
            "财务",
            "采购员"
          ]
        }
      ]
    },
    {
      "id": "production",
      "name": "生产流程",
      "mermaid": "mermaid/04-production-flow.mmd",
      "color": "#e0b34f",
      "summary": "销售订单驱动 MPS(仅创建人或超管可下发) â†’ ä¸‹å‘工单 â†’ æŒ‰å·¥è‰ºè·¯çº¿æ‹†å·¥åºä»»åŠ¡ â†’ é¢†æ–™/线边备料 â†’ æŠ¥å·¥(工序顺序硬校验+BPM,按工序工资字段+工艺路线绑定+主计价设备解析单价快照报工工资) â†’ BOM æ¶ˆè€—/半成品自动入库/成品待检 â†’ æœ«é“工序回写进度 â†’ è‡ªåŠ¨å®Œå·¥ â†’ ProductionFinishedEvent å›žå†™é”€å”®è®¢å•;报工工资在薪酬核算时按报工人汇总,可人工调整后计入实发;报工提交不再自动生成 IPQC,过程检验由质量部门手工建单,成品产出行必须完成 IPQC æ‰å¯å…¥åº“,半成品产出直接 PASS å¹¶æŒ‰ç”Ÿäº§ BOM ç›®æ ‡åº“位自动入库、不依赖倒冲开关、不走审批,合格率按质量规则调整工资(系数或不合格扣款)。报工同时按工序标准工时配置+生产长度+辅助项次数快照标准工时,实际工时与标准工时的差异进入工时考核报表(按人/工序/机台/周期),标准工时只用于考核对比、不参与工资计算。",
      "steps": [
        {
          "stage": "计划",
          "action": "MPS åˆ›å»º(手工或销售订单自动)→下发或合并下发为工单",
          "roles": [
            "计划员"
          ]
        },
        {
          "stage": "派工",
          "action": "工单确认按工艺路线逐工序生成任务,可指派/暂停/取消",
          "roles": [
            "车间主任"
          ]
        },
        {
          "stage": "备料",
          "action": "生产领料单出库或线边虚拟库备料",
          "roles": [
            "仓管"
          ]
        },
        {
          "stage": "报工",
          "action": "提交报工:上一道工序未报满直接拦截;启用 BPM èµ°å®¡æ‰¹ï¼›æŒ‰å·¥åºè®¡è´¹ç±»åž‹å¿«ç…§å·¥èµ„(计时=工时×定额,计件=数量×定额),记到报工人名下;同时录入生产长度与辅助项次数,按工序标准工时配置计算并快照标准工时(主工时+辅助工时)与计算依据,配置缺失时不阻断,转人工填工时并记录缺失原因",
          "roles": [
            "操作工"
          ]
        },
        {
          "stage": "工时考核",
          "action": "实际工时 vs æ ‡å‡†å·¥æ—¶å®žæ—¶æ¯”对,差异率超阈值(默认20%)提示;报表按人/工序/机台/周期汇总报工单数、生产长度、实际工时、标准工时、差异与差异率,供车间主任与工艺员核对",
          "roles": [
            "车间主任",
            "工艺员"
          ]
        },
        {
          "stage": "联动",
          "action": "审批通过: å·¥å•存在生产BOM即自动物料消耗扣线边库(允许负库存,不再依赖倒冲/质检开关);成品产出转待检验并生成 PENDING äº§å‡ºè¡Œï¼Œç­‰å¾…质量部门手工建 IPQC å¹¶å›žå†™ï¼›åŠæˆå“äº§å‡º(报工物料≠工单成品)直接 PASS å¹¶æŒ‰ BOM ç›®æ ‡åº“位立即入库,不等质检",
          "roles": [
            "系统自动"
          ]
        },
        {
          "stage": "检验",
          "action": "已完成 IPQC åœ¨è¯¦æƒ…页按物料+盘号+数量一致选择待检产出行关联:回写报工完成并拆分合格/不合格产出入库;未关联的已完成检验单不回写",
          "roles": [
            "IPQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "完工",
          "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS ç´¯è®¡åˆ¤æ–­å®Œæˆå¹¶å‘事件给销售订单",
          "roles": [
            "系统自动"
          ]
        }
      ]
    },
    {
      "id": "quality",
      "name": "质量流程",
      "mermaid": "mermaid/05-quality-flow.mmd",
      "color": "#e06c6c",
      "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + å®Œæˆæ—¶æŒ‰åˆæ ¼/不合格数量自动判定(1 æ ¡éªŒé€šè¿‡/2 æ ¡éªŒä¸é€šè¿‡) + NCR æäº¤ BPM å®¡æ‰¹åŽç”±è´¨é‡éƒ¨å¤„置再关闭;IPQC ç”±è´¨é‡éƒ¨é—¨æ‰‹å·¥æ–°å»ºå¹¶ç»‘定工单/工序/半成品/盘号,新建/编辑选工位即按工序带出该工序维护的自定义工资字段,检验信息可按行增删且每行只上传检验附件(不填检测值,附件按检验行明细 ID ç‹¬ç«‹ç»‘定);IPQC å®ŒæˆåŽæŒ‰ç‰©æ–™+盘号+数量关联报工待检行;报工提交不再自动生成 IPQC,成品产出行 PENDING ä¸”必须完成 IPQC æ‰èƒ½å…¥åº“,合格率参与报工工资质量调整,OQC å®ŒæˆåŽæŒ‰åŒæ ·è§„则关联销售出库待检行;质量统计提供整体合格率/不合格率并按工序、按生产工单汇总;AI æä¾›åˆ¤å®šå»ºè®®ï¼›NCR å®¡æ‰¹æ„è§ç”± BPM æµè½¬è®°å½•承载,处置方式限定让步放行/返修/返工/报废;检验单可由智能质检报告平台一键出件(generate-from-qc:数据从 MES å–,五道校验后渲染落库)。 è´¨é‡å·¡æ£€ä¸ºç‹¬ç«‹è®°å½• mes_qc_patrol,按 XJ_+日期+流水编码,不生成库存业务单据。",
      "steps": [
        {
          "stage": "来料",
          "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货",
          "roles": [
            "IQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "过程",
          "action": "报工产出行(成品 PENDING、半成品直接 PASS è‡ªåŠ¨å…¥åº“)→质量部门手工新建 IPQC å¹¶ç»‘定工单/工序/半成品/盘号→逐行上传检验附件;完成后按物料+盘号+数量一致关联报工待检行,合格拆行入库、不合格生成 NCR",
          "roles": [
            "IPQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "出货",
          "action": "销售出库提交后行转待检测→质检员独立录入 OQC;完成后按物料+盘号+数量一致关联出库待检行,全部合格后出库单进入待拣货",
          "roles": [
            "OQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "退货",
          "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后",
          "roles": [
            "RQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "导入",
          "action": "IQC/IPQC/OQC ä¸‰åˆ—表各支持 Excel æ‰¹é‡å¯¼å…¥(按钮在「新增」与「导出」之间,纯新增不做更新覆盖):一行一张单,编号留空自动生成,质检方案留空按物料+检验类型自动匹配,检测人员留空取当前登录用户;物料/供应商/客户/工单/工位按编码或名称精确匹配(IPQC ç‰©æ–™ç”±å·¥å•推导,模板不填),合格+不合格=检测/接收数量、IPQC å·¥åºŸ+料废+其他废品≤不合格、OQC å®¢æˆ·é¡»åœ¨æœ¬äººæ•°æ®æƒé™å†…;逐行导入部分成功,失败行进 failureList å¸¦è¡Œå·ä¸Žå¯æ‰§è¡ŒåŠ¨ä½œï¼ŒæˆåŠŸå•ä¸€å¾‹å¾…æ£€éªŒ0;同一批数据不可重复导入:文件内重复行与库内已存在同业务键单据(IQC ç‰©æ–™+供应商+供应商批次+来料日期、IPQC ç”Ÿäº§å·¥å•+检验类型+工位+检测日期、OQC ç‰©æ–™+客户+批次号+出货日期)均拒该行,提示含维度、双方实际值与「先删除该单据再重试」动作",
          "roles": [
            "质检员",
            "IQC è´¨æ£€å‘˜",
            "IPQC è´¨æ£€å‘˜",
            "OQC è´¨æ£€å‘˜"
          ]
        },
        {
          "stage": "不合格",
          "action": "NCR: å¾…评审(0)→提交BPM审批(2)→审批通过已评审(6)/驳回回待评审→质量部处置(让步放行/返修/返工/报废)→已处置(7)→关闭(8);审批意见由 BPM æµè½¬è®°å½•承载,未发布流程定义或全局免审批时提交后自动通过",
          "roles": [
            "质量工程师"
          ]
        },
        {
          "stage": "出件",
          "action": "两条出件入口:①通用 generate/preview(调用方自带上下文) æ¸²æŸ“ HTML å¹¶å†»ç»“数据快照,编号 QR+日期+4位流水按天独立,preview åªæ¸²æŸ“不落库不消耗编号,regenerate ç”¨å†»ç»“快照重渲且数据与编号不变并作废已归档 PDF,导出 PDF ç”±åŽç«¯è°ƒ Chromium æ‰“印实例已存的 HTML åŽå½’档附件中心;②generate-from-qc ç”±è´¨æ£€å•直接出件(数据经 MesQcReportApi ä»Ž MES å–,前端不拼上下文),出件前五道校验按业务优先级只报第一处:类型非法/单据不存在/未完成检验/未填判定/模板类型不符/无可用检验项,报告头另带单据原判定 qcResult+qcResultText ä¸Žå¼•擎判定并存,响应回 undecidableCount å¾…判定项数与 warnings(分组标题跳过/指标已删/未录实测值/样品超3),同一张单据可反复出多份",
          "roles": [
            "质检员"
          ]
        },
        {
          "stage": "建模板",
          "action": "AI å¯¼å…¥ï¼šä¸Šä¼ æ—¢æœ‰æ£€éªŒæŠ¥å‘Šæ–‡ä»¶(扫描件/照片、电子版 PDF、Word/Excel) â†’ è¯†åˆ«æˆæ¨¡æ¿è‰ç¨¿(不落库) â†’ äººå·¥åœ¨è®¾è®¡å™¨ç¡®è®¤è°ƒæ•´åŽä¿å­˜ä¸ºç‰ˆæœ¬ï¼›.doc/.xls æ—§æ ¼å¼ä¸æ”¯æŒ",
          "roles": [
            "质检员",
            "质量工程师"
          ]
        }
      ]
    },
    {
      "id": "warehouse",
      "name": "仓储流程",
      "mermaid": "mermaid/06-warehouse-flow.mmd",
      "color": "#7e6bd6",
      "summary": "15 ç±»å•据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN è¿½æº¯ã€åº“存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi ä¾› ERP æ”¶å‘。 åº“存冻结按来源(人工/盘点/调拨)独立登记于 mes_wm_material_stock_freeze_claim;库存事务冗余 work_order_id,出库台账可直接展示生产订单号。",
      "steps": [
        {
          "stage": "入库",
          "action": "采购入库/生产入库/产出入库/退货入库/外协入库/杂项入库 â†’ IN äº‹åŠ¡ï¼Œæ‰¹æ¬¡æ··æ”¾æ ¡éªŒã€å››çº§å†»ç»“æ ¡éªŒ",
          "roles": [
            "仓管"
          ]
        },
        {
          "stage": "出库",
          "action": "销售出库/领料/消耗/外协发料/供应商退货/杂项出库 â†’ OUT äº‹åŠ¡ï¼ŒcheckFlag æŽ§åˆ¶å¯å¦è´Ÿåº“å­˜",
          "roles": [
            "仓管",
            "系统自动"
          ]
        },
        {
          "stage": "移库",
          "action": "内部/外部调拨成对 MOVE äº‹åŠ¡ï¼Œåœ¨é€”æ•°é‡è·Ÿè¸ª",
          "roles": [
            "仓管"
          ]
        },
        {
          "stage": "盘点",
          "action": "方案→任务→明细行→结果,盘盈 ADJUST_IN、盘亏 ADJUST_OUT,可冻结与盲盘",
          "roles": [
            "盘点员"
          ]
        },
        {
          "stage": "追溯",
          "action": "批次/SN(uuid)/条码(18ç±»)/装箱嵌套支撑正反向追溯",
          "roles": [
            "质量",
            "售后"
          ]
        }
      ]
    },
    {
      "id": "aftersales",
      "name": "售后流程",
      "mermaid": "mermaid/07-after-sales-flow.mmd",
      "color": "#c78bd6",
      "summary": "工单(六态+问题判定) â†’ å®¢æˆ·è¯‰æ±‚/处理结果附件与流转记录留痕 â†’ ç»´ä¿®ä¸‰ç±»åž‹æˆ–由仓储物流发起销售退货(退货单创建即回写工单流转记录) â†’ RQC è´¨æ£€/入库/退款三回调逐级推进 â†’ å·¥å•完结关闭。",
      "steps": [
        {
          "stage": "受理",
          "action": "人工录入工单(关联客户/订单/合同),填写客户诉求反馈描述并上传附件,创建时指定的处理人直接进入处理中",
          "roles": [
            "客服"
          ]
        },
        {
          "stage": "判定",
          "action": "问题判定: ä¸€èˆ¬/ç»´ä¿®/退货,严重度 0-3",
          "roles": [
            "售后工程师"
          ]
        },
        {
          "stage": "处理",
          "action": "填写处理结果并上传处理结果附件;创建、更新、判定、完结、归档、取消、回访均写入流转记录",
          "roles": [
            "售后工程师"
          ]
        },
        {
          "stage": "ç»´ä¿®",
          "action": "现场/返厂/远程维修,费用与结果记录,完成联动工单完结",
          "roles": [
            "维修工程师"
          ]
        },
        {
          "stage": "退货",
          "action": "售后不再发起退货申请,由仓储物流发起销售退货单(RQC)→质检/入库回调→退款回调→关闭;创建退货单时按销售订单匹配最近一张未关闭/未取消工单,写入「仓储退货」流转记录,草稿或处理中工单推进为待退货",
          "roles": [
            "仓储",
            "质检",
            "财务"
          ]
        }
      ]
    },
    {
      "id": "finance",
      "name": "会计核算流程",
      "mermaid": "mermaid/12-finance-flow.mmd",
      "color": "#3ec9a7",
      "summary": "会计核算:凭证/分录/借贷平衡 -> è¿‡è´¦ -> æ€»è´¦/明细账/报表;固定资产折旧、无形资产摊销。销售采购财务闭环:ERP é”€å”®è®¢å• + MES å·²å®Œæˆå‡ºåº“单 -> CRM å¼€ç¥¨ç”³è¯·ï¼ˆé”€å”®è®¢å•/出库单选填,累计≤合同额)-> å®¡æ‰¹é€šè¿‡ -> å½•入发票并填写合同款/预付款/到货款/验收款/质保金收款节点 -> CRM å›žæ¬¾æŒ‰æ ¸é”€æ˜Žç»†åˆ†æ‘Šåˆ°è®¢å•/出库/开票 -> å®¢æˆ·åº”收对账、已发货未开票、未核销应收账龄、下月回款计划;来票必须同时绑定采购订单与 MES é‡‡è´­å…¥åº“单;凭证可关联来源单据(开票/回款/来票/付款)。",
      "steps": [
        {
          "stage": "建账",
          "action": "维护会计科目 tree(subjectCode å”¯ä¸€ï¼Œå¯å»ºä¸‹çº§),首次可导入约 68 æ¡ä¸€çº§ç§‘目种子",
          "roles": [
            "会计"
          ]
        },
        {
          "stage": "è®°è´¦",
          "action": "录入凭证(字号留空自动生成)+分录,分录逐行选科目并填借/贷;校验至少一行、借贷都不得同时大于0、合计相等且大于0",
          "roles": [
            "会计"
          ]
        },
        {
          "stage": "过账",
          "action": "未过账0 â†’ post è¿‡è´¦ â†’ å·²è¿‡è´¦10(不可再修改);未过账0 æˆ– å·²è¿‡è´¦10 å‡å¯ cancel ä½œåºŸ â†’ å·²ä½œåºŸ20",
          "roles": [
            "会计",
            "财务主管"
          ]
        },
        {
          "stage": "查账",
          "action": "总账按期间逐月汇总、明细账逐条分录并结转余额,两者只统计已过账10的凭证,并支持导出",
          "roles": [
            "会计",
            "财务主管"
          ]
        },
        {
          "stage": "资产",
          "action": "固定资产(1在用/2闲置/3维修中/4已报废)与无形资产(1在用/2已失效/3已摊销完)计提:传 ids æŒ‰é€‰ä¸­è®¡æï¼Œids ä¸ºç©ºå¯¹å…¨éƒ¨æœªæŠ¥åºŸ/未失效资产计提一个月",
          "roles": [
            "资产会计"
          ]
        },
        {
          "stage": "报表",
          "action": "看板汇总资产与凭证时点数 + ç» ErpFinanceStatisticsApi å– ERP å·²å®¡æ ¸æ”¶è´§ä»˜æ¬¾ä¸Žè´­é”€é‡‘额,输出月度收支趋势",
          "roles": [
            "财务主管",
            "管理层"
          ]
        },
        {
          "stage": "来票",
          "action": "选择采购订单 -> é€‰æ‹©è¯¥è®¢å•下已完成 MES é‡‡è´­å…¥åº“单 -> ä¿å­˜æ¥ç¥¨ï¼ˆitem_receipt_id/item_receipt_code)",
          "roles": [
            "采购",
            "财务"
          ]
        },
        {
          "stage": "开票申请",
          "action": "选择合同(销售订单与该订单下已完成出库单均为选填;选择合同时若该合同仅有一张已审核销售订单则自动带出),填写开票金额后提交 BPM å®¡æ‰¹",
          "roles": [
            "销售",
            "财务"
          ]
        },
        {
          "stage": "录入发票",
          "action": "审批通过后录入发票号码、日期、附件,并按销售订单填写合同款/预付款/到货款/验收款/质保金及各自时间节点",
          "roles": [
            "财务"
          ]
        },
        {
          "stage": "回款核销",
          "action": "回款单通过 items æŠŠé‡‘额核销到销售订单/销售出库/开票,明细合计必须等于回款金额;审核通过回写回款计划实际金额与状态",
          "roles": [
            "财务"
          ]
        },
        {
          "stage": "对账报表",
          "action": "按客户汇总订单、出库、开票、回款,输出未核销应收、已发货未开票、账龄、下月回款计划与月度趋势",
          "roles": [
            "财务",
            "销售"
          ]
        },
        {
          "stage": "入账",
          "action": "记账凭证填写 sourceType/sourceId/sourceNo å…³è”开票、回款、来票、付款等来源单据",
          "roles": [
            "财务"
          ]
        }
      ]
    },
    {
      "id": "ai",
      "name": "AI ä¸šåŠ¡æµç¨‹",
      "mermaid": "mermaid/11-ai-business-flow.mmd",
      "color": "#56c8d8",
      "summary": "统一模式:业务侧收集上下文(必要时调 MDM/SRM/CRM API å–名称)→组装 Prompt→chat/chatWithImage→解析 JSON→前端预填或建议;无 Function Calling;RAG çŸ¥è¯†åº“已建成但暂无业务消费。智能体物料导入为规则+Java é¢„检链路,经人工确认后幂等提交。",
      "steps": [
        {
          "stage": "录入提效",
          "action": "发票 OCR(图片多模态/PDF文本)、报价单 OCR â†’ ç»“构化预填",
          "roles": [
            "采购员",
            "销售"
          ]
        },
        {
          "stage": "主数据导入",
          "action": "上传物料 CSV/XLSX â†’ Python è§£æžå€™é€‰ â†’ Java åªè¯»é¢„检分类/单位/重复编码 â†’ äººå·¥ä¿®è®¢ç¡®è®¤ â†’ å¹‚等提交 â†’ é€è¡Œå›žæ‰§ï¼›åªæ–°å»ºã€ä¸æ›´æ–°ã€ä¸åŒæ­¥ MES",
          "roles": [
            "物料管理员"
          ]
        },
        {
          "stage": "辅助判定",
          "action": "质检单+指标+缺陷 â†’ å»ºè®®åˆæ ¼/特采/退货/报废+理由",
          "roles": [
            "质检员"
          ]
        },
        {
          "stage": "决策预测",
          "action": "缺料/生产时长/生产风险/交付四类预测供计划排产参考",
          "roles": [
            "计划员"
          ]
        },
        {
          "stage": "模板识别",
          "action": "上传既有检验报告文件(扫描件/照片、电子版 PDF、Word/Excel) â†’ åˆ†é€šé“抽取(有文本层走文本、扫描版逐页转图) â†’ å¤§æ¨¡åž‹æŒ‰å‰ç«¯ä¸Šä¼ çš„组件清单产出扁平组件草稿 â†’ äººå·¥åœ¨è®¾è®¡å™¨ç¡®è®¤åŽä¿å­˜ä¸ºæ¨¡æ¿ç‰ˆæœ¬",
          "roles": [
            "质检员",
            "质量工程师"
          ]
        },
        {
          "stage": "知识检索",
          "action": "文档→切片→Milvus å‘量→检索接口(预留)",
          "roles": [
            "—"
          ]
        }
      ]
    }
  ]
}</script>
<script type="application/json" id="data-roles">{
  "note": "实际 system_role ä»… 4 ä¸ªæœ‰æ•ˆè§’色(超级管理员/普通角色/CRM管理员/租户管理员)+1 æµ‹è¯•角色;业务分工主要通过『菜单权限 + æ•°æ®æƒé™ï¼ˆè´Ÿè´£äººåˆ¶ï¼‰ã€å®žçŽ°ï¼Œè€Œéžç»†åˆ†è§’è‰²ã€‚ä»¥ä¸‹ä¸šåŠ¡è§’è‰²ç”±å„æ¨¡å—åŠŸèƒ½ä¸Žæ•°æ®æƒé™æ¨¡åž‹æŽ¨å¯¼ã€‚",
  "systemRoles": [
    {"name": "超级管理员", "code": "super_admin", "desc": "代码直返全部菜单"},
    {"name": "普通角色", "code": "common", "desc": "依赖 system_role_menu é…æƒ"},
    {"name": "CRM ç®¡ç†å‘˜", "code": "crm_admin", "desc": "CRM æŸ¥çœ‹å…¨éƒ¨æ•°æ®"},
    {"name": "租户管理员", "code": "tenant_admin", "desc": "多租户已关闭,遗留"}
  ],
  "businessRoles": [
    {"role": "销售内勤/销售", "modules": ["CRM", "ERP"], "operations": ["线索客户商机维护", "报价与合同", "销售订单", "跟进记录", "投标保证金登记与退款跟踪"], "data": ["crm_*", "erp_sale_order"], "scope": "负责人(owner)+数据权限三级:只读/读写/负责人;公海池可领取;交接人应可通过 expandUserIds çœ‹åˆ°ç¦»èŒäººæ•°æ®(接口已备、待接入)"},
    {"role": "销售经理", "modules": ["CRM"], "operations": ["商机/合同审批参与", "销售目标制定", "业绩排名漏斗分析"], "data": ["团队数据(下属用户)"], "scope": "AdminUserApi ä¸‹çº§ç”¨æˆ·æ‰©å±•"},
    {"role": "采购员", "modules": ["ERP", "SRM"], "operations": ["采购申请/订单", "确认收货", "来票与付款发起"], "data": ["erp_purchase_*", "srm_*"], "scope": "requestUserId/supplierOwner å½’属"},
    {"role": "采购员/供应商管理员", "modules": ["SRM"], "operations": ["供应商档案维护", "询价比价与确认转订单"], "data": ["srm_supplier*", "srm_inquiry*"], "scope": "采购管理菜单权限;询价确认生成的采购订单草稿走 ERP BPM"},
    {"role": "计划员", "modules": ["MES"], "operations": ["MPS ä¸Žå·¥å•", "工艺路线维护", "AI é¢„测参考"], "data": ["mes_pro_*"], "scope": "车间/部门数据范围"},
    {"role": "操作工/班组长", "modules": ["MES"], "operations": ["任务接收", "报工提交(受工序顺序约束)", "安灯"], "data": ["mes_pro_feedback", "mes_pro_task"], "scope": "任务指派人可见"},
    {"role": "仓管员", "modules": ["MES-WM"], "operations": ["15 ç±»å‡ºå…¥åº“单据执行", "调拨", "盘点", "条码装箱"], "data": ["mes_wm_*"], "scope": "仓库维度;WMS å¯æŒ‰ä»“库配置出入库/调拨/盘点是否走 BPM"},
    {"role": "质检员", "modules": ["MES-QC"], "operations": ["IQC/IPQC/OQC/RQC æ£€éªŒ", "NCR è¯„审处置"], "data": ["mes_qc_*"], "scope": "inspectorUserId å½’属"},
    {"role": "设备员/维修工", "modules": ["MES-DV"], "operations": ["点检保养", "维修工单", "工装领用"], "data": ["mes_dv_*", "mes_tm_*"], "scope": "chargeUserId å½’属"},
    {"role": "客服/售后工程师", "modules": ["售后"], "operations": ["工单受理派工", "客户诉求/处理结果附件", "ç»´ä¿®", "退货转仓储发起"], "data": ["after_sale_*"], "scope": "行级数据权限四视图:我负责/我参与/下属负责/全部(aftersales:view-all)"},
    {"role": "财务", "modules": ["ERP", "CRM", "FIN"], "operations": ["收款单", "付款单", "开票审核", "结算账户", "会计科目维护", "记账凭证录入与过账/作废", "总账与明细账查询", "固定资产折旧计提", "无形资产摊销计提", "会计核算看板"], "data": ["erp_finance_*", "crm_invoice", "crm_receivable", "fin_account_subject", "fin_voucher", "fin_voucher_entry", "fin_fixed_asset", "fin_intangible_asset"], "scope": "financeUserId å½’属;财务核算模块按 finance:* æŽ¥å£æƒé™ + system_menu æŽˆæƒï¼Œæš‚无数据权限字段"},
    {"role": "HR", "modules": ["HRM"], "operations": ["员工档案入转调离", "考勤规则排班", "薪酬核算发放", "离职交接"], "data": ["hrm_*"], "scope": "部门范围 dataScope"},
    {"role": "管理层", "modules": ["BI"], "operations": ["查看采购销售/仓库/生产设备/质量/人力五大主题大屏"], "data": ["bi_dashboard"], "scope": "只读"}
  ]
 "note": "实际 system_role ä»… 4 ä¸ªæœ‰æ•ˆè§’色(超级管理员/普通角色/CRM管理员/租户管理员)+1 æµ‹è¯•角色;业务分工主要通过『菜单权限 + æ•°æ®æƒé™ï¼ˆè´Ÿè´£äººåˆ¶ï¼‰ã€å®žçŽ°ï¼Œè€Œéžç»†åˆ†è§’è‰²ã€‚ä»¥ä¸‹ä¸šåŠ¡è§’è‰²ç”±å„æ¨¡å—åŠŸèƒ½ä¸Žæ•°æ®æƒé™æ¨¡åž‹æŽ¨å¯¼ã€‚",
 "systemRoles": [
  {
   "name": "超级管理员",
   "code": "super_admin",
   "desc": "代码直返全部菜单"
  },
  {
   "name": "普通角色",
   "code": "common",
   "desc": "依赖 system_role_menu é…æƒ"
  },
  {
   "name": "CRM ç®¡ç†å‘˜",
   "code": "crm_admin",
   "desc": "CRM æŸ¥çœ‹å…¨éƒ¨æ•°æ®"
  },
  {
   "name": "租户管理员",
   "code": "tenant_admin",
   "desc": "多租户已关闭,遗留"
  }
 ],
 "businessRoles": [
  {
   "role": "销售内勤/销售",
   "modules": [
    "CRM",
    "ERP"
   ],
   "operations": [
    "线索客户商机维护",
    "报价与合同",
    "销售订单",
    "跟进记录",
    "投标保证金登记与退款跟踪"
   ],
   "data": [
    "crm_*",
    "erp_sale_order"
   ],
   "scope": "负责人(owner)+数据权限三级:只读/读写/负责人;公海池可领取;交接人应可通过 expandUserIds çœ‹åˆ°ç¦»èŒäººæ•°æ®(接口已备、待接入)"
  },
  {
   "role": "销售经理",
   "modules": [
    "CRM"
   ],
   "operations": [
    "商机/合同审批参与",
    "销售目标制定",
    "业绩排名漏斗分析"
   ],
   "data": [
    "团队数据(下属用户)"
   ],
   "scope": "AdminUserApi ä¸‹çº§ç”¨æˆ·æ‰©å±•"
  },
  {
   "role": "采购员",
   "modules": [
    "ERP",
    "SRM"
   ],
   "operations": [
    "采购申请/订单",
    "确认收货",
    "来票与付款发起"
   ],
   "data": [
    "erp_purchase_*",
    "srm_*"
   ],
   "scope": "requestUserId/supplierOwner å½’属"
  },
  {
   "role": "采购员/供应商管理员",
   "modules": [
    "SRM"
   ],
   "operations": [
    "供应商档案维护",
    "询价比价与确认转订单"
   ],
   "data": [
    "srm_supplier*",
    "srm_inquiry*"
   ],
   "scope": "采购管理菜单权限;询价确认生成的采购订单草稿走 ERP BPM"
  },
  {
   "role": "计划员",
   "modules": [
    "MES"
   ],
   "operations": [
    "MPS ä¸Žå·¥å•",
    "工艺路线维护",
    "AI é¢„测参考"
   ],
   "data": [
    "mes_pro_*"
   ],
   "scope": "车间/部门数据范围"
  },
  {
   "role": "操作工/班组长",
   "modules": [
    "MES"
   ],
   "operations": [
    "任务接收",
    "报工提交(受工序顺序约束)",
    "安灯"
   ],
   "data": [
    "mes_pro_feedback",
    "mes_pro_task"
   ],
   "scope": "任务指派人可见"
  },
  {
   "role": "仓管员",
   "modules": [
    "MES-WM"
   ],
   "operations": [
    "15 ç±»å‡ºå…¥åº“单据执行",
    "调拨",
    "盘点",
    "条码装箱"
   ],
   "data": [
    "mes_wm_*"
   ],
   "scope": "仓库维度;WMS å¯æŒ‰ä»“库配置出入库/调拨/盘点是否走 BPM"
  },
  {
   "role": "质检员",
   "modules": [
    "MES-QC"
   ],
   "operations": [
    "IQC/IPQC/OQC/RQC æ£€éªŒ",
    "NCR è¯„审处置"
   ],
   "data": [
    "mes_qc_*"
   ],
   "scope": "inspectorUserId å½’属"
  },
  {
   "role": "设备员/维修工",
   "modules": [
    "MES-DV"
   ],
   "operations": [
    "点检保养",
    "维修工单",
    "工装领用"
   ],
   "data": [
    "mes_dv_*",
    "mes_tm_*"
   ],
   "scope": "chargeUserId å½’属"
  },
  {
   "role": "客服/售后工程师",
   "modules": [
    "售后"
   ],
   "operations": [
    "工单受理派工",
    "客户诉求/处理结果附件",
    "ç»´ä¿®",
    "退货转仓储发起"
   ],
   "data": [
    "after_sale_*"
   ],
   "scope": "行级数据权限四视图:我负责/我参与/下属负责/全部(aftersales:view-all)"
  },
  {
   "role": "财务",
   "modules": [
    "ERP",
    "CRM",
    "FIN"
   ],
   "operations": [
    "收款单",
    "付款单",
    "开票审核",
    "结算账户",
    "会计科目维护",
    "记账凭证录入与过账/作废",
    "总账与明细账查询",
    "固定资产折旧计提",
    "无形资产摊销计提",
    "会计核算看板"
   ],
   "data": [
    "erp_finance_*",
    "crm_invoice",
    "crm_receivable",
    "fin_account_subject",
    "fin_voucher",
    "fin_voucher_entry",
    "fin_fixed_asset",
    "fin_intangible_asset"
   ],
   "scope": "financeUserId å½’属;财务核算模块按 finance:* æŽ¥å£æƒé™ + system_menu æŽˆæƒï¼Œæš‚无数据权限字段"
  },
  {
   "role": "HR",
   "modules": [
    "HRM"
   ],
   "operations": [
    "员工档案入转调离",
    "考勤规则排班",
    "薪酬核算发放",
    "离职交接"
   ],
   "data": [
    "hrm_*"
   ],
   "scope": "部门范围 dataScope"
  },
  {
   "role": "管理层",
   "modules": [
    "BI"
   ],
   "operations": [
    "查看采购销售/仓库/生产设备/质量/人力五大主题大屏"
   ],
   "data": [
    "bi_dashboard"
   ],
   "scope": "只读"
  }
 ]
}</script>
<pre style="display:none" class="mmd-src" data-mmd="01-business-mindmap">
mindmap
@@ -730,6 +2810,7 @@
flowchart TD
  A[线索 crm_clue] -->|转化·仅一次| B[客户 crm_customer]
  B --> C[联系人 crm_contact]
  B --> BADDR[客户收货地址 crm_customer_address·一户多地址·默认地址]
  B -.->|按招标方登记保证金| BB[投标保证金 crm_bid_bond·中标结果0待开标1已中标2未中标/退回状态]
  BB -.->|未中标·跟踪退回金额与日期| BB
  BB -->|中标后关联该客户已审核销售订单| H
@@ -741,10 +2822,15 @@
  D -->|直接建合同| G
  Q[合同 Excel å¯¼å…¥Â·ä¸€åˆåŒå¤šäº§å“æ˜Žç»†è¡Œ] -->|批量建草稿合同| G
  G -->|BPM审批通过20| H[生成 ERP é”€å”®è®¢å• XSDD]
  H -->|提交BPM| I[销售订单审批·审批中15→已审核20]
  H -->|手工/导入订单·提交BPM| I[销售订单·合同转来订单提交免BPM直通20·提交后不可修改]
  H -->|合同转来订单·提交免审批·直接生效| I
  H -.->|销售台账展示| H1[销售类型 1内销2外销3样品4其他·合同号可手工填·产品编号·生产数量]
  I -->|明细需生产·自动创建| J[MES ä¸»ç”Ÿäº§è®¡åˆ’ MPS]
  I --> K[发货通知单]
  I --> K[发货通知单·物流公司/快递费/开票状态·审批流默认物流审批]
  K --> KWORD[发货单 Word·按公司信息实时生成电子章·可手填签字]
  K --> KLEDGER[发货台账·按产品平铺·一行一个产品]
  K -->|审核通过| L0[销售出库单·提交]
  L0 --> LWORD[成品出库单 Word·带电子章]
  L0 -->|需检验行 oqcCheckFlag=1| L1[待检测1·出库行质量状态 PENDING]
  L0 -->|无需检验| L2[待拣货2]
  L1 -->|已完成 OQC æŒ‰ç‰©æ–™+盘号+数量一致选待检行关联| L3[出库行质量状态 PASS/FAIL]
@@ -753,8 +2839,9 @@
  L -->|回写出库数量与状态| M[销售订单 éƒ¨åˆ†å‡ºåº“2/全部出库3]
  G --> N[回款计划·按期数分期]
  G -->|提交BPM| O[开票 KP·审批中10→通过20·累计不超合同额]
  O -->|回款前必须先开票| P[回款 HK·创建即审批中10→手动审核20]
  P -->|校验合同剩余额与开票剩余额| N
  O -->|回款前必须先开票| P[回款 HK·未提交0 â†’ æäº¤BPM协同审批10 â†’ é€šè¿‡20/驳回30/取消40]
  P -.->|提交时重新校验合同剩余额与开票剩余额·审批意见由BPM流转记录承载·页面内手工审核关闭| P
  P -->|审批通过才回写回款计划已回款金额·审批节点「财务经理审批」按经理角色或签| N
</pre>
<pre style="display:none" class="mmd-src" data-mmd="03-purchase-flow">
flowchart TD
@@ -763,13 +2850,13 @@
  S3 -->|提交| C3
  C1[采购申请 CGSQ·草稿0→审批中10] -->|提交BPM| C2{审批}
  C2 -->|驳回30→可重置回草稿| C1
  C2 -->|通过20且有供应商·自动生成| C3[采购订单 CGDD·审批中15→已审核20]
  C2 -->|通过20且有供应商·自动生成| C3[采购台账 CGDD·采购类型/合同号/项目/付款日期/付款方式·审批中15→已审核20]
  C2 -->|通过20无供应商| C4[手动指定供应商生成订单]
  C4 --> C3
  C3 -->|确认收货| C5[到货通知单·草稿0→待质检2→待入库3→完成4]
  C5 -->|物料需检验·自动创建| C6[IQC æ¥æ–™æ£€éªŒ]
  C6 -->|合格1或特采2 å¯å…¥åº“| C7[采购入库单·草稿0→待上架2→待执行3→完成4]
  C6 -->|不合格退货3| C8[供应商退货·待拣货→待执行→完成·库存扣减]
  C6 -->|不合格退货3| C8[供应商退货·草稿→审批中(BPM é‡‡è´­â†’质量→仓库)→待拣货→待执行→完成·库存扣减;退款→负数冲红付款单;换货→到货通知+IQC é‡æ–°å…¥åº“]
  C6 -->|不合格报废4| C9[NCR ä¸åˆæ ¼å“å¤„ç½®]
  C7 -->|回写 inCount ä¸Ž inStatus æœªå…¥åº“0部分1全部2| C3
  C7 -->|回写申请明细 inCount| C1
@@ -782,20 +2869,26 @@
<pre style="display:none" class="mmd-src" data-mmd="04-production-flow">
flowchart TD
  P0[销售订单明细 needProduction=1] -->|审批通过自动创建| P1[MPS ä¸»ç”Ÿäº§è®¡åˆ’·草稿0→已下发1]
  P1 -->|下发或合并下发| P2[生产工单·草稿0→已确认1]
  P1 -->|下发或合并下发·仅创建人或超管可操作| P2[生产工单·草稿0→已确认1]
  P2 -->|按工艺路线工序自动生成| P3[生产任务×N·预备0→生产中1→部分完成2→完成4·可暂停9]
  P3 --> P4[生产领料 æˆ– çº¿è¾¹è™šæ‹Ÿåº“备料]
  P4 --> P5[报工提交·草稿0]
  P5 -->|校验:上一道工序未报满则拦截| P5
  P5 -->|保存时按工序标准工时配置+生产长度+辅助项次数| P5S[标准工时快照·主工时+辅助工时·配置缺失不阻断转手工填工时]
  P5S -.->|实际工时 vs æ ‡å‡†å·¥æ—¶Â·å·®å¼‚率超阈值默认20%提示| P5
  P5S -.->|按人/工序/机台/周期汇总| P5R[工时考核报表·报工单数/生产长度/实际/标准/差异率]
  P5 -->|工单绑定的工艺路线+主计价设备+辅助工时| P5W[工资快照·单价规则(工序+路线+设备+生效日期)优先·未命中回退工序工资定额·工资字段系数/加减/计价数量·辅助工时计薪·试算接口 preview-wage]
  P5 -->|提交后不再自动生成 IPQC·过程检验由质量部门在质量管理页手工新建并绑定工单/工序/半成品/盘号| P5Q[过程检验单·手工创建]
  P5 -->|启用BPM则提交审批| P6{报工审批}
  P6 -->|驳回| P5
  P6 -->|通过且倒冲工序| P7[按工单BOM自动生成物料消耗·扣线边虚拟库FIFO允许负库存]
  P6 -->|通过且质检工序| P8[报工转待检验·产出行 PENDING]
  P6 -->|通过且非质检关键工序| P9[产品产出直接完工入库]
  P6 -->|通过·按工序计费类型快照报工工资| P6W[报工工资·计时=工时×工资定额 / è®¡ä»¶=报工数量×工资定额·记到报工人名下]
  P6 -->|通过·工单存在生产BOM即按 BOM ç”Ÿæˆç‰©æ–™æ¶ˆè€—·不再依赖倒冲或质检开关| P7[按工单BOM自动生成物料消耗·扣线边虚拟库FIFO允许负库存]
  P6 -->|通过·成品产出| P8[报工转待检验·产出行 PENDING]
  P6 -->|通过·半成品产出·报工物料≠工单成品| P8S[半成品产出行直接 PASS·按生产BOM目标库位生成入库明细并立即完工·不等质检不入审批]
  P6 -->|通过·按快照重算工资并应用质量调整| P6W[报工工资·计件按合格数量·合格率命中系数或不合格扣款·记到报工人名下]
  P6W -.->|核算周期内已审批报工汇总| HRM[薪酬核算·报工工资可人工调整后计入实发]
  HRM -.->|点报工工资「明细」| HRMD[报工工资明细·按报工单展开主工资+辅助工资+工资字段(值/单价/金额)+质量调整=最终工资·与核算金额比对]
  P8 -->|已完成 IPQC æŒ‰ç‰©æ–™+盘号+数量一致选待检产出行关联| P8L[回写报工完成·报工状态→完成4]
  P8 -.->|产出行 PENDING æ—¶ä¸å…è®¸å®Œå·¥å…¥åº“·必须完成 IPQC å¹¶å›žå†™åˆæ ¼çŠ¶æ€| P8
  P8L -->|合格数量>0| P10[合格行入库·不合格数量拆出为不合格行]
  P8L -->|不合格数量>0| P11[NCR ä¸åˆæ ¼å“Â·ç­‰å¾…后续处置]
  P8 -.->|未关联的已完成 IPQC ä¸å›žå†™Â·éœ€åœ¨æ£€éªŒå•详情手动关联| P8
@@ -808,7 +2901,12 @@
<pre style="display:none" class="mmd-src" data-mmd="05-quality-flow">
flowchart TD
  T1[到货通知单 æˆ– å¤–协入库单] --> A1[IQC æ¥æ–™æ£€éªŒÂ·è‰ç¨¿0→完成4]
  T2[报工质检工序通过后产出行·独立录入 IPQC] --> A2[IPQC è¿‡ç¨‹æ£€éªŒÂ·è‰ç¨¿0→完成4]
  T2[报工后产出行·由质量部门手工新建 IPQC] --> A2[IPQC è¿‡ç¨‹æ£€éªŒÂ·è‰ç¨¿0→完成4]
  T2 -.->|新建时绑定生产工单·工序由工单工艺路线带出·半成品由该工序产出物料带出·盘号手工录入| A2
  A2 -.->|新建/编辑·选工位按工序带出该工序维护的自定义工资字段·数值/文本/下拉/布尔控件按字段类型决定| QWF[工资字段填写·由质检填写·报工审批重算工资优先取此值·未填回退工序固定值]
  A2 -.->|检验信息可手工增删检验行·每行只上传检验附件·不再填写检测值| QFILL[检验行·检验项+附件(每条检验行独立绑定)·保存后按明细ID关联]
  A2 -.->|完成状态是产出入库前置条件| QW[成品产出行 PENDING·完成 IPQC å›žå†™åˆæ ¼åŽæ‰å…è®¸å®Œå·¥å…¥åº“]
  A2 -.->|合格率参与报工工资| QW2[按工序+路线匹配质量规则·合格率系数或不合格数量扣款]
  T3[销售出库单待检测行·独立录入 OQC] --> A3[OQC å‡ºè´§æ£€éªŒÂ·è‰ç¨¿0→完成4]
  T4[生产退料单 æˆ– é”€å”®é€€è´§å• æˆ– å”®åŽé€€è´§] --> A4[RQC é€€è´§æ£€éªŒÂ·è‰ç¨¿0→完成4]
  T5[Excel æ‰¹é‡å¯¼å…¥Â·IQC IPQC OQC ä¸‰åˆ—表各一个入口·按钮在新增与导出之间] --> IMP{导入闸门·逐行解析·失败行互不影响}
@@ -833,9 +2931,12 @@
  A2 -->|已完成且未绑定来源·按物料+盘号+数量一致| Q5[关联报工待检行·回写报工完成与产出入库]
  A3 -->|已完成且未绑定来源·按物料+盘号+数量一致| Q6[关联销售出库待检行·回写出库行质量状态·全部合格推进待拣货]
  A4 -->|完成| Q7[退货单推进上架或入库·售后回调结论]
  N1[NCR å¾…评审0] -->|评审| N2[已评审6]
  N2 -->|处置方式:退货1报废2返工3让步接收4降级使用5| N3[已处置7]
  N1[NCR å¾…评审0] -->|提交审批·创建 BPM å®žä¾‹ key=mes_ncr_approve| N1A[审批中2]
  N1A -->|BPM é©³å›ž/取消·保留流程实例可查意见| N1
  N1A -->|BPM å®¡æ‰¹é€šè¿‡Â·å®¡æ‰¹èŠ‚ç‚¹ã€Œè´¨é‡éƒ¨é—¨å®¡æ‰¹ã€æŒ‰è´¨é‡éƒ¨é—¨è§’è‰²æˆ–ç­¾Â·æ„è§ç”± BPM æµè½¬è®°å½•承载| N2[已评审6·质量部待处置]
  N2 -->|质量部最终处置·让步放行4/返修6/返工3/报废2| N3[已处置7]
  N3 -->|关闭| N4[已关闭8]
  N1 -.->|未发布流程定义或全局免审批时·提交后自动置为已评审| N2
  Q8[支撑数据:质检方案·检验指标·缺陷库·指标结果] --- A1
  Q8H["检验指标的层级可在质检指标页维护·条目类型 æ‰€å±žåˆ†ç»„ å…¬å¼ æŽ’序号·只允许两层·删分组级联删子项"] --- Q8
  A1 -->|出件 generate-from-qc| GATE{出件六道校验·按业务优先级只报第一处}
@@ -900,6 +3001,9 @@
  RPTVSAVE -.->|设计器画布不跑绑定·组名格的 hidden å ä½ç¬¦ä¼šè¢«æµè§ˆå™¨å½“成真 hidden è®©æ•´è¡Œå·¦ç§»ã€æœ«åˆ—挤成竖排细条| CVNOTE
  RPTVSAVE -.->|自定列不带宽度声明·列宽只能由最小内容宽度倒推·而值是占位符这类不含空格的长串·整表会被撑出纸张、末列挤成竖条| CVWIDTH
  CVWIDTH["故自定列的单元格带 overflow-wrap:anywhere å…è®¸é•¿ä¸²æŠ˜è¡ŒÂ·é»˜è®¤åˆ—有手工宽度声明不加·实测加了反而把检验项目子项挤到 30 ä½™åƒç´ ã€è¡Œé«˜ç¿»å‡ å€"] --- RPTVSAVE
  PT[质量巡检 mes_qc_patrol·独立记录·不生成库存业务单据] -->|编码规则 QC_PATROL_CODE·XJ_+yyyyMMdd+3位流水| PT2[巡检查询/新增/修改/删除·菜单挂质量管理 5500]
  STAT[质量管理「质量统计」·菜单 1080080 æƒé™ mes:qc-statistics:query] -->|GET /mes/qc/statistics/summary·可选检测日期范围| STATD["整体合格率/不合格率·按检验类型 IQC/IPQC/OQC/RQC·按工序(IPQC process_id)·按生产工单(IPQC work_order_id)·仅统计已完成(status=4)检验单"]
</pre>
<pre style="display:none" class="mmd-src" data-mmd="06-warehouse-flow">
flowchart LR
@@ -926,10 +3030,11 @@
  W -->|占用预留 reservedQuantity| RES[库存占用 é”€å”®å‡ºåº“·领料·外协·杂项·调拨]
  MV[转移调拨·内部或外部] -->|成对事务 MOVE_OUT+MOVE_IN åœ¨é€”| W
  ST[盘点 æ–¹æ¡ˆâ†’任务→明细行→结果] -->|盘盈 ADJUST_IN·盘亏 ADJUST_OUT·可冻结库存| W
  W --> TX[事务流水 mes_wm_transaction åªè¯»å…¨é‡ç•™ç—•]
  W --> TX[事务流水 mes_wm_transaction åªè¯»å…¨é‡ç•™ç—•·work_order_id å†—余生产订单号供出库台账展示]
  BT[批次 mes_wm_batch·SN码·条码·装箱嵌套] --- W
  WIP[[线边虚拟库 WIP_VIRTUAL_WAREHOUSE·懒初始化]] --- O3
  FR[四级冻结:仓库·库区·库位·库存记录] --- W
  FC[冻结来源 mes_wm_material_stock_freeze_claim·MANUAL/STOCK_TAKING/TRANSFER·多来源独立持有·解冻只释放本来源] --- FR
</pre>
<pre style="display:none" class="mmd-src" data-mmd="07-after-sales-flow">
flowchart TD
@@ -937,7 +3042,7 @@
  K1 -->|创建时指定处理人,直接进入| K2[处理中10·问题判定:一般1ç»´ä¿®2退货3·严重度0-3]
  K2 -->|维修类| R1[维修记录·待处理0→维修中10→已完成20→已关闭30·现场返厂远程]
  R1 -->|completeRepair| K4[已完结50]
  K1 -.->|创建/更新/判定/完结/归档/取消/回访均留痕| FL[工单流转记录 after_sale_ticket_flow]
  K1 -.->|创建/更新/判定/完结/归档/取消/回访/仓储退货均留痕| FL[工单流转记录 after_sale_ticket_flow]
  K2 -->|退货类→仓储物流发起销售退货| T1[退货申请·草稿0·售后页面已下线,后台保留]
  T1 -->|approveReturn å®¡æ ¸é€šè¿‡| T2[已审核20·自动创建MES销售退货单]
  T2 --> T3[MES é”€å”®é€€è´§Â·è‰ç¨¿â†’待检验RQC→待上架→待入库→完成]
@@ -946,7 +3051,9 @@
  T5 -->|退款完成回调 onRefundCompleted è®°å½•实际退款额| T6[已退款60]
  T6 --> T7[已关闭70]
  T2 -.->|状态流转中| T8[审核中10·预留未启用]
  K2 -->|退货链路联动工单| K3[待入库30→待退款40]
  K2 -->|退货链路联动工单| K3[待退货30→待退款40]
  W1[仓储物流·创建销售退货单·关联销售订单] -.->|onWarehouseReturnCreated å†™å…¥ä»“储退货记录 RETURN| FL
  W1 -->|匹配该订单最近一张未关闭/未取消工单,草稿或处理中则推进| K3
  K3 --> K4
  K2 -->|resolveTicket å¡«å†™å¤„理结果+附件| K4
  K4 -->|closeTicket| K5[已关闭60]
@@ -1168,6 +3275,26 @@
  KB4 -.-> KB5[暂未接入业务问答流程·待确认]
  MODE[模型层:通义千问为主·预留DeepSeek/OpenAI/Ollama未实现] --- O2
  NOTE[全项目无Function Calling·AI只输出结构化JSON由业务侧解析] --- J2
  MODEL[模型管理:每类型每能力只允许一个启用·启用即自动关闭同组其他模型·列表按类型分组展示] --> MODEL2[对话取 text èƒ½åŠ›å¯ç”¨æ¨¡åž‹Â·å›¾ç‰‡è¯†åˆ«å– vision èƒ½åŠ›å¯ç”¨æ¨¡åž‹Â·å‘é‡åº“å–å¯ç”¨æ¨¡åž‹]
  MODEL2 -.-> O2
  MODEL2 -.-> W3
  MODEL2 -.-> KB3
  MODEL3[未启用对应能力模型时直接报错并提示去模型管理启用·不再静默回退] --- MODEL2
  A1[物料管理员上传物料CSV/XLSX→创建补录会话并绑定附件] --> A2[Java桥接授权:用户token+服务令牌+mdm:item:import]
  A2 --> A3[Python解析候选·识别公式/数字编码/缺失分类或单位]
  A3 --> A4[Java只读预检·分类单位编码存在性→CREATE/SKIP/BLOCKED+确定性摘要]
  A4 --> A5[人工审核修订·版本自增·重新预检]
  A5 --> A6[幂等提交·commandId+幂等键·逐行独立事务·提交前重查编码]
  A6 --> A7[逐行回执·SUCCESS/SKIPPED/FAILED+目标物料ID]
  A6 --> A8[超时或中断→查询命令回执对账·不盲目重试]
  A9[只新建不更新·syncMes=false·不触发MES同步] --- A6
  AIENV[AI æ€»å¼€å…³ VITE_APP_AI_ENABLE=false] --> AHIDE[隐藏 /ai æ¨¡å—路由与全部业务 AI å…¥å£ï¼šå‘票/报价识别录入·四类预测·质检判定·质检助手·智能体导入]
  AHIDE -.-> O6
  AHIDE -.-> Q4
  AHIDE -.-> J5
  AHIDE -.-> W11
  AHIDE -.-> A1
  AHIDE -.-> R2
</pre>
<pre style="display:none" class="mmd-src" data-mmd="12-finance-flow">
flowchart TD
@@ -1193,7 +3320,7 @@
  P[ERP å·²å®¡æ ¸å•据 status=20 æ”¶æ¬¾å• ä»˜æ¬¾å• é”€å”®è®¢å• é‡‡è´­è®¢å•] -->|ErpFinanceStatisticsApi.getMonthlyStatistics æŒ‰æœˆ| K
  subgraph é”€å”®é‡‡è´­è´¢åŠ¡é—­çŽ¯
    R1[ERP é”€å”®è®¢å• status=20] -->|选择订单| R2[MES é”€å”®å‡ºåº“单 status=4 å·²å®Œæˆ]
    R2 -->|开票申请·绑定订单+出库单| R3[CRM å¼€ç¥¨ crm_invoice entry_status=0 å¾…录入]
    R2 -->|开票申请·订单/出库单选填(选合同自动带出唯一订单)| R3[CRM å¼€ç¥¨ crm_invoice entry_status=0 å¾…录入]
    R3 -->|审批通过| R4[录入发票 invoiceNo/日期/附件]
    R4 -->|填写收款节点| R5[回款计划 crm_receivable_plan stage=1..5]
    R4 -->|entry_status=10 å·²å½•å…¥| R6[CRM å›žæ¬¾ crm_receivable]
docs/project-business/mermaid/02-sales-flow.mmd
@@ -13,7 +13,8 @@
  D -->|直接建合同| G
  Q[合同 Excel å¯¼å…¥Â·ä¸€åˆåŒå¤šäº§å“æ˜Žç»†è¡Œ] -->|批量建草稿合同| G
  G -->|BPM审批通过20| H[生成 ERP é”€å”®è®¢å• XSDD]
  H -->|提交BPM| I[销售订单审批·审批中15→已审核20]
  H -->|手工/导入订单·提交BPM| I[销售订单·合同转来订单提交免BPM直通20·提交后不可修改]
  H -->|合同转来订单·提交免审批·直接生效| I
  H -.->|销售台账展示| H1[销售类型 1内销2外销3样品4其他·合同号可手工填·产品编号·生产数量]
  I -->|明细需生产·自动创建| J[MES ä¸»ç”Ÿäº§è®¡åˆ’ MPS]
  I --> K[发货通知单·物流公司/快递费/开票状态·审批流默认物流审批]
docs/project-business/mermaid/03-purchase-flow.mmd
@@ -10,7 +10,7 @@
  C3 -->|确认收货| C5[到货通知单·草稿0→待质检2→待入库3→完成4]
  C5 -->|物料需检验·自动创建| C6[IQC æ¥æ–™æ£€éªŒ]
  C6 -->|合格1或特采2 å¯å…¥åº“| C7[采购入库单·草稿0→待上架2→待执行3→完成4]
  C6 -->|不合格退货3| C8[供应商退货·草稿→审批中(BPM é‡‡è´­â†’质量→仓库)→待拣货→待执行→完成·库存扣减<br/>退款→负数冲红付款单;换货→到货通知+IQC é‡æ–°å…¥åº“]
  C6 -->|不合格退货3| C8[供应商退货·草稿→审批中(BPM é‡‡è´­â†’质量→仓库)→待拣货→待执行→完成·库存扣减;退款→负数冲红付款单;换货→到货通知+IQC é‡æ–°å…¥åº“]
  C6 -->|不合格报废4| C9[NCR ä¸åˆæ ¼å“å¤„ç½®]
  C7 -->|回写 inCount ä¸Ž inStatus æœªå…¥åº“0部分1全部2| C3
  C7 -->|回写申请明细 inCount| C1
docs/project-business/mermaid/05-quality-flow.mmd
@@ -33,7 +33,10 @@
  N1[NCR å¾…评审0] -->|提交审批·创建 BPM å®žä¾‹ key=mes_ncr_approve| N1A[审批中2]
  N1A -->|BPM é©³å›ž/取消·保留流程实例可查意见| N1
  N1A -->|BPM å®¡æ‰¹é€šè¿‡Â·å®¡æ‰¹èŠ‚ç‚¹ã€Œè´¨é‡éƒ¨é—¨å®¡æ‰¹ã€æŒ‰è´¨é‡éƒ¨é—¨è§’è‰²æˆ–ç­¾Â·æ„è§ç”± BPM æµè½¬è®°å½•承载| N2[已评审6·质量部待处置]
  N2 -->|质量部最终处置·让步放行4/返修6/返工3/报废2| N3[已处置7]
  N2 -->|质量部最终处置·让步放行4/报废2| N3A[生成杂项入库单·自动提交并完成入库·处置弹窗选仓库/库区/库位·让步放行默认原材料仓·报废默认不合格品仓·批次管理物料必填批次号]
  N2 -->|质量部最终处置·返工3/返修6| N3B[生成自行生产类型生产订单(草稿)·产品=NCR物料·数量=不合格数量·BOM与条码自动生成·工艺路线人工补充后下达]
  N3A --> N3[已处置7]
  N3B --> N3
  N3 -->|关闭| N4[已关闭8]
  N1 -.->|未发布流程定义或全局免审批时·提交后自动置为已评审| N2
  Q8[支撑数据:质检方案·检验指标·缺陷库·指标结果] --- A1
docs/sql/config_export_all_20261108.sql
@@ -560,7 +560,7 @@
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='公司品牌信息配置表';
INSERT INTO `system_company_config` (`id`, `company_name`, `logo_url`, `logo_blob_id`, `login_title`, `login_description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1,'北京同仁堂','http://36.213.150.237:9016/admin-api/system/storage-blob/preview/ee1ba93d-c8f0-43ef-9609-b98f3f6a15a2_tongrentang_logo_transparent.png?publicKey=1e542685e9fb4e7c8cc152d3c57b633f',NULL,'北京同仁堂智造系统','北京同仁堂智造系统','1','2026-10-11 17:20:08','1','2026-10-11 17:20:31',0x00);
INSERT INTO `system_company_config` (`id`, `company_name`, `logo_url`, `logo_blob_id`, `login_title`, `login_description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1,'南通苏珩线缆',NULL,NULL,'南通苏珩线缆智造系统','南通苏珩线缆智造系统','1','2026-10-11 17:20:08','1','2026-11-09 11:14:14',0x00);
-- ***********************************************************************
@@ -7214,6 +7214,10 @@
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '单号',
  `name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '名称',
  `type` int DEFAULT NULL COMMENT '杂项类型',
  `source_doc_type` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源单据类型',
  `source_doc_id` bigint DEFAULT NULL COMMENT '来源单据 ID',
  `source_doc_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '来源单据编号',
  `receipt_date` datetime DEFAULT NULL COMMENT '入库日期',
  `status` int DEFAULT NULL COMMENT '状态',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
@@ -7235,6 +7239,7 @@
  `receipt_id` bigint DEFAULT NULL COMMENT '入库单 ID',
  `item_id` bigint DEFAULT NULL COMMENT '物料 ID',
  `quantity` decimal(24,6) DEFAULT NULL COMMENT '数量',
  `batch_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '批次号',
  `batch_id` bigint DEFAULT NULL COMMENT '批次 ID',
  `warehouse_id` bigint DEFAULT NULL COMMENT '仓库 ID',
  `location_id` bigint DEFAULT NULL COMMENT '库区 ID',
@@ -7255,6 +7260,10 @@
  `receipt_id` bigint DEFAULT NULL COMMENT '入库单 ID',
  `item_id` bigint DEFAULT NULL COMMENT '物料 ID',
  `quantity` decimal(24,6) DEFAULT NULL COMMENT '数量',
  `batch_code` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '批次号',
  `warehouse_id` bigint DEFAULT NULL COMMENT '仓库 ID',
  `location_id` bigint DEFAULT NULL COMMENT '库区 ID',
  `area_id` bigint DEFAULT NULL COMMENT '库位 ID',
  `remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
@@ -11392,13 +11401,13 @@
-- 4. å®¢æˆ·å¾€æ¥ / é”€å”®å¯¹è´¦ / æ ¸é”€ç®¡ç†èœå•
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show)
SELECT 1075660, '客户往来', 'crm:receivable:query', 2, 6, 1075497, 'customer-statement', '', 'crm/customerStatement/index', 0, 1, 1, 1
SELECT 1075660, '客户往来', 'crm:receivable:query', 2, 6, 1075497, 'customer-statement', 'ep:user', 'crm/customerStatement/index', 0, 1, 1, 1
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 1075660);
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show)
SELECT 1075661, '销售对账', 'crm:receivable:query', 2, 8, 2645, 'sale-reconciliation', '', 'erp/finance/reconciliation/index', 0, 1, 1, 1
SELECT 1075661, '销售对账', 'crm:receivable:query', 2, 8, 2645, 'sale-reconciliation', 'ep:document-checked', 'erp/finance/reconciliation/index', 0, 1, 1, 1
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 1075661);
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, status, visible, keep_alive, always_show)
SELECT 1075662, '核销管理', 'crm:receivable:query', 2, 9, 2645, 'write-off', '', 'erp/finance/writeoff/index', 0, 1, 1, 1
SELECT 1075662, '核销管理', 'crm:receivable:query', 2, 9, 2645, 'write-off', 'ep:stamp', 'erp/finance/writeoff/index', 0, 1, 1, 1
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM system_menu WHERE id = 1075662);
-- 5. è´¨é‡å·¡æ£€ç¼–码规则:XJ_ + yyyyMMdd + 3 ä½æµæ°´
@@ -11587,6 +11596,28 @@
                        `status` = 0, `deleted` = 0, `update_time` = NOW();
-- ============================================================
-- é˜¶æ®µä¸€è¿½åŠ ï¼ˆ2026-11-09):客户洽谈进度表
-- å®¢æˆ·è¯¦æƒ…「洽谈进度」页签维护多条记录;客户导出时取每个客户最新一条(按 progress_time å€’序)
-- å¹‚等:可重复执行
-- ============================================================
CREATE TABLE IF NOT EXISTS `crm_customer_progress`
(
    `id`            bigint                                                       NOT NULL AUTO_INCREMENT COMMENT '编号',
    `customer_id`   bigint                                                       NOT NULL COMMENT '客户编号',
    `content`       varchar(1000) COLLATE utf8mb4_unicode_ci                     NOT NULL COMMENT '洽谈进度内容',
    `progress_time` datetime                                                     NOT NULL COMMENT '洽谈时间',
    `creator`       varchar(64) COLLATE utf8mb4_unicode_ci                       DEFAULT '' COMMENT '创建者',
    `create_time`   datetime                                                     NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater`       varchar(64) COLLATE utf8mb4_unicode_ci                       DEFAULT '' COMMENT '更新者',
    `update_time`   datetime                                                     NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后更新时间',
    `deleted`       tinyint(1)                                                   NOT NULL DEFAULT '0' COMMENT '是否删除',
    PRIMARY KEY (`id`) USING BTREE,
    KEY `idx_customer_id` (`customer_id`) USING BTREE
) ENGINE = InnoDB
  DEFAULT CHARSET = utf8mb4
  COLLATE = utf8mb4_unicode_ci ROW_FORMAT = DYNAMIC COMMENT ='CRM å®¢æˆ·æ´½è°ˆè¿›åº¦è¡¨';
-- ============================================================
-- é˜¶æ®µäºŒè¿½åŠ ï¼ˆ2026-11-09):客户管理与联系人管理合并
-- è”系人不再作为销售管理下的独立模块,统一在「客户管理 â†’ å®¢æˆ·è¯¦æƒ… â†’ è”系人」页签维护
-- èœå• 2416「联系人管理」记录保留(路由、按钮权限、角色授权不变),仅从左侧菜单隐藏
@@ -11595,3 +11626,142 @@
UPDATE `system_menu`
SET `visible` = 0, `updater` = '1', `update_time` = NOW()
WHERE `id` = 2416 AND `deleted` = 0;
-- ============================================================
-- é˜¶æ®µä¸€è¿½åŠ ï¼ˆ2026-11-09):审批详情展示业务单据详情
-- æœºåˆ¶ï¼šBpmBusinessAttachServiceImpl#fillBusinessForm æŒ‰ã€Œæµç¨‹å®šä¹‰ Key â†’ æµç¨‹åˆ†ç±»ã€åŒ¹é…
--       bpm_business_attach(status=0 å¼€å¯ï¼‰ï¼Œå‘½ä¸­åŽæŠŠå®¡æ‰¹è¯¦æƒ…表单区渲染为业务详情组件,
--       ç»„件接收业务主键 businessKey(即单据 id),路径由前端 registerComponent åŠ¨æ€è§£æžã€‚
-- æœ¬æ¬¡è¡¥å……:
--   1. é”€å”®æŠ¥ä»·å®¡æ‰¹ï¼ˆæµç¨‹ Key QWEDD,分类 sale_quotation_approve)→ æŠ¥ä»·å•详情
--   2. è¯·å‡å®¡æ‰¹ï¼ˆhrm_leave / hrm_leave_approve)→ è¯·å‡ç”³è¯·è¯¦æƒ…
--   3. ç¦»èŒå®¡æ‰¹ï¼ˆhrm_resignation / hrm_resignation_approve)→ ç¦»èŒç”³è¯·è¯¦æƒ…
-- å¦è¡¥ hrm_audit_status å­—典:HRM å®¡æ‰¹è¯¦æƒ…用 DictTag ç¿»è¯‘审批状态,缺该字典时状态显示空白
-- å¹‚等:可重复执行
-- ============================================================
INSERT INTO `bpm_business_attach` (`id`, `process_definition_key`, `business_type`, `component_path`,
                                   `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (11, 'sale_quotation_approve', 'crm_sale_quotation',
        '/crm/saleQuotation/detail/approve/index.vue', 0, '销售报价审批展示报价详情',
        '1', NOW(), '1', NOW(), 0x00),
       (12, 'hrm_leave_approve', 'hrm_leave_application',
        '/hrm/leave/detail/approve/index.vue', 0, '请假审批展示请假申请详情',
        '1', NOW(), '1', NOW(), 0x00),
       (13, 'hrm_resignation_approve', 'hrm_resignation_application',
        '/hrm/resignation/detail/approve/index.vue', 0, '离职审批展示离职申请详情',
        '1', NOW(), '1', NOW(), 0x00)
ON DUPLICATE KEY UPDATE `business_type` = VALUES(`business_type`),
                        `component_path` = VALUES(`component_path`),
                        `status` = 0, `remark` = VALUES(`remark`),
                        `deleted` = 0, `update_time` = NOW();
-- ä¸åˆæ ¼å“å¤„理审批(mes_ncr_approve)→ ä¸åˆæ ¼å“å¤„理单详情
-- ä¸å†™æ­» id:避免与各环境既有自增 id å†²çªï¼›æŒ‰å”¯ä¸€é”® (process_definition_key, deleted) å¹‚ç­‰
INSERT INTO `bpm_business_attach` (`process_definition_key`, `business_type`, `component_path`,
                                   `status`, `remark`, `creator`, `updater`, `deleted`)
VALUES ('mes_ncr_approve', 'mes_qc_ncr', '/mes/qc/ncr/detail/approve/index.vue', 0,
        '不合格品处理审批展示不合格品处理单详情', '1', '1', 0x00)
ON DUPLICATE KEY UPDATE `business_type` = VALUES(`business_type`),
                        `component_path` = VALUES(`component_path`),
                        `status` = 0, `remark` = VALUES(`remark`),
                        `deleted` = 0, `update_time` = NOW();
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`,
                                `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (1065420, 'HRM å®¡æ‰¹çŠ¶æ€', 'hrm_audit_status', 0, '人事审批状态',
        '1', NOW(), '1', NOW(), 0)
ON DUPLICATE KEY UPDATE `name` = VALUES(`name`), `status` = 0,
                        `remark` = VALUES(`remark`), `deleted` = 0, `update_time` = NOW();
-- å–值对齐 HrmAuditStatusEnum:0=草稿,10=审批中,20=审批通过,30=审批驳回,40=已撤销
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`,
                                `color_type`, `css_class`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (1065530, 1, '草稿', '0', 'hrm_audit_status', 0, '', '', '1', NOW(), '1', NOW(), 0),
       (1065531, 2, '审批中', '10', 'hrm_audit_status', 0, '', '', '1', NOW(), '1', NOW(), 0),
       (1065532, 3, '审批通过', '20', 'hrm_audit_status', 0, '', '', '1', NOW(), '1', NOW(), 0),
       (1065533, 4, '审批驳回', '30', 'hrm_audit_status', 0, '', '', '1', NOW(), '1', NOW(), 0),
       (1065534, 5, '已撤销', '40', 'hrm_audit_status', 0, '', '', '1', NOW(), '1', NOW(), 0)
ON DUPLICATE KEY UPDATE `sort` = VALUES(`sort`), `label` = VALUES(`label`), `value` = VALUES(`value`),
                        `status` = 0, `deleted` = 0, `update_time` = NOW();
-- ============================================================
-- é˜¶æ®µä¸€è¿½åŠ ï¼ˆ2026-11-09):NCR å¤„置联动入库 / ç”Ÿäº§
-- èƒŒæ™¯ï¼šä¸åˆæ ¼å“å¤„理单处置为「让步放行」/「报废」时自动生成杂项入库单并入库存,
--       å¤„置为「返工」/「返修」时自动生成生产订单。
-- 1. mes_wm_misc_receipt è¡¥ type / source_doc_type / source_doc_id / source_doc_code
-- 2. mes_wm_misc_receipt_line è¡¥ batch_code / warehouse_id / location_id / area_id
-- 3. mes_wm_misc_receipt_detail è¡¥ batch_code
-- 4. mes_wm_misc_receipt_type å­—典补「让步放行入库(2)」「报废入库(3)」
-- æ–°å»ºåº“:上面建表语句已包含上述字段,本节 ALTER ä¼šè‡ªåŠ¨è·³è¿‡
-- å·²ä¸Šçº¿åº“:直接执行本节即可(MySQL 8.4 ä¸æ”¯æŒ ADD COLUMN IF NOT EXISTS)
-- ============================================================
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt' AND column_name = 'type');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt` ADD COLUMN `type` int DEFAULT NULL COMMENT ''杂项类型'' AFTER `name`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt' AND column_name = 'source_doc_type');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt` ADD COLUMN `source_doc_type` varchar(64) DEFAULT NULL COMMENT ''来源单据类型'' AFTER `type`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt' AND column_name = 'source_doc_id');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt` ADD COLUMN `source_doc_id` bigint DEFAULT NULL COMMENT ''来源单据 ID'' AFTER `source_doc_type`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt' AND column_name = 'source_doc_code');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt` ADD COLUMN `source_doc_code` varchar(100) DEFAULT NULL COMMENT ''来源单据编号'' AFTER `source_doc_id`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt_line' AND column_name = 'batch_code');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt_line` ADD COLUMN `batch_code` varchar(100) DEFAULT NULL COMMENT ''批次号'' AFTER `quantity`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt_line' AND column_name = 'warehouse_id');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt_line` ADD COLUMN `warehouse_id` bigint DEFAULT NULL COMMENT ''仓库 ID'' AFTER `batch_code`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt_line' AND column_name = 'location_id');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt_line` ADD COLUMN `location_id` bigint DEFAULT NULL COMMENT ''库区 ID'' AFTER `warehouse_id`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt_line' AND column_name = 'area_id');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt_line` ADD COLUMN `area_id` bigint DEFAULT NULL COMMENT ''库位 ID'' AFTER `location_id`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
SET @col_exists = (SELECT COUNT(*) FROM information_schema.columns
                   WHERE table_schema = DATABASE() AND table_name = 'mes_wm_misc_receipt_detail' AND column_name = 'batch_code');
SET @sql = IF(@col_exists = 0,
              'ALTER TABLE `mes_wm_misc_receipt_detail` ADD COLUMN `batch_code` varchar(100) DEFAULT NULL COMMENT ''批次号'' AFTER `quantity`',
              'SELECT 1');
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
-- æ‚项入库类型字典:让步放行入库 / æŠ¥åºŸå…¥åº“
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`,
                                `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)
VALUES (1065535, 2, '让步放行入库', '2', 'mes_wm_misc_receipt_type', 0, 'primary', '', '让步放行入库', '1', NOW(), '1', NOW(), 0),
       (1065536, 3, '报废入库', '3', 'mes_wm_misc_receipt_type', 0, 'danger', '', '报废入库', '1', NOW(), '1', NOW(), 0)
ON DUPLICATE KEY UPDATE `sort` = VALUES(`sort`), `label` = VALUES(`label`), `value` = VALUES(`value`),
                        `status` = 0, `remark` = VALUES(`remark`), `deleted` = 0, `update_time` = NOW();
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessStatusController.java
@@ -93,11 +93,13 @@
        }
        // 2. æ‹¼æŽ¥æ•°æ®
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(pageResult.getList(), statusType -> Long.parseLong(statusType.getCreator())));
                convertSet(pageResult.getList(), statusType -> NumberUtils.parseLong(statusType.getCreator())));
        Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
                convertSetByFlatMap(pageResult.getList(), CrmBusinessStatusTypeDO::getDeptIds, Collection::stream));
        return success(BeanUtils.toBean(pageResult, CrmBusinessStatusRespVO.class, statusTypeVO -> {
            statusTypeVO.setCreator(userMap.get(NumberUtils.parseLong(statusTypeVO.getCreator())).getNickname());
            // åˆ›å»ºäººå¯èƒ½å·²åˆ é™¤ï¼ˆå¦‚离职账号),显示“已删除用户”而不是抛 NPE å¯¼è‡´æ•´é¡µæŸ¥è¯¢å¤±è´¥
            AdminUserRespDTO creator = userMap.get(NumberUtils.parseLong(statusTypeVO.getCreator()));
            statusTypeVO.setCreator(creator != null ? creator.getNickname() : "已删除用户");
            statusTypeVO.setDeptNames(convertList(statusTypeVO.getDeptIds(),
                    deptId -> deptMap.containsKey(deptId) ? deptMap.get(deptId).getName() : null));
        }));
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -811,6 +811,8 @@
                    item.setProductId(cp.getItemId());
                    item.setProductPrice(cp.getContractPrice());
                    item.setCount(cp.getCount());
                    // ä¸Žé¡µé¢æ–°å¢žæ˜Žç»†çš„默认值一致:默认需要生产,转单后可在提交前按实际情况改为“不需要生产”
                    item.setNeedProduction(1);
                    return item;
                })
                .toList();
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerProgressServiceImpl.java
@@ -1,14 +1,18 @@
package cn.iocoder.yudao.module.crm.service.customer;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.progress.CrmCustomerProgressSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerProgressDO;
import cn.iocoder.yudao.module.crm.dal.mysql.customer.CrmCustomerProgressMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
@@ -39,6 +43,9 @@
    @Resource
    @Lazy
    private CrmPermissionService permissionService;
    @Resource
    private CrmCustomerService customerService;
    @Override
    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#createReqVO.customerId",
@@ -92,6 +99,19 @@
        return result;
    }
    /**
     * å¼‚常提示里用的客户名称:解析失败时回退为 ID,避免异常分支再抛二次异常
     */
    private String getCustomerDisplayName(Long customerId) {
        try {
            CrmCustomerDO customer = customerService.getCustomer(customerId);
            return customer != null && StrUtil.isNotBlank(customer.getName())
                    ? customer.getName() : "ID=" + customerId;
        } catch (Exception ex) {
            return "ID=" + customerId;
        }
    }
    private CrmCustomerProgressDO validateProgressExists(Long id) {
        CrmCustomerProgressDO progress = customerProgressMapper.selectById(id);
        if (progress == null) {
@@ -101,9 +121,16 @@
    }
    private void validateCustomerWritePermission(Long customerId) {
        // è¶…管 / CRM ç®¡ç†å‘˜ä¸Ž @CrmPermission åˆ‡é¢ä¿æŒä¸€è‡´ï¼Œç›´æŽ¥æ”¾è¡Œ
        if (CrmPermissionUtils.isCrmAdmin()) {
            return;
        }
        if (!permissionService.hasPermission(CrmBizTypeEnum.CRM_CUSTOMER.getType(), customerId,
                getLoginUserId(), CrmPermissionLevelEnum.WRITE)) {
            throw exception(CRM_PERMISSION_DENIED, "客户洽谈进度");
            throw new ServiceException(CRM_PERMISSION_DENIED.getCode(),
                    "没有权限修改/删除客户[" + getCustomerDisplayName(customerId)
                            + "]的洽谈进度:您不是该客户的负责人或团队成员。请让客户负责人在客户详情「团队成员」中把您加入,"
                            + "或切换到该客户负责人账号后重试。");
        }
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/invoice/CrmInvoiceServiceImpl.java
@@ -439,41 +439,42 @@
    /**
     * æ ¡éªŒé”€å”®è®¢å•、销售出库单,并回填出库单号
     *
     * é”€å”®è®¢å•、销售出库单均为选填:填写了才按对应规则校验,未填写直接跳过(与前端一致)。
     * æ ¡éªŒé¡ºåºï¼šé”€å”®è®¢å•存在 -> å·²å®¡æ ¸ -> å®¢æˆ·/合同一致 -> è®¢å•剩余可开票;
     * å†æ ¡éªŒå‡ºåº“单存在 -> å·²å®Œæˆ -> å±žäºŽè¯¥è®¢å• -> å®¢æˆ·ä¸€è‡´ -> å‡ºåº“单剩余可开票。
     */
    private void validateSaleOrderAndOut(CrmInvoiceSaveReqVO reqVO) {
        if (reqVO.getSaleOrderId() == null) {
            throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_REQUIRED);
        }
        // 1. é”€å”®è®¢å•
        ErpSaleOrderRespDTO saleOrder = saleOrderApi.getSaleOrder(reqVO.getSaleOrderId()).getCheckedData();
        if (saleOrder == null) {
            throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_NOT_EXISTS, reqVO.getSaleOrderId());
        }
        if (ObjectUtil.notEqual(saleOrder.getStatus(), ERP_SALE_ORDER_STATUS_APPROVE)) {
            throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_NOT_APPROVE, saleOrder.getNo(),
                    erpAuditStatusName(saleOrder.getStatus()));
        }
        if (ObjectUtil.notEqual(saleOrder.getCustomerId(), reqVO.getCustomerId())
                || (saleOrder.getContractId() != null
                && ObjectUtil.notEqual(saleOrder.getContractId(), reqVO.getContractId()))) {
            throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_MISMATCH, saleOrder.getNo(),
                    saleOrder.getCustomerId(), saleOrder.getContractId(), reqVO.getCustomerId(), reqVO.getContractId());
        }
        // 1.1 è®¢å•剩余可开票金额 = è®¢å•金额 - è¯¥è®¢å•已占用开票金额(审批中 + å®¡æ‰¹é€šè¿‡ï¼‰
        BigDecimal orderTotalPrice = zeroIfNull(saleOrder.getTotalPrice());
        BigDecimal orderInvoicedPrice = sumInvoicePrice(invoiceMapper.selectListBySaleOrderIdAndStatus(
                reqVO.getSaleOrderId(), INVOICE_OCCUPY_STATUSES), reqVO.getId());
        BigDecimal orderRemainPrice = orderTotalPrice.subtract(orderInvoicedPrice);
        if (reqVO.getPrice() != null && reqVO.getPrice().compareTo(orderRemainPrice) > 0) {
            throw exception(INVOICE_CREATE_FAIL_PRICE_EXCEEDS_SALE_ORDER, saleOrder.getNo(),
                    formatPrice(orderRemainPrice), formatPrice(orderTotalPrice), formatPrice(orderInvoicedPrice));
        // 1. é”€å”®è®¢å•(选填)
        ErpSaleOrderRespDTO saleOrder = null;
        if (reqVO.getSaleOrderId() != null) {
            saleOrder = saleOrderApi.getSaleOrder(reqVO.getSaleOrderId()).getCheckedData();
            if (saleOrder == null) {
                throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_NOT_EXISTS, reqVO.getSaleOrderId());
            }
            if (ObjectUtil.notEqual(saleOrder.getStatus(), ERP_SALE_ORDER_STATUS_APPROVE)) {
                throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_NOT_APPROVE, saleOrder.getNo(),
                        erpAuditStatusName(saleOrder.getStatus()));
            }
            if (ObjectUtil.notEqual(saleOrder.getCustomerId(), reqVO.getCustomerId())
                    || (saleOrder.getContractId() != null
                    && ObjectUtil.notEqual(saleOrder.getContractId(), reqVO.getContractId()))) {
                throw exception(INVOICE_CREATE_FAIL_SALE_ORDER_MISMATCH, saleOrder.getNo(),
                        saleOrder.getCustomerId(), saleOrder.getContractId(), reqVO.getCustomerId(), reqVO.getContractId());
            }
            // 1.1 è®¢å•剩余可开票金额 = è®¢å•金额 - è¯¥è®¢å•已占用开票金额(审批中 + å®¡æ‰¹é€šè¿‡ï¼‰
            BigDecimal orderTotalPrice = zeroIfNull(saleOrder.getTotalPrice());
            BigDecimal orderInvoicedPrice = sumInvoicePrice(invoiceMapper.selectListBySaleOrderIdAndStatus(
                    reqVO.getSaleOrderId(), INVOICE_OCCUPY_STATUSES), reqVO.getId());
            BigDecimal orderRemainPrice = orderTotalPrice.subtract(orderInvoicedPrice);
            if (reqVO.getPrice() != null && reqVO.getPrice().compareTo(orderRemainPrice) > 0) {
                throw exception(INVOICE_CREATE_FAIL_PRICE_EXCEEDS_SALE_ORDER, saleOrder.getNo(),
                        formatPrice(orderRemainPrice), formatPrice(orderTotalPrice), formatPrice(orderInvoicedPrice));
            }
        }
        // 2. å¼€ç¥¨ç”³è¯·å¿…须绑定已完成的销售出库单
        // 2. é”€å”®å‡ºåº“单(选填)
        if (reqVO.getSaleOutId() == null) {
            throw exception(INVOICE_CREATE_FAIL_SALE_OUT_REQUIRED);
            return;
        }
        MesWmProductSalesRespDTO saleOut = productSalesApi.getProductSales(reqVO.getSaleOutId());
        if (saleOut == null) {
@@ -483,7 +484,8 @@
            throw exception(INVOICE_CREATE_FAIL_SALE_OUT_NOT_FINISHED, saleOut.getCode(),
                    productSalesStatusName(saleOut.getStatus()));
        }
        if (saleOut.getSaleOrderId() != null && ObjectUtil.notEqual(saleOut.getSaleOrderId(), reqVO.getSaleOrderId())) {
        if (saleOrder != null && saleOut.getSaleOrderId() != null
                && ObjectUtil.notEqual(saleOut.getSaleOrderId(), reqVO.getSaleOrderId())) {
            throw exception(INVOICE_CREATE_FAIL_SALE_OUT_MISMATCH_ORDER, saleOut.getCode(),
                    saleOrder.getNo(), saleOut.getSaleOrderId());
        }
yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/sale/dto/ErpSaleOrderCreateReqDTO.java
@@ -61,6 +61,9 @@
        @Schema(description = "税率,百分比", example = "10")
        private BigDecimal taxPercent;
        @Schema(description = "是否需要生产(0=否 1=是)", example = "1")
        private Integer needProduction;
        @Schema(description = "备注", example = "备注")
        private String remark;
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -99,7 +99,7 @@
    }
    @PostMapping("/submit")
    @Operation(summary = "提交销售订单审批(协同办公)")
    @Operation(summary = "提交销售订单(合同转来的订单免审批直接生效,其余走协同办公审批)")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:update-status')")
    public CommonResult<Boolean> submitSaleOrder(@RequestParam("id") Long id,
                                                 @RequestParam("processDefinitionKey") String processDefinitionKey) {
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -415,7 +415,24 @@
            throw exception(SALE_ORDER_SUBMIT_FAIL_ITEM_EMPTY, saleOrder.getNo());
        }
        // 4. åˆ›å»º BPM æµç¨‹å®žä¾‹ï¼ˆå®¡æ‰¹åœ¨ååŒåŠžå…¬å®Œæˆï¼‰
        // 4. åˆåŒè½¬æ¥çš„销售订单(contractId éžç©ºï¼‰æ— éœ€å†èµ°å®¡æ‰¹ï¼š
        //    åˆåŒåœ¨ CRM å·²å®¡æ‰¹é€šè¿‡ï¼Œè®¢å•内容由合同带入,提交即生效。
        //    ç”Ÿæ•ˆåŽï¼šéœ€è¦ç”Ÿäº§çš„æ˜Žç»†è‡ªåŠ¨åˆ›å»º MPS æµè½¬ç”Ÿäº§æ¨¡å—;无需生产的明细可直接在仓储生成发货通知单。
        //    åŽ†å²åˆåŒç”Ÿæˆçš„è®¢å•æ˜Žç»†å¯èƒ½æœªç»´æŠ¤â€œæ˜¯å¦éœ€è¦ç”Ÿäº§â€ï¼ŒæŒ‰ç³»ç»Ÿé»˜è®¤ï¼ˆéœ€è¦ç”Ÿäº§ï¼‰è¡¥é½åŽå†ç”Ÿæ•ˆã€‚
        if (saleOrder.getContractId() != null) {
            items.stream()
                    .filter(item -> item.getNeedProduction() == null)
                    .forEach(item -> saleOrderItemMapper.updateById(
                            new ErpSaleOrderItemDO().setId(item.getId()).setNeedProduction(1)));
            saleOrderMapper.updateById(new ErpSaleOrderDO()
                    .setId(id)
                    .setStatus(ErpAuditStatus.APPROVING.getStatus())
                    .setProcessInstanceId(""));
            updateSaleOrderAuditStatus(id, cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus());
            return;
        }
        // 5. åˆ›å»º BPM æµç¨‹å®žä¾‹ï¼ˆå®¡æ‰¹åœ¨ååŒåŠžå…¬å®Œæˆï¼‰
        // å…¨å±€å…å®¡æ‰¹ï¼šä¸šåŠ¡æ ¡éªŒé€šè¿‡åŽç›´æŽ¥æ‰§è¡Œå®¡æ‰¹é€šè¿‡é€»è¾‘ï¼Œæ— éœ€æµç¨‹æ¨¡åž‹ã€‚
        if (processInstanceApi.isGlobalAutoApprove()) {
            saleOrderMapper.updateById(new ErpSaleOrderDO()
@@ -431,7 +448,7 @@
                        .setProcessDefinitionKey(processDefinitionKey)
                        .setBusinessKey(String.valueOf(id)));
        // 5. æ›´æ–°é”€å”®è®¢å•状态为审批中,记录流程实例编号
        // 6. æ›´æ–°é”€å”®è®¢å•状态为审批中,记录流程实例编号
        saleOrderMapper.updateById(new ErpSaleOrderDO()
                .setId(id)
                .setStatus(ErpAuditStatus.APPROVING.getStatus())
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/HrmEmployeeApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.hrm.api.employee;
import cn.iocoder.yudao.module.hrm.api.employee.dto.HrmEmployeeSimpleRespDTO;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
 * å‘˜å·¥ä¿¡æ¯ API æŽ¥å£
 * <p>
 * ä¾› MES ç­‰æ¨¡å—按岗位获取员工(报工人选择)使用。
 * å‘˜å·¥æ•°æ®æ¥è‡ªã€Œå‘˜å·¥ç®¡ç†ã€ï¼Œä¸è¦æ±‚员工开通系统账号
 *
 * @author è¶…级管理员
 */
public interface HrmEmployeeApi {
    /**
     * èŽ·å¾—æŒ‡å®šå²—ä½ä¸‹çš„åœ¨èŒ/试用员工列表
     *
     * @param postIds å²—位编号集合
     * @return å‘˜å·¥åˆ—表
     */
    List<HrmEmployeeSimpleRespDTO> getEmployeeListByPostIds(Collection<Long> postIds);
    /**
     * èŽ·å¾—å‘˜å·¥ Map
     *
     * @param ids å‘˜å·¥ç¼–号集合
     * @return å‘˜å·¥ Map
     */
    Map<Long, HrmEmployeeSimpleRespDTO> getEmployeeMap(Collection<Long> ids);
    /**
     * æ ¹æ®ç³»ç»Ÿç”¨æˆ·ç¼–号获得员工
     *
     * @param userId ç³»ç»Ÿç”¨æˆ·ç¼–号
     * @return å‘˜å·¥ï¼Œä¸å­˜åœ¨æ—¶è¿”回 null
     */
    HrmEmployeeSimpleRespDTO getEmployeeByUserId(Long userId);
}
yudao-module-hrm-api/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/dto/HrmEmployeeSimpleRespDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.hrm.api.employee.dto;
import lombok.Data;
import java.util.List;
/**
 * å‘˜å·¥ç²¾ç®€ä¿¡æ¯ DTO
 * <p>
 * ä¾› MES ç­‰æ¨¡å—按岗位获取员工(报工人选择)使用,不依赖员工是否开通系统账号
 *
 * @author è¶…级管理员
 */
@Data
public class HrmEmployeeSimpleRespDTO {
    /**
     * å‘˜å·¥ç¼–号
     */
    private Long id;
    /**
     * å‘˜å·¥å§“名
     */
    private String name;
    /**
     * éƒ¨é—¨ç¼–号
     */
    private Long deptId;
    /**
     * æ‰‹æœºå·
     */
    private String phone;
    /**
     * å‘˜å·¥çŠ¶æ€ï¼š1-在职 2-试用 3-离职
     */
    private Integer employeeStatus;
    /**
     * å…³è”的系统用户编号(未开通账号时为空)
     */
    private Long userId;
    /**
     * å²—位编号列表
     */
    private List<Long> postIds;
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/api/employee/HrmEmployeeApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.hrm.api.employee;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.hrm.api.employee.dto.HrmEmployeeSimpleRespDTO;
import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO;
import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
 * å‘˜å·¥ä¿¡æ¯ API å®žçް
 *
 * @author è¶…级管理员
 */
@Service
public class HrmEmployeeApiImpl implements HrmEmployeeApi {
    @Resource
    private HrmEmployeeService employeeService;
    @Override
    public List<HrmEmployeeSimpleRespDTO> getEmployeeListByPostIds(Collection<Long> postIds) {
        return convertList(employeeService.getEmployeeListByPostIds(postIds));
    }
    @Override
    public Map<Long, HrmEmployeeSimpleRespDTO> getEmployeeMap(Collection<Long> ids) {
        return CollectionUtils.convertMap(convertList(employeeService.getEmployeeList(ids)),
                HrmEmployeeSimpleRespDTO::getId);
    }
    @Override
    public HrmEmployeeSimpleRespDTO getEmployeeByUserId(Long userId) {
        if (userId == null) {
            return null;
        }
        HrmEmployeeDO employee = employeeService.getEmployeeByUserId(userId);
        return employee == null ? null : buildDTO(employee);
    }
    private List<HrmEmployeeSimpleRespDTO> convertList(List<HrmEmployeeDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        return CollectionUtils.convertList(list, this::buildDTO);
    }
    private HrmEmployeeSimpleRespDTO buildDTO(HrmEmployeeDO employee) {
        HrmEmployeeSimpleRespDTO dto = BeanUtils.toBean(employee, HrmEmployeeSimpleRespDTO.class);
        if (StrUtil.isNotBlank(employee.getPostIds())) {
            try {
                dto.setPostIds(JSONUtil.toList(employee.getPostIds(), Long.class));
            } catch (Exception ignored) {
                // åŽ†å²è„æ•°æ®ï¼šå²—ä½ JSON è§£æžå¤±è´¥æ—¶å¿½ç•¥ï¼Œä¸å½±å“å‘˜å·¥æœ¬èº«è¿”回
            }
        }
        return dto;
    }
}
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/dal/mysql/employee/HrmEmployeeMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.hrm.dal.mysql.employee;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
@@ -11,7 +12,9 @@
import java.time.LocalDate;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
 * å‘˜å·¥ä¿¡æ¯ Mapper
@@ -58,6 +61,27 @@
        return selectList(HrmEmployeeDO::getEmployeeStatus, employeeStatus);
    }
    /**
     * æŒ‰å²—位编号集合查询在职/试用员工
     * <p>
     * å‘˜å·¥è¡¨çš„ post_ids å­˜çš„æ˜¯ JSON æ•°ç»„(如 [17, 18]),使用 JSON_CONTAINS ç²¾ç¡®åŒ¹é…å²—位
     *
     * @param postIds å²—位编号集合
     * @return å‘˜å·¥åˆ—表
     */
    default List<HrmEmployeeDO> selectListByPostIds(Collection<Long> postIds) {
        if (CollUtil.isEmpty(postIds)) {
            return Collections.emptyList();
        }
        String postCondition = postIds.stream()
                .map(postId -> "JSON_CONTAINS(post_ids, '[" + postId + "]')")
                .collect(Collectors.joining(" OR "));
        return selectList(new LambdaQueryWrapperX<HrmEmployeeDO>()
                .in(HrmEmployeeDO::getEmployeeStatus, List.of(1, 2))
                .apply("(" + postCondition + ")")
                .orderByAsc(HrmEmployeeDO::getName));
    }
    default PageResult<HrmEmployeeDO> selectPage(HrmEmployeePageReqVO reqVO) {
        LambdaQueryWrapperX<HrmEmployeeDO> wrapper = new LambdaQueryWrapperX<HrmEmployeeDO>()
                .likeIfPresent(HrmEmployeeDO::getEmployeeNo, reqVO.getEmployeeNo())
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeService.java
@@ -85,6 +85,14 @@
    List<HrmEmployeeDO> getEmployeeList(Collection<Long> ids);
    /**
     * èŽ·å¾—æŒ‡å®šå²—ä½ä¸‹çš„åœ¨èŒ/试用员工列表
     *
     * @param postIds å²—位编号集合
     * @return å‘˜å·¥åˆ—表
     */
    List<HrmEmployeeDO> getEmployeeListByPostIds(Collection<Long> postIds);
    /**
     * èŽ·å¾—æŒ‡å®šçŠ¶æ€çš„å‘˜å·¥åˆ—è¡¨
     *
     * @param employeeStatus å‘˜å·¥çŠ¶æ€ï¼Œä¸º null æ—¶æŸ¥è¯¢å…¨éƒ¨
yudao-module-hrm/src/main/java/cn/iocoder/yudao/module/hrm/service/employee/HrmEmployeeServiceImpl.java
@@ -368,6 +368,11 @@
    }
    @Override
    public List<HrmEmployeeDO> getEmployeeListByPostIds(Collection<Long> postIds) {
        return employeeMapper.selectListByPostIds(postIds);
    }
    @Override
    public List<HrmEmployeeSimpleRespVO> getEmployeeSimpleList(Integer employeeStatus) {
        List<HrmEmployeeDO> list = employeeMapper.selectListByStatus(employeeStatus);
        // å¡«å……部门名称
yudao-module-mes/pom.xml
@@ -45,6 +45,13 @@
            <version>${revision}</version>
        </dependency>
        <!-- HRM äººåŠ›èµ„æº API(报工人按员工数据选择) -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-hrm-api</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- BPM å·¥ä½œæµ API -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/MesMdProductBomController.java
@@ -1,10 +1,17 @@
package cn.iocoder.yudao.module.mes.controller.admin.md.item;
import cn.hutool.core.collection.CollUtil;
import cn.idev.excel.ExcelWriter;
import cn.idev.excel.FastExcelFactory;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.http.HttpUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomSaveReqVO;
@@ -18,13 +25,19 @@
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -96,6 +109,45 @@
        return success(buildProductBomRespVOList(list));
    }
    @GetMapping("/get-import-template")
    @Operation(summary = "获得产品BOM导入模板")
    @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
    public void importTemplate(HttpServletResponse response) throws IOException {
        List<MesMdProductBomImportExcelVO> list = Arrays.asList(
                MesMdProductBomImportExcelVO.builder().parentItemCode("BV-2.5").bomItemCode("CU-T1-8")
                        .quantity(new BigDecimal("0.0227")).remark("示例:填写已存在的物料编码").build(),
                MesMdProductBomImportExcelVO.builder().parentItemCode("BV-2.5").bomItemCode("PVC-JY-70")
                        .quantity(new BigDecimal("0.0103")).remark("示例:同一父项可有多行子项").build()
        );
        // ä¸èƒ½å¤ç”¨ ExcelUtils.write:它会在表头前加 "*" å¿…填标记,导入时按表头名称匹配会读不到该列
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8("BOM导入模板.xlsx"));
        ExcelWriter writer = FastExcelFactory.write(response.getOutputStream(), MesMdProductBomImportExcelVO.class)
                .autoCloseStream(false)
                .registerWriteHandler(new ColumnWidthMatchStyleStrategy())
                .build();
        try {
            writer.write(list, FastExcelFactory.writerSheet("产品BOM").build());
        } finally {
            writer.finish();
            writer.close();
        }
    }
    @PostMapping("/import")
    @Operation(summary = "导入产品BOM")
    @Parameters({
            @Parameter(name = "file", description = "Excel æ–‡ä»¶", required = true),
            @Parameter(name = "updateSupport", description = "是否更新已存在的父子组合,默认为 false", example = "true")
    })
    @PreAuthorize("@ss.hasPermission('mes:md-item:create')")
    public CommonResult<MesMdProductBomImportRespVO> importExcel(
            @RequestParam("file") MultipartFile file,
            @RequestParam(value = "updateSupport", required = false, defaultValue = "false") Boolean updateSupport) throws Exception {
        List<MesMdProductBomImportExcelVO> list = ExcelUtils.read(file, MesMdProductBomImportExcelVO.class);
        return success(productBomService.importProductBomList(list, updateSupport));
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesMdProductBomRespVO> buildProductBomRespVOList(List<MesMdProductBomDO> list) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/bom/MesMdProductBomImportExcelVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom;
import cn.idev.excel.annotation.ExcelProperty;
import cn.iocoder.yudao.framework.excel.core.handler.ExcelRequired;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
 * MES äº§å“ BOM å¯¼å…¥ Excel VO
 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class MesMdProductBomImportExcelVO {
    @ExcelProperty("父项物料编码")
    @ExcelRequired
    private String parentItemCode;
    @ExcelProperty("子项物料编码")
    @ExcelRequired
    private String bomItemCode;
    @ExcelProperty("单位用量")
    @ExcelRequired
    private BigDecimal quantity;
    @ExcelProperty("备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/item/vo/bom/MesMdProductBomImportRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Builder;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - MES äº§å“ BOM å¯¼å…¥ Response VO")
@Data
@Builder
public class MesMdProductBomImportRespVO {
    @Schema(description = "创建成功的 BOM ç»„合数组,格式:父项编码→子项编码", requiredMode = Schema.RequiredMode.REQUIRED)
    private List<String> createList;
    @Schema(description = "更新成功的 BOM ç»„合数组,格式:父项编码→子项编码", requiredMode = Schema.RequiredMode.REQUIRED)
    private List<String> updateList;
    @Schema(description = "导入失败的组合集合,key ä¸ºâ€œç¬¬N行 çˆ¶é¡¹ç¼–码→子项编码”,value ä¸ºå¤±è´¥åŽŸå› ", requiredMode = Schema.RequiredMode.REQUIRED)
    private Map<String, String> failureList;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/workstation/MesMdWorkstationWorkerController.java
@@ -4,14 +4,15 @@
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.hrm.api.employee.HrmEmployeeApi;
import cn.iocoder.yudao.module.hrm.api.employee.dto.HrmEmployeeSimpleRespDTO;
import cn.iocoder.yudao.module.mes.controller.admin.md.workstation.vo.worker.MesMdWorkstationEmployeeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.workstation.vo.worker.MesMdWorkstationWorkerRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.workstation.vo.worker.MesMdWorkstationWorkerSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationWorkerDO;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationWorkerService;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.dept.dto.PostRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
@@ -21,6 +22,7 @@
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -41,7 +43,7 @@
    private PostApi postApi;
    @Resource
    private AdminUserApi adminUserApi;
    private HrmEmployeeApi hrmEmployeeApi;
    @PostMapping("/create")
    @Operation(summary = "创建人力资源")
@@ -77,9 +79,9 @@
    }
    @GetMapping("/list-user-by-workstation")
    @Operation(summary = "获得工作站对应岗位的员工列表")
    @Operation(summary = "获得工作站对应岗位的员工列表(取自员工管理数据,不要求开通系统账号)")
    @Parameter(name = "workstationId", description = "工作站编号", required = true)
    public CommonResult<List<AdminUserRespDTO>> getUserListByWorkstation(
    public CommonResult<List<MesMdWorkstationEmployeeRespVO>> getUserListByWorkstation(
            @RequestParam("workstationId") Long workstationId) {
        List<MesMdWorkstationWorkerDO> list = workstationWorkerService.getWorkstationWorkerListByWorkstationId(workstationId);
        if (CollUtil.isEmpty(list)) {
@@ -89,8 +91,17 @@
        if (CollUtil.isEmpty(postIds)) {
            return success(Collections.emptyList());
        }
        List<AdminUserRespDTO> userList = adminUserApi.getUserListByPostIds(postIds);
        return success(userList);
        List<HrmEmployeeSimpleRespDTO> employees = hrmEmployeeApi.getEmployeeListByPostIds(postIds);
        List<MesMdWorkstationEmployeeRespVO> result = new ArrayList<>(employees.size());
        for (HrmEmployeeSimpleRespDTO employee : employees) {
            MesMdWorkstationEmployeeRespVO vo = new MesMdWorkstationEmployeeRespVO();
            vo.setId(employee.getId());
            vo.setNickname(employee.getName());
            vo.setDeptId(employee.getDeptId());
            vo.setPhone(employee.getPhone());
            result.add(vo);
        }
        return success(result);
    }
    // ==================== æ‹¼æŽ¥ VO ====================
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/workstation/vo/worker/MesMdWorkstationEmployeeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.controller.admin.md.workstation.vo.worker;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
 * å·¥ä½œç«™å¯é€‰å‘˜å·¥ï¼ˆæŠ¥å·¥äººï¼‰Response VO
 * <p>
 * æ•°æ®æ¥è‡ªã€Œå‘˜å·¥ç®¡ç†ã€ï¼ŒæŒ‰å·¥ä½œç«™ç»‘定的岗位匹配员工,不要求员工开通系统账号。
 * å­—段保持 nickname å‘½åä»¥å…¼å®¹å‰ç«¯é€‰æ‹©å™¨ï¼ˆlabelField=nickname / valueField=id)
 */
@Schema(description = "管理后台 - å·¥ä½œç«™å¯é€‰å‘˜å·¥ Response VO")
@Data
public class MesMdWorkstationEmployeeRespVO {
    @Schema(description = "员工编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "员工姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王海峰")
    private String nickname;
    @Schema(description = "部门编号", example = "126")
    private Long deptId;
    @Schema(description = "手机号", example = "13700002101")
    private String phone;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -55,6 +55,8 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskPauseRecordRespVO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.module.hrm.api.employee.HrmEmployeeApi;
import cn.iocoder.yudao.module.hrm.api.employee.dto.HrmEmployeeSimpleRespDTO;
import java.io.IOException;
import java.math.BigDecimal;
@@ -79,8 +81,16 @@
    @Operation(summary = "获得各工作站未完成任务数量")
    public CommonResult<List<cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProWorkstationPendingCountVO>> getPendingWorkstationCounts() {
        Long userId = cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId();
        Long assigneeId = userId != null && !permissionApi.hasAnyRoles(userId,
                cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum.SUPER_ADMIN.getCode()) ? userId : null;
        // æŠ¥å·¥äººä»¥ã€Œå‘˜å·¥ç®¡ç†ã€ä¸­çš„员工为准:登录账号先映射到员工,再按员工编号过滤
        Long assigneeId = null;
        if (userId != null && !permissionApi.hasAnyRoles(userId,
                cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum.SUPER_ADMIN.getCode())) {
            HrmEmployeeSimpleRespDTO employee = hrmEmployeeApi.getEmployeeByUserId(userId);
            if (employee == null) {
                return success(Collections.emptyList());
            }
            assigneeId = employee.getId();
        }
        return success(pendingCountMapper.selectPendingCounts(assigneeId,
                cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum.PREPARE.getStatus(),
                cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum.PRODUCING.getStatus(),
@@ -129,6 +139,8 @@
    private MesProTaskPauseRecordMapper pauseRecordMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private HrmEmployeeApi hrmEmployeeApi;
    @Resource
    private cn.iocoder.yudao.module.system.api.permission.PermissionApi permissionApi;
@@ -224,9 +236,13 @@
    @Operation(summary = "获得待报工台账分页", description = "已排产且仍有待报工数量的任务(状态=进行中)")
    public CommonResult<PageResult<MesProTaskRespVO>> getPendingFeedbackPage(@Valid MesProTaskPageReqVO pageReqVO) {
        Long loginUserId = cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId();
        // å¦‚果当前登录人不是超级管理员,则仅查询分配给自己的生产工单
        // å¦‚果当前登录人不是超级管理员,则仅查询分配给自己的生产工单(登录账号先映射到员工)
        if (loginUserId != null && !permissionApi.hasAnyRoles(loginUserId, cn.iocoder.yudao.module.system.enums.permission.RoleCodeEnum.SUPER_ADMIN.getCode())) {
            pageReqVO.setAssigneeUserId(loginUserId);
            HrmEmployeeSimpleRespDTO employee = hrmEmployeeApi.getEmployeeByUserId(loginUserId);
            if (employee == null) {
                return success(PageResult.empty());
            }
            pageReqVO.setAssigneeUserId(employee.getId());
        }
        PageResult<MesProTaskDO> pageResult = taskService.getPendingFeedbackPage(pageReqVO);
        List<MesProTaskRespVO> respVOList = buildTaskRespVOList(pageResult.getList());
@@ -397,7 +413,8 @@
        Set<Long> allAssigneeUserIds = new HashSet<>();
        assigneeMap.values().forEach(assignees -> 
            assignees.forEach(assignee -> allAssigneeUserIds.add(assignee.getUserId())));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(allAssigneeUserIds);
        // æŠ¥å·¥äººå­˜çš„æ˜¯å‘˜å·¥ç¼–号,名称从「员工管理」取
        Map<Long, HrmEmployeeSimpleRespDTO> employeeMap = hrmEmployeeApi.getEmployeeMap(allAssigneeUserIds);
        // æ‹¼æŽ¥ VO
        return convertList(list, task -> {
@@ -436,10 +453,10 @@
                List<Long> assigneeUserIds = convertList(assignees, cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskAssigneeDO::getUserId);
                vo.setAssigneeUserIds(assigneeUserIds);
                List<String> assigneeUserNames = new ArrayList<>();
                for (Long userId : assigneeUserIds) {
                    AdminUserRespDTO user = userMap.get(userId);
                    if (user != null && user.getNickname() != null) {
                        assigneeUserNames.add(user.getNickname());
                for (Long employeeId : assigneeUserIds) {
                    HrmEmployeeSimpleRespDTO employee = employeeMap.get(employeeId);
                    if (employee != null && employee.getName() != null) {
                        assigneeUserNames.add(employee.getName());
                    }
                }
                vo.setAssigneeUserNames(assigneeUserNames);
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/MesQcNcrController.java
@@ -80,10 +80,8 @@
    @PutMapping("/dispose")
    @Operation(summary = "执行不合格品处置")
    @PreAuthorize("@ss.hasPermission('mes:qc-ncr:dispose')")
    public CommonResult<Boolean> disposeNcr(@Valid @RequestBody MesQcNcrDisposeReqVO disposeReqVO) {
        ncrService.disposeNcr(disposeReqVO.getId(), disposeReqVO.getDisposition(),
                disposeReqVO.getHandlerUserId(), disposeReqVO.getHandleResult());
        return success(true);
    public CommonResult<MesQcNcrDisposeRespVO> disposeNcr(@Valid @RequestBody MesQcNcrDisposeReqVO disposeReqVO) {
        return success(ncrService.disposeNcr(disposeReqVO));
    }
    @PutMapping("/submit-approval")
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/vo/MesQcNcrDisposeReqVO.java
@@ -22,4 +22,16 @@
    @Schema(description = "处理结果说明", example = "已完成报废出库")
    private String handleResult;
    @Schema(description = "入库仓库编号(处置为让步放行/报废时必填)", example = "7001")
    private Long warehouseId;
    @Schema(description = "入库库区编号(处置为让步放行/报废时必填)", example = "7101")
    private Long locationId;
    @Schema(description = "入库库位编号(处置为让步放行/报废时必填)", example = "7201")
    private Long areaId;
    @Schema(description = "批次号(物料启用批次管理时必填,不存在会自动创建批次)", example = "BATCH20261109001")
    private String batchCode;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/ncr/vo/MesQcNcrDisposeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - MES ä¸åˆæ ¼å“å¤„理单处置 Response VO")
@Data
public class MesQcNcrDisposeRespVO {
    @Schema(description = "处置方式(2-报废 3-返工 4-让步放行 6-返修)", example = "4")
    private Integer disposition;
    @Schema(description = "生成的单据类型:MISC_RECEIPT-杂项入库单,WORK_ORDER-生产订单", example = "MISC_RECEIPT")
    private String docType;
    @Schema(description = "生成的单据编号", example = "1024")
    private Long docId;
    @Schema(description = "生成的单据单号", example = "MR202611090001")
    private String docCode;
    @Schema(description = "生成的单据名称", example = "让步放行入库单-NCR20261109001")
    private String docName;
    @Schema(description = "提示文案", example = "已生成让步放行入库单[MR202611090001]并完成入库")
    private String message;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -106,6 +106,7 @@
    ErrorCode MD_PRODUCT_BOM_SELF_REFERENCE = new ErrorCode(1_040_107_001, "产品不能作为自身的BOM物料");
    ErrorCode MD_PRODUCT_BOM_CIRCULAR = new ErrorCode(1_040_107_002, "BOM物料存在闭环,无法新增");
    ErrorCode MD_PRODUCT_BOM_ITEM_INVALID = new ErrorCode(1_040_107_003, "选择的 BOM ç‰©æ–™ä¸å±žäºŽå½“前产品");
    ErrorCode MD_PRODUCT_BOM_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_040_107_004, "导入的 BOM æ•°æ®ä¸èƒ½ä¸ºç©º");
    // ========== MES åŸºç¡€æ•°æ®-产品SOP(1-040-108-000) ==========
    ErrorCode MD_PRODUCT_SOP_NOT_EXISTS = new ErrorCode(1_040_108_000, "产品SOP不存在");
@@ -511,6 +512,11 @@
    ErrorCode QC_NCR_DISPOSE_STATUS_INVALID = new ErrorCode(1_040_609_007, "不合格品处理单[{}]当前状态为[{}],只有审批通过后的[已评审]状态才可执行处置;请先提交审批并等待审批通过");
    ErrorCode QC_NCR_DISPOSITION_REQUIRED = new ErrorCode(1_040_609_008, "处置方式不能为空,请从[让步放行、返修、返工、报废]中选择一项");
    ErrorCode QC_NCR_DISPOSITION_INVALID = new ErrorCode(1_040_609_009, "处置方式[{}]不在允许范围内,请从[让步放行、返修、返工、报废]中选择一项");
    ErrorCode QC_NCR_DISPOSE_STORAGE_REQUIRED = new ErrorCode(1_040_609_011, "处置方式[{}]需要入库到指定库位:当前仓库[{}]、库区[{}]、库位[{}]未选全,请回到处置弹窗补选缺失项后重新提交");
    ErrorCode QC_NCR_DISPOSE_BATCH_REQUIRED = new ErrorCode(1_040_609_012, "物料[{}]启用了批次管理,处置方式[{}]入库时必须填写批次号;请在处置弹窗的[批次号]中填写后重试");
    ErrorCode QC_NCR_DISPOSE_QUANTITY_INVALID = new ErrorCode(1_040_609_013, "不合格数量[{}]必须大于 0 æ‰èƒ½ç”Ÿæˆå…¥åº“单/生产订单,请先在检验单中修正不合格数量");
    ErrorCode QC_NCR_DISPOSE_STORAGE_DISABLED = new ErrorCode(1_040_609_014, "处置方式[{}]所选库位[{}]不可用:仓库[{}]、库区[{}]与库位[{}]的归属关系不一致,请回到处置弹窗重新选择仓库/库区/库位");
    ErrorCode QC_NCR_DISPOSE_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_609_015, "物料[{}]在 MES äº§å“ä¸»æ•°æ®ä¸­ä¸å­˜åœ¨ï¼ˆMDM ç‰©æ–™ ID={}),无法生成返工/返修生产订单;请先在[物料产品]中维护该产品后再处置");
    // ========== MES ä»“库管理-仓库(1-040-700-000) ==========
    ErrorCode WM_WAREHOUSE_NOT_EXISTS = new ErrorCode(1_040_700_000, "仓库不存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/MesWmMiscReceiptTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.enums.wm;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES æ‚项入库单类型枚举
 * <p>
 * ä¸Žå­—å…¸ {@code mes_wm_misc_receipt_type} ä¿æŒä¸€è‡´
 */
@Getter
@AllArgsConstructor
public enum MesWmMiscReceiptTypeEnum implements ArrayValuable<Integer> {
    ADJUST(1, "库存调整"),
    CONCESSION(2, "让步放行入库"),
    SCRAP(3, "报废入库");
    private final Integer type;
    private final String name;
    @Override
    public Integer[] array() {
        return Arrays.stream(values()).map(MesWmMiscReceiptTypeEnum::getType).toArray(Integer[]::new);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomService.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.mes.service.md.item;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
@@ -77,4 +79,14 @@
     */
    void deleteProductBomByItemId(Long itemId);
    /**
     * å¯¼å…¥äº§å“ BOM(按父项/子项物料编码匹配,已存在的父子组合默认跳过,支持勾选更新用量)
     *
     * @param importList       å¯¼å…¥æ•°æ®
     * @param updateSupport    æ˜¯å¦æ›´æ–°å·²å­˜åœ¨çš„父子组合
     * @return å¯¼å…¥ç»“æžœ
     */
    MesMdProductBomImportRespVO importProductBomList(List<MesMdProductBomImportExcelVO> importList,
                                                     Boolean updateSupport);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdProductBomServiceImpl.java
@@ -1,10 +1,15 @@
package cn.iocoder.yudao.module.mes.service.md.item;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportExcelVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomImportRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.md.item.vo.bom.MesMdProductBomSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdProductBomDO;
import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdProductBomMapper;
import jakarta.annotation.Resource;
@@ -141,4 +146,132 @@
        productBomMapper.deleteByItemId(itemId);
    }
    @Override
    public MesMdProductBomImportRespVO importProductBomList(List<MesMdProductBomImportExcelVO> importList,
                                                            Boolean updateSupport) {
        if (CollUtil.isEmpty(importList)) {
            throw exception(MD_PRODUCT_BOM_IMPORT_LIST_IS_EMPTY);
        }
        MesMdProductBomImportRespVO respVO = MesMdProductBomImportRespVO.builder()
                .createList(new ArrayList<>()).updateList(new ArrayList<>())
                .failureList(new LinkedHashMap<>()).build();
        // 1. çŽ°æœ‰ BOM çš„父子组合索引 + é‚»æŽ¥è¡¨ï¼ˆç”¨äºŽåˆ¤é‡å’Œé—­çŽ¯æ£€æµ‹ï¼‰
        Map<String, MesMdProductBomDO> existingPairMap = new HashMap<>();
        Map<Long, Set<Long>> graph = new HashMap<>();
        for (MesMdProductBomDO bom : productBomMapper.selectAll()) {
            existingPairMap.put(pairKey(bom.getItemId(), bom.getBomItemId()), bom);
            graph.computeIfAbsent(bom.getItemId(), key -> new HashSet<>()).add(bom.getBomItemId());
        }
        Map<String, Optional<MesMdItemDO>> itemCache = new HashMap<>();
        Set<String> filePairs = new HashSet<>();
        // 2. é€è¡Œæ ¡éªŒ + å¯¼å…¥
        int rowNo = 0;
        for (MesMdProductBomImportExcelVO importVO : importList) {
            rowNo++;
            String parentCode = StrUtil.trim(importVO.getParentItemCode());
            String componentCode = StrUtil.trim(importVO.getBomItemCode());
            String rowKey = "第" + rowNo + "行 " + StrUtil.blankToDefault(parentCode, "(空父项)")
                    + "→" + StrUtil.blankToDefault(componentCode, "(空子项)");
            // 2.1 åŸºç¡€æ ¡éªŒ
            if (StrUtil.isBlank(parentCode) || StrUtil.isBlank(componentCode)) {
                respVO.getFailureList().put("第" + rowNo + "行",
                        StrUtil.isBlank(parentCode) && StrUtil.isBlank(componentCode) ? "父项物料编码和子项物料编码都不能为空"
                                : StrUtil.isBlank(parentCode) ? "父项物料编码不能为空" : "子项物料编码不能为空");
                continue;
            }
            if (importVO.getQuantity() == null || importVO.getQuantity().signum() <= 0) {
                respVO.getFailureList().put(rowKey, "单位用量必须大于 0,当前为:"
                        + (importVO.getQuantity() == null ? "空" : importVO.getQuantity().toPlainString()));
                continue;
            }
            // 2.2 çˆ¶é¡¹ã€å­é¡¹å¿…须已在 MES ç‰©æ–™ä¸­ç»´æŠ¤
            MesMdItemDO parent = lookupItem(itemCache, parentCode);
            MesMdItemDO component = lookupItem(itemCache, componentCode);
            if (parent == null) {
                respVO.getFailureList().put(rowKey, "父项物料编码[" + parentCode
                        + "]在生产物料管理中不存在,请先维护该物料后再导入");
                continue;
            }
            if (component == null) {
                respVO.getFailureList().put(rowKey, "子项物料编码[" + componentCode
                        + "]在生产物料管理中不存在,请先维护该物料后再导入");
                continue;
            }
            if (parent.getId().equals(component.getId())) {
                respVO.getFailureList().put(rowKey, "父项与子项不能是同一物料[" + parentCode + "]");
                continue;
            }
            String pairKey = pairKey(parent.getId(), component.getId());
            // 2.3 å·²å­˜åœ¨çš„父子组合:默认跳过,勾选“支持更新”时只更新用量/备注(状态保持原值)
            MesMdProductBomDO existing = existingPairMap.get(pairKey);
            if (existing != null) {
                if (!Boolean.TRUE.equals(updateSupport)) {
                    respVO.getFailureList().put(rowKey, "该父子组合已存在 BOM,本次未更新;"
                            + "如需覆盖用量请勾选“支持更新”后重新导入");
                    continue;
                }
                MesMdProductBomDO updateObj = new MesMdProductBomDO().setId(existing.getId())
                        .setQuantity(importVO.getQuantity())
                        .setRemark(StrUtil.blankToDefault(importVO.getRemark(), existing.getRemark()));
                productBomMapper.updateById(updateObj);
                respVO.getUpdateList().add(parentCode + "→" + componentCode);
                continue;
            }
            // 2.4 æ–‡ä»¶å†…重复
            if (filePairs.contains(pairKey)) {
                respVO.getFailureList().put(rowKey, "同一父子组合在文件中重复出现,请合并为一行后重新导入");
                continue;
            }
            // 2.5 é—­çŽ¯æ£€æµ‹ï¼ˆåŸºäºŽçŽ°æœ‰ BOM + æœ¬æ–‡ä»¶å·²æŽ¥å—的行)
            if (reaches(graph, component.getId(), parent.getId())) {
                respVO.getFailureList().put(rowKey, "新增后 BOM å°†å½¢æˆå¾ªçŽ¯å¼•ç”¨ï¼ˆ" + parentCode
                        + "→" + componentCode + "),请调整父子关系后重新导入");
                continue;
            }
            // 2.6 æ–°å¢ž
            MesMdProductBomSaveReqVO saveReqVO = new MesMdProductBomSaveReqVO();
            saveReqVO.setItemId(parent.getId());
            saveReqVO.setBomItemId(component.getId());
            saveReqVO.setQuantity(importVO.getQuantity());
            saveReqVO.setStatus(0); // å¯¼å…¥çš„ BOM é»˜è®¤å¯ç”¨
            saveReqVO.setRemark(StrUtil.blankToDefault(importVO.getRemark(), null));
            createProductBom(saveReqVO);
            filePairs.add(pairKey);
            graph.computeIfAbsent(parent.getId(), key -> new HashSet<>()).add(component.getId());
            respVO.getCreateList().add(parentCode + "→" + componentCode);
        }
        return respVO;
    }
    private MesMdItemDO lookupItem(Map<String, Optional<MesMdItemDO>> cache, String code) {
        return cache.computeIfAbsent(code,
                key -> Optional.ofNullable(itemService.getItemByCode(key))).orElse(null);
    }
    private static String pairKey(Long itemId, Long bomItemId) {
        return itemId + ":" + bomItemId;
    }
    /** æ²¿çŽ°æœ‰ï¼ˆå«æœ¬æ¬¡å·²æŽ¥å—çš„ï¼‰çˆ¶å­è¾¹ä»Ž start å‡ºå‘,判断能否到达 target */
    private static boolean reaches(Map<Long, Set<Long>> graph, Long start, Long target) {
        Deque<Long> stack = new ArrayDeque<>();
        Set<Long> visited = new HashSet<>();
        stack.push(start);
        while (!stack.isEmpty()) {
            Long node = stack.pop();
            if (!visited.add(node)) {
                continue;
            }
            if (node.equals(target)) {
                return true;
            }
            for (Long next : graph.getOrDefault(node, Set.of())) {
                stack.push(next);
            }
        }
        return false;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ncr/MesQcNcrService.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.mes.service.qc.ncr;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrDisposeReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrDisposeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO;
@@ -51,13 +53,18 @@
    /**
     * æ‰§è¡Œä¸åˆæ ¼å“å¤„ç½®
     * <p>
     * ä¸åŒå¤„置方式会联动生成业务单据并回写库存/生产:
     * <ul>
     *     <li>让步放行(4):生成杂项入库单(原材料仓),自动提交并完成入库</li>
     *     <li>报废(2):生成杂项入库单(不合格品仓),自动提交并完成入库</li>
     *     <li>返工(3)/返修(6):生成自行生产类型的生产订单(草稿,工艺路线由人工补充)</li>
     * </ul>
     *
     * @param id             ç¼–号
     * @param disposition    å¤„置方式(必填)
     * @param handlerUserId  å¤„理人ID
     * @param handleResult   å¤„理结果说明
     * @param disposeReqVO å¤„置信息(含入库仓库/库区/库位/批次号)
     * @return ç”Ÿæˆçš„业务单据信息
     */
    void disposeNcr(Long id, Integer disposition, Long handlerUserId, String handleResult);
    MesQcNcrDisposeRespVO disposeNcr(@Valid MesQcNcrDisposeReqVO disposeReqVO);
    /**
     * æäº¤ä¸åˆæ ¼å“å¤„理单审批
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/ncr/MesQcNcrServiceImpl.java
@@ -1,22 +1,49 @@
package cn.iocoder.yudao.module.mes.service.qc.ncr;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.exception.ServiceException;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrDisposeReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrDisposeRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.ncr.vo.MesQcNcrSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.miscreceipt.vo.MesWmMiscReceiptSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.miscreceipt.vo.line.MesWmMiscReceiptLineSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.ncr.MesQcNcrDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO;
import cn.iocoder.yudao.module.mes.dal.mysql.qc.ncr.MesQcNcrMapper;
import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesNcrDispositionEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesNcrStatusEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmMiscReceiptTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService;
import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService;
import cn.iocoder.yudao.module.mes.service.wm.miscreceipt.MesWmMiscReceiptLineService;
import cn.iocoder.yudao.module.mes.service.wm.miscreceipt.MesWmMiscReceiptService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
@@ -24,6 +51,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Map;
@@ -82,6 +110,42 @@
    @Resource
    @Lazy
    private MesQcRqcService rqcService;
    @Resource
    @Lazy
    private MesWmMiscReceiptService miscReceiptService;
    @Resource
    @Lazy
    private MesWmMiscReceiptLineService miscReceiptLineService;
    @Resource
    @Lazy
    private MesWmWarehouseService warehouseService;
    @Resource
    @Lazy
    private MesWmWarehouseLocationService warehouseLocationService;
    @Resource
    @Lazy
    private MesWmWarehouseAreaService warehouseAreaService;
    @Resource
    @Lazy
    private MesWmBatchService batchService;
    @Resource
    @Lazy
    private MesProWorkOrderService workOrderService;
    @Resource
    @Lazy
    private MesMdmItemService mdmItemService;
    @Resource
    @Lazy
    private MesMdItemService mesItemService;
    @Resource
    @Lazy
    private MesMdAutoCodeRecordService autoCodeRecordService;
    /**
     * æ‚项入库单来源单据类型:不合格品处理单
     */
    private static final String MISC_RECEIPT_SOURCE_TYPE_NCR = "QC_NCR";
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -150,24 +214,245 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void disposeNcr(Long id, Integer disposition, Long handlerUserId, String handleResult) {
    public MesQcNcrDisposeRespVO disposeNcr(MesQcNcrDisposeReqVO disposeReqVO) {
        // 1. æ ¡éªŒå­˜åœ¨ + å·²è¯„审(审批通过)状态
        MesQcNcrDO ncr = validateNcrExists(id);
        MesQcNcrDO ncr = validateNcrExists(disposeReqVO.getId());
        if (!MesNcrStatusEnum.REVIEWED.getStatus().equals(ncr.getStatus())) {
            throw exception(QC_NCR_DISPOSE_STATUS_INVALID, ncr.getCode(), resolveStatusName(ncr.getStatus()));
        }
        // 2. å¤„置方式必填,且必须是合法枚举值
        validateDisposition(disposition);
        validateDisposition(disposeReqVO.getDisposition());
        // 3. ä¸åˆæ ¼æ•°é‡å¿…须大于 0,否则无法生成入库单 / ç”Ÿäº§è®¢å•
        if (ncr.getUnqualifiedQuantity() == null
                || ncr.getUnqualifiedQuantity().compareTo(BigDecimal.ZERO) <= 0) {
            throw exception(QC_NCR_DISPOSE_QUANTITY_INVALID, ncr.getUnqualifiedQuantity());
        }
        // 3. æ›´æ–°å¤„置信息(处置方式以本次处置选择为准并落库)
        // TODO: WM æ¨¡å—集成 â€” å¤„置为退货时触发供应商退货,处置为报废时触发杂项出库
        // 4. æŒ‰å¤„置方式联动生成业务单据
        MesQcNcrDisposeRespVO respVO;
        if (Objects.equals(MesNcrDispositionEnum.CONCESSION.getType(), disposeReqVO.getDisposition())
                || Objects.equals(MesNcrDispositionEnum.SCRAP.getType(), disposeReqVO.getDisposition())) {
            // è®©æ­¥æ”¾è¡Œ / æŠ¥åºŸï¼šç”Ÿæˆæ‚项入库单并自动完成入库
            respVO = createMiscReceiptForDispose(ncr, disposeReqVO);
        } else {
            // è¿”å·¥ / è¿”修:生成自行生产类型的生产订单
            respVO = createReworkWorkOrderForDispose(ncr, disposeReqVO);
        }
        // 5. æ›´æ–°å¤„置信息(处置方式以本次处置选择为准并落库)
        ncrMapper.updateById(new MesQcNcrDO()
                .setId(id)
                .setDisposition(disposition)
                .setHandlerUserId(handlerUserId)
                .setId(ncr.getId())
                .setDisposition(disposeReqVO.getDisposition())
                .setHandlerUserId(disposeReqVO.getHandlerUserId())
                .setHandleDate(LocalDateTime.now())
                .setHandleResult(handleResult)
                .setHandleResult(disposeReqVO.getHandleResult())
                .setStatus(MesNcrStatusEnum.DISPOSED.getStatus()));
        return respVO;
    }
    /**
     * è®©æ­¥æ”¾è¡Œ / æŠ¥åºŸï¼šç”Ÿæˆæ‚项入库单,自动提交并完成入库
     */
    private MesQcNcrDisposeRespVO createMiscReceiptForDispose(MesQcNcrDO ncr, MesQcNcrDisposeReqVO reqVO) {
        boolean scrap = Objects.equals(MesNcrDispositionEnum.SCRAP.getType(), reqVO.getDisposition());
        String dispositionName = scrap ? MesNcrDispositionEnum.SCRAP.getName() : MesNcrDispositionEnum.CONCESSION.getName();
        MesWmMiscReceiptTypeEnum receiptType = scrap
                ? MesWmMiscReceiptTypeEnum.SCRAP : MesWmMiscReceiptTypeEnum.CONCESSION;
        // 4.1 æ ¡éªŒå…¥åº“仓库 / åº“区 / åº“位
        MesMdmItemDO item = mdmItemService.validateItemExists(ncr.getItemId());
        if (reqVO.getWarehouseId() == null || reqVO.getLocationId() == null || reqVO.getAreaId() == null) {
            throw new ServiceException(QC_NCR_DISPOSE_STORAGE_REQUIRED.getCode(),
                    String.format("处置方式[%s]需要入库到指定库位:当前仓库[%s]、库区[%s]、库位[%s]未选全,"
                                    + "请回到处置弹窗补选缺失项后重新提交",
                            dispositionName, resolveWarehouseName(reqVO.getWarehouseId()),
                            resolveLocationName(reqVO.getLocationId()), resolveAreaName(reqVO.getAreaId())));
        }
        try {
            warehouseAreaService.validateWarehouseAreaExists(
                    reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId());
        } catch (ServiceException ex) {
            throw new ServiceException(QC_NCR_DISPOSE_STORAGE_DISABLED.getCode(),
                    String.format("处置方式[%s]所选入库库位校验失败:仓库[%s]、库区[%s]、库位[%s]。原因:%s;"
                                    + "请回到处置弹窗重新选择仓库/库区/库位",
                            dispositionName, resolveWarehouseName(reqVO.getWarehouseId()),
                            resolveLocationName(reqVO.getLocationId()), resolveAreaName(reqVO.getAreaId()),
                            ex.getMessage()));
        }
        // 4.2 æ‰¹æ¬¡ç®¡ç†çš„物料:批次号必填,不存在时自动创建批次
        String batchCode = StrUtil.trimToNull(reqVO.getBatchCode());
        if (Boolean.TRUE.equals(item.getIsBatchManaged())) {
            if (batchCode == null) {
                throw new ServiceException(QC_NCR_DISPOSE_BATCH_REQUIRED.getCode(),
                        String.format("物料[%s]启用了批次管理,处置方式[%s]入库时必须填写批次号;"
                                + "请在处置弹窗的[批次号]中填写后重试", buildItemLabel(item), dispositionName));
            }
            MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO();
            batchReqVO.setItemId(ncr.getItemId());
            batchReqVO.setBatchCode(batchCode);
            batchReqVO.setReceiptDate(LocalDateTime.now());
            try {
                batchService.getOrGenerateBatchCode(batchReqVO);
            } catch (ServiceException ex) {
                throw new ServiceException(ex.getCode(),
                        String.format("为物料[%s]准备批次[%s]失败:%s;请补充批次信息后重新处置",
                                buildItemLabel(item), batchCode, ex.getMessage()));
            }
        }
        // 4.3 åˆ›å»ºæ‚项入库单
        String receiptCode = autoCodeRecordService.generateAutoCode(
                MesMdAutoCodeRuleCodeEnum.WM_MISC_RECEIPT_CODE.getCode());
        String receiptName = String.format("%s入库单-%s", dispositionName, ncr.getCode());
        MesWmMiscReceiptSaveReqVO receiptReqVO = new MesWmMiscReceiptSaveReqVO();
        receiptReqVO.setCode(receiptCode);
        receiptReqVO.setName(receiptName);
        receiptReqVO.setType(receiptType.getType());
        receiptReqVO.setSourceDocType(MISC_RECEIPT_SOURCE_TYPE_NCR);
        receiptReqVO.setSourceDocId(ncr.getId());
        receiptReqVO.setSourceDocCode(ncr.getCode());
        receiptReqVO.setReceiptDate(LocalDateTime.now());
        receiptReqVO.setRemark(String.format("由不合格品处理单[%s]处置为[%s]自动生成并完成入库",
                ncr.getCode(), dispositionName));
        Long receiptId = miscReceiptService.createMiscReceipt(receiptReqVO);
        // 4.4 åˆ›å»ºå…¥åº“行(同物料 / ä¸åˆæ ¼æ•°é‡ / ç›®æ ‡åº“位 / æ‰¹æ¬¡å·ï¼‰
        MesWmMiscReceiptLineSaveReqVO lineReqVO = new MesWmMiscReceiptLineSaveReqVO();
        lineReqVO.setReceiptId(receiptId);
        lineReqVO.setItemId(ncr.getItemId());
        lineReqVO.setQuantity(ncr.getUnqualifiedQuantity());
        lineReqVO.setBatchCode(batchCode);
        lineReqVO.setWarehouseId(reqVO.getWarehouseId());
        lineReqVO.setLocationId(reqVO.getLocationId());
        lineReqVO.setAreaId(reqVO.getAreaId());
        lineReqVO.setRemark(String.format("来源不合格品处理单[%s]", ncr.getCode()));
        miscReceiptLineService.createMiscReceiptLine(lineReqVO);
        // 4.5 è‡ªåŠ¨æäº¤å¹¶å®Œæˆå…¥åº“ï¼ˆå¢žåŠ åº“å­˜ + è®°å½•流水)
        miscReceiptService.submitMiscReceipt(receiptId);
        miscReceiptService.finishMiscReceipt(receiptId);
        MesQcNcrDisposeRespVO respVO = new MesQcNcrDisposeRespVO();
        respVO.setDisposition(reqVO.getDisposition());
        respVO.setDocType("MISC_RECEIPT");
        respVO.setDocId(receiptId);
        respVO.setDocCode(receiptCode);
        respVO.setDocName(receiptName);
        respVO.setMessage(String.format("已生成%s并完成入库,入库单号[%s]", receiptName, receiptCode));
        return respVO;
    }
    /**
     * è¿”å·¥ / è¿”修:生成自行生产类型的生产订单(草稿态,BOM/条码自动生成,工艺路线人工补充)
     */
    private MesQcNcrDisposeRespVO createReworkWorkOrderForDispose(MesQcNcrDO ncr, MesQcNcrDisposeReqVO reqVO) {
        boolean rework = Objects.equals(MesNcrDispositionEnum.REWORK.getType(), reqVO.getDisposition());
        String dispositionName = rework ? MesNcrDispositionEnum.REWORK.getName() : MesNcrDispositionEnum.REPAIR.getName();
        // ç”Ÿäº§è®¢å•的产品是 MES äº§å“ä¸»æ•°æ®ï¼ˆmes_md_item),NCR å­˜çš„æ˜¯ MDM ç‰©æ–™ï¼ˆmdm_item),需先映射
        MesMdItemDO product = mesItemService.getItemByMdmItemId(ncr.getItemId());
        if (product == null) {
            // å…¼å®¹åŽ†å²æ•°æ®ï¼šMES äº§å“ä¸Ž MDM ç‰©æ–™ä¸»é”®ä¸€è‡´æ—¶ç›´æŽ¥æŒ‰ä¸»é”®å–
            product = mesItemService.getItem(ncr.getItemId());
        }
        if (product == null) {
            MesMdmItemDO mdmItem = mdmItemService.getItem(ncr.getItemId());
            throw new ServiceException(QC_NCR_DISPOSE_PRODUCT_NOT_EXISTS.getCode(),
                    String.format("物料[%s]在 MES äº§å“ä¸»æ•°æ®ä¸­ä¸å­˜åœ¨ï¼ˆMDM ç‰©æ–™ ID=%s),无法生成返工/返修生产订单;"
                                    + "请先在[物料产品]中维护该产品后再处置",
                            mdmItem != null ? buildItemLabel(mdmItem) : "未知物料", ncr.getItemId()));
        }
        String workOrderCode = autoCodeRecordService.generateAutoCode(
                MesMdAutoCodeRuleCodeEnum.PRO_WORK_ORDER_CODE.getCode());
        String workOrderName = String.format("%s单-%s", dispositionName, ncr.getCode());
        MesProWorkOrderSaveReqVO workOrderReqVO = new MesProWorkOrderSaveReqVO();
        workOrderReqVO.setCode(workOrderCode);
        workOrderReqVO.setName(workOrderName);
        workOrderReqVO.setType(MesProWorkOrderTypeEnum.SELF.getType());
        workOrderReqVO.setOrderSourceType(MesProWorkOrderSourceTypeEnum.STORE.getType());
        workOrderReqVO.setOrderSourceCode(ncr.getCode());
        workOrderReqVO.setProductId(product.getId());
        workOrderReqVO.setQuantity(ncr.getUnqualifiedQuantity());
        workOrderReqVO.setRequestDate(LocalDateTime.now());
        workOrderReqVO.setRemark(String.format("由不合格品处理单[%s]处置为[%s]自动生成,请补充工艺路线后下达",
                ncr.getCode(), dispositionName));
        // å‡ºè´§æ£€éªŒæ¥æºçš„ NCR:带上客户,满足产品批次配置(clientFlag)对客户必填的要求
        Long clientId = resolveSourceClientId(ncr);
        if (clientId != null) {
            workOrderReqVO.setClientId(clientId);
        }
        Long workOrderId = workOrderService.createWorkOrder(workOrderReqVO);
        MesQcNcrDisposeRespVO respVO = new MesQcNcrDisposeRespVO();
        respVO.setDisposition(reqVO.getDisposition());
        respVO.setDocType("WORK_ORDER");
        respVO.setDocId(workOrderId);
        respVO.setDocCode(workOrderCode);
        respVO.setDocName(workOrderName);
        respVO.setMessage(String.format("已生成%s[%s],请到生产订单中补充工艺路线后下达", workOrderName, workOrderCode));
        return respVO;
    }
    /**
     * å–来源检验单上的客户:仅出货检验(OQC)带有客户信息
     */
    private Long resolveSourceClientId(MesQcNcrDO ncr) {
        if (!Objects.equals(MesQcTypeEnum.OQC.getType(), ncr.getQcType()) || ncr.getQcId() == null) {
            return null;
        }
        MesQcOqcDO oqc = oqcService.getOqc(ncr.getQcId());
        return oqc != null ? oqc.getClientId() : null;
    }
    /**
     * ç‰©æ–™å±•示:编码 + åç§°
     */
    private String buildItemLabel(MesMdmItemDO item) {
        if (item == null) {
            return "未知物料";
        }
        String code = StrUtil.blankToDefault(item.getCode(), "");
        String name = StrUtil.blankToDefault(item.getName(), "");
        String label = (code + " " + name).trim();
        return label.isEmpty() ? "ID=" + item.getId() : label;
    }
    /**
     * ä»“库名称,解析失败回退为可读 ID
     */
    private String resolveWarehouseName(Long warehouseId) {
        if (warehouseId == null) {
            return "未选择";
        }
        MesWmWarehouseDO warehouse = warehouseService.getWarehouse(warehouseId);
        return warehouse != null && StrUtil.isNotBlank(warehouse.getName())
                ? warehouse.getName() : "ID=" + warehouseId;
    }
    /**
     * åº“区名称,解析失败回退为可读 ID
     */
    private String resolveLocationName(Long locationId) {
        if (locationId == null) {
            return "未选择";
        }
        MesWmWarehouseLocationDO location = warehouseLocationService.getWarehouseLocation(locationId);
        return location != null && StrUtil.isNotBlank(location.getName())
                ? location.getName() : "ID=" + locationId;
    }
    /**
     * åº“位名称,解析失败回退为可读 ID
     */
    private String resolveAreaName(Long areaId) {
        if (areaId == null) {
            return "未选择";
        }
        MesWmWarehouseAreaDO area = warehouseAreaService.getWarehouseArea(areaId);
        return area != null && StrUtil.isNotBlank(area.getName())
                ? area.getName() : "ID=" + areaId;
    }
    @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/miscreceipt/MesWmMiscReceiptServiceImpl.java
@@ -128,6 +128,7 @@
        wmTransactionService.createTransactionList(convertList(lines, line -> new MesWmTransactionSaveReqDTO()
                .setType(MesWmTransactionTypeEnum.IN.getType()).setItemId(line.getItemId())
                .setQuantity(line.getQuantity()) // å…¥åº“数量为正数
                .setBatchCode(line.getBatchCode()) // æ‰¹æ¬¡ç®¡ç†ç‰©æ–™å…¥åº“必须带批次号
                .setWarehouseId(line.getWarehouseId()).setLocationId(line.getLocationId()).setAreaId(line.getAreaId())
                .setBizType(MesBizTypeConstants.WM_MISC_RECPT).setBizId(receipt.getId()).setBizCode(receipt.getCode()).setBizLineId(line.getId())));
    }