From 37fff9c937cfcaaa7ff5964853b3d91cbd2f5395 Mon Sep 17 00:00:00 2001 From: z Date: Wed, 27 May 2026 08:25:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E5=BA=8F=E6=8A=95=E6=96=99=E7=AE=A1?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../MatFeedExecuteController.java | 886 ++++++++++++++++++ .../vo/MatFeedExecuteCancelReqVO.java | 54 ++ .../vo/MatFeedExecuteConfirmReqVO.java | 85 ++ .../vo/MatFeedExecuteFeedInfoVO.java | 72 ++ .../vo/MatFeedExecuteFeedReqVO.java | 91 ++ .../vo/MatFeedExecuteInitVO.java | 56 ++ .../vo/MatFeedExecuteMatConfigVO.java | 50 + .../vo/MatFeedExecutePlanVO.java | 71 ++ .../vo/MatFeedExecuteStockVO.java | 72 ++ .../millexecute/MillExecuteController.java | 55 +- .../biz/dal/dataobject/matfeed/MatFeedDO.java | 3 +- .../rawstoragelog/RawStorageLogDO.java | 5 +- .../matfeeddetail/MatFeedDetailMapper.java | 12 + .../RawStorageInventoryMapper.java | 1 + .../src/api/biz/matfeedexecute/index.ts | 249 +++++ .../src/api/biz/millexecute/index.ts | 1 + .../src/types/auto-components.d.ts | 32 +- .../src/types/auto-imports.d.ts | 2 + .../src/views/biz/matfeedexecute/index.vue | 795 ++++++++++++++++ .../src/views/biz/millexecute/index.vue | 18 +- 20 files changed, 2583 insertions(+), 27 deletions(-) create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/MatFeedExecuteController.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteCancelReqVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteConfirmReqVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedInfoVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedReqVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteInitVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteMatConfigVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecutePlanVO.java create mode 100644 mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteStockVO.java create mode 100644 mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts create mode 100644 mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/MatFeedExecuteController.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/MatFeedExecuteController.java new file mode 100644 index 0000000..8848fa0 --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/MatFeedExecuteController.java @@ -0,0 +1,886 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute; + +import cn.hutool.core.util.ObjectUtil; +import com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult; +import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.ningxia.yunxi.chemmes.framework.security.core.util.SecurityFrameworkUtils; +import com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo.*; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.headno.HeadNoDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.machine.MachineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.machmat.MachMatDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.machmatdetail.MachMatDetailDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.material.MaterialDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.plan.PlanDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.planline.PlanLineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.planmachine.PlanMachineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.proline.ProLineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.rawstoragelog.RawStorageLogDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.shiftresult.ShiftResultDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.usermachine.UserMachineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.usermachine.UserMachineDetailDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.usermachine.UserMachineDetailMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.matfeed.MatFeedDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.matfeeddetail.MatFeedDetailDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.headno.HeadNoMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.machine.MachineMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.machmat.MachMatMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.machmatdetail.MachMatDetailMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.material.MaterialMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.matfeed.MatFeedMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.matfeeddetail.MatFeedDetailMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.plan.PlanMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.planline.PlanLineMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.planmachine.PlanMachineMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.proline.ProLineMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.rawstoragelog.RawStorageLogMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.shiftresult.ShiftResultMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.techproc.TechProcMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.usermachine.UserMachineMapper; +import com.ningxia.yunxi.chemmes.module.system.dal.dataobject.dict.DictDataDO; +import com.ningxia.yunxi.chemmes.module.system.dal.dataobject.user.AdminUserDO; +import com.ningxia.yunxi.chemmes.module.system.dal.mysql.dict.DictDataMapper; +import com.ningxia.yunxi.chemmes.module.system.dal.mysql.user.AdminUserMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.math.BigDecimal; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +import static com.ningxia.yunxi.chemmes.framework.common.pojo.CommonResult.success; + +/** + * 投料执行管理 Controller + * 功能:物料配置(表2) + 投料信息(表3) + 弹窗库存确认 + * 参考:磨机执行(MillExecute)的页面结构 + */ +@Tag(name = "管理后台 - 投料执行") +@RestController +@RequestMapping("/tpo/mat-feed-execute") +@Validated +public class MatFeedExecuteController { + + @Resource + private UserMachineMapper userMachineMapper; + @Resource + private UserMachineDetailMapper userMachineDetailMapper; + @Resource + private ShiftResultMapper shiftResultMapper; + @Resource + private PlanMapper planMapper; + @Resource + private PlanMachineMapper planMachineMapper; + @Resource + private MachineMapper machineMapper; + @Resource + private ProLineMapper proLineMapper; + @Resource + private PlanLineMapper planLineMapper; + @Resource + private HeadNoMapper headNoMapper; + @Resource + private MatFeedMapper matFeedMapper; + @Resource + private MatFeedDetailMapper matFeedDetailMapper; + @Resource + private MachMatMapper machMatMapper; + @Resource + private MachMatDetailMapper machMatDetailMapper; + @Resource + private MaterialMapper materialMapper; + @Resource + private DictDataMapper dictDataMapper; + @Resource + private TechProcMapper techProcMapper; + @Resource + private RawStorageLogMapper rawStorageLogMapper; + @Resource + private AdminUserMapper adminUserMapper; + + // ==================== 1、界面初始化 ==================== + + @GetMapping("/init") + @Operation(summary = "界面初始化 - 获取当前用户的产线、机台、班次信息") + public CommonResult init() { + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + String userId = String.valueOf(loginUserId); + + UserMachineDO userMachine = userMachineMapper.selectByUserId(userId); + List detailList = Collections.emptyList(); + if (userMachine != null) { + detailList = userMachineDetailMapper.selectListByUserMachId(userMachine.getId()); + if (detailList == null) { + detailList = Collections.emptyList(); + } + } + + List lineList = detailList.stream() + .filter(d -> d.getLineId() != null && d.getProLineName() != null) + .collect(Collectors.collectingAndThen( + Collectors.toMap( + UserMachineDetailDO::getLineId, + d -> MatFeedExecuteInitVO.LineOption.builder() + .lineId(d.getLineId()) + .lineName(d.getProLineName()) + .build(), + (a, b) -> a + ), + m -> new ArrayList<>(m.values()) + )); + + List machineList = detailList.stream() + .filter(d -> d.getMachineId() != null && d.getMachineName() != null) + .map(d -> MatFeedExecuteInitVO.MachineOption.builder() + .machineId(d.getMachineId()) + .machineName(d.getMachineName()) + .lineId(d.getLineId()) + .build()) + .collect(Collectors.toList()); + + LocalDate today = LocalDate.now(); + LocalDateTime nowDateTime = LocalDateTime.now(); + ShiftResultDO currentShift = shiftResultMapper.selectCurrent(nowDateTime); + + MatFeedExecuteInitVO vo = new MatFeedExecuteInitVO(); + vo.setLineList(lineList); + vo.setMachineList(machineList); + if (currentShift != null) { + vo.setShiftCd(currentShift.getClassRate()); + vo.setGroupCd(currentShift.getClassGroup()); + vo.setDefaultDate(today); + } + return success(vo); + } + + // ==================== 2、生产计划查询(表1) ==================== + // 与磨机执行的表1查询逻辑一致 + + @GetMapping("/plan-list") + @Operation(summary = "查询生产计划列表 - 表1") + public CommonResult> getPlanList( + @RequestParam("machineId") Integer machineId, + @RequestParam("lineId") Integer lineId, + @RequestParam("proDate") String proDate, + @RequestParam("shiftCd") String shiftCd, + @RequestParam(value = "groupCd", required = false) String groupCd) { + + List result = new ArrayList<>(); + LocalDate date = LocalDate.parse(proDate); + + // ===== Step 1: 查询 tpo_head_no,获取 plan_id 列表和抬头状态 ===== + // WHERE pro_date = :proDate AND shift_cd = :shiftCd AND machine_id = :machineId + LambdaQueryWrapperX headQuery = new LambdaQueryWrapperX() + .eq(HeadNoDO::getProDate, date) + .eq(HeadNoDO::getShiftCd, shiftCd) + .eq(HeadNoDO::getMachineId, machineId); + if (groupCd != null && !groupCd.isEmpty()) { + headQuery.eq(HeadNoDO::getGroupCd, groupCd); + } + List headNoList = headNoMapper.selectList(headQuery); + if (headNoList.isEmpty()) { + return success(result); + } + + // Map: planId -> HeadNoDO(用于获取 t3.pro_status 即机台计划状态) + Map headNoMap = new LinkedHashMap<>(); + List planIds = new ArrayList<>(); + for (HeadNoDO h : headNoList) { + if (h.getPlanId() != null && headNoMap.putIfAbsent(h.getPlanId(), h) == null) { + planIds.add(h.getPlanId()); + } + } + if (planIds.isEmpty()) { + return success(result); + } + + // ===== Step 2: 查询 tpl_plan 主表 ===== + List plans = planMapper.selectBatchIds(planIds); + Map planMap = plans.stream() + .collect(Collectors.toMap(PlanDO::getId, p -> p, (a, b) -> a)); + + // ===== Step 3: 查询 tpl_plan_line (JOIN tpl_plan.id = tpl_plan_line.pro_id) ===== + LambdaQueryWrapperX lineQuery = new LambdaQueryWrapperX() + .in(PlanLineDO::getProId, planIds); + if (lineId != null) { + lineQuery.eq(PlanLineDO::getLineId, lineId); + } + List planLines = planLineMapper.selectList(lineQuery); + // Map: planId -> PlanLineDO + Map planLineByPlanIdMap = planLines.stream() + .collect(Collectors.toMap(PlanLineDO::getProId, l -> l, (a, b) -> a)); + + // ===== Step 4: 查询 tpl_plan_machine (LEFT JOIN) ===== + // ON tpl_plan_line.id = tpl_plan_machine.pro_order_line_id + // AND tpl_plan_machine.machine_id = :machineId + // AND tpl_plan.pro_no = tpl_plan_machine.pro_no + Set planLineIds = planLines.stream() + .map(PlanLineDO::getId).collect(Collectors.toSet()); + Map planMachineByPlanIdMap = new HashMap<>(); + if (!planLineIds.isEmpty()) { + List planMachines = planMachineMapper.selectList( + new LambdaQueryWrapperX() + .in(PlanMachineDO::getProOrderLineId, planLineIds) + .eq(PlanMachineDO::getMachineId, machineId) + ); + // 构建映射: planLineId -> PlanLineDO + Map lineByIdMap = planLines.stream() + .collect(Collectors.toMap(PlanLineDO::getId, l -> l, (a, b) -> a)); + for (PlanMachineDO pm : planMachines) { + PlanLineDO matchedLine = lineByIdMap.get(pm.getProOrderLineId()); + if (matchedLine != null) { + // 校验 pro_no 匹配条件: t.pro_no = t2.pro_no + PlanDO matchedPlan = planMap.get(matchedLine.getProId()); + if (matchedPlan != null && matchedPlan.getProNo() != null + && matchedPlan.getProNo().equals(pm.getProNo())) { + planMachineByPlanIdMap.put(matchedLine.getProId(), pm); + } + } + } + } + + // ===== Step 5: 查询工艺流程名称 ===== + Map techProcMap = techProcMapper.selectList( + new LambdaQueryWrapperX() + ).stream().collect(Collectors.toMap( + com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.techproc.TechProcDO::getId, + com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.techproc.TechProcDO::getTechProc, + (a, b) -> a + )); + + // ===== Step 6: 构建结果 ===== + for (Integer planId : planIds) { + PlanDO plan = planMap.get(planId); + if (plan == null) continue; + + HeadNoDO headNo = headNoMap.get(planId); + PlanMachineDO pm = planMachineByPlanIdMap.get(planId); + PlanLineDO planLine = planLineByPlanIdMap.get(planId); + + // 机台计划状态: coalesce(t3.pro_status, '未执行') + String machineProStatus = (headNo != null && headNo.getProStatus() != null) + ? getProStatusName(headNo.getProStatus()) : "未执行"; + + // 计划状态: coalesce(t2.pro_status, t.plan_status) + String planStatus; + if (pm != null && pm.getProStatus() != null) { + planStatus = getProStatusName(pm.getProStatus()); + } else { + planStatus = getProStatusName(plan.getPlanStatus()); + } + + // 是否正在执行(用于前端底色浅蓝色) + boolean isExecuting = "执行中".equals(machineProStatus); + + String techProcName = plan.getProcessFlow() != null + ? techProcMap.getOrDefault(Integer.parseInt(plan.getProcessFlow()), plan.getTechProc()) + : plan.getTechProc(); + + MatFeedExecutePlanVO vo = MatFeedExecutePlanVO.builder() + .id(plan.getId()) + .planNo(plan.getProNo()) + .machineProStatus(machineProStatus) + .planStatus(planStatus) + .materialName(plan.getMaterialName()) + .spec(plan.getSpec()) + .planQty(plan.getPlanQty()) + .completeQty(plan.getCompleteQty()) + .planBgDate(plan.getPlanBgDate()) + .planEndDate(plan.getPlanEndDate()) + .techProc(techProcName) + .proDate(plan.getProDate()) + .machineCd(pm != null ? pm.getMachineCd() : null) + .machineName(pm != null ? pm.getMachineName() : null) + .machineId(pm != null ? pm.getMachineId() : null) + .lineName(planLine != null ? planLine.getLineName() + : (pm != null ? pm.getLineName() : null)) + .lineId(planLine != null ? planLine.getLineId() + : (pm != null ? pm.getLineId() : null)) + .isExecuting(isExecuting) + .build(); + + result.add(vo); + } + return success(result); + } + + private String getProStatusName(String proStatus) { + DictDataDO dictData = dictDataMapper.selectByDictTypeAndValue("plan_status", proStatus); + return dictData != null ? dictData.getLabel() : proStatus; + } + + // ==================== 3、物料配置查询(表2) ==================== + + @GetMapping("/mat-config") + @Operation(summary = "查询物料配置 - 表2(投入类物料)") + public CommonResult> getMatConfig( + @RequestParam(value = "planNo", required = false) String planNo, + @RequestParam("proDate") String proDate, + @RequestParam("shiftCd") String shiftCd, + @RequestParam("machineId") Integer machineId) { + + List result = new ArrayList<>(); + LocalDate date = LocalDate.parse(proDate); + + // ===== Step 1: 查询当前正在执行的计划 ===== + List executingHeads = headNoMapper.selectList( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getMachineId, machineId) + .eq(HeadNoDO::getProStatus, "2") + ); + + // 如果没有执行中的计划,物料配置返回空 + if (executingHeads.isEmpty()) { + return success(result); + } + + // ===== Step 2: 从 tpo_head_no, tpo_mat_feed 查询已投料数据 ===== + // SELECT t.* FROM tpo_head_no t, tpo_mat_feed t1 + // WHERE t.id = t1.head_id AND t.pro_date = proDate AND t.shift_cd = shiftCd + // AND t.plan_no = planNo AND t.machine_id = machineId + if (planNo != null && !planNo.isEmpty()) { + List matchedHeads = headNoMapper.selectList( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getProDate, date) + .eq(HeadNoDO::getShiftCd, shiftCd) + .eq(HeadNoDO::getPlanNo, planNo) + .eq(HeadNoDO::getMachineId, machineId) + ); + + if (!matchedHeads.isEmpty()) { + for (HeadNoDO headNo : matchedHeads) { + List feedList = matFeedMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDO::getHeadId, headNo.getId()) + ); + for (MatFeedDO feed : feedList) { + MatFeedExecuteMatConfigVO vo = MatFeedExecuteMatConfigVO.builder() + .id(feed.getId()) + .materialId(feed.getMaterialId()) + .materialCode(feed.getMaterialCode()) + .materialName(feed.getMaterialName()) + .matTypeRaw(feed.getMatType()) + .matType(convertMatTypeName(feed.getMatType())) + .spec(feed.getSpec()) + .unit(feed.getUnit()) + .atoQty(feed.getAtoQty() != null ? feed.getAtoQty() :null) + .confirmQty(feed.getConfirmQty()) + .dataSource(1) + .build(); + result.add(vo); + } + } + } + } + + // ===== Step 3: 如果步骤2没有数据,从机台投料配置表查询 ===== + // SELECT t1.material_name, t1.material_code, t1.mat_type, 其它字段null + // FROM tba_mach_mat t, tba_mach_mat_detail t1 + // WHERE t.id = t1.mach_mat_id AND t.enabled_status = 1 AND t1.is_input_mtrl = '1' + if (result.isEmpty()) { + List machMats = machMatMapper.selectList( + new LambdaQueryWrapperX() + .eq(MachMatDO::getMachineId, machineId) + .eq(MachMatDO::getEnabledStatus, 0) + ); + + for (MachMatDO mm : machMats) { + List details = machMatDetailMapper.selectList( + new LambdaQueryWrapperX() + .eq(MachMatDetailDO::getMachMatId, mm.getId()) + .eq(MachMatDetailDO::getIsInputMtrl, "1") + ); + + for (MachMatDetailDO md : details) { + // 根据物料id查询物料表获取规格型号 + MaterialDO material = md.getMaterialId() != null + ? materialMapper.selectById(md.getMaterialId()) : null; + MatFeedExecuteMatConfigVO vo = MatFeedExecuteMatConfigVO.builder() + .id(md.getId()) + .materialId(md.getMaterialId()) + .materialCode(md.getMaterialCode()) + .materialName(md.getMaterialName()) + .matTypeRaw(md.getMatType()) + .matType(convertMatTypeName(md.getMatType())) + .spec(material != null ? material.getSpec() : null) + .unit(md.getUnit()) + .atoQty(null) + .confirmQty(null) + .dataSource(2) + .build(); + result.add(vo); + } + } + } + + // 按物料编码升序排列 + result.sort(Comparator.comparing(MatFeedExecuteMatConfigVO::getMaterialCode, + Comparator.nullsLast(String::compareTo))); + + return success(result); + } + + // ==================== 4、投料信息查询(表3) ==================== + + @GetMapping("/feed-info") + @Operation(summary = "查询投料信息 - 表3") + public CommonResult> getFeedInfo( + @RequestParam(value = "planNo", required = false) String planNo, + @RequestParam("proDate") String proDate, + @RequestParam("shiftCd") String shiftCd, + @RequestParam("machineId") Integer machineId) { + + List result = new ArrayList<>(); + LocalDate date = LocalDate.parse(proDate); + + // 通过日期+班次+机台+planNo查询抬头单 + HeadNoDO headNo = headNoMapper.selectOne( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getMachineId, machineId) + .eq(HeadNoDO::getProDate, date) + .eq(HeadNoDO::getShiftCd, shiftCd) + .eqIfPresent(HeadNoDO::getPlanNo, planNo) + ); + + if (headNo == null) { + return success(result); + } + + // 查询投料主表 + List feedList = matFeedMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDO::getHeadId, headNo.getId()) + ); + + for (MatFeedDO feed : feedList) { + // 查询投料明细 + List details = matFeedDetailMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDetailDO::getFeedId, feed.getId()) + ); + for (MatFeedDetailDO detail : details) { + DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + MatFeedExecuteFeedInfoVO vo = MatFeedExecuteFeedInfoVO.builder() + .id(detail.getId()) + .materialId(detail.getMaterialId()) + .materialName(detail.getMaterialName()) + .matTypeRaw(detail.getMatType()) + .matType(convertMatTypeName(detail.getMatType())) + .confirmQty(detail.getConfirmQty()) + .feedQty(detail.getFeedQty()) + .lotNo(detail.getLotNo()) + .spec(detail.getSpec()) + .storeHouseName(detail.getStoreHouseName()) + .storeAreaName(detail.getStoreAreaName()) + .storeHouseId(detail.getStoreHouseId()) + .storeAreaId(detail.getStoreAreaId()) + .materialCode(detail.getMaterialCode()) + .planNo(feed.getPlanNo()) + .feedEmpName(detail.getFeedEmpName()) + .feedTime(detail.getFeedTime() != null ? detail.getFeedTime().format(dtf) : null) + .feedNo(detail.getFeedNo()) + .build(); + result.add(vo); + } + } + + return success(result); + } + + // ==================== 5、弹窗-库存信息查询 ==================== + + @GetMapping("/stock-info") + @Operation(summary = "弹窗-查询库存信息(根据物料配置列表查原材料的库存)") + public CommonResult> getStockInfo( + @RequestParam("machineId") Integer machineId, + @RequestParam("proDate") String proDate, + @RequestParam("shiftCd") String shiftCd, + @RequestParam("planNo") String planNo) { + + List result = new ArrayList<>(); + LocalDate date = LocalDate.parse(proDate); + + // 先获取当前物料配置列表(确认数量来源) + List matConfigList = this.getMatConfig(planNo, proDate, shiftCd, machineId) + .getData(); + if (matConfigList == null || matConfigList.isEmpty()) { + return success(result); + } + + // 构建物料ID->确认数量的映射 + Map confirmQtyMap = new LinkedHashMap<>(); + for (MatFeedExecuteMatConfigVO config : matConfigList) { + confirmQtyMap.put(config.getMaterialId(), config.getConfirmQty() != null ? + config.getConfirmQty() : BigDecimal.ZERO); + } + + int rowNum = 0; + // 对每个物料配置,从 twm_pro_storage_inventory 查询原材料库存 + for (MatFeedExecuteMatConfigVO config : matConfigList) { + rowNum++; + BigDecimal confirmQty = confirmQtyMap.getOrDefault(config.getMaterialId(), BigDecimal.ZERO); + + // TODO: 从存货台账(twm_pro_storage_inventory)或原料库存表查询该物料的库存记录 + // 这里先返回基础信息,后续可根据实际库存表结构调整查询逻辑 + MatFeedExecuteStockVO stock = MatFeedExecuteStockVO.builder() + .id(rowNum) + .rowNum(rowNum) + .materialName(config.getMaterialName()) + .matType(config.getMatType()) + .matTypeRaw(config.getMatTypeRaw()) + .spec(config.getSpec()) + .lotNo("") + .stockQty(BigDecimal.ZERO) // 需要从实际库存表查询 + .feedQty(confirmQty) // 默认填入确认数量,可手动修改 + .storeHouseName("") // 需要从实际库存表查询 + .storeAreaName("") // 需要从实际库存表查询 + .materialCode(config.getMaterialCode()) + .earliestInDate(null) // 需要从实际库存表查询 + .inventBillNo("") + .materialId(config.getMaterialId()) + .confirmQty(confirmQty) + .build(); + result.add(stock); + } + + return success(result); + } + + // ==================== 6、物料配置确认(表2确认) ==================== + + @PostMapping("/confirm-mat-config") + @Operation(summary = "物料配置确认 - 写入tpo_mat_feed") + @Transactional(rollbackFor = Exception.class) + public CommonResult confirmMatConfig(@Valid @RequestBody MatFeedExecuteConfirmReqVO reqVO) { + // (1) 校验产线/机台 + if (reqVO.getLineId() == null || reqVO.getMachineId() == null) { + return CommonResult.error(400, "产线、机台不能为空,请确认!"); + } + // (2) 校验日期/班次 + if (reqVO.getProDate() == null || reqVO.getProDate().isEmpty() + || reqVO.getShiftCd() == null || reqVO.getShiftCd().isEmpty()) { + return CommonResult.error(400, "生产日期或班次不能为空,请确认!"); + } + + LocalDate proDate = LocalDate.parse(reqVO.getProDate()); + Integer machineId = reqVO.getMachineId(); + String planNo = reqVO.getPlanNo(); + + // (3) 校验物料配置列表不为空 + if (reqVO.getMatConfigItems() == null || reqVO.getMatConfigItems().isEmpty()) { + return CommonResult.error(400, "物料配置信息列表为空,请查询数据!"); + } + + // (4) 循环管控:确认数量不能为空或0 + for (MatFeedExecuteConfirmReqVO.MatConfigItem item : reqVO.getMatConfigItems()) { + if (item.getConfirmQty() == null || item.getConfirmQty().compareTo(BigDecimal.ZERO) <= 0) { + return CommonResult.error(400, + "该物料(" + item.getMaterialName() + ")投料数量等于0,请确认!"); + } + } + + // (5) 校验机台计划状态存在'执行中' + List executingHeads = headNoMapper.selectList( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getMachineId, machineId) + .eq(HeadNoDO::getProStatus, "2") + ); + if (executingHeads == null || executingHeads.isEmpty()) { + return CommonResult.error(400, "当前机台没有执行中的计划,请确认"); + } + + // (6) 通过机台编码+日期+班次+计划号查询抬头单 + HeadNoDO headNo = headNoMapper.selectOne( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getProDate, proDate) + .eq(HeadNoDO::getShiftCd, reqVO.getShiftCd()) + .eqIfPresent(HeadNoDO::getMachineCd, reqVO.getMachineCd()) + .eq(HeadNoDO::getPlanNo, planNo) + ); + if (headNo == null) { + return CommonResult.error(400, "未找到对应的抬头单记录,请确认"); + } + + // 通过head_no查询tpo_mat_feed和tpo_mat_feed_detail是否已有数据 + List existingFeeds = matFeedMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDO::getHeadId, headNo.getId()) + ); + List existingDetails = Collections.emptyList(); + if (!existingFeeds.isEmpty()) { + List feedIds = existingFeeds.stream() + .map(MatFeedDO::getId).collect(Collectors.toList()); + existingDetails = matFeedDetailMapper.selectList( + new LambdaQueryWrapperX() + .in(MatFeedDetailDO::getFeedId, feedIds) + ); + } + boolean hasExistingData = !existingDetails.isEmpty(); + if (hasExistingData) { + return CommonResult.error(400, + "当前计划(" + planNo + "),当前日期、班次已投料,不允许修改!"); + } + // 先根据抬头单号删除 tpo_mat_feed 和 tpo_mat_feed_detail + for (MatFeedDO feed : existingFeeds) { + matFeedDetailMapper.delete( + new LambdaQueryWrapperX() + .eq(MatFeedDetailDO::getFeedId, feed.getId()) + ); + matFeedMapper.deleteById(feed.getId()); + } + // ========== 所有校验通过,开始写入 tpo_mat_feed ========== + for (MatFeedExecuteConfirmReqVO.MatConfigItem item : reqVO.getMatConfigItems()) { + MaterialDO material = item.getMaterialId() != null ? + materialMapper.selectById(item.getMaterialId()) : null; + + MatFeedDO matFeed = MatFeedDO.builder() + .planId(headNo.getPlanId()) + .planNo(planNo) + .headNo(headNo.getHeadNo()) + .headId(headNo.getId()) + .atoQty(item.getAtoQty()) + .confirmQty(item.getConfirmQty()) + .materialId(item.getMaterialId()) + .materialCode(item.getMaterialCode()) + .materialName(item.getMaterialName()) + .unit(material != null ? material.getUnit() : item.getUnit()) + .matType(material != null ? material.getMatType() : item.getMatTypeRaw()) + .spec(material != null ? material.getSpec() : item.getSpec()) + .build(); + matFeedMapper.insert(matFeed); + } + + return success(true); + } + + // ==================== 7、库存消耗(表3/弹窗确认) ==================== + + @PostMapping("/consume-stock") + @Operation(summary = "库存消耗确认 - 写入投料单号和出库流水") + @Transactional(rollbackFor = Exception.class) + public CommonResult consumeStock(@Valid @RequestBody MatFeedExecuteFeedReqVO reqVO) { + // (1) 校验产线/机台 + if (reqVO.getLineId() == null || reqVO.getMachineId() == null) { + return CommonResult.error(400, "产线、机台不能为空,请确认!"); + } + // (2) 校验日期/班次 + if (reqVO.getProDate() == null || reqVO.getProDate().isEmpty() + || reqVO.getShiftCd() == null || reqVO.getShiftCd().isEmpty()) { + return CommonResult.error(400, "生产日期或班次不能为空,请确认!"); + } + + LocalDate proDate = LocalDate.parse(reqVO.getProDate()); + Integer machineId = reqVO.getMachineId(); + String planNo = reqVO.getPlanNo(); + + // (3) 校验投料列表不为空 + if (reqVO.getFeedItems() == null || reqVO.getFeedItems().isEmpty()) { + return CommonResult.error(400, "投料信息列表为空,请刷新数据!"); + } + + // (4) 循环校验投料数量 > 0 + for (MatFeedExecuteFeedReqVO.FeedItem item : reqVO.getFeedItems()) { + if (item.getFeedQty() == null || item.getFeedQty().compareTo(BigDecimal.ZERO) <= 0) { + return CommonResult.error(400, + "该物料(" + item.getMaterialName() + ")投料数量等于0,请确认!"); + } + } + + // (5) 查询抬头单 + HeadNoDO headNo = headNoMapper.selectOne( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getMachineId, machineId) + .eq(HeadNoDO::getProDate, proDate) + .eq(HeadNoDO::getShiftCd, reqVO.getShiftCd()) + .eq(HeadNoDO::getPlanNo, planNo) + ); + if (headNo == null) { + return CommonResult.error(400, "当前计划抬头单不存在,请确认"); + } + + // ========== 开始写入 ========== + LocalDateTime now = LocalDateTime.now(); + Long loginUserId = SecurityFrameworkUtils.getLoginUserId(); + String nickname = ""; + if (loginUserId != null) { + AdminUserDO user = adminUserMapper.selectById(loginUserId); + nickname = user != null ? user.getNickname() : ""; + } + + // 生成投料单号 F + 年月日 + 2位流水号 + String feedNo = generateFeedNo(); + + // 更新/写入 tpo_mat_feed_detail 的投料信息 + for (MatFeedExecuteFeedReqVO.FeedItem item : reqVO.getFeedItems()) { + MaterialDO material = item.getMaterialId() != null ? + materialMapper.selectById(item.getMaterialId()) : null; + + String inventBillNo = generateInventBillNo(); + + // 查找对应的 mat_feed_detail 记录并更新 + List existingDetails = matFeedDetailMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDetailDO::getHeadId, headNo.getId()) + .eq(MatFeedDetailDO::getMaterialId, item.getMaterialId()) + ); + + MatFeedDetailDO detailToUpdate; + if (!existingDetails.isEmpty()) { + // 更新现有记录 + detailToUpdate = existingDetails.get(0); + detailToUpdate.setFeedNo(feedNo); + detailToUpdate.setFeedQty(item.getFeedQty()); + detailToUpdate.setLotNo(item.getLotNo()); + detailToUpdate.setStoreHouseId(item.getStoreHouseId()); + detailToUpdate.setStoreHouseName(item.getStoreHouseName()); + detailToUpdate.setStoreAreaId(item.getStoreAreaId()); + detailToUpdate.setStoreAreaName(item.getStoreAreaName()); + detailToUpdate.setFeedEmpId(loginUserId != null ? loginUserId.intValue() : null); + detailToUpdate.setFeedEmpName(nickname); + detailToUpdate.setFeedTime(now); + detailToUpdate.setInventBillNo(inventBillNo); + matFeedDetailMapper.updateById(detailToUpdate); + } else { + // 新增记录 + detailToUpdate = MatFeedDetailDO.builder() + .feedNo(feedNo) + .feedId(null) + .materialId(item.getMaterialId()) + .materialCode(material != null ? material.getMatCode() : item.getMaterialCode()) + .materialName(item.getMaterialName()) + .spec(material != null ? material.getSpec() : item.getSpec()) + .unit(material != null ? material.getUnit() : null) + .matType(item.getMatType()) + .feedQty(item.getFeedQty()) + .lotNo(item.getLotNo()) + .storeHouseId(item.getStoreHouseId()) + .storeHouseName(item.getStoreHouseName()) + .storeAreaId(item.getStoreAreaId()) + .storeAreaName(item.getStoreAreaName()) + .feedEmpId(loginUserId != null ? loginUserId.intValue() : null) + .feedEmpName(nickname) + .feedTime(now) + .headNo(headNo.getHeadNo()) + .headId(headNo.getId()) + .planId(headNo.getPlanId()) + .planNo(planNo) + .confirmQty(item.getConfirmQty()) + .inventBillNo(inventBillNo) + .build(); + matFeedDetailMapper.insert(detailToUpdate); + } + + // TODO: 写入出库流水 twm_raw_storage_log (operatorType='2', businessType='21') + } + + return success(true); + } + + /** + * 生成投料单号 + * 格式: F + 年份(4位) + 月(2位) + 日(2位) + 2位流水号 + */ + private String generateFeedNo() { + String ym = String.format("%tY% cancelFeed(@Valid @RequestBody MatFeedExecuteCancelReqVO reqVO) { + // (1)(2) 基础校验 + if (reqVO.getLineId() == null || reqVO.getMachineId() == null) { + return CommonResult.error(400, "产线、机台不能为空,请确认!"); + } + if (reqVO.getProDate() == null || reqVO.getProDate().isEmpty() + || reqVO.getShiftCd() == null || reqVO.getShiftCd().isEmpty()) { + return CommonResult.error(400, "生产日期或班次不能为空,请确认!"); + } + + String planNo = reqVO.getPlanNo(); + Integer machineId = reqVO.getMachineId(); + LocalDate proDate = LocalDate.parse(reqVO.getProDate()); + + // (3) 校验planNo + if (planNo == null || planNo.isEmpty()) { + return CommonResult.error(400, "选择执行计划为空,请选择!"); + } + + // (4) 查询抬头单 + HeadNoDO headNo = headNoMapper.selectOne( + new LambdaQueryWrapperX() + .eq(HeadNoDO::getMachineId, machineId) + .eq(HeadNoDO::getProDate, proDate) + .eq(HeadNoDO::getShiftCd, reqVO.getShiftCd()) + .eq(HeadNoDO::getPlanNo, planNo) + ); + if (headNo == null) { + return CommonResult.error(400, "当前计划不存在抬头单,请刷新界面"); + } + + // 删除 tpo_mat_feed 和 tpo_mat_feed_detail + List feedList = matFeedMapper.selectList( + new LambdaQueryWrapperX() + .eq(MatFeedDO::getHeadId, headNo.getId()) + ); + for (MatFeedDO feed : feedList) { + matFeedDetailMapper.delete( + new LambdaQueryWrapperX() + .eq(MatFeedDetailDO::getFeedId, feed.getId()) + ); + matFeedMapper.deleteById(feed.getId()); + } + + // 同时删除无feedId关联的detail记录(兜底) + matFeedDetailMapper.delete( + new LambdaQueryWrapperX() + .eq(MatFeedDetailDO::getHeadId, headNo.getId()) + .isNull(MatFeedDetailDO::getFeedId) + ); + + return success(true); + } + + // ==================== 内部方法 ==================== + + /** + * 物料类型编码转名称 + */ + private String convertMatTypeName(String matType) { + if (matType == null) return ""; + switch (matType) { + case "1": return "原材料"; + case "2": return "半成品"; + case "3": return "成品"; + case "4": return "联产品"; + case "6": return "废料"; + default: return matType; + } + } +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteCancelReqVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteCancelReqVO.java new file mode 100644 index 0000000..b143cfe --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteCancelReqVO.java @@ -0,0 +1,54 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 投料执行 - 取消(表3取消) Request VO + */ +@Schema(description = "管理后台 - 投料执行/投料取消请求VO") +@Data +public class MatFeedExecuteCancelReqVO { + + @Schema(description = "产线ID", required = true) + @NotNull(message = "产线不能为空") + private Integer lineId; + + @Schema(description = "产线名称") + private String lineName; + + @Schema(description = "产线编码") + private String lineCd; + + @Schema(description = "机台ID", required = true) + @NotNull(message = "机台不能为空") + private Integer machineId; + + @Schema(description = "机台名称") + private String machineName; + + @Schema(description = "机台编码") + private String machineCd; + + @Schema(description = "生产日期", required = true) + @NotNull(message = "生产日期不能为空") + private String proDate; + + @Schema(description = "班次", required = true) + @NotNull(message = "班次不能为空") + private String shiftCd; + + @Schema(description = "班组") + private String groupCd; + + @Schema(description = "生产计划号", required = true) + @NotNull(message = "生产计划号不能为空") + private String planNo; + + @Schema(description = "生产计划ID") + private Integer planId; +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteConfirmReqVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteConfirmReqVO.java new file mode 100644 index 0000000..bf12818 --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteConfirmReqVO.java @@ -0,0 +1,85 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; + +/** + * 投料执行 - 确认(表2确认) Request VO + */ +@Schema(description = "管理后台 - 投料执行/物料配置确认请求VO") +@Data +public class MatFeedExecuteConfirmReqVO { + + @Schema(description = "产线ID", required = true, example = "1") + @NotNull(message = "产线不能为空") + private Integer lineId; + + @Schema(description = "产线名称", example = "1号线") + private String lineName; + + @Schema(description = "产线编码") + private String lineCd; + + @Schema(description = "机台ID", required = true, example = "1") + @NotNull(message = "机台不能为空") + private Integer machineId; + + @Schema(description = "机台名称", example = "磨机01") + private String machineName; + + @Schema(description = "机台编码") + private String machineCd; + + @Schema(description = "生产日期", required = true, example = "2026-05-14") + @NotNull(message = "生产日期不能为空") + private String proDate; + + @Schema(description = "班次", required = true, example = "甲") + @NotNull(message = "班次不能为空") + private String shiftCd; + + @Schema(description = "班组") + private String groupCd; + + @Schema(description = "生产计划号", required = true) + @NotNull(message = "生产计划号不能为空") + private String planNo; + + @Schema(description = "生产计划ID") + private Integer planId; + + @Schema(description = "物料配置明细列表(表2所有行)") + private List matConfigItems; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "物料配置明细项") + public static class MatConfigItem { + @Schema(description = "ID") + private Integer id; + @Schema(description = "物料ID") + private Integer materialId; + @Schema(description = "物料名称") + private String materialName; + @Schema(description = "物料编码") + private String materialCode; + @Schema(description = "规格") + private String spec; + @Schema(description = "单位") + private String unit; + @Schema(description = "确认数量") + private BigDecimal confirmQty; + @Schema(description = "采集数量") + private BigDecimal atoQty; + @Schema(description = "数据来源 1=已投料数据 2=机台配置投入物料") + private Integer dataSource; + @Schema(description = "物料类型原始值") + private String matTypeRaw; + } +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedInfoVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedInfoVO.java new file mode 100644 index 0000000..795cc85 --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedInfoVO.java @@ -0,0 +1,72 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 投料执行 - 投料信息(表3) VO + */ +@Schema(description = "管理后台 - 投料执行/投料信息VO") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MatFeedExecuteFeedInfoVO { + + @Schema(description = "ID") + private Integer id; + + @Schema(description = "物料ID") + private Integer materialId; + + @Schema(description = "物料名称") + private String materialName; + + @Schema(description = "物料类型(原始值)") + private String matTypeRaw; + + @Schema(description = "物料类型(显示名称)") + private String matType; + + @Schema(description = "确认数量(来自物料配置的确认数量)") + private BigDecimal confirmQty; + + @Schema(description = "投料数量") + private BigDecimal feedQty; + + @Schema(description = "批次号") + private String lotNo; + + @Schema(description = "规格型号") + private String spec; + + @Schema(description = "仓库名称") + private String storeHouseName; + + @Schema(description = "库区名称") + private String storeAreaName; + + @Schema(description = "仓库ID") + private Integer storeHouseId; + + @Schema(description = "库区ID") + private Integer storeAreaId; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "计划号") + private String planNo; + + @Schema(description = "投料人") + private String feedEmpName; + + @Schema(description = "投料时间") + private String feedTime; + + @Schema(description = "投料单号") + private String feedNo; +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedReqVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedReqVO.java new file mode 100644 index 0000000..17eb02b --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteFeedReqVO.java @@ -0,0 +1,91 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotNull; + +/** + * 投料执行 - 库存消耗(表3库存消耗) Request VO + */ +@Schema(description = "管理后台 - 投料执行/库存消耗请求VO") +@Data +public class MatFeedExecuteFeedReqVO { + + @Schema(description = "产线ID", required = true) + @NotNull(message = "产线不能为空") + private Integer lineId; + + @Schema(description = "产线名称") + private String lineName; + + @Schema(description = "产线编码") + private String lineCd; + + @Schema(description = "机台ID", required = true) + @NotNull(message = "机台不能为空") + private Integer machineId; + + @Schema(description = "机台名称") + private String machineName; + + @Schema(description = "机台编码") + private String machineCd; + + @Schema(description = "生产日期", required = true) + @NotNull(message = "生产日期不能为空") + private String proDate; + + @Schema(description = "班次", required = true) + @NotNull(message = "班次不能为空") + private String shiftCd; + + @Schema(description = "班组") + private String groupCd; + + @Schema(description = "生产计划号", required = true) + @NotNull(message = "生产计划号不能为空") + private String planNo; + + @Schema(description = "生产计划ID") + private Integer planId; + + @Schema(description = "投料信息列表(弹窗中表2-库存信息,投料数量手动填写)") + private java.util.List feedItems; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "投料信息明细项") + public static class FeedItem { + @Schema(description = "ID(库存记录或mat_feed_detail的id)") + private Integer id; + @Schema(description = "物料ID") + private Integer materialId; + @Schema(description = "物料名称") + private String materialName; + @Schema(description = "物料类型") + private String matType; + @Schema(description = "规格") + private String spec; + @Schema(description = "批次号") + private String lotNo; + @Schema(description = "投料数量") + private java.math.BigDecimal feedQty; + @Schema(description = "仓库ID") + private Integer storeHouseId; + @Schema(description = "仓库名称") + private String storeHouseName; + @Schema(description = "库区ID") + private Integer storeAreaId; + @Schema(description = "库区名称") + private String storeAreaName; + @Schema(description = "物料编码") + private String materialCode; + @Schema(description = "存货账单号") + private String inventBillNo; + @Schema(description = "确认数量") + private java.math.BigDecimal confirmQty; + } +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteInitVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteInitVO.java new file mode 100644 index 0000000..a4d822a --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteInitVO.java @@ -0,0 +1,56 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.time.LocalDate; +import java.util.List; + +/** + * 投料执行 - 界面初始化 VO + */ +@Schema(description = "管理后台 - 投料执行/界面初始化VO") +@Data +public class MatFeedExecuteInitVO { + + @Schema(description = "产线列表", example = "[]") + private List lineList; + + @Schema(description = "机台列表", example = "[]") + private List machineList; + + @Schema(description = "默认生产日期", example = "2026-05-14") + private LocalDate defaultDate; + + @Schema(description = "默认班次", example = "甲") + private String shiftCd; + + @Schema(description = "默认班组", example = "A组") + private String groupCd; + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "产线选项") + public static class LineOption { + @Schema(description = "产线ID") + private Integer lineId; + @Schema(description = "产线名称") + private String lineName; + } + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + @Schema(description = "机台选项") + public static class MachineOption { + @Schema(description = "机台ID") + private Integer machineId; + @Schema(description = "机台名称") + private String machineName; + @Schema(description = "产线ID") + private Integer lineId; + } +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteMatConfigVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteMatConfigVO.java new file mode 100644 index 0000000..5e3d373 --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteMatConfigVO.java @@ -0,0 +1,50 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; + +/** + * 投料执行 - 物料配置(表2) VO + */ +@Schema(description = "管理后台 - 投料执行/物料配置VO") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MatFeedExecuteMatConfigVO { + + @Schema(description = "ID(来自tba_mach_mat_detail)") + private Integer id; + + @Schema(description = "物料ID") + private Integer materialId; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "物料名称") + private String materialName; + + @Schema(description = "物料类型(原始值)") + private String matTypeRaw; + + @Schema(description = "物料类型(显示名称)") + private String matType; + + @Schema(description = "规格型号") + private String spec; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "采集数量") + private BigDecimal atoQty; + + @Schema(description = "确认数量") + private BigDecimal confirmQty; + + @Schema(description = "数据来源 1=已投料数据 2=机台配置投入物料") + private Integer dataSource; +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecutePlanVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecutePlanVO.java new file mode 100644 index 0000000..35ea5f1 --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecutePlanVO.java @@ -0,0 +1,71 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 投料执行 - 生产计划(表1) VO + */ +@Schema(description = "管理后台 - 投料执行/生产计划VO") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MatFeedExecutePlanVO { + + @Schema(description = "计划ID") + private Integer id; + + @Schema(description = "生产计划号") + private String planNo; + + @Schema(description = "机台计划状态") + private String machineProStatus; + + @Schema(description = "计划状态") + private String planStatus; + + @Schema(description = "产品名称") + private String materialName; + + @Schema(description = "规格型号") + private String spec; + + @Schema(description = "计划数量") + private BigDecimal planQty; + + @Schema(description = "完成数量") + private BigDecimal completeQty; + @Schema(description = "计划开始日期") + private LocalDate planBgDate; + + @Schema(description = "计划结束日期") + private LocalDate planEndDate; + + @Schema(description = "工艺流程") + private String techProc; + + @Schema(description = "计划日期(生产日期)") + private LocalDate proDate; + + @Schema(description = "机台编码") + private String machineCd; + + @Schema(description = "机台名称") + private String machineName; + + @Schema(description = "机台ID") + private Integer machineId; + + @Schema(description = "产线名称") + private String lineName; + + @Schema(description = "产线ID") + private Integer lineId; + + @Schema(description = "是否正在执行(true=底色浅蓝色)") + private Boolean isExecuting; +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteStockVO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteStockVO.java new file mode 100644 index 0000000..febff1f --- /dev/null +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/matfeedexecute/vo/MatFeedExecuteStockVO.java @@ -0,0 +1,72 @@ +package com.ningxia.yunxi.chemmes.module.biz.controller.admin.matfeedexecute.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 投料执行 - 弹窗库存信息 VO + */ +@Schema(description = "管理后台 - 投料执行/弹窗库存信息VO") +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class MatFeedExecuteStockVO { + + @Schema(description = "ID") + private Integer id; + + @Schema(description = "序号(前端展示用)") + private Integer rowNum; + + @Schema(description = "物料名称") + private String materialName; + + @Schema(description = "物料类型(显示名称)") + private String matType; + + @Schema(description = "物料类型(原始值)") + private String matTypeRaw; + + @Schema(description = "规格") + private String spec; + + @Schema(description = "批次号") + private String lotNo; + + @Schema(description = "库存数量") + private BigDecimal stockQty; + + @Schema(description = "投料数量(可编辑)") + private BigDecimal feedQty; + + @Schema(description = "仓库名称") + private String storeHouseName; + + @Schema(description = "库区名称") + private String storeAreaName; + + @Schema(description = "物料编码") + private String materialCode; + + @Schema(description = "最早入库日期") + private LocalDate earliestInDate; + + @Schema(description = "存货账单号") + private String inventBillNo; + + @Schema(description = "物料ID") + private Integer materialId; + + @Schema(description = "仓库ID") + private Integer storeHouseId; + + @Schema(description = "库区ID") + private Integer storeAreaId; + + @Schema(description = "确认数量(来自表2)") + private BigDecimal confirmQty; +} diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/millexecute/MillExecuteController.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/millexecute/MillExecuteController.java index 4604d1b..d889f02 100644 --- a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/millexecute/MillExecuteController.java +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/controller/admin/millexecute/MillExecuteController.java @@ -17,6 +17,7 @@ import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.plan.PlanDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.planline.PlanLineDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.planmachine.PlanMachineDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.proline.ProLineDO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.rawstorageinventory.RawStorageInventoryDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.shiftresult.ShiftResultDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.usermachine.UserMachineDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.usermachine.UserMachineDetailDO; @@ -44,6 +45,7 @@ import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.plan.PlanMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.planline.PlanLineMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.planmachine.PlanMachineMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.proline.ProLineMapper; +import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.rawstorageinventory.RawStorageInventoryMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.shiftresult.ShiftResultMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.usermachine.UserMachineMapper; import com.ningxia.yunxi.chemmes.module.biz.dal.mysql.techproc.TechProcMapper; @@ -149,6 +151,8 @@ public class MillExecuteController { private MaterialMapper materialMapper; @Resource private AdminUserMapper adminUserMapper; + @Resource + private RawStorageInventoryMapper rawStorageInventoryMapper; // ==================== 1、界面初始化 ==================== @@ -245,6 +249,7 @@ public class MillExecuteController { @Operation(summary = "查询生产计划列表 - 表1") public CommonResult> getPlanList( @RequestParam("machineId") Integer machineId, + @RequestParam("lineId") Integer lineId, @RequestParam("proDate") String proDate, @RequestParam("shiftCd") String shiftCd, @RequestParam(value = "groupCd", required = false) String groupCd @@ -271,11 +276,25 @@ public class MillExecuteController { ).stream().map(HeadNoDO::getPlanId).distinct().collect(Collectors.toList()); // 条件2: plan_status in ('1','2','4') 的计划ID(已下发/执行中/暂停) - List statusPlanIds = planMapper.selectList( - new com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX() - .in(PlanDO::getPlanStatus, Arrays.asList("1", "2", "4")) - .select(PlanDO::getId) - ).stream().map(PlanDO::getId).collect(Collectors.toList()); + // 关联 tpl_plan_line 按 lineId 过滤 + List statusPlanIds = new ArrayList<>(); + if (lineId != null) { + // 先查询指定 lineId 的 tpl_plan_line 记录,获取 pro_id 列表 + List linePlanIds = planLineMapper.selectList( + new LambdaQueryWrapperX() + .eq(PlanLineDO::getLineId, lineId) + .select(PlanLineDO::getProId) + ).stream().map(PlanLineDO::getProId).distinct().collect(Collectors.toList()); + + if (!linePlanIds.isEmpty()) { + statusPlanIds = planMapper.selectList( + new LambdaQueryWrapperX() + .in(PlanDO::getId, linePlanIds) + .in(PlanDO::getPlanStatus, Arrays.asList("1", "2", "4")) + .select(PlanDO::getId) + ).stream().map(PlanDO::getId).collect(Collectors.toList()); + } + } // 合并所有计划ID(union all语义) Set allPlanIds = new LinkedHashSet<>(); @@ -767,7 +786,7 @@ public class MillExecuteController { return CommonResult.error(400, "该计划(" + planNo + ")不存在,请刷新数据!"); } // (3) 校验计划状态是否为已下发(1)或暂停(4) - if (!"1".equals(plan.getPlanStatus()) && !"4".equals(plan.getPlanStatus())) { + if (!"1".equals(plan.getPlanStatus()) && !"4".equals(plan.getPlanStatus())&& !"2".equals(plan.getPlanStatus())) { return CommonResult.error(400, "该计划(" + planNo + ")非下发或暂停状态,请确认!"); } @@ -1105,7 +1124,6 @@ public class MillExecuteController { // (6) 判断是否存在投料或产出数据 String headNoStr = confirmHead.getHeadNo(); Long matOutCount = matOutMapper.countByHeadNo(headNoStr); - // TODO: 投料表(tpo_mat_feed)尚未创建,待创建后补充投料数据检查 Long matFeedCount = matFeedMapper.countByHeadNo(headNoStr); if ((matOutCount != null && matOutCount > 0) || matFeedCount > 0) { return CommonResult.error(400, @@ -1237,6 +1255,9 @@ public class MillExecuteController { // --- B2.5: 根据物料ID查询物料表获取物料信息 --- MaterialDO material = materialMapper.selectById(item.getMaterialId()); + String inventBillNo = generateInventBillNo(); + + // B3: 插入 tpo_mat_out_detail MatOutDetailDO detail = MatOutDetailDO.builder() .outNo(outNo) @@ -1257,12 +1278,12 @@ public class MillExecuteController { .storeAreaName(wipConfig != null ? wipConfig.getStoreAreaName() : null) .storeAreCd(wipConfig != null ? wipConfig.getStoreAreCd() : null) .storeHouseCd(wipConfig != null ? wipConfig.getStoreHouseCd() : null) + .inventBillNo(inventBillNo) .build(); matOutDetailMapper.insert(detail); // --- C: 插入 twm_pro_storage_inventory (存货台账) --- - String inventBillNo = generateInventBillNo(); - ProStorageInventoryDO inventory = ProStorageInventoryDO.builder() + RawStorageInventoryDO inventory = RawStorageInventoryDO.builder() .storeHouseId(wipConfig != null ? wipConfig.getStoreHouseId() : null) .storeAreaId(wipConfig != null ? wipConfig.getStoreAreaId() : null) .storeHouseName(wipConfig != null ? wipConfig.getStoreHouseName() : null) @@ -1277,12 +1298,10 @@ public class MillExecuteController { .lotNo(lotNo) .yardQty(item.getOutQty()) .useQty(item.getOutQty()) - .planId(currentHead.getPlanId()) - .proNo(planNo) .inventBillNo(inventBillNo) .earStoreDate(LocalDate.now()) .build(); - proStorageInventoryMapper.insert(inventory); + rawStorageInventoryMapper.insert(inventory); // --- D: 插入 twm_raw_storage_log (出入库流水) --- RawStorageLogDO storageLog = RawStorageLogDO.builder() @@ -1308,7 +1327,7 @@ public class MillExecuteController { .relarionId(matOutId) .relarionDetailId(detail.getId()) .operatorName(nickname) -// .inventBillNo(inventBillNo) + .inventBillNo(inventBillNo) .build(); rawStorageLogMapper.insert(storageLog); } @@ -1362,7 +1381,7 @@ public class MillExecuteController { */ private String generateInventBillNo() { String ym = String.format("%tY%() - .eq(ProStorageInventoryDO::getInventBillNo, detail.getInventBillNo()) + RawStorageInventoryDO inventory = rawStorageInventoryMapper.selectOne( + new LambdaQueryWrapperX() + .eq(RawStorageInventoryDO::getInventBillNo, detail.getInventBillNo()) ); if (inventory != null) { BigDecimal yardQty = inventory.getYardQty() != null ? inventory.getYardQty() : BigDecimal.ZERO; @@ -1624,7 +1643,7 @@ public class MillExecuteController { case "2": return "半成品"; case "3": return "成品"; case "4": return "联产品"; - case "6": return "回收料"; + case "6": return "废料"; default: return matType; } } diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/matfeed/MatFeedDO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/matfeed/MatFeedDO.java index 2be61cb..ae78fdf 100644 --- a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/matfeed/MatFeedDO.java +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/matfeed/MatFeedDO.java @@ -1,5 +1,6 @@ package com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.matfeed; +import com.ningxia.yunxi.chemmes.framework.mybatis.core.dataobject.BaseDOWithoutLogic; import lombok.*; import java.util.*; import java.time.LocalDateTime; @@ -22,7 +23,7 @@ import com.ningxia.yunxi.chemmes.framework.mybatis.core.dataobject.BaseDO; @Builder @NoArgsConstructor @AllArgsConstructor -public class MatFeedDO extends BaseDO { +public class MatFeedDO extends BaseDOWithoutLogic { /** * 自增字段 diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/rawstoragelog/RawStorageLogDO.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/rawstoragelog/RawStorageLogDO.java index 84f99bf..8dec001 100644 --- a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/rawstoragelog/RawStorageLogDO.java +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/dataobject/rawstoragelog/RawStorageLogDO.java @@ -158,7 +158,10 @@ public class RawStorageLogDO extends BaseDOWithoutLogic { * 关联子单号id */ private Integer relarionDetailId; - private String inventBillNo; + + + + } diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/matfeeddetail/MatFeedDetailMapper.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/matfeeddetail/MatFeedDetailMapper.java index cab868f..7a778b7 100644 --- a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/matfeeddetail/MatFeedDetailMapper.java +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/matfeeddetail/MatFeedDetailMapper.java @@ -51,4 +51,16 @@ public interface MatFeedDetailMapper extends BaseMapperX { .orderByDesc(MatFeedDetailDO::getId)); } + /** + * 查询最大的投料单号 + */ + default String selectMaxFeedNo() { + List list = selectList( + new LambdaQueryWrapperX() + .isNotNull(MatFeedDetailDO::getFeedNo) + .orderByDesc(MatFeedDetailDO::getFeedNo) + .last("LIMIT 1") + ); + return (list != null && !list.isEmpty()) ? list.get(0).getFeedNo() : null; + } } \ No newline at end of file diff --git a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/rawstorageinventory/RawStorageInventoryMapper.java b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/rawstorageinventory/RawStorageInventoryMapper.java index 3603674..dcf6b9d 100644 --- a/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/rawstorageinventory/RawStorageInventoryMapper.java +++ b/mes-module-chemmes/mes-module-chemmes-biz/src/main/java/com/ningxia/yunxi/chemmes/module/biz/dal/mysql/rawstorageinventory/RawStorageInventoryMapper.java @@ -4,6 +4,7 @@ import com.ningxia.yunxi.chemmes.framework.common.pojo.PageResult; import com.ningxia.yunxi.chemmes.framework.mybatis.core.mapper.BaseMapperX; import com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX; import com.ningxia.yunxi.chemmes.module.biz.controller.admin.rawstorageinventory.vo.RawStorageInventoryPageReqVO; +import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.prostorageinventory.ProStorageInventoryDO; import com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.rawstorageinventory.RawStorageInventoryDO; import org.apache.ibatis.annotations.Mapper; diff --git a/mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts b/mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts new file mode 100644 index 0000000..8cb6de7 --- /dev/null +++ b/mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts @@ -0,0 +1,249 @@ +import request from '@/config/axios' + +// ================== 初始化相关 ================== +export interface LineOption { + lineId: number + lineName: string +} + +export interface MachineOption { + machineId: number + machineName: string + lineId?: number +} + +export interface MatFeedExecuteInitVO { + lineList: LineOption[] + machineList: MachineOption[] + defaultDate: string + shiftCd: string + groupCd: string +} + +// 初始化接口 +export const getMatFeedExecuteInit = async (): Promise => { + return await request.get({ url: '/tpo/mat-feed-execute/init' }) +} + +// ================== 生产计划(表1)相关 ================== +export interface MatFeedExecutePlanVO { + id: number + planNo: string + machineProStatus: string + planStatus: string + materialName: string + spec: string + planQty: number + completeQty: number + planBgDate: string + planEndDate: string + techProc: string + proDate: string + machineCd: string + machineName: string + machineId: number + lineName: string + lineId: number + lineCd: string + isExecuting: boolean +} + +// 生产计划查询 +export const getPlanList = async (params: { + machineId?: number + lineId?: number + proDate?: string + shiftCd?: string + groupCd?: string +}): Promise => { + return await request.get({ url: '/tpo/mat-feed-execute/plan-list', params }) +} + +// ================== 物料配置(表2)相关 ================== +export interface MatFeedExecuteMatConfigVO { + id: number + materialId: number + materialCode: string + materialName: string + matTypeRaw: string + matType: string + spec: string + unit: string + atoQty: number + confirmQty: number | string + dataSource: number // 1=已投料数据 2=机台配置投入物料 +} + +// 物料配置查询 +export const getMatConfig = async (params: { + machineId: number + planNo?: string + proDate: string + shiftCd: string +}): Promise => { + return await request.get({ url: '/tpo/mat-feed-execute/mat-config', params }) +} + +// ================== 投料信息(表3)相关 ================== +export interface MatFeedExecuteFeedInfoVO { + id: number + materialId: number + materialName: string + matTypeRaw: string + matType: string + confirmQty: number + feedQty: number + lotNo: string + spec: string + storeHouseName: string + storeAreaName: string + storeHouseId: number + storeAreaId: number + materialCode: string + planNo: string + feedEmpName: string + feedTime: string + feedNo: string +} + +// 投料信息查询 +export const getFeedInfo = async (params: { + machineId: number + planNo?: string + proDate: string + shiftCd: string +}): Promise => { + return await request.get({ url: '/tpo/mat-feed-execute/feed-info', params }) +} + +// ================== 弹窗库存信息相关 ================== +export interface MatFeedExecuteStockVO { + id: number + rowNum: number + materialName: string + matType: string + matTypeRaw: string + spec: string + lotNo: string + stockQty: number + feedQty: number | string // 可编辑,手动修改 + storeHouseName: string + storeAreaName: string + materialCode: string + earliestInDate: string + inventBillNo: string + materialId: number + storeHouseId: number + storeAreaId: number + confirmQty: number +} + +// 弹窗库存信息查询 +export const getStockInfo = async (params: { + machineId: number + proDate: string + shiftCd: string + planNo: string +}): Promise => { + return await request.get({ url: '/tpo/mat-feed-execute/stock-info', params }) +} + +// ================== 物料配置确认接口 ================== + +/** 物料配置确认 - 单条明细 */ +export interface MatConfigItem { + id: number + materialId: number + materialName?: string + materialCode?: string + spec?: string + unit?: string + confirmQty: number | string + atoQty?: number + dataSource?: number + matTypeRaw?: string +} + +/** 物料配置确认 Request VO */ +export interface MatFeedConfirmReqVO { + lineId: number + lineName?: string + lineCd?: string + machineId: number + machineName?: string + machineCd?: string + proDate: string + shiftCd: string + groupCd?: string + planNo: string + planId?: number + matConfigItems: MatConfigItem[] +} + +// 物料配置确认(写入tpo_mat_feed + tpo_mat_feed_detail) +export const confirmMatConfig = async (data: MatFeedConfirmReqVO): Promise => { + return await request.post({ url: '/tpo/mat-feed-execute/confirm-mat-config', data }) +} + +// ================== 库存消耗(投料)接口 ================== + +/** 库存消耗 - 单条明细 */ +export interface FeedItem { + id: number + materialId: number + materialName?: string + matType?: string + spec?: string + lotNo?: string + feedQty: number | string + storeHouseId?: number + storeHouseName?: string + storeAreaId?: number + storeAreaName?: string + materialCode?: string + inventBillNo?: string + confirmQty?: number +} + +/** 库存消耗 Request VO */ +export interface MatFeedFeedReqVO { + lineId: number + lineName?: string + lineCd?: string + machineId: number + machineName?: string + machineCd?: string + proDate: string + shiftCd: string + groupCd?: string + planNo: string + planId?: number + feedItems: FeedItem[] +} + +// 库存消耗确认(生成投料单号 + 写入出库流水) +export const consumeStock = async (data: MatFeedFeedReqVO): Promise => { + return await request.post({ url: '/tpo/mat-feed-execute/consume-stock', data }) +} + +// ================== 取消接口 ================== + +/** 取消 Request VO */ +export interface MatFeedCancelReqVO { + lineId: number + lineName?: string + lineCd?: string + machineId: number + machineName?: string + machineCd?: string + proDate: string + shiftCd: string + groupCd?: string + planNo: string + planId?: number +} + +// 投料取消 +export const cancelFeed = async (data: MatFeedCancelReqVO): Promise => { + return await request.put({ url: '/tpo/mat-feed-execute/cancel-feed', data }) +} diff --git a/mes-ui/mes-ui-admin-vue3/src/api/biz/millexecute/index.ts b/mes-ui/mes-ui-admin-vue3/src/api/biz/millexecute/index.ts index 2ec6d77..03616ad 100644 --- a/mes-ui/mes-ui-admin-vue3/src/api/biz/millexecute/index.ts +++ b/mes-ui/mes-ui-admin-vue3/src/api/biz/millexecute/index.ts @@ -59,6 +59,7 @@ export interface MillExecutePlanVO { // 生产计划查询 export const getPlanList = async (params: { machineId?: number + lineId?: number proDate?: string shiftCd?: string groupCd?: string diff --git a/mes-ui/mes-ui-admin-vue3/src/types/auto-components.d.ts b/mes-ui/mes-ui-admin-vue3/src/types/auto-components.d.ts index a549149..11a8628 100644 --- a/mes-ui/mes-ui-admin-vue3/src/types/auto-components.d.ts +++ b/mes-ui/mes-ui-admin-vue3/src/types/auto-components.d.ts @@ -19,7 +19,6 @@ declare module 'vue' { ContentDetailWrap: typeof import('./../components/ContentDetailWrap/src/ContentDetailWrap.vue')['default'] ContentWrap: typeof import('./../components/ContentWrap/src/ContentWrap.vue')['default'] CopperModal: typeof import('./../components/Cropper/src/CopperModal.vue')['default'] - copy: typeof import('./../views/biz/purreceipt copy/index.vue')['default'] CountTo: typeof import('./../components/CountTo/src/CountTo.vue')['default'] Crontab: typeof import('./../components/Crontab/src/Crontab.vue')['default'] Cropper: typeof import('./../components/Cropper/src/Cropper.vue')['default'] @@ -33,13 +32,27 @@ declare module 'vue' { Echart: typeof import('./../components/Echart/src/Echart.vue')['default'] Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] + ElAside: typeof import('element-plus/es')['ElAside'] + ElAutoResizer: typeof import('element-plus/es')['ElAutoResizer'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] + ElBadge: typeof import('element-plus/es')['ElBadge'] ElButton: typeof import('element-plus/es')['ElButton'] + ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup'] ElCard: typeof import('element-plus/es')['ElCard'] + ElCarousel: typeof import('element-plus/es')['ElCarousel'] + ElCarouselItem: typeof import('element-plus/es')['ElCarouselItem'] + ElCascader: typeof import('element-plus/es')['ElCascader'] ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] ElCol: typeof import('element-plus/es')['ElCol'] + ElCollapse: typeof import('element-plus/es')['ElCollapse'] + ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem'] + ElCollapseTransition: typeof import('element-plus/es')['ElCollapseTransition'] + ElColorPicker: typeof import('element-plus/es')['ElColorPicker'] + ElContainer: typeof import('element-plus/es')['ElContainer'] ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] + ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] + ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] ElDivider: typeof import('element-plus/es')['ElDivider'] ElDrawer: typeof import('element-plus/es')['ElDrawer'] @@ -55,27 +68,41 @@ declare module 'vue' { ElementTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/ElementTask.vue')['default'] ElForm: typeof import('element-plus/es')['ElForm'] ElFormItem: typeof import('element-plus/es')['ElFormItem'] + ElHeader: typeof import('element-plus/es')['ElHeader'] ElIcon: typeof import('element-plus/es')['ElIcon'] + ElImage: typeof import('element-plus/es')['ElImage'] ElImageViewer: typeof import('element-plus/es')['ElImageViewer'] ElInput: typeof import('element-plus/es')['ElInput'] ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] + ElLink: typeof import('element-plus/es')['ElLink'] + ElMain: typeof import('element-plus/es')['ElMain'] ElOption: typeof import('element-plus/es')['ElOption'] ElPagination: typeof import('element-plus/es')['ElPagination'] ElPopover: typeof import('element-plus/es')['ElPopover'] ElRadio: typeof import('element-plus/es')['ElRadio'] ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] + ElRate: typeof import('element-plus/es')['ElRate'] ElRow: typeof import('element-plus/es')['ElRow'] ElScrollbar: typeof import('element-plus/es')['ElScrollbar'] ElSelect: typeof import('element-plus/es')['ElSelect'] ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] + ElSlider: typeof import('element-plus/es')['ElSlider'] + ElSpace: typeof import('element-plus/es')['ElSpace'] ElSwitch: typeof import('element-plus/es')['ElSwitch'] ElTable: typeof import('element-plus/es')['ElTable'] ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] + ElTableV2: typeof import('element-plus/es')['ElTableV2'] ElTabPane: typeof import('element-plus/es')['ElTabPane'] ElTabs: typeof import('element-plus/es')['ElTabs'] ElTag: typeof import('element-plus/es')['ElTag'] + ElText: typeof import('element-plus/es')['ElText'] + ElTimeline: typeof import('element-plus/es')['ElTimeline'] + ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] + ElTimePicker: typeof import('element-plus/es')['ElTimePicker'] + ElTimeSelect: typeof import('element-plus/es')['ElTimeSelect'] ElTooltip: typeof import('element-plus/es')['ElTooltip'] + ElTransfer: typeof import('element-plus/es')['ElTransfer'] ElTree: typeof import('element-plus/es')['ElTree'] ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect'] ElUpload: typeof import('element-plus/es')['ElUpload'] @@ -96,9 +123,6 @@ declare module 'vue' { ProcessPalette: typeof import('./../components/bpmnProcessDesigner/package/palette/ProcessPalette.vue')['default'] ProcessViewer: typeof import('./../components/bpmnProcessDesigner/package/designer/ProcessViewer.vue')['default'] PropertiesPanel: typeof import('./../components/bpmnProcessDesigner/package/penal/PropertiesPanel.vue')['default'] - PurOrderSelectDialog: typeof import('./../views/biz/purreceipt copy/PurOrderSelectDialog.vue')['default'] - PurReceiptForm: typeof import('./../views/biz/purreceipt copy/PurReceiptForm.vue')['default'] - PurReceiptSelectDialog: typeof import('./../views/biz/purreturn/PurReceiptSelectDialog.vue')['default'] Qrcode: typeof import('./../components/Qrcode/src/Qrcode.vue')['default'] ReceiveTask: typeof import('./../components/bpmnProcessDesigner/package/penal/task/task-components/ReceiveTask.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] diff --git a/mes-ui/mes-ui-admin-vue3/src/types/auto-imports.d.ts b/mes-ui/mes-ui-admin-vue3/src/types/auto-imports.d.ts index f525c74..66aeaec 100644 --- a/mes-ui/mes-ui-admin-vue3/src/types/auto-imports.d.ts +++ b/mes-ui/mes-ui-admin-vue3/src/types/auto-imports.d.ts @@ -7,6 +7,8 @@ export {} declare global { const DICT_TYPE: typeof import('@/utils/dict')['DICT_TYPE'] const EffectScope: typeof import('vue')['EffectScope'] + const ElMessage: typeof import('element-plus/es')['ElMessage'] + const ElMessageBox: typeof import('element-plus/es')['ElMessageBox'] const computed: typeof import('vue')['computed'] const createApp: typeof import('vue')['createApp'] const customRef: typeof import('vue')['customRef'] diff --git a/mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue b/mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue new file mode 100644 index 0000000..1b1af66 --- /dev/null +++ b/mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue @@ -0,0 +1,795 @@ + + + + + diff --git a/mes-ui/mes-ui-admin-vue3/src/views/biz/millexecute/index.vue b/mes-ui/mes-ui-admin-vue3/src/views/biz/millexecute/index.vue index 025c831..c7a096d 100644 --- a/mes-ui/mes-ui-admin-vue3/src/views/biz/millexecute/index.vue +++ b/mes-ui/mes-ui-admin-vue3/src/views/biz/millexecute/index.vue @@ -121,7 +121,6 @@ - @@ -137,11 +136,11 @@ 自动采集 - + 确认 - + 取消 @@ -478,6 +477,12 @@ const canComplete = computed(() => { ) }) +// 产出确认:当前机台状态 = 执行中 才能确认,不需要选中子表行 +const canOutputConfirm = computed(() => { + return selectedRow.value && (selectedRow.value.machineProStatus === '执行中' + || selectedRow.value.machineProStatus === '2') +}) + // 单元格样式(执行中浅蓝底色) const getCellStyle = ({ row, column }) => { if (row.machineProStatus === '执行中' && column.property === 'machineProStatus') { @@ -592,6 +597,7 @@ const getPlanList = async () => { machineId: queryParams.machineId, proDate: defaultDate.value, shiftCd: defaultShiftCd.value, + lineId:queryParams.lineId }) // 查找列表中是否有执行中的计划,有则自动选中 @@ -873,6 +879,12 @@ const handleOutputConfirm = async () => { message.warning('请先选择生产计划') return } + // (0) 校验机台状态 = 执行中 + const status = selectedRow.value.machineProStatus + if (status !== '执行中' && status !== '2') { + message.warning('当前机台状态非执行中,不能进行产出确认!') + return + } // (1) 校验产线/机台 if (!queryParams.lineId || !queryParams.machineId) { message.warning('产线、机台不能为空,请确认!')