diff --git a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java index cee21e892..5d540f301 100644 --- a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java +++ b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUser.java @@ -21,4 +21,6 @@ public @interface WithMockLoginUser { long deptId() default 0L; boolean isSuperAdmin() default false; + + long organId() default 0L; } \ No newline at end of file diff --git a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java index e11cc95df..2b7b4b926 100644 --- a/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java +++ b/cf-framework/cf-spring-boot-starter-security/src/main/java/com/cf/imes/framework/security/test/WithMockLoginUserSecurityContextFactory.java @@ -2,6 +2,7 @@ package com.cf.imes.framework.security.test; import cn.hutool.core.util.RandomUtil; import com.cf.imes.framework.security.core.LoginUser; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -23,10 +24,13 @@ public class WithMockLoginUserSecurityContextFactory implements WithSecurityCont LoginUser loginUser = new LoginUser(); long annotationUserId = annotation.userId(); + long annotationOrganId = annotation.organId(); + String username = annotation.username(); + loginUser.setId(annotationUserId == 0L ? RandomUtil.randomLong() : annotationUserId); - loginUser.setNickname(RandomUtil.randomString(10)); + loginUser.setNickname(StringUtils.isNotEmpty(username) ? username : RandomUtil.randomString(10)); loginUser.setDeptId(RandomUtil.randomLong()); - loginUser.setOrganId(RandomUtil.randomLong()); + loginUser.setOrganId(annotationOrganId == 0L ? RandomUtil.randomLong() : annotationOrganId); loginUser.setIsSupAdmin(annotation.isSuperAdmin()); UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( diff --git a/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java b/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java index a8ba8883a..288df091d 100644 --- a/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java +++ b/cf-framework/cf-spring-boot-starter-test/src/main/java/com/cf/imes/framework/test/core/util/RandomUtils.java @@ -28,6 +28,8 @@ import java.util.stream.Stream; * @author 晨丰科技 */ public class RandomUtils { + private RandomUtils() { + } private static final int RANDOM_STRING_LENGTH = 10; @@ -51,7 +53,7 @@ public class RandomUtils { } // 如果是 type、status 结尾的字段,返回 tinyint 范围 if (CharSequenceUtil.endWithAnyIgnoreCase(attributeMetadata.getAttributeName(), - "type", "status", "category", "scope", "result", "sort", "unit")) { + "type", "status", "category", "scope", "result", "sort", "unit", "currency")) { return RandomUtil.randomInt(0, TINYINT_MAX + 1); } return RandomUtil.randomInt(); @@ -142,8 +144,7 @@ public class RandomUtils { @SafeVarargs public static List randomPojoList(Class clazz, Consumer... consumers) { int size = RandomUtil.randomInt(1, RANDOM_COLLECTION_LENGTH); - return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)) - .collect(Collectors.toList()); + return Stream.iterate(0, i -> i).limit(size).map(o -> randomPojo(clazz, consumers)).toList(); } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/user/UserProfileController.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/user/UserProfileController.java index 8c7a01185..26b238a7f 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/user/UserProfileController.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/user/UserProfileController.java @@ -61,7 +61,7 @@ public class UserProfileController { DeptDO dept = user.getDeptId() != null ? deptService.getDept(user.getDeptId()) : null; // 获得社交用户信息 List socialUsers = socialService.getSocialUserList(user.getId(), UserTypeEnum.ADMIN.getValue()); - return success(UserConvert.INSTANCE.convert(user, userRoles, dept, null, socialUsers)); + return success(UserConvert.INSTANCE.convert(user, userRoles, dept, socialUsers)); } @PutMapping("/update") diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/convert/user/UserConvert.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/convert/user/UserConvert.java index 138088465..65006ba87 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/convert/user/UserConvert.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/convert/user/UserConvert.java @@ -10,7 +10,6 @@ import com.cf.imes.module.system.controller.admin.user.vo.user.UserExportRespVO; import com.cf.imes.module.system.controller.admin.user.vo.user.UserRespVO; import com.cf.imes.module.system.controller.admin.user.vo.user.UserSimpleRespVO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; -import com.cf.imes.module.system.dal.dataobject.dept.PostDO; import com.cf.imes.module.system.dal.dataobject.permission.RoleDO; import com.cf.imes.module.system.dal.dataobject.social.SocialUserDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; @@ -59,11 +58,10 @@ public interface UserConvert { } default UserProfileRespVO convert(AdminUserDO user, List userRoles, - DeptDO dept, List posts, List socialUsers) { + DeptDO dept, List socialUsers) { UserProfileRespVO userVO = BeanUtils.toBean(user, UserProfileRespVO.class); userVO.setRoles(BeanUtils.toBean(userRoles, RoleSimpleRespVO.class)); userVO.setDept(BeanUtils.toBean(dept, DeptSimpleRespVO.class)); -// userVO.setPosts(BeanUtils.toBean(posts, PostSimpleRespVO.class)); userVO.setSocialUsers(BeanUtils.toBean(socialUsers, UserProfileRespVO.SocialUser.class)); return userVO; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/PostDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/PostDO.java deleted file mode 100644 index a5afaff58..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/PostDO.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.cf.imes.module.system.dal.dataobject.dept; - -import com.cf.imes.framework.common.enums.CommonStatusEnum; -import com.cf.imes.framework.mybatis.core.dataobject.BaseDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.cf.imes.framework.organ.core.db.OrganBaseDO; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 岗位表 - * - * @author ruoyi - */ -@TableName("system_post") -@KeySequence("system_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class PostDO extends OrganBaseDO { - - /** - * 岗位序号 - */ - @TableId - private Long id; - /** - * 岗位名称 - */ - private String name; - /** - * 岗位编码 - */ - private String code; - /** - * 岗位排序 - */ - private Integer sort; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 备注 - */ - private String remark; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/UserPostDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/UserPostDO.java deleted file mode 100644 index b38e30a2b..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/dept/UserPostDO.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.cf.imes.module.system.dal.dataobject.dept; - -import com.cf.imes.framework.mybatis.core.dataobject.BaseDO; -import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; -import lombok.EqualsAndHashCode; - -/** - * 用户和岗位关联 - * - * @author ruoyi - */ -@TableName("system_user_post") -@KeySequence("system_user_post_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -public class UserPostDO extends BaseDO { - - /** - * 自增主键 - */ - @TableId - private Long id; - /** - * 用户 ID - * - * 关联 {@link AdminUserDO#getId()} - */ - private Long userId; - /** - * 角色 ID - * - * 关联 {@link PostDO#getId()} - */ - private Long postId; - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/PostMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/PostMapper.java deleted file mode 100644 index 9b6e53a31..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/PostMapper.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.cf.imes.module.system.dal.mysql.dept; - -import com.cf.imes.framework.common.pojo.PageResult; -import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX; -import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.cf.imes.framework.organ.core.context.OrganContextHolder; -import com.cf.imes.framework.security.core.LoginUser; -import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; -import com.cf.imes.module.system.controller.admin.dept.vo.post.PostPageReqVO; -import com.cf.imes.module.system.dal.dataobject.dept.PostDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -@Mapper -public interface PostMapper extends BaseMapperX { - - default List selectList(Collection ids, Collection statuses, Long organId) { - return selectList(new LambdaQueryWrapperX() - .eqIfPresent(PostDO::getOrganId, organId) - .inIfPresent(PostDO::getId, ids) - .inIfPresent(PostDO::getStatus, statuses)); - } - - default PageResult selectPage(PostPageReqVO reqVO) { - LambdaQueryWrapperX lambdaQueryWrapperX = new LambdaQueryWrapperX<>(); - LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); - assert loginUser != null; - Boolean isSupAdmin = loginUser.getIsSupAdmin(); - if(isSupAdmin && !Objects.isNull(reqVO.getOrganId())) { - lambdaQueryWrapperX.eqIfPresent(PostDO::getOrganId, reqVO.getOrganId()); - }else{ - lambdaQueryWrapperX.eqIfPresent(PostDO::getOrganId, loginUser.getOrganId()); - } - - lambdaQueryWrapperX.likeIfPresent(PostDO::getCode, reqVO.getCode()) - .likeIfPresent(PostDO::getName, reqVO.getName()) - .eqIfPresent(PostDO::getStatus, reqVO.getStatus()); - - - if (reqVO.getOrder() == null) { - lambdaQueryWrapperX.orderByDesc(PostDO::getSort); - } else { - if (reqVO.getOrder().equals("ascend")) { - switch (reqVO.getField()) { - case "name" -> lambdaQueryWrapperX.orderByAsc(PostDO::getName); - case "code" -> lambdaQueryWrapperX.orderByAsc(PostDO::getCode); - case "sort" -> lambdaQueryWrapperX.orderByAsc(PostDO::getSort); - case "status" -> lambdaQueryWrapperX.orderByAsc(PostDO::getStatus); - case "create_time" -> lambdaQueryWrapperX.orderByAsc(PostDO::getCreateTime); - default -> { - // default不排序 - } - } - } else if (reqVO.getOrder().equals("descend")) { - switch (reqVO.getField()) { - case "name" -> lambdaQueryWrapperX.orderByDesc(PostDO::getName); - case "code" -> lambdaQueryWrapperX.orderByDesc(PostDO::getCode); - case "sort" -> lambdaQueryWrapperX.orderByDesc(PostDO::getSort); - case "status" -> lambdaQueryWrapperX.orderByDesc(PostDO::getStatus); - case "create_time" -> lambdaQueryWrapperX.orderByDesc(PostDO::getCreateTime); - default -> { - // default不排序 - } - } - } - } - - return selectPage(reqVO, lambdaQueryWrapperX); - } - - default PostDO selectByName(String name) { - return selectOne(PostDO::getName, name, PostDO::getOrganId, OrganContextHolder.getOrganId()); - } - - default PostDO selectByCode(String code) { - return selectOne(PostDO::getCode, code, PostDO::getOrganId, OrganContextHolder.getOrganId()); - } - -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/UserPostMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/UserPostMapper.java deleted file mode 100644 index f8017473c..000000000 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/dept/UserPostMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.cf.imes.module.system.dal.mysql.dept; - -import com.cf.imes.framework.mybatis.core.mapper.BaseMapperX; -import com.cf.imes.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.cf.imes.module.system.dal.dataobject.dept.UserPostDO; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface UserPostMapper extends BaseMapperX { - - default List selectListByUserId(Long userId) { - return selectList(UserPostDO::getUserId, userId); - } - - default void deleteByUserIdAndPostId(Long userId, Collection postIds) { - delete(new LambdaQueryWrapperX() - .eq(UserPostDO::getUserId, userId) - .in(UserPostDO::getPostId, postIds)); - } - - default List selectListByPostIds(Collection postIds) { - return selectList(UserPostDO::getPostId, postIds); - } - - default void deleteByUserId(Long userId) { - delete(Wrappers.lambdaUpdate(UserPostDO.class).eq(UserPostDO::getUserId, userId)); - } -} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImpl.java index b66e9b4d1..79cc45457 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImpl.java @@ -2,7 +2,6 @@ package com.cf.imes.module.system.service.funds.incomeandexpense; import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO; import com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper; -import com.cf.imes.module.system.service.organ.OrganService; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; @@ -17,9 +16,6 @@ public class IncomeExpenseServiceImpl implements IncomeExpenseService { @Resource private IncomeExpenseDetailsMapper incomeExpenseDetailsMapper; - @Resource - private OrganService organService; - @Override public Long createDetail(IncomeExpenseDetailsDO incomeExpenseDetailsDO) { incomeExpenseDetailsMapper.insert(incomeExpenseDetailsDO); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImpl.java index bf5f3df22..36cd5738c 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImpl.java @@ -7,7 +7,6 @@ import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; -import com.baomidou.mybatisplus.core.toolkit.ObjectUtils; import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants; import com.cf.imes.framework.common.pojo.PageResult; @@ -220,11 +219,12 @@ public class InvoiceServiceImpl implements InvoiceService { @Override @Transactional(rollbackFor = Exception.class) public void applyInvoice(InvoiceRecordsReqVO reqVO) { - Long organId = SecurityFrameworkUtils.getUserOrganId(); LoginUser loginUser = SecurityFrameworkUtils.getLoginUser(); if (loginUser == null) { throw new ServiceException(GlobalErrorCodeConstants.UNAUTHORIZED); } + Long organId = loginUser.getOrganId(); + List purchaseRecordId = reqVO.getPurchaseRecordId(); // 查询发票抬头 InvoiceTitleInfoDO invoiceTitleInfoDO = invoiceTitleInfoMapper.selectOne(new LambdaQueryWrapper() @@ -240,7 +240,7 @@ public class InvoiceServiceImpl implements InvoiceService { // 查询可开票金额:余额支付+扫码支付 BigDecimal invoicableAmount = purchaseRecordMapper.getInvoicableAmount(organId, purchaseRecordId); // 可开票金额不能为0 - AssertUtils.isTrue(ObjectUtils.isNull(invoicableAmount) || invoicableAmount.compareTo(BigDecimal.ZERO) == 0, INVOICE_AMOUNT_ZERO_ERROR); + AssertUtils.isTrue(invoicableAmount.compareTo(BigDecimal.ZERO) == 0, INVOICE_AMOUNT_ZERO_ERROR); // 更新购买记录表的发票状态 int update = purchaseRecordMapper.update(new LambdaUpdateWrapper() @@ -300,17 +300,15 @@ public class InvoiceServiceImpl implements InvoiceService { .in(PurchaseRecordDO::getId, purchaseIdList)); List invoicePurchaseRecords = new ArrayList<>(); - if (CollUtil.isNotEmpty(purchaseRecordDOS)) { - for (PurchaseRecordDO purchaseRecordDO : purchaseRecordDOS) { - InvoicePurchaseRecord invoicePurchaseRecord = new InvoicePurchaseRecord(); - invoicePurchaseRecord.setPurchaseRecordId(purchaseRecordDO.getId()); - invoicePurchaseRecord.setProductName(purchaseRecordDO.getProductName()); - invoicePurchaseRecord.setTradeType(TradeTypeEnum.PRODUCT.getCode()); - invoicePurchaseRecord.setInvoicableAmount(purchaseRecordDO.getTotalAmount()); - invoicePurchaseRecord.setTotalAmount(purchaseRecordDO.getTotalAmount()); - invoicePurchaseRecord.setCreateTime(purchaseRecordDO.getCreateTime()); - invoicePurchaseRecords.add(invoicePurchaseRecord); - } + for (PurchaseRecordDO purchaseRecordDO : purchaseRecordDOS) { + InvoicePurchaseRecord invoicePurchaseRecord = new InvoicePurchaseRecord(); + invoicePurchaseRecord.setPurchaseRecordId(purchaseRecordDO.getId()); + invoicePurchaseRecord.setProductName(purchaseRecordDO.getProductName()); + invoicePurchaseRecord.setTradeType(TradeTypeEnum.PRODUCT.getCode()); + invoicePurchaseRecord.setInvoicableAmount(purchaseRecordDO.getTotalAmount()); + invoicePurchaseRecord.setTotalAmount(purchaseRecordDO.getTotalAmount()); + invoicePurchaseRecord.setCreateTime(purchaseRecordDO.getCreateTime()); + invoicePurchaseRecords.add(invoicePurchaseRecord); } // 解压发票抬头 diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/invoice/InvoiceControllerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/invoice/InvoiceControllerTest.java new file mode 100644 index 000000000..31ebe682c --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/controller/admin/funds/invoice/InvoiceControllerTest.java @@ -0,0 +1,222 @@ +package com.cf.imes.module.system.controller.admin.funds.invoice; + +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.test.core.ut.BaseWebUnitTest; +import com.cf.imes.module.system.controller.admin.funds.invoice.vo.*; +import com.cf.imes.module.system.enums.pay.InvoiceIssueTypeEnum; +import com.cf.imes.module.system.enums.pay.InvoiceTypeEnum; +import com.cf.imes.module.system.service.funds.invoice.InvoiceService; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.http.MediaType; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.bean.override.mockito.MockitoBean; +import org.springframework.test.web.servlet.MockMvc; + +import java.math.BigDecimal; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +/** + * @author 发票管理 Controller 测试类 + */ +@WebMvcTest(controllers = InvoiceController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class) +class InvoiceControllerTest extends BaseWebUnitTest { + @Autowired + private MockMvc mockMvc; + + @MockitoBean + private InvoiceService invoiceService; + + @Autowired + private ObjectMapper objectMapper; + + @Test + void testGetOrgInvoiceTitle() throws Exception { + InvoiceTitleInfo respVO = new InvoiceTitleInfo(); + respVO.setId(1L); + respVO.setInvoiceTitle("测试公司"); + + when(invoiceService.getInvoiceTitle()).thenReturn(respVO); + + mockMvc.perform(get("/system/invoice/title")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.id").value(1L)) + .andExpect(jsonPath("$.data.invoiceTitle").value("测试公司")); + + verify(invoiceService).getInvoiceTitle(); + } + + @Test + void testInsetInvoiceTitle() throws Exception { + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("测试公司"); + reqVO.setIssueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()); + reqVO.setInvoiceType(InvoiceTypeEnum.VAT_GENERAL.getValue()); + reqVO.setEmail("test@test.com"); + + doNothing().when(invoiceService).insetInvoiceTitle(any()); + + mockMvc.perform(post("/system/invoice/title") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(invoiceService).insetInvoiceTitle(any()); + } + + @Test + void testGetInvoiceAmount() throws Exception { + InvoiceAmountRespVO respVO = new InvoiceAmountRespVO(); + respVO.setInvocable(new BigDecimal("100.00")); + respVO.setPendingInvoice(new BigDecimal("50.00")); + respVO.setInTheInvoice(new BigDecimal("30.00")); + respVO.setInvoiced(new BigDecimal("200.00")); + + when(invoiceService.getInvoiceAmount()).thenReturn(respVO); + + mockMvc.perform(get("/system/invoice/org/amount")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.invocable").value(100.00)) + .andExpect(jsonPath("$.data.pendingInvoice").value(50.00)) + .andExpect(jsonPath("$.data.inTheInvoice").value(30.00)) + .andExpect(jsonPath("$.data.invoiced").value(200.00)); + + verify(invoiceService).getInvoiceAmount(); + } + + @Test + void testGetManageEndPointInvoiceAmount() throws Exception { + InvoiceAmountRespVO respVO = new InvoiceAmountRespVO(); + respVO.setPendingInvoice(new BigDecimal("150.00")); + respVO.setInvoiced(new BigDecimal("300.00")); + + when(invoiceService.getManageInvoiceAmount()).thenReturn(respVO); + + mockMvc.perform(get("/system/invoice/manage/amount")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.pendingInvoice").value(150.00)) + .andExpect(jsonPath("$.data.invoiced").value(300.00)); + + verify(invoiceService).getManageInvoiceAmount(); + } + + @Test + void testGetAvailablePage() throws Exception { + InvoiceAmountAndAvailablePageReqVO reqVO = new InvoiceAmountAndAvailablePageReqVO(); + PageResult mockPage = new PageResult<>(); + mockPage.setTotal(1L); + InvoiceAvailablePageRespVO pageRespVO = new InvoiceAvailablePageRespVO(); + pageRespVO.setId(1L); + pageRespVO.setAvailableAmount(new BigDecimal("100.00")); + mockPage.setList(List.of(pageRespVO)); + + when(invoiceService.getAvailablePage(any())).thenReturn(mockPage); + + mockMvc.perform(get("/system/invoice/available") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.list[0].id").value(1L)) + .andExpect(jsonPath("$.data.list[0].availableAmount").value(100.00)); + + verify(invoiceService, times(1)).getAvailablePage(any()); + } + + @Test + void testApplyInvoiceRecords() throws Exception { + InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO(); + reqVO.setPurchaseRecordId(List.of(1L, 2L)); + + doNothing().when(invoiceService).applyInvoice(any()); + + mockMvc.perform(post("/system/invoice") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(invoiceService).applyInvoice(any()); + } + + @Test + void testGetInvoiceRecords() throws Exception { + InvoiceDetailsPageReqVO reqVO = new InvoiceDetailsPageReqVO(); + PageResult mockPage = new PageResult<>(); + mockPage.setTotal(1L); + InvoiceRecordsRespVO pageRespVO = new InvoiceRecordsRespVO(); + pageRespVO.setId(1L); + pageRespVO.setInvoiceAmount(new BigDecimal("150.00")); + mockPage.setList(List.of(pageRespVO)); + + when(invoiceService.getInvoiceRecords(any())).thenReturn(mockPage); + + mockMvc.perform(get("/system/invoice/records") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(reqVO))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.list[0].id").value(1L)) + .andExpect(jsonPath("$.data.list[0].invoiceAmount").value(150.00)); + + verify(invoiceService, times(1)).getInvoiceRecords(any()); + } + + @Test + void testGetInvoiceRecordDetail() throws Exception { + Long recordId = 1L; + InvoiceDetailsRespVO respVO = new InvoiceDetailsRespVO(); + respVO.setInvoiceNo("FP20231010101010"); + respVO.setRemark("测试备注"); + + when(invoiceService.getInvoiceRecordDetail(recordId)).thenReturn(respVO); + + mockMvc.perform(get("/system/invoice/record/{recordId}", recordId)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data.invoiceNo").value("FP20231010101010")) + .andExpect(jsonPath("$.data.remark").value("测试备注")); + + verify(invoiceService).getInvoiceRecordDetail(recordId); + } + + @Test + void testConfirmInvoice() throws Exception { + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + reqVO.setAgree(true); + reqVO.setInvoiceNo("FP20231010101010"); + reqVO.setRemark("同意开票"); + + doNothing().when(invoiceService).confirmInvoice(any(), any()); + + MockMultipartFile file = new MockMultipartFile( + "file", + "invoice.pdf", + "application/pdf", + "test data".getBytes() + ); + + // 使用param方式传递reqVO参数 + mockMvc.perform(multipart("/system/invoice/confirm") + .file(file) + .param("id", "1") + .param("agree", "true") + .param("invoiceNo", "FP20231010101010") + .param("remark", "同意开票")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.data").value(true)); + + verify(invoiceService).confirmInvoice(any(), any()); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/dal/redis/listener/SystemPermissionRefreshRedisListenerTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/dal/redis/listener/SystemPermissionRefreshRedisListenerTest.java index 94c1380ff..115164373 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/dal/redis/listener/SystemPermissionRefreshRedisListenerTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/dal/redis/listener/SystemPermissionRefreshRedisListenerTest.java @@ -17,9 +17,9 @@ import static org.mockito.Mockito.verify; */ class SystemPermissionRefreshRedisListenerTest { @Test - void onMessage_shouldClearDictDataI18nCache() { - SecurityFrameworkService securityFrameworkService = mock(SecurityFrameworkService.class); + void onMessage_shouldInvalidateAllPermissionsCache() { // given + SecurityFrameworkService securityFrameworkService = mock(SecurityFrameworkService.class); SystemPermissionRefreshRedisListener listener = new SystemPermissionRefreshRedisListener(securityFrameworkService); diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImplTest.java new file mode 100644 index 000000000..659e3bac6 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/incomeandexpense/IncomeExpenseServiceImplTest.java @@ -0,0 +1,64 @@ +package com.cf.imes.module.system.service.funds.incomeandexpense; + +import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest; +import com.cf.imes.module.system.dal.dataobject.incomeandexpense.IncomeExpenseDetailsDO; +import com.cf.imes.module.system.dal.mysql.incomeandexpense.IncomeExpenseDetailsMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IncomeExpenseServiceImpl} 的单元测试类 + * + * @author Gqr + */ +@Import(IncomeExpenseServiceImpl.class) +class IncomeExpenseServiceImplTest extends BaseDbAndRedisUnitTest { + + @Resource + private IncomeExpenseServiceImpl incomeExpenseService; + + @Resource + private IncomeExpenseDetailsMapper incomeExpenseDetailsMapper; + + @Test + void testCreateDetail() { + // 准备数据 + IncomeExpenseDetailsDO incomeExpenseDetailsDO = new IncomeExpenseDetailsDO(); + incomeExpenseDetailsDO.setBusinessNo("BUS20251215001"); + incomeExpenseDetailsDO.setOrderNo("ORD20251215001"); + incomeExpenseDetailsDO.setOrganId(1L); + incomeExpenseDetailsDO.setPurchaseId(100L); + incomeExpenseDetailsDO.setTradeType(1); + incomeExpenseDetailsDO.setIncomeExpenseType(2); + incomeExpenseDetailsDO.setAmount(new BigDecimal("100.00")); + incomeExpenseDetailsDO.setCurrency(1); + incomeExpenseDetailsDO.setCurrencyUnit("CNY"); + incomeExpenseDetailsDO.setRemark("测试收支明细"); + + // 调用方法 + Long id = incomeExpenseService.createDetail(incomeExpenseDetailsDO); + + // 验证结果 + assertNotNull(id); + assertTrue(id > 0); + + // 验证数据库中是否已插入数据 + IncomeExpenseDetailsDO dbRecord = incomeExpenseDetailsMapper.selectById(id); + assertNotNull(dbRecord); + assertEquals("BUS20251215001", dbRecord.getBusinessNo()); + assertEquals("ORD20251215001", dbRecord.getOrderNo()); + assertEquals(1L, dbRecord.getOrganId()); + assertEquals(100L, dbRecord.getPurchaseId()); + assertEquals(1, dbRecord.getTradeType()); + assertEquals(2, dbRecord.getIncomeExpenseType()); + assertEquals(new BigDecimal("100.00"), dbRecord.getAmount()); + assertEquals(1, dbRecord.getCurrency()); + assertEquals("CNY", dbRecord.getCurrencyUnit()); + assertEquals("测试收支明细", dbRecord.getRemark()); + } +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImplTest.java new file mode 100644 index 000000000..98286b158 --- /dev/null +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/invoice/InvoiceServiceImplTest.java @@ -0,0 +1,906 @@ +package com.cf.imes.module.system.service.funds.invoice; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.cf.imes.framework.common.exception.ServiceException; +import com.cf.imes.framework.common.exception.enums.GlobalErrorCodeConstants; +import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.security.core.util.SecurityFrameworkUtils; +import com.cf.imes.framework.security.test.WithMockLoginUser; +import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest; +import com.cf.imes.module.infra.api.file.FileApi; +import com.cf.imes.module.system.controller.admin.funds.invoice.vo.*; +import com.cf.imes.module.system.dal.dataobject.funds.invoice.InvoiceRecordsDO; +import com.cf.imes.module.system.dal.dataobject.funds.invoice.InvoiceTitleInfoDO; +import com.cf.imes.module.system.dal.dataobject.funds.purchase.PurchaseRecordDO; +import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO; +import com.cf.imes.module.system.dal.mysql.funds.invoice.InvoiceRecordsMapper; +import com.cf.imes.module.system.dal.mysql.funds.invoice.InvoiceTitleInfoMapper; +import com.cf.imes.module.system.dal.mysql.funds.purchase.PurchaseRecordMapper; +import com.cf.imes.module.system.enums.pay.InvoiceIssueTypeEnum; +import com.cf.imes.module.system.enums.pay.InvoiceStatusEnum; +import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum; +import com.cf.imes.module.system.enums.pay.TradeTypeEnum; +import com.cf.imes.module.system.service.organ.OrganService; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; +import org.springframework.mock.web.MockMultipartFile; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; + +import static com.cf.imes.framework.common.util.json.JsonUtils.zipString; +import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException; +import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +/** + * 发票服务测试类 + * + * @author Gqr + * @since 2025/12/15 15:31 + */ +@Import(InvoiceServiceImpl.class) +class InvoiceServiceImplTest extends BaseDbAndRedisUnitTest { + + @Resource + private InvoiceServiceImpl invoiceService; + + @Resource + private InvoiceTitleInfoMapper invoiceTitleInfoMapper; + + @Resource + private PurchaseRecordMapper purchaseRecordMapper; + + @Resource + private InvoiceRecordsMapper invoiceRecordsMapper; + + @MockitoBean + private OrganService organService; + + @MockitoBean + private FileApi fileApi; + + @Test + @WithMockLoginUser + void testGetInvoiceTitle() { + // 准备参数 + Long organId = SecurityFrameworkUtils.getUserOrganId(); + + // 使用真实数据查询发票抬头信息 + InvoiceTitleInfoDO invoiceTitleInfoDO = randomPojo(InvoiceTitleInfoDO.class, o -> { + o.setId(1L); + o.setOrganId(organId); + o.setInvoiceTitle("测试公司"); + }); + + invoiceTitleInfoMapper.insert(invoiceTitleInfoDO); + + // 调用 + InvoiceTitleInfo result = invoiceService.getInvoiceTitle(); + + // 断言 + assertNotNull(result); + assertEquals(1L, result.getId()); + assertEquals("测试公司", result.getInvoiceTitle()); + assertEquals(organId, result.getOrganId()); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_create_success() { + Long organId = SecurityFrameworkUtils.getUserOrganId(); + + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("测试公司"); + reqVO.setIssueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()); + reqVO.setInvoiceType(0); + reqVO.setTaxNumber("91330101MA2HXXXXXX"); + reqVO.setEmail("test@company.com"); + + invoiceService.insetInvoiceTitle(reqVO); + + // ====== 校验数据库 ====== + InvoiceTitleInfoDO dbRecord = invoiceTitleInfoMapper.selectOne( + new LambdaQueryWrapper() + .eq(InvoiceTitleInfoDO::getOrganId, organId) + ); + + assertNotNull(dbRecord); + assertEquals("测试公司", dbRecord.getInvoiceTitle()); + assertEquals("91330101MA2HXXXXXX", dbRecord.getTaxNumber()); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_enterpriseWithoutTaxNumber_throwException() { + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("测试公司"); + reqVO.setIssueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()); + reqVO.setInvoiceType(0); + reqVO.setTaxNumber(null); + + ServiceException ex = assertThrows( + ServiceException.class, + () -> invoiceService.insetInvoiceTitle(reqVO) + ); + + assertEquals( + INVOICE_TITLE_ENTERPRISE_TAXNUMBER_EMPTY_ERROR.getCode(), + ex.getCode() + ); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_sameOrgan_throwOrgExists() { + Long organId = SecurityFrameworkUtils.getUserOrganId(); + + invoiceTitleInfoMapper.insert(InvoiceTitleInfoDO.builder() + .organId(organId) + .issueType(InvoiceIssueTypeEnum.PERSONAL.getValue()) + .invoiceTitle("已存在") + .build()); + + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("新抬头"); + reqVO.setIssueType(InvoiceIssueTypeEnum.PERSONAL.getValue()); + + ServiceException ex = assertThrows( + ServiceException.class, + () -> invoiceService.insetInvoiceTitle(reqVO) + ); + + assertEquals( + INVOICE_TITLE_ORG_EXISTS_ERROR.getCode(), + ex.getCode() + ); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_update_notExists_throwException() { + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setId(999L); // 数据库不存在 + reqVO.setInvoiceTitle("测试抬头"); + reqVO.setIssueType(InvoiceIssueTypeEnum.PERSONAL.getValue()); + + ServiceException ex = assertThrows( + ServiceException.class, + () -> invoiceService.insetInvoiceTitle(reqVO) + ); + + assertEquals( + INVOICE_TITLE_NOT_EXISTS_ERROR.getCode(), + ex.getCode() + ); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_update_orgNotMatch_throwException() { + InvoiceTitleInfoDO exist = InvoiceTitleInfoDO.builder() + .organId(2L) + .issueType(InvoiceIssueTypeEnum.PERSONAL.getValue()) + .invoiceTitle("原抬头") + .build(); + invoiceTitleInfoMapper.insert(exist); + + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setId(exist.getId()); + reqVO.setInvoiceTitle("非法更新"); + reqVO.setIssueType(exist.getIssueType()); + + ServiceException ex = assertThrows( + ServiceException.class, + () -> invoiceService.insetInvoiceTitle(reqVO) + ); + + assertEquals( + INVOICE_TITLE_NOT_EXISTS_ERROR.getCode(), + ex.getCode() + ); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_insert_success() { + Long organId = SecurityFrameworkUtils.getUserOrganId(); + + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("新增抬头"); + reqVO.setIssueType(InvoiceIssueTypeEnum.PERSONAL.getValue()); + reqVO.setInvoiceType(0); + reqVO.setEmail("test@test.com"); + + invoiceService.insetInvoiceTitle(reqVO); + + // 验证数据库 + List list = invoiceTitleInfoMapper.selectList(null); + assertEquals(1, list.size()); + + InvoiceTitleInfoDO saved = list.get(0); + assertEquals(organId, saved.getOrganId()); + assertEquals("新增抬头", saved.getInvoiceTitle()); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_enterprise_taxNumberExists_throwException() { + // 先插入一条企业发票抬头 + InvoiceTitleInfoDO exist = InvoiceTitleInfoDO.builder() + .organId(2L) // 不同组织也没关系 + .invoiceTitle("已存在抬头") + .issueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()) + .taxNumber("91330100XXXXXX") + .build(); + invoiceTitleInfoMapper.insert(exist); + + // 再新增一条相同税号 + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setInvoiceTitle("新抬头"); + reqVO.setIssueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()); + reqVO.setTaxNumber("91330100XXXXXX"); + + ServiceException ex = assertThrows( + ServiceException.class, + () -> invoiceService.insetInvoiceTitle(reqVO) + ); + + assertEquals( + INVOICE_TITLE_TAXPAYERID_EXISTS_ERROR.getCode(), + ex.getCode() + ); + } + + @Test + @WithMockLoginUser + void insetInvoiceTitle_update_success() { + Long organId = SecurityFrameworkUtils.getUserOrganId(); + + // given:数据库已有数据(organId 一定要一致) + InvoiceTitleInfoDO exist = InvoiceTitleInfoDO.builder() + .organId(organId) + .invoiceTitle("旧抬头") + .issueType(InvoiceIssueTypeEnum.PERSONAL.getValue()) + .build(); + invoiceTitleInfoMapper.insert(exist); + + // when:更新 + InvoiceTitleSaveReqVO reqVO = new InvoiceTitleSaveReqVO(); + reqVO.setId(exist.getId()); + reqVO.setInvoiceTitle("新抬头"); + reqVO.setIssueType(InvoiceIssueTypeEnum.PERSONAL.getValue()); + + invoiceService.insetInvoiceTitle(reqVO); + + // then:断言更新成功 + InvoiceTitleInfoDO updated = invoiceTitleInfoMapper.selectById(exist.getId()); + assertEquals("新抬头", updated.getInvoiceTitle()); + } + + @Test + @WithMockLoginUser + void getAvailablePage_whenEmptyList_returnEmptyPageResult() { + // given + InvoiceAmountAndAvailablePageReqVO reqVO = new InvoiceAmountAndAvailablePageReqVO(); + + // when + PageResult result = + invoiceService.getAvailablePage(reqVO); + + // then + assertNotNull(result); + assertEquals(0, result.getTotal()); + assertTrue(CollUtil.isEmpty(result.getList())); + } + + @Test + @WithMockLoginUser(organId = 1L) + void getAvailablePage_whenHasData_returnConvertedPage() { + // given + InvoiceAmountAndAvailablePageReqVO reqVO = + new InvoiceAmountAndAvailablePageReqVO(); + + PurchaseRecordDO invoiceRecordsDO = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setCreateTime(LocalDateTime.now()); + o.setExternalSpent(new BigDecimal("100.00")); + o.setProductName("测试产品"); + o.setTotalAmount(new BigDecimal("200.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + o.setOrganId(1L); + }); + purchaseRecordMapper.insert(invoiceRecordsDO); + + // when + PageResult result = + invoiceService.getAvailablePage(reqVO); + + // then + assertNotNull(result); + assertEquals(1L, result.getTotal()); + assertEquals(1, result.getList().size()); + + InvoiceAvailablePageRespVO vo = result.getList().get(0); + assertEquals(1L, vo.getId()); + assertEquals(TradeTypeEnum.PRODUCT.getCode(), vo.getTradeType()); + assertEquals(invoiceRecordsDO.getExternalSpent(), vo.getAvailableAmount()); + assertEquals("测试产品", vo.getProductName()); + assertEquals(invoiceRecordsDO.getTotalAmount(), vo.getTotalAmount()); + } + + @Test + void applyInvoice_whenNoLogin_throwUnauthorized() { + InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO(); + + assertServiceException(() -> invoiceService.applyInvoice(reqVO), GlobalErrorCodeConstants.UNAUTHORIZED); + } + + @Test + @WithMockLoginUser(organId = 1L) + void applyInvoice_whenUninvoicableExist_throwException() { + InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO(); + assertServiceException(() -> invoiceService.applyInvoice(reqVO), INVOICE_TITLE_NOT_EXISTS_ERROR); + } + + @Test + @WithMockLoginUser(organId = 1L) + void applyInvoice_uninvoiceableExist() { + // 先插入一条企业发票抬头 + InvoiceTitleInfoDO exist = InvoiceTitleInfoDO.builder() + .organId(1L) // 不同组织也没关系 + .invoiceTitle("已存在抬头") + .issueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()) + .taxNumber("91330100XXXXXX") + .build(); + invoiceTitleInfoMapper.insert(exist); + + InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO(); + + PurchaseRecordDO invoiceRecordsDO = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setCreateTime(LocalDateTime.now()); + o.setExternalSpent(new BigDecimal("100.00")); + o.setProductName("测试产品"); + o.setTotalAmount(new BigDecimal("200.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode()); + o.setInitial(false); + o.setOrganId(1L); + }); + purchaseRecordMapper.insert(invoiceRecordsDO); + // 有不可开票状态的记录 + reqVO.setPurchaseRecordId(List.of(invoiceRecordsDO.getId())); + assertServiceException(() -> invoiceService.applyInvoice(reqVO), INVOICE_APPLY_UNINVOICEABLE_EXIST); + + // 可开金额为0 + invoiceRecordsDO.setId(null); + invoiceRecordsDO.setExternalSpent(BigDecimal.ZERO); + invoiceRecordsDO.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + purchaseRecordMapper.insert(invoiceRecordsDO); + reqVO.setPurchaseRecordId(List.of(invoiceRecordsDO.getId())); + assertServiceException(() -> invoiceService.applyInvoice(reqVO), INVOICE_AMOUNT_ZERO_ERROR); + + // 更新开票状态和开票id数量不匹配 + invoiceRecordsDO.setId(null); + invoiceRecordsDO.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + invoiceRecordsDO.setExternalSpent(new BigDecimal("100")); + purchaseRecordMapper.insert(invoiceRecordsDO); + + reqVO.setPurchaseRecordId(List.of(invoiceRecordsDO.getId(), 999L)); + assertServiceException( + () -> invoiceService.applyInvoice(reqVO), + ORG_INVOICE_PURCHASE_RECORD_UPDATE_NUM_NOT_MATCH_ERROR + ); + } + + + @Test + @WithMockLoginUser(organId = 1L) + void applyInvoice_success() { + // 先插入一条企业发票抬头 + InvoiceTitleInfoDO exist = InvoiceTitleInfoDO.builder() + .organId(1L) // 不同组织也没关系 + .invoiceTitle("已存在抬头") + .issueType(InvoiceIssueTypeEnum.ENTERPRISE.getValue()) + .taxNumber("91330100XXXXXX") + .build(); + invoiceTitleInfoMapper.insert(exist); + + InvoiceRecordsReqVO reqVO = new InvoiceRecordsReqVO(); + + PurchaseRecordDO invoiceRecordsDO = randomPojo(PurchaseRecordDO.class, o -> { + o.setCreateTime(LocalDateTime.now()); + o.setExternalSpent(new BigDecimal("100.00")); + o.setProductName("测试产品"); + o.setTotalAmount(new BigDecimal("200.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + o.setOrganId(1L); + }); + purchaseRecordMapper.insert(invoiceRecordsDO); + + Long recordId = invoiceRecordsDO.getId(); + reqVO.setPurchaseRecordId(List.of(recordId)); + + // 组织 + OrganizationDO org = new OrganizationDO(); + org.setName("测试组织"); + org.setId(1L); + when(organService.validOrgan(1L)).thenReturn(org); + + assertDoesNotThrow(() -> { + invoiceService.applyInvoice(reqVO); + }); + List invoiceRecordsDOS = invoiceRecordsMapper.selectList(); + assertEquals(1, invoiceRecordsDOS.size()); + InvoiceRecordsDO query = invoiceRecordsDOS.get(0); + assertNotNull(invoiceRecordsDO); + assertEquals(invoiceRecordsDO.getOrganId(), invoiceRecordsDO.getOrganId()); + assertEquals(query.getPurchaseIds(), List.of(invoiceRecordsDO.getId()).toString()); + assertEquals("测试组织", query.getOrganName()); + } + + @Test + @WithMockLoginUser + void getInvoiceRecords_whenEmptyList_returnEmptyPageResult() { + // given + InvoiceDetailsPageReqVO pageReqVO = new InvoiceDetailsPageReqVO(); + + // when + PageResult result = invoiceService.getInvoiceRecords(pageReqVO); + + // then + assertNotNull(result); + assertEquals(0, result.getTotal()); + assertTrue(CollUtil.isEmpty(result.getList())); + } + + @Test + @WithMockLoginUser + void getInvoiceRecords_whenHasData_returnConvertedPage() { + // given + InvoiceDetailsPageReqVO pageReqVO = new InvoiceDetailsPageReqVO(); + + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o->{ + o.setId(1L); + o.setInvoiceAmount(new BigDecimal("100.00")); + o.setStatus(InvoiceStatusEnum.INVOICABLE.getCode()); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + // when + PageResult result = invoiceService.getInvoiceRecords(pageReqVO); + + // then + assertNotNull(result); + assertEquals(1L, result.getTotal()); + assertEquals(1, result.getList().size()); + + InvoiceRecordsRespVO vo = result.getList().get(0); + assertEquals(1L, vo.getId()); + assertEquals(invoiceRecordsDO.getInvoiceAmount(), vo.getInvoiceAmount()); + assertEquals(invoiceRecordsDO.getStatus(), vo.getStatus()); + } + + @Test + @WithMockLoginUser + void getInvoiceRecordDetail_whenRecordNotExists_thenThrowException() { + assertServiceException(() -> invoiceService.getInvoiceRecordDetail(1L), INVOICE_NO_EXIST); + } + + @Test + @WithMockLoginUser + void getInvoiceRecordDetail_whenRecordExists_thenReturnDetail() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setInvoiceAmount(new BigDecimal("100.00")); + o.setPurchaseIds("[1, 2]"); + o.setInvoiceTitleInfo(zipString("{\"invoiceTitle\":\"测试公司\"}")); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + PurchaseRecordDO purchase1 = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setProductName("产品1"); + o.setTotalAmount(new BigDecimal("50.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase1); + + PurchaseRecordDO purchase2 = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(2L); + o.setOrganId(1L); + o.setProductName("产品2"); + o.setTotalAmount(new BigDecimal("50.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase2); + + // 调用方法 + InvoiceDetailsRespVO result = invoiceService.getInvoiceRecordDetail(1L); + + // 验证结果 + assertNotNull(result); + assertEquals(2, result.getInvoicePurchaseRecords().size()); + assertEquals("测试公司", result.getInvoiceTitleInfo().getInvoiceTitle()); + } + + @Test + void confirmInvoice_whenNoLogin_thenThrowUnauthorized() { + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), GlobalErrorCodeConstants.UNAUTHORIZED); + } + + @Test + @WithMockLoginUser + void confirmInvoice_whenRecordNotExists_thenThrowException() { + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), INVOICE_NO_EXIST); + } + + @Test + @WithMockLoginUser + void confirmInvoice_whenRecordAlreadySuccess_thenThrowException() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode()); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), THIS_INVOICE_IS_FAIL); + } + + @Test + @WithMockLoginUser + void confirmInvoice_whenRecordAlreadyFailed_thenThrowException() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.INVOICINGFAILED.getCode()); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), THIS_INVOICE_IS_FAIL); + } + + @Test + @WithMockLoginUser + void confirmInvoice_whenPurchaseRecordNotExists_thenThrowException() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + o.setPurchaseIds("[1]"); + o.setInvoiceTitleId(1L); + o.setOrganId(1L); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), PURCHASE_RECORD_NO_EXIST); + } + + @Test + @WithMockLoginUser + void confirmInvoice_agreeWithoutFile_thenThrowException() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + o.setPurchaseIds("[1]"); + o.setInvoiceTitleId(1L); + o.setOrganId(1L); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceTitleInfoDO title = randomPojo(InvoiceTitleInfoDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setInvoiceTitle("测试公司"); + }); + invoiceTitleInfoMapper.insert(title); + + PurchaseRecordDO purchase = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + reqVO.setAgree(true); + + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), ORG_INVOICE_AGREE_FILE_NULL_ERROR); + } + + @Test + @WithMockLoginUser + void confirmInvoice_rejectWithoutRemark_thenThrowException() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + o.setPurchaseIds("[1]"); + o.setInvoiceTitleId(1L); + o.setOrganId(1L); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceTitleInfoDO title = randomPojo(InvoiceTitleInfoDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setInvoiceTitle("测试公司"); + }); + invoiceTitleInfoMapper.insert(title); + + PurchaseRecordDO purchase = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + reqVO.setAgree(false); + reqVO.setRemark(null); + + assertServiceException(() -> invoiceService.confirmInvoice(reqVO, null), INVOICING_REJECT_REASON); + } + + @Test + @WithMockLoginUser(username = "测试用户", userId = 1L) + void confirmInvoice_agreeWithFile_thenSuccess() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + o.setPurchaseIds("[1]"); + o.setInvoiceTitleId(1L); + o.setOrganId(1L); + o.setInvoiceAmount(new BigDecimal("100.00")); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceTitleInfoDO title = randomPojo(InvoiceTitleInfoDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setInvoiceTitle("测试公司"); + }); + invoiceTitleInfoMapper.insert(title); + + PurchaseRecordDO purchase = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase); + + // Mock 文件上传 + MockMultipartFile file = new MockMultipartFile("file", "测试公司-20231010101010-100.pdf", "application/pdf", "content".getBytes()); + when(fileApi.createFileAndReturnId(any(byte[].class), anyString())).thenReturn(1L); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + reqVO.setAgree(true); + reqVO.setInvoiceNo("FP20231010101010"); + reqVO.setRemark("同意开票"); + + // 调用方法 + assertDoesNotThrow(() -> invoiceService.confirmInvoice(reqVO, file)); + + // 验证数据库更新 + InvoiceRecordsDO updatedRecord = invoiceRecordsMapper.selectById(1L); + assertEquals(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode(), updatedRecord.getStatus()); + assertEquals("FP20231010101010", updatedRecord.getInvoiceNo()); + assertEquals("同意开票", updatedRecord.getRemark()); + assertEquals("测试用户", updatedRecord.getInvoicePerson()); + assertEquals(1L, updatedRecord.getInvoicePersonId()); + assertNotNull(updatedRecord.getInvoiceTime()); + assertEquals(1L, updatedRecord.getInvoiceAttachmentId()); + + PurchaseRecordDO updatedPurchase = purchaseRecordMapper.selectById(1L); + assertEquals(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode(), updatedPurchase.getIsInvocing()); + } + + @Test + @WithMockLoginUser(userId = 1L, username = "测试用户") + void confirmInvoice_reject_thenSuccess() { + // 准备数据 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setId(1L); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + o.setPurchaseIds("[1]"); + o.setInvoiceTitleId(1L); + o.setOrganId(1L); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + + InvoiceTitleInfoDO title = randomPojo(InvoiceTitleInfoDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setInvoiceTitle("测试公司"); + }); + invoiceTitleInfoMapper.insert(title); + + PurchaseRecordDO purchase = randomPojo(PurchaseRecordDO.class, o -> { + o.setId(1L); + o.setOrganId(1L); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + }); + purchaseRecordMapper.insert(purchase); + + InvoiceConfirmReqVO reqVO = new InvoiceConfirmReqVO(); + reqVO.setId(1L); + reqVO.setAgree(false); + reqVO.setRemark("拒绝开票"); + + // 调用方法 + assertDoesNotThrow(() -> invoiceService.confirmInvoice(reqVO, null)); + + // 验证数据库更新 + InvoiceRecordsDO updatedRecord = invoiceRecordsMapper.selectById(1L); + assertEquals(InvoiceStatusEnum.INVOICINGFAILED.getCode(), updatedRecord.getStatus()); + assertEquals("拒绝开票", updatedRecord.getRemark()); + assertEquals("测试用户", updatedRecord.getInvoicePerson()); + assertEquals(1L, updatedRecord.getInvoicePersonId()); + assertNotNull(updatedRecord.getInvoiceTime()); + + PurchaseRecordDO updatedPurchase = purchaseRecordMapper.selectById(1L); + assertEquals(InvoiceStatusEnum.INVOICABLE.getCode(), updatedPurchase.getIsInvocing()); + } + + @Test + @WithMockLoginUser + void getInvoiceAmount_whenNoData_thenReturnZeroValues() { + // 调用方法 + InvoiceAmountRespVO result = invoiceService.getInvoiceAmount(); + + // 验证结果 + assertNotNull(result); + assertEquals(BigDecimal.ZERO, result.getInvocable()); + assertEquals(BigDecimal.ZERO, result.getInTheInvoice()); + assertEquals(BigDecimal.ZERO, result.getInvoiced()); + } + + @Test + @WithMockLoginUser + void getInvoiceAmount_whenHasData_thenReturnCorrectValues() { + // 可开票 + PurchaseRecordDO purchaseRecordDO = randomPojo(PurchaseRecordDO.class, o->{ + o.setExternalSpent(new BigDecimal("100.00")); + o.setPaymentMethod(PayChannelCodeEnum.ALIPAY_PC.getCode()); + o.setIsInvocing(InvoiceStatusEnum.INVOICABLE.getCode()); + o.setInitial(false); + o.setOrganId(SecurityFrameworkUtils.getUserOrganId()); + }); + purchaseRecordMapper.insert(purchaseRecordDO); + + InvoiceAmountRespVO result = invoiceService.getInvoiceAmount(); + assertNotNull(result); + assertEquals(new BigDecimal("100.00"), result.getInvocable()); + + // 开票中 + purchaseRecordDO.setId(null); + purchaseRecordDO.setIsInvocing(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + purchaseRecordMapper.insert(purchaseRecordDO); + result = invoiceService.getInvoiceAmount(); + assertNotNull(result); + assertEquals(new BigDecimal("100.00"), result.getInTheInvoice()); + + // 已开票 + purchaseRecordDO.setId(null); + purchaseRecordDO.setIsInvocing(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode()); + purchaseRecordMapper.insert(purchaseRecordDO); + result = invoiceService.getInvoiceAmount(); + assertNotNull(result); + assertEquals(new BigDecimal("100.00"), result.getInvoiced()); + } + + @Test + void getManageInvoiceAmount_whenNoData_thenReturnZeroValues() { + // 调用方法 + InvoiceAmountRespVO result = invoiceService.getManageInvoiceAmount(); + + // 验证结果 + assertNotNull(result); + assertEquals(BigDecimal.ZERO, result.getPendingInvoice()); + assertEquals(BigDecimal.ZERO, result.getInvoiced()); + } + + @Test + void getManageInvoiceAmount_whenHasData_thenReturnCorrectValues() { + // 待开票 + InvoiceRecordsDO invoiceRecordsDO = randomPojo(InvoiceRecordsDO.class, o -> { + o.setInvoiceAmount(new BigDecimal("150.00")); + o.setStatus(InvoiceStatusEnum.PENDINGINVOICING.getCode()); + }); + invoiceRecordsMapper.insert(invoiceRecordsDO); + InvoiceAmountRespVO result = invoiceService.getManageInvoiceAmount(); + assertNotNull(result); + assertEquals(new BigDecimal("150.00"), result.getPendingInvoice()); + + // 已开票 + invoiceRecordsDO.setId(null); + invoiceRecordsDO.setStatus(InvoiceStatusEnum.INVOICINGSUCCESSFUL.getCode()); + invoiceRecordsDO.setInvoiceAmount(new BigDecimal("300.00")); + invoiceRecordsMapper.insert(invoiceRecordsDO); + result = invoiceService.getManageInvoiceAmount(); + assertEquals(new BigDecimal("300.00"), result.getInvoiced()); + } + + @Test + void getFileExtension_whenFileNameIsNull_thenReturnEmpty() { + assertEquals("", InvoiceServiceImpl.getFileExtension(null)); + } + + @Test + void getFileExtension_whenFileNameIsEmpty_thenReturnEmpty() { + assertEquals("", InvoiceServiceImpl.getFileExtension("")); + } + + @Test + void getFileExtension_whenFileNameHasNoExtension_thenReturnEmpty() { + assertEquals("", InvoiceServiceImpl.getFileExtension("filename")); + } + + @Test + void getFileExtension_whenFileNameEndsWithDot_thenReturnEmpty() { + assertEquals("", InvoiceServiceImpl.getFileExtension("filename.")); + } + + @Test + void getFileExtension_whenFileNameHasExtension_thenReturnExtension() { + assertEquals(".pdf", InvoiceServiceImpl.getFileExtension("filename.pdf")); + } + + @Test + void getFileExtension_whenFileNameHasMultipleDots_thenReturnLastExtension() { + assertEquals(".pdf", InvoiceServiceImpl.getFileExtension("file.name.pdf")); + } + + @Test + void getFileExtension_whenFileNameHasExtensionInUpperCase_thenReturnLowerCaseExtension() { + assertEquals(".pdf", InvoiceServiceImpl.getFileExtension("filename.PDF")); + } + +} \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/productpromotion/ProductPromotionServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/productpromotion/ProductPromotionServiceImplTest.java index ad5dc70cb..a75337c3d 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/productpromotion/ProductPromotionServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/funds/productpromotion/ProductPromotionServiceImplTest.java @@ -2,6 +2,7 @@ package com.cf.imes.module.system.service.funds.productpromotion; import cn.hutool.core.bean.BeanUtil; import com.cf.imes.framework.common.pojo.PageResult; +import com.cf.imes.framework.common.util.i18n.core.util.I18nUtils; import com.cf.imes.framework.test.core.ut.BaseDbClearAfterClassAndRedisUnitTest; import com.cf.imes.module.system.controller.admin.funds.productpromotion.vo.ProductPromotionPageReqVO; import com.cf.imes.module.system.controller.admin.funds.productpromotion.vo.ProductPromotionSaveReqVO; @@ -50,6 +51,9 @@ class ProductPromotionServiceImplTest extends BaseDbClearAfterClassAndRedisUnitT @MockitoBean private ProductsDetailService productsDetailService; + @MockitoBean + private I18nUtils i18nUtils; + private static ProductPromotionDO createDO; @Test diff --git a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/user/AdminUserServiceImplTest.java b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/user/AdminUserServiceImplTest.java index 7d37ef62c..5da4b4fd6 100644 --- a/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/user/AdminUserServiceImplTest.java +++ b/cf-module-system/cf-module-system-biz/src/test/java/com/cf/imes/module/system/service/user/AdminUserServiceImplTest.java @@ -5,7 +5,6 @@ import com.cf.imes.framework.common.enums.CommonStatusEnum; import com.cf.imes.framework.common.exception.ServiceException; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.collection.ArrayUtils; -import com.cf.imes.framework.common.util.collection.CollectionUtils; import com.cf.imes.framework.test.core.ut.BaseDbUnitTest; import com.cf.imes.module.infra.api.file.FileApi; import com.cf.imes.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO; @@ -15,11 +14,8 @@ import com.cf.imes.module.system.controller.admin.user.vo.user.UserImportRespVO; import com.cf.imes.module.system.controller.admin.user.vo.user.UserPageReqVO; import com.cf.imes.module.system.controller.admin.user.vo.user.UserSaveReqVO; import com.cf.imes.module.system.dal.dataobject.dept.DeptDO; -import com.cf.imes.module.system.dal.dataobject.dept.PostDO; -import com.cf.imes.module.system.dal.dataobject.dept.UserPostDO; import com.cf.imes.module.system.dal.dataobject.organ.OrganizationDO; import com.cf.imes.module.system.dal.dataobject.user.AdminUserDO; -import com.cf.imes.module.system.dal.mysql.dept.UserPostMapper; import com.cf.imes.module.system.dal.mysql.user.AdminUserMapper; import com.cf.imes.module.system.enums.common.SexEnum; import com.cf.imes.module.system.service.dept.DeptService; @@ -27,11 +23,12 @@ import com.cf.imes.module.system.service.permission.PermissionService; import com.cf.imes.module.system.service.organ.OrganService; import org.junit.jupiter.api.Test; import org.mockito.stubbing.Answer; -import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.security.crypto.password.PasswordEncoder; import static com.cf.imes.framework.test.core.util.RandomUtils.*; import jakarta.annotation.Resource; +import org.springframework.test.context.bean.override.mockito.MockitoBean; + import java.io.ByteArrayInputStream; import java.util.Collection; import java.util.List; @@ -62,18 +59,16 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { @Resource private AdminUserMapper userMapper; - @Resource - private UserPostMapper userPostMapper; - @MockBean + @MockitoBean private DeptService deptService; - @MockBean + @MockitoBean private PermissionService permissionService; - @MockBean + @MockitoBean private PasswordEncoder passwordEncoder; - @MockBean + @MockitoBean private OrganService tenantService; - @MockBean + @MockitoBean private FileApi fileApi; @Test @@ -96,13 +91,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { o.setStatus(CommonStatusEnum.ENABLE.getStatus()); }); when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - // mock postService 的方法 - List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> - randomPojo(PostDO.class, o -> { - o.setId(postId); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - })); - //when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); // 调用 Long userId = userService.createUser(reqVO); @@ -111,10 +99,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { assertPojoEquals(reqVO, user, "password", "id"); assertEquals("chenfengyuanma", user.getPassword()); assertEquals(CommonStatusEnum.ENABLE.getStatus(), user.getStatus()); - // 断言关联岗位 - List userPosts = userPostMapper.selectListByUserId(user.getId()); - assertEquals(1L, userPosts.get(0).getPostId()); - assertEquals(2L, userPosts.get(1).getPostId()); } @Test @@ -137,8 +121,6 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { // mock 数据 AdminUserDO dbUser = randomAdminUserDO(o -> o.setPostIds(asSet(1L, 2L))); userMapper.insert(dbUser); - userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(1L)); - userPostMapper.insert(new UserPostDO().setUserId(dbUser.getId()).setPostId(2L)); // 准备参数 UserSaveReqVO reqVO = randomPojo(UserSaveReqVO.class, o -> { o.setId(dbUser.getId()); @@ -152,23 +134,12 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { o.setStatus(CommonStatusEnum.ENABLE.getStatus()); }); when(deptService.getDept(eq(dept.getId()))).thenReturn(dept); - // mock postService 的方法 - List posts = CollectionUtils.convertList(reqVO.getPostIds(), postId -> - randomPojo(PostDO.class, o -> { - o.setId(postId); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - })); - //when(postService.getPostList(eq(reqVO.getPostIds()), isNull())).thenReturn(posts); // 调用 userService.updateUser(reqVO); // 断言 AdminUserDO user = userMapper.selectById(reqVO.getId()); assertPojoEquals(reqVO, user, "password"); - // 断言关联岗位 - List userPosts = userPostMapper.selectListByUserId(user.getId()); - assertEquals(2L, userPosts.get(0).getPostId()); - assertEquals(3L, userPosts.get(1).getPostId()); } @Test @@ -627,12 +598,10 @@ public class AdminUserServiceImplTest extends BaseDbUnitTest { // mock user1 数据 AdminUserDO user1 = randomAdminUserDO(o -> o.setPostIds(asSet(10L, 30L))); userMapper.insert(user1); - userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(10L)); - userPostMapper.insert(new UserPostDO().setUserId(user1.getId()).setPostId(30L)); + // mock user2 数据 AdminUserDO user2 = randomAdminUserDO(o -> o.setPostIds(singleton(100L))); userMapper.insert(user2); - userPostMapper.insert(new UserPostDO().setUserId(user2.getId()).setPostId(100L)); // 调用 List result = userService.getUserListByPostIds(postIds); diff --git a/cf-module-system/cf-module-system-biz/src/test/resources/sql/clean.sql b/cf-module-system/cf-module-system-biz/src/test/resources/sql/clean.sql index b95c437fb..d8c40916e 100644 --- a/cf-module-system/cf-module-system-biz/src/test/resources/sql/clean.sql +++ b/cf-module-system/cf-module-system-biz/src/test/resources/sql/clean.sql @@ -40,4 +40,7 @@ DELETE FROM "products_details"; DELETE FROM "product_promotion"; DELETE FROM "advertisement"; DELETE FROM "product_delay_record"; -DELETE FROM "purchase_record" \ No newline at end of file +DELETE FROM "purchase_record"; +DELETE FROM "income_expense_details"; +DELETE FROM "invoice_title_info"; +DELETE FROM "invoice_records"; \ No newline at end of file diff --git a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql index 00bde23c0..1bffd9602 100644 --- a/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql +++ b/cf-module-system/cf-module-system-biz/src/test/resources/sql/create_tables.sql @@ -808,3 +808,77 @@ CREATE TABLE IF NOT EXISTS "purchase_record" "deleted" BIT NOT NULL DEFAULT FALSE COMMENT '是否删除:0否 1是', PRIMARY KEY ("id") ) COMMENT '购买记录表'; + +CREATE TABLE IF NOT EXISTS "income_expense_details" +( + "id" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT 'ID,主键', + "business_no" VARCHAR(64) DEFAULT NULL COMMENT '流水号', + "order_no" VARCHAR(64) DEFAULT NULL COMMENT '订单号', + "gift_money_id" BIGINT DEFAULT NULL COMMENT '充值-赠送金规则ID', + "purchase_id" BIGINT DEFAULT NULL COMMENT '购买-购买记录ID', + "organ_id" BIGINT NOT NULL DEFAULT '0' COMMENT '组织ID', + "trade_type" TINYINT NOT NULL COMMENT '交易类型 0充值 1软件购买', + "income_expense_type" TINYINT NOT NULL COMMENT '收支类型 0收入 1支出', + "cash_amount_change" DECIMAL(10, 2) NOT NULL DEFAULT '0.00' COMMENT '支出/入账现金金额', + "gift_amount_change" DECIMAL(10, 2) NOT NULL DEFAULT '0.00' COMMENT '支出/入账赠送金金额', + "available_amount" DECIMAL(10, 2) NOT NULL DEFAULT '0.00' COMMENT '可用余额(现金+赠送金)', + "amount" DECIMAL(10, 2) DEFAULT NULL COMMENT '现金余额', + "gift_amount" DECIMAL(10, 2) DEFAULT NULL COMMENT '赠送余额', + "currency" TINYINT DEFAULT NULL COMMENT '货币类型(字典)', + "currency_unit" VARCHAR(10) DEFAULT NULL COMMENT '货币单位', + "creator" VARCHAR(64) DEFAULT NULL COMMENT '创建者/操作人', + "create_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间/交易时间', + "updater" VARCHAR(64) DEFAULT NULL COMMENT '更新者', + "update_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + "remark" VARCHAR(255) DEFAULT NULL COMMENT '备注', + "deleted" BIT NOT NULL DEFAULT FALSE COMMENT '是否删除 0否 1是', + PRIMARY KEY ("id") +) COMMENT '收支明细表'; + +CREATE TABLE IF NOT EXISTS "invoice_title_info" +( + "id" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '主键,根据ID号规则生成', + "organ_id" BIGINT NOT NULL COMMENT '组织ID', + "invoice_title" VARCHAR(64) NOT NULL COMMENT '发票抬头', + "issue_type" TINYINT NOT NULL COMMENT '开具类型,0企业 1个人', + "invoice_type" TINYINT NOT NULL DEFAULT '0' COMMENT '发票类型,0增值税普通发票 1增值税专用发票', + "tax_number" VARCHAR(18) DEFAULT NULL COMMENT '统一社会信用代码', + "bank_name" VARCHAR(64) DEFAULT NULL COMMENT '开户银行名称', + "bank_account" VARCHAR(40) DEFAULT NULL COMMENT '开户银行账号', + "registered_address" VARCHAR(64) DEFAULT NULL COMMENT '注册场所地址', + "fixed_phone" VARCHAR(20) DEFAULT NULL COMMENT '注册固定电话', + "email" VARCHAR(255) DEFAULT '' COMMENT '邮箱', + "creator" VARCHAR(64) DEFAULT NULL COMMENT '创建者', + "create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + "updater" VARCHAR(64) DEFAULT NULL COMMENT '更新者', + "update_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" BIT DEFAULT FALSE COMMENT '是否删除,0否1是', + PRIMARY KEY ("id") +) COMMENT '发票抬头信息表'; + +CREATE TABLE IF NOT EXISTS "invoice_records" +( + "id" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '发票记录ID', + "organ_id" BIGINT NOT NULL DEFAULT '0' COMMENT '组织ID', + "organ_name" VARCHAR(30) DEFAULT NULL COMMENT '组织/发票抬头名称', + "user_id" BIGINT NOT NULL COMMENT '发票申请人ID', + "invoice_no" VARCHAR(32) DEFAULT NULL COMMENT '发票号', + "purchase_ids" VARCHAR(2048) DEFAULT NULL COMMENT '购买记录id集合', + "invoice_amount" DECIMAL(10, 2) NOT NULL DEFAULT '0.00' COMMENT '发票金额', + "status" TINYINT NOT NULL COMMENT '发票状态 1待开票 2已开票 3开票失败', + "invoice_attachment_path" VARCHAR(255) DEFAULT NULL COMMENT '发票附件保存路径', + "invoice_attachment_id" BIGINT DEFAULT NULL COMMENT '发票附件文件id', + "invoice_attachment_name" VARCHAR(255) DEFAULT NULL COMMENT '发票附件名称', + "invoice_person" VARCHAR(64) DEFAULT NULL COMMENT '开票人', + "invoice_person_id" BIGINT DEFAULT NULL COMMENT '开票人用户ID', + "invoice_time" TIMESTAMP DEFAULT NULL COMMENT '开票时间', + "invoice_title_id" BIGINT DEFAULT NULL COMMENT '抬头ID', + "invoice_title_info" CLOB DEFAULT NULL COMMENT '抬头的信息', + "remark" VARCHAR(1024) DEFAULT NULL COMMENT '备注', + "creator" VARCHAR(64) DEFAULT NULL COMMENT '创建者/申请人', + "create_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间/申请时间', + "updater" VARCHAR(64) DEFAULT NULL COMMENT '更新者', + "update_time" TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + "deleted" BIT NOT NULL DEFAULT FALSE COMMENT '是否删除 0否1是', + PRIMARY KEY ("id") +) COMMENT '开票记录表'; \ No newline at end of file