工序投料管理
This commit is contained in:
parent
203cc6e1bc
commit
37fff9c937
@ -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<MatFeedExecuteInitVO> init() {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
String userId = String.valueOf(loginUserId);
|
||||
|
||||
UserMachineDO userMachine = userMachineMapper.selectByUserId(userId);
|
||||
List<UserMachineDetailDO> detailList = Collections.emptyList();
|
||||
if (userMachine != null) {
|
||||
detailList = userMachineDetailMapper.selectListByUserMachId(userMachine.getId());
|
||||
if (detailList == null) {
|
||||
detailList = Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
List<MatFeedExecuteInitVO.LineOption> 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<MatFeedExecuteInitVO.MachineOption> 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<List<MatFeedExecutePlanVO>> 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<MatFeedExecutePlanVO> 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<HeadNoDO> headQuery = new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.eq(HeadNoDO::getProDate, date)
|
||||
.eq(HeadNoDO::getShiftCd, shiftCd)
|
||||
.eq(HeadNoDO::getMachineId, machineId);
|
||||
if (groupCd != null && !groupCd.isEmpty()) {
|
||||
headQuery.eq(HeadNoDO::getGroupCd, groupCd);
|
||||
}
|
||||
List<HeadNoDO> headNoList = headNoMapper.selectList(headQuery);
|
||||
if (headNoList.isEmpty()) {
|
||||
return success(result);
|
||||
}
|
||||
|
||||
// Map: planId -> HeadNoDO(用于获取 t3.pro_status 即机台计划状态)
|
||||
Map<Integer, HeadNoDO> headNoMap = new LinkedHashMap<>();
|
||||
List<Integer> 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<PlanDO> plans = planMapper.selectBatchIds(planIds);
|
||||
Map<Integer, PlanDO> 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<PlanLineDO> lineQuery = new LambdaQueryWrapperX<PlanLineDO>()
|
||||
.in(PlanLineDO::getProId, planIds);
|
||||
if (lineId != null) {
|
||||
lineQuery.eq(PlanLineDO::getLineId, lineId);
|
||||
}
|
||||
List<PlanLineDO> planLines = planLineMapper.selectList(lineQuery);
|
||||
// Map: planId -> PlanLineDO
|
||||
Map<Integer, PlanLineDO> 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<Integer> planLineIds = planLines.stream()
|
||||
.map(PlanLineDO::getId).collect(Collectors.toSet());
|
||||
Map<Integer, PlanMachineDO> planMachineByPlanIdMap = new HashMap<>();
|
||||
if (!planLineIds.isEmpty()) {
|
||||
List<PlanMachineDO> planMachines = planMachineMapper.selectList(
|
||||
new LambdaQueryWrapperX<PlanMachineDO>()
|
||||
.in(PlanMachineDO::getProOrderLineId, planLineIds)
|
||||
.eq(PlanMachineDO::getMachineId, machineId)
|
||||
);
|
||||
// 构建映射: planLineId -> PlanLineDO
|
||||
Map<Integer, PlanLineDO> 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<Integer, String> techProcMap = techProcMapper.selectList(
|
||||
new LambdaQueryWrapperX<com.ningxia.yunxi.chemmes.module.biz.dal.dataobject.techproc.TechProcDO>()
|
||||
).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<List<MatFeedExecuteMatConfigVO>> getMatConfig(
|
||||
@RequestParam(value = "planNo", required = false) String planNo,
|
||||
@RequestParam("proDate") String proDate,
|
||||
@RequestParam("shiftCd") String shiftCd,
|
||||
@RequestParam("machineId") Integer machineId) {
|
||||
|
||||
List<MatFeedExecuteMatConfigVO> result = new ArrayList<>();
|
||||
LocalDate date = LocalDate.parse(proDate);
|
||||
|
||||
// ===== Step 1: 查询当前正在执行的计划 =====
|
||||
List<HeadNoDO> executingHeads = headNoMapper.selectList(
|
||||
new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.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<HeadNoDO> matchedHeads = headNoMapper.selectList(
|
||||
new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.eq(HeadNoDO::getProDate, date)
|
||||
.eq(HeadNoDO::getShiftCd, shiftCd)
|
||||
.eq(HeadNoDO::getPlanNo, planNo)
|
||||
.eq(HeadNoDO::getMachineId, machineId)
|
||||
);
|
||||
|
||||
if (!matchedHeads.isEmpty()) {
|
||||
for (HeadNoDO headNo : matchedHeads) {
|
||||
List<MatFeedDO> feedList = matFeedMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDO>()
|
||||
.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<MachMatDO> machMats = machMatMapper.selectList(
|
||||
new LambdaQueryWrapperX<MachMatDO>()
|
||||
.eq(MachMatDO::getMachineId, machineId)
|
||||
.eq(MachMatDO::getEnabledStatus, 0)
|
||||
);
|
||||
|
||||
for (MachMatDO mm : machMats) {
|
||||
List<MachMatDetailDO> details = machMatDetailMapper.selectList(
|
||||
new LambdaQueryWrapperX<MachMatDetailDO>()
|
||||
.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<List<MatFeedExecuteFeedInfoVO>> getFeedInfo(
|
||||
@RequestParam(value = "planNo", required = false) String planNo,
|
||||
@RequestParam("proDate") String proDate,
|
||||
@RequestParam("shiftCd") String shiftCd,
|
||||
@RequestParam("machineId") Integer machineId) {
|
||||
|
||||
List<MatFeedExecuteFeedInfoVO> result = new ArrayList<>();
|
||||
LocalDate date = LocalDate.parse(proDate);
|
||||
|
||||
// 通过日期+班次+机台+planNo查询抬头单
|
||||
HeadNoDO headNo = headNoMapper.selectOne(
|
||||
new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.eq(HeadNoDO::getMachineId, machineId)
|
||||
.eq(HeadNoDO::getProDate, date)
|
||||
.eq(HeadNoDO::getShiftCd, shiftCd)
|
||||
.eqIfPresent(HeadNoDO::getPlanNo, planNo)
|
||||
);
|
||||
|
||||
if (headNo == null) {
|
||||
return success(result);
|
||||
}
|
||||
|
||||
// 查询投料主表
|
||||
List<MatFeedDO> feedList = matFeedMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDO>()
|
||||
.eq(MatFeedDO::getHeadId, headNo.getId())
|
||||
);
|
||||
|
||||
for (MatFeedDO feed : feedList) {
|
||||
// 查询投料明细
|
||||
List<MatFeedDetailDO> details = matFeedDetailMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.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<List<MatFeedExecuteStockVO>> getStockInfo(
|
||||
@RequestParam("machineId") Integer machineId,
|
||||
@RequestParam("proDate") String proDate,
|
||||
@RequestParam("shiftCd") String shiftCd,
|
||||
@RequestParam("planNo") String planNo) {
|
||||
|
||||
List<MatFeedExecuteStockVO> result = new ArrayList<>();
|
||||
LocalDate date = LocalDate.parse(proDate);
|
||||
|
||||
// 先获取当前物料配置列表(确认数量来源)
|
||||
List<MatFeedExecuteMatConfigVO> matConfigList = this.getMatConfig(planNo, proDate, shiftCd, machineId)
|
||||
.getData();
|
||||
if (matConfigList == null || matConfigList.isEmpty()) {
|
||||
return success(result);
|
||||
}
|
||||
|
||||
// 构建物料ID->确认数量的映射
|
||||
Map<Integer, BigDecimal> 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<Boolean> 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<HeadNoDO> executingHeads = headNoMapper.selectList(
|
||||
new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.eq(HeadNoDO::getMachineId, machineId)
|
||||
.eq(HeadNoDO::getProStatus, "2")
|
||||
);
|
||||
if (executingHeads == null || executingHeads.isEmpty()) {
|
||||
return CommonResult.error(400, "当前机台没有执行中的计划,请确认");
|
||||
}
|
||||
|
||||
// (6) 通过机台编码+日期+班次+计划号查询抬头单
|
||||
HeadNoDO headNo = headNoMapper.selectOne(
|
||||
new LambdaQueryWrapperX<HeadNoDO>()
|
||||
.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<MatFeedDO> existingFeeds = matFeedMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDO>()
|
||||
.eq(MatFeedDO::getHeadId, headNo.getId())
|
||||
);
|
||||
List<MatFeedDetailDO> existingDetails = Collections.emptyList();
|
||||
if (!existingFeeds.isEmpty()) {
|
||||
List<Integer> feedIds = existingFeeds.stream()
|
||||
.map(MatFeedDO::getId).collect(Collectors.toList());
|
||||
existingDetails = matFeedDetailMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.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<MatFeedDetailDO>()
|
||||
.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<Boolean> 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<HeadNoDO>()
|
||||
.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<MatFeedDetailDO> existingDetails = matFeedDetailMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.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%<tm%<td", LocalDate.now());
|
||||
String maxFeedNo = matFeedDetailMapper.selectMaxFeedNo();
|
||||
if (maxFeedNo == null || maxFeedNo.length() < 11
|
||||
|| !ym.equals(maxFeedNo.substring(1, 9))) {
|
||||
return "F" + ym + "01";
|
||||
}
|
||||
String prefix = maxFeedNo.substring(0, 9);
|
||||
int seqNum = Integer.parseInt(maxFeedNo.substring(maxFeedNo.length() - 2)) + 1;
|
||||
return prefix + String.format("%02d", seqNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成存货账单号
|
||||
*/
|
||||
private String generateInventBillNo() {
|
||||
// TODO: 根据实际业务实现
|
||||
return "CF" + String.format("%tY%<tm%<td", LocalDate.now()) + String.format("%02d",
|
||||
new Random().nextInt(99) + 1);
|
||||
}
|
||||
|
||||
// ==================== 8、取消(表3取消) ====================
|
||||
|
||||
@PutMapping("/cancel-feed")
|
||||
@Operation(summary = "投料信息取消")
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public CommonResult<Boolean> 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<HeadNoDO>()
|
||||
.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<MatFeedDO> feedList = matFeedMapper.selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDO>()
|
||||
.eq(MatFeedDO::getHeadId, headNo.getId())
|
||||
);
|
||||
for (MatFeedDO feed : feedList) {
|
||||
matFeedDetailMapper.delete(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.eq(MatFeedDetailDO::getFeedId, feed.getId())
|
||||
);
|
||||
matFeedMapper.deleteById(feed.getId());
|
||||
}
|
||||
|
||||
// 同时删除无feedId关联的detail记录(兜底)
|
||||
matFeedDetailMapper.delete(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<MatConfigItem> 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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<FeedItem> 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;
|
||||
}
|
||||
}
|
||||
@ -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<LineOption> lineList;
|
||||
|
||||
@Schema(description = "机台列表", example = "[]")
|
||||
private List<MachineOption> 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;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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<List<MillExecutePlanVO>> 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<Integer> statusPlanIds = planMapper.selectList(
|
||||
new com.ningxia.yunxi.chemmes.framework.mybatis.core.query.LambdaQueryWrapperX<PlanDO>()
|
||||
// 关联 tpl_plan_line 按 lineId 过滤
|
||||
List<Integer> statusPlanIds = new ArrayList<>();
|
||||
if (lineId != null) {
|
||||
// 先查询指定 lineId 的 tpl_plan_line 记录,获取 pro_id 列表
|
||||
List<Integer> linePlanIds = planLineMapper.selectList(
|
||||
new LambdaQueryWrapperX<PlanLineDO>()
|
||||
.eq(PlanLineDO::getLineId, lineId)
|
||||
.select(PlanLineDO::getProId)
|
||||
).stream().map(PlanLineDO::getProId).distinct().collect(Collectors.toList());
|
||||
|
||||
if (!linePlanIds.isEmpty()) {
|
||||
statusPlanIds = planMapper.selectList(
|
||||
new LambdaQueryWrapperX<PlanDO>()
|
||||
.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<Integer> 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%<tm%<td", LocalDate.now());
|
||||
String maxBillNo = proStorageInventoryMapper.selectMaxInventBillNo();
|
||||
String maxBillNo = rawStorageInventoryMapper.selectMaxBillNo();
|
||||
if (maxBillNo == null || maxBillNo.length() < 10
|
||||
|| !ym.equals(maxBillNo.substring(2, 10))) {
|
||||
return "CH" + ym + "01";
|
||||
@ -1480,9 +1499,9 @@ public class MillExecuteController {
|
||||
for (MatOutDetailDO detail : detailList) {
|
||||
if (detail.getInventBillNo() != null && !detail.getInventBillNo().isEmpty()) {
|
||||
// 查询存货台账
|
||||
ProStorageInventoryDO inventory = proStorageInventoryMapper.selectOne(
|
||||
new LambdaQueryWrapperX<ProStorageInventoryDO>()
|
||||
.eq(ProStorageInventoryDO::getInventBillNo, detail.getInventBillNo())
|
||||
RawStorageInventoryDO inventory = rawStorageInventoryMapper.selectOne(
|
||||
new LambdaQueryWrapperX<RawStorageInventoryDO>()
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
/**
|
||||
* 自增字段
|
||||
|
||||
@ -158,7 +158,10 @@ public class RawStorageLogDO extends BaseDOWithoutLogic {
|
||||
* 关联子单号id
|
||||
*/
|
||||
private Integer relarionDetailId;
|
||||
|
||||
private String inventBillNo;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -51,4 +51,16 @@ public interface MatFeedDetailMapper extends BaseMapperX<MatFeedDetailDO> {
|
||||
.orderByDesc(MatFeedDetailDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询最大的投料单号
|
||||
*/
|
||||
default String selectMaxFeedNo() {
|
||||
List<MatFeedDetailDO> list = selectList(
|
||||
new LambdaQueryWrapperX<MatFeedDetailDO>()
|
||||
.isNotNull(MatFeedDetailDO::getFeedNo)
|
||||
.orderByDesc(MatFeedDetailDO::getFeedNo)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
return (list != null && !list.isEmpty()) ? list.get(0).getFeedNo() : null;
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
|
||||
|
||||
249
mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts
Normal file
249
mes-ui/mes-ui-admin-vue3/src/api/biz/matfeedexecute/index.ts
Normal file
@ -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<MatFeedExecuteInitVO> => {
|
||||
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<MatFeedExecutePlanVO[]> => {
|
||||
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<MatFeedExecuteMatConfigVO[]> => {
|
||||
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<MatFeedExecuteFeedInfoVO[]> => {
|
||||
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<MatFeedExecuteStockVO[]> => {
|
||||
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<void> => {
|
||||
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<void> => {
|
||||
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<void> => {
|
||||
return await request.put({ url: '/tpo/mat-feed-execute/cancel-feed', data })
|
||||
}
|
||||
@ -59,6 +59,7 @@ export interface MillExecutePlanVO {
|
||||
// 生产计划查询
|
||||
export const getPlanList = async (params: {
|
||||
machineId?: number
|
||||
lineId?: number
|
||||
proDate?: string
|
||||
shiftCd?: string
|
||||
groupCd?: string
|
||||
|
||||
@ -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']
|
||||
|
||||
@ -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']
|
||||
|
||||
795
mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue
Normal file
795
mes-ui/mes-ui-admin-vue3/src/views/biz/matfeedexecute/index.vue
Normal file
@ -0,0 +1,795 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start;">
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item label="产线" prop="lineId">
|
||||
<el-select
|
||||
v-model="queryParams.lineId"
|
||||
placeholder="请选择产线"
|
||||
clearable
|
||||
class="!w-180px"
|
||||
@change="handleLineChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in lineOptions"
|
||||
:key="item.lineId"
|
||||
:label="item.lineName"
|
||||
:value="item.lineId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="机台" prop="machineId">
|
||||
<el-select
|
||||
v-model="queryParams.machineId"
|
||||
placeholder="请选择机台"
|
||||
clearable
|
||||
class="!w-180px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in filteredMachineOptions"
|
||||
:key="item.machineId"
|
||||
:label="item.machineName"
|
||||
:value="item.machineId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 右上角:日期、班次、批次更新 -->
|
||||
<div style="display: flex; align-items: center; gap: 14px; white-space: nowrap; flex-shrink: 0; padding-top: 4px;">
|
||||
<div>
|
||||
<span style="font-weight: bold; color: #409EFF;">{{ defaultDate || '-' }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-weight: bold; color: #409EFF;">
|
||||
<dict-tag v-if="defaultShiftCd" :type="DICT_TYPE.SHIFT_SCHEDULE" :value="defaultShiftCd"/>
|
||||
<span v-else>-</span>
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<span style="font-weight: bold; color: #409EFF;">{{ defaultTeamName || '-' }}</span>
|
||||
</div>
|
||||
<el-button type="warning" size="small" @click="openShiftDialog">
|
||||
<Icon icon="ep:clock" class="mr-5px"/>
|
||||
班次更新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表1 - 生产计划 -->
|
||||
<ContentWrap>
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 10px;">
|
||||
<span style="font-weight: bold; font-size: 14px;">生产计划</span>
|
||||
<div>
|
||||
<el-button type="primary" @click="handleQuery">
|
||||
<Icon icon="ep:search" class="mr-5px"/>
|
||||
查询
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table
|
||||
ref="planTableRef"
|
||||
v-loading="loading"
|
||||
:data="planList"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
:row-class-name="getRowClass"
|
||||
border
|
||||
height="300px"
|
||||
@row-click="handleRowClick"
|
||||
:cell-style="getCellStyle"
|
||||
>
|
||||
<el-table-column label="序号" align="center" type="index" width="60" fixed="left"/>
|
||||
<el-table-column label="机台计划状态" align="center" prop="machineProStatus" width="150"/>
|
||||
<el-table-column label="生产计划号" align="center" prop="planNo" width="150" />
|
||||
<el-table-column label="计划状态" align="center" prop="planStatus" width="100"/>
|
||||
<!-- <template #default="scope">-->
|
||||
<!-- <dict-tag :type="DICT_TYPE.PLAN_STATUS" :value="scope.row.planStatus"/>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column label="产品名称" align="center" prop="materialName" width="180" />
|
||||
<el-table-column label="规格型号" align="center" prop="spec" width="120"/>
|
||||
<el-table-column label="计划数量" align="center" prop="planQty" width="120"/>
|
||||
<el-table-column label="完成数量" align="center" prop="completeQty" width="140"/>
|
||||
<el-table-column label="计划开始日期" align="center" prop="planBgDate" width="150" :formatter="dateFormatter2"/>
|
||||
<el-table-column label="计划结束日期" align="center" prop="planEndDate" width="150" :formatter="dateFormatter2"/>
|
||||
<el-table-column label="工艺流程" align="center" prop="techProc" width="200" />
|
||||
<el-table-column label="计划日期" align="center" prop="proDate" width="150" :formatter="dateFormatter2"/>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表2 + 表3 -->
|
||||
<ContentWrap>
|
||||
<el-row :gutter="20">
|
||||
<!-- 表2 - 物料配置(左侧) -->
|
||||
<el-col :span="12">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||
<span style="font-weight: bold; font-size: 14px; margin-right: 15px;">物料配置</span>
|
||||
<el-button type="primary" size="small" @click="handleAutoCollect">
|
||||
<Icon icon="ep:collection" class="mr-5px"/>
|
||||
自动采集
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" :disabled="!canMatConfigConfirm" @click="handleMatConfigConfirm">
|
||||
<Icon icon="ep:check" class="mr-5px"/>
|
||||
确认
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="matConfigLoading"
|
||||
:data="matConfigList"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
border
|
||||
highlight-current-row
|
||||
height="280px"
|
||||
>
|
||||
<el-table-column label="序号" align="center" type="index" width="60"/>
|
||||
<el-table-column label="物料名称" align="center" prop="materialName" width="140"/>
|
||||
<el-table-column label="物料类型" align="center" prop="matType" width="90"/>
|
||||
<el-table-column label="采集数量" align="center" prop="atoQty" width="100"/>
|
||||
<el-table-column prop="confirmQty" align="center" width="150">
|
||||
<template #header>
|
||||
<span>确认数量(<span style="color: red">*</span>)</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="scope.row.confirmQty"
|
||||
type="text"
|
||||
inputmode="decimal"
|
||||
min="0"
|
||||
oninput="this.value=this.value.replace(/[^0-9.]/g,'').replace(/\.{2,}/g,'.').replace(/^(\d+)\.(\d{2}).*$/,'$1.$2')"
|
||||
placeholder="请输入确认数量"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规格" align="center" prop="spec" width="120"/>
|
||||
<el-table-column label="物料编码" align="center" prop="materialCode" width="140"/>
|
||||
<el-table-column label="单位" align="center" prop="unit" width="80">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.MAT_UNIT" :value="scope.row.unit" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
|
||||
<!-- 表3 - 投料信息(右侧) -->
|
||||
<el-col :span="12">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 10px;">
|
||||
<span style="font-weight: bold; font-size: 14px; margin-right: 15px;">投料信息</span>
|
||||
<el-button type="primary" size="small" :disabled="!canConsumeStock" @click="openStockDialogForConsume">
|
||||
<Icon icon="ep:box" class="mr-5px"/>
|
||||
库存消耗
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" :disabled="!selectedRow" @click="handleCancelFeed">
|
||||
<Icon icon="ep:close" class="mr-5px"/>
|
||||
取消
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="feedInfoLoading"
|
||||
:data="feedInfoList"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
border
|
||||
height="280px"
|
||||
>
|
||||
<el-table-column label="序号" align="center" type="index" width="60"/>
|
||||
<el-table-column label="物料名称" align="center" prop="materialName" width="120"/>
|
||||
<el-table-column label="物料类型" align="center" prop="matType" width="90"/>
|
||||
<el-table-column label="确认数量" align="center" prop="confirmQty" width="100"/>
|
||||
<el-table-column label="投料数量" align="center" prop="feedQty" width="100"/>
|
||||
<el-table-column label="批次号" align="center" prop="lotNo" width="150"/>
|
||||
<el-table-column label="规格" align="center" prop="spec" width="100"/>
|
||||
<el-table-column label="仓库" align="center" prop="storeHouseName" width="100"/>
|
||||
<el-table-column label="库区" align="center" prop="storeAreaName" width="100"/>
|
||||
<el-table-column label="物料编码" align="center" prop="materialCode" width="130"/>
|
||||
<el-table-column label="计划号" align="center" prop="planNo" width="140"/>
|
||||
<el-table-column label="投料人" align="center" prop="feedEmpName" width="90"/>
|
||||
<el-table-column label="投料时间" align="center" prop="feedTime" width="160"/>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 批次更新弹窗 -->
|
||||
<el-dialog v-model="shiftDialogVisible" title="批次更新" width="400px" :close-on-click-modal="false">
|
||||
<el-form :model="shiftForm" :rules="shiftFormRules" label-width="100px" ref="shiftFormRef">
|
||||
<el-form-item label="生产日期" prop="proDate">
|
||||
<el-date-picker
|
||||
v-model="shiftForm.proDate"
|
||||
value-format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="选择生产日期"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="班次" prop="shiftCd">
|
||||
<el-select v-model="shiftForm.shiftCd" placeholder="请选择班次" class="!w-240px" clearable>
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.SHIFT_SCHEDULE)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="班组" prop="teamName">
|
||||
<el-select v-model="shiftForm.teamName" placeholder="请选择班组" class="!w-240px" clearable>
|
||||
<el-option
|
||||
v-for="dict in getStrDictOptions(DICT_TYPE.TEAM_OR_GROUP)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="shiftDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSaveShift">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 库存信息弹窗(表2确认 / 表3库存消耗共用) -->
|
||||
<el-dialog v-model="stockDialogVisible" :title="stockDialogTitle" width="1100px" :close-on-click-modal="false">
|
||||
<!-- 弹窗上半部分:物料配置(只读展示) -->
|
||||
<div style="margin-bottom: 16px;">
|
||||
<span style="font-weight: bold; font-size: 14px;">物料配置</span>
|
||||
</div>
|
||||
<el-table
|
||||
:data="stockMatConfigList"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
border
|
||||
max-height="200px"
|
||||
size="small"
|
||||
>
|
||||
<el-table-column label="序号" align="center" type="index" width="55"/>
|
||||
<el-table-column label="物料名称" align="center" prop="materialName" width="120"/>
|
||||
<el-table-column label="物料类型" align="center" prop="matType" width="85"/>
|
||||
<el-table-column label="采集数量" align="center" prop="atoQty" width="90"/>
|
||||
<el-table-column label="确认数量(*)" align="center" prop="confirmQty" width="100"/>
|
||||
<el-table-column label="规格" align="center" prop="spec" width="100"/>
|
||||
<el-table-column label="物料编码" align="center" prop="materialCode" width="130"/>
|
||||
<el-table-column label="单位" align="center" prop="unit" width="70"/>
|
||||
</el-table>
|
||||
|
||||
<!-- 弹窗下半部分:库存信息 -->
|
||||
<div style="margin-top: 16px; margin-bottom: 12px;">
|
||||
<span style="font-weight: bold; font-size: 14px;">库存信息</span>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="stockDialogLoading"
|
||||
:data="stockInfoList"
|
||||
:stripe="true"
|
||||
:show-overflow-tooltip="true"
|
||||
border
|
||||
height="280px"
|
||||
>
|
||||
<el-table-column label="序号" align="center" prop="rowNum" width="55"/>
|
||||
<el-table-column label="物料名称" align="center" prop="materialName" width="110"/>
|
||||
<el-table-column label="物料类型" align="center" prop="matType" width="85"/>
|
||||
<el-table-column label="规格" align="center" prop="spec" width="100"/>
|
||||
<el-table-column label="批次号" align="center" prop="lotNo" width="130"/>
|
||||
<el-table-column label="库存数量" align="center" prop="stockQty" width="95"/>
|
||||
<el-table-column prop="feedQty" align="center" width="130">
|
||||
<template #header>
|
||||
<span>投料数量(<span style="color: red">*</span>)</span>
|
||||
</template>
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="scope.row.feedQty"
|
||||
type="text"
|
||||
inputmode="decimal"
|
||||
min="0"
|
||||
oninput="this.value=this.value.replace(/[^0-9.]/g,'').replace(/\.{2,}/g,'.').replace(/^(\d+)\.(\d{2}).*$/,'$1.$2')"
|
||||
placeholder=""
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="仓储" align="center" prop="storeHouseName" width="90"/>
|
||||
<el-table-column label="库区" align="center" prop="storeAreaName" width="90"/>
|
||||
<el-table-column label="物料编码" align="center" prop="materialCode" width="130"/>
|
||||
<el-table-column label="最早入库日期" align="center" prop="earliestInDate" width="130" :formatter="dateFormatter2"/>
|
||||
<el-table-column label="存货账单号" align="center" prop="inventBillNo" width="140"/>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="handleStockConfirm">确认</el-button>
|
||||
<el-button @click="stockDialogVisible = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="tsx">
|
||||
import * as MatFeedExecuteApi from '@/api/biz/matfeedexecute'
|
||||
import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter2, formatDate } from '@/utils/formatTime'
|
||||
|
||||
const message = useMessage()
|
||||
defineOptions({ name: 'MatFeedExecute' })
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false)
|
||||
const matConfigLoading = ref(false)
|
||||
const feedInfoLoading = ref(false)
|
||||
const stockDialogLoading = ref(false)
|
||||
|
||||
// 列表数据
|
||||
const planList = ref<MatFeedExecuteApi.MatFeedExecutePlanVO[]>([])
|
||||
const matConfigList = ref<MatFeedExecuteApi.MatFeedExecuteMatConfigVO[]>([])
|
||||
const feedInfoList = ref<MatFeedExecuteApi.MatFeedExecuteFeedInfoVO[]>([])
|
||||
|
||||
// 下拉选项
|
||||
const lineOptions = ref<MatFeedExecuteApi.LineOption[]>([])
|
||||
const machineOptions = ref<MatFeedExecuteApi.MachineOption[]>([])
|
||||
|
||||
// 默认值
|
||||
const defaultDate = ref('')
|
||||
const defaultShiftCd = ref('')
|
||||
const defaultTeamName = ref('')
|
||||
|
||||
// 选中行
|
||||
const selectedRow = ref<MatFeedExecuteApi.MatFeedExecutePlanVO | null>(null)
|
||||
const planTableRef = ref()
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
lineId: undefined as number | undefined,
|
||||
machineId: undefined as number | undefined
|
||||
})
|
||||
|
||||
// 根据产线过滤机台
|
||||
const filteredMachineOptions = computed(() => {
|
||||
if (!queryParams.lineId) return []
|
||||
return machineOptions.value.filter(item => item.lineId === queryParams.lineId)
|
||||
})
|
||||
|
||||
// 是否可以确认物料配置
|
||||
const canMatConfigConfirm = computed(() => {
|
||||
return selectedRow.value
|
||||
&& (selectedRow.value.isExecuting
|
||||
|| selectedRow.value.machineProStatus === '执行中'
|
||||
|| selectedRow.value.machineProStatus === '2')
|
||||
})
|
||||
|
||||
// 是否可以库存消耗
|
||||
const canConsumeStock = computed(() => {
|
||||
return selectedRow.value
|
||||
&& (selectedRow.value.isExecuting
|
||||
|| selectedRow.value.machineProStatus === '执行中'
|
||||
|| selectedRow.value.machineProStatus === '2')
|
||||
})
|
||||
|
||||
// 单元格样式 - 正在执行的计划底色浅蓝色
|
||||
const getCellStyle = ({ row }) => {
|
||||
if (row.isExecuting) {
|
||||
return { backgroundColor: '#e6f7ff' }
|
||||
}
|
||||
return {}
|
||||
}
|
||||
/** 通过 row-class-name 响应式控制选中行高亮(替代 highlight-current-row) */
|
||||
const getRowClass = ({ row }: { row: MatFeedExecuteApi.MatFeedExecutePlanVO }) => {
|
||||
if (selectedRow.value && selectedRow.value.planNo === row.planNo) {
|
||||
return 'current-row'
|
||||
}
|
||||
return ''
|
||||
}
|
||||
/** 查询生产计划列表 */
|
||||
const getPlanList = async () => {
|
||||
if (!queryParams.machineId || !queryParams.lineId) {
|
||||
message.warning('产线、机台不能为空,请确认')
|
||||
return
|
||||
}
|
||||
if (!defaultDate.value) {
|
||||
message.warning('生产日期不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
if (!defaultShiftCd.value) {
|
||||
message.warning('班次不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
try {
|
||||
planList.value = await MatFeedExecuteApi.getPlanList({
|
||||
machineId: queryParams.machineId,
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value,
|
||||
lineId: queryParams.lineId
|
||||
})
|
||||
|
||||
// 查找是否有执行中的计划并自动选中
|
||||
const executingPlan = planList.value.find(item => item.isExecuting)
|
||||
if (executingPlan) {
|
||||
handleRowClick(executingPlan!)
|
||||
} else {
|
||||
selectedRow.value = null
|
||||
matConfigList.value = []
|
||||
feedInfoList.value = []
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 产线变化 */
|
||||
const handleLineChange = () => {
|
||||
queryParams.machineId = undefined
|
||||
}
|
||||
|
||||
/** 点击生产计划行 */
|
||||
const handleRowClick = (row: MatFeedExecuteApi.MatFeedExecutePlanVO) => {
|
||||
selectedRow.value = row
|
||||
matConfigList.value = []
|
||||
feedInfoList.value = []
|
||||
if (!queryParams.machineId) return
|
||||
loadMatConfig(row)
|
||||
loadFeedInfo(row)
|
||||
}
|
||||
|
||||
/** 刷新主表并选中当前计划 */
|
||||
const refreshWithCurrentPlan = async () => {
|
||||
const planNo = selectedRow.value?.planNo
|
||||
await getPlanList()
|
||||
if (planNo) {
|
||||
const found = planList.value.find(item => item.planNo === planNo)
|
||||
if (found) handleRowClick(found)
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载物料配置(表2) */
|
||||
const loadMatConfig = async (row: MatFeedExecuteApi.MatFeedExecutePlanVO) => {
|
||||
if (!queryParams.machineId) return
|
||||
matConfigLoading.value = true
|
||||
try {
|
||||
matConfigList.value = await MatFeedExecuteApi.getMatConfig({
|
||||
machineId: queryParams.machineId,
|
||||
planNo: row.planNo || '',
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value
|
||||
})
|
||||
} catch {
|
||||
matConfigList.value = []
|
||||
} finally {
|
||||
matConfigLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载投料信息(表3) */
|
||||
const loadFeedInfo = async (row: MatFeedExecuteApi.MatFeedExecutePlanVO) => {
|
||||
if (!queryParams.machineId) return
|
||||
feedInfoLoading.value = true
|
||||
try {
|
||||
feedInfoList.value = await MatFeedExecuteApi.getFeedInfo({
|
||||
machineId: queryParams.machineId,
|
||||
planNo: row.planNo || '',
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value
|
||||
})
|
||||
} catch {
|
||||
feedInfoList.value = []
|
||||
} finally {
|
||||
feedInfoLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 查询按钮 */
|
||||
const handleQuery = () => getPlanList()
|
||||
|
||||
/** 自动采集 */
|
||||
const handleAutoCollect = async () => {
|
||||
if (!queryParams.lineId || !queryParams.machineId) {
|
||||
message.warning('产线、机台不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
if (!defaultDate.value || !defaultShiftCd.value) {
|
||||
message.warning('生产日期或班次不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
if (!selectedRow.value) {
|
||||
message.warning('请先选择生产计划!')
|
||||
return
|
||||
}
|
||||
// 重新加载物料配置(触发后端 getMatConfig 重新查询)
|
||||
await loadMatConfig(selectedRow.value)
|
||||
message.success('物料配置已刷新')
|
||||
}
|
||||
|
||||
// ================== 弹窗相关 ==================
|
||||
const stockDialogVisible = ref(false)
|
||||
const stockDialogTitle = ref('')
|
||||
const stockDialogMode = ref<'confirm' | 'consume'>('confirm') // confirm=物料确认, consume=库存消耗
|
||||
const stockMatConfigList = ref<any[]>([]) // 弹窗上半部分:物料配置(只读)
|
||||
const stockInfoList = ref<MatFeedExecuteApi.MatFeedExecuteStockVO[]>([]) // 弹窗下半部分:库存信息
|
||||
|
||||
/** 打开弹窗 - 物料配置确认模式 */
|
||||
const openStockDialog = () => {
|
||||
if (!selectedRow.value) {
|
||||
message.warning('请先选择生产计划')
|
||||
return
|
||||
}
|
||||
if (!canMatConfigConfirm.value) {
|
||||
message.warning('当前机台状态非执行中,不能进行物料确认!')
|
||||
return
|
||||
}
|
||||
|
||||
stockDialogMode.value = 'confirm'
|
||||
stockDialogTitle.value = '物料配置确认'
|
||||
|
||||
// 上半部分:复制当前表2数据作为只读展示
|
||||
stockMatConfigList.value = JSON.parse(JSON.stringify(matConfigList.value))
|
||||
|
||||
// 下半部分:加载库存信息
|
||||
loadStockData()
|
||||
|
||||
stockDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 打开弹窗 - 库存消耗模式 */
|
||||
const openStockDialogForConsume = () => {
|
||||
if (!selectedRow.value) {
|
||||
message.warning('请先选择生产计划')
|
||||
return
|
||||
}
|
||||
if (!canConsumeStock.value) {
|
||||
message.warning('当前机台状态非执行中,不能进行库存消耗!')
|
||||
return
|
||||
}
|
||||
|
||||
stockDialogMode.value = 'consume'
|
||||
stockDialogTitle.value = '库存消耗'
|
||||
|
||||
// 上半部分:复制当前表2数据
|
||||
stockMatConfigList.value = JSON.parse(JSON.stringify(matConfigList.value))
|
||||
|
||||
// 下半部分:加载库存信息
|
||||
loadStockData()
|
||||
|
||||
stockDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 加载弹窗中的库存信息 */
|
||||
const loadStockData = async () => {
|
||||
if (!selectedRow.value || !queryParams.machineId) return
|
||||
|
||||
stockDialogLoading.value = true
|
||||
try {
|
||||
const data = await MatFeedExecuteApi.getStockInfo({
|
||||
machineId: queryParams.machineId,
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value,
|
||||
planNo: selectedRow.value.planNo || ''
|
||||
})
|
||||
|
||||
// 如果是确认模式,feedQty默认填入confirmQty;如果是消耗模式也填入
|
||||
const list = data.map((item: any, idx: number) => ({
|
||||
...item,
|
||||
rowNum: idx + 1,
|
||||
feedQty: item.confirmQty != null ? String(item.confirmQty) : ''
|
||||
}))
|
||||
stockInfoList.value = list
|
||||
} catch {
|
||||
stockInfoList.value = []
|
||||
} finally {
|
||||
stockDialogLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 弹窗确认按钮 */
|
||||
const handleStockConfirm = async () => {
|
||||
if (stockDialogMode.value === 'confirm') {
|
||||
await handleMatConfigConfirm()
|
||||
} else {
|
||||
await handleConsumeStock()
|
||||
}
|
||||
}
|
||||
|
||||
/** 物料配置确认提交 */
|
||||
const handleMatConfigConfirm = async () => {
|
||||
// (3) 校验表2信息不为空
|
||||
if (!matConfigList.value || matConfigList.value.length === 0) {
|
||||
message.warning('物料配置信息列表为空,请查询数据!')
|
||||
return
|
||||
}
|
||||
// (4) 循环管控:确认数量不能为空或0
|
||||
for (const item of matConfigList.value) {
|
||||
const qty = Number(item.confirmQty)
|
||||
if (item.confirmQty == null || item.confirmQty === '' || isNaN(qty) || qty <= 0) {
|
||||
message.warning(`该物料(${item.materialName})投料数量等于0,请确认!`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// (5) 校验机台计划状态存在'执行中'
|
||||
if (!selectedRow.value || !selectedRow.value.isExecuting) {
|
||||
message.warning('当前机台没有执行中的计划,请确认')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await message.confirm('确定要确认当前物料配置吗?')
|
||||
|
||||
// 构建请求参数
|
||||
const matConfigItems = matConfigList.value.map(item => ({
|
||||
id: item.id,
|
||||
materialId: item.materialId,
|
||||
materialName: item.materialName,
|
||||
materialCode: item.materialCode,
|
||||
spec: item.spec,
|
||||
unit: item.unit,
|
||||
confirmQty: item.confirmQty,
|
||||
atoQty: item.atoQty,
|
||||
dataSource: item.dataSource,
|
||||
matTypeRaw: item.matTypeRaw
|
||||
}))
|
||||
|
||||
await MatFeedExecuteApi.confirmMatConfig({
|
||||
lineId: queryParams.lineId!,
|
||||
lineName: selectedRow.value!.lineName,
|
||||
lineCd: selectedRow.value!.lineCd || '',
|
||||
machineId: queryParams.machineId!,
|
||||
machineName: selectedRow.value!.machineName,
|
||||
machineCd: selectedRow.value!.machineCd || '',
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value,
|
||||
groupCd: defaultTeamName.value,
|
||||
planNo: selectedRow.value!.planNo,
|
||||
planId: selectedRow.value!.id,
|
||||
matConfigItems
|
||||
})
|
||||
message.success('物料配置确认成功')
|
||||
stockDialogVisible.value = false
|
||||
await refreshWithCurrentPlan()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 库存消耗提交 */
|
||||
const handleConsumeStock = async () => {
|
||||
// 校验投料数量
|
||||
for (const item of stockInfoList.value) {
|
||||
const qty = Number(item.feedQty)
|
||||
if (item.feedQty == null || item.feedQty === '' || isNaN(qty) || qty <= 0) {
|
||||
message.warning(`该物料(${item.materialName})投料数量等于0或为空,请确认!`)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await message.confirm('确定要进行库存消耗操作吗?')
|
||||
|
||||
const feedItems = stockInfoList.value.map(item => ({
|
||||
id: item.id,
|
||||
materialId: item.materialId,
|
||||
materialName: item.materialName,
|
||||
matType: item.matTypeRaw || item.matType,
|
||||
spec: item.spec,
|
||||
lotNo: item.lotNo,
|
||||
feedQty: item.feedQty,
|
||||
storeHouseId: item.storeHouseId,
|
||||
storeHouseName: item.storeHouseName,
|
||||
storeAreaId: item.storeAreaId,
|
||||
storeAreaName: item.storeAreaName,
|
||||
materialCode: item.materialCode,
|
||||
inventBillNo: item.inventBillNo,
|
||||
confirmQty: item.confirmQty
|
||||
}))
|
||||
|
||||
await MatFeedExecuteApi.consumeStock({
|
||||
lineId: queryParams.lineId!,
|
||||
lineName: selectedRow.value!.lineName,
|
||||
lineCd: '',
|
||||
machineId: queryParams.machineId!,
|
||||
machineName: selectedRow.value!.machineName,
|
||||
machineCd: selectedRow.value!.machineCd,
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value,
|
||||
groupCd: defaultTeamName.value,
|
||||
planNo: selectedRow.value!.planNo,
|
||||
planId: selectedRow.value!.id,
|
||||
feedItems
|
||||
})
|
||||
message.success('库存消耗成功')
|
||||
stockDialogVisible.value = false
|
||||
await refreshWithCurrentPlan()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 取消投料 */
|
||||
const handleCancelFeed = async () => {
|
||||
if (!selectedRow.value) {
|
||||
message.warning('请先选择生产计划')
|
||||
return
|
||||
}
|
||||
if (!queryParams.lineId || !queryParams.machineId) {
|
||||
message.warning('产线、机台不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
if (!defaultDate.value || !defaultShiftCd.value) {
|
||||
message.warning('生产日期或班次不能为空,请确认!')
|
||||
return
|
||||
}
|
||||
if (!selectedRow.value.planNo) {
|
||||
message.warning('选择执行计划为空,请选择!')
|
||||
return
|
||||
}
|
||||
try {
|
||||
await message.confirm('确定要取消当前投料信息吗?')
|
||||
await MatFeedExecuteApi.cancelFeed({
|
||||
lineId: queryParams.lineId!,
|
||||
lineName: selectedRow.value.lineName,
|
||||
lineCd: '',
|
||||
machineId: queryParams.machineId!,
|
||||
machineName: selectedRow.value.machineName,
|
||||
machineCd: selectedRow.value.machineCd,
|
||||
proDate: defaultDate.value,
|
||||
shiftCd: defaultShiftCd.value,
|
||||
groupCd: defaultTeamName.value,
|
||||
planNo: selectedRow.value.planNo,
|
||||
planId: selectedRow.value.id
|
||||
})
|
||||
message.success('投料取消成功')
|
||||
await refreshWithCurrentPlan()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// ================== 批次更新弹窗 ==================
|
||||
const shiftDialogVisible = ref(false)
|
||||
const shiftFormRef = ref()
|
||||
const shiftForm = reactive({ proDate: '', shiftCd: '', teamName: '' })
|
||||
const shiftFormRules = {
|
||||
proDate: [{ required: true, message: '生产日期不能为空', trigger: 'change' }],
|
||||
shiftCd: [{ required: true, message: '班次不能为空', trigger: 'change' }],
|
||||
teamName: [{ required: true, message: '班组不能为空', trigger: 'change' }]
|
||||
}
|
||||
|
||||
const openShiftDialog = () => {
|
||||
shiftForm.proDate = defaultDate.value || formatDate(new Date(), 'YYYY-MM-DD')
|
||||
shiftForm.shiftCd = defaultShiftCd.value || ''
|
||||
shiftForm.teamName = defaultTeamName.value || ''
|
||||
shiftDialogVisible.value = true
|
||||
nextTick(() => { shiftFormRef.value?.clearValidate() })
|
||||
}
|
||||
|
||||
const handleSaveShift = () => {
|
||||
if (!shiftForm.proDate) { message.warning('生产日期不能为空'); return }
|
||||
if (!shiftForm.shiftCd) { message.warning('班次不能为空'); return }
|
||||
if (!shiftForm.teamName) { message.warning('班组不能为空'); return }
|
||||
defaultDate.value = shiftForm.proDate
|
||||
defaultShiftCd.value = shiftForm.shiftCd
|
||||
defaultTeamName.value = shiftForm.teamName
|
||||
message.success('保存成功')
|
||||
shiftDialogVisible.value = false
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const initData = await MatFeedExecuteApi.getMatFeedExecuteInit()
|
||||
lineOptions.value = initData.lineList || []
|
||||
machineOptions.value = initData.machineList || []
|
||||
|
||||
if (lineOptions.value.length === 1) queryParams.lineId = lineOptions.value[0].lineId
|
||||
if (machineOptions.value.length === 1) queryParams.machineId = machineOptions.value[0].machineId
|
||||
|
||||
if (initData.defaultDate) defaultDate.value = formatDate(new Date(initData.defaultDate), 'YYYY-MM-DD')
|
||||
defaultShiftCd.value = initData.shiftCd || ''
|
||||
defaultTeamName.value = initData.groupCd || ''
|
||||
|
||||
if (queryParams.machineId && defaultDate.value && defaultShiftCd.value) {
|
||||
await getPlanList()
|
||||
}
|
||||
} catch {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@ -121,7 +121,6 @@
|
||||
<el-table-column label="计划开始日期" align="center" prop="planBgDate" width="150" :formatter="dateFormatter2"/>
|
||||
<el-table-column label="计划结束日期" align="center" prop="planEndDate" width="150" :formatter="dateFormatter2"/>
|
||||
<el-table-column label="工艺流程名称" align="center" prop="techProc" width="200" />
|
||||
|
||||
<el-table-column label="计划日期" align="center" prop="proDate" width="150" :formatter="dateFormatter2"/>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
@ -137,11 +136,11 @@
|
||||
<Icon icon="ep:collection" class="mr-5px"/>
|
||||
自动采集
|
||||
</el-button>
|
||||
<el-button type="primary" size="small" :disabled="!selectedOutputRow" @click="handleOutputConfirm">
|
||||
<el-button type="primary" size="small" :disabled="!canOutputConfirm" @click="handleOutputConfirm">
|
||||
<Icon icon="ep:check" class="mr-5px"/>
|
||||
确认
|
||||
</el-button>
|
||||
<el-button type="danger" size="small" :disabled="!selectedOutputRow" @click="handleOutputCancel">
|
||||
<el-button type="danger" size="small" @click="handleOutputCancel">
|
||||
<Icon icon="ep:close" class="mr-5px"/>
|
||||
取消
|
||||
</el-button>
|
||||
@ -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('产线、机台不能为空,请确认!')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user