Răsfoiți Sursa

新增采购退货接口

fgd 1 an în urmă
părinte
comite
55c439f687
23 a modificat fișierele cu 1075 adăugiri și 4 ștergeri
  1. 1 1
      sd-business/src/main/java/com/sd/business/controller/purchase/PurchaseBomController.java
  2. 75 0
      sd-business/src/main/java/com/sd/business/controller/purchase/PurchaseReturnController.java
  3. 16 0
      sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnBomDto.java
  4. 24 0
      sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnDto.java
  5. 18 0
      sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnSelectDto.java
  6. 47 0
      sd-business/src/main/java/com/sd/business/entity/purchase/po/PurchaseReturn.java
  7. 49 0
      sd-business/src/main/java/com/sd/business/entity/purchase/po/PurchaseReturnBom.java
  8. 53 0
      sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnBomVo.java
  9. 23 0
      sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnDetailVo.java
  10. 33 0
      sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnVo.java
  11. 5 0
      sd-business/src/main/java/com/sd/business/entity/warehouse/constant/WarehouseConstant.java
  12. 163 0
      sd-business/src/main/java/com/sd/business/flow/PurchaseReturnFlow.java
  13. 27 0
      sd-business/src/main/java/com/sd/business/mapper/purchase/PurchaseReturnBomMapper.java
  14. 25 0
      sd-business/src/main/java/com/sd/business/mapper/purchase/PurchaseReturnMapper.java
  15. 10 0
      sd-business/src/main/java/com/sd/business/service/inventory/InventoryService.java
  16. 61 1
      sd-business/src/main/java/com/sd/business/service/inventory/impl/InventoryServiceImpl.java
  17. 26 0
      sd-business/src/main/java/com/sd/business/service/purchase/PurchaseReturnBomService.java
  18. 62 0
      sd-business/src/main/java/com/sd/business/service/purchase/PurchaseReturnService.java
  19. 26 0
      sd-business/src/main/java/com/sd/business/service/purchase/impl/PurchaseReturnBomServiceImpl.java
  20. 277 0
      sd-business/src/main/java/com/sd/business/service/purchase/impl/PurchaseReturnServiceImpl.java
  21. 5 2
      sd-business/src/main/java/com/sd/business/util/CodeEnum.java
  22. 29 0
      sd-business/src/main/resources/mapper/purchase/PurchaseReturnBomMapper.xml
  23. 20 0
      sd-business/src/main/resources/mapper/purchase/PurchaseReturnMapper.xml

+ 1 - 1
sd-business/src/main/java/com/sd/business/controller/purchase/PurchaseBomController.java

