refactor(mes): 替换物料服务为MDM API并完善质检Word导出功能
- 将MesMdItemService和MesMdUnitMeasureService替换为MdmItemApi和MdmUnitMeasureApi
- 更新IPQC、IQC、OQC、RQC控制器中的物料和计量单位查询逻辑
- 在待检任务服务中添加MDM物料信息填充功能
- 完善产品检验单Word导出功能,实现检验/复核/批准签字自动带出姓名
- 更新文档中关于Word导出功能的描述,明确签字行的实现方式
- 修改本地环境数据库连接配置以适配新环境
- 更新流程图和数据文件中的相关描述信息
| | |
| | | {"stage": "联动", "action": "审批通过按工序标记: 倒冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]}, |
| | | {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS 累计判断完成并发事件给销售订单", "roles": ["系统自动"]}]}, |
| | | {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c", |
| | | "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + 判定四果(合格/特采/退货/报废;OQC 改按产品等级 1型/2型/废品,仅废品判不合格) + NCR 评审处置关闭;AI 提供判定建议;NCR 当前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告;并可导出产品检验单 Word(纸质检验单版式,判定结论/质检章/检验·复核·批准签字留空手填,Word 打印)。", |
| | | "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + 判定四果(合格/特采/退货/报废;OQC 改按产品等级 1型/2型/废品,仅废品判不合格) + NCR 评审处置关闭;AI 提供判定建议;NCR 当前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告;并可导出产品检验单 Word(纸质检验单版式,判定结论带出检验结果、检验·复核·批准签字带出检测人·复核人·审核人姓名、质检章留空盖章,Word 打印)。", |
| | | "steps": [ |
| | | {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC 质检员"]}, |
| | | {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC 质检员"]}, |
| | | {"stage": "出货", "action": "销售出库提交→OQC(检测结果按产品等级 1型/2型/废品必填,1型/2型判行合格,废品判行不合格),全部合格后出库单进入待拣货,出现废品则整单取消", "roles": ["OQC 质检员"]}, |
| | | {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC 质检员"]}, |
| | | {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report 聚合后前端打印检验报告(含采样地点/证书编号/执行标准/判定结论·质检专用章/复核·审核签字区;判定结论带出检验结果·质检章留空盖章·无平行测量时列头称测量值);/indicator-result/export-product-word 导出产品检验单Word(无技术指标列·判定结论带出检验结果·质检章/检验·复核·批准签字留空手填·平行测量顿号合并·下载后Word打印;质检单详情检测结果Tab可按样品查看只读检测值明细)", "roles": ["质检员", "复核人", "审核人"]}, |
| | | {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report 聚合后前端打印检验报告(含采样地点/证书编号/执行标准/判定结论·质检专用章/复核·审核签字区;判定结论带出检验结果·质检章留空盖章·无平行测量时列头称测量值);/indicator-result/export-product-word 导出产品检验单Word(无技术指标列·判定结论带出检验结果·检验·复核·批准签字带出检测人/复核人/审核人姓名·质检章留空盖章·平行测量顿号合并·下载后Word打印;质检单详情检测结果Tab可按样品查看只读检测值明细)", "roles": ["质检员", "复核人", "审核人"]}, |
| | | {"stage": "不合格", "action": "NCR: 待评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]}]}, |
| | | {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6", |
| | | "summary": "15 类单据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN 追溯、库存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi 供 ERP 收发。", |
| | |
| | | {"stage": "联动", "action": "审批通过按工序标记: 倒冲→自动物料消耗扣线边库(允许负库存);质检→产出待检行等 IPQC;关键非检→直接产出入库", "roles": ["系统自动"]}, |
| | | {"stage": "完工", "action": "仅末道工序回写工单已生产数量,达标自动完成;MPS 累计判断完成并发事件给销售订单", "roles": ["系统自动"]}]}, |
| | | {"id": "quality", "name": "质量流程", "mermaid": "mermaid/05-quality-flow.mmd", "color": "#e06c6c", |
| | | "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + 判定四果(合格/特采/退货/报废;OQC 改按产品等级 1型/2型/废品,仅废品判不合格) + NCR 评审处置关闭;AI 提供判定建议;NCR 当前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告;并可导出产品检验单 Word(纸质检验单版式,判定结论/质检章/检验·复核·批准签字留空手填,Word 打印)。", |
| | | "summary": "四道检验闸口(IQC/IPQC/OQC/RQC,二态 0→4) + 判定四果(合格/特采/退货/报废;OQC 改按产品等级 1型/2型/废品,仅废品判不合格) + NCR 评审处置关闭;AI 提供判定建议;NCR 当前仅台账不触发返工单。支持化工报告范式:分组指标树+公式文本+平行测量批次+采样表头+签字区,浏览器打印检验报告;并可导出产品检验单 Word(纸质检验单版式,判定结论带出检验结果、检验·复核·批准签字带出检测人·复核人·审核人姓名、质检章留空盖章,Word 打印)。", |
| | | "steps": [ |
| | | {"stage": "来料", "action": "到货通知/外协入库→IQC,结论回写推进待入库或触发退货", "roles": ["IQC 质检员"]}, |
| | | {"stage": "过程", "action": "报工质检工序产出行→IPQC,合格拆行入库、不合格生成 NCR", "roles": ["IPQC 质检员"]}, |
| | | {"stage": "出货", "action": "销售出库提交→OQC(检测结果按产品等级 1型/2型/废品必填,1型/2型判行合格,废品判行不合格),全部合格后出库单进入待拣货,出现废品则整单取消", "roles": ["OQC 质检员"]}, |
| | | {"stage": "退货", "action": "退料/销售退货/售后退货→RQC,结论回调单据与售后", "roles": ["RQC 质检员"]}, |
| | | {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report 聚合后前端打印检验报告(含采样地点/证书编号/执行标准/判定结论·质检专用章/复核·审核签字区;判定结论带出检验结果·质检章留空盖章·无平行测量时列头称测量值);/indicator-result/export-product-word 导出产品检验单Word(无技术指标列·判定结论带出检验结果·质检章/检验·复核·批准签字留空手填·平行测量顿号合并·下载后Word打印;质检单详情检测结果Tab可按样品查看只读检测值明细)", "roles": ["质检员", "复核人", "审核人"]}, |
| | | {"stage": "报告", "action": "检验结果按分组树+测量批次录入,/indicator-result/report 聚合后前端打印检验报告(含采样地点/证书编号/执行标准/判定结论·质检专用章/复核·审核签字区;判定结论带出检验结果·质检章留空盖章·无平行测量时列头称测量值);/indicator-result/export-product-word 导出产品检验单Word(无技术指标列·判定结论带出检验结果·检验·复核·批准签字带出检测人/复核人/审核人姓名·质检章留空盖章·平行测量顿号合并·下载后Word打印;质检单详情检测结果Tab可按样品查看只读检测值明细)", "roles": ["质检员", "复核人", "审核人"]}, |
| | | {"stage": "不合格", "action": "NCR: 待评审→评审→处置(退货/报废/返工/让步接收/降级)→关闭", "roles": ["质量工程师"]}]}, |
| | | {"id": "warehouse", "name": "仓储流程", "mermaid": "mermaid/06-warehouse-flow.mmd", "color": "#7e6bd6", |
| | | "summary": "15 类单据驱动库存台账(六元组、四量)变动,全部经事务流水留痕;支持调拨在途、盘点调整、批次/SN 追溯、库存占用与四级冻结、线边虚拟库负库存;对外 WmsStockApi 供 ERP 收发。", |
| | |
| | | Q8[支撑数据:质检方案·检验指标·缺陷库·指标结果] --- A1 |
| | | Q9[化工报告增强:指标分组树+公式文本·平行测量批次·采样地点/证书编号/执行标准表头·复核人/审核人] --- Q8 |
| | | A1 & A2 & A3 & A4 --> P1[检验报告单打印·前端浏览器打印·判定结论带出检验结果+质检专用章留空盖章+签字区·无平行测量时列头称测量值] |
| | | A1 & A2 & A3 & A4 --> P2[产品检验单Word导出·export-product-word·无技术指标列·判定结论带出检验结果/质检章留空盖章·Word打印] |
| | | A1 & A2 & A3 & A4 --> P2[产品检验单Word导出·export-product-word·无技术指标列·判定结论带出检验结果·检验/复核/批准签字带出检测人/复核人/审核人姓名·质检章留空盖章·Word打印] |
| | |
| | | | 检验日期 | `inspectDate`(新增响应字段) | | |
| | | | 检验项目 | 录入项名称 | **不含技术指标列**;分组行与公式文本不展示,分组下录入项平铺 | |
| | | | 检验结果 | 测量值 | 平行测量多值顿号合并(如 `10.023、10.115`);无值 `/` | |
| | | | 判定结论 | 留空 | 打印后手工填写 | |
| | | | 判定结论 | `checkResultText` | 自动带出检验结果文案(按 `qcType` 取字典,OQC 用 `mes_oqc_check_result`),未判定时留空 | |
| | | | 质检专用章 | 留空 | 打印后盖章 | |
| | | | 备注 | 单据 `remark`,为空时默认「检验结果仅对样品本身或本批次负责。」 | | |
| | | | 检验/复核/批准 | 固定留空 | 与判定结论/质检章一致,不输出姓名,打印后手写 | |
| | | | 检验 / 复核 / 批准 | `inspectorName` / `auditName` / `approveName` | 分别对应单据的检测人员、复核人、审核人;未指定人员时该段留空、打印后手写 | |
| | | |
| | | ## 接口变更点(`/mes/qc/indicator-result/report` 响应) |
| | | |
| | |
| | | |------|------| |
| | | | 多样品 | `resultId` 不传取最早样品;与 `/report` 行为一致 | |
| | | | 技术指标 | 本版式固定不输出阈值/标准值列;需要技术指标对照时使用既有「打印报告」化验记录版式 | |
| | | | 判定结论 | 系统不写入结论(含 checkResult),一律留空手填 | |
| | | | 判定结论 | 自动带出检验结果文案;单据未判定(`checkResult` 为空)时留空手填 | |
| | | | 检验/复核/批准 | 分别取检测人员、复核人、审核人姓名打印;三项均可为空(打印后手写),不因缺人员而报错 | |
| | | | 空值 | 文本空打印 `/`;批量/日期为空打印 `/` | |
| | | | 分组指标 | 分组本身不占行,仅平铺其下录入项(含中间参数行,如"试样质量 m/g") | |
| | | |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.ipqc.vo.MesQcIpqcSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO; |
| | | 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.indicator.MesQcIndicatorDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.ipqc.MesQcIpqcLineMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | 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.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | 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.qc.indicator.MesQcIndicatorService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.ipqc.MesQcIpqcService; |
| | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| | | |
| | | |
| | | @Tag(name = "管理后台 - MES 过程检验单(IPQC)") |
| | | @RestController |
| | | @RequestMapping("/mes/qc/ipqc") |
| | |
| | | @Resource |
| | | private MesQcIpqcService ipqcService; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private MesQcIndicatorResultService indicatorResultService; |
| | | @Resource |
| | |
| | | if (CollUtil.isEmpty(list)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcIpqcDO::getItemId)); |
| | | // 批量查询计量单位( |
| | | Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap( |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | // 批量查询计量单位 |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | // 批量查询检测/复核/审核人员 |
| | | Set<Long> qcUserIds = new HashSet<>(); |
| | | qcUserIds.addAll(convertSet(list, MesQcIpqcDO::getInspectorUserId)); |
| | |
| | | // 批量查询检测人员 |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesQcIpqcDO::getInspectorUserId)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcIpqcDO::getItemId)); |
| | | // 批量查询行明细 |
| | | List<MesQcIpqcLineDO> lines = ipqcLineMapper.selectListByIpqcIds(convertSet(list, MesQcIpqcDO::getId)); |
| | |
| | | for (MesQcIpqcDO ipqc : list) { |
| | | MesQcQualityExportVO vo = new MesQcQualityExportVO(); |
| | | vo.setDocName(ipqc.getName()); |
| | | MesMdItemDO item = itemMap.get(ipqc.getItemId()); |
| | | MdmItemRespDTO item = itemMap.get(ipqc.getItemId()); |
| | | vo.setItemName(item != null ? item.getName() : ""); |
| | | vo.setBatchNo(ipqc.getBatchCode()); |
| | | vo.setInspectDate(MesQcQualityExportVO.formatDateTime(ipqc.getInspectDate())); |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | 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.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | 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.dal.dataobject.qc.indicator.MesQcIndicatorDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.iqc.MesQcIqcLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.iqc.MesQcIqcLineMapper; |
| | | 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.enums.DictTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.service.qc.indicator.MesQcIndicatorService; |
| | |
| | | @Resource |
| | | private MesQcIndicatorService indicatorService; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private MesQcIqcLineMapper iqcLineMapper; |
| | | |
| | |
| | | Map<Long, SrmSupplierRespDTO> vendorMap = srmSupplierApi.getSupplierList( |
| | | convertSet(list, MesQcIqcDO::getVendorId)).getCheckedData().stream() |
| | | .collect(java.util.stream.Collectors.toMap(SrmSupplierRespDTO::getId, s -> s)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcIqcDO::getItemId)); |
| | | // 批量查询计量单位(通过物料的 unitMeasureId) |
| | | Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap( |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | // 批量查询检测/复核/审核人员 |
| | | Set<Long> qcUserIds = new HashSet<>(); |
| | | qcUserIds.addAll(convertSet(list, MesQcIqcDO::getInspectorUserId)); |
| | |
| | | // 批量查询检测人员 |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesQcIqcDO::getInspectorUserId)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcIqcDO::getItemId)); |
| | | // 批量查询行明细 |
| | | List<MesQcIqcLineDO> lines = iqcLineMapper.selectListByIqcIds(convertSet(list, MesQcIqcDO::getId)); |
| | |
| | | for (MesQcIqcDO iqc : list) { |
| | | MesQcQualityExportVO vo = new MesQcQualityExportVO(); |
| | | vo.setDocName(iqc.getName()); |
| | | MesMdItemDO item = itemMap.get(iqc.getItemId()); |
| | | MdmItemRespDTO item = itemMap.get(iqc.getItemId()); |
| | | vo.setItemName(item != null ? item.getName() : ""); |
| | | vo.setBatchNo(iqc.getVendorBatch()); |
| | | vo.setInspectDate(MesQcQualityExportVO.formatDateTime(iqc.getInspectDate())); |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO; |
| | | 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.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | 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.dal.dataobject.qc.indicator.MesQcIndicatorDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.oqc.MesQcOqcLineMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | 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.indicator.MesQcIndicatorService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.oqc.MesQcOqcService; |
| | |
| | | @Resource |
| | | private CrmCustomerApi customerApi; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private MesQcIndicatorResultService indicatorResultService; |
| | | @Resource |
| | |
| | | // 批量查询客户 |
| | | Map<Long, CrmCustomerRespDTO> clientMap = customerApi.getCustomerMap( |
| | | convertSet(list, MesQcOqcDO::getClientId)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcOqcDO::getItemId)); |
| | | // 批量查询计量单位 |
| | | Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap( |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | // 批量查询检测/复核/审核人员 |
| | | Set<Long> qcUserIds = new HashSet<>(); |
| | | qcUserIds.addAll(convertSet(list, MesQcOqcDO::getInspectorUserId)); |
| | |
| | | // 批量查询检测人员 |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesQcOqcDO::getInspectorUserId)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcOqcDO::getItemId)); |
| | | // 批量查询行明细 |
| | | List<MesQcOqcLineDO> lines = oqcLineMapper.selectListByOqcIds(convertSet(list, MesQcOqcDO::getId)); |
| | |
| | | for (MesQcOqcDO oqc : list) { |
| | | MesQcQualityExportVO vo = new MesQcQualityExportVO(); |
| | | vo.setDocName(oqc.getName()); |
| | | MesMdItemDO item = itemMap.get(oqc.getItemId()); |
| | | MdmItemRespDTO item = itemMap.get(oqc.getItemId()); |
| | | vo.setItemName(item != null ? item.getName() : ""); |
| | | vo.setBatchNo(oqc.getBatchCode()); |
| | | vo.setInspectDate(MesQcQualityExportVO.formatDateTime(oqc.getInspectDate())); |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.rqc.vo.MesQcRqcSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.vo.MesQcQualityExportVO; |
| | | 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.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | 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.dal.dataobject.qc.indicator.MesQcIndicatorDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.indicatorresult.MesQcIndicatorResultDetailDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.rqc.MesQcRqcLineMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | 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.indicator.MesQcIndicatorService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.indicatorresult.MesQcIndicatorResultService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.rqc.MesQcRqcService; |
| | |
| | | @Resource |
| | | private MesQcRqcService rqcService; |
| | | @Resource |
| | | private MesMdItemService itemService; |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MesMdUnitMeasureService unitMeasureService; |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | | private MesQcIndicatorResultService indicatorResultService; |
| | | @Resource |
| | |
| | | if (CollUtil.isEmpty(list)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcRqcDO::getItemId)); |
| | | // 批量查询计量单位 |
| | | Map<Long, MesMdUnitMeasureDO> unitMeasureMap = unitMeasureService.getUnitMeasureMap( |
| | | convertSet(itemMap.values(), MesMdItemDO::getUnitMeasureId)); |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | // 批量查询检测/复核/审核人员 |
| | | Set<Long> qcUserIds = new HashSet<>(); |
| | | qcUserIds.addAll(convertSet(list, MesQcRqcDO::getInspectorUserId)); |
| | |
| | | // 批量查询检测人员 |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesQcRqcDO::getInspectorUserId)); |
| | | // 批量查询物料 |
| | | Map<Long, MesMdItemDO> itemMap = itemService.getItemMap( |
| | | // 批量查询物料(MDM 物料) |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcRqcDO::getItemId)); |
| | | // 批量查询行明细 |
| | | List<MesQcRqcLineDO> lines = rqcLineMapper.selectListByRqcIds(convertSet(list, MesQcRqcDO::getId)); |
| | |
| | | for (MesQcRqcDO rqc : list) { |
| | | MesQcQualityExportVO vo = new MesQcQualityExportVO(); |
| | | vo.setDocName(rqc.getName()); |
| | | MesMdItemDO item = itemMap.get(rqc.getItemId()); |
| | | MdmItemRespDTO item = itemMap.get(rqc.getItemId()); |
| | | vo.setItemName(item != null ? item.getName() : ""); |
| | | vo.setBatchNo(rqc.getBatchCode()); |
| | | vo.setInspectDate(MesQcQualityExportVO.formatDateTime(rqc.getInspectDate())); |
| | |
| | | * 产品检验单(纸质"××产品质量检验单"版式)Word 构建器 |
| | | * |
| | | * 版式:标题(公司 + 产品检验单)→ 表头信息 5 行 → 检验项目/检验结果 两列矩阵 → |
| | | * 判定结论(自动带出检验结果)/质检专用章(留空盖章)→ 备注 → 检验/复核/批准签字行。 |
| | | * 判定结论(自动带出检验结果)/质检专用章(留空盖章)→ 备注 → |
| | | * 检验/复核/批准签字行(分别带出检测人、复核人、审核人姓名)。 |
| | | * 不含技术指标列;分组项不展示,录入项平铺;平行测量多值顿号合并。 |
| | | */ |
| | | public class MesQcProductReportWordBuilder { |
| | |
| | | writeItemTable(doc, report); |
| | | writeConclusionTable(doc, report); |
| | | writeRemarkTable(doc, report); |
| | | writeSignLine(doc); |
| | | writeSignLine(doc, report); |
| | | doc.write(out); |
| | | return out.toByteArray(); |
| | | } |
| | |
| | | |
| | | // ========== 签字行 ========== |
| | | |
| | | /** 签字行固定留空,与判定结论/质检专用章一致,打印后手写 */ |
| | | private static void writeSignLine(XWPFDocument doc) { |
| | | /** |
| | | * 签字行:检验/复核/批准分别带出检测人(inspectorUserId)、复核人(auditUserId)、 |
| | | * 审核人(approveUserId)姓名;未指定人员时该段留空,打印后手写 |
| | | */ |
| | | private static void writeSignLine(XWPFDocument doc, MesQcQcReportRespVO report) { |
| | | XWPFParagraph paragraph = doc.createParagraph(); |
| | | paragraph.setAlignment(ParagraphAlignment.LEFT); |
| | | paragraph.setSpacingBefore(400); |
| | | addRun(paragraph, "检验: 复核: 批准: ", 12, true); |
| | | String text = "检验:" + signName(report.getInspectorName()) |
| | | + " 复核:" + signName(report.getAuditName()) |
| | | + " 批准:" + signName(report.getApproveName()); |
| | | addRun(paragraph, text, 12, true); |
| | | } |
| | | |
| | | /** 签名段:有姓名则打印姓名并留签名余量,无姓名则整段留空便于手写 */ |
| | | private static String signName(String name) { |
| | | return notBlank(name) ? name + " " : " "; |
| | | } |
| | | |
| | | // ========== 表格基础 ========== |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.pendinginspect; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | 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.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.mysql.qc.pendinginspect.MesQcPendingInspectMapper; |
| | |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| | | |
| | | /** |
| | | * MES 待检任务 Service 实现类 |
| | |
| | | |
| | | @Resource |
| | | private MesQcPendingInspectMapper pendingInspectMapper; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | |
| | | @Override |
| | | public PageResult<MesQcPendingInspectRespVO> getPendingInspectPage(MesQcPendingInspectPageReqVO pageReqVO) { |
| | |
| | | IPage<MesQcPendingInspectRespVO> page = MyBatisUtils.buildPage(pageReqVO); |
| | | pendingInspectMapper.selectQcPendingPage(page, |
| | | pageReqVO.getSourceDocCode(), pageReqVO.getQcType(), pageReqVO.getItemId()); |
| | | List<MesQcPendingInspectRespVO> list = page.getRecords(); |
| | | // 填充物料信息(来源单据的 item_id 为 MDM 物料 ID) |
| | | if (CollUtil.isNotEmpty(list)) { |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesQcPendingInspectRespVO::getItemId)); |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | list.forEach(vo -> findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | | unitMeasure -> vo.setUnitName(unitMeasure.getName())); |
| | | })); |
| | | } |
| | | // 返回结果 |
| | | return new PageResult<>(page.getRecords(), page.getTotal()); |
| | | return new PageResult<>(list, page.getTotal()); |
| | | } |
| | | |
| | | } |
| | |
| | | // 备注默认文案 |
| | | assertTrue(tables.get(3).getRow(0).getCell(1).getText().contains("检验结果仅对样品本身或本批次负责")); |
| | | |
| | | // 签字行留空(打印后手写),不得出现姓名 |
| | | // 签字行带出姓名:检验=检测人、复核=复核人、批准=审核人 |
| | | String signLine = doc.getParagraphs().stream() |
| | | .filter(p -> p.getText().contains("批准:")) |
| | | .findFirst().orElseThrow().getText(); |
| | | assertTrue(signLine.contains("检验:") && signLine.contains("复核:")); |
| | | assertTrue(!signLine.contains("李博") && !signLine.contains("周亚洁") && !signLine.contains("包翠萍"), |
| | | "签字行应留空,不输出检验/复核/批准姓名"); |
| | | assertTrue(signLine.contains("检验:李博"), "检验应带出检测人姓名:" + signLine); |
| | | assertTrue(signLine.contains("复核:周亚洁"), "复核应带出复核人姓名:" + signLine); |
| | | assertTrue(signLine.contains("批准:包翠萍"), "批准应带出审核人姓名:" + signLine); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | public void testBuildWithoutSigners() throws IOException { |
| | | MesQcQcReportRespVO report = randomReport(); |
| | | report.setInspectorName(null).setAuditName("").setApproveName(null); |
| | | byte[] docx = MesQcProductReportWordBuilder.build(report); |
| | | try (InputStream in = new ByteArrayInputStream(docx); XWPFDocument doc = new XWPFDocument(in)) { |
| | | String signLine = doc.getParagraphs().stream() |
| | | .filter(p -> p.getText().contains("批准:")) |
| | | .findFirst().orElseThrow().getText(); |
| | | assertTrue(signLine.contains("检验:") && signLine.contains("复核:") && signLine.contains("批准:"), |
| | | "未指定人员时仍保留三个栏位供手写:" + signLine); |
| | | } |
| | | } |
| | | |