| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | |
| | | BigDecimal totalReturnQty = oldReturnQty.add(currentReturnQty); |
| | | if (currentReturnQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | String returnBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo()); |
| | | addInventory(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE); |
| | | addInventoryRecordOnly(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE); |
| | | } |
| | | |
| | | BigDecimal actualQty = defaultDecimal(oldPick.getQuantity()) |
| | |
| | | batchNoList = Collections.singletonList(null); |
| | | } |
| | | |
| | | // 先查询各批次库存记录,并按首次入库时间升序排列(先进先出), |
| | | // 不依赖前端勾选顺序,避免人工多选时扣错批次。 |
| | | Map<String, StockInventory> inventoryMap = new LinkedHashMap<>(); |
| | | for (String currentBatchNo : batchNoList) { |
| | | inventoryMap.put(currentBatchNo, |
| | | stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo))); |
| | | } |
| | | // 首次入库时间以入库记录为准:历史数据批量导入时 stock_inventory.create_time 可能相同, |
| | | // 不能准确反映批次先后。 |
| | | Map<String, LocalDateTime> firstInTimeMap = new HashMap<>(); |
| | | List<String> nonNullBatchNoList = batchNoList.stream() |
| | | .filter(StringUtils::isNotEmpty) |
| | | .collect(Collectors.toList()); |
| | | if (!nonNullBatchNoList.isEmpty()) { |
| | | List<StockInRecord> stockInRecords = stockInRecordService.list( |
| | | Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .in(StockInRecord::getBatchNo, nonNullBatchNoList)); |
| | | for (StockInRecord stockInRecord : stockInRecords) { |
| | | if (stockInRecord == null || stockInRecord.getBatchNo() == null |
| | | || stockInRecord.getCreateTime() == null) { |
| | | continue; |
| | | } |
| | | firstInTimeMap.merge(stockInRecord.getBatchNo(), stockInRecord.getCreateTime(), |
| | | (a, b) -> a.isBefore(b) ? a : b); |
| | | } |
| | | } |
| | | List<String> fifoBatchNoList = inventoryMap.entrySet().stream() |
| | | .sorted(Comparator |
| | | .comparing((Map.Entry<String, StockInventory> entry) -> firstInTimeMap.get(entry.getKey()), |
| | | Comparator.nullsLast(Comparator.naturalOrder())) |
| | | .thenComparing(entry -> entry.getValue() == null ? null : entry.getValue().getCreateTime(), |
| | | Comparator.nullsLast(Comparator.naturalOrder())) |
| | | .thenComparing(entry -> { |
| | | StockInventory inventory = entry.getValue(); |
| | | return inventory == null || inventory.getId() == null |
| | | ? Long.MAX_VALUE : inventory.getId(); |
| | | })) |
| | | .map(Map.Entry::getKey) |
| | | .collect(Collectors.toList()); |
| | | |
| | | // 先计算各批次可用量,避免边扣边算导致判断不一致。 |
| | | Map<String, BigDecimal> availableQuantityMap = new LinkedHashMap<>(); |
| | | BigDecimal totalAvailableQuantity = BigDecimal.ZERO; |
| | | // 遍历批次,计算每个批次可用库存。 |
| | | for (String currentBatchNo : batchNoList) { |
| | | // 查询当前规格+批次的库存记录。 |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo)); |
| | | // 按先进先出顺序计算每个批次可用库存。 |
| | | for (String currentBatchNo : fifoBatchNoList) { |
| | | StockInventory stockInventory = inventoryMap.get(currentBatchNo); |
| | | BigDecimal availableQuantity = BigDecimal.ZERO; |
| | | if (stockInventory != null) { |
| | | availableQuantity = defaultDecimal(stockInventory.getQualitity()) |
| | |
| | | } |
| | | } |
| | | |
| | | private void addInventoryRecordOnly(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | String stockInRecordType) { |
| | | // 仅记录入库申请,不做审核通过。 |
| | | BigDecimal addQuantity = defaultDecimal(quantity); |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | stockInventoryDto.setRecordType(stockInRecordType); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("退料入库记录保存失败:" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) { |
| | | // 解析新增场景的领料明细集合。 |
| | | if (dto == null) { |