@@ -22,7 +22,7 @@ public class PurchaseBomController {
     private PurchaseBomService purchaseBomService;
 
     /**
-     * 采购合同 bom分页
+     * 采购入库 bom分页
      */
     @PostMapping("/page")
     public Page<PurchaseBomSelectVo> page(@RequestBody PurchaseBomSelectDto dto) {

+ 75 - 0
sd-business/src/main/java/com/sd/business/controller/purchase/PurchaseReturnController.java

@@ -0,0 +1,75 @@
+package com.sd.business.controller.purchase;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.fjhx.flow.enums.FlowStatusEnum;
+import com.ruoyi.common.core.domain.BaseSelectDto;
+import com.sd.business.entity.purchase.dto.PurchaseReturnDto;
+import com.sd.business.entity.purchase.dto.PurchaseReturnSelectDto;
+import com.sd.business.entity.purchase.vo.PurchaseReturnBomVo;
+import com.sd.business.entity.purchase.vo.PurchaseReturnDetailVo;
+import com.sd.business.entity.purchase.vo.PurchaseReturnVo;
+import com.sd.business.service.purchase.PurchaseReturnService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 采购退货 前端控制器
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@RestController
+@RequestMapping("/purchaseReturn")
+public class PurchaseReturnController {
+    @Autowired
+    private PurchaseReturnService purchaseReturnService;
+
+    /**
+     * 采购退货分页
+     */
+    @PostMapping("/page")
+    public Page<PurchaseReturnVo> page(@RequestBody PurchaseReturnSelectDto dto) {
+        return purchaseReturnService.getPage(dto);
+    }
+
+    /**
+     * 采购退货明细
+     */
+    @PostMapping("/detail")
+    public PurchaseReturnDetailVo detail(@RequestBody BaseSelectDto dto) {
+        return purchaseReturnService.detail(dto.getId());
+    }
+
+    /**
+     * 采购退货新增
+     */
+    @PostMapping("/add")
+    public void add(@RequestBody PurchaseReturnDto purchaseReturnDto) {
+        purchaseReturnDto.setFlowStatus(FlowStatusEnum.READY_START.getKey());
+        purchaseReturnService.add(purchaseReturnDto);
+    }
+
+    /**
+     * 采购退货编辑
+     */
+    @PostMapping("/edit")
+    public void edit(@RequestBody PurchaseReturnDto purchaseReturnDto) {
+        purchaseReturnService.edit(purchaseReturnDto);
+    }
+
+
+    /**
+     * 采购合同bom列表
+     */
+    @PostMapping("/getPurchaseBomPage")
+    public List<PurchaseReturnBomVo> getPurchaseBomPage(@RequestBody BaseSelectDto dto) {
+        return purchaseReturnService.getPurchaseBomPage(dto.getId());
+    }
+}

+ 16 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnBomDto.java

@@ -0,0 +1,16 @@
+package com.sd.business.entity.purchase.dto;
+
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 采购退货bom新增编辑入参实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnBomDto extends PurchaseReturnBom {
+}

+ 24 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnDto.java

@@ -0,0 +1,24 @@
+package com.sd.business.entity.purchase.dto;
+
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 采购退货新增编辑入参实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnDto extends PurchaseReturn {
+
+    /**
+     * 采购退货bom数据
+     */
+    private List<PurchaseReturnBom> purchaseReturnBomList;
+}

+ 18 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/dto/PurchaseReturnSelectDto.java

@@ -0,0 +1,18 @@
+package com.sd.business.entity.purchase.dto;
+
+
+import com.ruoyi.common.core.domain.BaseSelectDto;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 采购退货列表查询入参实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnSelectDto extends BaseSelectDto {
+
+}

+ 47 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/po/PurchaseReturn.java

@@ -0,0 +1,47 @@
+package com.sd.business.entity.purchase.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.core.domain.BasePo;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * <p>
+ * 采购退货
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+@TableName("purchase_return")
+public class PurchaseReturn extends BasePo {
+
+    /**
+     * 退货单号
+     */
+    private String code;
+
+    /**
+     * 退货金额
+     */
+    private BigDecimal returnAmount;
+
+    /**
+     * 退货备注
+     */
+    private String remark;
+
+    /**
+     * 流程id
+     */
+    private Long flowId;
+
+    /**
+     * 流程状态 0未发起 1进行中 2已通过 3已驳回 4已作废
+     */
+    private Integer flowStatus;
+}

+ 49 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/po/PurchaseReturnBom.java

@@ -0,0 +1,49 @@
+package com.sd.business.entity.purchase.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.ruoyi.common.core.domain.BasePo;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+
+/**
+ * <p>
+ * 采购退货bom
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+@TableName("purchase_return_bom")
+public class PurchaseReturnBom extends BasePo {
+
+    /**
+     * 采购退货id
+     */
+    private Long purchaseReturnId;
+
+    /**
+     * 采购合同id
+     */
+    private Long purchaseId;
+
+    /**
+     * 采购合同Bom id
+     */
+    private Long purchaseBomId;
+
+    /**
+     * bom规格id
+     */
+    private Long bomSpecId;
+
+    /**
+     * 退货数量
+     */
+    private BigDecimal returnQuantity;
+
+}

+ 53 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnBomVo.java

@@ -0,0 +1,53 @@
+package com.sd.business.entity.purchase.vo;
+
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 采购退货bom列表查询返回值实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnBomVo extends PurchaseReturnBom {
+
+    /**
+     * 采购合同单号
+     */
+    private String purchaseCode;
+
+    /**
+     * 品号
+     */
+    private String bomSpecCode;
+
+    /**
+     * 品名
+     */
+    private String bomSpecName;
+
+    /**
+     * 含税单价
+     */
+    private BigDecimal unitPrice;
+
+    /**
+     * 采购数量
+     */
+    private BigDecimal purchaseQuantity;
+
+    /**
+     * 到货数量
+     */
+    private BigDecimal arrivalQuantity;
+
+    /**
+     * 已退货数量
+     */
+    private BigDecimal finishReturnQuantity;
+}

+ 23 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnDetailVo.java

@@ -0,0 +1,23 @@
+package com.sd.business.entity.purchase.vo;
+
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.List;
+
+/**
+ * 采购退货详情 查询返回值实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnDetailVo extends PurchaseReturn {
+
+    /**
+     * 退货bom列表
+     */
+    List<PurchaseReturnBomVo> returnBomList;
+}

+ 33 - 0
sd-business/src/main/java/com/sd/business/entity/purchase/vo/PurchaseReturnVo.java

@@ -0,0 +1,33 @@
+package com.sd.business.entity.purchase.vo;
+
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.Date;
+
+/**
+ * 采购退货列表查询返回值实体
+ *
+ * @author
+ * @since 2023-09-27
+ */
+@Getter
+@Setter
+public class PurchaseReturnVo extends PurchaseReturn {
+
+    /**
+     * 关联合同数量
+     */
+    private Long purchaseCount;
+
+    /**
+     * 退货时间
+     */
+    private Date returnTime;
+
+    /**
+     * 发起人
+     */
+    private String createName;
+}

+ 5 - 0
sd-business/src/main/java/com/sd/business/entity/warehouse/constant/WarehouseConstant.java

@@ -15,4 +15,9 @@ public interface WarehouseConstant {
      */
     Long PACKAGING_MATERIAL = 1684037201379213314L;
 
+    /**
+     * 采购次品仓
+     */
+    Long PURCHASE_DEFECTIVE = 1684037092708990977L;
+
 }

+ 163 - 0
sd-business/src/main/java/com/sd/business/flow/PurchaseReturnFlow.java

