mirror of
https://gitee.com/zhijiantianya/yudao-cloud.git
synced 2026-10-06 07:22:58 +08:00
【同步】BOOT 和 CLOUD 的功能(oa)
This commit is contained in:
+11
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.fms.service.closing;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingOverviewRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingQueryReqVO;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.YearMonth;
|
||||
|
||||
@@ -57,6 +58,16 @@ public interface FmsClosingPeriodService {
|
||||
*/
|
||||
FmsClosingOverviewRespVO getClosingOverview(FmsClosingQueryReqVO queryReqVO, Long userId);
|
||||
|
||||
/**
|
||||
* 获得指定会计期间的损益结转余额(末级损益科目本期借贷差额绝对值之和)
|
||||
*
|
||||
* @param accountSetId 账套编号
|
||||
* @param month 会计期间
|
||||
* @param userId 用户编号
|
||||
* @return 损益余额
|
||||
*/
|
||||
BigDecimal getProfitLossBalance(Long accountSetId, String month, Long userId);
|
||||
|
||||
/**
|
||||
* 结账
|
||||
*
|
||||
|
||||
+5
@@ -198,6 +198,11 @@ public class FmsClosingPeriodServiceImpl implements FmsClosingPeriodService {
|
||||
.setCanClose(canClose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigDecimal getProfitLossBalance(Long accountSetId, String month, Long userId) {
|
||||
return calculateProfitLossBalance(accountSetId, LocalDateTimeUtils.parseYearMonth(month), userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@LogRecord(type = FMS_CLOSING_TYPE, subType = FMS_CLOSING_PERIOD_CLOSE_SUB_TYPE,
|
||||
|
||||
+10
@@ -53,6 +53,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
|
||||
import static cn.iocoder.yudao.module.fms.enums.ErrorCodeConstants.ACCOUNT_SET_NOT_INITIALIZED;
|
||||
import static cn.iocoder.yudao.module.fms.enums.ErrorCodeConstants.SUBJECT_CODE_DUPLICATE;
|
||||
import static cn.iocoder.yudao.module.fms.enums.ErrorCodeConstants.SUBJECT_CODE_RULE_INVALID;
|
||||
import static cn.iocoder.yudao.module.fms.enums.ErrorCodeConstants.SUBJECT_CURRENCY_INVALID;
|
||||
@@ -161,6 +162,9 @@ public class FmsSubjectServiceImpl implements FmsSubjectService {
|
||||
accountSetService.validateAccountSetWritePermission(createReqVO.getAccountSetId(), userId);
|
||||
// 1.2 查询财务参数
|
||||
FmsFinanceParameterDO financeParameter = financeParameterService.getFinanceParameter(createReqVO.getAccountSetId());
|
||||
if (financeParameter == null) {
|
||||
throw exception(ACCOUNT_SET_NOT_INITIALIZED);
|
||||
}
|
||||
// 1.3 校验科目编码唯一
|
||||
validateSubjectCodeUnique(null, createReqVO.getAccountSetId(), createReqVO.getCode());
|
||||
// 1.4 校验上级科目和科目编码规则
|
||||
@@ -209,6 +213,9 @@ public class FmsSubjectServiceImpl implements FmsSubjectService {
|
||||
// 1.2 查询财务参数
|
||||
FmsFinanceParameterDO financeParameter = financeParameterService.getFinanceParameter(
|
||||
updateReqVO.getAccountSetId());
|
||||
if (financeParameter == null) {
|
||||
throw exception(ACCOUNT_SET_NOT_INITIALIZED);
|
||||
}
|
||||
// 1.3 校验科目存在
|
||||
FmsSubjectDO subject = validateSubjectExists(updateReqVO.getAccountSetId(), updateReqVO.getId());
|
||||
if (ObjUtil.notEqual(subject.getParentId(), updateReqVO.getParentId())) {
|
||||
@@ -416,6 +423,9 @@ public class FmsSubjectServiceImpl implements FmsSubjectService {
|
||||
accountSetService.validateAccountSetWritePermission(accountSetId, userId);
|
||||
// 1.3 加载导入依赖
|
||||
FmsFinanceParameterDO financeParameter = financeParameterService.getFinanceParameter(accountSetId);
|
||||
if (financeParameter == null) {
|
||||
throw exception(ACCOUNT_SET_NOT_INITIALIZED);
|
||||
}
|
||||
Map<String, FmsSubjectDO> subjectMap = convertMap(
|
||||
subjectMapper.selectListByAccountSetIdAndType(accountSetId, null), FmsSubjectDO::getCode);
|
||||
Map<String, Long> auxiliaryTypeIdMap = auxiliaryTypeService.getAuxiliaryTypeIdMap(accountSetId);
|
||||
|
||||
+5
-8
@@ -7,8 +7,6 @@ import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingOverviewRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingQueryReqVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.subjectbalance.FmsLedgerSubjectBalanceRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.report.vo.balance.FmsBalanceSheetCheckRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.report.vo.balance.FmsBalanceSheetRowRespVO;
|
||||
@@ -292,7 +290,7 @@ public class FmsBalanceSheetServiceImpl implements FmsBalanceSheetService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断查询期间内各月的损益是否均已结转,逐月检查结账概览的损益余额
|
||||
* 判断查询期间内各月的损益是否均已结转
|
||||
*
|
||||
* @param listReqVO 查询条件
|
||||
* @param userId 用户编号
|
||||
@@ -302,12 +300,11 @@ public class FmsBalanceSheetServiceImpl implements FmsBalanceSheetService {
|
||||
YearMonth endMonth = LocalDateTimeUtils.parseYearMonth(listReqVO.getEndMonth());
|
||||
for (YearMonth currentMonth = LocalDateTimeUtils.parseYearMonth(listReqVO.getStartMonth());
|
||||
!currentMonth.isAfter(endMonth); currentMonth = currentMonth.plusMonths(1)) {
|
||||
FmsClosingQueryReqVO closingQueryReqVO = new FmsClosingQueryReqVO();
|
||||
closingQueryReqVO.setAccountSetId(listReqVO.getAccountSetId());
|
||||
closingQueryReqVO.setMonth(currentMonth.toString());
|
||||
FmsClosingOverviewRespVO overview = closingPeriodService.getClosingOverview(closingQueryReqVO, userId);
|
||||
// 直接查询损益余额,避免报表校验与结账概况循环调用
|
||||
BigDecimal profitLossBalance = closingPeriodService.getProfitLossBalance(
|
||||
listReqVO.getAccountSetId(), currentMonth.toString(), userId);
|
||||
// 当月损益余额不为零时,说明存在尚未结转的损益
|
||||
if (NumberUtils.zeroIfNull(overview.getProfitLossBalance()).signum() != 0) {
|
||||
if (NumberUtils.zeroIfNull(profitLossBalance).signum() != 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -116,6 +116,41 @@ public class FmsSubjectServiceImplTest extends BaseDbUnitTest {
|
||||
assertTrue(subjects.get(1).getAuxiliaryTypeIds().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSubject_accountSetNotInitialized() {
|
||||
// 准备参数
|
||||
FmsSubjectSaveReqVO reqVO = buildSubjectSaveReqVO(1L, "1001", "库存现金");
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> subjectService.createSubject(reqVO, 10L), ACCOUNT_SET_NOT_INITIALIZED);
|
||||
// 断言未写入科目
|
||||
assertEquals(0, subjectMapper.selectCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSubject_accountSetNotInitialized() {
|
||||
// 准备参数
|
||||
FmsSubjectSaveReqVO reqVO = buildSubjectSaveReqVO(1L, "1001", "库存现金");
|
||||
reqVO.setId(1L);
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> subjectService.updateSubject(reqVO, 10L), ACCOUNT_SET_NOT_INITIALIZED);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportSubjectList_accountSetNotInitialized() {
|
||||
// 准备参数
|
||||
FmsSubjectImportExcelVO importVO = new FmsSubjectImportExcelVO();
|
||||
importVO.setCode("1001");
|
||||
importVO.setName("库存现金");
|
||||
|
||||
// 调用,并断言异常
|
||||
assertServiceException(() -> subjectService.importSubjectList(1L, Collections.singletonList(importVO), 10L),
|
||||
ACCOUNT_SET_NOT_INITIALIZED);
|
||||
// 断言未写入科目
|
||||
assertEquals(0, subjectMapper.selectCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSubject_success() {
|
||||
// 准备参数
|
||||
|
||||
+6
-5
@@ -21,7 +21,6 @@ import cn.iocoder.yudao.module.fms.dal.mysql.report.balance.FmsBalanceSheetConfi
|
||||
import cn.iocoder.yudao.module.fms.dal.mysql.report.balance.FmsBalanceSheetReportMapper;
|
||||
import cn.iocoder.yudao.module.fms.enums.config.FmsSubjectTypeEnum;
|
||||
import cn.iocoder.yudao.module.fms.enums.report.FmsReportTypeEnum;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.closing.vo.FmsClosingOverviewRespVO;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingPeriodService;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingSchemeService;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingVoucherService;
|
||||
@@ -46,6 +45,7 @@ import java.util.List;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
@@ -206,8 +206,8 @@ public class FmsBalanceSheetServiceImplTest extends BaseDbUnitTest {
|
||||
FmsTrialBalanceRespVO trialBalance = new FmsTrialBalanceRespVO();
|
||||
trialBalance.setBalanced(false);
|
||||
when(initialBalanceService.getTrialBalance(1L, 10L)).thenReturn(trialBalance);
|
||||
when(closingPeriodService.getClosingOverview(any(), eq(10L)))
|
||||
.thenReturn(new FmsClosingOverviewRespVO().setProfitLossBalance(new BigDecimal("20.00")));
|
||||
when(closingPeriodService.getProfitLossBalance(1L, "2026-08", 10L))
|
||||
.thenReturn(new BigDecimal("20.00"));
|
||||
|
||||
// 调用
|
||||
FmsBalanceSheetCheckRespVO result = balanceSheetService.checkBalanceSheet(buildQueryReqVO(), 10L);
|
||||
@@ -241,14 +241,15 @@ public class FmsBalanceSheetServiceImplTest extends BaseDbUnitTest {
|
||||
.setChildren(Collections.emptyList())));
|
||||
when(initialBalanceService.getTrialBalance(1L, 10L))
|
||||
.thenReturn(new FmsTrialBalanceRespVO().setBalanced(true));
|
||||
when(closingPeriodService.getClosingOverview(any(), eq(10L)))
|
||||
.thenReturn(new FmsClosingOverviewRespVO().setProfitLossBalance(BigDecimal.ZERO));
|
||||
when(closingPeriodService.getProfitLossBalance(1L, "2026-08", 10L))
|
||||
.thenReturn(BigDecimal.ZERO);
|
||||
|
||||
// 调用
|
||||
FmsBalanceSheetCheckRespVO result = balanceSheetService.checkBalanceSheet(buildQueryReqVO(), 10L);
|
||||
|
||||
// 断言
|
||||
assertEquals(0, result.getUnmappedSubjects().size());
|
||||
assertTrue(result.getProfitLossTransferred());
|
||||
}
|
||||
|
||||
// ========== 随机对象 ==========
|
||||
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
package cn.iocoder.yudao.module.fms.service.report;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.ledger.vo.subjectbalance.FmsLedgerSubjectBalanceRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.report.vo.FmsReportItemRespVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.report.vo.FmsReportListReqVO;
|
||||
import cn.iocoder.yudao.module.fms.controller.admin.report.vo.balance.FmsBalanceSheetRowRespVO;
|
||||
import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsAccountSetDO;
|
||||
import cn.iocoder.yudao.module.fms.dal.dataobject.config.FmsSubjectDO;
|
||||
import cn.iocoder.yudao.module.fms.dal.dataobject.report.FmsReportTemplateDO;
|
||||
import cn.iocoder.yudao.module.fms.dal.mysql.report.FmsReportTemplateMapper;
|
||||
import cn.iocoder.yudao.module.fms.enums.report.FmsReportTypeEnum;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingPeriodService;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingSchemeService;
|
||||
import cn.iocoder.yudao.module.fms.service.closing.FmsClosingVoucherService;
|
||||
import cn.iocoder.yudao.module.fms.service.config.FmsAccountSetService;
|
||||
import cn.iocoder.yudao.module.fms.service.config.FmsFinanceParameterService;
|
||||
import cn.iocoder.yudao.module.fms.service.config.FmsInitialBalanceService;
|
||||
import cn.iocoder.yudao.module.fms.service.config.FmsSubjectService;
|
||||
import cn.iocoder.yudao.module.fms.service.ledger.FmsLedgerService;
|
||||
import cn.iocoder.yudao.module.fms.service.voucher.FmsVoucherService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@Import({FmsBalanceSheetServiceImpl.class, FmsIncomeStatementServiceImpl.class,
|
||||
FmsCashFlowStatementServiceImpl.class, FmsReportCommonServiceImpl.class})
|
||||
public class FmsReportTemplateServiceTest extends BaseDbUnitTest {
|
||||
|
||||
@Resource
|
||||
private DataSource dataSource;
|
||||
@Resource
|
||||
private FmsReportTemplateMapper reportTemplateMapper;
|
||||
@Resource
|
||||
private FmsBalanceSheetServiceImpl balanceSheetService;
|
||||
@Resource
|
||||
private FmsIncomeStatementServiceImpl incomeStatementService;
|
||||
@Resource
|
||||
private FmsCashFlowStatementServiceImpl cashFlowStatementService;
|
||||
|
||||
@MockitoBean
|
||||
private FmsAccountSetService accountSetService;
|
||||
@MockitoBean
|
||||
private FmsSubjectService subjectService;
|
||||
@MockitoBean
|
||||
private FmsLedgerService ledgerService;
|
||||
@MockitoBean
|
||||
private FmsFinanceParameterService financeParameterService;
|
||||
@MockitoBean
|
||||
private FmsInitialBalanceService initialBalanceService;
|
||||
@MockitoBean
|
||||
private FmsClosingPeriodService closingPeriodService;
|
||||
@MockitoBean
|
||||
private FmsClosingSchemeService closingSchemeService;
|
||||
@MockitoBean
|
||||
private FmsClosingVoucherService closingVoucherService;
|
||||
@MockitoBean
|
||||
private FmsVoucherService voucherService;
|
||||
|
||||
private final List<FmsSubjectDO> subjects = new ArrayList<>();
|
||||
private final List<FmsLedgerSubjectBalanceRespVO> balances = new ArrayList<>();
|
||||
|
||||
@BeforeEach
|
||||
public void before() {
|
||||
// 初始化报表模板
|
||||
initializeTemplates();
|
||||
// mock 账套、科目和账簿余额
|
||||
FmsAccountSetDO accountSet = new FmsAccountSetDO().setId(1L).setInitialized(true)
|
||||
.setStartTime(LocalDateTime.of(2026, 1, 1, 0, 0));
|
||||
when(accountSetService.validateAccountSetReadPermission(1L, 10L)).thenReturn(accountSet);
|
||||
when(subjectService.getSubjectList(1L, null, 10L)).thenReturn(subjects);
|
||||
when(ledgerService.getSubjectBalanceList(any(), eq(10L))).thenReturn(balances);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializeTemplates_repeatExecution() {
|
||||
// mock 数据:模拟用户修改已有报表公式
|
||||
FmsReportTemplateDO template = CollUtil.findOne(
|
||||
reportTemplateMapper.selectListByType(FmsReportTypeEnum.INCOME_STATEMENT.getType()),
|
||||
item -> Integer.valueOf(21).equals(item.getRowNo()));
|
||||
reportTemplateMapper.updateById(new FmsReportTemplateDO().setId(template.getId()).setFormula("[\"L1\"]"));
|
||||
|
||||
// 调用
|
||||
initializeTemplates();
|
||||
|
||||
// 断言
|
||||
assertEquals(58, reportTemplateMapper.selectListByType(FmsReportTypeEnum.BALANCE_SHEET.getType()).size());
|
||||
assertEquals(31, reportTemplateMapper.selectListByType(FmsReportTypeEnum.INCOME_STATEMENT.getType()).size());
|
||||
assertEquals(25, reportTemplateMapper.selectListByType(FmsReportTypeEnum.CASH_FLOW_STATEMENT.getType()).size());
|
||||
assertEquals(21, reportTemplateMapper.selectListByType(FmsReportTypeEnum.CASH_FLOW_ADJUSTMENT.getType()).size());
|
||||
assertEquals(135, reportTemplateMapper.selectCount());
|
||||
assertEquals("[\"L1\"]", reportTemplateMapper.selectById(template.getId()).getFormula());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializeTemplates_preserveExistingFormulas() {
|
||||
// mock 数据:模拟旧版报表公式
|
||||
List<FmsReportTemplateDO> templates = new ArrayList<>();
|
||||
for (FmsReportTemplateDO template : reportTemplateMapper.selectList()) {
|
||||
String oldFormula;
|
||||
if (FmsReportTypeEnum.BALANCE_SHEET.getType().equals(template.getType()) && (template.getRowNo() == 9
|
||||
|| template.getRowNo() == 24 || template.getRowNo() == 25)) {
|
||||
oldFormula = template.getFormula().replace("\"operator\":\"-\"", "\"operator\":\"+\"");
|
||||
} else if (FmsReportTypeEnum.INCOME_STATEMENT.getType().equals(template.getType())
|
||||
&& template.getRowNo() == 21) {
|
||||
oldFormula = "[\"L1-L2-L3-L11-L14-L18\"]";
|
||||
} else if (FmsReportTypeEnum.CASH_FLOW_STATEMENT.getType().equals(template.getType())
|
||||
&& template.getRowNo() == 19) {
|
||||
oldFormula = "[\"L14+L15-L17-L18\"]";
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
templates.add(template);
|
||||
reportTemplateMapper.updateById(new FmsReportTemplateDO().setId(template.getId()).setFormula(oldFormula));
|
||||
}
|
||||
|
||||
// 调用
|
||||
initializeTemplates();
|
||||
|
||||
// 断言
|
||||
assertEquals(5, templates.size());
|
||||
for (FmsReportTemplateDO template : templates) {
|
||||
assertEquals(template.getFormula(), reportTemplateMapper.selectById(template.getId()).getFormula());
|
||||
}
|
||||
assertEquals(135, reportTemplateMapper.selectCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializeTemplates_preserveLegacySubjectIds() {
|
||||
// mock 数据:模拟使用历史科目编号的公式
|
||||
FmsReportTemplateDO template = CollUtil.findOne(
|
||||
reportTemplateMapper.selectListByType(FmsReportTypeEnum.BALANCE_SHEET.getType()),
|
||||
item -> Integer.valueOf(25).equals(item.getRowNo()));
|
||||
String oldFormula = "[{\"operator\":\"+\",\"rules\":0,\"subjectId\":1371,"
|
||||
+ "\"subjectName\":\"无形资产\",\"subjectNumber\":\"1701\"},"
|
||||
+ "{\"operator\":\"+\",\"rules\":0,\"subjectId\":1372,"
|
||||
+ "\"subjectName\":\"累计摊销\",\"subjectNumber\":\"1702\"}]";
|
||||
reportTemplateMapper.updateById(new FmsReportTemplateDO().setId(template.getId()).setFormula(oldFormula));
|
||||
|
||||
// 调用
|
||||
initializeTemplates();
|
||||
|
||||
// 断言
|
||||
assertEquals(template.getFormula(), reportTemplateMapper.selectById(template.getId()).getFormula());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBalanceSheet_capitalContribution() {
|
||||
// mock 数据:投入资本 100 元
|
||||
mockSubjectBalance("1001", 1, 0, 100, 100, 0);
|
||||
mockSubjectBalance("3001", 2, 0, -100, 0, 100);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsBalanceSheetRowRespVO> balanceSheet = balanceSheetService.getBalanceSheet(reqVO, 10L);
|
||||
List<FmsReportItemRespVO> cashFlow = cashFlowStatementService.getCashFlowStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertAssetAmount(balanceSheet, 30, 100);
|
||||
FmsBalanceSheetRowRespVO total = CollUtil.findOne(balanceSheet,
|
||||
row -> Integer.valueOf(53).equals(row.getLiabilityRowNo()));
|
||||
assertNotNull(total);
|
||||
assertAmount(100, total.getLiabilityClosingAmount());
|
||||
assertItemAmount(cashFlow, 15, 100);
|
||||
assertItemAmount(cashFlow, 22, 100);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBalanceSheet_inventoryAndContraAccounts() {
|
||||
// mock 数据:存货、固定资产、无形资产及其备抵科目
|
||||
mockSubjectBalance("1403", 1, 0, 30, 30, 0);
|
||||
mockSubjectBalance("1407", 2, 0, -5, 0, 5);
|
||||
mockSubjectBalance("1621", 1, 0, 100, 100, 0);
|
||||
mockSubjectBalance("1622", 2, 0, -20, 0, 20);
|
||||
mockSubjectBalance("1701", 1, 0, 100, 100, 0);
|
||||
mockSubjectBalance("1702", 2, 0, -10, 0, 10);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsBalanceSheetRowRespVO> result = balanceSheetService.getBalanceSheet(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertAssetAmount(result, 9, 25);
|
||||
assertAssetAmount(result, 24, 80);
|
||||
assertAssetAmount(result, 25, 90);
|
||||
assertAssetAmount(result, 30, 195);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCashFlowStatement_creditSale() {
|
||||
// mock 数据:赊销 100 元并结转利润,尚未收款
|
||||
mockSubjectBalance("1001", 1, 0, 0, 0, 0);
|
||||
mockSubjectBalance("1122", 1, 0, 100, 100, 0);
|
||||
mockSubjectBalance("5001", 2, 0, 0, 0, 100);
|
||||
mockSubjectBalance("3103", 2, 0, -100, 0, 100);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsReportItemRespVO> incomeItems = incomeStatementService.getIncomeStatement(reqVO, 10L);
|
||||
List<FmsReportItemRespVO> result = cashFlowStatementService.getCashFlowStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertItemAmount(incomeItems, 1, 100);
|
||||
assertItemAmount(result, 1, 0);
|
||||
assertItemAmount(result, 6, 0);
|
||||
assertItemAmount(result, 20, 0);
|
||||
assertItemAmount(result, 22, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCashFlowStatement_purchaseInventory() {
|
||||
// mock 数据:期初现金 100 元,支付 40 元购入原材料
|
||||
mockSubjectBalance("1001", 1, 100, 60, 0, 40);
|
||||
mockSubjectBalance("1403", 1, 0, 40, 40, 0);
|
||||
mockSubjectBalance("3001", 2, -100, -100, 0, 0);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsReportItemRespVO> result = cashFlowStatementService.getCashFlowStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertItemAmount(result, 3, 40);
|
||||
assertItemAmount(result, 6, 0);
|
||||
assertItemAmount(result, 20, -40);
|
||||
assertItemAmount(result, 22, 60);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCashFlowStatement_payWagesAndIncomeTax() {
|
||||
// mock 数据:计提并支付工资 30 元、所得税 10 元,期末应付款为零
|
||||
mockSubjectBalance("1001", 1, 100, 60, 0, 40);
|
||||
mockSubjectBalance("3001", 2, -100, -100, 0, 0);
|
||||
mockSubjectBalance("3103", 2, 0, 40, 40, 0);
|
||||
mockSubjectBalance("5602", 1, 0, 0, 30, 0);
|
||||
mockSubjectBalance("5801", 1, 0, 0, 10, 0);
|
||||
mockSubjectBalance("2211", 2, 0, 0, 30, 30);
|
||||
mockSubjectBalance("2221", 2, 0, 0, 10, 10);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsReportItemRespVO> result = cashFlowStatementService.getCashFlowStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertItemAmount(result, 4, 30);
|
||||
assertItemAmount(result, 5, 10);
|
||||
assertItemAmount(result, 6, 0);
|
||||
assertItemAmount(result, 20, -40);
|
||||
assertItemAmount(result, 22, 60);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCashFlowStatement_repayLoan() {
|
||||
// mock 数据:期初现金 100 元,偿还借款 30 元
|
||||
mockSubjectBalance("1001", 1, 100, 70, 0, 30);
|
||||
mockSubjectBalance("2001", 2, -100, -70, 30, 0);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsReportItemRespVO> result = cashFlowStatementService.getCashFlowStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertItemAmount(result, 16, 30);
|
||||
assertItemAmount(result, 19, -30);
|
||||
assertItemAmount(result, 6, 0);
|
||||
assertItemAmount(result, 22, 70);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIncomeStatement_investmentIncomeAndNonOperatingExpense() {
|
||||
// mock 数据:投资收益 20 元,营业外支出 5 元
|
||||
mockSubjectBalance("5111", 2, 0, -20, 0, 20);
|
||||
mockSubjectBalance("5711", 1, 0, 5, 5, 0);
|
||||
|
||||
// 准备参数
|
||||
FmsReportListReqVO reqVO = buildQueryReqVO();
|
||||
|
||||
// 调用
|
||||
List<FmsReportItemRespVO> result = incomeStatementService.getIncomeStatement(reqVO, 10L);
|
||||
|
||||
// 断言
|
||||
assertItemAmount(result, 20, 20);
|
||||
assertItemAmount(result, 21, 20);
|
||||
assertItemAmount(result, 24, 5);
|
||||
assertItemAmount(result, 32, 15);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行报表模板升级脚本
|
||||
*/
|
||||
private void initializeTemplates() {
|
||||
new ResourceDatabasePopulator(new FileSystemResource("../../sql/mysql/upgrade/20260926-fms-report-template.sql"))
|
||||
.execute(dataSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟科目余额和发生额:余额借方为正、贷方为负,发生额已扣除损益结转数据
|
||||
*/
|
||||
private void mockSubjectBalance(String code, int direction, int opening, int closing, int debit, int credit) {
|
||||
Long id = Long.valueOf(code);
|
||||
subjects.add(new FmsSubjectDO().setId(id).setAccountSetId(1L).setCode(code).setName(code)
|
||||
.setLevel(1).setBalanceDirection(direction));
|
||||
balances.add(new FmsLedgerSubjectBalanceRespVO().setSubjectId(id).setSubjectCode(code)
|
||||
.setOpeningDebitAmount(BigDecimal.valueOf(Math.max(opening, 0)))
|
||||
.setOpeningCreditAmount(BigDecimal.valueOf(Math.max(-opening, 0)))
|
||||
.setEndingDebitAmount(BigDecimal.valueOf(Math.max(closing, 0)))
|
||||
.setEndingCreditAmount(BigDecimal.valueOf(Math.max(-closing, 0)))
|
||||
.setPeriodDebitAmount(BigDecimal.valueOf(debit)).setPeriodCreditAmount(BigDecimal.valueOf(credit))
|
||||
.setYearDebitAmount(BigDecimal.valueOf(debit)).setYearCreditAmount(BigDecimal.valueOf(credit))
|
||||
.setChildren(Collections.emptyList()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造 2026 年 8 月的报表查询参数
|
||||
*/
|
||||
private FmsReportListReqVO buildQueryReqVO() {
|
||||
FmsReportListReqVO reqVO = new FmsReportListReqVO();
|
||||
reqVO.setAccountSetId(1L);
|
||||
reqVO.setStartMonth("2026-08");
|
||||
reqVO.setEndMonth("2026-08");
|
||||
return reqVO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言报表指定行的本期和本年金额
|
||||
*/
|
||||
private void assertItemAmount(List<FmsReportItemRespVO> rows, int rowNo, int expected) {
|
||||
FmsReportItemRespVO item = CollUtil.findOne(rows, row -> Integer.valueOf(rowNo).equals(row.getRowNo()));
|
||||
assertNotNull(item);
|
||||
assertAmount(expected, item.getCurrentAmount());
|
||||
assertAmount(expected, item.getYearAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断言资产负债表指定行的资产期末余额
|
||||
*/
|
||||
private void assertAssetAmount(List<FmsBalanceSheetRowRespVO> rows, int rowNo, int expected) {
|
||||
FmsBalanceSheetRowRespVO item = CollUtil.findOne(rows,
|
||||
row -> Integer.valueOf(rowNo).equals(row.getAssetRowNo()));
|
||||
assertNotNull(item);
|
||||
assertAmount(expected, item.getAssetClosingAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* 按数值比较金额,忽略 BigDecimal 的小数位数差异
|
||||
*/
|
||||
private void assertAmount(int expected, BigDecimal actual) {
|
||||
assertNotNull(actual);
|
||||
assertEquals(0, BigDecimal.valueOf(expected).compareTo(actual), "实际金额:" + actual);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user