APP订单列表显示未支付取消的订单 库存管理一些问题修改
This commit is contained in:
@@ -161,6 +161,9 @@ public class InventoryManagementController {
|
||||
titleRow.createCell(5).setCellValue("订单编号");
|
||||
titleRow.createCell(6).setCellValue("出库时间");
|
||||
titleRow.createCell(7).setCellValue("come");
|
||||
titleRow.createCell(8).setCellValue("套书名称");
|
||||
titleRow.createCell(9).setCellValue("套书商品ID");
|
||||
titleRow.createCell(10).setCellValue("出库记录ID");
|
||||
int rowIndex = 1;
|
||||
for (Map<String, Object> map : maps) {
|
||||
Row row = sheet.createRow(rowIndex++);
|
||||
@@ -172,6 +175,9 @@ public class InventoryManagementController {
|
||||
row.createCell(5).setCellValue(cellStr(map.get("orderSn")));
|
||||
row.createCell(6).setCellValue(cellStr(map.get("createTime")));
|
||||
row.createCell(7).setCellValue(cellStr(map.get("come")));
|
||||
row.createCell(8).setCellValue(cellStr(map.get("setProductName")));
|
||||
row.createCell(9).setCellValue(cellStr(map.get("setProductId")));
|
||||
row.createCell(10).setCellValue(cellStr(map.get("stockRecordId")));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,6 @@ import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -231,8 +230,10 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
String outboundType = resolveOutboundType(dto.getOutboundType());
|
||||
List<CostSplit> splits = deductPurchaseStockFifo(dto.getProductId(), dto.getMerchantId(), dto.getQuantity());
|
||||
for (CostSplit split : splits) {
|
||||
BigDecimal saleUnitCost = (dto.getUnitCost() != null && dto.getUnitCost().compareTo(BigDecimal.ZERO) > 0)
|
||||
? dto.getUnitCost() : split.unitCost;
|
||||
insertSaleRecord(dto.getProductId(), dto.getMerchantId(), outboundType,
|
||||
-split.quantity, split.unitCost, dto.getRemark(), null, null, null, split.purchaseRecordId);
|
||||
-split.quantity, saleUnitCost, dto.getRemark(), null, null, null, split.purchaseRecordId);
|
||||
}
|
||||
adjustShopProductStock(dto.getProductId(), -dto.getQuantity());
|
||||
return;
|
||||
@@ -244,12 +245,13 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
}
|
||||
|
||||
// 采购:跨商户调拨 = 来源方按成本批次多条出库 + 本商户一条采购(成本取接口传值)
|
||||
// 灵枢→众秒:灵枢出库 outbound_type=zm;众秒→灵枢:众秒出库 outbound_type=ls
|
||||
if (isCrossMerchantTransfer(dto.getMerchantId(), dto.getSource())) {
|
||||
int sourceMerchantId = sourceToMerchantId(dto.getSource());
|
||||
String saleTargetSource = merchantIdToSource(dto.getMerchantId());
|
||||
List<CostSplit> splits = deductPurchaseStockFifo(dto.getProductId(), sourceMerchantId, dto.getQuantity());
|
||||
for (CostSplit split : splits) {
|
||||
insertRecord(dto.getProductId(), sourceMerchantId, saleTargetSource, null,
|
||||
insertRecord(dto.getProductId(), sourceMerchantId, saleTargetSource, saleTargetSource,
|
||||
InvStockRecord.BIZ_TYPE_SALE, -split.quantity, split.unitCost, dto.getRemark(),
|
||||
null, null, null, split.purchaseRecordId, null);
|
||||
}
|
||||
@@ -1166,7 +1168,6 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
return new ArrayList<>();
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
Map<Long, Integer> setPurchaseQtyCache = new HashMap<>();
|
||||
Map<Long, List<InvStockRecord>> bindCache = new HashMap<>();
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (Map<String, Object> item : list) {
|
||||
@@ -1175,10 +1176,11 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
continue;
|
||||
}
|
||||
if (recordBizType == InvStockRecord.BIZ_TYPE_SALE) {
|
||||
// 套书绑定内部出库不进入售出明细导出
|
||||
if (isSetBookBindOutbound(item.get("outboundType"))) {
|
||||
continue;
|
||||
}
|
||||
result.addAll(expandSaleRecordForExport(item, sdf, setPurchaseQtyCache, bindCache));
|
||||
result.addAll(expandSaleRecordForExport(item, sdf, bindCache));
|
||||
} else if (recordBizType == InvStockRecord.BIZ_TYPE_PURCHASE) {
|
||||
result.add(buildPurchaseExportRow(item, sdf));
|
||||
}
|
||||
@@ -1209,50 +1211,93 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
row.put("orderSn", item.get("orderSn") == null ? "" : item.get("orderSn"));
|
||||
row.put("createTime", formatExportTime(item.get("createTime"), sdf));
|
||||
row.put("come", item.get("come"));
|
||||
row.put("setProductId", "");
|
||||
row.put("setProductName", "");
|
||||
row.put("stockRecordId", item.get("id"));
|
||||
return row;
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> expandSaleRecordForExport(Map<String, Object> item, SimpleDateFormat sdf,
|
||||
Map<Long, Integer> setPurchaseQtyCache,
|
||||
Map<Long, List<InvStockRecord>> bindCache) {
|
||||
Integer productId = toIntegerSafe(item.get("productId"));
|
||||
Long purchaseRecordId = toLongSafe(item.get("purchaseRecordId"));
|
||||
if (productId != null && purchaseRecordId != null && isSetBookProductByProductId(productId)) {
|
||||
List<InvStockRecord> binds = bindCache.computeIfAbsent(purchaseRecordId, this::listSetBookComponentOutbounds);
|
||||
if (!binds.isEmpty()) {
|
||||
int saleQty = Math.abs(toIntValue(item.get("quantity")));
|
||||
int setPurchaseQty = setPurchaseQtyCache.computeIfAbsent(purchaseRecordId, this::resolveSetPurchaseQuantity);
|
||||
return buildSetBookExpandedSaleRows(item, binds, saleQty, setPurchaseQty, sdf);
|
||||
}
|
||||
if (productId == null || !isSetBookProductByProductId(productId)) {
|
||||
return Collections.singletonList(buildSaleExportRow(item, sdf));
|
||||
}
|
||||
return Collections.singletonList(buildSaleExportRow(item, sdf));
|
||||
List<ShopProduct> components = listSetBookComponentProducts(productId);
|
||||
if (components.isEmpty()) {
|
||||
return Collections.singletonList(buildSaleExportRow(item, sdf));
|
||||
}
|
||||
int saleQty = Math.abs(toIntValue(item.get("quantity")));
|
||||
Integer merchantId = toIntegerSafe(item.get("merchantId"));
|
||||
// 套书售出流水的 purchase_record_id = 套书采购流水 id,对应单品出库的 set_purchase_record_id
|
||||
Long setPurchaseRecordId = toLongSafe(item.get("purchaseRecordId"));
|
||||
Map<Integer, BigDecimal> costByProductId = Collections.emptyMap();
|
||||
if (setPurchaseRecordId != null) {
|
||||
List<InvStockRecord> binds = bindCache.computeIfAbsent(setPurchaseRecordId, this::listSetBookComponentOutbounds);
|
||||
costByProductId = resolveComponentCostFromBinds(binds);
|
||||
}
|
||||
String setProductName = item.get("productName") == null ? "" : item.get("productName").toString();
|
||||
return buildSetBookExpandedSaleRows(item, components, saleQty, merchantId, costByProductId,
|
||||
productId, setProductName, sdf);
|
||||
}
|
||||
|
||||
private List<Map<String, Object>> buildSetBookExpandedSaleRows(Map<String, Object> parent,
|
||||
List<InvStockRecord> binds,
|
||||
int saleQty, int setPurchaseQty,
|
||||
SimpleDateFormat sdf) {
|
||||
Map<Integer, String> productNames = loadProductNameMap(
|
||||
binds.stream().map(InvStockRecord::getProductId).filter(Objects::nonNull).collect(Collectors.toSet()));
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
for (InvStockRecord bind : binds) {
|
||||
int bindQty = bind.getQuantity() == null ? 0 : Math.abs(bind.getQuantity());
|
||||
int exportQty = setPurchaseQty > 0
|
||||
? (int) Math.round(bindQty * (double) saleQty / setPurchaseQty)
|
||||
: saleQty;
|
||||
if (exportQty <= 0) {
|
||||
/** 套书关联图书 → 只取「只绑了 1 本书」的单品商品 */
|
||||
private List<ShopProduct> listSetBookComponentProducts(Integer setProductId) {
|
||||
List<Integer> bookIds = listBookIdsByProductId(setProductId);
|
||||
if (bookIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<ShopProduct> components = new ArrayList<>();
|
||||
Set<Integer> seen = new HashSet<>();
|
||||
for (Integer bookId : bookIds) {
|
||||
ShopProduct component = findComponentProductByBookId(bookId, setProductId);
|
||||
if (component == null || component.getProductId() == null) {
|
||||
continue;
|
||||
}
|
||||
// findComponentProductByBookId 已优先只绑 1 本书;再兜底确认
|
||||
if (listBookIdsByProductId(component.getProductId()).size() != 1) {
|
||||
continue;
|
||||
}
|
||||
if (seen.add(component.getProductId())) {
|
||||
components.add(component);
|
||||
}
|
||||
}
|
||||
return components;
|
||||
}
|
||||
|
||||
/**
|
||||
* 套书售出导出:按套内单品展开,数量=套书出库数量;
|
||||
* 成本优先:单品 books 出库.purchase_record_id → 入库记录.unit_cost;
|
||||
* 无 set_purchase_record_id / 无绑定时:inv_product_cost.cost_price。
|
||||
*/
|
||||
private List<Map<String, Object>> buildSetBookExpandedSaleRows(Map<String, Object> parent,
|
||||
List<ShopProduct> components,
|
||||
int saleQty,
|
||||
Integer merchantId,
|
||||
Map<Integer, BigDecimal> costByProductId,
|
||||
Integer setProductId,
|
||||
String setProductName,
|
||||
SimpleDateFormat sdf) {
|
||||
List<Map<String, Object>> rows = new ArrayList<>();
|
||||
for (ShopProduct component : components) {
|
||||
Integer componentId = component.getProductId();
|
||||
BigDecimal unitCost = costByProductId == null ? null : costByProductId.get(componentId);
|
||||
if (unitCost == null) {
|
||||
unitCost = lookupInvProductCostPrice(componentId, merchantId);
|
||||
}
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("exportCategory", "sale");
|
||||
row.put("productId", bind.getProductId());
|
||||
row.put("productName", productNames.getOrDefault(bind.getProductId(), ""));
|
||||
row.put("productId", componentId);
|
||||
row.put("productName", component.getProductName() == null ? "" : component.getProductName());
|
||||
row.put("outboundTypeLabel", parent.get("outboundTypeLabel"));
|
||||
row.put("unitCost", bind.getUnitCost());
|
||||
row.put("quantity", exportQty);
|
||||
row.put("unitCost", unitCost);
|
||||
row.put("quantity", saleQty);
|
||||
row.put("orderSn", parent.get("orderSn") == null ? "" : parent.get("orderSn"));
|
||||
row.put("createTime", formatExportTime(parent.get("createTime"), sdf));
|
||||
row.put("come", parent.get("come"));
|
||||
row.put("setProductId", setProductId);
|
||||
row.put("setProductName", setProductName == null ? "" : setProductName);
|
||||
row.put("stockRecordId", parent.get("id"));
|
||||
rows.add(row);
|
||||
}
|
||||
if (rows.isEmpty()) {
|
||||
@@ -1261,15 +1306,76 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
return rows;
|
||||
}
|
||||
|
||||
private int resolveSetPurchaseQuantity(Long setPurchaseRecordId) {
|
||||
if (setPurchaseRecordId == null) {
|
||||
return 0;
|
||||
/**
|
||||
* 根据套书绑定的单品出库流水:purchase_record_id → 入库记录取 unit_cost;
|
||||
* 同一单品多条 FIFO 按数量加权平均。
|
||||
*/
|
||||
private Map<Integer, BigDecimal> resolveComponentCostFromBinds(List<InvStockRecord> binds) {
|
||||
if (binds == null || binds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
InvStockRecord purchase = invStockRecordDao.selectById(setPurchaseRecordId);
|
||||
if (purchase == null || purchase.getQuantity() == null) {
|
||||
return 0;
|
||||
Map<Integer, BigDecimal> costSum = new HashMap<>();
|
||||
Map<Integer, Integer> qtySum = new HashMap<>();
|
||||
Map<Long, BigDecimal> purchaseCostCache = new HashMap<>();
|
||||
for (InvStockRecord bind : binds) {
|
||||
Integer componentId = bind.getProductId();
|
||||
if (componentId == null) {
|
||||
continue;
|
||||
}
|
||||
int qty = bind.getQuantity() == null ? 0 : Math.abs(bind.getQuantity());
|
||||
if (qty <= 0) {
|
||||
continue;
|
||||
}
|
||||
BigDecimal unitCost = resolveInboundUnitCost(bind.getPurchaseRecordId(), purchaseCostCache);
|
||||
if (unitCost == null) {
|
||||
unitCost = bind.getUnitCost();
|
||||
}
|
||||
if (unitCost == null) {
|
||||
continue;
|
||||
}
|
||||
costSum.merge(componentId, unitCost.multiply(BigDecimal.valueOf(qty)), BigDecimal::add);
|
||||
qtySum.merge(componentId, qty, Integer::sum);
|
||||
}
|
||||
return Math.abs(purchase.getQuantity());
|
||||
Map<Integer, BigDecimal> result = new HashMap<>();
|
||||
for (Map.Entry<Integer, BigDecimal> entry : costSum.entrySet()) {
|
||||
int qty = qtySum.getOrDefault(entry.getKey(), 0);
|
||||
if (qty <= 0) {
|
||||
continue;
|
||||
}
|
||||
result.put(entry.getKey(),
|
||||
entry.getValue().divide(BigDecimal.valueOf(qty), 2, java.math.RoundingMode.HALF_UP));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal resolveInboundUnitCost(Long purchaseRecordId, Map<Long, BigDecimal> cache) {
|
||||
if (purchaseRecordId == null) {
|
||||
return null;
|
||||
}
|
||||
if (cache.containsKey(purchaseRecordId)) {
|
||||
return cache.get(purchaseRecordId);
|
||||
}
|
||||
InvStockRecord purchase = invStockRecordDao.selectById(purchaseRecordId);
|
||||
BigDecimal unitCost = null;
|
||||
if (purchase != null && purchase.getBizType() != null
|
||||
&& purchase.getBizType() == InvStockRecord.BIZ_TYPE_PURCHASE) {
|
||||
unitCost = purchase.getUnitCost();
|
||||
}
|
||||
cache.put(purchaseRecordId, unitCost);
|
||||
return unitCost;
|
||||
}
|
||||
|
||||
private BigDecimal lookupInvProductCostPrice(Integer productId, Integer merchantId) {
|
||||
if (productId == null || merchantId == null) {
|
||||
return null;
|
||||
}
|
||||
InvProductCost cost = invProductCostDao.selectOne(
|
||||
new LambdaQueryWrapper<InvProductCost>()
|
||||
.eq(InvProductCost::getProductId, productId)
|
||||
.eq(InvProductCost::getMerchantId, merchantId)
|
||||
.last("LIMIT 1")
|
||||
);
|
||||
return cost == null ? null : cost.getCostPrice();
|
||||
}
|
||||
|
||||
private List<InvStockRecord> listSetBookComponentOutbounds(Long setPurchaseRecordId) {
|
||||
@@ -1290,26 +1396,6 @@ public class InventoryManagementServiceImpl extends ServiceImpl<InvStockRecordDa
|
||||
return listBookIdsByProductId(productId).size() > 1;
|
||||
}
|
||||
|
||||
private Map<Integer, String> loadProductNameMap(Set<Integer> productIds) {
|
||||
if (productIds == null || productIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<ShopProduct> products = shopProductDao.selectList(
|
||||
new LambdaQueryWrapper<ShopProduct>()
|
||||
.select(ShopProduct::getProductId, ShopProduct::getProductName)
|
||||
.in(ShopProduct::getProductId, productIds)
|
||||
);
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
if (products != null) {
|
||||
for (ShopProduct product : products) {
|
||||
if (product.getProductId() != null) {
|
||||
map.put(product.getProductId(), product.getProductName());
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private String formatExportTime(Object createTime, SimpleDateFormat sdf) {
|
||||
if (createTime instanceof Date) {
|
||||
return sdf.format((Date) createTime);
|
||||
|
||||
@@ -61,11 +61,11 @@ public class OrderCancelConsumer {
|
||||
if (!Constants.ORDER_STATUS_TO_BE_PAID.equals(buyOrder.getOrderStatus())) {
|
||||
return;
|
||||
}
|
||||
// TODO 上线全量后删除此测试账号限制
|
||||
/* // TODO 上线全量后删除此测试账号限制
|
||||
if (!isTesterOrder(buyOrder)) {
|
||||
log.info("订单超时取消跳过:非测试账号, orderId={}, userId={}", buyOrder.getOrderId(), buyOrder.getUserId());
|
||||
return;
|
||||
}
|
||||
}*/
|
||||
buyOrder.setOrderStatus(Constants.ORDER_STATUS_OUT_OF_TIME);
|
||||
|
||||
// 回滚优惠券(buy_order.coupon_id 存的是 coupon_history.id),保留订单 couponId 便于查单
|
||||
|
||||
Reference in New Issue
Block a user