@@ -0,0 +1,163 @@
+package com.sd.business.flow;
+
+import com.alibaba.fastjson.JSONObject;
+import com.fjhx.flow.core.FlowDelegate;
+import com.fjhx.flow.core.FlowThreadLocalUtil;
+import com.fjhx.flow.enums.FlowStatusEnum;
+import com.ruoyi.common.core.domain.BaseIdPo;
+import com.ruoyi.common.core.domain.BasePo;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.sd.business.entity.purchase.dto.PurchaseReturnDto;
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import com.sd.business.service.inventory.InventoryService;
+import com.sd.business.service.purchase.PurchaseReturnBomService;
+import com.sd.business.service.purchase.PurchaseReturnService;
+import com.sd.framework.util.Assert;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 发起采购退货流程
+ */
+@Service
+public class PurchaseReturnFlow extends FlowDelegate {
+
+    @Autowired
+    private PurchaseReturnService purchaseReturnService;
+
+    @Autowired
+    private PurchaseReturnBomService purchaseReturnBomService;
+
+    @Autowired
+    private InventoryService inventoryService;
+
+    @Override
+    public String getFlowKey() {
+        return "purchase_return";
+    }
+
+    @Override
+    public Long start(Long flowId, JSONObject submitData) {
+        PurchaseReturnDto purchaseReturn = submitData.toJavaObject(PurchaseReturnDto.class);
+        validated(purchaseReturn);
+        purchaseReturn.setFlowStatus(FlowStatusEnum.IN_PROGRESS.getKey());
+        purchaseReturn.setFlowId(flowId);
+
+        if (purchaseReturn.getId() == null) {
+            purchaseReturnService.add(purchaseReturn);
+        } else {
+            purchaseReturnService.edit(purchaseReturn);
+        }
+
+        return purchaseReturn.getId();
+    }
+
+    @Override
+    public void end(Long flowId, Long businessId, JSONObject submitData) {
+        purchaseReturnService.update(q -> q
+                .eq(BaseIdPo::getId, businessId)
+                .set(PurchaseReturn::getFlowStatus, FlowStatusEnum.PASS.getKey())
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
+        );
+        // 出库采购退货
+        purchaseReturnService.outPurchaseReturn(businessId);
+    }
+
+    /**
+     * 流程退回到发起人
+     *
+     * @param flowId     流程id
+     * @param businessId 业务id
+     * @param flowStatus 流程状态枚举
+     */
+    @Override
+    public void returnToOriginator(Long flowId, Long businessId, FlowStatusEnum flowStatus) {
+        purchaseReturnService.update(q -> q
+                .eq(BaseIdPo::getId, businessId)
+                .set(PurchaseReturn::getFlowStatus, flowStatus.getKey())
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
+        );
+    }
+
+    /**
+     * 流程重新发起
+     *
+     * @param flowId     流程id
+     * @param businessId 业务id
+     * @param flowStatus 流程状态枚举
+     * @param submitData 发起参数
+     */
+    @Override
+    public void relaunch(Long flowId, Long businessId, FlowStatusEnum flowStatus, JSONObject submitData) {
+        PurchaseReturnDto purchaseReturn = submitData.toJavaObject(PurchaseReturnDto.class);
+        validated(purchaseReturn);
+        purchaseReturnService.update(q -> q.eq(BaseIdPo::getId, businessId)
+                .eq(PurchaseReturn::getFlowId, flowId)
+                .set(PurchaseReturn::getFlowStatus, FlowStatusEnum.IN_PROGRESS.getKey())
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId()));
+    }
+
+    /**
+     * 流程驳回
+     *
+     * @param flowId     流程id
+     * @param businessId 业务id
+     * @param flowStatus 流程状态枚举
+     */
+    @Override
+    public void reject(Long flowId, Long businessId, FlowStatusEnum flowStatus) {
+        purchaseReturnService.update(q -> q
+                .eq(BaseIdPo::getId, FlowThreadLocalUtil.getBusinessId())
+                .set(PurchaseReturn::getFlowStatus, flowStatus.getKey())
+                .set(BasePo::getUpdateTime, new Date())
+                .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
+        );
+        // 驳回解锁库存
+        List<PurchaseReturnBom> purchaseReturnBomList = purchaseReturnBomService.list(q -> q
+                .eq(PurchaseReturnBom::getPurchaseReturnId, businessId));
+
+        Map<Long, BigDecimal> bomMap = purchaseReturnBomList.stream()
+                .collect(Collectors.toMap(
+                        PurchaseReturnBom::getBomSpecId,
+                        PurchaseReturnBom::getReturnQuantity,
+                        BigDecimal::add
+                ));
+        inventoryService.unlockPurchaseDefectiveStorage(bomMap);
+    }
+
+    /**
+     * 作废
+     *
+     * @param flowId     流程id
+     * @param businessId 业务id
+     * @param flowStatus 流程状态枚举
+     */
+    @Override
+    public void cancellation(Long flowId, Long businessId, FlowStatusEnum flowStatus) {
+        reject(flowId, businessId, flowStatus);
+    }
+
+    /**
+     * 验证数据
+     */
+    private void validated(PurchaseReturnDto purchaseReturnDto) {
+        List<PurchaseReturnBom> purchaseReturnBomList = purchaseReturnDto.getPurchaseReturnBomList();
+        Assert.notEmpty(purchaseReturnBomList, "采购退货bom数据不能为空");
+
+        for (PurchaseReturnBom purchaseReturnBom : purchaseReturnBomList) {
+            Assert.notNull(purchaseReturnBom.getPurchaseBomId(), "bom规格Id不能为空");
+            Assert.notNull(purchaseReturnBom.getReturnQuantity(), "退货数量不能为空");
+        }
+    }
+
+}

