| | |
| | | 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.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; |
| | |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.request.ErpPurchaseRequestDeptStatisticsRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDeptStatisticsDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderItemMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseOrderMapper; |
| | |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import com.mzt.logapi.starter.annotation.LogRecord; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.flowable.engine.repository.ProcessDefinition; |
| | |
| | | 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.*; |
| | | import static cn.iocoder.yudao.module.erp.enums.LogRecordConstants.*; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | |
| | | /** |
| | |
| | | throw exception(PURCHASE_REQUEST_NO_EXISTS); |
| | | } |
| | | |
| | | // 4. 获取当前用户信息 |
| | | Long userId = Long.parseLong(getCurrentUserId()); |
| | | // 4. 解析申请人 / 申请部门:未指定时取当前登录用户及其所属部门 |
| | | Long userId = createReqVO.getRequestUserId() != null |
| | | ? createReqVO.getRequestUserId() : Long.parseLong(getCurrentUserId()); |
| | | AdminUserRespDTO user = adminUserApi.getUser(userId); |
| | | Long deptId = user != null ? user.getDeptId() : null; |
| | | Long deptId = createReqVO.getRequestDeptId() != null |
| | | ? createReqVO.getRequestDeptId() : (user != null ? user.getDeptId() : null); |
| | | |
| | | // 5. 插入申请单 |
| | | ErpPurchaseRequestDO purchaseRequest = BeanUtils.toBean(createReqVO, ErpPurchaseRequestDO.class, req -> req |
| | |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | @LogRecord(type = ERP_PURCHASE_REQUEST_TYPE, subType = ERP_PURCHASE_REQUEST_IMPORT_SUB_TYPE, bizNo = "0", |
| | | success = ERP_PURCHASE_REQUEST_IMPORT_SUCCESS) |
| | | public ErpPurchaseRequestImportRespVO importPurchaseRequestList(List<ErpPurchaseRequestImportExcelVO> importList) { |
| | | ErpPurchaseRequestImportRespVO respVO = ErpPurchaseRequestImportRespVO.builder() |
| | | .createList(new ArrayList<>()) |
| | |
| | | .failureList(new LinkedHashMap<>()) |
| | | .build(); |
| | | if (CollUtil.isEmpty(importList)) { |
| | | // 记录导入结果汇总,供 @LogRecord 的 success 文案引用 |
| | | OperateLogUtils.putImportSummary(respVO.getCreateList(), respVO.getUpdateList(), respVO.getFailureList()); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | respVO.getFailureList().put(label, resolveImportErrorMessage(ex)); |
| | | } |
| | | } |
| | | // 记录导入结果汇总,供 @LogRecord 的 success 文案引用 |
| | | OperateLogUtils.putImportSummary(respVO.getCreateList(), respVO.getUpdateList(), respVO.getFailureList()); |
| | | return respVO; |
| | | } |
| | | |
| | |
| | | |
| | | private void calculateTotalPrice(ErpPurchaseRequestDO purchaseRequest, List<ErpPurchaseRequestItemDO> requestItems) { |
| | | purchaseRequest.setTotalCount(getSumValue(requestItems, ErpPurchaseRequestItemDO::getCount, BigDecimal::add)); |
| | | purchaseRequest.setTotalProductPrice(getSumValue(requestItems, ErpPurchaseRequestItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | | purchaseRequest.setTotalTaxPrice(getSumValue(requestItems, ErpPurchaseRequestItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | | purchaseRequest.setTotalPrice(purchaseRequest.getTotalProductPrice().add(purchaseRequest.getTotalTaxPrice())); |
| | | // 明细的 totalPrice 是「数量 × 含税单价」,即含税总价;不含税金额 = 含税总价 − 税额 |
| | | BigDecimal totalPriceWithTax = getSumValue(requestItems, ErpPurchaseRequestItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO); |
| | | BigDecimal totalTaxPrice = getSumValue(requestItems, ErpPurchaseRequestItemDO::getTaxPrice, BigDecimal::add, BigDecimal.ZERO); |
| | | purchaseRequest.setTotalTaxPrice(totalTaxPrice); |
| | | purchaseRequest.setTotalPrice(totalPriceWithTax); |
| | | purchaseRequest.setTotalProductPrice(totalPriceWithTax.subtract(totalTaxPrice)); |
| | | // 计算优惠价格 |
| | | if (purchaseRequest.getDiscountPercent() == null) { |
| | | purchaseRequest.setDiscountPercent(BigDecimal.ZERO); |
| | |
| | | private List<ErpPurchaseRequestItemDO> validatePurchaseRequestItems(List<ErpPurchaseRequestSaveReqVO.Item> list) { |
| | | return convertList(list, o -> BeanUtils.toBean(o, ErpPurchaseRequestItemDO.class, item -> { |
| | | if (item.getProductPrice() != null && item.getCount() != null) { |
| | | // 产品单价为含税单价:行总价 = 数量 × 含税单价,税额再从含税总价里倒算 |
| | | item.setTotalPrice(MoneyUtils.priceMultiply(item.getProductPrice(), item.getCount())); |
| | | if (item.getTaxPercent() != null) { |
| | | item.setTaxPrice(MoneyUtils.priceMultiplyPercent(item.getTotalPrice(), item.getTaxPercent())); |
| | | } |
| | | item.setTaxPrice(MoneyUtils.priceCalculateTaxIncluded(item.getTotalPrice(), item.getTaxPercent())); |
| | | } |
| | | })); |
| | | } |
| | |
| | | } |
| | | |
| | | // 3. 创建 BPM 流程实例 |
| | | // 全局免审批:业务校验通过后直接执行审批通过逻辑,无需流程模型。 |
| | | if (processInstanceApi.isGlobalAutoApprove()) { |
| | | purchaseRequestMapper.updateById(new ErpPurchaseRequestDO() |
| | | .setId(id) |
| | | .setStatus(ErpPurchaseRequestStatusEnum.PROCESS.getStatus()) |
| | | .setProcessInstanceId("")); |
| | | updatePurchaseRequestAuditStatus(id, cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum.APPROVE.getStatus()); |
| | | return; |
| | | } |
| | | |
| | | String processInstanceId = processInstanceApi.createProcessInstance(userId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(processDefinitionKey) |
| | |
| | | return purchaseOrder.getId(); |
| | | } |
| | | |
| | | @Override |
| | | public List<ErpPurchaseRequestDeptStatisticsRespVO> getDeptStatistics(LocalDateTime beginTime, LocalDateTime endTime) { |
| | | List<ErpPurchaseRequestDeptStatisticsDO> statisticsList = purchaseRequestMapper.selectDeptStatistics(beginTime, endTime); |
| | | if (CollUtil.isEmpty(statisticsList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(statisticsList, ErpPurchaseRequestDeptStatisticsDO::getDeptId)); |
| | | return BeanUtils.toBean(statisticsList, ErpPurchaseRequestDeptStatisticsRespVO.class, vo -> { |
| | | if (vo.getDeptId() == null) { |
| | | vo.setDeptName("未指定部门"); |
| | | return; |
| | | } |
| | | DeptRespDTO dept = deptMap.get(vo.getDeptId()); |
| | | // 部门已删除时回退为可读编号,避免出现空白的部门列 |
| | | vo.setDeptName(dept != null ? dept.getName() : "部门(ID=" + vo.getDeptId() + ")"); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 校验采购申请审批分类和流程定义是否存在 |
| | | */ |