ContractFlow.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. package com.fjhx.sale.flow;
  2. import cn.hutool.core.util.ObjectUtil;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
  5. import com.baomidou.mybatisplus.core.toolkit.IdWorker;
  6. import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
  7. import com.fjhx.area.utils.CustomizeAreaUtil;
  8. import com.fjhx.common.entity.InOutBo;
  9. import com.fjhx.common.enums.CodingRuleEnum;
  10. import com.fjhx.common.enums.FlowStatusEnum1;
  11. import com.fjhx.common.enums.InOutType;
  12. import com.fjhx.common.service.coding.CodingRuleService;
  13. import com.fjhx.common.service.file.FtpFileService;
  14. import com.fjhx.file.utils.ObsFileUtil;
  15. import com.fjhx.flow.core.FlowDelegate;
  16. import com.fjhx.flow.enums.FlowStatusEnum;
  17. import com.fjhx.item.entity.product.po.ProductBomDetail;
  18. import com.fjhx.item.entity.product.po.ProductInfo;
  19. import com.fjhx.item.entity.product.po.ProductStockInfo;
  20. import com.fjhx.item.enums.ProductAvailableRecordType;
  21. import com.fjhx.item.service.product.ProductBomDetailService;
  22. import com.fjhx.item.service.product.ProductInfoService;
  23. import com.fjhx.item.service.product.ProductStockInfoService;
  24. import com.fjhx.purchase.entity.subscribe.po.SubscribeDetail;
  25. import com.fjhx.purchase.service.subscribe.SubscribeDetailService;
  26. import com.fjhx.sale.entity.contract.dto.ContractDto;
  27. import com.fjhx.sale.entity.contract.po.Contract;
  28. import com.fjhx.sale.entity.contract.po.ContractProduct;
  29. import com.fjhx.sale.entity.contract.po.ContractProject;
  30. import com.fjhx.sale.service.contract.ContractProductService;
  31. import com.fjhx.sale.service.contract.ContractProjectService;
  32. import com.fjhx.sale.service.contract.ContractService;
  33. import com.ruoyi.common.annotation.LogicIgnore;
  34. import com.ruoyi.common.core.domain.BasePo;
  35. import com.ruoyi.common.core.domain.entity.SysDept;
  36. import com.ruoyi.common.core.domain.entity.SysUser;
  37. import com.ruoyi.common.exception.ServiceException;
  38. import com.ruoyi.common.utils.SecurityUtils;
  39. import com.ruoyi.common.utils.StringUtils;
  40. import com.ruoyi.framework.mybatis.holder.TenantHolder;
  41. import com.ruoyi.system.service.ISysDeptService;
  42. import org.springframework.beans.factory.annotation.Autowired;
  43. import org.springframework.stereotype.Component;
  44. import java.math.BigDecimal;
  45. import java.util.*;
  46. import java.util.stream.Collectors;
  47. /**
  48. * 外销合同流程
  49. *
  50. * @Author:caozj
  51. * @DATE:2023/4/3 17:38
  52. */
  53. @Component
  54. //@DS(SourceConstant.SALE)
  55. public class ContractFlow extends FlowDelegate {
  56. @Autowired
  57. private CodingRuleService codingRuleService;
  58. @Autowired
  59. private ContractService contractService;
  60. @Autowired
  61. private ContractProductService contractProductService;
  62. @Autowired
  63. private ContractProjectService contractProjectService;
  64. @Autowired
  65. private FtpFileService ftpFileService;
  66. @Autowired
  67. private ProductInfoService productInfoService;
  68. @Autowired
  69. private SubscribeDetailService subscribeDetailService;
  70. @Autowired
  71. private ProductBomDetailService productBomDetailService;
  72. @Autowired
  73. private ISysDeptService deptService;
  74. @Autowired
  75. private ProductStockInfoService productStockInfoService;
  76. @Override
  77. public String getFlowKey() {
  78. return "contract_flow";
  79. }
  80. /**
  81. * 发起流程
  82. *
  83. * @param flowId 流程ID
  84. * @param submitData 采购付款数据
  85. * @return 业务id
  86. */
  87. @Override
  88. public Long start(Long flowId, JSONObject submitData) {
  89. ContractDto contract = submitData.toJavaObject(ContractDto.class);
  90. contract.setFlowId(flowId);
  91. if (StringUtils.isEmpty(contract.getCurrency())) {
  92. throw new ServiceException("币种不能为空");
  93. }
  94. contract.setCode(codingRuleService.createCode(CodingRuleEnum.CONTRACT.getKey(), contract.getBuyCorporationId()));
  95. // 保存合同产品
  96. List<ContractProduct> contractProductList = contract.getContractProductList();
  97. // 赋值待处理数量
  98. if (CollectionUtils.isNotEmpty(contractProductList)) {
  99. contractProductList.forEach(item -> item.setExpendQuantity(item.getQuantity()));
  100. }
  101. return start(contract);
  102. }
  103. /**
  104. * 结束流程
  105. *
  106. * @param flowId 流程ID
  107. * @param businessId 业务ID
  108. * @param submitData 数据
  109. */
  110. @Override
  111. public void end(Long flowId, Long businessId, JSONObject submitData) {
  112. // 通过业务ID查询合同数据
  113. Contract contract = contractService.getById(businessId);
  114. if (ObjectUtils.isEmpty(contract)) {
  115. throw new ServiceException("合同不存在");
  116. }
  117. // 修改合同状态为审批通过
  118. contract.setStatus(FlowStatusEnum1.PASS.getKey());
  119. contract.setApprovedDate(new Date());
  120. contractService.updateById(contract);
  121. //生成物料待采购数据
  122. createMaterialWaitPurchase(contract);
  123. }
  124. public Long start(ContractDto contract) {
  125. // 赋值城市省份信息
  126. CustomizeAreaUtil.setAreaId(contract);
  127. SysUser loginUser = SecurityUtils.getLoginUser().getUser();
  128. contract.setUserName(loginUser.getNickName());
  129. contract.setBuyCityId(contract.getCityId());
  130. contract.setBuyCountryId(contract.getCountryId());
  131. contract.setBuyProvinceId(contract.getProvinceId());
  132. contract.setStatus(FlowStatusEnum1.UNDER_REVIEW.getKey());
  133. //根据归属公司将数据赋值给指定公司
  134. Long companyId = contract.getCompanyId();
  135. TenantHolder.setIgnore(true);
  136. SysDept country = deptService.getById(companyId);
  137. TenantHolder.clear();
  138. if (ObjectUtil.isEmpty(country)) {
  139. throw new ServiceException("归属公司不存在");
  140. }
  141. SecurityUtils.setTenantId(country.getTenantId());
  142. contractService.saveOrUpdate(contract);
  143. SecurityUtils.clearTenantId();
  144. // 保存合同产品
  145. List<ContractProduct> contractProductList = contract.getContractProductList();
  146. contractProductList = ObjectUtil.isEmpty(contractProductList) ? new ArrayList<>() : contractProductList;
  147. for (ContractProduct c : contractProductList) {
  148. //对新数据创建id
  149. c.setId(ObjectUtil.isEmpty(c.getId()) ? IdWorker.getId() : c.getId());
  150. //赋值合同Id
  151. c.setContractId(contract.getId());
  152. //保存设计稿图
  153. ObsFileUtil.editFile(c.getFileList(), c.getId());
  154. //保存生产源文件
  155. String prodFilePath = c.getProdFilePath();
  156. if (ObjectUtil.isNotEmpty(prodFilePath) && prodFilePath.startsWith("/temp")) {
  157. String targetFolderPath = String.format("/contractProduct/%s", c.getId());
  158. com.alibaba.fastjson2.JSONObject prodFile = ftpFileService.moveFolder(prodFilePath, targetFolderPath);
  159. c.setProdFilePath(prodFile.getString("path"));
  160. }
  161. }
  162. contractProductService.editLinked(contractProductList, ContractProduct::getContractId, contract.getId());
  163. // 保存收费项目
  164. List<ContractProject> contractProjectList = contract.getContractProjectList();
  165. contractProjectList = ObjectUtil.isEmpty(contractProjectList) ? new ArrayList<>() : contractProjectList;
  166. for (ContractProject c : contractProjectList) {
  167. c.setContractId(contract.getId());
  168. }
  169. contractProjectService.editLinked(contractProjectList, ContractProject::getContractId, contract.getId());
  170. return contract.getId();
  171. }
  172. /**
  173. * 重新发起
  174. *
  175. * @param flowId
  176. * @param businessId
  177. * @param flowStatus
  178. * @param submitData
  179. */
  180. @Override
  181. @LogicIgnore
  182. public void relaunch(Long flowId, Long businessId, FlowStatusEnum flowStatus, JSONObject submitData) {
  183. //删除采购合同
  184. ContractDto contractDto = submitData.toJavaObject(ContractDto.class);
  185. if (ObjectUtils.isEmpty(contractDto)) {
  186. throw new ServiceException("合同数据不能为空");
  187. }
  188. start(contractDto);
  189. }
  190. /**
  191. * 驳回
  192. *
  193. * @param flowId
  194. * @param businessId
  195. * @param flowStatus
  196. */
  197. @Override
  198. public void reject(Long flowId, Long businessId, FlowStatusEnum flowStatus) {
  199. contractService.update(q -> q
  200. .eq(Contract::getId, businessId)
  201. .set(Contract::getStatus, 20)//20为驳回
  202. .set(Contract::getUpdateTime, new Date())
  203. .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
  204. );
  205. }
  206. @Override
  207. public void cancellation(Long flowId, Long businessId, FlowStatusEnum flowStatus) {
  208. super.cancellation(flowId, businessId, flowStatus);
  209. contractService.update(q -> q
  210. .eq(Contract::getId, businessId)
  211. .set(Contract::getStatus, FlowStatusEnum1.CANCELLATION.getKey())
  212. .set(BasePo::getUpdateTime, new Date())
  213. .set(BasePo::getUpdateUser, SecurityUtils.getUserId())
  214. );
  215. }
  216. /**
  217. * 创建合同产品 物料待采购信息
  218. */
  219. private void createMaterialWaitPurchase(Contract contract) {
  220. //生成物料待采购数据
  221. List<ContractProduct> contractProductList = contractProductService.list(q -> q.eq(ContractProduct::getContractId, contract.getId()));
  222. List<Long> productIds = contractProductList.stream().map(ContractProduct::getProductId).collect(Collectors.toList());
  223. //获取产品列表
  224. Map<Long, ProductInfo> productInfoMap = productInfoService.mapKEntity(ProductInfo::getId, q -> q.in(ProductInfo::getId, productIds));
  225. //获取物料信息
  226. Map<Long, List<ProductBomDetail>> productBomMap = productBomDetailService.mapKGroup(ProductBomDetail::getProductId, q -> q.in(ProductBomDetail::getProductId, productIds));
  227. //待采购列表
  228. List<SubscribeDetail> subscribeDetailList = new ArrayList<>();
  229. for (ContractProduct contractProduct : contractProductList) {
  230. Long productId = contractProduct.getProductId();
  231. ProductInfo productInfo = productInfoMap.get(productId);
  232. //获取物料列表,以及原材料
  233. List<ProductBomDetail> productBomDetails = productBomMap.get(productId);
  234. if (ObjectUtil.isNotEmpty(productBomDetails)) {
  235. List<Long> materialIds = productBomDetails.stream().map(ProductBomDetail::getMaterialId).collect(Collectors.toList());
  236. Map<Long, ProductInfo> materialMap = productInfoService.mapKEntity(ProductInfo::getId, q -> q.in(ProductInfo::getId, materialIds));
  237. //获取合同归属归属的物料可用库存
  238. Map<Long, BigDecimal> availableQuantityMap = productStockInfoService.mapKV(ProductStockInfo::getId, ProductStockInfo::getAvailableQuantity, q -> q
  239. .in(ProductStockInfo::getProductId, materialIds)
  240. .eq(ProductStockInfo::getCompanyId, contract.getCompanyId())
  241. );
  242. //遍历物料列表
  243. for (ProductBomDetail productBomDetail : productBomDetails) {
  244. Long materialId = productBomDetail.getMaterialId();
  245. ProductInfo materialInfo = materialMap.get(materialId);
  246. BigDecimal multiply = productBomDetail.getQuantity().multiply(contractProduct.getQuantity());
  247. //获取产品安全库存
  248. BigDecimal stockThreshold = materialInfo.getStockThreshold();
  249. //获取可用库存
  250. BigDecimal availableQuantity = availableQuantityMap.getOrDefault(materialId, BigDecimal.ZERO);
  251. //计数需要采购的数量(需采购量 = 安全库存 - (可用库存 - 合同量)若 需采购量<0,则按0算不采购)
  252. BigDecimal subtract = availableQuantity.subtract(multiply);
  253. BigDecimal requiredQuantity = stockThreshold.subtract(subtract);
  254. //计算可用库存
  255. if (requiredQuantity.compareTo(BigDecimal.ZERO) < 0) {
  256. //需要采购的数量<0 可用库存 = 可用库存 - 合同量
  257. InOutBo inOutBo = new InOutBo();
  258. inOutBo.setProductId(materialId);
  259. inOutBo.setQuantity(multiply);
  260. productInfoService.editAvailableQuantity(Arrays.asList(inOutBo), InOutType.OUT, contractProduct.getId(), ProductAvailableRecordType.SALE_PASS, contract.getCompanyId());
  261. } else {
  262. //需要采购的数量>=0 可用库存 = 安全库存
  263. InOutBo inOutBo = new InOutBo();
  264. inOutBo.setProductId(materialId);
  265. inOutBo.setQuantity(stockThreshold);
  266. productInfoService.editAvailableQuantity(Arrays.asList(inOutBo), InOutType.EQ, contractProduct.getId(), ProductAvailableRecordType.SALE_PASS, contract.getCompanyId());
  267. }
  268. //需要采购的数量大于0生成待采购数据
  269. if (requiredQuantity.compareTo(BigDecimal.ZERO) > 0) {
  270. SubscribeDetail subscribeDetail = new SubscribeDetail();
  271. subscribeDetail.setProductId(materialId);
  272. subscribeDetail.setCount(requiredQuantity);
  273. subscribeDetail.setStatus(15);//待采购
  274. subscribeDetail.setContractId(contract.getId());
  275. subscribeDetail.setContractDetailId(contractProduct.getId());
  276. subscribeDetail.setDataType(1);
  277. subscribeDetailList.add(subscribeDetail);
  278. }
  279. }
  280. }
  281. }
  282. //根据归属公司将数据赋值给指定公司
  283. Long companyId = contract.getCompanyId();
  284. SysDept company = deptService.getById(companyId);
  285. if (ObjectUtil.isEmpty(companyId)) {
  286. throw new ServiceException("归属公司不存在");
  287. }
  288. //保存待采购明细
  289. subscribeDetailService.saveBatch(subscribeDetailList);
  290. }
  291. }