+ 27 - 0
sd-business/src/main/java/com/sd/business/mapper/purchase/PurchaseReturnBomMapper.java

@@ -0,0 +1,27 @@
+package com.sd.business.mapper.purchase;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import com.sd.business.entity.purchase.vo.PurchaseReturnBomVo;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 采购退货bom Mapper 接口
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+public interface PurchaseReturnBomMapper extends BaseMapper<PurchaseReturnBom> {
+
+    /**
+     * 查询采购退货bom列表
+     * @param wrapper
+     * @return
+     */
+    List<PurchaseReturnBomVo> getList(@Param("ew") IWrapper<PurchaseReturnBom> wrapper);
+}

+ 25 - 0
sd-business/src/main/java/com/sd/business/mapper/purchase/PurchaseReturnMapper.java

@@ -0,0 +1,25 @@
+package com.sd.business.mapper.purchase;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import com.sd.business.entity.purchase.vo.PurchaseReturnVo;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * <p>
+ * 采购退货 Mapper 接口
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+public interface PurchaseReturnMapper extends BaseMapper<PurchaseReturn> {
+
+    /**
+     * 采购合同分页
+     */
+    Page<PurchaseReturnVo> getPage(@Param("page") Page<Object> page, @Param("ew") IWrapper<PurchaseReturn> wrapper);
+
+}

+ 10 - 0
sd-business/src/main/java/com/sd/business/service/inventory/InventoryService.java

@@ -103,4 +103,14 @@ public interface InventoryService extends BaseService<Inventory> {
      */
     void purchaseInStorage(Long purchaseId, Long departmentId, Long warehouseId, List<? extends InOutFun> list);
 
+    /**
+     * 锁定采购次品仓库存
+     */
+    boolean lockPurchaseDefectiveStorage(Map<Long, BigDecimal> map);
+
+    /**
+     * 解锁采购次品仓库存
+     */
+    void unlockPurchaseDefectiveStorage(Map<Long, BigDecimal> map);
+
 }

+ 61 - 1
sd-business/src/main/java/com/sd/business/service/inventory/impl/InventoryServiceImpl.java

@@ -1,6 +1,7 @@
 package com.sd.business.service.inventory.impl;
 
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -407,7 +408,7 @@ public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory
                 inventory.setQuantity(inventory.getQuantity().add(lockQuantity));
                 inventory.setLockQuantity(inventory.getLockQuantity().subtract(lockQuantity));
             }
-
+            updateBatchById(list);
         }
 
     }
@@ -472,6 +473,65 @@ public class InventoryServiceImpl extends ServiceImpl<InventoryMapper, Inventory
         }
     }
 
+    @Override
+    public boolean lockPurchaseDefectiveStorage(Map<Long, BigDecimal> map) {
+        if (ObjectUtil.isEmpty(map)) {
+            return true;
+        }
+        synchronized (this) {
+
+            List<Inventory> list = list(q -> q
+                    .in(Inventory::getBomSpecId, map.keySet())
+                    .eq(Inventory::getWarehouseId, WarehouseConstant.PURCHASE_DEFECTIVE)
+                    .eq(Inventory::getDepartmentId, DepartmentConstant.SD_SPORTS)
+            );
+
+            if (map.size() != list.size()) {
+                return false;
+            }
+
+            for (Inventory inventory : list) {
+                BigDecimal lockQuantity = map.get(inventory.getBomSpecId());
+                if (inventory.getQuantity().compareTo(lockQuantity) >= 0) {
+                    inventory.setQuantity(inventory.getQuantity().subtract(lockQuantity));
+                    inventory.setLockQuantity(inventory.getLockQuantity().add(lockQuantity));
+                } else {
+                    return false;
+                }
+            }
+
+            this.updateBatchById(list);
+        }
+
+        return true;
+    }
+
+
+    @Override
+    public void unlockPurchaseDefectiveStorage(Map<Long, BigDecimal> map) {
+
+        if (ObjectUtil.isEmpty(map)) {
+            return;
+        }
+
+        synchronized (this) {
+
+            List<Inventory> list = list(q -> q
+                    .in(Inventory::getBomSpecId, map.keySet())
+                    .eq(Inventory::getWarehouseId, WarehouseConstant.PURCHASE_DEFECTIVE)
+                    .eq(Inventory::getDepartmentId, DepartmentConstant.SD_SPORTS)
+            );
+
+            for (Inventory inventory : list) {
+                BigDecimal lockQuantity = map.get(inventory.getBomSpecId());
+                inventory.setQuantity(inventory.getQuantity().add(lockQuantity));
+                inventory.setLockQuantity(inventory.getLockQuantity().subtract(lockQuantity));
+            }
+            updateBatchById(list);
+        }
+
+    }
+
     /**
      * 通过事业部id和出库id获取bom规格库存
      */

+ 26 - 0
sd-business/src/main/java/com/sd/business/service/purchase/PurchaseReturnBomService.java

@@ -0,0 +1,26 @@
+package com.sd.business.service.purchase;
+
+import com.ruoyi.common.core.service.BaseService;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import com.sd.business.entity.purchase.vo.PurchaseReturnBomVo;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 采购退货bom 服务类
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+public interface PurchaseReturnBomService extends BaseService<PurchaseReturnBom> {
+
+
+    /**
+     * 根据采购退货id查询退货bom列表
+     * @param purchaseReturnId
+     * @return
+     */
+    List<PurchaseReturnBomVo> selectListByPurchaseReturnId(Long purchaseReturnId);
+}

