liyong
4 天以前 be5783c8a7e13bbc76d33c95643d75779cce21db
Merge remote-tracking branch 'origin/dev_business' into dev_business
已添加23个文件
已修改66个文件
已删除18个文件
4927 ■■■■■ 文件已修改
.claude/rules/powershell-commands.md 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.claude/worktrees/agent-a1e034d925913c04a @ a26b31 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
TODO.md 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mps_integration.md 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/sql/mes_pro_mps.sql 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApi.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApi.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApiImpl.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/quotation/CrmSaleQuotationMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderItemDO.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOrderStatusListener.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/MesProMpsApi.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/dto/MesProMpsCreateReqDTO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/dto/MesProMpsRespDTO.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApi.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/mps/MesProMpsApiImpl.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApiImpl.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/MesMdClientController.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientImportExcelVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientImportRespVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientPageReqVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientRespVO.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientSaveReqVO.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/MesMdVendorController.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorImportExcelVO.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorImportRespVO.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorPageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorRespVO.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorSaveReqVO.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/MesProMpsController.java 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsPageReqVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsRespVO.java 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsSaveReqVO.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderController.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/batch/MesWmBatchController.java 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/MesWmOutsourceReceiptController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueController.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnvendor/MesWmReturnVendorController.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/client/MesMdClientDO.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/vendor/MesMdVendorDO.java 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/mps/MesProMpsDO.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/mps/MesProMpsMergeItemDO.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/iqc/MesQcIqcDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/oqc/MesQcOqcDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/arrivalnotice/MesWmArrivalNoticeDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/itemreceipt/MesWmItemReceiptDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourceissue/MesWmOutsourceIssueDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/packages/MesWmPackageDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnvendor/MesWmReturnVendorDO.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/client/MesMdClientMapper.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/vendor/MesMdVendorMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMapper.java 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMergeItemMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderMapper.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/batch/MesWmBatchMapper.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/packages/MesWmPackageMapper.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesMapper.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProMpsStatusEnum.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client/MesMdClientService.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client/MesMdClientServiceImpl.java 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemService.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsService.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java 433 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java 35 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.claude/rules/powershell-commands.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,35 @@
# PowerShell å‘½ä»¤è§„则
## è§„则
由于项目运行在 Windows çŽ¯å¢ƒä¸‹ï¼Œç”¨æˆ·ç»ˆç«¯é»˜è®¤ä½¿ç”¨ PowerShell è€Œéž bash。在提供命令行指导时,必须使用 PowerShell è¯­æ³•。
## å¸¸ç”¨å‘½ä»¤å¯¹ç…§
| æ“ä½œ | Bash | PowerShell |
|------|------|-------------|
| åˆ é™¤ç›®å½• | `rm -rf <path>` | `Remove-Item -Recurse -Force <path>` æˆ–简写 `ri -r -fo <path>` |
| å¤åˆ¶æ–‡ä»¶ | `cp <src> <dest>` | `Copy-Item <src> <dest>` æˆ–简写 `cp <src> <dest>` |
| ç§»åŠ¨æ–‡ä»¶ | `mv <src> <dest>` | `Move-Item <src> <dest>` æˆ–简写 `mv <src> <dest>` |
| åˆ›å»ºç›®å½• | `mkdir -p <path>` | `New-Item -ItemType Directory -Path <path>` æˆ–简写 `md <path>` |
| æŸ¥çœ‹æ–‡ä»¶ | `cat <file>` | `Get-Content <file>` æˆ–简写 `gc <file>` |
| åˆ—出文件 | `ls` | `Get-ChildItem` æˆ–简写 `ls`/`dir` |
| æŸ¥æ‰¾æ–‡ä»¶ | `find . -name "*.java"` | `Get-ChildItem -Recurse -Filter "*.java"` æˆ–简写 `gci -r -fi "*.java"` |
| æŸ¥æ‰¾å†…容 | `grep -r "pattern" .` | `Select-String -Path "*.java" -Pattern "pattern"` æˆ–简写 `sls "pattern" *.java -r` |
## åˆ é™¤ç›®å½•示例
```powershell
# æŽ¨èï¼šä½¿ç”¨ Remove-Item å®Œæ•´å‘½ä»¤
Remove-Item -Recurse -Force yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client
# ç®€å†™å½¢å¼
ri -r -fo yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client
```
## æ³¨æ„äº‹é¡¹
- PowerShell å‚数使用 `-` å‰ç¼€ï¼Œå¦‚ `-Recurse`、`-Force`
- å‚数可以简写首字母,如 `-r`、`-fo`
- è·¯å¾„分隔符在 PowerShell ä¸­å¯ä»¥ä½¿ç”¨ `/` æˆ– `\`,推荐使用 `/` ä»¥ä¿æŒè·¨å¹³å°å…¼å®¹
- é¿å… `-rf` è¿™æ ·çš„ bash é£Žæ ¼å‚数组合,PowerShell ä¸æ”¯æŒ
.claude/worktrees/agent-a1e034d925913c04a
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1 @@
Subproject commit a26b31cc9f3ee9b21b1a754e80fa7359e8a7a8f8
TODO.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
# åˆ é™¤ MES å®¢æˆ·å’Œä¾›åº”商模块 - è¿›åº¦è·Ÿè¸ª
## ä»»åŠ¡æ¦‚è¿°
删除 MES æ¨¡å—中独立的客户(MesMdClient)和供应商(MesMdVendor)管理功能,改为复用:
- **客户**:使用 CRM çš„客户管理(CrmCustomerApi)
- **供应商**:使用采购模块的供应商管理(ErpSupplierApi)
---
## å·²å®Œæˆ âœ…
### Step 1: æ‰©å±• CRM å’Œ ERP API
- [x] CrmCustomerApi å·²æœ‰ `validateCustomer` æ–¹æ³•,可直接使用
- [x] ErpSupplierApi å·²æœ‰ `validateSupplier` æ–¹æ³•(已校验存在+启用),可直接使用
### Step 2: æ›¿æ¢ Service å±‚引用
**客户校验替换(4个文件):**
- [x] MesWmProductSalesServiceImpl.java
- [x] MesWmSalesNoticeServiceImpl.java
- [x] MesWmReturnSalesServiceImpl.java
- [x] MesQcOqcServiceImpl.java
**供应商校验替换(5个文件):**
- [x] MesWmReturnVendorServiceImpl.java
- [x] MesWmOutsourceReceiptServiceImpl.java
- [x] MesWmOutsourceIssueServiceImpl.java
- [x] MesQcIqcServiceImpl.java
### Step 3: æ›¿æ¢ Controller å±‚引用
**客户信息拼接(已完成):**
- [x] MesProTaskController.java
- [x] MesProMpsController.java
- [x] MesQcOqcController.java
- [x] MesQcPendingInspectController.java
- [x] MesWmProductIssueController.java
- [x] MesWmBatchController.java
- [x] MesWmPackageController.java
**供应商信息拼接(已完成):**
- [x] MesWmMaterialStockController.java
- [x] MesWmBatchController.java
- [x] MesWmReturnVendorController.java
- [x] MesWmOutsourceReceiptController.java
- [x] MesWmOutsourceIssueController.java
- [x] MesQcIqcController.java
- [x] MesQcPendingInspectController.java
- [x] MesProWorkOrderController.java
### Step 4: æ›´æ–° DO æ³¨é‡Š
**客户(6个文件):**
- [x] MesProWorkOrderDO
- [x] MesProTaskDO
- [x] MesProMpsDO
- [x] MesQcOqcDO
- [x] MesWmBatchDO
- [x] MesWmPackageDO
**供应商(7个文件):**
- [x] MesWmReturnVendorDO
- [x] MesWmOutsourceReceiptDO
- [x] MesWmOutsourceIssueDO
- [x] MesWmMaterialStockDO
- [x] MesWmBatchDO
- [x] MesQcIqcDO
- [x] MesProWorkOrderDO
**其他相关 DO æ–‡ä»¶ï¼š**
- [x] MesWmArrivalNoticeDO
- [x] MesWmItemReceiptDO
- [x] MesWmProductSalesDO
- [x] MesWmSalesNoticeDO
### Step 5: æ¸…理无用 import
- [x] åˆ é™¤æ‰€æœ‰ DO æ–‡ä»¶ä¸­çš„ `MesMdClientDO` å’Œ `MesMdVendorDO` import è¯­å¥
### Step 6: åˆ é™¤ MES å®¢æˆ·/供应商模块文件
**待删除目录(需手动执行):**
```
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/client/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/vendor/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/client/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/vendor/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client/
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/vendor/
```
### Step 7: åˆ é™¤æ•°æ®åº“表
- [x] `DROP TABLE IF EXISTS mes_md_client;` âœ… å·²æ‰§è¡Œ
- [x] `DROP TABLE IF EXISTS mes_md_vendor;` âœ… å·²æ‰§è¡Œ
### ç¼–译验证
- [x] `mvn compile -pl yudao-module-mes -am -q` ç¼–译通过 âœ…
### Step 8: å®¢æˆ·æƒé™è¿‡æ»¤ï¼ˆä¸»æŸ¥è¯¢ï¼‰
**已添加 CRM API æ–¹æ³•:**
- [x] CrmCustomerApi.getPermittedCustomerIds() - èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
**已添加客户权限过滤的 Mapper å’Œ Service:**
- [x] MesWmSalesNoticeMapper + MesWmSalesNoticeService - å‘货通知单
- [x] MesWmReturnSalesMapper + MesWmReturnSalesService - é”€å”®é€€è´§å•
- [x] MesWmProductSalesMapper + MesWmProductSalesService - é”€å”®å‡ºåº“单
- [x] MesWmPackageMapper + MesWmPackageService - è£…箱单
- [x] MesWmBatchMapper + MesWmBatchService - æ‰¹æ¬¡ç®¡ç†
- [x] MesQcOqcMapper + MesQcOqcService - å‡ºè´§æ£€éªŒå•
- [x] MesProWorkOrderMapper + MesProWorkOrderService - ç”Ÿäº§å·¥å•
- [x] MesProMpsMapper + MesProMpsService - ç”Ÿäº§ä¸»è®¡åˆ’
- [x] ErpSaleOrderMapper + ErpSaleOrderService - ERP é”€å”®è®¢å•
**编译验证:**
- [x] `mvn compile -pl yudao-module-mes -am -q` ç¼–译通过 âœ…
- [x] `mvn compile -pl yudao-module-erp -am -q` ç¼–译通过 âœ…
### Step 9: CRM æ¨¡å—客户权限过滤
**已添加客户权限过滤:**
- [x] CrmContractMapper + CrmContractService - åˆåŒç®¡ç†
- [x] CrmSaleQuotationMapper + CrmSaleQuotationService - é”€å”®æŠ¥ä»·å•
**编译验证:**
- [x] `mvn compile -pl yudao-module-crm -am -q` ç¼–译通过 âœ…
---
## å¾…手动执行
### åˆ é™¤æ¨¡å—文件
在 IDE æˆ–终端中执行以下命令删除 MES å®¢æˆ·/供应商模块文件:
```bash
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/client
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/vendor
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/client
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/vendor
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client
rm -rf yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/vendor
```
---
## ä¿®æ”¹è§„则总结
- **客户 API è°ƒç”¨**:
  - `clientService.getClientMap(ids)` â†’ `customerApi.getCustomerMap(ids)`(直接返回 Map)
  - `clientService.validateClientExistsAndEnable(id)` â†’ `customerApi.validateCustomer(id)`
- **供应商 API è°ƒç”¨**:
  - `vendorService.getVendorMap(ids)` â†’ `supplierApi.getSupplierList(ids).getCheckedData()`(返回 List,需转 Map)
  - `vendorService.validateVendorExistsAndEnable(id)` â†’ `supplierApi.validateSupplier(id)`
- **DTO å­—段差异**:
  - `CrmCustomerRespDTO` æ—  `code` å’Œ `nickname` å­—段,只有 `name`
  - `ErpSupplierRespDTO` æ—  `code` å’Œ `nickname` å­—段,只有 `name`
docs/mps_integration.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,224 @@
# ç”Ÿäº§ä¸»è®¡åˆ’(MPS)- å‰ç«¯è”调方案
## æ¶‰åŠé¡µé¢
- é”€å”®è®¢å•列表页(新增"是否需要生产"列)
- é”€å”®è®¢å•详情页(新增"是否需要生产"开关)
- ç”Ÿäº§ä¸»è®¡åˆ’列表页(新增)
- ç”Ÿäº§ä¸»è®¡åˆ’详情页(新增)
- ç”Ÿäº§ä¸»è®¡åˆ’新增页(新增,支持手动创建)
## ä¸šåŠ¡æµç¨‹ä¸Žæ•°æ®å¸¦å…¥
### 1. æ‰‹åŠ¨åˆ›å»º MPS
```
生产主计划新增页
    â†“
填写:产品、数量、客户、需求日期等
    â†“
提交创建 MPS(编码自动生成)
    â†“
MPS çŠ¶æ€ä¸ºè‰ç¨¿
```
**自动生成编码格式**:`MPS + yyyyMMdd + 001`(如 MPS20240701001)
### 2. é”€å”®è®¢å•审核通过 -> è‡ªåŠ¨åˆ›å»º MPS
```
销售订单审核通过
    â†“
BPM ç›‘听器触发
    â†“
遍历销售订单明细,对于 need_production=1 çš„æ˜Žç»†
    â†“
调用 MesProMpsApi.createMps() åˆ›å»º MPS
    â†“
更新销售订单明细的 mps_id å­—段
```
**数据带入:**
- MPS.sale_order_id â† é”€å”®è®¢å•.id
- MPS.sale_order_no â† é”€å”®è®¢å•.no
- MPS.sale_order_item_id â† é”€å”®è®¢å•明细.id
- MPS.product_id â† é”€å”®è®¢å•明细.productId
- MPS.quantity â† é”€å”®è®¢å•明细.count
- MPS.client_id â† é”€å”®è®¢å•.customerId
- MPS.request_date â† é”€å”®è®¢å•.orderTime
### 2. MPS ä¸‹å‘ -> ç”Ÿæˆç”Ÿäº§è®¢å•
```
MPS å•条下发
    â†“
创建生产工单(类型=自行生产,来源类型=客户订单)
    â†“
工单自动确认为已确认状态
    â†“
更新 MPS çŠ¶æ€ä¸ºå·²ä¸‹å‘ï¼Œå…³è”å·¥å• ID
```
### 3. MPS åˆå¹¶ä¸‹å‘
```
选择多条相同产品的 MPS
    â†“
校验:相同产品、草稿状态
    â†“
计算合并数量 = SUM(quantity)
    â†“
创建一条生产工单(数量为合并数量)
    â†“
更新所有 MPS çŠ¶æ€ä¸ºå·²ä¸‹å‘ï¼Œå…³è”åŒä¸€å·¥å•
    â†“
