SyncTescoSystemService.java 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. package com.jsh.erp.service;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alibaba.fastjson.JSONObject;
  5. import com.baomidou.mybatisplus.core.toolkit.StringUtils;
  6. import com.jsh.erp.constants.ExceptionConstants;
  7. import com.jsh.erp.datasource.entities.DepotHeadVo4Body;
  8. import com.jsh.erp.datasource.entities.MaterialVo4Unit;
  9. import com.jsh.erp.datasource.entities.Supplier;
  10. import com.jsh.erp.datasource.entities.Unit;
  11. import com.jsh.erp.datasource.tesco.request.ErpXsddReqVO;
  12. import com.jsh.erp.datasource.vo.DepotHeadXsddRequestVO;
  13. import com.jsh.erp.datasource.vo.DepotItemXsddRequestVO;
  14. import com.jsh.erp.datasource.vo.Material4UnitPrice;
  15. import com.jsh.erp.exception.BusinessRunTimeException;
  16. import org.slf4j.Logger;
  17. import org.slf4j.LoggerFactory;
  18. import org.springframework.beans.BeanUtils;
  19. import org.springframework.stereotype.Service;
  20. import org.springframework.transaction.annotation.Transactional;
  21. import org.springframework.util.CollectionUtils;
  22. import javax.annotation.Resource;
  23. import java.math.BigDecimal;
  24. import java.util.ArrayList;
  25. import java.util.Date;
  26. import java.util.List;
  27. import java.util.Map;
  28. import java.util.stream.Collectors;
  29. import java.util.stream.Stream;
  30. /**
  31. * @Description TODO
  32. * @Author MS.BLUE
  33. * @Date 2025-04-12
  34. */
  35. @Service
  36. public class SyncTescoSystemService {
  37. private Logger logger = LoggerFactory.getLogger(SyncTescoSystemService.class);
  38. @Resource
  39. private MaterialService materialService;
  40. @Resource
  41. private DepotHeadService depotHeadService;
  42. @Resource
  43. private SequenceService sequenceService;
  44. @Resource
  45. private SupplierService supplierService;
  46. public String syncSystemSku(String param) {
  47. logger.info("获取商品系统sku,返回信息集采系统 param:{}", param);
  48. JSONObject result = new JSONObject();
  49. JSONArray dataArray = new JSONArray();
  50. try {
  51. // 参数校验
  52. if (param == null || param.isEmpty()) {
  53. result.put("code", 1);
  54. result.put("msg", "参数不能为空");
  55. return result.toJSONString();
  56. }
  57. // 解析传入的JSON参数
  58. JSONArray barCodeArray = JSON.parseArray(param);
  59. if (barCodeArray == null || barCodeArray.isEmpty()) {
  60. result.put("code", 1);
  61. result.put("msg", "参数格式错误");
  62. return result.toJSONString();
  63. }
  64. // 提取 barCode 到字符串数组
  65. List<String> barCodeList = new ArrayList<>();
  66. for (int i = 0; i < barCodeArray.size(); i++) {
  67. JSONObject barCodeObj = barCodeArray.getJSONObject(i);
  68. String barCode = barCodeObj.getString("barCode");
  69. barCodeList.add(barCode);
  70. }
  71. // 查询商品信息
  72. List<MaterialVo4Unit> materialList = materialService.getMaterialByBarCode(barCodeList);
  73. if (materialList != null && !materialList.isEmpty()) {
  74. result.put("code", 0);
  75. for (MaterialVo4Unit material : materialList) {
  76. JSONObject item = new JSONObject();
  77. item.put("barCode", material.getmBarCode());
  78. item.put("systemSku", material.getSystemSku());
  79. dataArray.add(item);
  80. }
  81. } else {
  82. result.put("code", 1);
  83. result.put("msg", "未找到对应商品信息");
  84. }
  85. } catch (Exception e) {
  86. e.printStackTrace();
  87. result.put("code", 1);
  88. result.put("msg", "系统异常,请稍后重试");
  89. }
  90. // 将数据数组放入返回结果
  91. result.put("data", dataArray);
  92. logger.info(" result:{}", result);
  93. return result.toJSONString();
  94. }
  95. /**
  96. *
  97. 集采下单成功->封装同步erp销售订单请求体
  98. {
  99. orderSn:"订单编号",
  100. customer:{//客户信息
  101. name:"客户名称",
  102. contact:"客户联系方式",
  103. address:"客户地址"
  104. },
  105. item:[{//订单商品信息
  106. erp_sku:"erp_sku编号",
  107. unitPrice:"商品单价",
  108. quantity:"商品下单数量",
  109. price:"商品实付价格"
  110. }]
  111. }
  112. * @param param
  113. * @return
  114. */
  115. public String syncOrder(String param) {
  116. logger.info("同步集采订单-》销售订单 param:{}", param);
  117. JSONObject result = new JSONObject();
  118. try {
  119. // 解析传入的JSON参数为 ErpXsddReqVO
  120. ErpXsddReqVO erpXsddReqVO = JSON.parseObject(param, ErpXsddReqVO.class);
  121. if (erpXsddReqVO == null) {
  122. result.put("code", 1);
  123. result.put("msg", "参数不能为空");
  124. return result.toJSONString();
  125. }
  126. // 校验订单编号
  127. if (erpXsddReqVO.getOrderSn() == null || erpXsddReqVO.getOrderSn().isEmpty()) {
  128. result.put("code", 1);
  129. result.put("msg", "订单编号不能为空");
  130. return result.toJSONString();
  131. }
  132. // 校验客户信息
  133. if (erpXsddReqVO.getCustomer() == null) {
  134. result.put("code", 1);
  135. result.put("msg", "客户信息不能为空");
  136. return result.toJSONString();
  137. }
  138. // 校验订单商品信息
  139. if (erpXsddReqVO.getItems() == null || erpXsddReqVO.getItems().isEmpty()) {
  140. result.put("code", 1);
  141. result.put("msg", "订单商品信息不能为空");
  142. return result.toJSONString();
  143. }
  144. // 构建订单和商品信息
  145. buildOrderAndItem(erpXsddReqVO);
  146. // 返回成功结果
  147. result.put("code", 0);
  148. result.put("msg", "订单同步成功");
  149. } catch (Exception e) {
  150. logger.error("同步订单发生异常", e);
  151. result.put("code", 1);
  152. result.put("msg", "系统异常,请稍后重试");
  153. }
  154. return result.toJSONString();
  155. }
  156. @Transactional(value = "transactionManager", rollbackFor = Exception.class)
  157. private void buildOrderAndItem(ErpXsddReqVO erpXsddReqVO) throws Exception {
  158. // 1. 初始化订单基本信息
  159. DepotHeadXsddRequestVO order = initSalesOrder(erpXsddReqVO);
  160. // 2. 处理订单商品项
  161. List<DepotItemXsddRequestVO> itemList = processOrderItems(erpXsddReqVO, order);
  162. // 3. 提交订单
  163. depotHeadService.syncOrderToXsdd(order, itemList);
  164. }
  165. private DepotHeadXsddRequestVO initSalesOrder(ErpXsddReqVO erpXsddReqVO) throws Exception {
  166. DepotHeadXsddRequestVO order = new DepotHeadXsddRequestVO();
  167. order.setType("其它");
  168. order.setSubType("销售订单");
  169. order.setLinkTesco(erpXsddReqVO.getOrderSn());
  170. ErpXsddReqVO.Customer customer = erpXsddReqVO.getCustomer();
  171. // 根据手机号查询客户
  172. Supplier supplier = supplierService.getCustomerByPhone(customer.getAccount());
  173. if (supplier == null) {
  174. // 如果客户不存在,创建新客户
  175. supplier = new Supplier();
  176. supplier.setSupplier(customer.getName());
  177. supplier.setTelephone(customer.getAccount());
  178. supplier.setContacts(customer.getReceiverName());
  179. supplier.setPhoneNum(customer.getReceiverPhone());
  180. supplier.setAddress(customer.getReceiverAddress());
  181. supplier = supplierService.createCustomer(supplier);
  182. }
  183. // 绑定客户 ID
  184. order.setOrganId(supplier.getId());
  185. order.setReceiverName(customer.getReceiverName());
  186. order.setReceiverPhone(customer.getReceiverPhone());
  187. order.setReceiverAddress(customer.getReceiverAddress());
  188. order.setRemark("集采同步订单");
  189. String number = sequenceService.buildOnlyNumber();
  190. String defaultNumber = "XSDD" + number;
  191. order.setDefaultNumber(defaultNumber);
  192. order.setNumber(defaultNumber);
  193. order.setCreateTime(new Date());
  194. order.setOperTime(new Date());
  195. order.setCreator(1L); // 创建人ID
  196. order.setAccountId(1L); // 账户ID
  197. order.setChangeAmount(BigDecimal.ZERO);
  198. order.setBackAmount(BigDecimal.ZERO);
  199. order.setTotalPrice(erpXsddReqVO.getTotalPrice());
  200. order.setPayType("现付");
  201. order.setDiscount(BigDecimal.ZERO);
  202. order.setDiscountMoney(BigDecimal.ZERO);
  203. order.setDiscountLastMoney(erpXsddReqVO.getTotalPrice());
  204. order.setStatus("0");
  205. order.setPurchaseStatus("0");
  206. order.setSource("2"); // 来源集采商城
  207. order.setTenantId(null);
  208. order.setDeleteFlag("0");
  209. return order;
  210. }
  211. /**
  212. * 处理订单商品项
  213. */
  214. private List<DepotItemXsddRequestVO> processOrderItems(ErpXsddReqVO erpXsddReqVO, DepotHeadXsddRequestVO order) throws Exception {
  215. List<DepotItemXsddRequestVO> itemList = new ArrayList<>();
  216. List<ErpXsddReqVO.OrderItem> reqVOItems = erpXsddReqVO.getItems();
  217. // 1. 获取所有商品信息
  218. List<String> systemSkuList = reqVOItems.stream().map(ErpXsddReqVO.OrderItem::getErpSku).collect(Collectors.toList());
  219. List<MaterialVo4Unit> materialList = materialService.getMaterialBySystemSku(systemSkuList);
  220. Map<String, List<MaterialVo4Unit>> skuToMaterialMap = materialList.stream().collect(Collectors.groupingBy(MaterialVo4Unit::getSystemSku));
  221. // 2. 处理每个订单项
  222. for (ErpXsddReqVO.OrderItem reqVOItem : reqVOItems) {
  223. try {
  224. processSingleOrderItem(reqVOItem, order, itemList, skuToMaterialMap);
  225. } catch (Exception e) {
  226. logger.error("处理订单项失败 erp_sku: {}: {}", reqVOItem.getErpSku(), e.getMessage());
  227. throw e;
  228. }
  229. }
  230. return itemList;
  231. }
  232. /**
  233. * 处理单个订单项
  234. */
  235. private void processSingleOrderItem(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, Map<String, List<MaterialVo4Unit>> skuToMaterialMap) throws Exception {
  236. // 1. 参数校验
  237. validateOrderItem(reqVOItem);
  238. // 2. 获取商品信息
  239. List<MaterialVo4Unit> materials = skuToMaterialMap.get(reqVOItem.getErpSku());
  240. if (CollectionUtils.isEmpty(materials)) {
  241. throw new BusinessRunTimeException(ExceptionConstants.MATERIAL_ERP_SKU_NOT_DECIMAL_CODE, String.format(ExceptionConstants.MATERIAL_ERP_SKU_NOT_DECIMAL_MSG, reqVOItem.getErpSku()));
  242. }
  243. // 3. 获取单位信息
  244. MaterialVo4Unit primaryMaterial = materials.get(0);
  245. Material4UnitPrice material4UnitPrice = getMaterial4UnitPrice(primaryMaterial);
  246. // 4. 匹配销售单位
  247. UnitMatchResult matchResult = matchUnitAndCalculateBaseQuantity(reqVOItem.getUnitPrice(), BigDecimal.valueOf(reqVOItem.getQuantity()), material4UnitPrice);
  248. // 5. 根据单位类型分配库存
  249. if (matchResult.isLargeUnit()) {
  250. allocateLargeUnitStock(reqVOItem, order, itemList, materials, matchResult, material4UnitPrice);
  251. } else {
  252. allocateBasicUnitStock(reqVOItem, order, itemList, materials, matchResult);
  253. }
  254. }
  255. /**
  256. * 分配大单位库存(不拆分配)
  257. */
  258. private void allocateLargeUnitStock(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, List<MaterialVo4Unit> materials,
  259. UnitMatchResult matchResult, Material4UnitPrice material4UnitPrice) throws Exception {
  260. String orderUnit = matchResult.getMatchedUnit();
  261. BigDecimal unitRatio = getUnitRatio(orderUnit, material4UnitPrice);
  262. BigDecimal remainingOrderQty = matchResult.getOrderUnitQuantity();
  263. // 分配库存
  264. for (MaterialVo4Unit m : materials) {
  265. if (remainingOrderQty.compareTo(BigDecimal.ZERO) <= 0)
  266. break;
  267. BigDecimal inventory = m.getInventory();
  268. if (inventory.compareTo(unitRatio) < 0)
  269. continue;
  270. // 计算可分配数量(向下取整)
  271. BigDecimal allocatableUnits = inventory.divideToIntegralValue(unitRatio).min(remainingOrderQty);
  272. if (allocatableUnits.compareTo(BigDecimal.ZERO) > 0) {
  273. BigDecimal allocBaseQty = allocatableUnits.multiply(unitRatio);
  274. // 创建订单项
  275. DepotItemXsddRequestVO item = createOrderItem(order, m, orderUnit, allocatableUnits, allocBaseQty, reqVOItem.getUnitPrice());
  276. itemList.add(item);
  277. // 更新库存
  278. m.setInventory(inventory.subtract(allocBaseQty));
  279. // 更新剩余需求
  280. remainingOrderQty = remainingOrderQty.subtract(allocatableUnits);
  281. logger.info("分配库存:批次[{}] 分配 {} {} (剩余需求: {})", m.getBatchNumber(), allocatableUnits.toPlainString(), orderUnit, remainingOrderQty.toPlainString());
  282. }
  283. }
  284. // 检查是否完全分配
  285. if (remainingOrderQty.compareTo(BigDecimal.ZERO) > 0) {
  286. throw new RuntimeException(buildStockShortageMessage(reqVOItem.getErpSku(), matchResult.getOrderUnitQuantity(), remainingOrderQty, orderUnit, materials, true, // 是大单位
  287. material4UnitPrice));
  288. }
  289. }
  290. /**
  291. * 分配基本单位库存
  292. */
  293. private void allocateBasicUnitStock(ErpXsddReqVO.OrderItem reqVOItem, DepotHeadXsddRequestVO order, List<DepotItemXsddRequestVO> itemList, List<MaterialVo4Unit> materials, UnitMatchResult matchResult) throws Exception {
  294. // 获取基本参数
  295. BigDecimal remainingQty = matchResult.getBaseQuantity(); // 剩余需求数量(基本单位)
  296. String basicUnit = matchResult.getMatchedUnit(); // 基本单位名称
  297. String erpSku = reqVOItem.getErpSku();
  298. BigDecimal unitPrice = reqVOItem.getUnitPrice();
  299. // 记录原始需求用于错误提示
  300. BigDecimal originalQty = remainingQty;
  301. // 遍历库存批次进行分配
  302. for (MaterialVo4Unit m : materials) {
  303. if (remainingQty.compareTo(BigDecimal.ZERO) <= 0) {
  304. break; // 需求已满足
  305. }
  306. BigDecimal inventory = m.getInventory();
  307. if (inventory.compareTo(BigDecimal.ZERO) <= 0) {
  308. continue; // 跳过无库存批次
  309. }
  310. // 计算当前批次可分配数量
  311. BigDecimal allocateQty = inventory.min(remainingQty);
  312. // 创建订单明细项
  313. DepotItemXsddRequestVO item = createOrderItem(order, m, basicUnit, allocateQty, allocateQty, unitPrice);
  314. itemList.add(item);
  315. // 更新库存
  316. m.setInventory(inventory.subtract(allocateQty));
  317. // 更新剩余需求
  318. remainingQty = remainingQty.subtract(allocateQty);
  319. logger.info("分配基本单位库存:批次[{}] 分配 {} {} (剩余需求: {})", m.getBatchNumber(), allocateQty.toPlainString(), basicUnit, remainingQty.toPlainString());
  320. }
  321. // 检查是否完全分配
  322. if (remainingQty.compareTo(BigDecimal.ZERO) > 0) {
  323. // 在allocateBasicUnitStock方法中调用
  324. throw new RuntimeException(buildStockShortageMessage(erpSku, originalQty, remainingQty, basicUnit, materials, false, null));
  325. }
  326. }
  327. /**
  328. * 构建库存不足提示信息(通用方法,支持基本单位和大单位)
  329. *
  330. * @param erpSku 商品SKU
  331. * @param totalQty 总需求数量
  332. * @param remainingQty 剩余未分配数量
  333. * @param orderUnit 订单单位
  334. * @param materials 库存列表
  335. * @param isLargeUnit 是否为大单位
  336. * @param material4UnitPrice 单位价格信息(大单位时需要)
  337. * @return 完整的库存不足提示信息
  338. */
  339. private String buildStockShortageMessage(String erpSku, BigDecimal totalQty, BigDecimal remainingQty, String orderUnit, List<MaterialVo4Unit> materials, boolean isLargeUnit, Material4UnitPrice material4UnitPrice) {
  340. StringBuilder sb = new StringBuilder();
  341. sb.append("商品[").append(erpSku).append("]库存不足!\n");
  342. sb.append("需求: ").append(totalQty.toPlainString()).append(" ").append(orderUnit).append("\n");
  343. sb.append("未分配: ").append(remainingQty.toPlainString()).append(" ").append(orderUnit).append("\n");
  344. sb.append("当前库存:\n");
  345. // 获取基本单位名称
  346. String basicUnit = isLargeUnit ? material4UnitPrice.getBasicUnit() : orderUnit;
  347. materials.forEach(m -> {
  348. // 计算当前批次的显示库存量
  349. String stockDisplay;
  350. if (isLargeUnit) {
  351. BigDecimal unitRatio = getUnitRatio(orderUnit, material4UnitPrice);
  352. BigDecimal stockInOrderUnit = m.getInventory().divideToIntegralValue(unitRatio);
  353. stockDisplay = String.format("%s %s (%s %s)", stockInOrderUnit.toPlainString(), orderUnit, m.getInventory().toPlainString(), basicUnit);
  354. } else {
  355. stockDisplay = m.getInventory().toPlainString() + " " + basicUnit;
  356. }
  357. sb.append(String.format("批次[%s] 生产日期[%s] - 库存: %s\n", m.getBatchNumber(), m.getProductionDate(), stockDisplay));
  358. });
  359. // 添加建议提示
  360. if (isLargeUnit) {
  361. sb.append("\n提示:").append(orderUnit).append("与").append(basicUnit).append("的换算比例为: 1").append(orderUnit).append("=").append(getUnitRatio(orderUnit, material4UnitPrice).toPlainString()).append(basicUnit);
  362. }
  363. return sb.toString();
  364. }
  365. /**
  366. * 订单项参数校验
  367. */
  368. private void validateOrderItem(ErpXsddReqVO.OrderItem item) throws Exception {
  369. if (item.getQuantity() == null || item.getQuantity() <= 0) {
  370. throw new RuntimeException("商品数量必须大于0");
  371. }
  372. if (item.getUnitPrice() == null || item.getUnitPrice().compareTo(BigDecimal.ZERO) <= 0) {
  373. throw new RuntimeException("商品单价必须大于0");
  374. }
  375. if (StringUtils.isBlank(item.getErpSku())) {
  376. throw new RuntimeException("商品SKU不能为空");
  377. }
  378. }
  379. /**
  380. * 创建订单明细项
  381. */
  382. private DepotItemXsddRequestVO createOrderItem(DepotHeadXsddRequestVO order, MaterialVo4Unit material, String unit, BigDecimal operNumber, BigDecimal basicNumber, BigDecimal unitPrice) {
  383. DepotItemXsddRequestVO item = new DepotItemXsddRequestVO();
  384. item.setHeaderId(order.getId());
  385. item.setMaterialId(material.getId());
  386. item.setMaterialExtendId(material.getMeId());
  387. item.setMaterialUnit(unit);
  388. item.setSku(material.getSku());
  389. item.setDepotId(material.getDepotId());
  390. item.setOperNumber(operNumber);
  391. item.setBasicNumber(basicNumber);
  392. item.setUnitPrice(unitPrice);
  393. item.setTaxRate(BigDecimal.ONE);
  394. item.setTaxMoney(BigDecimal.ZERO);
  395. item.setBatchNumber("");
  396. item.setTaxLastMoney(unitPrice.multiply(operNumber));
  397. item.setAllPrice(unitPrice.multiply(operNumber));
  398. return item;
  399. }
  400. private Material4UnitPrice getMaterial4UnitPrice(MaterialVo4Unit material) throws Exception {
  401. Material4UnitPrice material4UnitPrice = new Material4UnitPrice();
  402. Unit unitInfo = new Unit(); // 查询多单位信息
  403. unitInfo.setBasicUnit(material.getCommodityUnit());
  404. if(material.getUnitId() != null){
  405. unitInfo = materialService.findUnit(material.getId());
  406. }
  407. BeanUtils.copyProperties(unitInfo, material4UnitPrice);
  408. // 设置基础单位价格、基础数量
  409. material4UnitPrice.setBasicUnitPrice(material.getWholesaleDecimal());
  410. material4UnitPrice.setBasicUnitNumber(BigDecimal.ONE);
  411. // 设置其他单位价格、其他数量
  412. if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnit())) {
  413. material4UnitPrice.setOtherUnitPrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatio()));
  414. material4UnitPrice.setOtherUnitNumber(material4UnitPrice.getRatio());
  415. }
  416. // 设置其他单位2价格、其他数量2
  417. if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnitTwo())) {
  418. material4UnitPrice.setOtherUnitTwoPrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatioTwo()));
  419. material4UnitPrice.setOtherUnitTwoNumber(material4UnitPrice.getRatioTwo());
  420. }
  421. // 设置其他单位3价格、其他数量3
  422. if (StringUtils.isNotEmpty(material4UnitPrice.getOtherUnitThree())) {
  423. material4UnitPrice.setOtherUnitThreePrice(material.getWholesaleDecimal().multiply(material4UnitPrice.getRatioThree()));
  424. material4UnitPrice.setOtherUnitThreeNumber(material4UnitPrice.getRatioThree());
  425. }
  426. return material4UnitPrice;
  427. }
  428. /**
  429. * 根据单价匹配单位并计算基本单位数量<br>
  430. * 如果订单商品单价和商品最小单位单价相等,则使用最小单位, remainingQuantity = quantity<br>
  431. * 如果订单商品单价和商品其他单位单价相等,则使用其他单位, remainingQuantity = quantity * material4UnitPrice.getRatio()<br>
  432. * 如果订单商品单价和商品其他单位2单价相等,则使用其他单位2, remainingQuantity = quantity * material4UnitPrice.getRatioTwo()<br>
  433. * 如果订单商品单价和商品其他单位3单价相等,则使用其他单位3, remainingQuantity = quantity * material4UnitPrice.getRatioThree()<br>
  434. */
  435. private UnitMatchResult matchUnitAndCalculateBaseQuantity(BigDecimal orderUnitPrice, BigDecimal orderQuantity, Material4UnitPrice material4UnitPrice) {
  436. // 允许的价格误差范围
  437. final BigDecimal PRICE_TOLERANCE = new BigDecimal("0.001");
  438. // 1. 检查是否匹配基本单位价格
  439. if (material4UnitPrice.getBasicUnitPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getBasicUnitPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
  440. return new UnitMatchResult(material4UnitPrice.getBasicUnit(), orderQuantity, orderQuantity, false);
  441. }
  442. // 2. 检查是否匹配副单位价格
  443. if (material4UnitPrice.getOtherUnitPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
  444. BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatio());
  445. return new UnitMatchResult(material4UnitPrice.getOtherUnit(), orderQuantity, baseQuantity, true);
  446. }
  447. // 3. 检查是否匹配副单位2价格
  448. if (material4UnitPrice.getOtherUnitTwoPrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitTwoPrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
  449. BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatioTwo());
  450. return new UnitMatchResult(material4UnitPrice.getOtherUnitTwo(), orderQuantity, baseQuantity, true);
  451. }
  452. // 4. 检查是否匹配副单位3价格
  453. if (material4UnitPrice.getOtherUnitThreePrice() != null && orderUnitPrice.subtract(material4UnitPrice.getOtherUnitThreePrice()).abs().compareTo(PRICE_TOLERANCE) <= 0) {
  454. BigDecimal baseQuantity = orderQuantity.multiply(material4UnitPrice.getRatioThree());
  455. return new UnitMatchResult(material4UnitPrice.getOtherUnitThree(), orderQuantity, baseQuantity, true);
  456. }
  457. // 5. 默认返回基本单位(如果无法匹配)
  458. return new UnitMatchResult(material4UnitPrice.getBasicUnit(), orderQuantity, orderQuantity, false);
  459. }
  460. /**
  461. * 将基本单位数量转换为订单单位数量
  462. */
  463. private BigDecimal convertToOrderUnitQuantity(BigDecimal baseQuantity, String orderUnit, Material4UnitPrice material4UnitPrice) {
  464. if (orderUnit.equals(material4UnitPrice.getBasicUnit())) {
  465. return baseQuantity;
  466. } else if (orderUnit.equals(material4UnitPrice.getOtherUnit())) {
  467. return baseQuantity.divide(material4UnitPrice.getRatio());
  468. } else if (orderUnit.equals(material4UnitPrice.getOtherUnitTwo())) {
  469. return baseQuantity.divide(material4UnitPrice.getRatioTwo());
  470. } else if (orderUnit.equals(material4UnitPrice.getOtherUnitThree())) {
  471. return baseQuantity.divide(material4UnitPrice.getRatioThree());
  472. }
  473. return baseQuantity;
  474. }
  475. /**
  476. * 获取单位的换算比例
  477. */
  478. private BigDecimal getUnitRatio(String unit, Material4UnitPrice material4UnitPrice) {
  479. if (unit.equals(material4UnitPrice.getBasicUnit())) {
  480. return BigDecimal.ONE;
  481. } else if (unit.equals(material4UnitPrice.getOtherUnit())) {
  482. return material4UnitPrice.getRatio();
  483. } else if (unit.equals(material4UnitPrice.getOtherUnitTwo())) {
  484. return material4UnitPrice.getRatioTwo();
  485. } else if (unit.equals(material4UnitPrice.getOtherUnitThree())) {
  486. return material4UnitPrice.getRatioThree();
  487. }
  488. return BigDecimal.ONE;
  489. }
  490. /**
  491. * 单位匹配结果类
  492. */
  493. private static class UnitMatchResult {
  494. private String matchedUnit; // 匹配到的单位
  495. private BigDecimal orderUnitQuantity; // 订单单位数量
  496. private BigDecimal baseQuantity; // 基本单位数量
  497. private boolean isLargeUnit; // 是否是大单位
  498. public UnitMatchResult(String matchedUnit, BigDecimal orderUnitQuantity,
  499. BigDecimal baseQuantity, boolean isLargeUnit) {
  500. this.matchedUnit = matchedUnit;
  501. this.orderUnitQuantity = orderUnitQuantity;
  502. this.baseQuantity = baseQuantity;
  503. this.isLargeUnit = isLargeUnit;
  504. }
  505. // getter方法
  506. public String getMatchedUnit() {
  507. return matchedUnit;
  508. }
  509. public BigDecimal getBaseQuantity() {
  510. return baseQuantity;
  511. }
  512. public boolean isLargeUnit() {
  513. return isLargeUnit;
  514. }
  515. public BigDecimal getOrderUnitQuantity() {
  516. return orderUnitQuantity;
  517. }
  518. }
  519. }