| | |
| | | @RequiredArgsConstructor |
| | | public class ProductionOrderPickServiceImpl extends ServiceImpl<ProductionOrderPickMapper, ProductionOrderPick> implements ProductionOrderPickService { |
| | | |
| | | private static final byte PICK_TYPE_NORMAL = 1; |
| | | private static final byte PICK_TYPE_FEEDING = 2; |
| | | |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | |
| | | orderPick.setTechnologyOperationId(resolvedDto.getTechnologyOperationId()); |
| | | orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity()); |
| | | orderPick.setBom(resolvedDto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | |
| | | BigDecimal.ZERO, |
| | | resolvedDto.getPickQuantity(), |
| | | resolvedDto.getPickType(), |
| | | resolvedDto.getRemark()); |
| | | resolvedDto.getRemark(), |
| | | resolvedDto.getFeedingReason()); |
| | | } |
| | | return true; |
| | | } |
| | |
| | | Map<Long, ProductionOrderPick> existingPickMap = existingPickList.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionOrderPick::getId, Function.identity(), (a, b) -> a)); |
| | | |
| | | if (isFeedingRequest(dto)) { |
| | | processFeedingPickItems(dto, existingPickMap, productionOrderId); |
| | | return true; |
| | | } |
| | | if (isReturnRequest(dto)) { |
| | | processReturnPickItems(dto, existingPickMap, productionOrderId); |
| | | return true; |
| | | } |
| | | |
| | | processDeletePickIds(dto, existingPickMap, productionOrderId); |
| | | |
| | |
| | | oldQuantity, |
| | | BigDecimal.ZERO, |
| | | rootDto.getPickType(), |
| | | rootDto.getRemark()); |
| | | rootDto.getRemark(), |
| | | rootDto.getFeedingReason()); |
| | | existingPickMap.remove(deleteId); |
| | | } |
| | | } |
| | |
| | | .filter(item -> item.getId() != null) |
| | | .filter(item -> Objects.equals(item.getProductionOrderId(), productionOrderId)) |
| | | .filter(item -> !keepPickIdSet.contains(item.getId())) |
| | | .collect(Collectors.toList()); |
| | | .toList(); |
| | | for (ProductionOrderPick missingPick : missingPickList) { |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity()); |
| | |
| | | oldQuantity, |
| | | BigDecimal.ZERO, |
| | | rootDto.getPickType(), |
| | | rootDto.getRemark()); |
| | | rootDto.getRemark(), |
| | | rootDto.getFeedingReason()); |
| | | existingPickMap.remove(missingPick.getId()); |
| | | } |
| | | } |
| | |
| | | orderPick.setTechnologyOperationId(dto.getTechnologyOperationId()); |
| | | orderPick.setDemandedQuantity(dto.getDemandedQuantity()); |
| | | orderPick.setBom(dto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | |
| | | BigDecimal.ZERO, |
| | | dto.getPickQuantity(), |
| | | dto.getPickType(), |
| | | dto.getRemark()); |
| | | dto.getRemark(), |
| | | dto.getFeedingReason()); |
| | | } |
| | | |
| | | private void processFeedingPickItems(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(rootDto, pickItems.get(i)); |
| | | if (isEmptyUpdateItem(resolvedDto)) { |
| | | continue; |
| | | } |
| | | if (!isFeedingPick(resolvedDto)) { |
| | | throw new ServiceException("补料请求中的领料类型必须全部为2"); |
| | | } |
| | | if (resolvedDto.getProductionOrderId() == null) { |
| | | resolvedDto.setProductionOrderId(productionOrderId); |
| | | } |
| | | validateFeedingParam(resolvedDto, rowNo); |
| | | |
| | | ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("第" + rowNo + "条领料记录不存在或不属于当前订单"); |
| | | } |
| | | addFeedingPick(resolvedDto, oldPick, rowNo); |
| | | } |
| | | } |
| | | |
| | | private void addFeedingPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) { |
| | | if (dto.getProductModelId() != null && !Objects.equals(dto.getProductModelId(), oldPick.getProductModelId())) { |
| | | throw new ServiceException("第" + rowNo + "条补料产品规格与领料记录不一致"); |
| | | } |
| | | Long productModelId = oldPick.getProductModelId(); |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = batchNoList.isEmpty() |
| | | ? resolveInventoryBatchNoFromStored(oldPick.getBatchNo()) |
| | | : pickInventoryBatchNo(batchNoList); |
| | | BigDecimal feedingQuantity = dto.getFeedingQuantity(); |
| | | |
| | | subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo); |
| | | |
| | | BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId()); |
| | | BigDecimal afterFeedingQty = beforeFeedingQty.add(feedingQuantity); |
| | | insertPickRecord(oldPick.getId(), |
| | | dto.getProductionOrderId(), |
| | | dto.getProductionOperationTaskId(), |
| | | productModelId, |
| | | inventoryBatchNo, |
| | | feedingQuantity, |
| | | beforeFeedingQty, |
| | | afterFeedingQty, |
| | | PICK_TYPE_FEEDING, |
| | | dto.getRemark(), |
| | | dto.getFeedingReason()); |
| | | |
| | | ProductionOrderPick updatePick = new ProductionOrderPick(); |
| | | updatePick.setId(oldPick.getId()); |
| | | updatePick.setFeedingQty(afterFeedingQty); |
| | | updatePick.setActualQty(calculateActualQty(oldPick, afterFeedingQty)); |
| | | int affected = baseMapper.updateById(updatePick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "条补料总量更新失败"); |
| | | } |
| | | oldPick.setFeedingQty(afterFeedingQty); |
| | | oldPick.setActualQty(updatePick.getActualQty()); |
| | | } |
| | | |
| | | private void processReturnPickItems(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(rootDto, pickItems.get(i)); |
| | | if (isEmptyUpdateItem(resolvedDto)) { |
| | | continue; |
| | | } |
| | | if (resolvedDto.getProductionOrderId() == null) { |
| | | resolvedDto.setProductionOrderId(productionOrderId); |
| | | } |
| | | validateReturnParam(resolvedDto, rowNo); |
| | | |
| | | ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("第" + rowNo + "条领料记录不存在或不属于当前订单"); |
| | | } |
| | | updateReturnPick(resolvedDto, oldPick, rowNo); |
| | | } |
| | | } |
| | | |
| | | private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) { |
| | | ProductionOrderPick updatePick = new ProductionOrderPick(); |
| | | updatePick.setId(oldPick.getId()); |
| | | updatePick.setReturnQty(dto.getReturnQty()); |
| | | updatePick.setActualQty(dto.getActualQty()); |
| | | updatePick.setReturned(true); |
| | | int affected = baseMapper.updateById(updatePick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "条退料信息更新失败"); |
| | | } |
| | | oldPick.setReturnQty(updatePick.getReturnQty()); |
| | | oldPick.setActualQty(updatePick.getActualQty()); |
| | | oldPick.setReturned(true); |
| | | } |
| | | |
| | | private void updateExistingPick(ProductionOrderPickDto dto, |
| | |
| | | oldQuantity, |
| | | newQuantity, |
| | | dto.getPickType(), |
| | | dto.getRemark()); |
| | | dto.getRemark(), |
| | | dto.getFeedingReason()); |
| | | } |
| | | } |
| | | |
| | |
| | | BigDecimal beforeQuantity, |
| | | BigDecimal afterQuantity, |
| | | Byte pickType, |
| | | String remark) { |
| | | String remark, |
| | | String feedingReason) { |
| | | ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord(); |
| | | pickRecord.setPickId(pickId); |
| | | pickRecord.setProductionOrderId(productionOrderId); |
| | |
| | | pickRecord.setPickQuantity(defaultDecimal(pickQuantity)); |
| | | pickRecord.setBeforeQuantity(defaultDecimal(beforeQuantity)); |
| | | pickRecord.setAfterQuantity(defaultDecimal(afterQuantity)); |
| | | pickRecord.setPickType(pickType == null ? (byte) 1 : pickType); |
| | | pickRecord.setPickType(pickType == null ? PICK_TYPE_NORMAL : pickType); |
| | | pickRecord.setRemark(remark); |
| | | pickRecord.setFeedingReason(feedingReason); |
| | | productionOrderPickRecordMapper.insert(pickRecord); |
| | | } |
| | | |
| | |
| | | && StringUtils.isEmpty(dto.getBatchNo()) |
| | | && (dto.getBatchNoList() == null || dto.getBatchNoList().isEmpty()) |
| | | && dto.getPickType() == null |
| | | && dto.getFeedingQuantity() == null |
| | | && StringUtils.isEmpty(dto.getFeedingReason()) |
| | | && dto.getReturnQty() == null |
| | | && dto.getActualQty() == null |
| | | && dto.getReturned() == null |
| | | && dto.getProductionOperationTaskId() == null |
| | | && dto.getTechnologyOperationId() == null |
| | | && StringUtils.isEmpty(dto.getOperationName()) |
| | |
| | | merged.setPickQuantity(itemDto.getPickQuantity()); |
| | | merged.setPickType(itemDto.getPickType()); |
| | | merged.setRemark(itemDto.getRemark()); |
| | | merged.setFeedingReason(itemDto.getFeedingReason()); |
| | | merged.setFeedingQuantity(itemDto.getFeedingQuantity()); |
| | | merged.setTechnologyOperationId(itemDto.getTechnologyOperationId()); |
| | | merged.setOperationName(itemDto.getOperationName()); |
| | | merged.setDemandedQuantity(itemDto.getDemandedQuantity()); |
| | | merged.setBom(itemDto.getBom()); |
| | | merged.setReturnQty(itemDto.getReturnQty()); |
| | | merged.setActualQty(itemDto.getActualQty()); |
| | | merged.setReturned(itemDto.getReturned()); |
| | | } |
| | | if (merged.getId() == null) { |
| | | merged.setId(rootDto.getId()); |
| | |
| | | if (merged.getRemark() == null) { |
| | | merged.setRemark(rootDto.getRemark()); |
| | | } |
| | | if (merged.getFeedingReason() == null) { |
| | | merged.setFeedingReason(rootDto.getFeedingReason()); |
| | | } |
| | | if (merged.getFeedingQuantity() == null) { |
| | | merged.setFeedingQuantity(rootDto.getFeedingQuantity()); |
| | | } |
| | | if (merged.getTechnologyOperationId() == null) { |
| | | merged.setTechnologyOperationId(rootDto.getTechnologyOperationId()); |
| | | } |
| | |
| | | } |
| | | if (merged.getBom() == null) { |
| | | merged.setBom(rootDto.getBom()); |
| | | } |
| | | if (merged.getReturnQty() == null) { |
| | | merged.setReturnQty(rootDto.getReturnQty()); |
| | | } |
| | | if (merged.getActualQty() == null) { |
| | | merged.setActualQty(rootDto.getActualQty()); |
| | | } |
| | | if (merged.getReturned() == null) { |
| | | merged.setReturned(rootDto.getReturned()); |
| | | } |
| | | return merged; |
| | | } |
| | |
| | | if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("第" + rowNo + "条领料数量必须大于0"); |
| | | } |
| | | if (dto.getPickType() != null && dto.getPickType() != 1 && dto.getPickType() != 2) { |
| | | if (dto.getPickType() != null && dto.getPickType() != PICK_TYPE_NORMAL && dto.getPickType() != PICK_TYPE_FEEDING) { |
| | | throw new ServiceException("第" + rowNo + "条领料类型只能是1或2"); |
| | | } |
| | | } |
| | | |
| | | private void validateFeedingParam(ProductionOrderPickDto dto, int rowNo) { |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "条生产订单ID不能为空"); |
| | | } |
| | | if (dto.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "条领料ID不能为空"); |
| | | } |
| | | if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("第" + rowNo + "条本次补料数量必须大于0"); |
| | | } |
| | | if (!isFeedingPick(dto)) { |
| | | throw new ServiceException("第" + rowNo + "条补料类型必须为2"); |
| | | } |
| | | } |
| | | |
| | | private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) { |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "条生产订单ID不能为空"); |
| | | } |
| | | if (dto.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "条领料ID不能为空"); |
| | | } |
| | | if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "条退料数量不能为空且不能小于0"); |
| | | } |
| | | if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "条实际数量不能为空且不能小于0"); |
| | | } |
| | | } |
| | | |
| | | private boolean isFeedingRequest(ProductionOrderPickDto dto) { |
| | | if (isFeedingPick(dto)) { |
| | | return true; |
| | | } |
| | | if (dto.getPickList() == null || dto.getPickList().isEmpty()) { |
| | | return false; |
| | | } |
| | | return dto.getPickList().stream() |
| | | .filter(Objects::nonNull) |
| | | .anyMatch(this::isFeedingPick); |
| | | } |
| | | |
| | | private boolean isFeedingPick(ProductionOrderPickDto dto) { |
| | | return dto != null && Objects.equals(dto.getPickType(), PICK_TYPE_FEEDING); |
| | | } |
| | | |
| | | private boolean isReturnRequest(ProductionOrderPickDto dto) { |
| | | if (isReturnPick(dto)) { |
| | | return true; |
| | | } |
| | | if (dto.getPickList() == null || dto.getPickList().isEmpty()) { |
| | | return false; |
| | | } |
| | | return dto.getPickList().stream() |
| | | .filter(Objects::nonNull) |
| | | .anyMatch(this::isReturnPick); |
| | | } |
| | | |
| | | private boolean isReturnPick(ProductionOrderPickDto dto) { |
| | | return dto != null && Boolean.TRUE.equals(dto.getReturned()); |
| | | } |
| | | |
| | | private BigDecimal sumFeedingQuantity(Long productionOrderId, Long pickId) { |
| | | List<ProductionOrderPickRecord> feedingRecords = productionOrderPickRecordMapper.selectList( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId) |
| | | .eq(ProductionOrderPickRecord::getPickId, pickId) |
| | | .eq(ProductionOrderPickRecord::getPickType, PICK_TYPE_FEEDING)); |
| | | return feedingRecords.stream() |
| | | .map(ProductionOrderPickRecord::getPickQuantity) |
| | | .map(this::defaultDecimal) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | } |
| | | |
| | | private BigDecimal calculateActualQty(ProductionOrderPick pick, BigDecimal feedingQty) { |
| | | return defaultDecimal(pick.getQuantity()) |
| | | .add(defaultDecimal(feedingQty)) |
| | | .subtract(defaultDecimal(pick.getReturnQty())); |
| | | } |
| | | |
| | | private String normalizeBatchNo(String batchNo) { |
| | |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | } |
| | | |