+ 62 - 0
sd-business/src/main/java/com/sd/business/service/purchase/PurchaseReturnService.java

@@ -0,0 +1,62 @@
+package com.sd.business.service.purchase;
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.common.core.service.BaseService;
+import com.sd.business.entity.purchase.dto.PurchaseReturnDto;
+import com.sd.business.entity.purchase.dto.PurchaseReturnSelectDto;
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import com.sd.business.entity.purchase.vo.PurchaseReturnBomVo;
+import com.sd.business.entity.purchase.vo.PurchaseReturnDetailVo;
+import com.sd.business.entity.purchase.vo.PurchaseReturnVo;
+
+import java.util.List;
+
+/**
+ * <p>
+ * 采购退货 服务类
+ * </p>
+ *
+ * @author
+ * @since 2023-09-27
+ */
+public interface PurchaseReturnService extends BaseService<PurchaseReturn> {
+    /**
+     * 采购退货分页
+     * @param dto
+     * @return
+     */
+    Page<PurchaseReturnVo> getPage(PurchaseReturnSelectDto dto);
+
+    /**
+     * 采购退货新增
+     * @param purchaseReturnDto
+     */
+    void add(PurchaseReturnDto purchaseReturnDto);
+
+    /**
+     * 采购退货编辑
+     * @param purchaseReturnDto
+     */
+    void edit(PurchaseReturnDto purchaseReturnDto);
+
+    /**
+     * 采购退货详情
+     * @param id
+     * @return
+     */
+    PurchaseReturnDetailVo detail(Long id);
+
+    /**
+     * 采购合同bom列表
+     *
+     * @param purchaseId
+     * @return
+     */
+    List<PurchaseReturnBomVo> getPurchaseBomPage(Long purchaseId);
+
+    /**
+     * 出库采购退货
+     * @param id
+     */
+    void outPurchaseReturn(Long id);
+}

+ 26 - 0
sd-business/src/main/java/com/sd/business/service/purchase/impl/PurchaseReturnBomServiceImpl.java

@@ -0,0 +1,26 @@
+package com.sd.business.service.purchase.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import com.sd.business.entity.purchase.vo.PurchaseReturnBomVo;
+import com.sd.business.mapper.purchase.PurchaseReturnBomMapper;
+import com.sd.business.service.purchase.PurchaseReturnBomService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collections;
+import java.util.List;
+
+@Service
+public class PurchaseReturnBomServiceImpl extends ServiceImpl<PurchaseReturnBomMapper, PurchaseReturnBom> implements PurchaseReturnBomService {
+    @Override
+    public List<PurchaseReturnBomVo> selectListByPurchaseReturnId(Long purchaseReturnId) {
+        if (purchaseReturnId == null) {
+            return Collections.emptyList();
+        }
+        IWrapper<PurchaseReturnBom> wrapper = getWrapper();
+        wrapper.eq("prb", PurchaseReturnBom::getPurchaseReturnId, purchaseReturnId);
+        wrapper.orderByDesc("prb", PurchaseReturnBom::getId);
+        return this.baseMapper.getList(wrapper);
+    }
+}

+ 277 - 0
sd-business/src/main/java/com/sd/business/service/purchase/impl/PurchaseReturnServiceImpl.java

