fix(智慧超市):库存pd导入适配

This commit is contained in:
zjf committed 2025-08-08 17:38:22 +08:00
1 parent 77ede3ccd9
commit 918aa5bbd1
7 files changed
+291 -20

No files matched your search

@@ -80,5 +80,16 @@ public class PDItemSaveDTO {
@Schema(description="备注")
private String remark;
/**
* 出入库数量
*/
@Schema(description="出入库数量")
private Double quantity;
/**
* 包装单位id
*/
@Schema(description="包装单位id")
private Long unitId;
}
@@ -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<PdExcelImportDto> dtos, BindingResult bindingResult) {
List<PdExcelImportDto> 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
@@ -114,7 +114,7 @@ public interface SmErpInventoryRecordsService extends IService<SmErpInventoryRec
* @param pdSaveDTO
* @return
*/
Object savePD(PDSaveDTO pdSaveDTO);
int savePD(PDSaveDTO pdSaveDTO);
@@ -12,6 +12,7 @@ import com.bsd.pjcloud.supermarket.dto.cgplan.ErpCgPlanDTO;
import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto;
import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportNewDto;
import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuPageDto;
import com.bsd.pjcloud.supermarket.dto.inventoryrecords.PdExcelImportDto;
import com.bsd.pjcloud.supermarket.entity.SmErpSpuEntity;
import com.bsd.pjcloud.supermarket.vo.ad.SmErpSpuDetailsVo;
import com.bsd.pjcloud.supermarket.vo.ad.SpuVo;
@@ -129,6 +130,14 @@ public interface SmErpSpuService extends IService<SmErpSpuEntity> {
*/
R importErpSpuNewUpdate(List<SmErpSpuImportNewDto> smErpSpuImportNewDtos, BindingResult bindingResult);
/**
* 导入盘点Excel数据
* @param dtos 导入数据列表
* @param bindingResult 错误信息列表
* @return R
*/
R importPD(List<PdExcelImportDto> dtos, BindingResult bindingResult);
/**
* 导出
@@ -106,8 +106,6 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
int insert = baseMapper.insert(smErpCgDdEntity);
List<SmErpInventoryRecordsItemEntity> cgPlanItemEntities = BeanUtils.toBean(items, SmErpInventoryRecordsItemEntity.class);
// int sum1 = cgPlanItemEntities.stream().mapToInt(SmErpInventoryRecordsItemEntity::getTotalAmount).sum();
List<UpdateStockDTO> updateStockDTOS = new ArrayList<>();
for (SmErpInventoryRecordsItemEntity cgPlanItemEntity : cgPlanItemEntities) {
cgPlanItemEntity.setInventoryId(smErpCgDdEntity.getId());
@@ -221,7 +219,9 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
Map<Long, Integer> 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<SmErpInventory
@Override
@Transactional
public Object savePD(PDSaveDTO pdSaveDTO) {
public int savePD(PDSaveDTO pdSaveDTO) {
Long deptId = SecurityUtils.getUser().getDeptId();
SmErpInventoryRecordsEntity smErpCgDdEntity = BeanUtils.toBean(pdSaveDTO, SmErpInventoryRecordsEntity.class);
smErpCgDdEntity.setDeptId(deptId);
smErpCgDdEntity.setCheckStatus("0");
smErpCgDdEntity.setDeptId(deptId);
List<PDItemSaveDTO> items = pdSaveDTO.getItems();
int insert = baseMapper.insert(smErpCgDdEntity);
List<SmErpInventoryRecordsItemEntity> cgPlanItemEntities = BeanUtils.toBean(items, SmErpInventoryRecordsItemEntity.class);
List<UpdateStockDTO> updateStockDTOS = new ArrayList<>();
List<SmErpInventoryRecordsItemEntity> smErpInventoryRecordsItemEntities = new ArrayList<>();
for (PDItemSaveDTO item : items){
@@ -606,16 +604,21 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
smErpInventoryRecordsItemEntity.setDeptId(deptId);
String orderType = item.getOrderType();
smErpInventoryRecordsItemEntity.setOrderType(orderType);
// 使用从 DTO 传入的 quantity
Double quantity = item.getQuantity();
// 确保quantity不为null,如果为null则设为0
if (quantity == null) {
quantity = 0.0;
}
smErpInventoryRecordsItemEntity.setQuantity(quantity);
if (orderType.equals(OrderTypeEnum.RK.getType())){
Double profit = item.getProfit();
updateStockDTO.setIncrCount(profit);
smErpInventoryRecordsItemEntity.setQuantity(profit);
updateStockDTO.setIncrCount(quantity);
}else if (orderType.equals(OrderTypeEnum.CK.getType())) {
Double loss = item.getLoss();
updateStockDTO.setIncrCount(loss);
smErpInventoryRecordsItemEntity.setQuantity(loss);
updateStockDTO.setIncrCount(-quantity);
}else if (orderType.equals(OrderTypeEnum.No.getType())){
//无需更新库存
//无需更新库存,设置为0
updateStockDTO.setIncrCount(0.0);
}else {
throw new RuntimeException("订单类型错误");
}
@@ -623,7 +626,7 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl<SmErpInventory
smErpInventoryRecordsItemEntities.add(smErpInventoryRecordsItemEntity);
updateStockDTOS.add(updateStockDTO);
}
smErpInventoryRecordsItemMapper.insert(cgPlanItemEntities);
smErpInventoryRecordsItemMapper.insert(smErpInventoryRecordsItemEntities);
//todo 实际上应该审批后才会入库
//更新库存
@@ -22,6 +22,7 @@ import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportDto;
import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuImportNewDto;
import com.bsd.pjcloud.supermarket.dto.erpspu.SmErpSpuPageDto;
import com.bsd.pjcloud.supermarket.dto.inventoryrecords.UpdateStockDTO;
import com.bsd.pjcloud.supermarket.dto.inventoryrecords.*;
import com.bsd.pjcloud.supermarket.entity.*;
import com.bsd.pjcloud.supermarket.mapper.*;
import com.bsd.pjcloud.supermarket.service.*;
@@ -56,6 +57,7 @@ import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
@@ -92,6 +94,9 @@ public class SmErpSpuServiceImpl extends ServiceImpl<SmErpSpuMapper, SmErpSpuEnt
private final SmErpInventoryRecordsMapper smErpInventoryRecordsMapper;
private final SmErpInventoryRecordsItemMapper smErpInventoryRecordsItemMapper;
private final SmErpInventoryRecordsService smErpInventoryRecordsService;
private final SmErpInventoryRecordsItemService smErpInventoryRecordsItemService;
@Autowired
private Sequence sequence;
@@ -368,6 +373,7 @@ public class SmErpSpuServiceImpl extends ServiceImpl<SmErpSpuMapper, SmErpSpuEnt
smErpInventoryRecordsEntity.setInventoryType(InventoryTypeEnum.LSCK.getType());
smErpInventoryRecordsEntity.setOrderType(OrderTypeEnum.CK.getType());
smErpInventoryRecordsEntity.setDeviceType(updateStockReqDTO.getDeviceType());
smErpInventoryRecordsEntity.setDeptId(SecurityUtils.getUser().getDeptId());
List<SmErpInventoryRecordsItemEntity> smErpInventoryRecordsItemEntityList = new ArrayList<>();
@@ -436,6 +442,7 @@ public class SmErpSpuServiceImpl extends ServiceImpl<SmErpSpuMapper, SmErpSpuEnt
smErpInventoryRecordsEntity.setInventoryType(InventoryTypeEnum.LSCK.getType());
smErpInventoryRecordsEntity.setOrderType(OrderTypeEnum.CK.getType());
smErpInventoryRecordsEntity.setDeviceType(updateStockReqDTO.getDeviceType());
smErpInventoryRecordsEntity.setDeptId(SecurityUtils.getUser().getDeptId());
List<SmErpInventoryRecordsItemEntity> smErpInventoryRecordsItemEntityList = new ArrayList<>();
@@ -1782,4 +1789,221 @@ public class SmErpSpuServiceImpl extends ServiceImpl<SmErpSpuMapper, SmErpSpuEnt
}};
}
@Override
@Transactional(rollbackFor = Exception.class)
public R importPD(List<PdExcelImportDto> dtos, BindingResult bindingResult) {
List<Map<String, Object>> errorList = new ArrayList<>();
List<PDItemSaveDTO> 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<SmErpInventoryRecordsEntity> 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<SmErpInventoryRecordsEntity> retailRecords = smErpInventoryRecordsService.list(retailWrapper);
// 收集所有零售记录的ID
List<Long> retailRecordIds = retailRecords.stream()
.map(SmErpInventoryRecordsEntity::getId)
.collect(Collectors.toList());
// 查询零售出库明细
Map<Long, Double> spuSalesMap = new HashMap<>();
if (!retailRecordIds.isEmpty()) {
LambdaQueryWrapper<SmErpInventoryRecordsItemEntity> itemWrapper = new LambdaQueryWrapper<>();
itemWrapper.in(SmErpInventoryRecordsItemEntity::getInventoryId, retailRecordIds);
List<SmErpInventoryRecordsItemEntity> 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<String> 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<SmErpSpuEntity>()
.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<SmErpStockEntity>()
.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<SmErpUnitEntity> unitList = smErpUnitService.list(new LambdaQueryWrapper<SmErpUnitEntity>()
.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<SmErpStockEntity>()
.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);
}
}
@@ -41,6 +41,11 @@ public class SmErpStockServiceImpl extends ServiceImpl<SmErpStockMapper, SmErpSt
public void updateStock(List<UpdateStockDTO> updateStockDTOS) {
// 更新库存(手动乐观锁)
for (UpdateStockDTO item : updateStockDTOS) {
// 确保incrCount不为null
if (item.getIncrCount() == null) {
item.setIncrCount(0.0);
}
// 1. 先查询当前版本号
LambdaQueryWrapper<SmErpStockEntity> smErpStockEntityLambdaQueryWrapper = new LambdaQueryWrapper<SmErpStockEntity>();
smErpStockEntityLambdaQueryWrapper.eq(SmErpStockEntity::getErpSpuId,item.getId());