记录合并明细到 mes_pro_mps_merge_item
```
## API
### MPS ç›¸å…³æŽ¥å£
| æ–¹æ³• | è·¯å¾„ | è¯´æ˜Ž |
|------|------|------|
| POST | /mes/pro-mps/create | åˆ›å»º MPS |
| PUT | /mes/pro-mps/update | æ›´æ–° MPS |
| DELETE | /mes/pro-mps/delete | åˆ é™¤ MPS |
| GET | /mes/pro-mps/get | èŽ·å– MPS è¯¦æƒ… |
| GET | /mes/pro-mps/page | åˆ†é¡µæŸ¥è¯¢ MPS |
| POST | /mes/pro-mps/issue | ä¸‹å‘单条 MPS |
| POST | /mes/pro-mps/merge-issue | åˆå¹¶ä¸‹å‘多条 MPS |
| POST | /mes/pro-mps/cancel | å–消 MPS |
| GET | /mes/pro-mps/export-excel | å¯¼å‡º MPS |
### é”€å”®è®¢å•接口变更
| æ–¹æ³• | è·¯å¾„ | å˜æ›´è¯´æ˜Ž |
|------|------|----------|
| GET | /erp/sale-order/get | å“åº”新增 needProduction、mpsId å­—段 |
| GET | /erp/sale-order/page | å“åº”新增 needProduction å­—段 |
| POST | /erp/sale-order/create | è¯·æ±‚新增 needProduction å­—段 |
| PUT | /erp/sale-order/update | è¯·æ±‚新增 needProduction å­—段 |
## è¯·æ±‚参数
### MPS åˆ›å»ºè¯·æ±‚
```json
{
  "productId": 300,
  "quantity": 100.00,
  "unitMeasureId": 1,
  "clientId": 400,
  "requestDate": "2024-01-01T00:00:00",
  "remark": "备注"
}
```
**说明**:
- `code` å­—段可选,不填则自动生成
- `saleOrderId`、`saleOrderNo`、`saleOrderItemId` å¯é€‰ï¼Œæ‰‹åŠ¨åˆ›å»ºæ—¶ä¸å¡«
```
### MPS ä¸‹å‘请求
```
POST /mes/pro-mps/issue?id=123
```
### MPS åˆå¹¶ä¸‹å‘请求
```json
// POST /mes/pro-mps/merge-issue
[123, 124, 125]
```
## å“åº”字段
### MPS è¯¦æƒ…响应
| å­—段 | ç±»åž‹ | è¯´æ˜Ž |
|------|------|------|
| id | Long | MPS ç¼–号 |
| code | String | MPS ç¼–码 |
| status | Integer | çŠ¶æ€ï¼ˆ0=草稿 1=已下发 2=已完成 3=已取消) |
| saleOrderId | Long | é”€å”®è®¢å•ID |
| saleOrderNo | String | é”€å”®è®¢å•号 |
| saleOrderItemId | Long | é”€å”®è®¢å•明细ID |
| productId | Long | äº§å“ID |
| productCode | String | äº§å“ç¼–码 |
| productName | String | äº§å“åç§° |
| productSpecification | String | è§„格型号 |
| unitMeasureName | String | å•位 |
| quantity | BigDecimal | è®¡åˆ’生产数量 |
| quantityProduced | BigDecimal | å·²ç”Ÿäº§æ•°é‡ |
| clientId | Long | å®¢æˆ·ID |
| clientCode | String | å®¢æˆ·ç¼–码 |
| clientName | String | å®¢æˆ·åç§° |
| requestDate | DateTime | éœ€æ±‚日期 |
| issueDate | DateTime | ä¸‹å‘日期 |
| finishDate | DateTime | å®Œæˆæ—¥æœŸ |
| workOrderId | Long | ç”Ÿäº§å·¥å•ID |
| workOrderCode | String | ç”Ÿäº§å·¥å•编码 |
| mergeFlag | Integer | æ˜¯å¦åˆå¹¶ä¸‹å‘ |
| mergeBatchNo | String | åˆå¹¶æ‰¹æ¬¡å· |
| remark | String | å¤‡æ³¨ |
| createTime | DateTime | åˆ›å»ºæ—¶é—´ |
### å“åº”示例
```json
{
  "code": 0,
  "data": {
    "id": 1024,
    "code": "MPS-001",
    "status": 0,
    "saleOrderId": 100,
    "saleOrderNo": "SO-001",
    "productId": 300,
    "productCode": "P-001",
    "productName": "产品A",
    "quantity": 100.00,
    "quantityProduced": 0,
    "clientId": 400,
    "clientName": "客户A",
    "requestDate": "2024-01-01T00:00:00",
    "mergeFlag": 0,
    "createTime": "2024-01-01T10:00:00"
  },
  "msg": "操作成功"
}
```
## å­—段展示规则
| å­—段 | å±•示位置 | è¯´æ˜Ž |
|------|----------|------|
| MPS ç¼–码 | åˆ—表、详情 | ä¸»é”®æ ‡è¯† |
| çŠ¶æ€ | åˆ—表、详情 | è‰ç¨¿(蓝)、已下发(绿)、已完成(ç´«)、已取消(红) |
| é”€å”®è®¢å•号 | åˆ—表、详情 | å…³è”来源 |
| äº§å“ç¼–码/名称 | åˆ—表、详情 | ç”Ÿäº§å¯¹è±¡ |
| è®¡åˆ’数量 | åˆ—表、详情 | æ ¸å¿ƒæ•°æ® |
| å·²ç”Ÿäº§æ•°é‡ | è¯¦æƒ… | è¿›åº¦è·Ÿè¸ª |
| å®¢æˆ·åç§° | åˆ—表、详情 | éœ€æ±‚æ–¹ |
| éœ€æ±‚日期 | åˆ—表、详情 | äº¤æœŸè¦æ±‚ |
| ç”Ÿäº§å·¥å•编码 | åˆ—表、详情 | ä¸‹å‘后显示,可点击跳转 |
## ä¸šåŠ¡è§„åˆ™è¯´æ˜Ž
| åœºæ™¯ | è§„则 |
|------|------|
| åˆ›å»º MPS | ä»…草稿状态可编辑、删除 |
| ä¸‹å‘ MPS | ä»…草稿状态可下发,下发后生成生产工单并自动确认 |
| åˆå¹¶ä¸‹å‘ | è‡³å°‘选择 2 æ¡ï¼Œå¿…须相同产品,必须都是草稿状态 |
| å–消 MPS | ä»…已下发状态可取消 |
| å®Œæˆ MPS | ç”±ç”Ÿäº§å·¥å•完成时自动触发 |
## æ³¨æ„äº‹é¡¹
1. **销售订单审批流程**:销售订单审核通过后会自动创建 MPS,无需前端手动触发
2. **MPS ä¸Žé”€å”®è®¢å•关联**:通过 saleOrderItemId å­—段关联,可追溯来源
3. **合并下发限制**:只能合并相同产品的 MPS,不同产品不能合并
4. **状态不可逆**:下发后无法回到草稿状态,只能取消或完成
5. **生产进度同步**:MPS çš„已生产数量由生产工单报工时自动更新
docs/sql/mes_pro_mps.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
-- =============================================
-- MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)表
-- =============================================
CREATE TABLE IF NOT EXISTS `mes_pro_mps` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `code` varchar(64) NOT NULL COMMENT 'MPS ç¼–码',
    `status` tinyint NOT NULL DEFAULT 0 COMMENT '状态(0=草稿 1=已下发 2=已完成 3=已取消)',
    -- æ¥æºä¿¡æ¯ï¼ˆæ¥è‡ªé”€å”®è®¢å•)
    `sale_order_id` bigint DEFAULT NULL COMMENT '销售订单ID',
    `sale_order_no` varchar(64) DEFAULT NULL COMMENT '销售订单号',
    `sale_order_item_id` bigint DEFAULT NULL COMMENT '销售订单明细ID',
    -- äº§å“ä¿¡æ¯
    `product_id` bigint NOT NULL COMMENT '产品编号',
    `quantity` decimal(24,6) NOT NULL COMMENT '计划生产数量',
    `quantity_produced` decimal(24,6) DEFAULT 0 COMMENT '已生产数量',
    `unit_measure_id` bigint DEFAULT NULL COMMENT '计量单位ID',
    -- å®¢æˆ·ä¿¡æ¯
    `client_id` bigint DEFAULT NULL COMMENT '客户编号',
    -- æ—¶é—´ä¿¡æ¯
    `request_date` datetime DEFAULT NULL COMMENT '需求日期',
    `issue_date` datetime DEFAULT NULL COMMENT '下发日期',
    `finish_date` datetime DEFAULT NULL COMMENT '完成日期',
    -- ç”Ÿäº§å·¥å•关联(下发后生成)
    `work_order_id` bigint DEFAULT NULL COMMENT '生产工单ID',
    `work_order_code` varchar(64) DEFAULT NULL COMMENT '生产工单编码',
    -- åˆå¹¶ä¸‹å‘标识
    `merge_flag` tinyint DEFAULT 0 COMMENT '是否合并下发(0=否 1=是)',
    `merge_batch_no` varchar(64) DEFAULT NULL COMMENT '合并批次号',
    -- å…¶ä»–
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`),
    KEY `idx_sale_order_item_id` (`sale_order_item_id`),
    KEY `idx_product_id` (`product_id`),
    KEY `idx_status` (`status`),
    KEY `idx_merge_batch_no` (`merge_batch_no`)
) ENGINE=InnoDB COMMENT='MES ç”Ÿäº§ä¸»è®¡åˆ’';
-- =============================================
-- MPS åˆå¹¶ä¸‹å‘明细表
-- =============================================
CREATE TABLE IF NOT EXISTS `mes_pro_mps_merge_item` (
    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
    `merge_batch_no` varchar(64) NOT NULL COMMENT '合并批次号',
    `mps_id` bigint NOT NULL COMMENT 'MPS ç¼–号',
    `work_order_id` bigint DEFAULT NULL COMMENT '生成的生产工单ID',
    `quantity` decimal(24,6) NOT NULL COMMENT '分配数量',
    `remark` varchar(500) DEFAULT NULL COMMENT '备注',
    `creator` varchar(64) DEFAULT '' COMMENT '创建者',
    `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    `updater` varchar(64) DEFAULT '' COMMENT '更新者',
    `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
    `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
    PRIMARY KEY (`id`),
    KEY `idx_merge_batch_no` (`merge_batch_no`),
    KEY `idx_mps_id` (`mps_id`)
) ENGINE=InnoDB COMMENT='MPS åˆå¹¶ä¸‹å‘明细';
-- =============================================
-- é”€å”®è®¢å•明细表扩展字段
-- =============================================
ALTER TABLE `erp_sale_order_items`
ADD COLUMN `need_production` tinyint DEFAULT 0 COMMENT '是否需要生产(0=否 1=是)' AFTER `out_count`,
ADD COLUMN `mps_id` bigint DEFAULT NULL COMMENT '关联的MPS编号' AFTER `need_production`;
-- =============================================
-- å­—典类型 - MPS çŠ¶æ€
-- =============================================
INSERT INTO `system_dict_type` (`name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
('MPS çŠ¶æ€', 'mes_pro_mps_status', 0, 'MPS çŠ¶æ€', '1', NOW(), '1', NOW(), b'0');
-- =============================================
-- å­—典数据 - MPS çŠ¶æ€
-- =============================================
INSERT INTO `system_dict_data` (`sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES
(0, '草稿', '0', 'mes_pro_mps_status', 0, 'info', '', '', '1', NOW(), '1', NOW(), b'0'),
(1, '已下发', '1', 'mes_pro_mps_status', 0, 'success', '', '', '1', NOW(), '1', NOW(), b'0'),
(2, '已完成', '2', 'mes_pro_mps_status', 0, 'primary', '', '', '1', NOW(), '1', NOW(), b'0'),
(3, '已取消', '3', 'mes_pro_mps_status', 0, 'danger', '', '', '1', NOW(), '1', NOW(), b'0');
yudao-module-crm-api/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApi.java
@@ -47,4 +47,13 @@
     */
    CrmCustomerRespDTO validateCustomer(Long id);
    /**
     * èŽ·å–å½“å‰ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
     *
     * åŒ…括:用户自己在团队中、用户的下级在团队中、公海数据
     *
     * @return æœ‰æƒé™çš„客户 ID åˆ—表(null è¡¨ç¤ºä¸é™åˆ¶ï¼Œå³è¶…管)
     */
    List<Long> getPermittedCustomerIds();
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApi.java
@@ -47,4 +47,13 @@
     */
    CrmCustomerRespDTO validateCustomer(Long id);
    /**
     * èŽ·å–å½“å‰ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
     *
     * åŒ…括:用户自己在团队中、用户的下级在团队中、公海数据
     *
     * @return æœ‰æƒé™çš„客户 ID åˆ—表
     */
    List<Long> getPermittedCustomerIds();
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/api/customer/CrmCustomerApiImpl.java
@@ -1,15 +1,25 @@
package cn.iocoder.yudao.module.crm.api.customer;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.permission.CrmPermissionDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.util.CrmPermissionUtils;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.anyMatch;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
/**
 * CRM å®¢æˆ· API å®žçŽ°ç±»
@@ -21,6 +31,10 @@
    @Resource
    private CrmCustomerService customerService;
    @Resource
    private CrmPermissionService permissionService;
    @Resource
    private AdminUserApi adminUserApi;
    @Override
    public CrmCustomerRespDTO getCustomer(Long id) {
@@ -30,15 +44,25 @@
    @Override
    public List<CrmCustomerRespDTO> getCustomerList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<CrmCustomerDO> customers = customerService.getCustomerList(ids);
        return BeanUtils.toBean(customers, CrmCustomerRespDTO.class);
        // è¿‡æ»¤æŽ‰ç”¨æˆ·æ²¡æœ‰æƒé™çš„客户
        List<CrmCustomerDO> filteredCustomers = filterCustomersByPermission(customers);
        return BeanUtils.toBean(filteredCustomers, CrmCustomerRespDTO.class);
    }
    @Override
    public Map<Long, CrmCustomerRespDTO> getCustomerMap(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyMap();
        }
        List<CrmCustomerDO> customers = customerService.getCustomerList(ids);
        return customers.stream()
                .collect(java.util.stream.Collectors.toMap(
        // è¿‡æ»¤æŽ‰ç”¨æˆ·æ²¡æœ‰æƒé™çš„客户
        List<CrmCustomerDO> filteredCustomers = filterCustomersByPermission(customers);
        return filteredCustomers.stream()
                .collect(Collectors.toMap(
                        CrmCustomerDO::getId,
                        customer -> BeanUtils.toBean(customer, CrmCustomerRespDTO.class)));
    }
@@ -49,4 +73,130 @@
        return getCustomer(id);
    }
}
    @Override
    public List<Long> getPermittedCustomerIds() {
        // 1. è¶…级管理员有所有权限
        if (CrmPermissionUtils.isCrmAdmin()) {
            return null; // null è¡¨ç¤ºä¸é™åˆ¶
        }
        Long userId = WebFrameworkUtils.getLoginUserId();
        if (userId == null) {
            return Collections.emptyList();
        }
        // 2. èŽ·å–ç”¨æˆ·è‡ªå·±æœ‰æƒé™çš„å®¢æˆ· ID
        List<CrmPermissionDO> userPermissions = permissionService.getPermissionListByBizTypeAndUserId(
                CrmBizTypeEnum.CRM_CUSTOMER.getType(), userId);
        Set<Long> permittedIds = new HashSet<>(convertSet(userPermissions, CrmPermissionDO::getBizId));
        // 3. èŽ·å–ç”¨æˆ·çš„ä¸‹çº§ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID
        List<AdminUserRespDTO> subordinates = adminUserApi.getUserListBySubordinate(userId);
        if (CollUtil.isNotEmpty(subordinates)) {
            Set<Long> subordinateUserIds = convertSet(subordinates, AdminUserRespDTO::getId);
            for (Long subordinateId : subordinateUserIds) {
                List<CrmPermissionDO> subordinatePermissions = permissionService.getPermissionListByBizTypeAndUserId(
                        CrmBizTypeEnum.CRM_CUSTOMER.getType(), subordinateId);
                permittedIds.addAll(convertSet(subordinatePermissions, CrmPermissionDO::getBizId));
            }
        }
        // 4. æŸ¥è¯¢æ‰€æœ‰å®¢æˆ·ï¼Œæ‰¾å‡ºå…¬æµ·æ•°æ®ï¼ˆæ²¡æœ‰å›¢é˜Ÿæˆå‘˜çš„客户)
        // å…¬æµ·æ•°æ®æ‰€æœ‰äººéƒ½æœ‰è¯»æƒé™
        List<CrmCustomerDO> allCustomers = customerService.getCustomerList();
        Set<Long> allCustomerIds = convertSet(allCustomers, CrmCustomerDO::getId);
        // èŽ·å–æœ‰æƒé™é…ç½®çš„å®¢æˆ·
        List<CrmPermissionDO> allPermissions = permissionService.getPermissionListByBiz(
                CrmBizTypeEnum.CRM_CUSTOMER.getType(), allCustomerIds);
        Set<Long> hasTeamCustomerIds = convertSet(allPermissions, CrmPermissionDO::getBizId);
        // å…¬æµ·æ•°æ® = æ‰€æœ‰å®¢æˆ· - æœ‰å›¢é˜Ÿçš„客户
        Set<Long> publicSeaCustomerIds = new HashSet<>(allCustomerIds);
        publicSeaCustomerIds.removeAll(hasTeamCustomerIds);
        // åˆå¹¶ï¼šæœ‰æƒé™çš„客户 + å…¬æµ·å®¢æˆ·
        permittedIds.addAll(publicSeaCustomerIds);
        return new ArrayList<>(permittedIds);
    }
    /**
     * è¿‡æ»¤æŽ‰ç”¨æˆ·æ²¡æœ‰æƒé™çš„客户
     * <p>
     * æƒé™åˆ¤æ–­é€»è¾‘:
     * 1. è¶…级管理员有所有权限
     * 2. å…¬æµ·æ•°æ®ï¼ˆæ²¡æœ‰å›¢é˜Ÿæˆå‘˜ï¼‰æ‰€æœ‰äººéƒ½æœ‰è¯»æƒé™
     * 3. ç”¨æˆ·è‡ªå·±åœ¨å›¢é˜Ÿæˆå‘˜ä¸­
     * 4. ç”¨æˆ·çš„下级在团队成员中
     *
     * @param customers å®¢æˆ·åˆ—表
     * @return è¿‡æ»¤åŽçš„客户列表
     */
    private List<CrmCustomerDO> filterCustomersByPermission(List<CrmCustomerDO> customers) {
        if (CollUtil.isEmpty(customers)) {
            return Collections.emptyList();
        }
        // 1. è¶…级管理员有所有权限
        if (CrmPermissionUtils.isCrmAdmin()) {
            return customers;
        }
        Long userId = WebFrameworkUtils.getLoginUserId();
        if (userId == null) {
            // æ²¡æœ‰ç™»å½•用户,返回空列表
            return Collections.emptyList();
        }
        // 2. èŽ·å–è¿™äº›å®¢æˆ·çš„æƒé™é…ç½®
        Set<Long> customerIds = convertSet(customers, CrmCustomerDO::getId);
        List<CrmPermissionDO> permissionList = permissionService.getPermissionListByBiz(
                CrmBizTypeEnum.CRM_CUSTOMER.getType(), customerIds);
        Map<Long, List<CrmPermissionDO>> permissionMap = permissionList.stream()
                .collect(Collectors.groupingBy(CrmPermissionDO::getBizId));
        // 3. èŽ·å–ç”¨æˆ·çš„ä¸‹çº§ç”¨æˆ·
        List<AdminUserRespDTO> subordinates = adminUserApi.getUserListBySubordinate(userId);
        Set<Long> subordinateUserIds = convertSet(subordinates, AdminUserRespDTO::getId);
        // 4. è¿‡æ»¤æœ‰æƒé™çš„客户
        return customers.stream()
                .filter(customer -> hasCustomerPermission(customer.getId(), permissionMap, userId, subordinateUserIds))
                .collect(Collectors.toList());
    }
    /**
     * åˆ¤æ–­ç”¨æˆ·æ˜¯å¦æœ‰å®¢æˆ·æƒé™
     */
    private boolean hasCustomerPermission(Long customerId,
                                           Map<Long, List<CrmPermissionDO>> permissionMap,
                                           Long userId,
                                           Set<Long> subordinateUserIds) {
        List<CrmPermissionDO> permissions = permissionMap.get(customerId);
        // å…¬æµ·æ•°æ®ï¼ˆæ²¡æœ‰å›¢é˜Ÿæˆå‘˜ï¼‰ï¼Œæ‰€æœ‰äººéƒ½æœ‰è¯»æƒé™
        if (CollUtil.isEmpty(permissions)) {
            return true;
        }
        // æ²¡æœ‰è´Ÿè´£äººçš„æƒ…况,所有人都有读权限
        if (!anyMatch(permissions, item -> item.getLevel() == 1)) { // 1 = è´Ÿè´£äºº
            return true;
        }
        // æ£€æŸ¥ç”¨æˆ·è‡ªå·±åœ¨å›¢é˜Ÿæˆå‘˜ä¸­
        if (anyMatch(permissions, item -> item.getUserId().equals(userId))) {
            return true;
        }
        // æ£€æŸ¥ç”¨æˆ·çš„下级在团队成员中
        for (Long subordinateId : subordinateUserIds) {
            if (anyMatch(permissions, item -> item.getUserId().equals(subordinateId))) {
                return true;
            }
        }
        return false;
    }
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/contract/CrmContractMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.dal.mysql.contract;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
@@ -51,6 +52,18 @@
    }
    default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId, CrmContractConfigDO config) {
        return selectPage(pageReqVO, userId, config, null);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     *
     * @param pageReqVO æŸ¥è¯¢æ¡ä»¶
     * @param userId ç”¨æˆ·ID
     * @param config åˆåŒé…ç½®
     * @param permittedCustomerIds æœ‰æƒé™çš„客户ID列表(null表示不限制)
     */
    default PageResult<CrmContractDO> selectPage(CrmContractPageReqVO pageReqVO, Long userId, CrmContractConfigDO config, List<Long> permittedCustomerIds) {
        MPJLambdaWrapperX<CrmContractDO> query = new MPJLambdaWrapperX<>();
        // æ‹¼æŽ¥æ•°æ®æƒé™çš„æŸ¥è¯¢æ¡ä»¶
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_CONTRACT.getType(),
@@ -64,6 +77,14 @@
                .eqIfPresent(CrmContractDO::getAuditStatus, pageReqVO.getAuditStatus())
                .orderByDesc(CrmContractDO::getId);
        // å®¢æˆ·æƒé™è¿‡æ»¤
        if (permittedCustomerIds != null) {
            if (CollUtil.isEmpty(permittedCustomerIds)) {
                return PageResult.empty();
            }
            query.in(CrmContractDO::getCustomerId, permittedCustomerIds);
        }
        // Backlog: å³å°†åˆ°æœŸçš„合同
        LocalDateTime beginOfToday = LocalDateTimeUtil.beginOfDay(LocalDateTime.now());
        LocalDateTime endOfToday = LocalDateTimeUtil.endOfDay(LocalDateTime.now());
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/quotation/CrmSaleQuotationMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.crm.dal.mysql.quotation;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -27,6 +28,17 @@
    }
    default PageResult<CrmSaleQuotationDO> selectPage(CrmSaleQuotationPageReqVO pageReqVO, Long userId) {
        return selectPage(pageReqVO, userId, null);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     *
     * @param pageReqVO æŸ¥è¯¢æ¡ä»¶
     * @param userId ç”¨æˆ·ID
     * @param permittedCustomerIds æœ‰æƒé™çš„客户ID列表(null表示不限制)
     */
    default PageResult<CrmSaleQuotationDO> selectPage(CrmSaleQuotationPageReqVO pageReqVO, Long userId, List<Long> permittedCustomerIds) {
        MPJLambdaWrapperX<CrmSaleQuotationDO> query = new MPJLambdaWrapperX<>();
        // æ‹¼æŽ¥æ•°æ®æƒé™çš„æŸ¥è¯¢æ¡ä»¶
        CrmPermissionUtils.appendPermissionCondition(query, CrmBizTypeEnum.CRM_SALE_QUOTATION.getType(),
@@ -39,6 +51,15 @@
                .eqIfPresent(CrmSaleQuotationDO::getBusinessId, pageReqVO.getBusinessId())
                .eqIfPresent(CrmSaleQuotationDO::getStatus, pageReqVO.getStatus())
                .orderByDesc(CrmSaleQuotationDO::getId);
        // å®¢æˆ·æƒé™è¿‡æ»¤
        if (permittedCustomerIds != null) {
            if (CollUtil.isEmpty(permittedCustomerIds)) {
                return PageResult.empty();
            }
            query.in(CrmSaleQuotationDO::getCustomerId, permittedCustomerIds);
        }
        return selectJoinPage(pageReqVO, CrmSaleQuotationDO.class, query);
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractServiceImpl.java
@@ -28,6 +28,7 @@
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
@@ -99,6 +100,8 @@
    private CrmPermissionService crmPermissionService;
    @Resource
    private CrmCustomerService customerService;
    @Resource
    private CrmCustomerApi customerApi;
    @Resource
    private CrmBusinessService businessService;
    @Resource
@@ -468,8 +471,10 @@
                return PageResult.empty();
            }
        }
        // 2. æŸ¥è¯¢åˆ†é¡µ
        return contractMapper.selectPage(pageReqVO, userId, config);
        // 2. èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedCustomerIds = customerApi.getPermittedCustomerIds();
        // 3. æŸ¥è¯¢åˆ†é¡µ
        return contractMapper.selectPage(pageReqVO, userId, config, permittedCustomerIds);
    }
    @Override
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerService.java
@@ -78,6 +78,13 @@
    List<CrmCustomerDO> getCustomerList(Collection<Long> ids);
    /**
     * èŽ·å¾—æ‰€æœ‰å®¢æˆ·åˆ—è¡¨
     *
     * @return å®¢æˆ·åˆ—表
     */
    List<CrmCustomerDO> getCustomerList();
    /**
     * èŽ·å¾—å®¢æˆ· Map
     *
     * @param ids å®¢æˆ·ç¼–号数组
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/customer/CrmCustomerServiceImpl.java
@@ -494,6 +494,11 @@
    }
    @Override
    public List<CrmCustomerDO> getCustomerList() {
        return customerMapper.selectList();
    }
    @Override
    public PageResult<CrmCustomerDO> getCustomerPage(CrmCustomerPageReqVO pageReqVO, Long userId) {
        return customerMapper.selectPage(pageReqVO, userId);
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/quotation/CrmSaleQuotationServiceImpl.java
@@ -20,6 +20,7 @@
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
@@ -60,6 +61,8 @@
    @Resource
    private CrmCustomerService customerService;
    @Resource
    private CrmCustomerApi customerApi;
    @Resource
    private CrmBusinessService businessService;
    @Resource
@@ -229,7 +232,9 @@
    @Override
    public PageResult<CrmSaleQuotationDO> getSaleQuotationPage(CrmSaleQuotationPageReqVO pageReqVO, Long userId) {
        return saleQuotationMapper.selectPage(pageReqVO, userId);
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedCustomerIds = customerApi.getPermittedCustomerIds();
        return saleQuotationMapper.selectPage(pageReqVO, userId, permittedCustomerIds);
    }
    @Override
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -18,6 +18,7 @@
import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.api.salesnotice.MesWmSalesNoticeApi;
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;
@@ -55,6 +56,8 @@
    private MdmItemApi mdmItemApi;
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MesWmSalesNoticeApi salesNoticeApi;
    @PostMapping("/create")
    @Operation(summary = "创建销售订单")
@@ -165,6 +168,9 @@
        // 1.4 ç®¡ç†å‘˜ä¿¡æ¯
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(pageResult.getList(), saleOrder -> Long.parseLong(saleOrder.getCreator())));
        // 1.5 å‘货通知单信息
        Map<Long, Boolean> salesNoticeMap = salesNoticeApi.checkSalesNoticeBySaleOrderIds(
                convertSet(pageResult.getList(), ErpSaleOrderDO::getId));
        // 2. å¼€å§‹æ‹¼æŽ¥
        return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> {
            saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class,
@@ -173,6 +179,8 @@
            saleOrder.setProductNames(CollUtil.join(saleOrder.getItems(), ",", ErpSaleOrderRespVO.Item::getProductName));
            MapUtils.findAndThen(customerMap, saleOrder.getCustomerId(), supplier -> saleOrder.setCustomerName(supplier.getName()));
            MapUtils.findAndThen(userMap, Long.parseLong(saleOrder.getCreator()), user -> saleOrder.setCreatorName(user.getNickname()));
            // è®¾ç½®æ˜¯å¦å·²ç”Ÿæˆå‘货通知单
            saleOrder.setHasSalesNotice(salesNoticeMap.getOrDefault(saleOrder.getId(), false));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderRespVO.java
@@ -104,6 +104,11 @@
    @Schema(description = "出库状态", example = "0")
    private Integer outStatus;
    // ========== å‘货通知 ==========
    @Schema(description = "是否已生成发货通知单", example = "false")
    private Boolean hasSalesNotice;
    @Data
    public static class Item {
@@ -137,6 +142,14 @@
        @Schema(description = "销售出库数量", example = "100.00")
        private BigDecimal outCount;
        // ========== ç”Ÿäº§å…³è” ==========
        @Schema(description = "是否需要生产(0=否 1=是)", example = "1")
        private Integer needProduction;
        @Schema(description = "关联的 MPS ç¼–号", example = "1024")
        private Long mpsId;
        // ========== å…³è”字段 ==========
        @Schema(description = "产品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "巧克力")
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/vo/order/ErpSaleOrderSaveReqVO.java
@@ -74,6 +74,9 @@
        @Schema(description = "税率,百分比", example = "99.88")
        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/dal/dataobject/sale/ErpSaleOrderItemDO.java
@@ -84,4 +84,18 @@
     */
    private BigDecimal outCount;
    // ========== ç”Ÿäº§å…³è” ==========
    /**
     * æ˜¯å¦éœ€è¦ç”Ÿäº§ï¼ˆ0=否 1=是)
     */
    private Integer needProduction;
    /**
     * å…³è”çš„ MPS ç¼–号
     *
     * å…³è” {@link cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO#getId()}
     */
    private Long mpsId;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/mysql/sale/ErpSaleOrderMapper.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.erp.dal.mysql.sale;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
@@ -9,6 +10,8 @@
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * ERP é”€å”®è®¢å• Mapper
@@ -35,6 +38,33 @@
        return selectJoinPage(reqVO, ErpSaleOrderDO.class, query);
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<ErpSaleOrderDO> selectPage(ErpSaleOrderPageReqVO reqVO, List<Long> permittedCustomerIds) {
        MPJLambdaWrapperX<ErpSaleOrderDO> query = new MPJLambdaWrapperX<ErpSaleOrderDO>()
                .likeIfPresent(ErpSaleOrderDO::getNo, reqVO.getNo())
                .eqIfPresent(ErpSaleOrderDO::getCustomerId, reqVO.getCustomerId())
                .betweenIfPresent(ErpSaleOrderDO::getOrderTime, reqVO.getOrderTime())
                .eqIfPresent(ErpSaleOrderDO::getStatus, reqVO.getStatus())
                .likeIfPresent(ErpSaleOrderDO::getRemark, reqVO.getRemark())
                .eqIfPresent(ErpSaleOrderDO::getCreator, reqVO.getCreator())
                .orderByDesc(ErpSaleOrderDO::getId);
        // å®¢æˆ·æƒé™è¿‡æ»¤
        if (permittedCustomerIds != null) {
            if (CollUtil.isEmpty(permittedCustomerIds)) {
                return PageResult.empty();
            }
            query.in(ErpSaleOrderDO::getCustomerId, permittedCustomerIds);
        }
        if (reqVO.getProductId() != null) {
            query.leftJoin(ErpSaleOrderItemDO.class, ErpSaleOrderItemDO::getOrderId, ErpSaleOrderDO::getId)
                    .eq(reqVO.getProductId() != null, ErpSaleOrderItemDO::getProductId, reqVO.getProductId())
                    .groupBy(ErpSaleOrderDO::getId); // é¿å… 1 å¯¹å¤šæŸ¥è¯¢ï¼Œäº§ç”Ÿç›¸åŒçš„ 1
        }
        return selectJoinPage(reqVO, ErpSaleOrderDO.class, query);
    }
    default int updateByIdAndStatus(Long id, Integer status, ErpSaleOrderDO updateObj) {
        return update(updateObj, new LambdaUpdateWrapper<ErpSaleOrderDO>()
                .eq(ErpSaleOrderDO::getId, id).eq(ErpSaleOrderDO::getStatus, status));
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -309,7 +309,8 @@
    @Override
    public PageResult<ErpSaleOrderDO> getSaleOrderPage(ErpSaleOrderPageReqVO pageReqVO) {
        return saleOrderMapper.selectPage(pageReqVO);
        List<Long> permittedCustomerIds = customerApi.getPermittedCustomerIds();
        return saleOrderMapper.selectPage(pageReqVO, permittedCustomerIds);
    }
    // ==================== è®¢å•项 ====================
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOrderStatusListener.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.erp.service.sale.listener;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO;
import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper;
import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderMapper;
import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.api.mps.MesProMpsApi;
import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsCreateReqDTO;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
 * é”€å”®è®¢å•审批状态监听器
 *
 * ç›‘听 BPM æµç¨‹å®žä¾‹çŠ¶æ€å˜æ›´äº‹ä»¶ï¼Œå®¡æ ¸é€šè¿‡æ—¶è‡ªåŠ¨åˆ›å»º MPS
 *
 * @author èŠ‹é“æºç 
 */
@Component
@Slf4j
public class ErpSaleOrderStatusListener extends cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener {
    /**
     * é”€å”®è®¢å•审批流程定义 Key
     */
    private static final String PROCESS_DEFINITION_KEY = "sale_order_approve";
    @Resource
    private ErpSaleOrderMapper saleOrderMapper;
    @Resource
    private ErpSaleOrderItemMapper saleOrderItemMapper;
    @Resource
    private MesProMpsApi mpsApi;
    @Resource
    private MdmItemApi mdmItemApi;
    @Override
    protected String getProcessDefinitionKey() {
        return PROCESS_DEFINITION_KEY;
    }
    @Override
    protected void onEvent(BpmProcessInstanceStatusEvent event) {
        Long orderId;
        try {
            orderId = Long.parseLong(event.getBusinessKey());
        } catch (NumberFormatException e) {
            log.warn("[onEvent] businessKey({}) ä¸æ˜¯æœ‰æ•ˆçš„销售订单编号", event.getBusinessKey());
            return;
        }
        log.info("[onEvent] æ”¶åˆ° BPM å®¡æ‰¹äº‹ä»¶ï¼ŒorderId={}, status={}", orderId, event.getStatus());
        // åªæœ‰å®¡æ‰¹é€šè¿‡æ‰åˆ›å»º MPS
        if (!BpmTaskStatusEnum.APPROVE.getStatus().equals(event.getStatus())) {
            return;
        }
        try {
            // 1. èŽ·å–é”€å”®è®¢å•
            ErpSaleOrderDO saleOrder = saleOrderMapper.selectById(orderId);
            if (saleOrder == null) {
                log.warn("[onEvent] é”€å”®è®¢å•不存在,orderId={}", orderId);
                return;
            }
            // 2. èŽ·å–é”€å”®è®¢å•æ˜Žç»†
            List<ErpSaleOrderItemDO> items = saleOrderItemMapper.selectListByOrderId(orderId);
            if (CollUtil.isEmpty(items)) {
                log.info("[onEvent] é”€å”®è®¢å•({}) æ²¡æœ‰æ˜Žç»†ï¼Œæ— éœ€åˆ›å»º MPS", orderId);
                return;
            }
            // 3. æ›´æ–°è®¢å•状态为已审核
            saleOrderMapper.updateById(new ErpSaleOrderDO()
                    .setId(orderId)
                    .setStatus(ErpAuditStatus.APPROVE.getStatus()));
            // 4. éåŽ†æ˜Žç»†ï¼Œä¸ºéœ€è¦ç”Ÿäº§çš„æ˜Žç»†åˆ›å»º MPS
            int mpsCount = 0;
            for (ErpSaleOrderItemDO item : items) {
                if (item.getNeedProduction() == null || item.getNeedProduction() != 1) {
                    continue;
                }
                try {
                    Long mpsId = createMpsForSaleOrderItem(saleOrder, item);
                    if (mpsId != null) {
                        mpsCount++;
                        // æ›´æ–°é”€å”®è®¢å•明细的 MPS å…³è”
                        saleOrderItemMapper.updateById(new ErpSaleOrderItemDO()
                                .setId(item.getId())
                                .setMpsId(mpsId));
                    }
                } catch (Exception e) {
                    log.error("[onEvent] åˆ›å»º MPS å¤±è´¥ï¼ŒsaleOrderItemId={}", item.getId(), e);
                }
            }
            log.info("[onEvent] é”€å”®è®¢å•({}) å®¡æ‰¹å®Œæˆï¼Œåˆ›å»º MPS {} æ¡", orderId, mpsCount);
        } catch (Exception e) {
            log.error("[onEvent] å¤„理销售订单审批事件失败,orderId={}", orderId, e);
        }
    }
    /**
     * ä¸ºé”€å”®è®¢å•明细创建 MPS
     *
     * æ³¨æ„ï¼šé”€å”®è®¢å•明细的 productId æ˜¯ MDM ç‰©æ–™ ID,需要映射到 MES äº§å“
     */
    private Long createMpsForSaleOrderItem(ErpSaleOrderDO saleOrder, ErpSaleOrderItemDO item) {
        // èŽ·å– MDM ç‰©æ–™ä¿¡æ¯ï¼ˆç”¨äºŽèŽ·å–äº§å“åç§°ï¼‰
        String productName = null;
        try {
            MdmItemRespDTO mdmItem = mdmItemApi.getItem(item.getProductId()).getCheckedData();
            if (mdmItem != null) {
                productName = mdmItem.getName();
            }
        } catch (Exception e) {
            log.warn("[createMpsForSaleOrderItem] èŽ·å– MDM ç‰©æ–™ä¿¡æ¯å¤±è´¥ï¼ŒproductId={}", item.getProductId(), e);
        }
        MesProMpsCreateReqDTO createReqDTO = new MesProMpsCreateReqDTO();
        createReqDTO.setSaleOrderId(saleOrder.getId());
        createReqDTO.setSaleOrderNo(saleOrder.getNo());
        createReqDTO.setSaleOrderItemId(item.getId());
        createReqDTO.setMdmItemId(item.getProductId()); // é”€å”®è®¢å•明细的 productId æ˜¯ MDM ç‰©æ–™ ID
        createReqDTO.setProductName(productName); // äº§å“åç§°ç”¨äºŽé”™è¯¯æç¤º
        createReqDTO.setQuantity(item.getCount());
        createReqDTO.setClientId(saleOrder.getCustomerId());
        createReqDTO.setRequestDate(formatRequestDate(saleOrder.getOrderTime()));
        createReqDTO.setRemark("自动生成,来自销售订单:" + saleOrder.getNo());
        Long mpsId = mpsApi.createMps(createReqDTO);
        log.info("[createMpsForSaleOrderItem] MPS åˆ›å»ºæˆåŠŸï¼ŒmpsId={}, saleOrderItemId={}", mpsId, item.getId());
        return mpsId;
    }
    /**
     * æ ¼å¼åŒ–需求日期
     */
    private String formatRequestDate(LocalDateTime dateTime) {
        if (dateTime == null) {
            return null;
        }
        return dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/MesProMpsApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.mes.api.mps;
import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsCreateReqDTO;
import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsRespDTO;
import java.util.List;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)API æŽ¥å£
 *
 * ä¾› ERP æ¨¡å—调用,在销售订单审核通过时创建 MPS
 *
 * @author èŠ‹é“æºç 
 */
public interface MesProMpsApi {
    /**
     * åˆ›å»º MPS
     *
     * @param createReqDTO åˆ›å»ºä¿¡æ¯
     * @return MPS ç¼–号
     */
    Long createMps(MesProMpsCreateReqDTO createReqDTO);
    /**
     * æ ¹æ® ID èŽ·å– MPS
     *
     * @param id MPS ç¼–号
     * @return MPS ä¿¡æ¯
     */
    MesProMpsRespDTO getMps(Long id);
    /**
     * æ ¹æ®é”€å”®è®¢å•明细ID获取 MPS
     *
     * @param saleOrderItemId é”€å”®è®¢å•明细ID
     * @return MPS ä¿¡æ¯
     */
    MesProMpsRespDTO getMpsBySaleOrderItemId(Long saleOrderItemId);
    /**
     * æ‰¹é‡èŽ·å– MPS
     *
     * @param saleOrderItemIds é”€å”®è®¢å•明细ID列表
     * @return MPS åˆ—表
     */
    List<MesProMpsRespDTO> getMpsListBySaleOrderItemIds(List<Long> saleOrderItemIds);
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/dto/MesProMpsCreateReqDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.mes.api.mps.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
/**
 * MPS åˆ›å»ºè¯·æ±‚ DTO
 *
 * @author èŠ‹é“æºç 
 */
@Schema(description = "RPC æœåŠ¡ - MPS åˆ›å»º Request DTO")
@Data
public class MesProMpsCreateReqDTO {
    @Schema(description = "销售订单ID", example = "100")
    private Long saleOrderId;
    @Schema(description = "销售订单号", example = "SO-001")
    private String saleOrderNo;
    @Schema(description = "销售订单明细ID", example = "200")
    private Long saleOrderItemId;
    @Schema(description = "MDM ç‰©æ–™ID(销售订单明细的 productId)", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
    private Long mdmItemId;
    @Schema(description = "产品名称(用于错误提示)", example = "电路板A")
    private String productName;
    @Schema(description = "计划生产数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
    private BigDecimal quantity;
    @Schema(description = "客户ID", example = "300")
    private Long clientId;
    @Schema(description = "需求日期", example = "2024-01-01")
    private String requestDate;
    @Schema(description = "备注", example = "自动生成")
    private String remark;
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/mps/dto/MesProMpsRespDTO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.mes.api.mps.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MPS å“åº” DTO
 *
 * @author èŠ‹é“æºç 
 */
@Schema(description = "RPC æœåŠ¡ - MPS Response DTO")
@Data
public class MesProMpsRespDTO {
    @Schema(description = "MPS ç¼–号", example = "1024")
    private Long id;
    @Schema(description = "MPS ç¼–码", example = "MPS-001")
    private String code;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "销售订单ID", example = "100")
    private Long saleOrderId;
    @Schema(description = "销售订单号", example = "SO-001")
    private String saleOrderNo;
    @Schema(description = "销售订单明细ID", example = "200")
    private Long saleOrderItemId;
    @Schema(description = "产品ID", example = "100")
    private Long productId;
    @Schema(description = "计划生产数量", example = "100.00")
    private BigDecimal quantity;
    @Schema(description = "已生产数量", example = "50.00")
    private BigDecimal quantityProduced;
    @Schema(description = "客户ID", example = "300")
    private Long clientId;
    @Schema(description = "需求日期")
    private LocalDateTime requestDate;
    @Schema(description = "下发日期")
    private LocalDateTime issueDate;
    @Schema(description = "完成日期")
    private LocalDateTime finishDate;
    @Schema(description = "生产工单ID", example = "500")
    private Long workOrderId;
    @Schema(description = "生产工单编码", example = "WO-001")
    private String workOrderCode;
    @Schema(description = "是否合并下发", example = "0")
    private Integer mergeFlag;
    @Schema(description = "合并批次号", example = "MERGE-001")
    private String mergeBatchNo;
}
yudao-module-mes-api/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApi.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.mes.api.salesnotice;
import java.util.Collection;
import java.util.Map;
/**
 * MES å‘货通知单 API æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesWmSalesNoticeApi {
    /**
     * æ ¹æ®é”€å”®è®¢å• ID åˆ—表检查是否已生成发货通知单
     *
     * @param saleOrderIds é”€å”®è®¢å• ID åˆ—表
     * @return Map<销售订单ID, æ˜¯å¦å·²ç”Ÿæˆå‘货通知单>
     */
    Map<Long, Boolean> checkSalesNoticeBySaleOrderIds(Collection<Long> saleOrderIds);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/mps/MesProMpsApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.mes.api.mps;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsCreateReqDTO;
import cn.iocoder.yudao.module.mes.api.mps.dto.MesProMpsRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
import cn.iocoder.yudao.module.mes.service.pro.mps.MesProMpsService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collections;
import java.util.List;
/**
 * MPS API å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesProMpsApiImpl implements MesProMpsApi {
    @Resource
    private MesProMpsService mpsService;
    @Override
    public Long createMps(MesProMpsCreateReqDTO createReqDTO) {
        return mpsService.createMps(
                createReqDTO.getSaleOrderId(),
                createReqDTO.getSaleOrderNo(),
                createReqDTO.getSaleOrderItemId(),
                createReqDTO.getMdmItemId(),
                createReqDTO.getProductName(),
                createReqDTO.getQuantity(),
                createReqDTO.getClientId(),
                createReqDTO.getRequestDate(),
                createReqDTO.getRemark()
        );
    }
    @Override
    public MesProMpsRespDTO getMps(Long id) {
        MesProMpsDO mps = mpsService.getMps(id);
        return BeanUtils.toBean(mps, MesProMpsRespDTO.class);
    }
    @Override
    public MesProMpsRespDTO getMpsBySaleOrderItemId(Long saleOrderItemId) {
        MesProMpsDO mps = mpsService.getMpsBySaleOrderItemId(saleOrderItemId);
        return BeanUtils.toBean(mps, MesProMpsRespDTO.class);
    }
    @Override
    public List<MesProMpsRespDTO> getMpsListBySaleOrderItemIds(List<Long> saleOrderItemIds) {
        if (saleOrderItemIds == null || saleOrderItemIds.isEmpty()) {
            return Collections.emptyList();
        }
        List<MesProMpsDO> mpsList = mpsService.getMpsListBySaleOrderItemIds(saleOrderItemIds);
        return BeanUtils.toBean(mpsList, MesProMpsRespDTO.class);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/api/salesnotice/MesWmSalesNoticeApiImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.api.salesnotice;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice.MesWmSalesNoticeMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
 * MES å‘货通知单 API å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class MesWmSalesNoticeApiImpl implements MesWmSalesNoticeApi {
    @Resource
    private MesWmSalesNoticeMapper salesNoticeMapper;
    @Override
    public Map<Long, Boolean> checkSalesNoticeBySaleOrderIds(Collection<Long> saleOrderIds) {
        if (saleOrderIds == null || saleOrderIds.isEmpty()) {
            return Collections.emptyMap();
        }
        // æŸ¥è¯¢å·²å­˜åœ¨çš„销售订单 ID
        List<Long> existingSaleOrderIds = salesNoticeMapper.selectExistingSaleOrderIds(saleOrderIds);
        // æž„建返回结果
        Map<Long, Boolean> result = new HashMap<>();
        for (Long saleOrderId : saleOrderIds) {
            result.put(saleOrderId, existingSaleOrderIds.contains(saleOrderId));
        }
        return result;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/MesMdClientController.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientImportExcelVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientImportRespVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientPageReqVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientRespVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/client/vo/MesMdClientSaveReqVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/MesMdVendorController.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorImportExcelVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorImportRespVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorPageReqVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorRespVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/md/vendor/vo/MesMdVendorSaveReqVO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/MesProMpsController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,156 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.mps;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.mps.MesProMpsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
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 java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - MES ç”Ÿäº§ä¸»è®¡åˆ’")
@RestController
@RequestMapping("/mes/pro-mps")
@Validated
public class MesProMpsController {
    @Resource
    private MesProMpsService mpsService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private CrmCustomerApi customerApi;
    @PostMapping("/create")
    @Operation(summary = "创建生产主计划")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:create')")
    public CommonResult<Long> createMps(@Valid @RequestBody MesProMpsSaveReqVO createReqVO) {
        return success(mpsService.createMps(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新生产主计划")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:update')")
    public CommonResult<Boolean> updateMps(@Valid @RequestBody MesProMpsSaveReqVO updateReqVO) {
        mpsService.updateMps(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除生产主计划")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:delete')")
    public CommonResult<Boolean> deleteMps(@RequestParam("id") Long id) {
        mpsService.deleteMps(id);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得生产主计划")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:query')")
    public CommonResult<MesProMpsRespVO> getMps(@RequestParam("id") Long id) {
        MesProMpsDO mps = mpsService.getMps(id);
        if (mps == null) {
            return success(null);
        }
        return success(buildMpsRespVOList(ListUtil.of(mps)).get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得生产主计划分页")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:query')")
    public CommonResult<PageResult<MesProMpsRespVO>> getMpsPage(@Valid MesProMpsPageReqVO pageReqVO) {
        PageResult<MesProMpsDO> pageResult = mpsService.getMpsPage(pageReqVO);
        return success(new PageResult<>(buildMpsRespVOList(pageResult.getList()), pageResult.getTotal()));
    }
    @GetMapping("/export-excel")
    @Operation(summary = "导出生产主计划 Excel")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportMpsExcel(@Valid MesProMpsPageReqVO pageReqVO,
                               HttpServletResponse response) throws IOException {
        pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
        List<MesProMpsDO> list = mpsService.getMpsPage(pageReqVO).getList();
        List<MesProMpsRespVO> voList = buildMpsRespVOList(list);
        ExcelUtils.write(response, "生产主计划.xls", "数据", MesProMpsRespVO.class, voList);
    }
    @PostMapping("/issue")
    @Operation(summary = "下发生产主计划")
    @Parameter(name = "id", description = "MPS ç¼–号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:issue')")
    public CommonResult<Long> issueMps(@RequestParam("id") Long id) {
        return success(mpsService.issueMps(id));
    }
    @PostMapping("/merge-issue")
    @Operation(summary = "合并下发生产主计划")
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:merge-issue')")
    public CommonResult<Long> mergeAndIssueMps(@RequestBody List<Long> mpsIds) {
        return success(mpsService.mergeAndIssueMps(mpsIds));
    }
    @PostMapping("/cancel")
    @Operation(summary = "取消生产主计划")
    @Parameter(name = "id", description = "MPS ç¼–号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:pro-mps:cancel')")
    public CommonResult<Boolean> cancelMps(@RequestParam("id") Long id) {
        mpsService.cancelMps(id);
        return success(true);
    }
    // ==================== æ‹¼æŽ¥ VO ====================
    private List<MesProMpsRespVO> buildMpsRespVOList(List<MesProMpsDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        // 1. æ‰¹é‡èŽ·å–å…³è”æ•°æ®
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
                convertSet(list, MesProMpsDO::getProductId));
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(list, MesProMpsDO::getClientId));
        // 2. æ‹¼æŽ¥ VO
        return BeanUtils.toBean(list, MesProMpsRespVO.class, vo -> {
            MapUtils.findAndThen(itemMap, vo.getProductId(), item -> {
                vo.setProductName(item.getName())
                        .setProductCode(item.getCode())
                        .setProductSpecification(item.getSpecification());
            });
            MapUtils.findAndThen(clientMap, vo.getClientId(),
                    client -> vo.setClientName(client.getName()));
        });
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,46 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - MES ç”Ÿäº§ä¸»è®¡åˆ’分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesProMpsPageReqVO extends PageParam {
    @Schema(description = "MPS ç¼–码", example = "MPS-001")
    private String code;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "产品编号", example = "100")
    private Long productId;
    @Schema(description = "客户编号", example = "300")
    private Long clientId;
    @Schema(description = "销售订单号", example = "SO-001")
    private String saleOrderNo;
    @Schema(description = "生产工单ID", example = "500")
    private Long workOrderId;
    @Schema(description = "需求日期")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] requestDate;
    @Schema(description = "创建时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,125 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo;
import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat;
import cn.iocoder.yudao.framework.excel.core.convert.DictConvert;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES ç”Ÿäº§ä¸»è®¡åˆ’ Response VO")
@Data
@ExcelIgnoreUnannotated
public class MesProMpsRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @ExcelProperty("编号")
    private Long id;
    @Schema(description = "MPS ç¼–码", example = "MPS-001")
    @ExcelProperty("MPS ç¼–码")
    private String code;
    @Schema(description = "状态", example = "0")
    @ExcelProperty(value = "状态", converter = DictConvert.class)
    @DictFormat(DictTypeConstants.MES_PRO_MPS_STATUS)
    private Integer status;
    // ========== æ¥æºä¿¡æ¯ ==========
    @Schema(description = "销售订单ID", example = "100")
    private Long saleOrderId;
    @Schema(description = "销售订单号", example = "SO-001")
    @ExcelProperty("销售订单号")
    private String saleOrderNo;
    @Schema(description = "销售订单明细ID", example = "200")
    private Long saleOrderItemId;
    // ========== äº§å“ä¿¡æ¯ ==========
    @Schema(description = "产品编号", example = "100")
    private Long productId;
    @Schema(description = "产品编码", example = "P-001")
    @ExcelProperty("产品编码")
    private String productCode;
    @Schema(description = "产品名称", example = "电路板 A")
    @ExcelProperty("产品名称")
    private String productName;
    @Schema(description = "规格型号", example = "100x200mm")
    @ExcelProperty("规格型号")
    private String productSpecification;
    @Schema(description = "单位名称", example = "个")
    @ExcelProperty("单位")
    private String unitMeasureName;
    @Schema(description = "计划生产数量", example = "100.00")
    @ExcelProperty("计划生产数量")
    private BigDecimal quantity;
    @Schema(description = "已生产数量", example = "50.00")
    @ExcelProperty("已生产数量")
    private BigDecimal quantityProduced;
    // ========== å®¢æˆ·ä¿¡æ¯ ==========
    @Schema(description = "客户编号", example = "300")
    private Long clientId;
    @Schema(description = "客户编码", example = "C-001")
    @ExcelProperty("客户编码")
    private String clientCode;
    @Schema(description = "客户名称", example = "客户 A")
    @ExcelProperty("客户名称")
    private String clientName;
    // ========== æ—¶é—´ä¿¡æ¯ ==========
    @Schema(description = "需求日期")
    @ExcelProperty("需求日期")
    private LocalDateTime requestDate;
    @Schema(description = "下发日期")
    @ExcelProperty("下发日期")
    private LocalDateTime issueDate;
    @Schema(description = "完成日期")
    @ExcelProperty("完成日期")
    private LocalDateTime finishDate;
    // ========== ç”Ÿäº§å·¥å•关联 ==========
    @Schema(description = "生产工单ID", example = "500")
    private Long workOrderId;
    @Schema(description = "生产工单编码", example = "WO-001")
    @ExcelProperty("生产工单编码")
    private String workOrderCode;
    // ========== åˆå¹¶ä¸‹å‘标识 ==========
    @Schema(description = "是否合并下发", example = "0")
    private Integer mergeFlag;
    @Schema(description = "合并批次号", example = "MERGE-001")
    private String mergeBatchNo;
    @Schema(description = "备注", example = "备注")
    @ExcelProperty("备注")
    private String remark;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    @ExcelProperty("创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/mps/vo/MesProMpsSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - MES ç”Ÿäº§ä¸»è®¡åˆ’新增/修改 Request VO")
@Data
public class MesProMpsSaveReqVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "MPS ç¼–码(新增时不填自动生成)", example = "MPS-001")
    private String code;
    // ========== æ¥æºä¿¡æ¯ ==========
    @Schema(description = "销售订单ID", example = "100")
    private Long saleOrderId;
    @Schema(description = "销售订单号", example = "SO-001")
    private String saleOrderNo;
    @Schema(description = "销售订单明细ID", example = "200")
    private Long saleOrderItemId;
    // ========== äº§å“ä¿¡æ¯ ==========
    @Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
    @NotNull(message = "产品不能为空")
    private Long productId;
    @Schema(description = "计划生产数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00")
    @NotNull(message = "计划生产数量不能为空")
    private BigDecimal quantity;
    @Schema(description = "计量单位ID", example = "1")
    private Long unitMeasureId;
    // ========== å®¢æˆ·ä¿¡æ¯ ==========
    @Schema(description = "客户编号", example = "300")
    private Long clientId;
    // ========== æ—¶é—´ä¿¡æ¯ ==========
    @Schema(description = "需求日期")
    private LocalDateTime requestDate;
    @Schema(description = "备注", example = "备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/task/MesProTaskController.java
@@ -14,7 +14,8 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.task.vo.MesProTaskSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
@@ -22,7 +23,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
@@ -72,7 +73,7 @@
    @Resource
    private MesMdItemService itemService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesMdUnitMeasureService unitMeasureService;
    @Resource
@@ -232,7 +233,7 @@
                convertSet(list, MesProTaskDO::getItemId));
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> customerMap = customerApi.getCustomerMap(
                convertSet(list, MesProTaskDO::getClientId));
        // å·¥åºçš„ checkFlag:批量查询后构建 routeId -> processId -> checkFlag çš„双层 Map
        Set<Long> routeIds = convertSet(list, MesProTaskDO::getRouteId);
@@ -279,8 +280,8 @@
                findAndThen(unitMeasureMap, item.getUnitMeasureId(), unit ->
                        vo.setUnitMeasureName(unit.getName()));
            });
            findAndThen(clientMap, task.getClientId(), c ->
                    vo.setClientName(c.getName()));
            findAndThen(customerMap, task.getClientId(), customer ->
                    vo.setClientName(customer.getName()));
            findAndThen(routeProcessCheckFlagMap, task.getRouteId(), processCheckMap ->
                    findAndThen(processCheckMap, task.getProcessId(), vo::setCheckFlag));
            return vo;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderController.java
@@ -12,15 +12,15 @@
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -50,9 +50,9 @@
    @Resource
    private MesMdItemService itemService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdUnitMeasureService unitMeasureService;
@@ -148,10 +148,11 @@
        // 1. æ‰¹é‡èŽ·å–å…³è”æ•°æ®
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
                convertSet(list, MesProWorkOrderDO::getProductId));
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> customerMap = customerApi.getCustomerMap(
                convertSet(list, MesProWorkOrderDO::getClientId));
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesProWorkOrderDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> supplierMap = supplierApi.getSupplierList(
                convertSet(list, MesProWorkOrderDO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        // å•位从产品关联的 item èŽ·å¾—
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
@@ -165,10 +166,10 @@
                MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(),
                        unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName()));
            });
            MapUtils.findAndThen(clientMap, vo.getClientId(),
                    client -> vo.setClientName(client.getName()).setClientCode(client.getCode()));
            MapUtils.findAndThen(vendorMap, vo.getVendorId(),
                    vendor -> vo.setVendorName(vendor.getName()).setVendorCode(vendor.getCode()));
            MapUtils.findAndThen(customerMap, vo.getClientId(),
                    customer -> vo.setClientName(customer.getName()));
            MapUtils.findAndThen(supplierMap, vo.getVendorId(),
                    supplier -> vo.setVendorName(supplier.getName()));
            MapUtils.findAndThen(parentMap, vo.getParentId(),
                    parent -> vo.setParentCode(parent.getCode()));
        });
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/iqc/MesQcIqcController.java
@@ -10,13 +10,13 @@
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.iqc.vo.MesQcIqcSaveReqVO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.qc.iqc.MesQcIqcService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
@@ -50,7 +50,7 @@
    @Resource
    private MesQcIqcService iqcService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdItemService itemService;
    @Resource
@@ -128,8 +128,9 @@
            return Collections.emptyList();
        }
        // æ‰¹é‡æŸ¥è¯¢ä¾›åº”商
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesQcIqcDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesQcIqcDO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        // æ‰¹é‡æŸ¥è¯¢ç‰©æ–™
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
                convertSet(list, MesQcIqcDO::getItemId));
@@ -142,7 +143,7 @@
        // æ‹¼è£… VO
        return BeanUtils.toBean(list, MesQcIqcRespVO.class, vo -> {
            findAndThen(vendorMap, vo.getVendorId(),
                    vendor -> vo.setVendorNickname(vendor.getNickname()));
                    vendor -> vo.setVendorNickname(vendor.getName()));
            findAndThen(itemMap, vo.getItemId(), item -> {
                vo.setItemCode(item.getCode());
                vo.setItemName(item.getName());
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/oqc/MesQcOqcController.java
@@ -10,11 +10,12 @@
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
@@ -49,7 +50,7 @@
    @Resource
    private MesQcOqcService oqcService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesMdItemService itemService;
    @Resource
@@ -127,7 +128,7 @@
            return Collections.emptyList();
        }
        // æ‰¹é‡æŸ¥è¯¢å®¢æˆ·
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(list, MesQcOqcDO::getClientId));
        // æ‰¹é‡æŸ¥è¯¢ç‰©æ–™
        Map<Long, MesMdItemDO> itemMap = itemService.getItemMap(
@@ -141,7 +142,7 @@
        // æ‹¼è£… VO
        return BeanUtils.toBean(list, MesQcOqcRespVO.class, vo -> {
            findAndThen(clientMap, vo.getClientId(),
                    client -> vo.setClientNickname(client.getNickname()));
                    client -> vo.setClientNickname(client.getName()));
            findAndThen(itemMap, vo.getItemId(), item -> {
                vo.setItemCode(item.getCode()).setItemName(item.getName()).setItemSpecification(item.getSpecification());
                findAndThen(unitMeasureMap, item.getUnitMeasureId(),
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/pendinginspect/MesQcPendingInspectController.java
@@ -5,16 +5,16 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.unitmeasure.MesMdUnitMeasureService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.qc.pendinginspect.MesQcPendingInspectService;
@@ -48,9 +48,9 @@
    @Resource
    private MesMdUnitMeasureService unitMeasureService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesMdWorkstationService workstationService;
    @Resource
@@ -80,10 +80,11 @@
        Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap(
                convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId));
        // æ‰¹é‡æŸ¥è¯¢ä¾›åº”商(IQC åœºæ™¯ï¼‰
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesQcPendingInspectRespVO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesQcPendingInspectRespVO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        // æ‰¹é‡æŸ¥è¯¢å®¢æˆ·ï¼ˆOQC/RQC åœºæ™¯ï¼‰
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(list, MesQcPendingInspectRespVO::getClientId));
        // æ‰¹é‡æŸ¥è¯¢å·¥ä½œç«™ï¼ˆIPQC/RQC åœºæ™¯ï¼‰
        Map<Long, MesMdWorkstationDO> workstationMap = workstationService.getWorkstationMap(
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/batch/MesWmBatchController.java
@@ -7,21 +7,21 @@
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchRespVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.tm.tool.MesTmToolDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
@@ -60,9 +60,9 @@
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
@@ -116,9 +116,10 @@
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap(
                convertSet(list, MesWmBatchDO::getItemId));
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesWmBatchDO::getVendorId));
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesWmBatchDO::getVendorId)).getCheckedData().stream()
                .collect(Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(list, MesWmBatchDO::getClientId));
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
                convertSet(list, MesWmBatchDO::getWorkOrderId));
@@ -140,9 +141,9 @@
                        unitMeasure -> vo.setUnitName(unitMeasure.getName()));
            });
            MapUtils.findAndThen(vendorMap, vo.getVendorId(),
                    vendor -> vo.setVendorCode(vendor.getCode()).setVendorName(vendor.getName()));
                    vendor -> vo.setVendorName(vendor.getName()));
            MapUtils.findAndThen(clientMap, vo.getClientId(),
                    client -> vo.setClientCode(client.getCode()).setClientName(client.getName()));
                    client -> vo.setClientName(client.getName()));
            MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(),
                    workOrder -> vo.setWorkOrderCode(workOrder.getCode()));
            MapUtils.findAndThen(taskMap, vo.getTaskId(),
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/materialstock/MesWmMaterialStockController.java
@@ -12,10 +12,11 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmMaterialStockFreezeReqVO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
@@ -23,7 +24,6 @@
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService;
import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService;
@@ -68,7 +68,7 @@
    @Resource
    private MesWmWarehouseAreaService areaService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @GetMapping("/get")
    @Operation(summary = "获得库存记录")
@@ -144,8 +144,9 @@
                convertSet(list, MesWmMaterialStockDO::getLocationId));
        Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap(
                convertSet(list, MesWmMaterialStockDO::getAreaId));
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesWmMaterialStockDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesWmMaterialStockDO::getVendorId)).getCheckedData().stream()
                .collect(Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmMaterialStockRespVO.class, vo -> {
            MapUtils.findAndThen(itemMap, vo.getItemId(), item -> {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java
@@ -11,10 +11,10 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssuePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourceissue.vo.MesWmOutsourceIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue.MesWmOutsourceIssueDO;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.outsourceissue.MesWmOutsourceIssueService;
import io.swagger.v3.oas.annotations.Operation;
@@ -45,7 +45,7 @@
    @Resource
    private MesWmOutsourceIssueService outsourceIssueService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesProWorkOrderService workOrderService;
@@ -157,14 +157,15 @@
            return Collections.emptyList();
        }
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesWmOutsourceIssueDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesWmOutsourceIssueDO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
                convertSet(list, MesWmOutsourceIssueDO::getWorkOrderId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmOutsourceIssueRespVO.class, vo -> {
            MapUtils.findAndThen(vendorMap, vo.getVendorId(), vendor ->
                    vo.setVendorCode(vendor.getCode()).setVendorName(vendor.getName()));
                    vo.setVendorName(vendor.getName()));
            MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(), workOrder ->
                    vo.setWorkOrderCode(workOrder.getCode()).setWorkOrderName(workOrder.getName()));
        });
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourcereceipt/MesWmOutsourceReceiptController.java
@@ -11,10 +11,10 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.outsourcereceipt.vo.MesWmOutsourceReceiptSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt.MesWmOutsourceReceiptDO;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.outsourcereceipt.MesWmOutsourceReceiptService;
import io.swagger.v3.oas.annotations.Operation;
@@ -45,7 +45,7 @@
    @Resource
    private MesWmOutsourceReceiptService outsourceReceiptService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesProWorkOrderService workOrderService;
@@ -149,8 +149,9 @@
            return Collections.emptyList();
        }
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesWmOutsourceReceiptDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesWmOutsourceReceiptDO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        Map<Long, MesProWorkOrderDO> workOrderMap = workOrderService.getWorkOrderMap(
                convertSet(list, MesWmOutsourceReceiptDO::getWorkOrderId));
        // 2. æž„建结果
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageController.java
@@ -8,10 +8,10 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.MesWmPackagePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.MesWmPackageRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.MesWmPackageSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.packages.MesWmPackageDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mes.service.wm.packages.MesWmPackageService;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
@@ -44,7 +44,7 @@
    @Resource
    private MesWmPackageService packageService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
@@ -131,7 +131,7 @@
            return Collections.emptyList();
        }
        // æ‰¹é‡æŸ¥è¯¢å®¢æˆ·
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(list, MesWmPackageDO::getClientId));
        // æ‰¹é‡æŸ¥è¯¢è®¡é‡å•位(尺寸 + é‡é‡ï¼‰
        Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList(
@@ -143,8 +143,7 @@
        // æž„建返回结果
        return BeanUtils.toBean(list, MesWmPackageRespVO.class, vo -> {
            MapUtils.findAndThen(clientMap, vo.getClientId(),
                    client -> vo.setClientCode(client.getCode()).setClientName(client.getName())
                            .setClientNickname(client.getNickname()));
                    client -> vo.setClientName(client.getName()));
            MapUtils.findAndThen(unitMeasureMap, vo.getSizeUnitId(),
                    unit -> vo.setSizeUnitName(unit.getName()));
            MapUtils.findAndThen(unitMeasureMap, vo.getWeightUnitId(),
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/productissue/MesWmProductIssueController.java
@@ -11,12 +11,13 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssuePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssueRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.productissue.vo.MesWmProductIssueSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productissue.MesWmProductIssueDO;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.service.md.workstation.MesMdWorkstationService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
@@ -55,7 +56,7 @@
    @Resource
    private MesProTaskService taskService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @PostMapping("/create")
    @Operation(summary = "创建领料出库单")
@@ -171,7 +172,7 @@
                convertSet(list, MesWmProductIssueDO::getWorkOrderId));
        Map<Long, MesProTaskDO> taskMap = taskService.getTaskMap(
                convertSet(list, MesWmProductIssueDO::getTaskId));
        Map<Long, MesMdClientDO> clientMap = clientService.getClientMap(
        Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap(
                convertSet(workOrderMap.values(), MesProWorkOrderDO::getClientId));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmProductIssueRespVO.class, vo -> {
@@ -182,7 +183,7 @@
            MapUtils.findAndThen(workOrderMap, vo.getWorkOrderId(), workOrder -> {
                vo.setWorkOrderCode(workOrder.getCode());
                MapUtils.findAndThen(clientMap, workOrder.getClientId(), client ->
                        vo.setClientCode(client.getCode()).setClientName(client.getName()));
                        vo.setClientName(client.getName()));
            });
        });
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnvendor/MesWmReturnVendorController.java
@@ -11,9 +11,9 @@
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.MesWmReturnVendorPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.MesWmReturnVendorRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnvendor.vo.MesWmReturnVendorSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor.MesWmReturnVendorDO;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.wm.returnvendor.MesWmReturnVendorService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
@@ -43,7 +43,7 @@
    @Resource
    private MesWmReturnVendorService returnVendorService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @PostMapping("/create")
    @Operation(summary = "创建供应商退货单")
@@ -153,12 +153,13 @@
            return Collections.emptyList();
        }
        // 1. èŽ·å¾—å…³è”æ•°æ®
        Map<Long, MesMdVendorDO> vendorMap = vendorService.getVendorMap(
                convertSet(list, MesWmReturnVendorDO::getVendorId));
        Map<Long, ErpSupplierRespDTO> vendorMap = supplierApi.getSupplierList(
                convertSet(list, MesWmReturnVendorDO::getVendorId)).getCheckedData().stream()
                .collect(java.util.stream.Collectors.toMap(ErpSupplierRespDTO::getId, s -> s));
        // 2. æž„建结果
        return BeanUtils.toBean(list, MesWmReturnVendorRespVO.class, vo ->
                MapUtils.findAndThen(vendorMap, vo.getVendorId(), vendor ->
                        vo.setVendorCode(vendor.getCode()).setVendorName(vendor.getName()).setVendorNickname(vendor.getNickname())));
                        vo.setVendorName(vendor.getName())));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/client/MesMdClientDO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/md/vendor/MesMdVendorDO.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/mps/MesProMpsDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.DictTypeConstants;
import cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pro_mps")
@KeySequence("mes_pro_mps_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProMpsDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * MPS ç¼–码
     */
    private String code;
    /**
     * çŠ¶æ€
     *
     * å­—å…¸ {@link DictTypeConstants#MES_PRO_MPS_STATUS}
     * æžšä¸¾ {@link MesProMpsStatusEnum}
     */
    private Integer status;
    // ========== æ¥æºä¿¡æ¯ï¼ˆæ¥è‡ªé”€å”®è®¢å•) ==========
    /**
     * é”€å”®è®¢å•ID
     */
    private Long saleOrderId;
    /**
     * é”€å”®è®¢å•号
     */
    private String saleOrderNo;
    /**
     * é”€å”®è®¢å•明细ID
     */
    private Long saleOrderItemId;
    // ========== äº§å“ä¿¡æ¯ ==========
    /**
     * äº§å“ç¼–号
     *
     * å…³è” {@link MesMdItemDO#getId()}
     */
    private Long productId;
    /**
     * è®¡åˆ’生产数量
     */
    private BigDecimal quantity;
    /**
     * å·²ç”Ÿäº§æ•°é‡
     */
    private BigDecimal quantityProduced;
    /**
     * è®¡é‡å•位ID
     */
    private Long unitMeasureId;
    // ========== å®¢æˆ·ä¿¡æ¯ ==========
    /**
     * å®¢æˆ·ç¼–号
     *
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
    // ========== æ—¶é—´ä¿¡æ¯ ==========
    /**
     * éœ€æ±‚日期
     */
    private LocalDateTime requestDate;
    /**
     * ä¸‹å‘日期
     */
    private LocalDateTime issueDate;
    /**
     * å®Œæˆæ—¥æœŸ
     */
    private LocalDateTime finishDate;
    // ========== ç”Ÿäº§å·¥å•关联(下发后生成) ==========
    /**
     * ç”Ÿäº§å·¥å•ID
     *
     * å…³è” {@link MesProWorkOrderDO#getId()}
     */
    private Long workOrderId;
    /**
     * ç”Ÿäº§å·¥å•编码
     */
    private String workOrderCode;
    // ========== åˆå¹¶ä¸‹å‘标识 ==========
    /**
     * æ˜¯å¦åˆå¹¶ä¸‹å‘(0=否 1=是)
     */
    private Integer mergeFlag;
    /**
     * åˆå¹¶æ‰¹æ¬¡å·
     */
    private String mergeBatchNo;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/mps/MesProMpsMergeItemDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.math.BigDecimal;
/**
 * MPS åˆå¹¶ä¸‹å‘明细 DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("mes_pro_mps_merge_item")
@KeySequence("mes_pro_mps_merge_item_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesProMpsMergeItemDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * åˆå¹¶æ‰¹æ¬¡å·
     */
    private String mergeBatchNo;
    /**
     * MPS ç¼–号
     *
     * å…³è” {@link MesProMpsDO#getId()}
     */
    private Long mpsId;
    /**
     * ç”Ÿæˆçš„生产工单ID
     *
     * å…³è” {@link MesProWorkOrderDO#getId()}
     */
    private Long workOrderId;
    /**
     * åˆ†é…æ•°é‡
     */
    private BigDecimal quantity;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/task/MesProTaskDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.task;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO;
@@ -100,7 +99,7 @@
    /**
     * å®¢æˆ·ç¼–号
     *
     * å…³è” {@link MesMdClientDO#getId()}
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java
@@ -1,9 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderTypeEnum;
@@ -92,13 +90,13 @@
    /**
     * å®¢æˆ·ç¼–号
     *
     * å…³è” {@link MesMdClientDO#getId()}
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
    /**
     * ä¾›åº”商编号
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/iqc/MesQcIqcDO.java
@@ -2,7 +2,6 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeLineDO;
@@ -86,7 +85,7 @@
    /**
     * ä¾›åº”商 ID
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/qc/oqc/MesQcOqcDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -81,7 +80,7 @@
    /**
     * å®¢æˆ· ID
     *
     * å…³è” {@link MesMdClientDO#getId()}
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/arrivalnotice/MesWmArrivalNoticeDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmArrivalNoticeStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -50,7 +49,7 @@
    /**
     * ä¾›åº”商编号
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/batch/MesWmBatchDO.java
@@ -1,9 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.task.MesProTaskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
@@ -60,13 +58,13 @@
    /**
     * ä¾›åº”商ID
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
     * å®¢æˆ·ID
     *
     * å…³è” {@link MesMdClientDO#getId()}
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/itemreceipt/MesWmItemReceiptDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemreceipt;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.arrivalnotice.MesWmArrivalNoticeDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmItemReceiptStatusEnum;
@@ -60,7 +59,7 @@
    /**
     * ä¾›åº”商编号
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/materialstock/MesWmMaterialStockDO.java
@@ -3,7 +3,6 @@
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO;
@@ -87,7 +86,7 @@
    /**
     * ä¾›åº”商编号
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourceissue/MesWmOutsourceIssueDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourceissue;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceIssueStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
@@ -43,7 +42,7 @@
    /**
     * ä¾›åº”商ID
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/outsourcereceipt/MesWmOutsourceReceiptDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.outsourcereceipt;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceReceiptStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -46,7 +45,7 @@
    /**
     * ä¾›åº”商编号
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/packages/MesWmPackageDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.packages;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.unitmeasure.MesMdUnitMeasureDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -54,7 +53,7 @@
    /**
     * å®¢æˆ· ID
     *
     * å…³è” {@link MesMdClientDO#getId()}
     * å…³è” CRM å®¢æˆ· {@link cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO#getId()}
     */
    private Long clientId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/productsales/MesWmProductSalesDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/returnvendor/MesWmReturnVendorDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnvendor;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.vendor.MesMdVendorDO;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnVendorStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
@@ -44,7 +43,7 @@
    /**
     * ä¾›åº”商 ID
     *
     * å…³è” {@link MesMdVendorDO#getId()}
     * å…³è” ERP ä¾›åº”商 {@link cn.iocoder.yudao.module.erp.api.purchase.dto.ErpSupplierRespDTO#getId()}
     */
    private Long vendorId;
    /**
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/wm/salesnotice/MesWmSalesNoticeDO.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.client.MesMdClientDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/client/MesMdClientMapper.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/item/MesMdItemMapper.java
@@ -43,4 +43,8 @@
        return selectCount(MesMdItemDO::getUnitMeasureId, unitMeasureId);
    }
    default MesMdItemDO selectByMdmItemId(Long mdmItemId) {
        return selectOne(MesMdItemDO::getMdmItemId, mdmItemId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/md/vendor/MesMdVendorMapper.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.mps;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
import cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)Mapper
 *
 * @author èŠ‹é“æºç 
 */
@Mapper
public interface MesProMpsMapper extends BaseMapperX<MesProMpsDO> {
    default PageResult<MesProMpsDO> selectPage(MesProMpsPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesProMpsDO>()
                .likeIfPresent(MesProMpsDO::getCode, reqVO.getCode())
                .eqIfPresent(MesProMpsDO::getStatus, reqVO.getStatus())
                .eqIfPresent(MesProMpsDO::getProductId, reqVO.getProductId())
                .eqIfPresent(MesProMpsDO::getClientId, reqVO.getClientId())
                .likeIfPresent(MesProMpsDO::getSaleOrderNo, reqVO.getSaleOrderNo())
                .eqIfPresent(MesProMpsDO::getWorkOrderId, reqVO.getWorkOrderId())
                .betweenIfPresent(MesProMpsDO::getRequestDate, reqVO.getRequestDate())
                .betweenIfPresent(MesProMpsDO::getCreateTime, reqVO.getCreateTime())
                .orderByDesc(MesProMpsDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesProMpsDO> selectPage(MesProMpsPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesProMpsDO> wrapper = new LambdaQueryWrapperX<MesProMpsDO>()
                .likeIfPresent(MesProMpsDO::getCode, reqVO.getCode())
                .eqIfPresent(MesProMpsDO::getStatus, reqVO.getStatus())
                .eqIfPresent(MesProMpsDO::getProductId, reqVO.getProductId())
                .eqIfPresent(MesProMpsDO::getClientId, reqVO.getClientId())
                .likeIfPresent(MesProMpsDO::getSaleOrderNo, reqVO.getSaleOrderNo())
                .eqIfPresent(MesProMpsDO::getWorkOrderId, reqVO.getWorkOrderId())
                .betweenIfPresent(MesProMpsDO::getRequestDate, reqVO.getRequestDate())
                .betweenIfPresent(MesProMpsDO::getCreateTime, reqVO.getCreateTime())
                .orderByDesc(MesProMpsDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesProMpsDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesProMpsDO selectByCode(String code) {
        return selectOne(MesProMpsDO::getCode, code);
    }
    default MesProMpsDO selectBySaleOrderItemId(Long saleOrderItemId) {
        return selectOne(MesProMpsDO::getSaleOrderItemId, saleOrderItemId);
    }
    default List<MesProMpsDO> selectListBySaleOrderItemIds(Collection<Long> saleOrderItemIds) {
        if (saleOrderItemIds == null || saleOrderItemIds.isEmpty()) {
            return Collections.emptyList();
        }
        return selectList(MesProMpsDO::getSaleOrderItemId, saleOrderItemIds);
    }
    /**
     * èŽ·å–å¯åˆå¹¶çš„ MPS åˆ—表(相同产品、草稿状态)
     */
    default List<MesProMpsDO> selectMergeableList(Long productId) {
        return selectList(new LambdaQueryWrapperX<MesProMpsDO>()
                .eq(MesProMpsDO::getProductId, productId)
                .eq(MesProMpsDO::getStatus, MesProMpsStatusEnum.DRAFT.getStatus())
                .orderByAsc(MesProMpsDO::getId));
    }
    /**
     * æ›´æ–°å·²ç”Ÿäº§æ•°é‡
     */
    default void updateProducedQuantity(Long id, BigDecimal incrQuantity) {
        update(null, new LambdaUpdateWrapper<MesProMpsDO>()
                .eq(MesProMpsDO::getId, id)
                .setSql("quantity_produced = IFNULL(quantity_produced, 0) + " + incrQuantity));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/mps/MesProMpsMergeItemMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.mps;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsMergeItemDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * MPS åˆå¹¶ä¸‹å‘明细 Mapper
 *
 * @author èŠ‹é“æºç 
 */
@Mapper
public interface MesProMpsMergeItemMapper extends BaseMapperX<MesProMpsMergeItemDO> {
    default List<MesProMpsMergeItemDO> selectListByMergeBatchNo(String mergeBatchNo) {
        return selectList(MesProMpsMergeItemDO::getMergeBatchNo, mergeBatchNo);
    }
    default List<MesProMpsMergeItemDO> selectListByMpsId(Long mpsId) {
        return selectList(MesProMpsMergeItemDO::getMpsId, mpsId);
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/pro/workorder/MesProWorkOrderMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -9,6 +10,7 @@
import org.apache.ibatis.annotations.Mapper;
import java.math.BigDecimal;
import java.util.List;
/**
 * MES ç”Ÿäº§å·¥å• Mapper
@@ -31,6 +33,29 @@
                .orderByDesc(MesProWorkOrderDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesProWorkOrderDO> selectPage(MesProWorkOrderPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesProWorkOrderDO> wrapper = new LambdaQueryWrapperX<MesProWorkOrderDO>()
                .likeIfPresent(MesProWorkOrderDO::getCode, reqVO.getCode())
                .likeIfPresent(MesProWorkOrderDO::getName, reqVO.getName())
                .eqIfPresent(MesProWorkOrderDO::getType, reqVO.getType())
                .likeIfPresent(MesProWorkOrderDO::getOrderSourceCode, reqVO.getOrderSourceCode())
                .eqIfPresent(MesProWorkOrderDO::getProductId, reqVO.getProductId())
                .eqIfPresent(MesProWorkOrderDO::getClientId, reqVO.getClientId())
                .eqIfPresent(MesProWorkOrderDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesProWorkOrderDO::getRequestDate, reqVO.getRequestDate())
                .orderByDesc(MesProWorkOrderDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesProWorkOrderDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesProWorkOrderDO selectByCode(String code) {
        return selectOne(MesProWorkOrderDO::getCode, code);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/oqc/MesQcOqcMapper.java
@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.mes.dal.mysql.qc.oqc;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * MES å‡ºè´§æ£€éªŒå•(OQC) Mapper
@@ -25,6 +28,26 @@
                .orderByDesc(MesQcOqcDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesQcOqcDO> selectPage(MesQcOqcPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesQcOqcDO> wrapper = new LambdaQueryWrapperX<MesQcOqcDO>()
                .likeIfPresent(MesQcOqcDO::getCode, reqVO.getCode())
                .eqIfPresent(MesQcOqcDO::getClientId, reqVO.getClientId())
                .likeIfPresent(MesQcOqcDO::getBatchCode, reqVO.getBatchCode())
                .eqIfPresent(MesQcOqcDO::getItemId, reqVO.getItemId())
                .eqIfPresent(MesQcOqcDO::getCheckResult, reqVO.getCheckResult())
                .orderByDesc(MesQcOqcDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesQcOqcDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesQcOqcDO selectByCode(String code) {
        return selectOne(MesQcOqcDO::getCode, code);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/batch/MesWmBatchMapper.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.mes.dal.mysql.wm.batch;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
@@ -50,6 +51,37 @@
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesWmBatchDO> selectPage(MesWmBatchPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesWmBatchDO> wrapper = new LambdaQueryWrapperX<MesWmBatchDO>()
                .likeIfPresent(MesWmBatchDO::getCode, reqVO.getCode())
                .eqIfPresent(MesWmBatchDO::getItemId, reqVO.getItemId())
                .eqIfPresent(MesWmBatchDO::getVendorId, reqVO.getVendorId())
                .eqIfPresent(MesWmBatchDO::getClientId, reqVO.getClientId())
                .likeIfPresent(MesWmBatchDO::getSalesOrderCode, reqVO.getSalesOrderCode())
                .likeIfPresent(MesWmBatchDO::getPurchaseOrderCode, reqVO.getPurchaseOrderCode())
                .eqIfPresent(MesWmBatchDO::getWorkOrderId, reqVO.getWorkOrderId())
                .eqIfPresent(MesWmBatchDO::getTaskId, reqVO.getTaskId())
                .eqIfPresent(MesWmBatchDO::getWorkstationId, reqVO.getWorkstationId())
                .eqIfPresent(MesWmBatchDO::getToolId, reqVO.getToolId())
                .eqIfPresent(MesWmBatchDO::getMoldId, reqVO.getMoldId())
                .likeIfPresent(MesWmBatchDO::getLotNumber, reqVO.getLotNumber())
                .eqIfPresent(MesWmBatchDO::getQualityStatus, reqVO.getQualityStatus())
                .betweenIfPresent(MesWmBatchDO::getProduceDate, reqVO.getProduceDate())
                .betweenIfPresent(MesWmBatchDO::getExpireDate, reqVO.getExpireDate())
                .betweenIfPresent(MesWmBatchDO::getReceiptDate, reqVO.getReceiptDate())
                .orderByDesc(MesWmBatchDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesWmBatchDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    /**
     * æ ¹æ®å‚数查询匹配的第一条批次记录
     * <p>
     * ä½¿ç”¨ NULL å€¼ç²¾ç¡®åŒ¹é…ï¼Œè¿”回 ID æœ€å°çš„æ‰¹æ¬¡
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/packages/MesWmPackageMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.mysql.wm.packages;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -30,6 +31,27 @@
                .orderByDesc(MesWmPackageDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesWmPackageDO> selectPage(MesWmPackagePageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesWmPackageDO> wrapper = new LambdaQueryWrapperX<MesWmPackageDO>()
                .likeIfPresent(MesWmPackageDO::getCode, reqVO.getCode())
                .likeIfPresent(MesWmPackageDO::getSalesOrderCode, reqVO.getSalesOrderCode())
                .eqIfPresent(MesWmPackageDO::getClientId, reqVO.getClientId())
                .eqIfPresent(MesWmPackageDO::getParentId, reqVO.getParentId())
                .eqIfPresent(MesWmPackageDO::getInspectorUserId, reqVO.getInspectorUserId())
                .eqIfPresent(MesWmPackageDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesWmPackageDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesWmPackageDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesWmPackageDO selectByCode(String code) {
        return selectOne(MesWmPackageDO::getCode, code);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/productsales/MesWmProductSalesMapper.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
@@ -27,6 +28,26 @@
                .orderByDesc(MesWmProductSalesDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesWmProductSalesDO> selectPage(MesWmProductSalesPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesWmProductSalesDO> wrapper = new LambdaQueryWrapperX<MesWmProductSalesDO>()
                .likeIfPresent(MesWmProductSalesDO::getCode, reqVO.getCode())
                .likeIfPresent(MesWmProductSalesDO::getName, reqVO.getName())
                .eqIfPresent(MesWmProductSalesDO::getClientId, reqVO.getClientId())
                .eqIfPresent(MesWmProductSalesDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesWmProductSalesDO::getSalesDate, reqVO.getShipmentDate())
                .orderByDesc(MesWmProductSalesDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesWmProductSalesDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesWmProductSalesDO selectByCode(String code) {
        return selectOne(MesWmProductSalesDO::getCode, code);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/returnsales/MesWmReturnSalesMapper.java
@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.mes.dal.mysql.wm.returnsales;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.wm.returnsales.vo.MesWmReturnSalesPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
 * MES é”€å”®é€€è´§å• Mapper
@@ -26,6 +29,27 @@
                .orderByDesc(MesWmReturnSalesDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     */
    default PageResult<MesWmReturnSalesDO> selectPage(MesWmReturnSalesPageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesWmReturnSalesDO> wrapper = new LambdaQueryWrapperX<MesWmReturnSalesDO>()
                .likeIfPresent(MesWmReturnSalesDO::getCode, reqVO.getCode())
                .likeIfPresent(MesWmReturnSalesDO::getName, reqVO.getName())
                .likeIfPresent(MesWmReturnSalesDO::getSalesOrderCode, reqVO.getSalesOrderCode())
                .eqIfPresent(MesWmReturnSalesDO::getClientId, reqVO.getClientId())
                .eqIfPresent(MesWmReturnSalesDO::getStatus, reqVO.getStatus())
                .betweenIfPresent(MesWmReturnSalesDO::getReturnDate, reqVO.getReturnDate())
                .orderByDesc(MesWmReturnSalesDO::getId);
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                return PageResult.empty();
            }
            wrapper.in(MesWmReturnSalesDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesWmReturnSalesDO selectByCode(String code) {
        return selectOne(new LambdaQueryWrapperX<MesWmReturnSalesDO>()
                .eq(MesWmReturnSalesDO::getCode, code));
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/salesnotice/MesWmSalesNoticeMapper.java
@@ -1,11 +1,15 @@
package cn.iocoder.yudao.module.mes.dal.mysql.wm.salesnotice;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.wm.salesnotice.vo.MesWmSalesNoticePageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
 * MES å‘货通知单 Mapper
@@ -23,6 +27,32 @@
                .orderByDesc(MesWmSalesNoticeDO::getId));
    }
    /**
     * åˆ†é¡µæŸ¥è¯¢ï¼ˆå¸¦å®¢æˆ·æƒé™è¿‡æ»¤ï¼‰
     *
     * @param reqVO æŸ¥è¯¢æ¡ä»¶
     * @param permittedClientIds æœ‰æƒé™çš„客户 ID åˆ—表(null è¡¨ç¤ºä¸é™åˆ¶ï¼‰
     * @return åˆ†é¡µç»“æžœ
     */
    default PageResult<MesWmSalesNoticeDO> selectPage(MesWmSalesNoticePageReqVO reqVO, List<Long> permittedClientIds) {
        LambdaQueryWrapperX<MesWmSalesNoticeDO> wrapper = new LambdaQueryWrapperX<MesWmSalesNoticeDO>()
                .likeIfPresent(MesWmSalesNoticeDO::getCode, reqVO.getCode())
                .likeIfPresent(MesWmSalesNoticeDO::getName, reqVO.getName())
                .likeIfPresent(MesWmSalesNoticeDO::getSaleOrderCode, reqVO.getSaleOrderCode())
                .eqIfPresent(MesWmSalesNoticeDO::getClientId, reqVO.getClientId())
                .eqIfPresent(MesWmSalesNoticeDO::getStatus, reqVO.getStatus())
                .orderByDesc(MesWmSalesNoticeDO::getId);
        // å®¢æˆ·æƒé™è¿‡æ»¤
        if (permittedClientIds != null) {
            if (CollUtil.isEmpty(permittedClientIds)) {
                // ç©ºåˆ—表表示没有任何权限,返回空结果
                return PageResult.empty();
            }
            wrapper.in(MesWmSalesNoticeDO::getClientId, permittedClientIds);
        }
        return selectPage(reqVO, wrapper);
    }
    default MesWmSalesNoticeDO selectByCode(String code) {
        return selectOne(MesWmSalesNoticeDO::getCode, code);
    }
@@ -31,4 +61,13 @@
        return selectOne(MesWmSalesNoticeDO::getSaleOrderId, saleOrderId);
    }
    /**
     * æŸ¥è¯¢å·²å­˜åœ¨å‘货通知单的销售订单 ID åˆ—表
     */
    default List<Long> selectExistingSaleOrderIds(Collection<Long> saleOrderIds) {
        return selectObjs(new LambdaQueryWrapperX<MesWmSalesNoticeDO>()
                .select(MesWmSalesNoticeDO::getSaleOrderId)
                .in(MesWmSalesNoticeDO::getSaleOrderId, saleOrderIds));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
@@ -19,6 +19,7 @@
    String MES_PRO_WORK_ORDER_STATUS = "mes_pro_work_order_status"; // MES ç”Ÿäº§å·¥å•状态
    String MES_PRO_WORK_ORDER_SOURCE_TYPE = "mes_pro_work_order_source_type"; // MES å·¥å•来源类型
    String MES_PRO_WORK_ORDER_TYPE = "mes_pro_work_order_type"; // MES å·¥å•类型
    String MES_PRO_MPS_STATUS = "mes_pro_mps_status"; // MES ç”Ÿäº§ä¸»è®¡åˆ’状态
    String MES_PRO_LINK_TYPE = "mes_pro_link_type"; // MES å·¥åºå…³ç³»ç±»åž‹
    String MES_TIME_UNIT_TYPE = "mes_time_unit_type"; // MES æ—¶é—´å•位
    String MES_PRO_ANDON_STATUS = "mes_pro_andon_status"; // MES å®‰ç¯å¤„置状态
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -322,6 +322,17 @@
    ErrorCode WORK_RECORD_NOT_CLOCK_IN = new ErrorCode(1_040_508_001, "当前用户未上工,无法下工");
    ErrorCode WORK_RECORD_ALREADY_CLOCK_IN = new ErrorCode(1_040_508_002, "当前用户已上工,请先下工再操作");
    // ========== MES ç”Ÿäº§ç®¡ç†-生产主计划 MPS(1-040-509-000) ==========
    ErrorCode PRO_MPS_NOT_EXISTS = new ErrorCode(1_040_509_000, "MPS ä¸å­˜åœ¨");
    ErrorCode PRO_MPS_NOT_DRAFT = new ErrorCode(1_040_509_001, "只有草稿状态的 MPS æ‰èƒ½æ‰§è¡Œæ­¤æ“ä½œ");
    ErrorCode PRO_MPS_NOT_ISSUED = new ErrorCode(1_040_509_002, "只有已下发状态的 MPS æ‰èƒ½æ‰§è¡Œæ­¤æ“ä½œ");
    ErrorCode PRO_MPS_ALREADY_ISSUED = new ErrorCode(1_040_509_003, "MPS å·²ä¸‹å‘,无法修改");
    ErrorCode PRO_MPS_MERGE_AT_LEAST_TWO = new ErrorCode(1_040_509_004, "合并下发至少需要选择两条 MPS");
    ErrorCode PRO_MPS_MERGE_PRODUCT_MISMATCH = new ErrorCode(1_040_509_005, "合并下发的 MPS å¿…须是相同产品");
    ErrorCode PRO_MPS_CODE_DUPLICATE = new ErrorCode(1_040_509_006, "MPS ç¼–码已存在");
    ErrorCode PRO_MPS_HAS_WORK_ORDER = new ErrorCode(1_040_509_007, "MPS å·²å…³è”生产工单,无法删除");
    ErrorCode PRO_MPS_MES_PRODUCT_NOT_EXISTS = new ErrorCode(1_040_509_008, "MES äº§å“ä¸å­˜åœ¨ï¼Œæ— æ³•创建 MPS,产品:{}");
    // ========== MES è´¨é‡ç®¡ç†-质检方案(1-040-600-000) ==========
    ErrorCode QC_TEMPLATE_NOT_EXISTS = new ErrorCode(1_040_600_000, "质检方案不存在");
    ErrorCode QC_TEMPLATE_CODE_DUPLICATE = new ErrorCode(1_040_600_001, "质检方案编号已存在");
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProMpsStatusEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.enums.pro;
import cn.iocoder.yudao.framework.common.core.ArrayValuable;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Arrays;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)状态枚举
 *
 * @author èŠ‹é“æºç 
 */
@Getter
@AllArgsConstructor
public enum MesProMpsStatusEnum implements ArrayValuable<Integer> {
    /**
     * è‰ç¨¿
     */
    DRAFT(0, "草稿"),
    /**
     * å·²ä¸‹å‘
     */
    ISSUED(1, "已下发"),
    /**
     * å·²å®Œæˆ
     */
    FINISHED(2, "已完成"),
    /**
     * å·²å–消
     */
    CANCELED(3, "已取消");
    public static final Integer[] ARRAYS = Arrays.stream(values())
            .map(MesProMpsStatusEnum::getStatus).toArray(Integer[]::new);
    /**
     * çŠ¶æ€å€¼
     */
    private final Integer status;
    /**
     * çŠ¶æ€å
     */
    private final String name;
    @Override
    public Integer[] array() {
        return ARRAYS;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client/MesMdClientService.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/client/MesMdClientServiceImpl.java
ÎļþÒÑɾ³ý
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemService.java
@@ -134,4 +134,21 @@
     */
    Long getItemCountByUnitMeasureId(Long unitMeasureId);
    /**
     * æ ¹æ® MDM ç‰©æ–™ ID èŽ·å– MES äº§å“
     *
     * @param mdmItemId MDM ç‰©æ–™ ID
     * @return MES äº§å“ï¼Œä¸å­˜åœ¨è¿”回 null
     */
    MesMdItemDO getItemByMdmItemId(Long mdmItemId);
    /**
     * æ ¹æ® MDM ç‰©æ–™ ID æ ¡éªŒ MES äº§å“å­˜åœ¨
     *
     * @param mdmItemId MDM ç‰©æ–™ ID
     * @return MES äº§å“
     * @throws cn.iocoder.yudao.framework.common.exception.ServiceException å¦‚果产品不存在
     */
    MesMdItemDO validateItemExistsByMdmItemId(Long mdmItemId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java
@@ -478,4 +478,21 @@
        return 1; // é»˜è®¤åŽŸæ–™
    }
    @Override
    public MesMdItemDO getItemByMdmItemId(Long mdmItemId) {
        if (mdmItemId == null) {
            return null;
        }
        return itemMapper.selectByMdmItemId(mdmItemId);
    }
    @Override
    public MesMdItemDO validateItemExistsByMdmItemId(Long mdmItemId) {
        MesMdItemDO item = getItemByMdmItemId(mdmItemId);
        if (item == null) {
            throw exception(MD_ITEM_NOT_EXISTS);
        }
        return item;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,148 @@
package cn.iocoder.yudao.module.mes.service.pro.mps;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
import jakarta.validation.Valid;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)Service æŽ¥å£
 *
 * @author èŠ‹é“æºç 
 */
public interface MesProMpsService {
    /**
     * åˆ›å»º MPS(供 API è°ƒç”¨ï¼Œè‡ªåŠ¨æ˜ å°„ç‰©æ–™ï¼‰
     *
     * @param saleOrderId é”€å”®è®¢å•ID
     * @param saleOrderNo é”€å”®è®¢å•号
     * @param saleOrderItemId é”€å”®è®¢å•明细ID
     * @param mdmItemId MDM ç‰©æ–™ID
     * @param productName äº§å“åç§°ï¼ˆç”¨äºŽé”™è¯¯æç¤ºï¼‰
     * @param quantity è®¡åˆ’数量
     * @param clientId å®¢æˆ·ID
     * @param requestDate éœ€æ±‚日期
     * @param remark å¤‡æ³¨
     * @return MPS ç¼–号,如果 MES äº§å“ä¸å­˜åœ¨è¿”回 null
     * @throws cn.iocoder.yudao.framework.common.exception.ServiceException å¦‚æžœ MES äº§å“ä¸å­˜åœ¨
     */
    Long createMps(Long saleOrderId, String saleOrderNo, Long saleOrderItemId,
                   Long mdmItemId, String productName, BigDecimal quantity, Long clientId,
                   String requestDate, String remark);
    /**
     * åˆ›å»º MPS
     *
     * @param createReqVO åˆ›å»ºä¿¡æ¯
     * @return MPS ç¼–号
     */
    Long createMps(@Valid MesProMpsSaveReqVO createReqVO);
    /**
     * æ›´æ–° MPS
     *
     * @param updateReqVO æ›´æ–°ä¿¡æ¯
     */
    void updateMps(@Valid MesProMpsSaveReqVO updateReqVO);
    /**
     * åˆ é™¤ MPS(仅草稿状态)
     *
     * @param id ç¼–号
     */
    void deleteMps(Long id);
    /**
     * ä¸‹å‘单条 MPS
     *
     * @param id MPS ç¼–号
     * @return ç”Ÿæˆçš„工单 ID
     */
    Long issueMps(Long id);
    /**
     * åˆå¹¶ä¸‹å‘多条 MPS
     * å°†å¤šæ¡ç›¸åŒäº§å“çš„ MPS åˆå¹¶ç”Ÿæˆä¸€æ¡ç”Ÿäº§å·¥å•
     *
     * @param mpsIds MPS ç¼–号列表
     * @return ç”Ÿæˆçš„工单 ID
     */
    Long mergeAndIssueMps(List<Long> mpsIds);
    /**
     * å–消 MPS
     *
     * @param id ç¼–号
     */
    void cancelMps(Long id);
    /**
     * å®Œæˆ MPS(由生产工单完成时触发)
     *
     * @param id ç¼–号
     */
    void finishMps(Long id);
    /**
     * æ ¹æ®é”€å”®è®¢å•明细获取 MPS
     *
     * @param saleOrderItemId é”€å”®è®¢å•明细ID
     * @return MPS
     */
    MesProMpsDO getMpsBySaleOrderItemId(Long saleOrderItemId);
    /**
     * èŽ·å– MPS
     *
     * @param id ç¼–号
     * @return MPS
     */
    MesProMpsDO getMps(Long id);
    /**
     * èŽ·å– MPS åˆ†é¡µ
     *
     * @param pageReqVO åˆ†é¡µæŸ¥è¯¢
     * @return åˆ†é¡µç»“æžœ
     */
    PageResult<MesProMpsDO> getMpsPage(MesProMpsPageReqVO pageReqVO);
    /**
     * èŽ·å– MPS åˆ—表
     *
     * @param ids ç¼–号列表
     * @return MPS åˆ—表
     */
    List<MesProMpsDO> getMpsList(Collection<Long> ids);
    /**
     * æ ¹æ®é”€å”®è®¢å•明细ID列表获取 MPS åˆ—表
     *
     * @param saleOrderItemIds é”€å”®è®¢å•明细ID列表
     * @return MPS åˆ—表
     */
    List<MesProMpsDO> getMpsListBySaleOrderItemIds(Collection<Long> saleOrderItemIds);
    /**
     * èŽ·å–å¯åˆå¹¶çš„ MPS åˆ—表(相同产品、草稿状态)
     *
     * @param productId äº§å“ID
     * @return MPS åˆ—表
     */
    List<MesProMpsDO> getMergeableMpsList(Long productId);
    /**
     * æ›´æ–°å·²ç”Ÿäº§æ•°é‡
     *
     * @param id MPS ç¼–号
     * @param incrQuantity å¢žé‡
     */
    void updateProducedQuantity(Long id, BigDecimal incrQuantity);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/mps/MesProMpsServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,433 @@
package cn.iocoder.yudao.module.mes.service.pro.mps;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsMergeItemDO;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.mps.MesProMpsMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.pro.mps.MesProMpsMergeItemMapper;
import cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum;
import cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderSourceTypeEnum;
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.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*;
/**
 * MES ç”Ÿäº§ä¸»è®¡åˆ’(MPS)Service å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
@Slf4j
public class MesProMpsServiceImpl implements MesProMpsService {
    /**
     * MPS ç¼–码规则
     */
    private static final String MPS_CODE_RULE = "MPS";
    @Resource
    private MesProMpsMapper mpsMapper;
    @Resource
    private MesProMpsMergeItemMapper mergeItemMapper;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
    private MesMdItemService itemService;
    @Resource
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Resource
    private CrmCustomerApi customerApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createMps(Long saleOrderId, String saleOrderNo, Long saleOrderItemId,
                          Long mdmItemId, String productName, BigDecimal quantity, Long clientId,
                          String requestDate, String remark) {
        // 1. æ ¹æ® MDM ç‰©æ–™ ID æŸ¥æ‰¾ MES äº§å“
        cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO mesItem = itemService.getItemByMdmItemId(mdmItemId);
        if (mesItem == null) {
            // MES äº§å“ä¸å­˜åœ¨ï¼ŒæŠ›å‡ºå¼‚常并包含产品名称提示
            throw exception(PRO_MPS_MES_PRODUCT_NOT_EXISTS, productName != null ? productName : mdmItemId);
        }
        // 2. ç”Ÿæˆ MPS ç¼–码
        String code = generateMpsCode();
        // 3. è§£æžéœ€æ±‚日期
        LocalDateTime requestDateTime = null;
        if (StrUtil.isNotBlank(requestDate)) {
            try {
                requestDateTime = LocalDateTime.parse(requestDate, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            } catch (Exception e) {
                requestDateTime = LocalDateTime.parse(requestDate, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
            }
        }
        // 4. åˆ›å»º MPS(使用 MES äº§å“ ID)
        MesProMpsDO mps = new MesProMpsDO();
        mps.setCode(code);
        mps.setStatus(MesProMpsStatusEnum.DRAFT.getStatus());
        mps.setSaleOrderId(saleOrderId);
        mps.setSaleOrderNo(saleOrderNo);
        mps.setSaleOrderItemId(saleOrderItemId);
        mps.setProductId(mesItem.getId()); // ä½¿ç”¨ MES äº§å“ ID
        mps.setQuantity(quantity);
        mps.setQuantityProduced(BigDecimal.ZERO);
        mps.setUnitMeasureId(mesItem.getUnitMeasureId()); // ä»Ž MES äº§å“èŽ·å–è®¡é‡å•ä½
        mps.setClientId(clientId);
        mps.setRequestDate(requestDateTime);
        mps.setMergeFlag(0);
        mps.setRemark(remark);
        mpsMapper.insert(mps);
        log.info("[createMps] åˆ›å»º MPS æˆåŠŸï¼Œid={}, code={}, saleOrderItemId={}, mdmItemId={}, productId={}",
                mps.getId(), mps.getCode(), mps.getSaleOrderItemId(), mdmItemId, mesItem.getId());
        return mps.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createMps(MesProMpsSaveReqVO createReqVO) {
        // æ ¡éªŒäº§å“å­˜åœ¨
        itemService.validateItemExists(createReqVO.getProductId());
        // å¦‚果未填写编码,自动生成
        if (StrUtil.isBlank(createReqVO.getCode())) {
            createReqVO.setCode(generateMpsCode());
        } else {
            // æ ¡éªŒç¼–码唯一
            validateMpsCodeUnique(null, createReqVO.getCode());
        }
        // æ’å…¥
        MesProMpsDO mps = BeanUtils.toBean(createReqVO, MesProMpsDO.class);
        mps.setStatus(MesProMpsStatusEnum.DRAFT.getStatus());
        mps.setQuantityProduced(BigDecimal.ZERO);
        mps.setMergeFlag(0);
        mpsMapper.insert(mps);
        log.info("[createMps] æ‰‹åŠ¨åˆ›å»º MPS æˆåŠŸï¼Œid={}, code={}", mps.getId(), mps.getCode());
        return mps.getId();
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateMps(MesProMpsSaveReqVO updateReqVO) {
        // æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        validateMpsExistsAndDraft(updateReqVO.getId());
        // æ ¡éªŒç¼–码唯一
        validateMpsCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
        // æ›´æ–°
        MesProMpsDO updateObj = BeanUtils.toBean(updateReqVO, MesProMpsDO.class);
        mpsMapper.updateById(updateObj);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteMps(Long id) {
        // æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        MesProMpsDO mps = validateMpsExistsAndDraft(id);
        // æ ¡éªŒæ˜¯å¦å·²å…³è”工单
        if (mps.getWorkOrderId() != null) {
            throw exception(PRO_MPS_HAS_WORK_ORDER);
        }
        // åˆ é™¤
        mpsMapper.deleteById(id);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long issueMps(Long id) {
        // æ ¡éªŒå­˜åœ¨ + è‰ç¨¿çŠ¶æ€
        MesProMpsDO mps = validateMpsExistsAndDraft(id);
        // åˆ›å»ºç”Ÿäº§å·¥å•
        Long workOrderId = createWorkOrderFromMps(mps, mps.getQuantity());
        // æ›´æ–° MPS çŠ¶æ€
        mpsMapper.updateById(new MesProMpsDO()
                .setId(id)
                .setStatus(MesProMpsStatusEnum.ISSUED.getStatus())
                .setWorkOrderId(workOrderId)
                .setIssueDate(LocalDateTime.now()));
        log.info("[issueMps] MPS ä¸‹å‘成功,mpsId={}, workOrderId={}", id, workOrderId);
        return workOrderId;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long mergeAndIssueMps(List<Long> mpsIds) {
        // æ ¡éªŒ MPS åˆ—表
        if (CollUtil.isEmpty(mpsIds) || mpsIds.size() < 2) {
            throw exception(PRO_MPS_MERGE_AT_LEAST_TWO);
        }
        List<MesProMpsDO> mpsList = mpsMapper.selectBatchIds(mpsIds);
        if (mpsList.size() != mpsIds.size()) {
            throw exception(PRO_MPS_NOT_EXISTS);
        }
        // æ ¡éªŒï¼šå¿…须是相同产品、草稿状态
        Long productId = null;
        for (MesProMpsDO mps : mpsList) {
            if (!MesProMpsStatusEnum.DRAFT.getStatus().equals(mps.getStatus())) {
                throw exception(PRO_MPS_NOT_DRAFT);
            }
            if (productId == null) {
                productId = mps.getProductId();
            } else if (!productId.equals(mps.getProductId())) {
                throw exception(PRO_MPS_MERGE_PRODUCT_MISMATCH);
            }
        }
        // è®¡ç®—合并数量
        BigDecimal totalQuantity = mpsList.stream()
                .map(MesProMpsDO::getQuantity)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        // å–第一条 MPS ä½œä¸ºä¸»è®°å½•,创建生产工单
        MesProMpsDO firstMps = mpsList.get(0);
        Long workOrderId = createWorkOrderFromMps(firstMps, totalQuantity);
        // ç”Ÿæˆåˆå¹¶æ‰¹æ¬¡å·
        String mergeBatchNo = generateMergeBatchNo();
        // æ›´æ–°æ‰€æœ‰ MPS çŠ¶æ€ï¼Œè®°å½•åˆå¹¶å…³ç³»
        for (MesProMpsDO mps : mpsList) {
            mpsMapper.updateById(new MesProMpsDO()
                    .setId(mps.getId())
                    .setStatus(MesProMpsStatusEnum.ISSUED.getStatus())
                    .setWorkOrderId(workOrderId)
                    .setMergeFlag(1)
                    .setMergeBatchNo(mergeBatchNo)
                    .setIssueDate(LocalDateTime.now()));
            // è®°å½•合并明细
            MesProMpsMergeItemDO mergeItem = new MesProMpsMergeItemDO();
            mergeItem.setMergeBatchNo(mergeBatchNo);
            mergeItem.setMpsId(mps.getId());
            mergeItem.setWorkOrderId(workOrderId);
            mergeItem.setQuantity(mps.getQuantity());
            mergeItemMapper.insert(mergeItem);
        }
        log.info("[mergeAndIssueMps] MPS åˆå¹¶ä¸‹å‘成功,mpsIds={}, workOrderId={}, totalQuantity={}",
                mpsIds, workOrderId, totalQuantity);
        return workOrderId;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelMps(Long id) {
        // æ ¡éªŒå­˜åœ¨
        MesProMpsDO mps = validateMpsExists(id);
        // æ ¡éªŒçŠ¶æ€ï¼šåªæœ‰å·²ä¸‹å‘çŠ¶æ€æ‰èƒ½å–æ¶ˆ
        if (!MesProMpsStatusEnum.ISSUED.getStatus().equals(mps.getStatus())) {
            throw exception(PRO_MPS_NOT_ISSUED);
        }
        // çº§è”取消关联的生产工单
        if (mps.getWorkOrderId() != null) {
            try {
                workOrderService.cancelWorkOrder(mps.getWorkOrderId());
                log.info("[cancelMps] çº§è”取消生产工单,workOrderId={}", mps.getWorkOrderId());
            } catch (Exception e) {
                // å·¥å•可能已完成或已取消,记录日志但不阻止 MPS å–消
                log.warn("[cancelMps] å–消生产工单失败,workOrderId={},原因:{}", mps.getWorkOrderId(), e.getMessage());
            }
        }
        // æ›´æ–° MPS çŠ¶æ€
        mpsMapper.updateById(new MesProMpsDO()
                .setId(id)
                .setStatus(MesProMpsStatusEnum.CANCELED.getStatus()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void finishMps(Long id) {
        // æ ¡éªŒå­˜åœ¨
        MesProMpsDO mps = validateMpsExists(id);
        // æ ¡éªŒçŠ¶æ€ï¼šåªæœ‰å·²ä¸‹å‘çŠ¶æ€æ‰èƒ½å®Œæˆ
        if (!MesProMpsStatusEnum.ISSUED.getStatus().equals(mps.getStatus())) {
            throw exception(PRO_MPS_NOT_ISSUED);
        }
        // æ›´æ–°çŠ¶æ€
        mpsMapper.updateById(new MesProMpsDO()
                .setId(id)
                .setStatus(MesProMpsStatusEnum.FINISHED.getStatus())
                .setFinishDate(LocalDateTime.now()));
    }
    @Override
    public MesProMpsDO getMpsBySaleOrderItemId(Long saleOrderItemId) {
        return mpsMapper.selectBySaleOrderItemId(saleOrderItemId);
    }
    @Override
    public MesProMpsDO getMps(Long id) {
        return mpsMapper.selectById(id);
    }
    @Override
    public PageResult<MesProMpsDO> getMpsPage(MesProMpsPageReqVO pageReqVO) {
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return mpsMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
    public List<MesProMpsDO> getMpsList(Collection<Long> ids) {
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        return mpsMapper.selectBatchIds(ids);
    }
    @Override
    public List<MesProMpsDO> getMpsListBySaleOrderItemIds(Collection<Long> saleOrderItemIds) {
        return mpsMapper.selectListBySaleOrderItemIds(saleOrderItemIds);
    }
    @Override
    public List<MesProMpsDO> getMergeableMpsList(Long productId) {
        return mpsMapper.selectMergeableList(productId);
    }
    @Override
    public void updateProducedQuantity(Long id, BigDecimal incrQuantity) {
        mpsMapper.updateProducedQuantity(id, incrQuantity);
    }
    // ========== ç§æœ‰æ–¹æ³• ==========
    /**
     * ç”Ÿæˆ MPS ç¼–码
     */
    private String generateMpsCode() {
        try {
            return autoCodeRecordService.generateAutoCode(MPS_CODE_RULE);
        } catch (Exception e) {
            // å¦‚果编码规则不存在,使用时间戳生成
            log.warn("[generateMpsCode] ç¼–码规则 {} ä¸å­˜åœ¨ï¼Œä½¿ç”¨æ—¶é—´æˆ³ç”Ÿæˆ", MPS_CODE_RULE);
            return "MPS" + System.currentTimeMillis();
        }
    }
    /**
     * ç”Ÿæˆåˆå¹¶æ‰¹æ¬¡å·
     */
    private String generateMergeBatchNo() {
        return "MERGE" + System.currentTimeMillis();
    }
    /**
     * æ ¡éªŒ MPS å­˜åœ¨
     */
    private MesProMpsDO validateMpsExists(Long id) {
        MesProMpsDO mps = mpsMapper.selectById(id);
        if (mps == null) {
            throw exception(PRO_MPS_NOT_EXISTS);
        }
        return mps;
    }
    /**
     * æ ¡éªŒ MPS å­˜åœ¨ + è‰ç¨¿çŠ¶æ€
     */
    private MesProMpsDO validateMpsExistsAndDraft(Long id) {
        MesProMpsDO mps = validateMpsExists(id);
        if (!MesProMpsStatusEnum.DRAFT.getStatus().equals(mps.getStatus())) {
            throw exception(PRO_MPS_NOT_DRAFT);
        }
        return mps;
    }
    /**
     * æ ¡éªŒ MPS ç¼–码唯一
     */
    private void validateMpsCodeUnique(Long id, String code) {
        if (StrUtil.isBlank(code)) {
            return;
        }
        MesProMpsDO mps = mpsMapper.selectByCode(code);
        if (mps == null) {
            return;
        }
        if (id == null) {
            throw exception(PRO_MPS_CODE_DUPLICATE);
        }
        if (!id.equals(mps.getId())) {
            throw exception(PRO_MPS_CODE_DUPLICATE);
        }
    }
    /**
     * æ ¹æ® MPS åˆ›å»ºç”Ÿäº§å·¥å•
     */
    private Long createWorkOrderFromMps(MesProMpsDO mps, BigDecimal quantity) {
        MesProWorkOrderSaveReqVO workOrderReq = new MesProWorkOrderSaveReqVO();
        workOrderReq.setCode(generateWorkOrderCode());
        workOrderReq.setName("生产工单-" + mps.getCode());
        workOrderReq.setType(1); // è‡ªè¡Œç”Ÿäº§
        workOrderReq.setOrderSourceType(MesProWorkOrderSourceTypeEnum.ORDER.getType());
        workOrderReq.setOrderSourceCode(mps.getSaleOrderNo());
        workOrderReq.setProductId(mps.getProductId());
        workOrderReq.setQuantity(quantity);
        workOrderReq.setClientId(mps.getClientId());
        workOrderReq.setRequestDate(mps.getRequestDate() != null ? mps.getRequestDate() : LocalDateTime.now());
        workOrderReq.setRemark("来自 MPS: " + mps.getCode());
        Long workOrderId = workOrderService.createWorkOrder(workOrderReq);
        // å·¥å•直接确认为已确认状态(跳过草稿)
        workOrderService.confirmWorkOrder(workOrderId);
        return workOrderId;
    }
    /**
     * ç”Ÿæˆç”Ÿäº§å·¥å•编码
     */
    private String generateWorkOrderCode() {
        try {
            return autoCodeRecordService.generateAutoCode("WO");
        } catch (Exception e) {
            log.warn("[generateWorkOrderCode] ç¼–码规则 WO ä¸å­˜åœ¨ï¼Œä½¿ç”¨æ—¶é—´æˆ³ç”Ÿæˆ");
            return "WO" + System.currentTimeMillis();
        }
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java
@@ -15,6 +15,7 @@
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.pro.task.MesProTaskService;
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -51,6 +52,8 @@
    private MesWmBarcodeService barcodeService;
    @Resource
    private MesProTaskService taskService;
    @Resource
    private CrmCustomerApi customerApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -140,7 +143,9 @@
    @Override
    public PageResult<MesProWorkOrderDO> getWorkOrderPage(MesProWorkOrderPageReqVO pageReqVO) {
        return workOrderMapper.selectPage(pageReqVO);
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return workOrderMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/iqc/MesQcIqcServiceImpl.java
@@ -17,7 +17,6 @@
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
import cn.iocoder.yudao.module.mes.service.qc.template.MesQcTemplateItemService;
@@ -64,7 +63,7 @@
    @Resource
    private MesWmOutsourceReceiptService outsourceReceiptService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdItemService itemService;
    @Resource
@@ -76,10 +75,6 @@
    @Resource
    private MdmItemApi mdmItemApi;
    @Resource
    @Lazy
    private ErpSupplierApi supplierApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -126,14 +121,9 @@
    private void validateIqcSaveData(MesQcIqcSaveReqVO reqVO) {
        // æ ¡éªŒç¼–号唯一
        validateIqcCodeUnique(reqVO.getId(), reqVO.getCode());
        // æ ¡éªŒä¾›åº”商存在(优先校验 ERP ä¾›åº”商,失败则校验 MDM ä¾›åº”商)
        // æ ¡éªŒä¾›åº”商存在
        if (reqVO.getVendorId() != null) {
            try {
                supplierApi.validateSupplier(reqVO.getVendorId());
            } catch (Exception e) {
                // ERP ä¾›åº”商校验失败,尝试 MDM ä¾›åº”商
                vendorService.validateVendorExists(reqVO.getVendorId());
            }
            supplierApi.validateSupplier(reqVO.getVendorId());
        }
        // æ ¡éªŒç‰©æ–™å­˜åœ¨ï¼ˆä½¿ç”¨ MDM ç‰©æ–™ï¼‰
        if (reqVO.getItemId() != null) {
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcService.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.defectrecord.MesQcDefectRecordDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
import jakarta.validation.Valid;
import java.util.List;
@@ -77,4 +78,15 @@
     */
    void recalculateDefectStats(Long oqcId, List<MesQcDefectRecordDO> records);
    /**
     * æ ¹æ®é”€å”®å‡ºåº“单明细自动创建 OQC æ£€éªŒå•
     *
     * @param salesId       é”€å”®å‡ºåº“单 ID
     * @param line          é”€å”®å‡ºåº“单明细
     * @param salesCode     é”€å”®å‡ºåº“单编码
     * @param clientId      å®¢æˆ· ID
     * @return OQC æ£€éªŒå• ID
     */
    Long createOqcFromProductSales(Long salesId, MesWmProductSalesLineDO line, String salesCode, Long clientId);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/oqc/MesQcOqcServiceImpl.java
@@ -13,7 +13,6 @@
import cn.iocoder.yudao.module.mes.enums.qc.MesQcStatusEnum;
import cn.iocoder.yudao.module.mes.enums.qc.MesQcTypeEnum;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService;
import cn.iocoder.yudao.module.mes.service.qc.defectrecord.MesQcDefectRecordService;
import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService;
@@ -22,6 +21,8 @@
import cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesService;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
@@ -58,9 +59,6 @@
    private MesQcDefectRecordService defectRecordService;
    @Resource
    @Lazy
    private MesMdClientService clientService;
    @Resource
    @Lazy
    private MesMdItemService itemService;
    @Resource
    @Lazy
@@ -74,6 +72,10 @@
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private MdmItemApi mdmItemApi;
    @Resource
    private CrmCustomerApi customerApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -118,8 +120,8 @@
    private void validateOqcSaveData(MesQcOqcSaveReqVO reqVO) {
        // æ ¡éªŒç¼–号唯一
        validateOqcCodeUnique(reqVO.getId(), reqVO.getCode());
        // æ ¡éªŒå®¢æˆ·ã€ç‰©æ–™ã€æ£€æµ‹äººå‘˜å­˜åœ¨
        clientService.validateClientExists(reqVO.getClientId());
        // æ ¡éªŒç‰©æ–™ã€æ£€æµ‹äººå‘˜å­˜åœ¨
        // æ³¨ï¼šå®¢æˆ· ID æ¥è‡ª CRM,不需要在 MES ä¸­æ ¡éªŒ
        itemService.validateItemExists(reqVO.getItemId());
        adminUserApi.validateUser(reqVO.getInspectorUserId());
    }
@@ -246,7 +248,9 @@
    @Override
    public PageResult<MesQcOqcDO> getOqcPage(MesQcOqcPageReqVO pageReqVO) {
        return oqcMapper.selectPage(pageReqVO);
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return oqcMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
@@ -289,4 +293,66 @@
        oqcMapper.updateById(updateOqc);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createOqcFromProductSales(Long salesId, MesWmProductSalesLineDO line, String salesCode, Long clientId) {
        // 1. æ ¡éªŒç‰©æ–™æ˜¯å¦é…ç½®äº† OQC æ£€éªŒæ¨¡æ¿
        MesQcTemplateItemDO templateItem;
        try {
            templateItem = templateItemService.getRequiredTemplateByItemIdAndType(
                    line.getItemId(), MesQcTypeEnum.OQC.getType());
        } catch (Exception e) {
            // èŽ·å–ç‰©æ–™ä¿¡æ¯ï¼Œæä¾›æ›´æ˜Žç¡®çš„é”™è¯¯æç¤º
            String itemInfo = getItemInfo(line.getItemId());
            throw exception(QC_NO_TEMPLATE_FOR_ITEM, itemInfo);
        }
        Long templateId = templateItem.getTemplateId();
        // 2. ç”Ÿæˆæ£€éªŒå•编码
        String code = "OQC" + System.currentTimeMillis();
        // 3. åˆ›å»º OQC ä¸»è¡¨ï¼ˆå¾…检验状态,初始数量为 0)
        MesQcOqcDO oqc = new MesQcOqcDO();
        oqc.setCode(code);
        oqc.setName(salesCode + "-出货检验");
        oqc.setTemplateId(templateId);
        oqc.setSourceDocType(MesBizTypeConstants.WM_PRODUCT_SALES);
        oqc.setSourceDocId(salesId);
        oqc.setSourceLineId(line.getId());
        oqc.setSourceDocCode(salesCode);
        oqc.setClientId(clientId);
        oqc.setItemId(line.getItemId());
        oqc.setBatchCode(line.getBatchCode());
        oqc.setOutQuantity(line.getQuantity());
        oqc.setCheckQuantity(BigDecimal.ZERO); // å¾…填写
        oqc.setQualifiedQuantity(BigDecimal.ZERO); // å¾…填写
        oqc.setUnqualifiedQuantity(BigDecimal.ZERO); // å¾…填写
        oqc.setOutDate(java.time.LocalDateTime.now());
        oqc.setStatus(MesQcStatusEnum.DRAFT.getStatus());
        oqcMapper.insert(oqc);
        // 4. ä»Žæ¨¡æ¿æŒ‡æ ‡è‡ªåŠ¨ç”Ÿæˆæ£€éªŒè¡Œ
        oqcLineService.createLinesFromTemplate(oqc.getId(), templateId);
        return oqc.getId();
    }
    /**
     * èŽ·å–ç‰©æ–™ä¿¡æ¯ï¼ˆç¼–ç +名称)
     */
    private String getItemInfo(Long itemId) {
        if (itemId == null) {
            return "未知物料";
        }
        try {
            cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData();
            if (item != null) {
                return item.getCode() + "-" + item.getName();
            }
        } catch (Exception e) {
            // å¿½ç•¥å¼‚常
        }
        return "物料ID:" + itemId;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/batch/MesWmBatchServiceImpl.java
@@ -14,6 +14,7 @@
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemBatchConfigRespDTO;
import jakarta.annotation.Resource;
@@ -56,10 +57,14 @@
    private MesMdAutoCodeRecordService autoCodeRecordService;
    @Resource
    private MesWmBarcodeService barcodeService;
    @Resource
    private CrmCustomerApi customerApi;
    @Override
    public PageResult<MesWmBatchDO> getBatchPage(MesWmBatchPageReqVO pageReqVO) {
        return batchMapper.selectPage(pageReqVO);
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return batchMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourceissue/MesWmOutsourceIssueServiceImpl.java
@@ -21,7 +21,7 @@
import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutsourceIssueStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -56,7 +56,7 @@
    @Resource
    private MesWmOutsourceIssueDetailService outsourceIssueDetailService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesProWorkOrderService workOrderService;
    @Resource
@@ -259,7 +259,7 @@
        validateCodeUnique(saveReqVO.getId(), saveReqVO.getCode());
        // æ ¡éªŒä¾›åº”商存在
        if (saveReqVO.getVendorId() != null) {
            vendorService.validateVendorExistsAndEnable(saveReqVO.getVendorId());
            supplierApi.validateSupplier(saveReqVO.getVendorId());
        }
        // æ ¡éªŒå·¥å•存在且类型为外协(代工)
        MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(saveReqVO.getWorkOrderId());
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/outsourcereceipt/MesWmOutsourceReceiptServiceImpl.java
@@ -21,7 +21,7 @@
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -56,7 +56,7 @@
    @Resource
    private MesWmTransactionService wmTransactionService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
@@ -309,7 +309,7 @@
        // æ ¡éªŒç¼–码唯一
        validateCodeUnique(id, reqVO.getCode());
        // æ ¡éªŒä¾›åº”商存在
        vendorService.validateVendorExistsAndEnable(reqVO.getVendorId());
        supplierApi.validateSupplier(reqVO.getVendorId());
        // æ ¡éªŒå¤–协工单存在
        if (reqVO.getWorkOrderId() != null) {
            workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId());
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/packages/MesWmPackageServiceImpl.java
@@ -4,6 +4,7 @@
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.MesWmPackagePageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.MesWmPackageSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.packages.MesWmPackageDO;
@@ -39,6 +40,8 @@
    private MesWmPackageLineService packageLineService;
    @Resource
    private MesWmBarcodeService barcodeService;
    @Resource
    private CrmCustomerApi customerApi;
    @Override
    public Long createPackage(MesWmPackageSaveReqVO createReqVO) {
@@ -93,7 +96,8 @@
    @Override
    public PageResult<MesWmPackageDO> getPackagePage(MesWmPackagePageReqVO pageReqVO) {
        return packageMapper.selectPage(pageReqVO);
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return packageMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesLineServiceImpl.java
@@ -17,6 +17,7 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeLineDO;
import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeLineService;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
@@ -56,6 +57,9 @@
    @Resource
    @Lazy
    private MesWmSalesNoticeLineService salesNoticeLineService;
    @Resource
    @Lazy
    private MesQcOqcService oqcService;
    @Override
    public Long createProductSalesLine(MesWmProductSalesLineSaveReqVO createReqVO) {
@@ -79,6 +83,34 @@
        // æ›´æ–°
        MesWmProductSalesLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmProductSalesLineDO.class);
        productSalesLineMapper.updateById(updateObj);
        // æ£€æŸ¥æ˜¯å¦éœ€è¦åˆ›å»º OQC æ£€éªŒå•:从不需要检验变为需要检验
        if (Boolean.TRUE.equals(updateReqVO.getOqcCheckFlag())
                && !Boolean.TRUE.equals(oldLine.getOqcCheckFlag())) {
            // èŽ·å–å‡ºåº“å•ä¿¡æ¯
            MesWmProductSalesDO sales = productSalesService.getProductSales(updateReqVO.getSalesId());
            if (sales == null) {
                log.warn("[updateProductSalesLine] å‡ºåº“单不存在,salesId={}", updateReqVO.getSalesId());
                return;
            }
            // æž„建出库明细 DO
            MesWmProductSalesLineDO line = new MesWmProductSalesLineDO();
            line.setId(updateReqVO.getId());
            line.setItemId(updateReqVO.getItemId());
            line.setBatchCode(updateReqVO.getBatchCode());
            line.setQuantity(updateReqVO.getQuantity());
            // åˆ›å»º OQC æ£€éªŒå•,让异常抛出
            Long oqcId = oqcService.createOqcFromProductSales(
                    sales.getId(), line, sales.getCode(), sales.getClientId());
            // æ›´æ–°å‡ºåº“明细的 oqcId å’Œè´¨é‡çŠ¶æ€
            productSalesLineMapper.updateById(new MesWmProductSalesLineDO()
                    .setId(updateReqVO.getId())
                    .setOqcId(oqcId)
                    .setQualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()));
        }
    }
    @Override
@@ -175,7 +207,8 @@
            }
            MesWmSalesNoticeLineDO noticeLine = salesNoticeLineService.validateSalesNoticeLineExists(
                    noticeLineId, sales.getNoticeId());
            // æ ¡éªŒå…³é”®å­—段一致性:物料、数量、批次号、OQC æ£€éªŒæ ‡è¯†
            // æ ¡éªŒå…³é”®å­—段一致性:物料、数量、批次号
            // æ³¨ï¼šOQC æ£€éªŒæ ‡è¯†å…è®¸åœ¨å‡ºåº“时独立设置,不强制与通知单一致
            if (ObjUtil.notEqual(reqVO.getItemId(), noticeLine.getItemId())) {
                throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_ITEM_MISMATCH);
            }
@@ -186,10 +219,6 @@
            if (StrUtil.isNotBlank(noticeLine.getBatchCode())
                    && ObjUtil.notEqual(reqVO.getBatchCode(), noticeLine.getBatchCode())) {
                throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_BATCH_MISMATCH);
            }
            if (noticeLine.getOqcCheckFlag() != null
                    && ObjUtil.notEqual(reqVO.getOqcCheckFlag(), noticeLine.getOqcCheckFlag())) {
                throw exception(WM_PRODUCT_SALES_LINE_NOTICE_LINE_OQC_MISMATCH);
            }
            return;
        }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/productsales/MesWmProductSalesServiceImpl.java
@@ -14,21 +14,25 @@
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesLineMapper;
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmSalesNoticeStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmStockReserveBizTypeEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService;
import cn.iocoder.yudao.module.mes.service.wm.salesnotice.MesWmSalesNoticeService;
import cn.iocoder.yudao.module.mes.service.wm.stockreserve.MesWmStockReserveService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService;
import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
import cn.iocoder.yudao.module.erp.api.sale.enums.ErpSaleOrderOutStatusEnum;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -47,17 +51,20 @@
 */
@Service
@Validated
@Slf4j
public class MesWmProductSalesServiceImpl implements MesWmProductSalesService {
    @Resource
    private MesWmProductSalesMapper productSalesMapper;
    @Resource
    private MesWmProductSalesLineMapper productSalesLineMapper;
    @Resource
    private MesWmProductSalesLineService productSalesLineService;
    @Resource
    private MesWmProductSalesDetailService productSalesDetailService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesWmSalesNoticeService salesNoticeService;
    @Resource
@@ -66,6 +73,9 @@
    private MesWmMaterialStockService materialStockService;
    @Resource
    private MesWmStockReserveService stockReserveService;
    @Resource
    @Lazy
    private MesQcOqcService oqcService;
    @Resource
    private ErpSaleOrderApi saleOrderApi;
@@ -114,7 +124,8 @@
    @Override
    public PageResult<MesWmProductSalesDO> getProductSalesPage(MesWmProductSalesPageReqVO pageReqVO) {
        return productSalesMapper.selectPage(pageReqVO);
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return productSalesMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
@@ -134,10 +145,26 @@
            }
        }
        // èŽ·å–å‡ºåº“å•ä¿¡æ¯
        MesWmProductSalesDO sales = productSalesMapper.selectById(id);
        // æ£€æŸ¥æ‰€æœ‰è¡Œçš„ oqcCheckFlag:如果有需要 OQC æ£€éªŒçš„行,进入待检测状态
        boolean needOqc = CollectionUtils.anyMatch(lines,
                line -> Boolean.TRUE.equals(line.getOqcCheckFlag()));
        if (needOqc) {
            // ä¸ºéœ€è¦ OQC æ£€éªŒä¸”尚未创建检验单的明细创建 OQC æ£€éªŒå•
            List<MesWmProductSalesLineDO> oqcLines = CollectionUtils.filterList(lines,
                    line -> Boolean.TRUE.equals(line.getOqcCheckFlag()) && line.getOqcId() == null);
            for (MesWmProductSalesLineDO line : oqcLines) {
                // ç›´æŽ¥è°ƒç”¨ï¼Œè®©å¼‚常抛出,保证数据一致性
                Long oqcId = oqcService.createOqcFromProductSales(
                        sales.getId(), line, sales.getCode(), sales.getClientId());
                // æ›´æ–°å‡ºåº“明细的 oqcId
                productSalesLineMapper.updateById(new MesWmProductSalesLineDO()
                        .setId(line.getId())
                        .setOqcId(oqcId));
            }
            // æ‰¹é‡åˆå§‹åŒ– OQC è¡Œçš„质量状态为"待检验"
            List<Long> oqcLineIds = convertList(
                    CollectionUtils.filterList(lines, line -> Boolean.TRUE.equals(line.getOqcCheckFlag())),
@@ -366,7 +393,7 @@
        // æ ¡éªŒç¼–码唯一
        validateCodeUnique(id, reqVO.getCode());
        // æ ¡éªŒå®¢æˆ·å­˜åœ¨
        clientService.validateClientExistsAndEnable(reqVO.getClientId());
        customerApi.validateCustomer(reqVO.getClientId());
        // æ ¡éªŒå‘货通知单
        if (reqVO.getNoticeId() != null) {
            MesWmSalesNoticeDO notice = salesNoticeService.validateSalesNoticeExists(reqVO.getNoticeId());
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnsales/MesWmReturnSalesServiceImpl.java
@@ -18,7 +18,7 @@
import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
@@ -53,7 +53,7 @@
    @Resource
    private MesWmReturnSalesDetailService returnSalesDetailService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private MesMdmItemService mdmItemService;
    @Resource
@@ -106,7 +106,8 @@
    @Override
    public PageResult<MesWmReturnSalesDO> getReturnSalesPage(MesWmReturnSalesPageReqVO pageReqVO) {
        return returnSalesMapper.selectPage(pageReqVO);
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return returnSalesMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
@@ -272,7 +273,7 @@
     */
    private void validateSaveData(MesWmReturnSalesSaveReqVO reqVO) {
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
        clientService.validateClientExistsAndEnable(reqVO.getClientId());
        customerApi.validateCustomer(reqVO.getClientId());
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/returnvendor/MesWmReturnVendorServiceImpl.java
@@ -15,7 +15,7 @@
import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnVendorStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum;
import cn.iocoder.yudao.module.mes.service.md.vendor.MesMdVendorService;
import cn.iocoder.yudao.module.erp.api.purchase.ErpSupplierApi;
import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService;
import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO;
import jakarta.annotation.Resource;
@@ -45,7 +45,7 @@
    @Resource
    private MesWmReturnVendorDetailService returnVendorDetailService;
    @Resource
    private MesMdVendorService vendorService;
    private ErpSupplierApi supplierApi;
    @Resource
    private MesWmTransactionService wmTransactionService;
@@ -217,7 +217,7 @@
            validateReturnVendorExistsAndPrepare(reqVO.getId());
        }
        // æ ¡éªŒä¾›åº”商存在
        vendorService.validateVendorExistsAndEnable(reqVO.getVendorId());
        supplierApi.validateSupplier(reqVO.getVendorId());
        // æ ¡éªŒç¼–码唯一
        validateCodeUnique(reqVO.getId(), reqVO.getCode());
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/salesnotice/MesWmSalesNoticeServiceImpl.java
@@ -17,7 +17,7 @@
import cn.iocoder.yudao.module.mes.enums.wm.MesWmOutStatusEnum;
import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum;
import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService;
import cn.iocoder.yudao.module.mes.service.md.client.MesMdClientService;
import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi;
import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi;
import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO;
import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderRespDTO;
@@ -51,7 +51,7 @@
    @Resource
    private MesWmSalesNoticeLineService salesNoticeLineService;
    @Resource
    private MesMdClientService clientService;
    private CrmCustomerApi customerApi;
    @Resource
    private ErpSaleOrderApi saleOrderApi;
@@ -103,12 +103,22 @@
    @Override
    public MesWmSalesNoticeDO getSalesNotice(Long id) {
        return salesNoticeMapper.selectById(id);
        MesWmSalesNoticeDO notice = salesNoticeMapper.selectById(id);
        // æ ¡éªŒå®¢æˆ·æƒé™
        if (notice != null && notice.getClientId() != null) {
            List<Long> permittedIds = customerApi.getPermittedCustomerIds();
            if (permittedIds != null && !permittedIds.contains(notice.getClientId())) {
                return null; // æ— æƒé™åˆ™è¿”回 null
            }
        }
        return notice;
    }
    @Override
    public PageResult<MesWmSalesNoticeDO> getSalesNoticePage(MesWmSalesNoticePageReqVO pageReqVO) {
        return salesNoticeMapper.selectPage(pageReqVO);
        // èŽ·å–ç”¨æˆ·æœ‰æƒé™çš„å®¢æˆ· ID åˆ—表
        List<Long> permittedClientIds = customerApi.getPermittedCustomerIds();
        return salesNoticeMapper.selectPage(pageReqVO, permittedClientIds);
    }
    @Override
@@ -166,7 +176,7 @@
        validateNoticeCodeUnique(saveReqVO.getId(), saveReqVO.getCode());
        // æ ¡éªŒå®¢æˆ·å­˜åœ¨
        if (saveReqVO.getClientId() != null) {
            clientService.validateClientExistsAndEnable(saveReqVO.getClientId());
            customerApi.validateCustomer(saveReqVO.getClientId());
        }
    }