CRM-合同: 新增合同关联商品

This commit is contained in:
puhui999 2024-02-05 00:14:50 +08:00
parent e66ded2860
commit 84f3b230d2
24 changed files with 302 additions and 397 deletions

View File

@ -14,8 +14,8 @@ import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusiness
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeQueryVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.businessstatus.CrmBusinessStatusConvert;
import cn.iocoder.yudao.module.crm.convert.businessstatustype.CrmBusinessStatusTypeConvert;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusConvert;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessStatusTypeConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusTypeDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessStatusService;

View File

@ -1,18 +1,18 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.business;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.enums.business.CrmBizEndStatus;
import cn.iocoder.yudao.module.crm.framework.operatelog.core.CrmCustomerParseFunction;
import com.mzt.logapi.starter.annotation.DiffLogField;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ -58,7 +58,6 @@ public class CrmBusinessSaveReqVO {
@DiffLogField(name = "商机金额")
private Integer price;
// TODO @lzxhqs折扣使用 Integer 类型存储时默认 * 100展示的时候前端需要 / 100避免精度丢失问题
@Schema(description = "整单折扣")
@DiffLogField(name = "整单折扣")
private Integer discountPercent;
@ -75,11 +74,29 @@ public class CrmBusinessSaveReqVO {
@InEnum(CrmBizEndStatus.class)
private Integer endStatus;
// TODO @lzxhqs不设置默认 new ArrayList<>()一般 pojo 不设置默认值哈
@Schema(description = "商机产品列表")
private List<CrmBusinessProductSaveReqVO> products = new ArrayList<>();
@Schema(description = "联系人编号", example = "110")
private Long contactId; // 使用场景联系人详情添加商机时如果需要关联两者需要传递 contactId 字段
@Schema(description = "产品列表")
private List<CrmBusinessProductItem> productItems;
@Schema(description = "产品列表")
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class CrmBusinessProductItem {
@Schema(description = "产品编号", example = "20529")
@NotNull(message = "产品编号不能为空")
private Long id;
@Schema(description = "产品数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "8911")
@NotNull(message = "产品数量不能为空")
private Integer count;
@Schema(description = "产品折扣")
private Integer discountPercent;
}
}

View File

@ -1,15 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
// TODO @lzxhqs这个类如果没用到可以考虑删除哈
@Schema(description = "管理后台 - 商机产品分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class CrmBusinessProductPageReqVO extends PageParam {
}

View File

@ -1,11 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 商机产品关联 Response VO")
@Data
@ExcelIgnoreUnannotated
public class CrmBusinessProductRespVO {
}

View File

@ -1,49 +0,0 @@
package cn.iocoder.yudao.module.crm.controller.admin.business.vo.product;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.math.BigDecimal;
@Schema(description = "管理后台 - CRM 商机产品关联表 创建/更新 Request VO")
@Data
public class CrmBusinessProductSaveReqVO {
@Schema(description = "主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "32129")
private Long id;
@Schema(description = "商机编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "商机编号不能为空")
private Long businessId;
@Schema(description = "产品编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "产品编号不能为空")
private Long productId;
@Schema(description = "产品单价", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "产品单价不能为空")
private BigDecimal price;
@Schema(description = "销售价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "销售价格不能为空")
private BigDecimal salesPrice;
@Schema(description = "数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "数量不能为空")
private BigDecimal num;
@Schema(description = "折扣", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "折扣不能为空")
private BigDecimal discount;
@Schema(description = "小计(折扣后价格)", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotNull(message = "小计(折扣后价格)不能为空")
private BigDecimal subtotal;
@Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED, example = "30320")
@NotEmpty(message = "单位不能为空")
private String unit;
}

View File

@ -11,12 +11,11 @@ import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.*;
import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
@ -30,7 +29,6 @@ import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -43,7 +41,6 @@ import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@ -62,11 +59,7 @@ public class CrmContractController {
@Resource
private CrmBusinessService businessService;
@Resource
@Lazy
private CrmBusinessProductService businessProductService;
@Resource
private CrmProductService productService;
@Resource
private AdminUserApi adminUserApi;
@ -105,22 +98,9 @@ public class CrmContractController {
return success(null);
}
// 2.1 拼接合同信息
// 2. 拼接合同信息
List<CrmContractRespVO> respVOList = buildContractDetailList(Collections.singletonList(contract));
// 2.2 拼接产品信息
// TODO @puhui999下面这块也可以搞到 convert 里哈可以在 ContractDetailList 加个开关是不是查询商品信息psjdk21 的方法不太能去用因为 jdk8 项目要兼容
CrmContractRespVO respVO = respVOList.get(0);
List<CrmBusinessProductDO> businessProductList = businessProductService.getBusinessProductListByContractId(id);
Map<Long, CrmBusinessProductDO> businessProductMap = convertMap(businessProductList, CrmBusinessProductDO::getProductId);
List<CrmProductDO> productList = productService.getProductListByIds(convertSet(businessProductList, CrmBusinessProductDO::getProductId));
respVO.setProductItems(convertList(productList, product -> {
CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class);
findAndThen(businessProductMap, product.getId(), businessProduct -> {
productItemRespVO.setCount(businessProduct.getCount()).setDiscountPercent(businessProduct.getDiscountPercent());
});
return productItemRespVO;
}));
return success(respVO);
return success(respVOList.get(0));
}
@GetMapping("/page")
@ -151,6 +131,22 @@ public class CrmContractController {
BeanUtils.toBean(pageResult.getList(), CrmContractExcelVO.class));
}
@PutMapping("/transfer")
@Operation(summary = "合同转移")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
contractService.transferContract(reqVO, getLoginUserId());
return success(true);
}
@PutMapping("/submit")
@Operation(summary = "提交合同审批")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
contractService.submitContract(id, getLoginUserId());
return success(true);
}
/**
* 构建详细的合同结果
*
@ -173,23 +169,15 @@ public class CrmContractController {
// 4. 获取商机
Map<Long, CrmBusinessDO> businessMap = convertMap(businessService.getBusinessList(convertSet(contractList,
CrmContractDO::getBusinessId)), CrmBusinessDO::getId);
return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap);
// 5. 获取合同关联的商品
Map<Long, CrmContractProductDO> contractProductMap = null;
List<CrmProductDO> productList = null;
if (contractList.size() == 1) {
List<CrmContractProductDO> contractProductList = contractService.getContractProductListByContractId(contractList.get(0).getId());
contractProductMap = convertMap(contractProductList, CrmContractProductDO::getProductId);
productList = productService.getProductListByIds(convertSet(contractProductList, CrmContractProductDO::getProductId));
}
@PutMapping("/transfer")
@Operation(summary = "合同转移")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> transferContract(@Valid @RequestBody CrmContractTransferReqVO reqVO) {
contractService.transferContract(reqVO, getLoginUserId());
return success(true);
}
@PutMapping("/submit")
@Operation(summary = "提交合同审批")
@PreAuthorize("@ss.hasPermission('crm:contract:update')")
public CommonResult<Boolean> submitContract(@RequestParam("id") Long id) {
contractService.submitContract(id, getLoginUserId());
return success(true);
return CrmContractConvert.INSTANCE.convertList(contractList, userMap, customerList, contactMap, businessMap, contractProductMap, productList);
}
}

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.convert.businessstatus;
package cn.iocoder.yudao.module.crm.convert.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.status.CrmBusinessStatusRespVO;

View File

@ -1,4 +1,4 @@
package cn.iocoder.yudao.module.crm.convert.businessstatustype;
package cn.iocoder.yudao.module.crm.convert.business;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.type.CrmBusinessStatusTypeRespVO;

View File

@ -1,21 +0,0 @@
package cn.iocoder.yudao.module.crm.convert.businessproduct;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
// TODO @lzxhqs看看是不是用 BeanUtils 替代了
/**
* @author lzxhqs
* @version 1.0
* @title CrmBusinessProductConvert
* @description
* @create 2024/1/12
*/
@Mapper
public interface CrmBusinessProductConvert {
CrmBusinessProductConvert INSTANCE = Mappers.getMapper(CrmBusinessProductConvert.class);
CrmBusinessProductDO convert(CrmBusinessProductSaveReqVO product);
}

View File

@ -1,12 +1,16 @@
package cn.iocoder.yudao.module.crm.convert.contract;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractRespVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionTransferReqBO;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import org.mapstruct.Mapper;
@ -34,7 +38,8 @@ public interface CrmContractConvert {
default List<CrmContractRespVO> convertList(List<CrmContractDO> contractList, Map<Long, AdminUserRespDTO> userMap,
List<CrmCustomerDO> customerList, Map<Long, CrmContactDO> contactMap,
Map<Long, CrmBusinessDO> businessMap) {
Map<Long, CrmBusinessDO> businessMap, Map<Long, CrmContractProductDO> contractProductMap,
List<CrmProductDO> productList) {
List<CrmContractRespVO> respVOList = BeanUtils.toBean(contractList, CrmContractRespVO.class);
// 拼接关联字段
Map<Long, CrmCustomerDO> customerMap = convertMap(customerList, CrmCustomerDO::getId);
@ -46,7 +51,21 @@ public interface CrmContractConvert {
findAndThen(contactMap, contract.getContactId(), contact -> contract.setContactName(contact.getName()));
findAndThen(businessMap, contract.getBusinessId(), business -> contract.setBusinessName(business.getName()));
});
if (CollUtil.isNotEmpty(respVOList) && respVOList.size() == 1) {
setContractRespVOProductItems(respVOList.get(0), contractProductMap, productList);
}
return respVOList;
}
default void setContractRespVOProductItems(CrmContractRespVO respVO, Map<Long, CrmContractProductDO> contractProductMap,
List<CrmProductDO> productList) {
respVO.setProductItems(CollectionUtils.convertList(productList, product -> {
CrmContractRespVO.CrmContractProductItemRespVO productItemRespVO = BeanUtils.toBean(product, CrmContractRespVO.CrmContractProductItemRespVO.class);
findAndThen(contractProductMap, product.getId(), contractProduct -> {
productItemRespVO.setCount(contractProduct.getCount()).setDiscountPercent(contractProduct.getDiscountPercent());
});
return productItemRespVO;
}));
}
}

View File

@ -1,9 +1,7 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.business;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.enums.DictTypeConstants;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@ -41,19 +39,10 @@ public class CrmBusinessProductDO extends BaseDO {
* 关联 {@link CrmProductDO#getId()}
*/
private Long productId;
// TODO 芋艿需要在看下 CRM
/**
* 合同编号
*
* 关联 {@link CrmContractDO#getId()}
*/
private Long contractId;
/**
* 产品单价
*/
private Integer price;
/**
* 销售价格, 单位
*/
@ -71,11 +60,4 @@ public class CrmBusinessProductDO extends BaseDO {
*/
private Integer totalPrice;
/**
* 单位
*
* 字典 {@link DictTypeConstants#CRM_PRODUCT_UNIT}
*/
private Integer unit;
}

View File

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.crm.dal.dataobject.contract;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
* 合同产品关联表 DO
*
* @author HUIHUI
*/
@TableName("crm_contract_product")
@KeySequence("crm_contract_product_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CrmContractProductDO extends BaseDO {
/**
* 主键
*/
@TableId
private Long id;
/**
* 产品编号
*
* 关联 {@link CrmProductDO#getId()}
*/
private Long productId;
/**
* 合同编号
*
* 关联 {@link CrmContractDO#getId()}
*/
private Long contractId;
/**
* 产品单价
*/
private Integer price;
/**
* 销售价格, 单位
*/
private Integer salesPrice;
/**
* 数量
*/
private Integer count;
/**
* 折扣
*/
private Integer discountPercent;
/**
* 总计价格折扣后价格
*/
private Integer totalPrice;
}

View File

@ -1,4 +0,0 @@
/**
* 合同
*/
package cn.iocoder.yudao.module.crm.dal.dataobject.contract;

View File

@ -2,9 +2,12 @@ package cn.iocoder.yudao.module.crm.dal.mysql.business;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 商机产品 Mapper
*
@ -21,4 +24,8 @@ public interface CrmBusinessProductMapper extends BaseMapperX<CrmBusinessProduct
return selectOne(CrmBusinessProductDO::getBusinessId, id);
}
default List<CrmBusinessProductDO> selectListByBusinessId(Long businessId) {
return selectList(new LambdaQueryWrapperX<CrmBusinessProductDO>().eq(CrmBusinessProductDO::getBusinessId, businessId));
}
}

View File

@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.crm.dal.mysql.contract;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 合同产品 Mapper
*
* @author HUIHUI
*/
@Mapper
public interface CrmContractProductMapper extends BaseMapperX<CrmContractProductDO> {
default void deleteByContractId(Long contractId) { // TODO @lzxhqs第一个方法和类之间最好空一行
delete(CrmContractProductDO::getContractId, contractId);
}
default CrmContractProductDO selectByContractId(Long contractId) {
return selectOne(CrmContractProductDO::getContractId, contractId);
}
default List<CrmContractProductDO> selectListByContractId(Long contractId) {
return selectList(new LambdaQueryWrapperX<CrmContractProductDO>().eq(CrmContractProductDO::getContractId, contractId));
}
}

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.crm.dal.mysql.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX;
import cn.iocoder.yudao.module.crm.controller.admin.product.vo.product.CrmProductPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
@ -10,9 +9,6 @@ import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.util.CrmQueryWrapperUtils;
import org.apache.ibatis.annotations.Mapper;
import java.util.Collection;
import java.util.List;
/**
* CRM 产品 Mapper
*
@ -38,9 +34,4 @@ public interface CrmProductMapper extends BaseMapperX<CrmProductDO> {
return selectOne(CrmProductDO::getNo, no);
}
// TODO @puhui999selectBatchIds
default List<CrmProductDO> selectListByIds(Collection<Long> ids) {
return selectList(new LambdaQueryWrapperX<CrmProductDO>().in(CrmProductDO::getId, ids));
}
}

View File

@ -1,57 +0,0 @@
package cn.iocoder.yudao.module.crm.service.business;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import java.util.List;
/**
* 商机产品关联表 Service 接口
*
* @author lzxhqs
*/
public interface CrmBusinessProductService {
/**
* 批量新增商机产品关联数据
*
* @param list 商机产品集合
*/
void createBusinessProductBatch(List<CrmBusinessProductDO> list);
/**
* 批量更新商机产品表
*
* @param list 商机产品数据集合
*/
void updateBusinessProductBatch(List<CrmBusinessProductDO> list);
/**
* 批量删除
*
* @param list 需要删除的商机产品集合
*/
void deleteBusinessProductBatch(List<CrmBusinessProductDO> list);
/**
* 根据商机编号删除商机产品关联数据
*
* @param businessId 商机id
*/
void deleteBusinessProductByBusinessId(Long businessId);
/**
* 根据商机编号获取商机产品关联数据集合
*
* @param businessId 商机编号
*/
List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId);
/**
* 根据合同编号获得合同关联的商品列表
*
* @param contractId 合同编号
* @return 关联的商品列表
*/
List<CrmBusinessProductDO> getBusinessProductListByContractId(Long contractId);
}

View File

@ -1,55 +0,0 @@
package cn.iocoder.yudao.module.crm.service.business;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.List;
/**
* 商机产品关联表 Service 实现类
*
* @author lzxhqs
*/
@Service
@Validated
public class CrmBusinessProductServiceImpl implements CrmBusinessProductService {
@Resource
private CrmBusinessProductMapper businessProductMapper;
@Override
public void createBusinessProductBatch(List<CrmBusinessProductDO> list) {
businessProductMapper.insertBatch(list);
}
@Override
public void updateBusinessProductBatch(List<CrmBusinessProductDO> list) {
businessProductMapper.updateBatch(list);
}
// TODO @puhui999这个方法可以直接调用 deleteList 方法然后传递 ids 就好了
@Override
public void deleteBusinessProductBatch(List<CrmBusinessProductDO> list) {
businessProductMapper.deleteBatchIds(CollectionUtils.convertList(list, CrmBusinessProductDO::getId));
}
@Override
public void deleteBusinessProductByBusinessId(Long businessId) {
businessProductMapper.deleteByBusinessId(businessId);
}
@Override
public List<CrmBusinessProductDO> getBusinessProductListByContractId(Long contractId) {
return businessProductMapper.selectList(CrmBusinessProductDO::getContractId, contractId);
}
@Override
public List<CrmBusinessProductDO> getBusinessProductListByBusinessId(Long businessId) {
return businessProductMapper.selectList(CrmBusinessProductDO::getBusinessId, businessId);
}
}

View File

@ -5,17 +5,18 @@ import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessPageReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.business.CrmBusinessTransferReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.business.vo.product.CrmBusinessProductSaveReqVO;
import cn.iocoder.yudao.module.crm.convert.business.CrmBusinessConvert;
import cn.iocoder.yudao.module.crm.convert.businessproduct.CrmBusinessProductConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
@ -24,6 +25,7 @@ import cn.iocoder.yudao.module.crm.service.contract.CrmContractService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
import cn.iocoder.yudao.module.crm.service.permission.bo.CrmPermissionCreateReqBO;
import cn.iocoder.yudao.module.crm.service.product.CrmProductService;
import com.mzt.logapi.context.LogRecordContext;
import com.mzt.logapi.service.impl.DiffParseFunction;
import com.mzt.logapi.starter.annotation.LogRecord;
@ -35,11 +37,12 @@ import org.springframework.validation.annotation.Validated;
import java.util.Collection;
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.convertSet;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_CONTRACT_EXISTS;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.BUSINESS_NOT_EXISTS;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.crm.enums.LogRecordConstants.*;
/**
@ -53,9 +56,9 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
@Resource
private CrmBusinessMapper businessMapper;
@Resource
private CrmBusinessProductService businessProductService;
private CrmBusinessProductMapper businessProductMapper;
@Resource
@Lazy // 延迟加载避免循环依赖
private CrmContractService contractService;
@ -63,6 +66,8 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
private CrmPermissionService permissionService;
@Resource
private CrmContactBusinessService contactBusinessService;
@Resource
private CrmProductService productService;
@Override
@Transactional(rollbackFor = Exception.class)
@ -71,12 +76,15 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
public Long createBusiness(CrmBusinessSaveReqVO createReqVO, Long userId) {
createReqVO.setId(null);
// 1. 插入商机
CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class)
.setOwnerUserId(userId);
CrmBusinessDO business = BeanUtils.toBean(createReqVO, CrmBusinessDO.class).setOwnerUserId(userId);
businessMapper.insert(business);
// TODO 商机待定插入商机与产品的关联表校验商品存在
if (CollUtil.isNotEmpty(createReqVO.getProducts())) {
createBusinessProducts(createReqVO.getProducts(), business.getId(), false);
// 1.2 插入商机关联商品
if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
List<CrmBusinessProductDO> productList = convertBusinessProductList(createReqVO, business.getId());
businessProductMapper.insertBatch(productList);
// 更新合同商品总金额
businessMapper.updateById(new CrmBusinessDO().setId(business.getId()).setProductPrice(
getSumValue(productList, CrmBusinessProductDO::getTotalPrice, Integer::sum)));
}
// TODO 商机待定在联系人的详情页如果直接新建商机则需要关联下这里要搞个 CrmContactBusinessDO
createContactBusiness(business.getId(), createReqVO.getContactId());
@ -92,13 +100,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
}
// TODO @lzxhqsCrmContactBusinessService 调用这个这样逻辑才能收敛哈
/**
* @param businessId 商机id
* @param contactId 联系人id
* @throws
* @description 联系人与商机的关联
* @author lzxhqs
*/
private void createContactBusiness(Long businessId, Long contactId) {
CrmContactBusinessDO contactBusiness = new CrmContactBusinessDO();
contactBusiness.setBusinessId(businessId);
@ -106,37 +107,6 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
contactBusinessService.insert(contactBusiness);
}
// TODO @lzxhqs这个方法注释格式不对删除@description然后把 插入商机产品关联表 作为方法注释
/**
* 插入商机产品关联表
*
* @param products 产品集合
* @param businessId 商机id
* @param updateFlag 更新标识 true 代表更新
* @author lzxhqs
*/
private void createBusinessProducts(List<CrmBusinessProductSaveReqVO> products, Long businessId, Boolean updateFlag) {
List<CrmBusinessProductDO> list = CollectionUtils.convertList(products, product ->
CrmBusinessProductConvert.INSTANCE.convert(product).setBusinessId(businessId));
if (Boolean.TRUE.equals(updateFlag)) {
// 根据商机 id从商机产品关联表中获取已存在的数据集合
List<CrmBusinessProductDO> oldProducts = businessProductService.getBusinessProductListByBusinessId(businessId);
List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, list, (oldValue, newValue) ->
ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId()));
if (CollUtil.isNotEmpty(diffList.getFirst())) {
businessProductService.createBusinessProductBatch(diffList.getFirst());
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
businessProductService.updateBusinessProductBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
businessProductService.deleteBusinessProductBatch(diffList.get(2));
}
} else {
businessProductService.createBusinessProductBatch(list);
}
}
@Override
@Transactional(rollbackFor = Exception.class)
@LogRecord(type = CRM_BUSINESS_TYPE, subType = CRM_BUSINESS_UPDATE_SUB_TYPE, bizNo = "{{#updateReqVO.id}}",
@ -146,16 +116,12 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
// 1. 校验存在
CrmBusinessDO oldBusiness = validateBusinessExists(updateReqVO.getId());
// 2. 更新商机
// 2.1 更新商机
CrmBusinessDO updateObj = BeanUtils.toBean(updateReqVO, CrmBusinessDO.class);
businessMapper.updateById(updateObj);
// TODO 商机待定插入商机与产品的关联表校验商品存在
// TODO @lzxhqscreateBusinessProducts 可以抽成两个方法一个新增一个修改修改需要把 businessProductService.deleteByBusinessId(updateReqVO.getId()); 一起处理进去
if (CollUtil.isNotEmpty(updateReqVO.getProducts())) {
createBusinessProducts(updateReqVO.getProducts(), updateReqVO.getId(), true);
} else {
businessProductService.deleteBusinessProductByBusinessId(updateReqVO.getId());
}
// 2.2 更新商机关联商品
List<CrmBusinessProductDO> productList = convertBusinessProductList(updateReqVO, updateObj.getId());
updateBusinessProduct(productList, updateObj.getId());
// TODO @商机待定如果状态发生变化插入商机状态变更记录表
// 3. 记录操作日志上下文
@ -188,6 +154,43 @@ public class CrmBusinessServiceImpl implements CrmBusinessService {
LogRecordContext.putVariable("businessName", business.getName());
}
private void updateBusinessProduct(List<CrmBusinessProductDO> newProductList, Long businessId) {
List<CrmBusinessProductDO> oldProducts = businessProductMapper.selectListByBusinessId(businessId);
List<List<CrmBusinessProductDO>> diffList = CollectionUtils.diffList(oldProducts, newProductList, (oldValue, newValue) -> {
boolean condition = ObjectUtil.equal(oldValue.getProductId(), newValue.getProductId());
if (condition) {
newValue.setId(oldValue.getId()); // 更新需要原始编号
}
return condition;
});
if (CollUtil.isNotEmpty(diffList.get(0))) {
businessProductMapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
businessProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
businessProductMapper.deleteBatchIds(convertSet(diffList.get(2), CrmBusinessProductDO::getId));
}
}
private List<CrmBusinessProductDO> convertBusinessProductList(CrmBusinessSaveReqVO reqVO, Long businessId) {
// 校验商品存在
Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmBusinessSaveReqVO.CrmBusinessProductItem::getId);
List<CrmProductDO> productList = productService.getProductList(productIds);
if (CollUtil.isEmpty(productIds) || productList.size() != productIds.size()) {
throw exception(PRODUCT_NOT_EXISTS);
}
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
return convertList(reqVO.getProductItems(), productItem -> {
CrmProductDO product = productMap.get(productItem.getId());
return BeanUtils.toBean(product, CrmBusinessProductDO.class)
.setId(null).setProductId(productItem.getId()).setBusinessId(businessId)
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
.setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
});
}
/**
* 删除校验合同是关联合同
*

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageR
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
import jakarta.validation.Valid;
@ -134,4 +135,12 @@ public interface CrmContractService {
*/
Long getContractCountByBusinessId(Long businessId);
/**
* 获取合同商品列表
*
* @param contactId 合同编号
* @return 合同商品列表
*/
List<CrmContractProductDO> getContractProductListByContractId(Long contactId);
}

