| | |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationOptionRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationPageReqVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationRevisionRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.quotation.vo.CrmSaleQuotationSaveReqVO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.bidbond.CrmBidBondDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationProductDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.quotation.CrmSaleQuotationRevisionDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| | | import cn.iocoder.yudao.module.crm.dal.mysql.bidbond.CrmBidBondMapper; |
| | | import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService; |
| | | import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; |
| | | import cn.iocoder.yudao.module.crm.enums.quotation.CrmSaleQuotationStatusEnum; |
| | | import cn.iocoder.yudao.module.crm.service.quotation.CrmSaleQuotationExcelRenderer; |
| | | import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; |
| | | import cn.iocoder.yudao.module.crm.service.quotation.CrmSaleQuotationService; |
| | | import cn.iocoder.yudao.module.system.service.companyconfig.SystemCompanyConfigService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.net.URLEncoder; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.UPDATE; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.SALE_QUOTATION_EXPORT_FAIL_NOT_APPROVE; |
| | | import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.SALE_QUOTATION_NOT_EXISTS; |
| | | |
| | | @Tag(name = "管理后台 - CRM 销售报价单") |
| | | @RestController |
| | |
| | | |
| | | @Resource |
| | | private CrmSaleQuotationService saleQuotationService; |
| | | |
| | | @Resource |
| | | private CrmBidBondMapper bidBondMapper; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | |
| | | @Resource |
| | | private CrmCustomerService customerService; |
| | | @Resource |
| | | private CrmContactService contactService; |
| | | @Resource |
| | | private CrmBusinessService businessService; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | @Resource |
| | | private SystemCompanyConfigService companyConfigService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "创建销售报价单") |
| | |
| | | ExcelUtils.write(response, "销售报价单.xls", "数据", CrmSaleQuotationRespVO.class, list); |
| | | } |
| | | |
| | | @GetMapping({"/export-word", "/export-quotation-excel"}) |
| | | @Operation(summary = "导出销售报价单 Excel(带电子章)") |
| | | @Parameter(name = "id", description = "报价单编号", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:export')") |
| | | @ApiAccessLog(operateType = EXPORT) |
| | | public void exportSaleQuotationWord(@RequestParam("id") Long id, |
| | | HttpServletResponse response) throws IOException { |
| | | CrmSaleQuotationDO saleQuotation = saleQuotationService.getSaleQuotation(id); |
| | | if (saleQuotation == null) { |
| | | throw exception(SALE_QUOTATION_NOT_EXISTS); |
| | | } |
| | | if (!CrmSaleQuotationStatusEnum.APPROVE.getStatus().equals(saleQuotation.getStatus()) |
| | | && !CrmSaleQuotationStatusEnum.CONVERTED.getStatus().equals(saleQuotation.getStatus())) { |
| | | CrmSaleQuotationStatusEnum current = CrmSaleQuotationStatusEnum.getByStatus(saleQuotation.getStatus()); |
| | | throw exception(SALE_QUOTATION_EXPORT_FAIL_NOT_APPROVE, saleQuotation.getNo(), |
| | | current != null ? current.getName() : String.valueOf(saleQuotation.getStatus())); |
| | | } |
| | | CrmSaleQuotationRespVO respVO = buildSaleQuotationRespVO(saleQuotation, |
| | | saleQuotationService.getSaleQuotationProductListByQuotationId(id)); |
| | | var companyConfig = companyConfigService.getCompanyConfig(); |
| | | String companyName = companyConfig != null ? companyConfig.getCompanyName() : null; |
| | | var customer = saleQuotation.getCustomerId() == null ? null : customerService.getCustomer(saleQuotation.getCustomerId()); |
| | | var contact = saleQuotation.getContactId() == null ? null : contactService.getContact(saleQuotation.getContactId()); |
| | | String phone = contact == null ? null : (contact.getMobile() != null && !contact.getMobile().isBlank() |
| | | ? contact.getMobile() : contact.getTelephone()); |
| | | byte[] content = CrmSaleQuotationExcelRenderer.render(respVO, companyName, |
| | | customer == null ? null : customer.getDetailAddress(), contact == null ? null : contact.getName(), phone); |
| | | |
| | | String filename = "报价单-" + saleQuotation.getNo() |
| | | + (respVO.getCustomerName() != null ? "-" + respVO.getCustomerName() : "") + ".xlsx"; |
| | | String encoded = URLEncoder.encode(filename, StandardCharsets.UTF_8).replace("+", "%20"); |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
| | | response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + encoded); |
| | | response.getOutputStream().write(content); |
| | | } |
| | | |
| | | @PutMapping("/submit") |
| | | @Operation(summary = "提交销售报价审批") |
| | | @Parameter(name = "id", description = "报价单编号", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:update')") |
| | | public CommonResult<Boolean> submitSaleQuotation(@RequestParam("id") Long id, |
| | | @RequestParam(value = "processDefinitionKey", required = false) String processDefinitionKey) { |
| | | saleQuotationService.submitSaleQuotation(id, processDefinitionKey, getLoginUserId()); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/approve-process-list") |
| | | @Operation(summary = "获取销售报价审批流程列表") |
| | | public CommonResult<List<Map<String, Object>>> getSaleQuotationApproveProcessList() { |
| | | return success(saleQuotationService.getSaleQuotationApproveProcessDefinitionList()); |
| | | } |
| | | |
| | | @PostMapping("/convert-contract") |
| | | @Operation(summary = "转为合同") |
| | | @Parameter(name = "id", description = "报价单编号", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:convert-contract')") |
| | | public CommonResult<Long> convertToContract(@RequestParam("id") Long id) { |
| | | return success(saleQuotationService.convertToContract(id, getLoginUserId())); |
| | | } |
| | | |
| | | @PutMapping("/void") |
| | | @Operation(summary = "作废销售报价单") |
| | | @Parameter(name = "id", description = "报价单编号", required = true) |
| | | @Parameter(name = "reason", description = "作废原因", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:void')") |
| | | @ApiAccessLog(operateType = UPDATE) |
| | | public CommonResult<Boolean> voidSaleQuotation(@RequestParam("id") Long id, |
| | | @RequestParam("reason") String reason) { |
| | | saleQuotationService.voidSaleQuotation(id, reason); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/revision/list") |
| | | @Operation(summary = "获得报价单轮次流水列表") |
| | | @Parameter(name = "quotationId", description = "报价单编号", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:query')") |
| | | public CommonResult<List<CrmSaleQuotationRevisionRespVO>> getRevisionList( |
| | | @RequestParam("quotationId") Long quotationId) { |
| | | List<CrmSaleQuotationRevisionDO> list = saleQuotationService.getSaleQuotationRevisionList(quotationId); |
| | | return success(BeanUtils.toBean(list, CrmSaleQuotationRevisionRespVO.class)); |
| | | } |
| | | |
| | | @GetMapping("/revision/export-excel") |
| | | @Operation(summary = "导出报价单轮次流水 Excel") |
| | | @Parameter(name = "quotationId", description = "报价单编号", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:export')") |
| | | @ApiAccessLog(operateType = EXPORT) |
| | | public void exportRevisionExcel(@RequestParam("quotationId") Long quotationId, |
| | | HttpServletResponse response) throws IOException { |
| | | List<CrmSaleQuotationRevisionRespVO> list = BeanUtils.toBean( |
| | | saleQuotationService.getSaleQuotationRevisionList(quotationId), |
| | | CrmSaleQuotationRevisionRespVO.class); |
| | | ExcelUtils.write(response, "报价轮次流水.xls", "数据", CrmSaleQuotationRevisionRespVO.class, list); |
| | | } |
| | | |
| | | @GetMapping("/bindable-list") |
| | | @Operation(summary = "获得客户下可绑定投标的报价单列表") |
| | | @Parameter(name = "customerId", description = "客户编号", required = true) |
| | | @Parameter(name = "bidBondId", description = "当前投标保证金编号(编辑时用于保留自身绑定)", required = false) |
| | | @PreAuthorize("@ss.hasPermission('crm:sale-quotation:query')") |
| | | public CommonResult<List<CrmSaleQuotationOptionRespVO>> getBindableList( |
| | | @RequestParam("customerId") Long customerId, |
| | | @RequestParam(value = "bidBondId", required = false) Long bidBondId) { |
| | | return success(BeanUtils.toBean( |
| | | saleQuotationService.getBindableQuotationList(customerId, bidBondId), |
| | | CrmSaleQuotationOptionRespVO.class)); |
| | | } |
| | | |
| | | // ==================== 构建响应 VO ==================== |
| | |
| | | } |
| | | // 3. 填充关联信息 |
| | | fillSaleQuotationRelatedFields(respVO); |
| | | // 3.1 填充关联投标的中标结果 |
| | | if (saleQuotation.getBidBondId() != null) { |
| | | CrmBidBondDO bidBond = bidBondMapper.selectById(saleQuotation.getBidBondId()); |
| | | if (bidBond != null) { |
| | | respVO.setBidResult(bidBond.getBidResult()); |
| | | } |
| | | } |
| | | // 4. 填充附件列表 |
| | | respVO.setAttachmentList(storageAttachmentApi.listAttachments("crm_sale_quotation", saleQuotation.getId())); |
| | | return respVO; |
| | |
| | | // 1.6 商机信息(过滤 null,businessId 为可选字段) |
| | | Map<Long, CrmBusinessDO> businessMap = businessService.getBusinessMap( |
| | | convertSet(pageResult.getList(), CrmSaleQuotationDO::getBusinessId, vo -> vo.getBusinessId() != null)); |
| | | // 1.7 投标信息(过滤 null,bidBondId 为可选字段) |
| | | Set<Long> bidBondIds = convertSet(pageResult.getList(), CrmSaleQuotationDO::getBidBondId, |
| | | vo -> vo.getBidBondId() != null); |
| | | Map<Long, CrmBidBondDO> bidBondMap = bidBondIds.isEmpty() ? Map.of() |
| | | : convertMap(bidBondMapper.selectByIds(bidBondIds), CrmBidBondDO::getId); |
| | | |
| | | // 2. 开始拼接 |
| | | return BeanUtils.toBean(pageResult, CrmSaleQuotationRespVO.class, vo -> { |
| | |
| | | MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); |
| | | MapUtils.findAndThen(deptMap, vo.getOwnerUserId(), dept -> vo.setOwnerUserDeptName(dept.getName())); |
| | | MapUtils.findAndThen(businessMap, vo.getBusinessId(), business -> vo.setBusinessName(business.getName())); |
| | | MapUtils.findAndThen(bidBondMap, vo.getBidBondId(), bidBond -> vo.setBidResult(bidBond.getBidResult())); |
| | | }); |
| | | } |
| | | |