| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.production.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderPickDto; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | | import com.ruoyi.production.mapper.ProductionOperationTaskMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderPickMapper; |
| | | import com.ruoyi.production.mapper.ProductionOrderPickRecordMapper; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.pojo.ProductionOrderPick; |
| | | import com.ruoyi.production.pojo.ProductionOrderPickRecord; |
| | | import com.ruoyi.production.service.ProductionOrderPickService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.*; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * çã å´æ£°åæ¡ç»¾è¯ç«æµ ?éå¶
å§ç¹çµå¹ç»«? |
| | | * </p> |
| | | * |
| | | * @author éºîî±æîæ¬¢éå çé»å¿¥ç´é夿ªºéîå¾ |
| | | * @since 2026-04-21 03:55:52 |
| | | */ |
| | | @Service |
| | | @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; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StockInventoryService stockInventoryService; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean savePick(ProductionOrderPickDto dto) { |
| | | List<ProductionOrderPickDto> pickItems = resolvePickItems(dto); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); |
| | | validatePickParam(resolvedDto, rowNo); |
| | | |
| | | List<String> batchNoList = resolveBatchNoList(resolvedDto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(resolvedDto.getProductionOrderId()); |
| | | orderPick.setProductModelId(resolvedDto.getProductModelId()); |
| | | orderPick.setBatchNo(storedBatchNo); |
| | | orderPick.setQuantity(resolvedDto.getPickQuantity()); |
| | | orderPick.setRemark(resolvedDto.getRemark()); |
| | | orderPick.setOperationName(resolvedDto.getOperationName()); |
| | | orderPick.setTechnologyOperationId(resolvedDto.getTechnologyOperationId()); |
| | | orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity()); |
| | | orderPick.setBom(resolvedDto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | | resolvedDto.getProductionOrderId(), |
| | | resolvedDto.getProductionOperationTaskId(), |
| | | resolvedDto.getProductModelId(), |
| | | inventoryBatchNo, |
| | | resolvedDto.getPickQuantity(), |
| | | BigDecimal.ZERO, |
| | | resolvedDto.getPickQuantity(), |
| | | resolvedDto.getPickType(), |
| | | resolvedDto.getRemark(), |
| | | resolvedDto.getFeedingReason()); |
| | | } |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean updatePick(ProductionOrderPickDto dto) { |
| | | if (dto == null) { |
| | | throw new ServiceException("åæ´åæ°ä¸è½ä¸ºç©º"); |
| | | } |
| | | Long productionOrderId = resolveProductionOrderId(dto); |
| | | if (productionOrderId == null) { |
| | | throw new ServiceException("ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOrderId); |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("ç产订åä¸åå¨"); |
| | | } |
| | | |
| | | List<ProductionOrderPick> existingPickList = baseMapper.selectList( |
| | | Wrappers.<ProductionOrderPick>lambdaQuery() |
| | | .eq(ProductionOrderPick::getProductionOrderId, productionOrderId)); |
| | | 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); |
| | | |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(dto); |
| | | Set<Long> keepPickIdSet = new HashSet<>(); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); |
| | | if (isEmptyUpdateItem(resolvedDto)) { |
| | | continue; |
| | | } |
| | | if (resolvedDto.getProductionOrderId() == null) { |
| | | resolvedDto.setProductionOrderId(productionOrderId); |
| | | } |
| | | validatePickParam(resolvedDto, rowNo); |
| | | |
| | | if (resolvedDto.getId() == null) { |
| | | addNewPickInUpdate(resolvedDto, rowNo); |
| | | continue; |
| | | } |
| | | keepPickIdSet.add(resolvedDto.getId()); |
| | | updateExistingPick(resolvedDto, rowNo, existingPickMap); |
| | | } |
| | | processMissingPickItems(dto, existingPickMap, productionOrderId, keepPickIdSet); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> listPickedDetail(Long productionOrderId) { |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<ProductionOrderPickVo> detailList = baseMapper.listPickedDetailByOrderId(productionOrderId); |
| | | fillBatchNoList(detailList); |
| | | fillSelectableBatchNoList(detailList); |
| | | return detailList; |
| | | } |
| | | |
| | | private void processDeletePickIds(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | if (rootDto.getDeletePickIds() == null || rootDto.getDeletePickIds().isEmpty()) { |
| | | return; |
| | | } |
| | | Set<Long> deleteIdSet = new LinkedHashSet<>(rootDto.getDeletePickIds()); |
| | | for (Long deleteId : deleteIdSet) { |
| | | if (deleteId == null) { |
| | | continue; |
| | | } |
| | | ProductionOrderPick existingPick = existingPickMap.get(deleteId); |
| | | if (existingPick == null || !Objects.equals(existingPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("è¦å é¤çé¢æè®°å½ä¸å卿ä¸å±äºå½å订åï¼ID=" + deleteId); |
| | | } |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity()); |
| | | addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | int affected = baseMapper.deleteById(deleteId); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("å é¤é¢æå¤±è´¥ï¼ID=" + deleteId); |
| | | } |
| | | insertPickRecord(existingPick.getId(), |
| | | existingPick.getProductionOrderId(), |
| | | rootDto.getProductionOperationTaskId(), |
| | | existingPick.getProductModelId(), |
| | | oldBatchNo, |
| | | oldQuantity, |
| | | oldQuantity, |
| | | BigDecimal.ZERO, |
| | | rootDto.getPickType(), |
| | | rootDto.getRemark(), |
| | | rootDto.getFeedingReason()); |
| | | existingPickMap.remove(deleteId); |
| | | } |
| | | } |
| | | |
| | | private void processMissingPickItems(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId, |
| | | Set<Long> keepPickIdSet) { |
| | | if (rootDto.getPickList() == null) { |
| | | return; |
| | | } |
| | | List<ProductionOrderPick> missingPickList = existingPickMap.values().stream() |
| | | .filter(Objects::nonNull) |
| | | .filter(item -> item.getId() != null) |
| | | .filter(item -> Objects.equals(item.getProductionOrderId(), productionOrderId)) |
| | | .filter(item -> !keepPickIdSet.contains(item.getId())) |
| | | .toList(); |
| | | for (ProductionOrderPick missingPick : missingPickList) { |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity()); |
| | | addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | int affected = baseMapper.deleteById(missingPick.getId()); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("å é¤é¢æå¤±è´¥ï¼ID=" + missingPick.getId()); |
| | | } |
| | | insertPickRecord(missingPick.getId(), |
| | | missingPick.getProductionOrderId(), |
| | | rootDto.getProductionOperationTaskId(), |
| | | missingPick.getProductModelId(), |
| | | oldBatchNo, |
| | | oldQuantity, |
| | | oldQuantity, |
| | | BigDecimal.ZERO, |
| | | rootDto.getPickType(), |
| | | rootDto.getRemark(), |
| | | rootDto.getFeedingReason()); |
| | | existingPickMap.remove(missingPick.getId()); |
| | | } |
| | | } |
| | | |
| | | private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) { |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(dto.getProductionOrderId()); |
| | | orderPick.setProductModelId(dto.getProductModelId()); |
| | | orderPick.setBatchNo(storedBatchNo); |
| | | orderPick.setQuantity(dto.getPickQuantity()); |
| | | orderPick.setRemark(dto.getRemark()); |
| | | orderPick.setOperationName(dto.getOperationName()); |
| | | orderPick.setTechnologyOperationId(dto.getTechnologyOperationId()); |
| | | orderPick.setDemandedQuantity(dto.getDemandedQuantity()); |
| | | orderPick.setBom(dto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | | dto.getProductionOrderId(), |
| | | dto.getProductionOperationTaskId(), |
| | | dto.getProductModelId(), |
| | | inventoryBatchNo, |
| | | dto.getPickQuantity(), |
| | | BigDecimal.ZERO, |
| | | dto.getPickQuantity(), |
| | | dto.getPickType(), |
| | | 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()) |
| | | : formatBatchNoStorage(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, |
| | | int rowNo, |
| | | Map<Long, ProductionOrderPick> existingPickMap) { |
| | | ProductionOrderPick oldPick = existingPickMap.get(dto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), dto.getProductionOrderId())) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æè®°å½ä¸å卿ä¸å±äºå½å订å"); |
| | | } |
| | | |
| | | Long oldProductModelId = oldPick.getProductModelId(); |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(oldPick.getQuantity()); |
| | | |
| | | Long newProductModelId = dto.getProductModelId(); |
| | | List<String> newBatchNoList = resolveBatchNoList(dto); |
| | | String newBatchNo = pickInventoryBatchNo(newBatchNoList); |
| | | String newStoredBatchNo = formatBatchNoStorage(newBatchNoList); |
| | | BigDecimal newQuantity = dto.getPickQuantity(); |
| | | |
| | | boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId) |
| | | && Objects.equals(oldBatchNo, newBatchNo); |
| | | if (sameStockKey) { |
| | | BigDecimal delta = newQuantity.subtract(oldQuantity); |
| | | if (delta.compareTo(BigDecimal.ZERO) > 0) { |
| | | subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo); |
| | | } else if (delta.compareTo(BigDecimal.ZERO) < 0) { |
| | | addInventory(oldProductModelId, oldBatchNo, delta.abs()); |
| | | } |
| | | } else { |
| | | addInventory(oldProductModelId, oldBatchNo, oldQuantity); |
| | | subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo); |
| | | } |
| | | |
| | | oldPick.setProductModelId(newProductModelId); |
| | | oldPick.setBatchNo(newStoredBatchNo); |
| | | oldPick.setQuantity(newQuantity); |
| | | oldPick.setRemark(dto.getRemark()); |
| | | oldPick.setOperationName(dto.getOperationName()); |
| | | oldPick.setTechnologyOperationId(dto.getTechnologyOperationId()); |
| | | if (dto.getDemandedQuantity() != null) { |
| | | oldPick.setDemandedQuantity(dto.getDemandedQuantity()); |
| | | } |
| | | if (dto.getBom() != null) { |
| | | oldPick.setBom(dto.getBom()); |
| | | } |
| | | int affected = baseMapper.updateById(oldPick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ´æ°å¤±è´¥"); |
| | | } |
| | | |
| | | BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity; |
| | | if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) { |
| | | insertPickRecord(oldPick.getId(), |
| | | dto.getProductionOrderId(), |
| | | dto.getProductionOperationTaskId(), |
| | | newProductModelId, |
| | | newBatchNo, |
| | | recordQuantity, |
| | | oldQuantity, |
| | | newQuantity, |
| | | dto.getPickType(), |
| | | dto.getRemark(), |
| | | dto.getFeedingReason()); |
| | | } |
| | | } |
| | | |
| | | private void insertPickRecord(Long pickId, |
| | | Long productionOrderId, |
| | | Long productionOperationTaskId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal pickQuantity, |
| | | BigDecimal beforeQuantity, |
| | | BigDecimal afterQuantity, |
| | | Byte pickType, |
| | | String remark, |
| | | String feedingReason) { |
| | | ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord(); |
| | | pickRecord.setPickId(pickId); |
| | | pickRecord.setProductionOrderId(productionOrderId); |
| | | pickRecord.setProductionOperationTaskId(productionOperationTaskId); |
| | | pickRecord.setProductModelId(productModelId); |
| | | pickRecord.setBatchNo(batchNo); |
| | | pickRecord.setPickQuantity(defaultDecimal(pickQuantity)); |
| | | pickRecord.setBeforeQuantity(defaultDecimal(beforeQuantity)); |
| | | pickRecord.setAfterQuantity(defaultDecimal(afterQuantity)); |
| | | pickRecord.setPickType(pickType == null ? PICK_TYPE_NORMAL : pickType); |
| | | pickRecord.setRemark(remark); |
| | | pickRecord.setFeedingReason(feedingReason); |
| | | productionOrderPickRecordMapper.insert(pickRecord); |
| | | } |
| | | |
| | | private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) { |
| | | BigDecimal deductQuantity = defaultDecimal(quantity); |
| | | if (deductQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | |
| | | List<String> batchNoList = parseBatchNoValue(batchNo); |
| | | if (batchNoList.isEmpty()) { |
| | | batchNoList = Collections.singletonList(null); |
| | | } |
| | | |
| | | Map<String, BigDecimal> availableQuantityMap = new LinkedHashMap<>(); |
| | | BigDecimal totalAvailableQuantity = BigDecimal.ZERO; |
| | | for (String currentBatchNo : batchNoList) { |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo)); |
| | | BigDecimal availableQuantity = BigDecimal.ZERO; |
| | | if (stockInventory != null) { |
| | | availableQuantity = defaultDecimal(stockInventory.getQualitity()) |
| | | .subtract(defaultDecimal(stockInventory.getLockedQuantity())); |
| | | if (availableQuantity.compareTo(BigDecimal.ZERO) < 0) { |
| | | availableQuantity = BigDecimal.ZERO; |
| | | } |
| | | } |
| | | availableQuantityMap.put(currentBatchNo, availableQuantity); |
| | | totalAvailableQuantity = totalAvailableQuantity.add(availableQuantity); |
| | | } |
| | | |
| | | if (deductQuantity.compareTo(totalAvailableQuantity) > 0) { |
| | | BigDecimal shortQuantity = deductQuantity.subtract(totalAvailableQuantity); |
| | | throw new ServiceException("颿å¯ç¨åºåä¸è¶³ï¼å¯ç¨åºå为" + formatQuantity(totalAvailableQuantity) |
| | | + "ï¼è¿å·®" + formatQuantity(shortQuantity)); |
| | | } |
| | | |
| | | BigDecimal remainingQuantity = deductQuantity; |
| | | for (Map.Entry<String, BigDecimal> entry : availableQuantityMap.entrySet()) { |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | break; |
| | | } |
| | | BigDecimal availableQuantity = defaultDecimal(entry.getValue()); |
| | | if (availableQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | BigDecimal currentDeductQuantity = remainingQuantity.min(availableQuantity); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(entry.getKey()); |
| | | stockInventoryDto.setQualitity(currentDeductQuantity); |
| | | int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败"); |
| | | } |
| | | remainingQuantity = remainingQuantity.subtract(currentDeductQuantity); |
| | | } |
| | | |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败ï¼å©ä½å¾
æ£åæ°é为" + formatQuantity(remainingQuantity)); |
| | | } |
| | | } |
| | | |
| | | private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) { |
| | | BigDecimal addQuantity = defaultDecimal(quantity); |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode())); |
| | | stockInventoryDto.setRecordId(0L); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) { |
| | | if (dto == null) { |
| | | throw new ServiceException("颿忰ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getPickList() != null && !dto.getPickList().isEmpty()) { |
| | | return dto.getPickList(); |
| | | } |
| | | return Collections.singletonList(dto); |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolveUpdateItems(ProductionOrderPickDto dto) { |
| | | if (dto.getPickList() != null) { |
| | | return dto.getPickList(); |
| | | } |
| | | if (isEmptyUpdateItem(dto)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return Collections.singletonList(dto); |
| | | } |
| | | |
| | | private boolean isEmptyUpdateItem(ProductionOrderPickDto dto) { |
| | | return dto.getId() == null |
| | | && dto.getProductModelId() == null |
| | | && dto.getPickQuantity() == null |
| | | && 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()) |
| | | && dto.getDemandedQuantity() == null |
| | | && dto.getBom() == null |
| | | && StringUtils.isEmpty(dto.getRemark()); |
| | | } |
| | | |
| | | private Long resolveProductionOrderId(ProductionOrderPickDto dto) { |
| | | if (dto.getProductionOrderId() != null) { |
| | | return dto.getProductionOrderId(); |
| | | } |
| | | if (dto.getPickList() == null || dto.getPickList().isEmpty()) { |
| | | return null; |
| | | } |
| | | return dto.getPickList().stream() |
| | | .filter(Objects::nonNull) |
| | | .map(ProductionOrderPickDto::getProductionOrderId) |
| | | .filter(Objects::nonNull) |
| | | .findFirst() |
| | | .orElse(null); |
| | | } |
| | | |
| | | private ProductionOrderPickDto mergeDto(ProductionOrderPickDto rootDto, ProductionOrderPickDto itemDto) { |
| | | ProductionOrderPickDto merged = new ProductionOrderPickDto(); |
| | | if (itemDto != null) { |
| | | merged.setId(itemDto.getId()); |
| | | merged.setProductionOrderId(itemDto.getProductionOrderId()); |
| | | merged.setProductionOperationTaskId(itemDto.getProductionOperationTaskId()); |
| | | merged.setProductModelId(itemDto.getProductModelId()); |
| | | merged.setBatchNo(itemDto.getBatchNo()); |
| | | merged.setBatchNoList(itemDto.getBatchNoList()); |
| | | 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.getProductionOrderId() == null) { |
| | | merged.setProductionOrderId(rootDto.getProductionOrderId()); |
| | | } |
| | | if (merged.getProductionOperationTaskId() == null) { |
| | | merged.setProductionOperationTaskId(rootDto.getProductionOperationTaskId()); |
| | | } |
| | | if (merged.getProductModelId() == null) { |
| | | merged.setProductModelId(rootDto.getProductModelId()); |
| | | } |
| | | if (merged.getBatchNo() == null) { |
| | | merged.setBatchNo(rootDto.getBatchNo()); |
| | | } |
| | | if (merged.getBatchNoList() == null || merged.getBatchNoList().isEmpty()) { |
| | | merged.setBatchNoList(rootDto.getBatchNoList()); |
| | | } |
| | | if (merged.getPickQuantity() == null) { |
| | | merged.setPickQuantity(rootDto.getPickQuantity()); |
| | | } |
| | | if (merged.getPickType() == null) { |
| | | merged.setPickType(rootDto.getPickType()); |
| | | } |
| | | 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.getOperationName() == null) { |
| | | merged.setOperationName(rootDto.getOperationName()); |
| | | } |
| | | if (merged.getDemandedQuantity() == null) { |
| | | merged.setDemandedQuantity(rootDto.getDemandedQuantity()); |
| | | } |
| | | 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; |
| | | } |
| | | |
| | | private void validatePickParam(ProductionOrderPickDto dto, int rowNo) { |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getProductModelId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡äº§åè§æ ¼IDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ°éä¸è½å°äº0"); |
| | | } |
| | | 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) { |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | return null; |
| | | } |
| | | String trimBatchNo = batchNo.trim(); |
| | | return trimBatchNo.isEmpty() ? null : trimBatchNo; |
| | | } |
| | | private List<String> resolveBatchNoList(ProductionOrderPickDto dto) { |
| | | List<String> normalizedBatchNoList = normalizeBatchNoList(dto.getBatchNoList()); |
| | | if (!normalizedBatchNoList.isEmpty()) { |
| | | return normalizedBatchNoList; |
| | | } |
| | | return parseBatchNoValue(dto.getBatchNo()); |
| | | } |
| | | |
| | | private String pickInventoryBatchNo(List<String> batchNoList) { |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return null; |
| | | } |
| | | return batchNoList.get(0); |
| | | } |
| | | |
| | | private String resolveInventoryBatchNoFromStored(String storedBatchNo) { |
| | | return pickInventoryBatchNo(parseBatchNoValue(storedBatchNo)); |
| | | } |
| | | |
| | | private String formatBatchNoStorage(List<String> batchNoList) { |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return null; |
| | | } |
| | | if (batchNoList.size() == 1) { |
| | | return batchNoList.get(0); |
| | | } |
| | | return String.join(",", batchNoList); |
| | | } |
| | | |
| | | private List<String> normalizeBatchNoList(List<String> batchNoList) { |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | | LinkedHashSet<String> normalizedSet = new LinkedHashSet<>(); |
| | | for (String batchNo : batchNoList) { |
| | | String normalizedBatchNo = normalizeBatchNo(batchNo); |
| | | if (!StringUtils.isEmpty(normalizedBatchNo)) { |
| | | normalizedSet.add(normalizedBatchNo); |
| | | } |
| | | } |
| | | return new ArrayList<>(normalizedSet); |
| | | } |
| | | |
| | | private void fillBatchNoList(List<ProductionOrderPickVo> detailList) { |
| | | if (detailList == null || detailList.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<String, LinkedHashSet<String>> batchNoGroupMap = new HashMap<>(); |
| | | for (ProductionOrderPickVo detail : detailList) { |
| | | String key = buildBatchNoGroupKey(detail); |
| | | LinkedHashSet<String> batchSet = batchNoGroupMap.computeIfAbsent(key, k -> new LinkedHashSet<>()); |
| | | batchSet.addAll(parseBatchNoValue(detail.getBatchNo())); |
| | | if (detail.getBatchNoList() != null && !detail.getBatchNoList().isEmpty()) { |
| | | batchSet.addAll(normalizeBatchNoList(detail.getBatchNoList())); |
| | | } |
| | | } |
| | | for (ProductionOrderPickVo detail : detailList) { |
| | | String key = buildBatchNoGroupKey(detail); |
| | | LinkedHashSet<String> batchSet = batchNoGroupMap.get(key); |
| | | detail.setBatchNoList(batchSet == null ? Collections.emptyList() : new ArrayList<>(batchSet)); |
| | | } |
| | | } |
| | | |
| | | private void fillSelectableBatchNoList(List<ProductionOrderPickVo> detailList) { |
| | | if (detailList == null || detailList.isEmpty()) { |
| | | return; |
| | | } |
| | | Set<Long> productModelIdSet = detailList.stream() |
| | | .map(ProductionOrderPickVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | if (productModelIdSet.isEmpty()) { |
| | | return; |
| | | } |
| | | List<StockInventory> stockBatchList = stockInventoryMapper.listSelectableBatchNoByProductModelIds( |
| | | new ArrayList<>(productModelIdSet)); |
| | | Map<Long, LinkedHashSet<String>> stockBatchMap = new HashMap<>(); |
| | | for (StockInventory stockInventory : stockBatchList) { |
| | | if (stockInventory == null || stockInventory.getProductModelId() == null) { |
| | | continue; |
| | | } |
| | | String normalizedBatchNo = normalizeBatchNo(stockInventory.getBatchNo()); |
| | | if (StringUtils.isEmpty(normalizedBatchNo)) { |
| | | continue; |
| | | } |
| | | stockBatchMap.computeIfAbsent(stockInventory.getProductModelId(), k -> new LinkedHashSet<>()) |
| | | .add(normalizedBatchNo); |
| | | } |
| | | for (ProductionOrderPickVo detail : detailList) { |
| | | LinkedHashSet<String> mergedBatchSet = new LinkedHashSet<>(); |
| | | mergedBatchSet.addAll(normalizeBatchNoList(detail.getBatchNoList())); |
| | | LinkedHashSet<String> selectableBatchSet = stockBatchMap.get(detail.getProductModelId()); |
| | | if (selectableBatchSet != null) { |
| | | mergedBatchSet.addAll(selectableBatchSet); |
| | | } |
| | | detail.setBatchNoList(new ArrayList<>(mergedBatchSet)); |
| | | } |
| | | } |
| | | |
| | | private String buildBatchNoGroupKey(ProductionOrderPickVo detail) { |
| | | return String.valueOf(detail.getProductionOrderId()) + "|" |
| | | + String.valueOf(detail.getProductModelId()) + "|" |
| | | + String.valueOf(detail.getTechnologyOperationId()) + "|" |
| | | + String.valueOf(detail.getOperationName()); |
| | | } |
| | | |
| | | private List<String> parseBatchNoValue(String rawBatchNoValue) { |
| | | String normalizedValue = normalizeBatchNo(rawBatchNoValue); |
| | | if (StringUtils.isEmpty(normalizedValue)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) { |
| | | String value = normalizedValue.substring(1, normalizedValue.length() - 1); |
| | | if (StringUtils.isEmpty(value)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<String> parsed = Arrays.stream(value.split(",")) |
| | | .map(item -> item == null ? null : item.trim().replace("\"", "").replace("'", "")) |
| | | .collect(Collectors.toList()); |
| | | return normalizeBatchNoList(parsed); |
| | | } |
| | | if (normalizedValue.contains(",")) { |
| | | List<String> parsed = Arrays.stream(normalizedValue.split(",")) |
| | | .map(item -> item == null ? null : item.trim()) |
| | | .collect(Collectors.toList()); |
| | | return normalizeBatchNoList(parsed); |
| | | } |
| | | return Collections.singletonList(normalizedValue); |
| | | } |
| | | |
| | | private LambdaQueryWrapper<StockInventory> buildStockWrapper(Long productModelId, String batchNo) { |
| | | LambdaQueryWrapper<StockInventory> wrapper = Wrappers.<StockInventory>lambdaQuery() |
| | | .eq(StockInventory::getProductModelId, productModelId); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | wrapper.isNull(StockInventory::getBatchNo); |
| | | } else { |
| | | wrapper.eq(StockInventory::getBatchNo, batchNo); |
| | | } |
| | | return wrapper; |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private String formatQuantity(BigDecimal value) { |
| | | return defaultDecimal(value).stripTrailingZeros().toPlainString(); |
| | | } |
| | | } |