View File

@ -15,15 +15,15 @@ import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractPageR
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractSaveReqVO;
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.CrmContractTransferReqVO;
import cn.iocoder.yudao.module.crm.convert.contract.CrmContractConvert;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.product.CrmProductDO;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.contract.CrmContractProductMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.framework.permission.core.annotations.CrmPermission;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessProductService;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService;
import cn.iocoder.yudao.module.crm.service.followup.bo.CrmUpdateFollowUpReqBO;
@ -63,12 +63,12 @@ public class CrmContractServiceImpl implements CrmContractService {
@Resource
private CrmContractMapper contractMapper;
@Resource
private CrmContractProductMapper contractProductMapper;
@Resource
private CrmPermissionService crmPermissionService;
@Resource
private CrmBusinessProductService businessProductService;
@Resource
private CrmProductService productService;
@Resource
private CrmCustomerService customerService;
@ -89,11 +89,14 @@ public class CrmContractServiceImpl implements CrmContractService {
// 1.1 插入合同
CrmContractDO contract = BeanUtils.toBean(createReqVO, CrmContractDO.class).setId(null);
contractMapper.insert(contract);
// 1.2 插入商机关联商品
// 1.2 插入合同关联商品
if (CollUtil.isNotEmpty(createReqVO.getProductItems())) { // 如果有的话
List<CrmBusinessProductDO> businessProduct = convertBusinessProductList(createReqVO, contract.getId());
businessProductService.createBusinessProductBatch(businessProduct);
List<CrmContractProductDO> productList = convertContractProductList(createReqVO, contract.getId());
contractProductMapper.insertBatch(productList);
// 更新合同商品总金额
contractMapper.updateById(new CrmContractDO().setId(contract.getId()).setProductPrice(
getSumValue(productList, CrmContractProductDO::getTotalPrice, Integer::sum)));
// TODO @puhui999: 如果存在合同关联了商机则更新商机商品关联
}
// 2. 创建数据权限
@ -137,29 +140,29 @@ public class CrmContractServiceImpl implements CrmContractService {
if (CollUtil.isEmpty(updateReqVO.getProductItems())) {
return;
}
List<CrmBusinessProductDO> newProductList = convertBusinessProductList(updateReqVO, contractId);
List<CrmBusinessProductDO> oldProductList = businessProductService.getBusinessProductListByContractId(contractId);
List<List<CrmBusinessProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> {
if (ObjUtil.notEqual(oldObj.getProductId(), newObj.getProductId())) {
return false;
}
List<CrmContractProductDO> newProductList = convertContractProductList(updateReqVO, contractId);
List<CrmContractProductDO> oldProductList = contractProductMapper.selectListByContractId(contractId);
List<List<CrmContractProductDO>> diffList = diffList(oldProductList, newProductList, (oldObj, newObj) -> {
boolean equal = ObjUtil.equal(oldObj.getProductId(), newObj.getProductId());
if (equal) {
newObj.setId(oldObj.getId()); // 设置一下老的编号更新时需要使用
return true;
}
return equal;
});
if (CollUtil.isNotEmpty(diffList.getFirst())) {
businessProductService.createBusinessProductBatch(diffList.getFirst());
if (CollUtil.isNotEmpty(diffList.get(0))) {
contractProductMapper.insertBatch(diffList.get(0));
}
if (CollUtil.isNotEmpty(diffList.get(1))) {
businessProductService.updateBusinessProductBatch(diffList.get(1));
contractProductMapper.updateBatch(diffList.get(1));
}
if (CollUtil.isNotEmpty(diffList.get(2))) {
businessProductService.deleteBusinessProductBatch(diffList.get(2));
contractProductMapper.deleteBatchIds(convertList(diffList.get(2), CrmContractProductDO::getId));
}
}
// TODO @合同待定缺一个取消合同的接口只有草稿审批中可以取消CrmAuditStatusEnum
private List<CrmBusinessProductDO> convertBusinessProductList(CrmContractSaveReqVO reqVO, Long contractId) {
private List<CrmContractProductDO> convertContractProductList(CrmContractSaveReqVO reqVO, Long contractId) {
// 校验商品存在
Set<Long> productIds = convertSet(reqVO.getProductItems(), CrmContractSaveReqVO.CrmContractProductItem::getId);
List<CrmProductDO> productList = productService.getProductList(productIds);
@ -169,8 +172,8 @@ public class CrmContractServiceImpl implements CrmContractService {
Map<Long, CrmProductDO> productMap = convertMap(productList, CrmProductDO::getId);
return convertList(reqVO.getProductItems(), productItem -> {
CrmProductDO product = productMap.get(productItem.getId());
return BeanUtils.toBean(product, CrmBusinessProductDO.class)
.setId(null).setBusinessId(reqVO.getBusinessId()).setProductId(productItem.getId()).setContractId(contractId)
return BeanUtils.toBean(product, CrmContractProductDO.class)
.setId(null).setProductId(productItem.getId()).setContractId(contractId)
.setCount(productItem.getCount()).setDiscountPercent(productItem.getDiscountPercent())
.setTotalPrice(MoneyUtils.calculator(product.getPrice(), productItem.getCount(), productItem.getDiscountPercent()));
});
@ -313,5 +316,11 @@ public class CrmContractServiceImpl implements CrmContractService {
public Long getContractCountByBusinessId(Long businessId) {
return contractMapper.selectCountByBusinessId(businessId);
}
@Override
public List<CrmContractProductDO> getContractProductListByContractId(Long contactId) {
return contractProductMapper.selectListByContractId(contactId);
}
// TODO @合同待定需要新增一个 ContractConfigDO 合同配置重点是到期提醒
}

View File

@ -160,7 +160,7 @@ public class CrmProductServiceImpl implements CrmProductService {
if (CollUtil.isEmpty(ids)) {
return Collections.emptyList();
}
return productMapper.selectListByIds(ids);
return productMapper.selectBatchIds(ids);
}
}

View File

@ -1,24 +1,23 @@
package cn.iocoder.yudao.module.erp.service.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.unit.ErpProductUnitSaveReqVO;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductUnitMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.erp.ErrorCodeConstants.PRODUCT_UNIT_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**

View File

@ -1,24 +1,23 @@
package cn.iocoder.yudao.module.erp.service.product;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import jakarta.annotation.Resource;
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.Import;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.*;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.*;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.*;
import static cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime;
import static cn.iocoder.yudao.framework.common.util.object.ObjectUtils.cloneIgnoreId;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertPojoEquals;
import static cn.iocoder.yudao.framework.test.core.util.AssertUtils.assertServiceException;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
import static cn.iocoder.yudao.module.erp.ErrorCodeConstants.PRODUCT_NOT_EXISTS;
import static org.junit.jupiter.api.Assertions.*;
/**