|
@@ -1,14 +1,25 @@
|
|
|
package com.jsh.erp.service;
|
|
|
|
|
|
+import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONArray;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
|
|
|
+import com.jsh.erp.constants.ExceptionConstants;
|
|
|
import com.jsh.erp.datasource.entities.DepotHeadVo4Body;
|
|
|
import com.jsh.erp.datasource.entities.MaterialVo4Unit;
|
|
|
+import com.jsh.erp.datasource.entities.Supplier;
|
|
|
+import com.jsh.erp.datasource.entities.Unit;
|
|
|
+import com.jsh.erp.datasource.tesco.request.ErpXsddReqVO;
|
|
|
import com.jsh.erp.datasource.vo.DepotHeadXsddRequestVO;
|
|
|
import com.jsh.erp.datasource.vo.DepotItemXsddRequestVO;
|
|
|
+import com.jsh.erp.datasource.vo.Material4UnitPrice;
|
|
|
+import com.jsh.erp.exception.BusinessRunTimeException;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+import org.springframework.util.CollectionUtils;
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
import java.math.BigDecimal;
|
|
@@ -38,6 +49,9 @@ public class SyncTescoSystemService {
|
|
|
@Resource
|
|
|
private SequenceService sequenceService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private SupplierService supplierService;
|
|
|
+
|
|
|
public String syncSystemSku(String param) {
|
|
|
logger.info("获取商品系统sku,返回信息集采系统 param:{}", param);
|
|
|
JSONObject result = new JSONObject();
|
|
@@ -52,7 +66,7 @@ public class SyncTescoSystemService {
|
|
|
}
|
|
|
|
|
|
// 解析传入的JSON参数
|
|
|
- JSONArray barCodeArray = JSONArray.parseArray(param);
|
|
|
+ JSONArray barCodeArray = JSON.parseArray(param);
|
|
|
if (barCodeArray == null || barCodeArray.isEmpty()) {
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "参数格式错误");
|
|
@@ -82,13 +96,14 @@ public class SyncTescoSystemService {
|
|
|
result.put("msg", "未找到对应商品信息");
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
+ e.printStackTrace();
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "系统异常,请稍后重试");
|
|
|
}
|
|
|
|
|
|
// 将数据数组放入返回结果
|
|
|
result.put("data", dataArray);
|
|
|
- logger.info(" result:{}", param);
|
|
|
+ logger.info(" result:{}", result);
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
@@ -118,39 +133,37 @@ public class SyncTescoSystemService {
|
|
|
logger.info("同步集采订单-》销售订单 param:{}", param);
|
|
|
JSONObject result = new JSONObject();
|
|
|
try {
|
|
|
- // 解析传入的JSON参数
|
|
|
- JSONObject paramJson = JSONObject.parseObject(param);
|
|
|
- if (paramJson == null || paramJson.isEmpty()) {
|
|
|
+ // 解析传入的JSON参数为 ErpXsddReqVO
|
|
|
+ ErpXsddReqVO erpXsddReqVO = JSON.parseObject(param, ErpXsddReqVO.class);
|
|
|
+ if (erpXsddReqVO == null) {
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "参数不能为空");
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
|
// 校验订单编号
|
|
|
- String orderSn = paramJson.getString("orderSn");
|
|
|
- if (orderSn == null || orderSn.isEmpty()) {
|
|
|
+ if (erpXsddReqVO.getOrderSn() == null || erpXsddReqVO.getOrderSn().isEmpty()) {
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "订单编号不能为空");
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
|
// 校验客户信息
|
|
|
- JSONObject customer = paramJson.getJSONObject("customer");
|
|
|
- if (customer == null || customer.isEmpty()) {
|
|
|
+ if (erpXsddReqVO.getCustomer() == null) {
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "客户信息不能为空");
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
|
// 校验订单商品信息
|
|
|
- JSONArray itemArray = paramJson.getJSONArray("item");
|
|
|
- if (itemArray == null || itemArray.isEmpty()) {
|
|
|
+ if (erpXsddReqVO.getItems() == null || erpXsddReqVO.getItems().isEmpty()) {
|
|
|
result.put("code", 1);
|
|
|
result.put("msg", "订单商品信息不能为空");
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
|
- buildOrderAndItem(paramJson, itemArray);
|
|
|
+ // 构建订单和商品信息
|
|
|
+ buildOrderAndItem(erpXsddReqVO);
|
|
|
|
|
|
// 返回成功结果
|
|
|
result.put("code", 0);
|
|
@@ -163,107 +176,449 @@ public class SyncTescoSystemService {
|
|
|
return result.toJSONString();
|
|
|
}
|
|
|
|
|
|
- private void buildOrderAndItem(JSONObject orderJson, JSONArray itemJsonArray) throws Exception {
|
|
|
+ @Transactional(value = "transactionManager", rollbackFor = Exception.class)
|
|
|
+ private void buildOrderAndItem(ErpXsddReqVO erpXsddReqVO) throws Exception {
|
|
|
+ // 1. 初始化订单基本信息
|
|
|
+ DepotHeadXsddRequestVO order = initSalesOrder(erpXsddReqVO);
|
|
|
+
|
|
|
+ // 2. 处理订单商品项
|
|
|
+ List<DepotItemXsddRequestVO> itemList = processOrderItems(erpXsddReqVO, order);
|
|
|
+
|
|
|
+ // 3. 提交订单
|
|
|
+ depotHeadService.syncOrderToXsdd(order, itemList);
|
|
|
+ }
|
|
|
+
|
|
|
+ private DepotHeadXsddRequestVO initSalesOrder(ErpXsddReqVO erpXsddReqVO) throws Exception {
|
|
|
DepotHeadXsddRequestVO order = new DepotHeadXsddRequestVO();
|
|
|
- order.setType("其他");
|
|
|
+ order.setType("其它");
|
|
|
order.setSubType("销售订单");
|
|
|
- String orderSn = orderJson.getString("orderSn");
|
|
|
+ order.setLinkTesco(erpXsddReqVO.getOrderSn());
|
|
|
+
|
|
|
+ ErpXsddReqVO.Customer customer = erpXsddReqVO.getCustomer();
|
|
|
+ // 根据手机号查询客户
|
|
|
+ Supplier supplier = supplierService.getCustomerByPhone(customer.getAccount());
|
|
|
+ if (supplier == null) {
|
|
|
+ // 如果客户不存在,创建新客户
|
|
|
+ supplier = new Supplier();
|
|
|
+ supplier.setSupplier(customer.getName());
|
|
|
+ supplier.setTelephone(customer.getAccount());
|
|
|
+ supplier.setContacts(customer.getReceiverName());
|
|
|
+ supplier.setPhoneNum(customer.getReceiverPhone());
|
|
|
+ supplier.setAddress(customer.getReceiverAddress());
|
|
|
+ supplier = supplierService.createCustomer(supplier);
|
|
|
+ }
|
|
|
|
|
|
+ // 绑定客户 ID
|
|
|
+ order.setOrganId(supplier.getId());
|
|
|
+ order.setReceiverName(customer.getReceiverName());
|
|
|
+ order.setReceiverPhone(customer.getReceiverPhone());
|
|
|
+ order.setReceiverAddress(customer.getReceiverAddress());
|
|
|
+ order.setRemark("集采同步订单");
|
|
|
String number = sequenceService.buildOnlyNumber();
|
|
|
String defaultNumber = "XSDD" + number;
|
|
|
order.setDefaultNumber(defaultNumber);
|
|
|
order.setNumber(defaultNumber);
|
|
|
order.setCreateTime(new Date());
|
|
|
order.setOperTime(new Date());
|
|
|
- order.setOrganId(1L);// 供应商
|
|
|
- order.setCreator(1L);// 创建人
|
|
|
- order.setAccountId(1L);// 账户
|
|
|
+ order.setCreator(1L); // 创建人ID
|
|
|
+ order.setAccountId(1L); // 账户ID
|
|
|
order.setChangeAmount(BigDecimal.ZERO);
|
|
|
order.setBackAmount(BigDecimal.ZERO);
|
|
|
- order.setTotalPrice(orderJson.getBigDecimal("totalPrice"));
|
|
|
+ order.setTotalPrice(erpXsddReqVO.getTotalPrice());
|
|
|
order.setPayType("现付");
|
|
|
order.setDiscount(BigDecimal.ZERO);
|
|
|
order.setDiscountMoney(BigDecimal.ZERO);
|
|
|
- order.setDiscountLastMoney(orderJson.getBigDecimal("totalPrice"));
|
|
|
+ order.setDiscountLastMoney(erpXsddReqVO.getTotalPrice());
|
|
|
order.setStatus("0");
|
|
|
order.setPurchaseStatus("0");
|
|
|
- order.setSource("2");// 来源集采商城
|
|
|
+ order.setSource("2"); // 来源集采商城
|
|
|
order.setTenantId(null);
|
|
|
order.setDeleteFlag("0");
|
|
|
+ return order;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理订单商品项
|
|
|
+ */
|
|
|
+ private List<DepotItemXsddRequestVO> processOrderItems(ErpXsddReqVO erpXsddReqVO, DepotHeadXsddRequestVO order) throws Exception {
|
|
|
|
|
|
List<DepotItemXsddRequestVO> itemList = new ArrayList<>();
|
|
|
+ List<ErpXsddReqVO.OrderItem> reqVOItems = erpXsddReqVO.getItems();
|
|
|
|
|
|
- List<String> systemSkuList = itemJsonArray.stream().map(JSONObject.class::cast).map(json -> json.getString("erp_sku")).collect(Collectors.toList());
|
|
|
+ // 1. 获取所有商品信息
|
|
|
+ List<String> systemSkuList = reqVOItems.stream().map(ErpXsddReqVO.OrderItem::getErpSku).collect(Collectors.toList());
|
|
|
+ List<MaterialVo4Unit> materialList = materialService.getMaterialBySystemSku(systemSkuList);
|
|
|
+ Map<String, List<MaterialVo4Unit>> skuToMaterialMap = materialList.stream().collect(Collectors.groupingBy(MaterialVo4Unit::getSystemSku));
|
|
|
|
|
|
- Map<String, JSONObject> skuToJsonMap = itemJsonArray.stream().map(JSONObject.class::cast).collect(Collectors.toMap(json -> json.getString("erp_sku"), json -> json));
|
|
|
+ // 2. 处理每个订单项
|
|
|
+ for (ErpXsddReqVO.OrderItem reqVOItem : reqVOItems) {
|
|
|
+ try {
|
|
|
+ processSingleOrderItem(reqVOItem, order, itemList, skuToMaterialMap);
|
|
|
+ } catch (Exception e) {
|
|
|
+ logger.error("处理订单项失败 erp_sku: {}: {}", reqVOItem.getErpSku(), e.getMessage());
|
|
|
+ throw e;
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- List<MaterialVo4Unit> materialList = materialService.getMaterialBySystemSku(systemSkuList);
|
|
|
+ return itemList;
|
|
|
+ }
|
|
|
|
|
|
- Map<String, List<MaterialVo4Unit>> skuToMaterialMap = materialList.stream().collect(Collectors.groupingBy(MaterialVo4Unit::getSystemSku));
|
|
|
|
|
|
- for (Map.Entry<String, JSONObject> entry : skuToJsonMap.entrySet()) {
|
|
|
- String erpSku = entry.getKey(); // 获取 erp_sku
|
|
|
- JSONObject itemJson = entry.getValue(); // 获取对应的 JSONObject
|
|
|
+ /**
|
|
|
+ * 处理单个订单项
|
|
|
+ */
|
|
|
+ private void processSingleOrderItem(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, Map<String, List<MaterialVo4Unit>> skuToMaterialMap) throws Exception {
|
|
|
|
|
|
- Integer quantity = itemJson.getInteger("quantity");
|
|
|
- BigDecimal unitPrice = itemJson.getBigDecimal("unitPrice");
|
|
|
- BigDecimal price = itemJson.getBigDecimal("price");
|
|
|
+ // 1. 参数校验
|
|
|
+ validateOrderItem(reqVOItem);
|
|
|
|
|
|
- if (quantity == null || unitPrice == null || price == null) {
|
|
|
- logger.warn("商品信息不完整,erp_sku: {}", erpSku);
|
|
|
+ // 2. 获取商品信息
|
|
|
+ List<MaterialVo4Unit> materials = skuToMaterialMap.get(reqVOItem.getErpSku());
|
|
|
+ if (CollectionUtils.isEmpty(materials)) {
|
|
|
+ throw new BusinessRunTimeException(ExceptionConstants.MATERIAL_ERP_SKU_NOT_DECIMAL_CODE, String.format(ExceptionConstants.MATERIAL_ERP_SKU_NOT_DECIMAL_MSG, reqVOItem.getErpSku()));
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 获取单位信息
|
|
|
+ MaterialVo4Unit primaryMaterial = materials.get(0);
|
|
|
+ Material4UnitPrice material4UnitPrice = getMaterial4UnitPrice(primaryMaterial);
|
|
|
+
|
|
|
+ // 4. 匹配销售单位
|
|
|
+ UnitMatchResult matchResult = matchUnitAndCalculateBaseQuantity(reqVOItem.getUnitPrice(), BigDecimal.valueOf(reqVOItem.getQuantity()), material4UnitPrice);
|
|
|
+
|
|
|
+ // 5. 根据单位类型分配库存
|
|
|
+ if (matchResult.isLargeUnit()) {
|
|
|
+ allocateLargeUnitStock(reqVOItem, order, itemList, materials, matchResult, material4UnitPrice);
|
|
|
+ } else {
|
|
|
+ allocateBasicUnitStock(reqVOItem, order, itemList, materials, matchResult);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分配大单位库存(不拆分配)
|
|
|
+ */
|
|
|
+ private void allocateLargeUnitStock(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, List<MaterialVo4Unit> materials,
|
|
|
+ UnitMatchResult matchResult, Material4UnitPrice material4UnitPrice) throws Exception {
|
|
|
+
|
|
|
+ String orderUnit = matchResult.getMatchedUnit();
|
|
|
+ BigDecimal unitRatio = getUnitRatio(orderUnit, material4UnitPrice);
|
|
|
+ BigDecimal remainingOrderQty = matchResult.getOrderUnitQuantity();
|
|
|
+
|
|
|
+ // 分配库存
|
|
|
+ for (MaterialVo4Unit m : materials) {
|
|
|
+ if (remainingOrderQty.compareTo(BigDecimal.ZERO) <= 0)
|
|
|
+ break;
|
|
|
+
|
|
|
+ BigDecimal inventory = m.getInventory();
|
|
|
+ if (inventory.compareTo(unitRatio) < 0)
|
|
|
continue;
|
|
|
+
|
|
|
+ // 计算可分配数量(向下取整)
|
|
|
+ BigDecimal allocatableUnits = inventory.divideToIntegralValue(unitRatio).min(remainingOrderQty);
|
|
|
+
|
|
|
+ if (allocatableUnits.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ BigDecimal allocBaseQty = allocatableUnits.multiply(unitRatio);
|
|
|
+
|
|
|
+ // 创建订单项
|
|
|
+ DepotItemXsddRequestVO item = createOrderItem(order, m, orderUnit, allocatableUnits, allocBaseQty, reqVOItem.getUnitPrice());
|
|
|
+ itemList.add(item);
|
|
|
+
|
|
|
+ // 更新库存
|
|
|
+ m.setInventory(inventory.subtract(allocBaseQty));
|
|
|
+
|
|
|
+ // 更新剩余需求
|
|
|
+ remainingOrderQty = remainingOrderQty.subtract(allocatableUnits);
|
|
|
+
|
|
|
+ logger.info("分配库存:批次[{}] 分配 {} {} (剩余需求: {})", m.getBatchNumber(), allocatableUnits.toPlainString(), orderUnit, remainingOrderQty.toPlainString());
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- List<MaterialVo4Unit> materials = skuToMaterialMap.get(erpSku);
|
|
|
-
|
|
|
- if (materials != null && !materials.isEmpty()) {
|
|
|
- BigDecimal remainingQuantity = itemJson.getBigDecimal("quantity"); // 剩余数量
|
|
|
- for (MaterialVo4Unit material : materials) {
|
|
|
- BigDecimal inventory = material.getInventory(); // 当前商品库存
|
|
|
- if (inventory.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
- continue; // 如果库存为0,跳过
|
|
|
- }
|
|
|
-
|
|
|
- DepotItemXsddRequestVO item = new DepotItemXsddRequestVO();
|
|
|
- item.setHeaderId(order.getId());
|
|
|
- item.setMaterialId(material.getId());
|
|
|
- item.setMaterialExtendId(material.getMeId());
|
|
|
- item.setMaterialUnit(material.getUnit());
|
|
|
- item.setSku(material.getSku());
|
|
|
- item.setDepotId(material.getDepotId());
|
|
|
- item.setTaxRate(BigDecimal.ONE);
|
|
|
- item.setTaxMoney(BigDecimal.ZERO);
|
|
|
- item.setBatchNumber("");
|
|
|
- item.setUnitPrice(unitPrice); // 设置单价
|
|
|
- if (inventory.compareTo(remainingQuantity) >= 0) {
|
|
|
- // 如果当前商品库存大于等于剩余数量,直接使用剩余数量
|
|
|
- item.setOperNumber(remainingQuantity);
|
|
|
- // 计算实付价格:单价 × 剩余数量
|
|
|
- BigDecimal taxLastMoney = itemJson.getBigDecimal("unitPrice").multiply(remainingQuantity);
|
|
|
- item.setTaxLastMoney(taxLastMoney);
|
|
|
- item.setAllPrice(taxLastMoney);
|
|
|
- itemList.add(item);
|
|
|
- break; // 结束循环
|
|
|
- } else {
|
|
|
- // 如果当前商品库存小于剩余数量,使用当前商品库存
|
|
|
- item.setOperNumber(inventory);
|
|
|
- // 计算实付价格:单价 × 剩余数量
|
|
|
- BigDecimal taxLastMoney = itemJson.getBigDecimal("unitPrice").multiply(remainingQuantity);
|
|
|
- item.setTaxLastMoney(taxLastMoney);
|
|
|
- item.setAllPrice(taxLastMoney);
|
|
|
- itemList.add(item);
|
|
|
- remainingQuantity = remainingQuantity.subtract(inventory); // 减少剩余数量
|
|
|
- }
|
|
|
- }
|
|
|
+ // 检查是否完全分配
|
|
|
+ if (remainingOrderQty.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ throw new RuntimeException(buildStockShortageMessage(reqVOItem.getErpSku(), matchResult.getOrderUnitQuantity(), remainingOrderQty, orderUnit, materials, true, // 是大单位
|
|
|
+ material4UnitPrice));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 分配基本单位库存
|
|
|
+ */
|
|
|
+ private void allocateBasicUnitStock(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, List<MaterialVo4Unit> materials, UnitMatchResult matchResult) throws Exception {
|
|
|
+
|
|
|
+ // 获取基本参数
|
|
|
+ BigDecimal remainingQty = matchResult.getBaseQuantity(); // 剩余需求数量(基本单位)
|
|
|
+ String basicUnit = matchResult.getMatchedUnit(); // 基本单位名称
|
|
|
+ String erpSku = reqVOItem.getErpSku();
|
|
|
+ BigDecimal unitPrice = reqVOItem.getUnitPrice();
|
|
|
+
|
|
|
+ // 记录原始需求用于错误提示
|
|
|
+ BigDecimal originalQty = remainingQty;
|
|
|
+
|
|
|
+ // 遍历库存批次进行分配
|
|
|
+ for (MaterialVo4Unit m : materials) {
|
|
|
+ if (remainingQty.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ break; // 需求已满足
|
|
|
+ }
|
|
|
+
|
|
|
+ BigDecimal inventory = m.getInventory();
|
|
|
+ if (inventory.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ continue; // 跳过无库存批次
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算当前批次可分配数量
|
|
|
+ BigDecimal allocateQty = inventory.min(remainingQty);
|
|
|
+
|
|
|
+ // 创建订单明细项
|
|
|
+ DepotItemXsddRequestVO item = createOrderItem(order, m, basicUnit, allocateQty, allocateQty, unitPrice);
|
|
|
+ itemList.add(item);
|
|
|
+
|
|
|
+ // 更新库存
|
|
|
+ m.setInventory(inventory.subtract(allocateQty));
|
|
|
+
|
|
|
+ // 更新剩余需求
|
|
|
+ remainingQty = remainingQty.subtract(allocateQty);
|
|
|
+
|
|
|
+ logger.info("分配基本单位库存:批次[{}] 分配 {} {} (剩余需求: {})", m.getBatchNumber(), allocateQty.toPlainString(), basicUnit, remainingQty.toPlainString());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否完全分配
|
|
|
+ if (remainingQty.compareTo(BigDecimal.ZERO) > 0) {
|
|
|
+ // 在allocateBasicUnitStock方法中调用
|
|
|
+ throw new RuntimeException(buildStockShortageMessage(erpSku, originalQty, remainingQty, basicUnit, materials, false, null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 构建库存不足提示信息(通用方法,支持基本单位和大单位)
|
|
|
+ *
|
|
|
+ * @param erpSku 商品SKU
|
|
|
+ * @param totalQty 总需求数量
|
|
|
+ * @param remainingQty 剩余未分配数量
|
|
|
+ * @param orderUnit 订单单位
|
|
|
+ * @param materials 库存列表
|
|
|
+ * @param isLargeUnit 是否为大单位
|
|
|
+ * @param material4UnitPrice 单位价格信息(大单位时需要)
|
|
|
+ * @return 完整的库存不足提示信息
|
|
|
+ */
|
|
|
+ private String buildStockShortageMessage(String erpSku, BigDecimal totalQty, BigDecimal remainingQty, String orderUnit, List<MaterialVo4Unit> materials, boolean isLargeUnit, Material4UnitPrice material4UnitPrice) {
|
|
|
+
|
|
|
+ StringBuilder sb = new StringBuilder();
|
|
|
+ sb.append("商品[").append(erpSku).append("]库存不足!\n");
|
|
|
+ sb.append("需求: ").append(totalQty.toPlainString()).append(" ").append(orderUnit).append("\n");
|
|
|
+ sb.append("未分配: ").append(remainingQty.toPlainString()).append(" ").append(orderUnit).append("\n");
|
|
|
+ sb.append("当前库存:\n");
|
|
|
+
|
|
|
+ // 获取基本单位名称
|
|
|
+ String basicUnit = isLargeUnit ? material4UnitPrice.getBasicUnit() : orderUnit;
|
|
|
+
|
|
|
+ materials.forEach(m -> {
|
|
|
+ // 计算当前批次的显示库存量
|
|
|
+ String stockDisplay;
|
|
|
+ if (isLargeUnit) {
|
|
|
+ BigDecimal unitRatio = getUnitRatio(orderUnit, material4UnitPrice);
|
|
|
+ BigDecimal stockInOrderUnit = m.getInventory().divideToIntegralValue(unitRatio);
|
|
|
+ stockDisplay = String.format("%s %s (%s %s)", stockInOrderUnit.toPlainString(), orderUnit, m.getInventory().toPlainString(), basicUnit);
|
|
|
} else {
|
|
|
- // 如果未找到对应的 MaterialVo4Unit,可以记录日志或处理异常
|
|
|
- logger.warn("未找到 erp_sku: {} 对应的商品信息", erpSku);
|
|
|
+ stockDisplay = m.getInventory().toPlainString() + " " + basicUnit;
|
|
|
}
|
|
|
+
|
|
|
+ sb.append(String.format("批次[%s] 生产日期[%s] - 库存: %s\n", m.getBatchNumber(), m.getProductionDate(), stockDisplay));
|
|
|
+ });
|
|
|
+
|
|
|
+ // 添加建议提示
|
|
|
+ if (isLargeUnit) {
|
|
|
+ sb.append("\n提示:").append(orderUnit).append("与").append(basicUnit).append("的换算比例为: 1").append(orderUnit).append("=").append(getUnitRatio(orderUnit, material4UnitPrice).toPlainString()).append(basicUnit);
|
|
|
}
|
|
|
- depotHeadService.syncOrderToXsdd(order, itemList);
|
|
|
+
|
|
|
+ return sb.toString();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 订单项参数校验
|
|
|
+ */
|
|
|
+ private void validateOrderItem(ErpXsddReqVO.OrderItem item) throws Exception {
|
|
|
+ if (item.getQuantity() == null || item.getQuantity() <= 0) {
|
|
|
+ throw new RuntimeException("商品数量必须大于0");
|
|
|
+ }
|
|
|
+ if (item.getUnitPrice() == null || item.getUnitPrice().compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
+ throw new RuntimeException("商品单价必须大于0");
|
|
|
+ }
|
|
|
+ if (StringUtils.isBlank(item.getErpSku())) {
|
|
|
+ throw new RuntimeException("商品SKU不能为空");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 创建订单明细项
|
|
|
+ */
|
|
|
+ private DepotItemXsddRequestVO createOrderItem(DepotHeadXsddRequestVO order, MaterialVo4Unit material, String unit, BigDecimal operNumber, BigDecimal basicNumber, BigDecimal unitPrice) {
|
|
|
+ DepotItemXsddRequestVO item = new DepotItemXsddRequestVO();
|
|
|
+ item.setHeaderId(order.getId());
|
|
|
+ item.setMaterialId(material.getId());
|
|
|
+ item.setMaterialExtendId(material.getMeId());
|
|
|
+ item.setMaterialUnit(unit);
|
|
|
+ item.setSku(material.getSku());
|
|
|
+ item.setDepotId(material.getDepotId());
|
|
|
+ item.setOperNumber(operNumber);
|
|
|
+ item.setBasicNumber(basicNumber);
|
|
|
+ item.setUnitPrice(unitPrice);
|
|
|
+ item.setTaxRate(BigDecimal.ONE);
|
|
|
+ item.setTaxMoney(BigDecimal.ZERO);
|
|
|
+ item.setBatchNumber("");
|
|
|
+ item.setTaxLastMoney(unitPrice.multiply(operNumber));
|
|
|
+ item.setAllPrice(unitPrice.multiply(operNumber));
|
|
|
+ return item;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ private Material4UnitPrice getMaterial4UnitPrice(MaterialVo4Unit material) throws Exception {
|
|
|
+ Material4UnitPrice material4UnitPrice = new Material4UnitPrice();
|
|
|
+ Unit unitInfo = new Unit(); // 查询多单位信息
|
|
|
+ unitInfo.setBasicUnit(material.getCommodityUnit());
|
|
|
+ if(material.getUnitId() != null){
|
|
|
+ unitInfo = materialService.findUnit(material.getId());
|
|
|
+ }
|
|
|
+
|
|
|
+ BeanUtils.copyProperties(unitInfo, material4UnitPrice);
|
|
|
+
|
|
|
+ // 设置基础单位价格、基础数量
|
|
|
+ material4UnitPrice.setBasicUnitPrice(material.getWholesaleDecimal());
|
|
|
+ material4UnitPrice.setBasicUnitNumber(BigDecimal.ONE);
|
|
|
+
|
|
|
+ // 设置其他单位价格、其他数量
|
|
|
+ if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnit())) {
|
|
|
+ material4UnitPrice.setOtherUnitPrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatio()));
|
|
|
+ material4UnitPrice.setOtherUnitNumber(material4UnitPrice.getRatio());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置其他单位2价格、其他数量2
|
|
|
+ if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnitTwo())) {
|
|
|
+ material4UnitPrice.setOtherUnitTwoPrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatioTwo()));
|
|
|
+ material4UnitPrice.setOtherUnitTwoNumber(material4UnitPrice.getRatioTwo());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置其他单位3价格、其他数量3
|
|
|
+ if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnitThree())) {
|
|
|
+ material4UnitPrice.setOtherUnitThreePrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatioThree()));
|
|
|
+ material4UnitPrice.setOtherUnitThreeNumber(material4UnitPrice.getRatioThree());
|
|
|
+ }
|
|
|
+ return material4UnitPrice;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据单价匹配单位并计算基本单位数量<br>
|
|
|
+ * 如果订单商品单价和商品最小单位单价相等,则使用最小单位, remainingQuantity = quantity<br>
|
|
|
+ * 如果订单商品单价和商品其他单位单价相等,则使用其他单位, remainingQuantity = quantity * material4UnitPrice.getRatio()<br>
|
|
|
+ * 如果订单商品单价和商品其他单位2单价相等,则使用其他单位2, remainingQuantity = quantity * material4UnitPrice.getRatioTwo()<br>
|
|
|
+ * 如果订单商品单价和商品其他单位3单价相等,则使用其他单位3, remainingQuantity = quantity * material4UnitPrice.getRatioThree()<br>
|
|
|
+ */
|
|
|
+ private UnitMatchResult matchUnitAndCalculateBaseQuantity(BigDecimal orderUnitPrice, BigDecimal orderQuantity, Material4UnitPrice material4UnitPrice) {
|
|
|
+ // 允许的价格误差范围
|
|
|
+ final BigDecimal PRICE_TOLERANCE = new BigDecimal("0.001");
|
|
|
+
|
|
|
+ // 1. 检查是否匹配基本单位价格
|
|
|
+ if (material4UnitPrice.getBasicUnitPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getBasicUnitPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
|
|
|
+ return new UnitMatchResult(material4UnitPrice.getBasicUnit(), orderQuantity, orderQuantity, false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 检查是否匹配副单位价格
|
|
|
+ if (material4UnitPrice.getOtherUnitPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
|
|
|
+ BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatio());
|
|
|
+ return new UnitMatchResult(material4UnitPrice.getOtherUnit(), orderQuantity, baseQuantity, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 检查是否匹配副单位2价格
|
|
|
+ if (material4UnitPrice.getOtherUnitTwoPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitTwoPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
|
|
|
+ BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatioTwo());
|
|
|
+ return new UnitMatchResult(material4UnitPrice.getOtherUnitTwo(), orderQuantity, baseQuantity, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 检查是否匹配副单位3价格
|
|
|
+ if (material4UnitPrice.getOtherUnitThreePrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitThreePrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
|
|
|
+ BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatioThree());
|
|
|
+ return new UnitMatchResult(material4UnitPrice.getOtherUnitThree(), orderQuantity, baseQuantity, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 默认返回基本单位(如果无法匹配)
|
|
|
+ return new UnitMatchResult(material4UnitPrice.getBasicUnit(), orderQuantity, orderQuantity, false);
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 将基本单位数量转换为订单单位数量
|
|
|
+ */
|
|
|
+ private BigDecimal convertToOrderUnitQuantity(BigDecimal baseQuantity, String orderUnit, Material4UnitPrice material4UnitPrice) {
|
|
|
+ if (orderUnit.equals(material4UnitPrice.getBasicUnit())) {
|
|
|
+ return baseQuantity;
|
|
|
+ } else if (orderUnit.equals(material4UnitPrice.getOtherUnit())) {
|
|
|
+ return baseQuantity.divide(material4UnitPrice.getRatio());
|
|
|
+ } else if (orderUnit.equals(material4UnitPrice.getOtherUnitTwo())) {
|
|
|
+ return baseQuantity.divide(material4UnitPrice.getRatioTwo());
|
|
|
+ } else if (orderUnit.equals(material4UnitPrice.getOtherUnitThree())) {
|
|
|
+ return baseQuantity.divide(material4UnitPrice.getRatioThree());
|
|
|
+ }
|
|
|
+ return baseQuantity;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 获取单位的换算比例
|
|
|
+ */
|
|
|
+ private BigDecimal getUnitRatio(String unit, Material4UnitPrice material4UnitPrice) {
|
|
|
+ if (unit.equals(material4UnitPrice.getBasicUnit())) {
|
|
|
+ return BigDecimal.ONE;
|
|
|
+ } else if (unit.equals(material4UnitPrice.getOtherUnit())) {
|
|
|
+ return material4UnitPrice.getRatio();
|
|
|
+ } else if (unit.equals(material4UnitPrice.getOtherUnitTwo())) {
|
|
|
+ return material4UnitPrice.getRatioTwo();
|
|
|
+ } else if (unit.equals(material4UnitPrice.getOtherUnitThree())) {
|
|
|
+ return material4UnitPrice.getRatioThree();
|
|
|
+ }
|
|
|
+ return BigDecimal.ONE;
|
|
|
+ }
|
|
|
|
|
|
+ /**
|
|
|
+ * 单位匹配结果类
|
|
|
+ */
|
|
|
+ private static class UnitMatchResult {
|
|
|
+ private String matchedUnit; // 匹配到的单位
|
|
|
+ private BigDecimal orderUnitQuantity; // 订单单位数量
|
|
|
+ private BigDecimal baseQuantity; // 基本单位数量
|
|
|
+ private boolean isLargeUnit; // 是否是大单位
|
|
|
+
|
|
|
+ public UnitMatchResult(String matchedUnit, BigDecimal orderUnitQuantity,
|
|
|
+ BigDecimal baseQuantity, boolean isLargeUnit) {
|
|
|
+ this.matchedUnit = matchedUnit;
|
|
|
+ this.orderUnitQuantity = orderUnitQuantity;
|
|
|
+ this.baseQuantity = baseQuantity;
|
|
|
+ this.isLargeUnit = isLargeUnit;
|
|
|
+ }
|
|
|
+
|
|
|
+ // getter方法
|
|
|
+ public String getMatchedUnit() {
|
|
|
+ return matchedUnit;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getBaseQuantity() {
|
|
|
+ return baseQuantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ public boolean isLargeUnit() {
|
|
|
+ return isLargeUnit;
|
|
|
+ }
|
|
|
+
|
|
|
+ public BigDecimal getOrderUnitQuantity() {
|
|
|
+ return orderUnitQuantity;
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
|
|
|
}
|