| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.service.purchase; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.returns.ErpPurchaseReturnSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseReturnItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnItemMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseReturnMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.mes.api.wms.dto.WmsPurchaseInboundReqDTO; |
| | | import cn.iocoder.yudao.module.mes.api.wms.stock.WmsStockApi; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| | | import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; |
| | | |
| | | // TODO èè¿ï¼è®°å½æä½æ¥å¿ |
| | | |
| | | /** |
| | | * ERP éè´éè´§ Service å®ç°ç±» |
| | | * |
| | | * @author èéæºç |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class ErpPurchaseReturnServiceImpl implements ErpPurchaseReturnService { |
| | | |
| | | @Resource |
| | | private ErpPurchaseReturnMapper purchaseReturnMapper; |
| | | @Resource |
| | | private ErpPurchaseReturnItemMapper purchaseReturnItemMapper; |
| | | |
| | | @Resource |
| | | private ErpNoRedisDAO noRedisDAO; |
| | | |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | @Lazy // å»¶è¿å è½½ï¼é¿å
循ç¯ä¾èµ |
| | | private ErpPurchaseOrderService purchaseOrderService; |
| | | @Resource |
| | | private ErpAccountService accountService; |
| | | @Resource |
| | | private WmsStockApi wmsStockApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createPurchaseReturn(ErpPurchaseReturnSaveReqVO createReqVO) { |
| | | // 1.1 æ ¡éªéè´è®¢åå·²å®¡æ ¸ |
| | | ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(createReqVO.getOrderId()); |
| | | // 1.2 æ ¡éªéè´§é¡¹çæææ§ |
| | | List<ErpPurchaseReturnItemDO> purchaseReturnItems = validatePurchaseReturnItems(createReqVO.getItems()); |
| | | // 1.3 æ ¡éªç»ç®è´¦æ· |
| | | accountService.validateAccount(createReqVO.getAccountId()); |
| | | // 1.4 çæéè´§åå·ï¼å¹¶æ ¡éªå¯ä¸æ§ |
| | | String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_RETURN_NO_PREFIX); |
| | | if (purchaseReturnMapper.selectByNo(no) != null) { |
| | | throw exception(PURCHASE_RETURN_NO_EXISTS); |
| | | } |
| | | |
| | | // 2.1 æå
¥éè´§ |
| | | ErpPurchaseReturnDO purchaseReturn = BeanUtils.toBean(createReqVO, ErpPurchaseReturnDO.class, in -> in |
| | | .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus())) |
| | | .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); |
| | | calculateTotalPrice(purchaseReturn, purchaseReturnItems); |
| | | purchaseReturnMapper.insert(purchaseReturn); |
| | | // 2.2 æå
¥é货项 |
| | | purchaseReturnItems.forEach(o -> o.setReturnId(purchaseReturn.getId())); |
| | | purchaseReturnItemMapper.insertBatch(purchaseReturnItems); |
| | | |
| | | // 3. æ´æ°éè´è®¢åçéè´§æ°é |
| | | updatePurchaseOrderReturnCount(createReqVO.getOrderId()); |
| | | return purchaseReturn.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updatePurchaseReturn(ErpPurchaseReturnSaveReqVO updateReqVO) { |
| | | // 1.1 æ ¡éªåå¨ |
| | | ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(updateReqVO.getId()); |
| | | if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) { |
| | | throw exception(PURCHASE_RETURN_UPDATE_FAIL_APPROVE, purchaseReturn.getNo()); |
| | | } |
| | | // 1.2 æ ¡éªéè´è®¢åå·²å®¡æ ¸ |
| | | ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(updateReqVO.getOrderId()); |
| | | // 1.3 æ ¡éªç»ç®è´¦æ· |
| | | accountService.validateAccount(updateReqVO.getAccountId()); |
| | | // 1.4 æ ¡éªè®¢åé¡¹çæææ§ |
| | | List<ErpPurchaseReturnItemDO> purchaseReturnItems = validatePurchaseReturnItems(updateReqVO.getItems()); |
| | | |
| | | // 2.1 æ´æ°éè´§ |
| | | ErpPurchaseReturnDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseReturnDO.class) |
| | | .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId()); |
| | | calculateTotalPrice(updateObj, purchaseReturnItems); |
| | | purchaseReturnMapper.updateById(updateObj); |
| | | // 2.2 æ´æ°é货项 |
| | | updatePurchaseReturnItemList(updateReqVO.getId(), purchaseReturnItems); |
| | | |
| | | // 3.1 æ´æ°éè´è®¢åçåºåºæ°é |
| | | updatePurchaseOrderReturnCount(updateObj.getOrderId()); |
| | | // 3.2 注æï¼å¦æéè´è®¢åç¼å·åæ´äºï¼éè¦æ´æ°âèâéè´è®¢åçåºåºæ°é |
| | | if (ObjectUtil.notEqual(purchaseReturn.getOrderId(), updateObj.getOrderId())) { |
| | | updatePurchaseOrderReturnCount(purchaseReturn.getOrderId()); |
| | | } |
| | | } |
| | | |
| | | private void calculateTotalPrice(ErpPurchaseReturnDO purchaseReturn, List<ErpPurchaseReturnItemDO> purchaseReturnItems) { |
| | | purchaseReturn.setTotalCount(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getCount, BigDecimal::add)); |
| | | purchaseReturn.setTotalProductPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | | purchaseReturn.setTotalTaxPrice(getSumValue(purchaseReturnItems, ErpPurchaseReturnItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | | purchaseReturn.setTotalPrice(purchaseReturn.getTotalProductPrice().add(purchaseReturn.getTotalTaxPrice())); |
| | | // 计ç®ä¼æ ä»·æ ¼ |
| | | if (purchaseReturn.getDiscountPercent() == null) { |
| | | purchaseReturn.setDiscountPercent(BigDecimal.ZERO); |
| | | } |
| | | purchaseReturn.setDiscountPrice(MoneyUtils.priceMultiplyPercent(purchaseReturn.getTotalPrice(), purchaseReturn.getDiscountPercent())); |
| | | purchaseReturn.setTotalPrice(purchaseReturn.getTotalPrice().subtract(purchaseReturn.getDiscountPrice()).add(purchaseReturn.getOtherPrice())); |
| | | } |
| | | |
| | | private void updatePurchaseOrderReturnCount(Long orderId) { |
| | | // 1.1 æ¥è¯¢éè´è®¢å对åºçéè´åºåºåå表 |
| | | List<ErpPurchaseReturnDO> purchaseReturns = purchaseReturnMapper.selectListByOrderId(orderId); |
| | | // 1.2 æ¥è¯¢å¯¹åºçéè´è®¢å项çéè´§æ°é |
| | | Map<Long, BigDecimal> returnCountMap = purchaseReturnItemMapper.selectOrderItemCountSumMapByReturnIds( |
| | | convertList(purchaseReturns, ErpPurchaseReturnDO::getId)); |
| | | // 2. æ´æ°éè´è®¢åçåºåºæ°é |
| | | purchaseOrderService.updatePurchaseOrderReturnCount(orderId, returnCountMap); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updatePurchaseReturnStatus(Long id, Integer status) { |
| | | boolean approve = ErpAuditStatus.APPROVE.getStatus().equals(status); |
| | | // 1.1 æ ¡éªåå¨ |
| | | ErpPurchaseReturnDO purchaseReturn = validatePurchaseReturnExists(id); |
| | | // 1.2 æ ¡éªç¶æ |
| | | if (purchaseReturn.getStatus().equals(status)) { |
| | | throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL); |
| | | } |
| | | // 1.3 æ ¡éªå·²é款 |
| | | if (!approve && purchaseReturn.getRefundPrice().compareTo(BigDecimal.ZERO) > 0) { |
| | | throw exception(PURCHASE_RETURN_PROCESS_FAIL_EXISTS_REFUND); |
| | | } |
| | | |
| | | // 2. æ´æ°ç¶æ |
| | | int updateCount = purchaseReturnMapper.updateByIdAndStatus(id, purchaseReturn.getStatus(), |
| | | new ErpPurchaseReturnDO().setStatus(status)); |
| | | if (updateCount == 0) { |
| | | throw exception(approve ? PURCHASE_RETURN_APPROVE_FAIL : PURCHASE_RETURN_PROCESS_FAIL); |
| | | } |
| | | |
| | | // 3. å®¡æ ¸éè¿æ¶ï¼åæ¥ MES ä»å¨åè´§ï¼éè´éè´§åºåºï¼ |
| | | if (approve) { |
| | | syncToWmsPurchaseReturnOutbound(purchaseReturn); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 忥éè´éè´§å° WMSï¼MES ä»å¨ï¼åºå |
| | | */ |
| | | private void syncToWmsPurchaseReturnOutbound(ErpPurchaseReturnDO purchaseReturn) { |
| | | List<ErpPurchaseReturnItemDO> purchaseReturnItems = purchaseReturnItemMapper.selectListByReturnId(purchaseReturn.getId()); |
| | | if (CollUtil.isEmpty(purchaseReturnItems)) { |
| | | return; |
| | | } |
| | | WmsPurchaseInboundReqDTO reqDTO = new WmsPurchaseInboundReqDTO() |
| | | .setBizId(purchaseReturn.getId()) |
| | | .setBizCode(purchaseReturn.getNo()) |
| | | .setVendorId(purchaseReturn.getSupplierId()) |
| | | .setReceiptTime(purchaseReturn.getReturnTime()) |
| | | .setItems(convertList(purchaseReturnItems, item -> new WmsPurchaseInboundReqDTO.Item() |
| | | .setItemId(item.getProductId()) |
| | | .setWarehouseId(item.getWarehouseId()) |
| | | .setQuantity(item.getCount()) |
| | | .setBizLineId(item.getId()))); |
| | | try { |
| | | wmsStockApi.purchaseReturnOutbound(reqDTO); |
| | | } catch (Exception e) { |
| | | log.error("[syncToWmsPurchaseReturnOutbound] åæ¥å¤±è´¥ï¼purchaseReturnId={}", purchaseReturn.getId(), e); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void updatePurchaseReturnRefundPrice(Long id, BigDecimal refundPrice) { |
| | | ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id); |
| | | if (purchaseReturn.getRefundPrice().equals(refundPrice)) { |
| | | return; |
| | | } |
| | | if (refundPrice.compareTo(purchaseReturn.getTotalPrice()) > 0) { |
| | | throw exception(PURCHASE_RETURN_FAIL_REFUND_PRICE_EXCEED, refundPrice, purchaseReturn.getTotalPrice()); |
| | | } |
| | | purchaseReturnMapper.updateById(new ErpPurchaseReturnDO().setId(id).setRefundPrice(refundPrice)); |
| | | } |
| | | |
| | | private List<ErpPurchaseReturnItemDO> validatePurchaseReturnItems(List<ErpPurchaseReturnSaveReqVO.Item> list) { |
| | | // 1. æ ¡éªç©æåå¨ï¼ç©æä¸»æ°æ®å·²è¿ç§»å° MDMï¼ |
| | | Set<Long> productIds = convertSet(list, ErpPurchaseReturnSaveReqVO.Item::getProductId); |
| | | Map<Long, MdmItemRespDTO> itemMap = productIds.isEmpty() ? Collections.emptyMap() : mdmItemApi.getItemMap(productIds); |
| | | if (itemMap.size() != productIds.size()) { |
| | | throw exception(PRODUCT_NOT_EXISTS); |
| | | } |
| | | // 2. 转å为 ErpPurchaseReturnItemDO å表 |
| | | return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseReturnItemDO.class, item -> { |
| | | item.setProductUnitId(itemMap.get(item.getProductId()).getUnitMeasureId()); |
| | | item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); |
| | | if (item.getTotalPrice() == null) { |
| | | return; |
| | | } |
| | | if (item.getTaxPercent() != null) { |
| | | item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); |
| | | } |
| | | })); |
| | | } |
| | | |
| | | private void updatePurchaseReturnItemList(Long id, List<ErpPurchaseReturnItemDO> newList) { |
| | | // ç¬¬ä¸æ¥ï¼å¯¹æ¯æ°èæ°æ®ï¼è·å¾æ·»å ãä¿®æ¹ãå é¤çå表 |
| | | List<ErpPurchaseReturnItemDO> oldList = purchaseReturnItemMapper.selectListByReturnId(id); |
| | | List<List<ErpPurchaseReturnItemDO>> diffList = diffList(oldList, newList, // id ä¸åï¼å°±è®¤ä¸ºæ¯ä¸åçè®°å½ |
| | | (oldVal, newVal) -> oldVal.getId().equals(newVal.getId())); |
| | | |
| | | // ç¬¬äºæ¥ï¼æ¹éæ·»å ãä¿®æ¹ãå é¤ |
| | | if (CollUtil.isNotEmpty(diffList.get(0))) { |
| | | diffList.get(0).forEach(o -> o.setReturnId(id)); |
| | | purchaseReturnItemMapper.insertBatch(diffList.get(0)); |
| | | } |
| | | if (CollUtil.isNotEmpty(diffList.get(1))) { |
| | | purchaseReturnItemMapper.updateBatch(diffList.get(1)); |
| | | } |
| | | if (CollUtil.isNotEmpty(diffList.get(2))) { |
| | | purchaseReturnItemMapper.deleteByIds(convertList(diffList.get(2), ErpPurchaseReturnItemDO::getId)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deletePurchaseReturn(List<Long> ids) { |
| | | // 1. æ ¡éªä¸å¤äºå·²å®¡æ¹ |
| | | List<ErpPurchaseReturnDO> purchaseReturns = purchaseReturnMapper.selectByIds(ids); |
| | | if (CollUtil.isEmpty(purchaseReturns)) { |
| | | return; |
| | | } |
| | | purchaseReturns.forEach(purchaseReturn -> { |
| | | if (ErpAuditStatus.APPROVE.getStatus().equals(purchaseReturn.getStatus())) { |
| | | throw exception(PURCHASE_RETURN_DELETE_FAIL_APPROVE, purchaseReturn.getNo()); |
| | | } |
| | | }); |
| | | |
| | | // 2. éåå é¤ï¼å¹¶è®°å½æä½æ¥å¿ |
| | | purchaseReturns.forEach(purchaseReturn -> { |
| | | // 2.1 å é¤è®¢å |
| | | purchaseReturnMapper.deleteById(purchaseReturn.getId()); |
| | | // 2.2 å é¤è®¢å项 |
| | | purchaseReturnItemMapper.deleteByReturnId(purchaseReturn.getId()); |
| | | |
| | | // 2.3 æ´æ°éè´è®¢åçåºåºæ°é |
| | | updatePurchaseOrderReturnCount(purchaseReturn.getOrderId()); |
| | | }); |
| | | |
| | | } |
| | | |
| | | private ErpPurchaseReturnDO validatePurchaseReturnExists(Long id) { |
| | | ErpPurchaseReturnDO purchaseReturn = purchaseReturnMapper.selectById(id); |
| | | if (purchaseReturn == null) { |
| | | throw exception(PURCHASE_RETURN_NOT_EXISTS); |
| | | } |
| | | return purchaseReturn; |
| | | } |
| | | |
| | | @Override |
| | | public ErpPurchaseReturnDO getPurchaseReturn(Long id) { |
| | | return purchaseReturnMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public ErpPurchaseReturnDO validatePurchaseReturn(Long id) { |
| | | ErpPurchaseReturnDO purchaseReturn = getPurchaseReturn(id); |
| | | if (ObjectUtil.notEqual(purchaseReturn.getStatus(), ErpAuditStatus.APPROVE.getStatus())) { |
| | | throw exception(PURCHASE_RETURN_NOT_APPROVE); |
| | | } |
| | | return purchaseReturn; |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<ErpPurchaseReturnDO> getPurchaseReturnPage(ErpPurchaseReturnPageReqVO pageReqVO) { |
| | | return purchaseReturnMapper.selectPage(pageReqVO); |
| | | } |
| | | |
| | | // ==================== éè´é货项 ==================== |
| | | |
| | | @Override |
| | | public List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnId(Long returnId) { |
| | | return purchaseReturnItemMapper.selectListByReturnId(returnId); |
| | | } |
| | | |
| | | @Override |
| | | public List<ErpPurchaseReturnItemDO> getPurchaseReturnItemListByReturnIds(Collection<Long> returnIds) { |
| | | if (CollUtil.isEmpty(returnIds)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | return purchaseReturnItemMapper.selectListByReturnIds(returnIds); |
| | | } |
| | | |
| | | } |