| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.packages; |
| | | |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.line.MesWmPackageLinePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.packages.vo.line.MesWmPackageLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.process.MesProProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderProcessDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.packages.MesWmPackageLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.packages.MesWmPackageLineMapper; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemService; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PACKAGE_LINE_NOT_EXISTS; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * MES 装箱明细 Service 实现类 |
| | |
| | | @Validated |
| | | public class MesWmPackageLineServiceImpl implements MesWmPackageLineService { |
| | | |
| | | private static final String ASSEMBLY_PROCESS_CODE = "PRO-ASSEMBLY"; |
| | | private static final String ASSEMBLY_PROCESS_NAME = "组装"; |
| | | |
| | | @Resource |
| | | private MesWmPackageLineMapper packageLineMapper; |
| | | |
| | |
| | | @Resource |
| | | private MesMdmItemService mdmItemService; |
| | | @Resource |
| | | private MesMdItemService mdItemService; |
| | | @Resource |
| | | private MesMdItemTypeService itemTypeService; |
| | | @Resource |
| | | private MesProWorkOrderService workOrderService; |
| | | @Resource |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @Resource |
| | | private MesProProcessService processService; |
| | | @Resource |
| | | private MesProFeedbackMapper feedbackMapper; |
| | | |
| | | @Override |
| | | public Long createPackageLine(MesWmPackageLineSaveReqVO createReqVO) { |
| | | // 校验数据 |
| | | validateLineSaveData(createReqVO); |
| | | int boxCount = validateLineSaveData(createReqVO); |
| | | |
| | | // 插入 |
| | | MesWmPackageLineDO line = BeanUtils.toBean(createReqVO, MesWmPackageLineDO.class); |
| | | line.setBoxCount(boxCount); |
| | | packageLineMapper.insert(line); |
| | | return line.getId(); |
| | | } |
| | |
| | | MesWmPackageLineDO line = validatePackageLineExists(updateReqVO.getId()); |
| | | // 校验数据 |
| | | updateReqVO.setPackageId(line.getPackageId()); |
| | | validateLineSaveData(updateReqVO); |
| | | int boxCount = validateLineSaveData(updateReqVO); |
| | | |
| | | // 更新 |
| | | MesWmPackageLineDO updateObj = BeanUtils.toBean(updateReqVO, MesWmPackageLineDO.class); |
| | | updateObj.setBoxCount(boxCount); |
| | | packageLineMapper.updateById(updateObj); |
| | | } |
| | | |
| | |
| | | // ========== 校验方法 ========== |
| | | |
| | | /** |
| | | * 校验保存时的关联数据 |
| | | * 校验保存时的关联数据,并返回按 数量 / 每箱数量 向上取整计算的箱数 |
| | | */ |
| | | private void validateLineSaveData(MesWmPackageLineSaveReqVO reqVO) { |
| | | private int validateLineSaveData(MesWmPackageLineSaveReqVO reqVO) { |
| | | // 校验装箱单状态为草稿 |
| | | packageService.validatePackageStatusDraft(reqVO.getPackageId()); |
| | | // 校验产品物料存在 |
| | | mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| | | // 校验工单已确认 |
| | | workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId()); |
| | | |
| | | // 校验物料档案:必须属于成品分类,且已维护每箱数量 |
| | | MesMdItemDO item = mdItemService.getItem(reqVO.getItemId()); |
| | | if (item == null) { |
| | | throw exception(WM_PACKAGE_LINE_BOX_QUANTITY_NOT_CONFIGURED, "ID=" + reqVO.getItemId()); |
| | | } |
| | | String itemName = StrUtil.isNotBlank(item.getName()) ? item.getName() : item.getCode(); |
| | | MesMdItemTypeDO itemType = item.getItemTypeId() != null |
| | | ? itemTypeService.getItemTypeMap(Collections.singleton(item.getItemTypeId())) |
| | | .get(item.getItemTypeId()) |
| | | : null; |
| | | if (itemType == null || !Boolean.TRUE.equals(itemType.getFinishedFlag())) { |
| | | throw exception(WM_PACKAGE_LINE_ITEM_NOT_FINISHED, itemName); |
| | | } |
| | | if (item.getBoxQuantity() == null || item.getBoxQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw exception(WM_PACKAGE_LINE_BOX_QUANTITY_NOT_CONFIGURED, itemName); |
| | | } |
| | | |
| | | // 校验工单已确认,且装箱物料必须为工单的产品 |
| | | MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId()); |
| | | if (ObjUtil.notEqual(reqVO.getItemId(), workOrder.getProductId())) { |
| | | MesMdItemDO product = workOrder.getProductId() != null |
| | | ? mdItemService.getItem(workOrder.getProductId()) : null; |
| | | String productName = product != null |
| | | ? (StrUtil.isNotBlank(product.getName()) ? product.getName() : product.getCode()) |
| | | : "ID=" + workOrder.getProductId(); |
| | | throw exception(WM_PACKAGE_LINE_WORK_ORDER_PRODUCT_MISMATCH, |
| | | itemName, workOrder.getCode(), productName); |
| | | } |
| | | |
| | | // 校验同一工单累计装箱数量不超过工单数量(草稿与已完成的装箱单都计入) |
| | | BigDecimal plannedQuantity = packageLineMapper.selectListByWorkOrderId(workOrder.getId()).stream() |
| | | .filter(line -> ObjUtil.notEqual(line.getId(), reqVO.getId())) |
| | | .map(MesWmPackageLineDO::getQuantity) |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | BigDecimal totalQuantity = plannedQuantity.add(reqVO.getQuantity()); |
| | | if (workOrder.getQuantity() != null && totalQuantity.compareTo(workOrder.getQuantity()) > 0) { |
| | | throw exception(WM_PACKAGE_LINE_WORK_ORDER_QUANTITY_EXCEED, |
| | | workOrder.getCode(), workOrder.getQuantity(), plannedQuantity, reqVO.getQuantity()); |
| | | } |
| | | |
| | | // 校验组装工序已完成报工:组装合格数量必须覆盖累计装箱数量 |
| | | validateAssemblyCompleted(workOrder, plannedQuantity, reqVO.getQuantity(), totalQuantity); |
| | | |
| | | // 箱数 = 数量 / 每箱数量,向上取整 |
| | | return reqVO.getQuantity().divide(item.getBoxQuantity(), 0, RoundingMode.CEILING).intValue(); |
| | | } |
| | | |
| | | /** |
| | | * 校验工单的【组装】工序已完成报工,且合格数量不少于累计装箱数量 |
| | | */ |
| | | private void validateAssemblyCompleted(MesProWorkOrderDO workOrder, BigDecimal packedQuantity, |
| | | BigDecimal currentQuantity, BigDecimal totalQuantity) { |
| | | List<Long> processIds = workOrderProcessService.getWorkOrderProcessListByWorkOrderId(workOrder.getId()) |
| | | .stream() |
| | | .map(MesProWorkOrderProcessDO::getProcessId) |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .toList(); |
| | | List<Long> assemblyProcessIds = processService.getProcessMap(processIds).values().stream() |
| | | .filter(process -> ASSEMBLY_PROCESS_CODE.equalsIgnoreCase(process.getCode()) |
| | | || ASSEMBLY_PROCESS_NAME.equals(process.getName())) |
| | | .map(MesProProcessDO::getId) |
| | | .toList(); |
| | | if (assemblyProcessIds.isEmpty()) { |
| | | throw exception(WM_PACKAGE_LINE_ASSEMBLY_PROCESS_NOT_FOUND, workOrder.getCode()); |
| | | } |
| | | BigDecimal assembledQuantity = feedbackMapper.selectFinishedQualifiedQuantitySum( |
| | | workOrder.getId(), assemblyProcessIds); |
| | | if (assembledQuantity.compareTo(totalQuantity) < 0) { |
| | | throw exception(WM_PACKAGE_LINE_ASSEMBLY_NOT_COMPLETED, |
| | | workOrder.getCode(), assembledQuantity, totalQuantity, packedQuantity, currentQuantity); |
| | | } |
| | | } |
| | | |
| | | private MesWmPackageLineDO validatePackageLineExists(Long id) { |