@@ -0,0 +1,277 @@
+package com.sd.business.service.purchase.impl;
+
+import cn.hutool.core.bean.BeanUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.dynamic.datasource.annotation.DSTransactional;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fjhx.flow.enums.FlowStatusEnum;
+import com.ruoyi.common.constant.StatusConstant;
+import com.ruoyi.common.core.domain.BaseIdPo;
+import com.ruoyi.common.core.domain.entity.SysUser;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.common.utils.wrapper.IWrapper;
+import com.ruoyi.system.service.ISysUserService;
+import com.sd.business.entity.bom.po.BomSpec;
+import com.sd.business.entity.department.constant.DepartmentConstant;
+import com.sd.business.entity.in.dto.InOutStorageDto;
+import com.sd.business.entity.in.emums.InOutTypeEnum;
+import com.sd.business.entity.in.emums.OutDetailTypeEnum;
+import com.sd.business.entity.in.po.InOutStorageBom;
+import com.sd.business.entity.inventory.po.Inventory;
+import com.sd.business.entity.purchase.dto.PurchaseReturnDto;
+import com.sd.business.entity.purchase.dto.PurchaseReturnSelectDto;
+import com.sd.business.entity.purchase.po.Purchase;
+import com.sd.business.entity.purchase.po.PurchaseBom;
+import com.sd.business.entity.purchase.po.PurchaseReturn;
+import com.sd.business.entity.purchase.po.PurchaseReturnBom;
+import com.sd.business.entity.purchase.vo.*;
+import com.sd.business.entity.warehouse.constant.WarehouseConstant;
+import com.sd.business.mapper.purchase.PurchaseReturnMapper;
+import com.sd.business.service.bom.BomSpecService;
+import com.sd.business.service.in.InOutStorageService;
+import com.sd.business.service.inventory.InventoryService;
+import com.sd.business.service.purchase.PurchaseBomService;
+import com.sd.business.service.purchase.PurchaseReturnBomService;
+import com.sd.business.service.purchase.PurchaseReturnService;
+import com.sd.business.service.purchase.PurchaseService;
+import com.sd.business.util.CodeEnum;
+import com.sd.framework.util.Assert;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@Service
+public class PurchaseReturnServiceImpl extends ServiceImpl<PurchaseReturnMapper, PurchaseReturn> implements PurchaseReturnService {
+    @Autowired
+    private ISysUserService userService;
+
+    @Autowired
+    private PurchaseReturnBomService purchaseReturnBomService;
+
+    @Autowired
+    private InventoryService inventoryService;
+
+    @Autowired
+    private BomSpecService bomSpecService;
+
+    @Autowired
+    private PurchaseService purchaseService;
+
+    @Autowired
+    private PurchaseBomService purchaseBomService;
+
+    @Autowired
+    private InOutStorageService inOutStorageService;
+
+    @Override
+    public Page<PurchaseReturnVo> getPage(PurchaseReturnSelectDto dto) {
+        IWrapper<PurchaseReturn> wrapper = getWrapper();
+        wrapper.orderByDesc("pr", PurchaseReturn::getId);
+
+        // 查询采购退货数据
+        Page<PurchaseReturnVo> page = this.baseMapper.getPage(dto.getPage(), wrapper);
+
+        List<PurchaseReturnVo> records = page.getRecords();
+        if (records.size() == 0) {
+            return page;
+        }
+        records.forEach(item -> {
+            // 采购合同数量
+            long count = purchaseReturnBomService.count(q -> q
+                    .eq(PurchaseReturnBom::getPurchaseReturnId, item.getId())
+                    .groupBy(PurchaseReturnBom::getPurchaseId));
+            // 发起人
+            SysUser user = userService.selectUserById(item.getCreateUser());
+            // 如果采购退货通过,退货时间取最新的更新时间
+            if (Objects.equals(FlowStatusEnum.PASS.getKey(), item.getFlowStatus())) {
+                item.setReturnTime(item.getUpdateTime());
+            }
+            item.setPurchaseCount(count);
+            item.setCreateName(user.getNickName());
+        });
+        return page;
+    }
+
+    @DSTransactional
+    @Override
+    public void add(PurchaseReturnDto purchaseReturnDto) {
+        // 保存采购退货
+        purchaseReturnDto.setCode(CodeEnum.PURCHASE_TH_CODE.getCode());
+        this.save(purchaseReturnDto);
+
+        List<PurchaseReturnBom> purchaseReturnBomList = purchaseReturnDto.getPurchaseReturnBomList();
+        // bom id
+        List<Long> bomSpecIds = purchaseReturnBomList.stream().map(PurchaseReturnBom::getBomSpecId).collect(Collectors.toList());
+        Assert.notEmpty(bomSpecIds, "采购bom列表不能为空");
+        // bom库存
+        Map<Long, Inventory> inventoryMap = inventoryService.mapKEntity(Inventory::getBomSpecId,
+                q -> q.eq(Inventory::getWarehouseId, WarehouseConstant.PURCHASE_DEFECTIVE)
+                        .eq(Inventory::getDepartmentId, DepartmentConstant.SD_SPORTS)
+                        .in(Inventory::getBomSpecId, bomSpecIds));
+        // bom规格信息
+        Map<Long, BomSpec> bomSpecMap = bomSpecService.mapKEntity(BaseIdPo::getId, q -> q.in(BaseIdPo::getId, bomSpecIds));
+        purchaseReturnBomList.forEach(item -> {
+            item.setPurchaseReturnId(purchaseReturnDto.getId());
+            BomSpec bomSpec = bomSpecMap.get(item.getPurchaseBomId());
+            if (bomSpec == null) {
+                throw new ServiceException("未知bom规格id!");
+            }
+            Inventory inventory = inventoryMap.get(item.getPurchaseBomId());
+            if (inventory == null || inventory.getQuantity().compareTo(item.getReturnQuantity()) < 0) {
+                throw new ServiceException("退货失败,品名为:" + bomSpec.getName() + " 的bom库存不足,库存为:" +
+                        (inventory == null ? BigDecimal.ZERO : inventory.getQuantity()));
+            }
+        });
+        Map<Long, BigDecimal> map = purchaseReturnBomList.stream()
+                .collect(Collectors.toMap(
+                        PurchaseReturnBom::getBomSpecId,
+                        PurchaseReturnBom::getReturnQuantity,
+                        BigDecimal::add
+                ));
+
+        boolean result = inventoryService.lockPurchaseDefectiveStorage(map);
+        if (!result) {
+            throw new ServiceException("库存不足,退货失败");
+        }
+        purchaseReturnBomService.saveBatch(purchaseReturnBomList);
+    }
+
+    @Override
+    public void edit(PurchaseReturnDto purchaseReturnDto) {
+        // 状态为未发起时, 流程重新发起
+        if (ObjectUtil.equals(purchaseReturnDto.getFlowStatus(), FlowStatusEnum.READY_START.getKey())) {
+            purchaseReturnDto.setFlowStatus(FlowStatusEnum.IN_PROGRESS.getKey());
+        }
+        // 保存采购退货
+        this.updateById(purchaseReturnDto);
+
+        List<PurchaseReturnBom> newPurchaseReturnBomList = purchaseReturnDto.getPurchaseReturnBomList();
+        Assert.notEmpty(newPurchaseReturnBomList, "采购bom列表不能为空");
+        List<PurchaseReturnBom> oldPurchaseReturnBomList = purchaseReturnBomService.list(q -> q
+                .eq(PurchaseReturnBom::getPurchaseReturnId, purchaseReturnDto.getId()));
+
+        // 旧采购bom
+        Map<Long, BigDecimal> oldBomMap = oldPurchaseReturnBomList.stream()
+                .collect(Collectors.toMap(
+                        PurchaseReturnBom::getBomSpecId,
+                        PurchaseReturnBom::getReturnQuantity,
+                        BigDecimal::add
+                ));
+        Map<Long, BigDecimal> newBomMap = newPurchaseReturnBomList.stream()
+                .collect(Collectors.toMap(
+                        PurchaseReturnBom::getBomSpecId,
+                        PurchaseReturnBom::getReturnQuantity,
+                        BigDecimal::add
+                ));
+        // 查询所有bom进行对比
+        Set<Long> bomIds = new HashSet<>(oldBomMap.keySet());
+        bomIds.addAll(newBomMap.keySet());
+        Map<Long, BigDecimal> lockBomMap = new HashMap<>();
+        Map<Long, BigDecimal> unlockBomMap = new HashMap<>();
+        for (Long bomId : bomIds) {
+            BigDecimal oldReturnQuantity = oldBomMap.get(bomId);
+            BigDecimal newReturnQuantity = newBomMap.get(bomId);
+            // 旧bom没有,新增锁定库存,反之则解锁库存
+            if (oldReturnQuantity == null) {
+                lockBomMap.put(bomId, newReturnQuantity);
+                continue;
+            } else if (newReturnQuantity == null) {
+                unlockBomMap.put(bomId, oldReturnQuantity);
+                continue;
+            }
+            // 都有值,判断数量
+            if (oldReturnQuantity.compareTo(newReturnQuantity) == 0) {
+                continue;
+            }
+            // 旧bom大于新bom则解锁,反之加锁库存
+            if (oldReturnQuantity.compareTo(newReturnQuantity) > 0) {
+                unlockBomMap.put(bomId, oldReturnQuantity.subtract(newReturnQuantity));
+            } else {
+                lockBomMap.put(bomId, newReturnQuantity.subtract(oldReturnQuantity));
+            }
+        }
+        // 对原数据有修改时,更新数据
+        if (!lockBomMap.isEmpty() || !unlockBomMap.isEmpty()) {
+            if (!lockBomMap.isEmpty()) {
+                boolean result = inventoryService.lockPurchaseDefectiveStorage(lockBomMap);
+                if (!result) {
+                    throw new ServiceException("库存不足,新增退货失败");
+                }
+            }
+            if (!unlockBomMap.isEmpty()) {
+                inventoryService.unlockPurchaseDefectiveStorage(unlockBomMap);
+            }
+            purchaseReturnBomService.editLinked(newPurchaseReturnBomList, PurchaseReturnBom::getPurchaseReturnId, purchaseReturnDto.getId());
+        }
+    }
+
+    @Override
+    public PurchaseReturnDetailVo detail(Long id) {
+        PurchaseReturn purchaseReturn = this.getById(id);
+        PurchaseReturnDetailVo result = BeanUtil.toBean(purchaseReturn, PurchaseReturnDetailVo.class);
+        // 查询bom列表
+        List<PurchaseReturnBomVo> purchaseReturnBomList = purchaseReturnBomService.selectListByPurchaseReturnId(purchaseReturn.getId());
+        result.setReturnBomList(purchaseReturnBomList);
+        return result;
+    }
+
+    @Override
+    public List<PurchaseReturnBomVo> getPurchaseBomPage(Long purchaseId) {
+        Purchase purchase = purchaseService.getById(purchaseId);
+        if (purchase == null) {
+            return Collections.emptyList();
+        }
+        List<PurchaseBom> list = purchaseBomService.list(q -> q.eq(PurchaseBom::getPurchaseId, purchaseId));
+        if (list.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<Long> bomIds = list.stream().map(PurchaseBom::getBomSpecId).collect(Collectors.toList());
+        Map<Long, BomSpec> bomSpecMap = bomSpecService.mapKEntity(BaseIdPo::getId, q -> q.in(BaseIdPo::getId, bomIds));
+        List<PurchaseReturnBomVo> purchaseBomList = list.stream().map(item -> {
+            BomSpec bomSpec = bomSpecMap.get(item.getBomSpecId());
+            PurchaseReturnBomVo vo = new PurchaseReturnBomVo();
+            vo.setBomSpecId(bomSpec.getId());
+            vo.setPurchaseBomId(item.getId());
+            vo.setPurchaseId(purchaseId);
+            vo.setPurchaseCode(purchase.getCode());
+            vo.setBomSpecCode(bomSpec.getCode());
+            vo.setBomSpecName(bomSpec.getName());
+            vo.setUnitPrice(item.getUnitPrice());
+            vo.setPurchaseQuantity(item.getPurchaseQuantity());
+            vo.setArrivalQuantity(item.getArrivalQuantity());
+            vo.setFinishReturnQuantity(item.getReturnQuantity());
+            return vo;
+        }).collect(Collectors.toList());
+        return purchaseBomList;
+    }
+
+    @Override
+    public void outPurchaseReturn(Long id) {
+        PurchaseReturn purchaseReturn = this.getById(id);
+
+        List<PurchaseReturnBom> purchaseReturnBomList = purchaseReturnBomService.list(q -> q.eq(PurchaseReturnBom::getPurchaseReturnId, id));
+        // 出库信息
+        List<InOutStorageBom> inOutStorageBomList = purchaseReturnBomList.stream().map(item -> {
+            InOutStorageBom inOutStorageBom = new InOutStorageBom();
+            inOutStorageBom.setBomSpecId(item.getBomSpecId());
+            inOutStorageBom.setQuantity(item.getReturnQuantity());
+            return inOutStorageBom;
+        }).collect(Collectors.toList());
+        // 查询创建人
+        SysUser user = userService.selectUserById(purchaseReturn.getCreateUser());
+        InOutStorageDto lockPurchaseDefective = new InOutStorageDto();
+        lockPurchaseDefective.setType(InOutTypeEnum.OUT.getKey());
+        lockPurchaseDefective.setDetailType(OutDetailTypeEnum.PURCHASE.getKey());
+        lockPurchaseDefective.setWarehouseId(WarehouseConstant.PURCHASE_DEFECTIVE);
+        lockPurchaseDefective.setDepartmentId(DepartmentConstant.SD_SPORTS);
+        lockPurchaseDefective.setApplicant(user.getNickName());
+        lockPurchaseDefective.setRemark(purchaseReturn.getRemark());
+        lockPurchaseDefective.setLockStorage(StatusConstant.YES);
+        lockPurchaseDefective.setInOutStorageBomList(inOutStorageBomList);
+        inOutStorageService.add(lockPurchaseDefective);
+    }
+}

+ 5 - 2
sd-business/src/main/java/com/sd/business/util/CodeEnum.java

@@ -12,6 +12,7 @@ import com.sd.business.service.in.InOutStorageService;
 import com.sd.business.service.lend.LendService;
 import com.sd.business.service.order.OrderExchangeService;
 import com.sd.business.service.order.OrderService;
+import com.sd.business.service.purchase.PurchaseReturnService;
 import com.sd.business.service.purchase.PurchaseService;
 import com.sd.business.service.statement.StatementOfAccountService;
 import lombok.Getter;
@@ -33,13 +34,15 @@ public enum CodeEnum {
     // 对账单号
     STATEMENT_OF_ACCOUNT_CODE("SOA", "-yyMMdd-", "code", 6, StatementOfAccountService.class),
     // 采购单号
-    PURCHASE_CODE("PA", "-yyMMdd-", "code", 6, PurchaseService.class),
+    PURCHASE_CODE("PUR", "-yyMMdd-", "code", 6, PurchaseService.class),
     // 退货单号
     TH_CODE("TH", "-yyMMdd-", "code", 6, OrderExchangeService.class),
     // 换货单号
     HH_CODE("HH", "-yyMMdd-", "code", 6, OrderExchangeService.class),
     // 送货单号
-    SH_CODE("SH", "-yyMMddHH-", "delivery_code", 3, OrderService.class);
+    SH_CODE("SH", "-yyMMddHH-", "delivery_code", 3, OrderService.class),
+    // 采购退货单号
+    PURCHASE_TH_CODE("TH", "-yyMMdd-", "code", 6, PurchaseReturnService.class);
 
     // 编码前缀
     private final String prefix;

+ 29 - 0
sd-business/src/main/resources/mapper/purchase/PurchaseReturnBomMapper.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sd.business.mapper.purchase.PurchaseReturnBomMapper">
+
+    <select id="getPage" resultType="com.sd.business.entity.purchase.vo.PurchaseReturnBomVo">
+        select
+            prb.id,
+            prb.purchase_return_id,
+            prb.purchase_id,
+            prb.purchase_bom_id,
+            prb.return_quantity,
+            prb.create_user,
+            prb.create_time,
+            prb.update_user,
+            prb.update_time,
+            p.code purchaseCode,
+            bs.code bomSpecCode,
+            bs.name bomSpecName,
+            pb.unit_price unitPrice,
+            pb.purchase_quantity purchaseQuantity,
+            pb.arrival_quantity arrivalQuantity,
+            pb.return_quantity finishReturnQuantity
+        from purchase_return_bom prb
+            left join purchase_bom pb on prb.purchase_bom_id = pb.id
+            left join purchase p on pb.purchase_id = p.id
+            left join bom_spec bs on pb.bom_spec_id = bs.id
+            ${ew.customSqlSegment}
+    </select>
+</mapper>

+ 20 - 0
sd-business/src/main/resources/mapper/purchase/PurchaseReturnMapper.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.sd.business.mapper.purchase.PurchaseReturnMapper">
+    <select id="getPage" resultType="com.sd.business.entity.purchase.vo.PurchaseReturnVo">
+        select
+            pr.id,
+            pr.code,
+            pr.return_amount,
+            pr.remark,
+            pr.flow_id,
+            pr.flow_status,
+            pr.create_user,
+            pr.create_time,
+            pr.update_user,
+            pr.update_time
+        from purchase_return pr
+            ${ew.customSqlSegment}
+    </select>
+
+</mapper>