diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/PDItemSaveDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/PDItemSaveDTO.java index ec68dbc13..5644fc3fc 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/PDItemSaveDTO.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/PDItemSaveDTO.java @@ -80,5 +80,16 @@ public class PDItemSaveDTO { @Schema(description="备注") private String remark; + /** + * 出入库数量 + */ + @Schema(description="出入库数量") + private Double quantity; + + /** + * 包装单位id + */ + @Schema(description="包装单位id") + private Long unitId; } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java index 6bc4b7eae..5193643df 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpSpuController.java @@ -19,10 +19,8 @@ import com.bsd.pjcloud.supermarket.dto.SupermarketSpuUpdateStockReqDTO; import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto; import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuPageDto; import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportNewDto; -import com.bsd.pjcloud.supermarket.entity.SmErpAuxiliaryCodeEntity; -import com.bsd.pjcloud.supermarket.entity.SmErpSpuEntity; -import com.bsd.pjcloud.supermarket.entity.SmErpStockEntity; -import com.bsd.pjcloud.supermarket.entity.SmErpUnitEntity; +import com.bsd.pjcloud.supermarket.dto.inventoryrecords.*; +import com.bsd.pjcloud.supermarket.entity.*; import com.bsd.pjcloud.supermarket.service.*; import com.bsd.pjcloud.supermarket.vo.ad.SmErpSpuDetailsVo; import com.bsd.pjcloud.supermarket.vo.ad.SpuVo; @@ -72,7 +70,7 @@ public class SmErpSpuController { private final SmErpStockService smErpStockService; - + private final SmErpInventoryRecordsService smErpInventoryRecordsService; /** @@ -297,6 +295,27 @@ public class SmErpSpuController { return smErpSpuService.importErpSpuNewUpdate(collect, bindingResult); } + /** + * 导入盘点Excel数据 + * @param dtos 导入数据列表 + * @param bindingResult 错误信息列表 + * @return R + */ + @Operation(summary = "导入盘点Excel", description = "导入盘点Excel数据并生成盘点单") + @SysLog("导入盘点Excel") + @PostMapping("/importPD") + @PreAuthorize("@pms.hasPermission('supermarket_supermarketSpu_import')") + @Idempotent(expireTime = 10, info = "请勿重复导入,10秒后再重新尝试") + public R importPD(@RequestExcel List dtos, BindingResult bindingResult) { + List collect = dtos.stream() + .filter(dto -> StringUtils.isNotBlank(dto.getProductCode())) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(collect)) { + return R.failed("导入数据不能为空"); + } + return smErpSpuService.importPD(collect, bindingResult); + } + /** * 条件查询spu单表 列表信息 * @param supermarketSpuQueryDTO 商品spu diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java index f8d50fd06..f5c603720 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java @@ -114,7 +114,7 @@ public interface SmErpInventoryRecordsService extends IService { */ R importErpSpuNewUpdate(List smErpSpuImportNewDtos, BindingResult bindingResult); + /** + * 导入盘点Excel数据 + * @param dtos 导入数据列表 + * @param bindingResult 错误信息列表 + * @return R + */ + R importPD(List dtos, BindingResult bindingResult); + /** * 导出 diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java index c06d48731..5699750ab 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java @@ -106,8 +106,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl cgPlanItemEntities = BeanUtils.toBean(items, SmErpInventoryRecordsItemEntity.class); -// int sum1 = cgPlanItemEntities.stream().mapToInt(SmErpInventoryRecordsItemEntity::getTotalAmount).sum(); - List updateStockDTOS = new ArrayList<>(); for (SmErpInventoryRecordsItemEntity cgPlanItemEntity : cgPlanItemEntities) { cgPlanItemEntity.setInventoryId(smErpCgDdEntity.getId()); @@ -221,7 +219,9 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl inventoryTotalAmountMap = cgPlanItemEntities.stream() .collect(Collectors.groupingBy( SmErpInventoryRecordsItemEntity::getInventoryId, - Collectors.summingInt(SmErpInventoryRecordsItemEntity::getTotalAmount) + Collectors.summingInt(item -> + (int) (Math.abs(item.getQuantity()) * item.getUnitPrice()) + ) )); // 将计算出的 totalAmount 总和设置回 pageData 的 records 中 records.forEach(record -> { @@ -582,16 +582,14 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl items = pdSaveDTO.getItems(); int insert = baseMapper.insert(smErpCgDdEntity); - List cgPlanItemEntities = BeanUtils.toBean(items, SmErpInventoryRecordsItemEntity.class); List updateStockDTOS = new ArrayList<>(); List smErpInventoryRecordsItemEntities = new ArrayList<>(); for (PDItemSaveDTO item : items){ @@ -606,16 +604,21 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl smErpInventoryRecordsItemEntityList = new ArrayList<>(); @@ -436,6 +442,7 @@ public class SmErpSpuServiceImpl extends ServiceImpl smErpInventoryRecordsItemEntityList = new ArrayList<>(); @@ -1782,4 +1789,221 @@ public class SmErpSpuServiceImpl extends ServiceImpl dtos, BindingResult bindingResult) { + List> errorList = new ArrayList<>(); + List pdItemList = new ArrayList<>(); + int successCount = 0; + int total = dtos.size(); + + // 定义基准日期:2024年8月7日 + LocalDate baseDate = LocalDate.of(2024, 8, 7); + LocalDate currentDate = LocalDate.now(); + + // 查询从基准日期到当前日期的零售出库记录 + LambdaQueryWrapper retailWrapper = new LambdaQueryWrapper<>(); + retailWrapper.eq(SmErpInventoryRecordsEntity::getInventoryType, "5"); // LSCK 零售出库 + retailWrapper.ge(SmErpInventoryRecordsEntity::getInventoryDate, baseDate); + retailWrapper.le(SmErpInventoryRecordsEntity::getInventoryDate, currentDate); + retailWrapper.eq(SmErpInventoryRecordsEntity::getCheckStatus, "1"); // 已审核通过的记录 + + List retailRecords = smErpInventoryRecordsService.list(retailWrapper); + + // 收集所有零售记录的ID + List retailRecordIds = retailRecords.stream() + .map(SmErpInventoryRecordsEntity::getId) + .collect(Collectors.toList()); + + // 查询零售出库明细 + Map spuSalesMap = new HashMap<>(); + if (!retailRecordIds.isEmpty()) { + LambdaQueryWrapper itemWrapper = new LambdaQueryWrapper<>(); + itemWrapper.in(SmErpInventoryRecordsItemEntity::getInventoryId, retailRecordIds); + List retailItems = smErpInventoryRecordsItemService.list(itemWrapper); + + // 按商品ID汇总销量 + for (SmErpInventoryRecordsItemEntity item : retailItems) { + Long spuId = item.getErpSpuId(); + Double quantity = item.getQuantity(); + spuSalesMap.merge(spuId, quantity, Double::sum); + } + } + + for (int i = 0; i < dtos.size(); i++) { + PdExcelImportDto dto = dtos.get(i); + int rowNumber = i + 1; + List errorReasons = new ArrayList<>(); + + // 验证必填字段 + if (StringUtils.isBlank(dto.getProductCode())) { + errorReasons.add("货号不能为空"); + errorReasons.add("解决方案:请填写正确的商品货号"); + } + if (dto.getCheckQty() == null) { + errorReasons.add("盘点数量不能为空"); + errorReasons.add("解决方案:请填写实际盘点数量"); + } + if (dto.getPurchasePrice() == null) { + errorReasons.add("进价不能为空"); + errorReasons.add("解决方案:请填写商品进价"); + } + + if (!errorReasons.isEmpty()) { + errorList.add(createErrorInfo(rowNumber, dto.getProductCode(), errorReasons)); + continue; + } + + // 根据货号查找商品 + SmErpSpuEntity spu = this.getOne(new LambdaQueryWrapper() + .eq(SmErpSpuEntity::getProductCode, dto.getProductCode())); + + if (spu == null) { + errorReasons.add("货号:" + dto.getProductCode() + " 商品名称:" + dto.getProductName() + " - 商品不存在"); + errorReasons.add("解决方案:请先通过商品管理功能导入该商品的基础信息,或检查货号是否正确"); + errorList.add(createErrorInfo(rowNumber, dto.getProductCode(), errorReasons)); + continue; + } + + // 计算从8月7日到当前的零售销量 + Double salesQuantity = spuSalesMap.getOrDefault(spu.getId(), 0.0); + + // 计算真实的剩余库存:Excel盘点数量 - 零售销量 + Double checkQty = dto.getCheckQty() != null ? dto.getCheckQty() : 0.0; + Double realRemainingStock = checkQty - salesQuantity; + + // 检查是否已有期初数据 + SmErpStockEntity existingStock = smErpStockService.getOne(new LambdaQueryWrapper() + .eq(SmErpStockEntity::getErpSpuId, spu.getId())); + + // 确定账面数量(期初数量) + Double accountNumber; + // 每次盘点都支持期初调整,使用Excel中的期初数量 + accountNumber = dto.getInitialQty() != null ? dto.getInitialQty() : dto.getPurchaseTotalQty(); + // 如果期初数量为空,使用0作为默认值 + if (accountNumber == null) { + accountNumber = 0.0; + } + + // 创建盘点明细 + PDItemSaveDTO item = new PDItemSaveDTO(); + item.setErpSpuId(spu.getId()); + item.setAccountNumber(accountNumber); + item.setRealNumber(realRemainingStock); // 使用计算后的真实剩余库存 + // 处理价格,如果为空则使用0 + Double purchasePrice = dto.getPurchasePrice() != null ? dto.getPurchasePrice() : 0.0; + Double retailPrice = dto.getRetailPrice() != null ? dto.getRetailPrice() : purchasePrice; + item.setUnitPrice((int) (purchasePrice * 100)); // 转换为分 + item.setRealPrice((int) (retailPrice * 100)); // 转换为分 + + // 根据单位查找包装单位ID + Long unitId = null; + if (StringUtils.isNotBlank(dto.getUnit())) { + List unitList = smErpUnitService.list(new LambdaQueryWrapper() + .eq(SmErpUnitEntity::getSpuId, spu.getId()) + .eq(SmErpUnitEntity::getUnit, dto.getUnit())); + SmErpUnitEntity unit=null; + if (CollectionUtils.isNotEmpty( unitList)){ + unit = unitList.get(0); + } + if (unit != null) { + unitId = unit.getId(); + } else { + // 记录单位不匹配的警告信息 + String unitWarning = "警告:商品 '" + spu.getProductName() + "' 未找到单位 '" + dto.getUnit() + "' 的包装单位"; + if (item.getRemark() != null) { + item.setRemark(item.getRemark() + ";" + unitWarning); + } else { + item.setRemark(unitWarning); + } + } + } + item.setUnitId(unitId); + + // 计算盘盈盘亏 + Double difference = realRemainingStock - accountNumber; +// item.setDifference(difference); + + if (difference > 0) { + item.setProfit(difference); + item.setLoss(0.0); + item.setOrderType(OrderTypeEnum.RK.getType()); // 盘盈 + } else if (difference < 0) { + item.setProfit(0.0); + item.setLoss(Math.abs(difference)); + item.setOrderType(OrderTypeEnum.CK.getType()); // 盘亏 + } else { + item.setProfit(0.0); + item.setLoss(0.0); + item.setOrderType(OrderTypeEnum.No.getType()); // 无盈亏 + } + + item.setQuantity(Math.abs(difference)); + + // 计算盘后总数和总金额 + item.setTotalNumber(realRemainingStock.intValue()); + item.setTotalAmount((int) (retailPrice * 100 * realRemainingStock)); + + // 记录期初调整信息 + if (existingStock != null && !Objects.equals(existingStock.getBaseStock(), accountNumber)) { + String adjustmentNote = String.format("期初调整:原期初 %.2f → 新期初 %.2f", existingStock.getBaseStock(), accountNumber); + if (item.getRemark() != null) { + item.setRemark(item.getRemark() + ";" + adjustmentNote); + } else { + item.setRemark(adjustmentNote); + } + } + + // 记录零售销量信息 + if (salesQuantity > 0) { + String salesNote = String.format("零售销量:%.2f (8月7日至今)", salesQuantity); + if (item.getRemark() != null) { + item.setRemark(item.getRemark() + ";" + salesNote); + } else { + item.setRemark(salesNote); + } + } + + pdItemList.add(item); + successCount++; + } + + // 如果有成功的数据,则执行导入 + if (!pdItemList.isEmpty()) { + // 创建盘点主记录 + PDSaveDTO pdSaveDTO = new PDSaveDTO(); + pdSaveDTO.setInventoryCode("PD" + System.currentTimeMillis()); + pdSaveDTO.setInventoryDate(LocalDate.now()); + pdSaveDTO.setInventoryType("7"); // KCPD 库存盘点 + pdSaveDTO.setHandlerUserId(SecurityUtils.getUser().getId()); + pdSaveDTO.setItems(pdItemList); + + // 保存盘点记录 + int i = smErpInventoryRecordsService.savePD(pdSaveDTO); + if (i > 0) { + // 更新库存记录 + for (PDItemSaveDTO item : pdItemList) { + SmErpStockEntity stock = smErpStockService.getOne(new LambdaQueryWrapper() + .eq(SmErpStockEntity::getErpSpuId, item.getErpSpuId())); + + if (stock != null) { + stock.setBaseStock(item.getAccountNumber()); + stock.setCurrentStock(item.getRealNumber()); + smErpStockService.updateById(stock); + } else { + // 创建新的库存记录 + SmErpStockEntity newStock = new SmErpStockEntity(); + newStock.setErpSpuId(item.getErpSpuId()); + newStock.setBaseStock(item.getAccountNumber()); + newStock.setCurrentStock(item.getRealNumber()); + smErpStockService.save(newStock); + } + } + } + } + + // 返回结果:成功数量 + 错误列表 + return buildImportResult(total, successCount, errorList); + } + } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpStockServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpStockServiceImpl.java index abf2c73ff..364de448e 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpStockServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpStockServiceImpl.java @@ -41,6 +41,11 @@ public class SmErpStockServiceImpl extends ServiceImpl updateStockDTOS) { // 更新库存(手动乐观锁) for (UpdateStockDTO item : updateStockDTOS) { + // 确保incrCount不为null + if (item.getIncrCount() == null) { + item.setIncrCount(0.0); + } + // 1. 先查询当前版本号 LambdaQueryWrapper smErpStockEntityLambdaQueryWrapper = new LambdaQueryWrapper(); smErpStockEntityLambdaQueryWrapper.eq(SmErpStockEntity::getErpSpuId,item.getId());