SpringBoot+Vue 自动生成会计凭证:一张报销单怎样变成借贷分录
自动制证的核心,是把已经确认的业务事实,按可解释的规则转换为分录。
图1|AI生成的转换关系示意,1500×1000。1,060/1,000/60及图中的人员、日期、科目是独立假设;拆税是配置示例,不是当前缺省报销模板效果。图中“符合会计准则”表示目标,实际政策与抵扣资格须经企业财务确认。
一、先看产品能交接什么,再问分录怎样生成
报销页面已经有费用、发票、部门和借款冲抵,为什么财务还要重新录凭证?
研发常给出的答案是“审批通过后插两条分录”。但同一张单据可能同时包含可抵扣税额、不同费用类型、多个承担部门和借款冲抵。只把票面总额复制两次,借贷可能平衡,业务口径却可能错。
RuoYi Office 当前有业务来源队列、凭证模板、表达式科目、辅助核算取值与凭证分录这些衔接能力。实施人员能查看模板取数,研发能沿来源追到业务快照,财务能决定什么时候生成。
本文沿“报销事实 → 来源快照 → 模板映射 → 分录校验”拆解。账套与科目的基础模型见第10篇,审核、过账和冲销留给第13篇。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
本次证据截止2026年11月1日。 当前运行后端是资产教程专用包,未由最新源码重建。“工作树逻辑”“现成页面观察”“独立假设与模型验证”分别表述,不合并为最新版全链路测试通过。
二、把报销金额拆清楚:费用、税额和待付款不是同一个数
报销快照应保存各金额的业务含义,而不是只传一个模糊的amount。
图2|本次独立只读会话打开报销id=3,单号FI504-2026081500003:500.00报销、200.00冲抵、300.00应付,明细净额442.48、税额57.52。保留“流程实例不存在”提示;页面能读,不代表这条历史流程完整。未提交、审批或付款。
这张图解释了三个不同问题:费用发生多少、其中税额多少、还要付员工多少。不能因为本次只付300,就把500的费用事实缩成300;也不能看到57.52税额,就未经资格确认全部记为可抵扣进项税。
当前fillAmounts累计每行总额、净额和税额,并仅对deductible=true的行累计deductTaxAmount。当净额和税额同时提供时,会核对二者相加是否等于该行两位小数的总额;缺省净额取总额、税额取零。
这属于金额结构校验,不能代替税务资格判断,也不能扩写为“所有缺失或异常金额组合已经拒绝”。
来源快照保存什么
来源快照是制证取数时保存的业务字段集合。 模板不应依赖今天重新查询业务主表得到的任意新值。
报销enqueueVoucher先整理表头,再保存lines。字段不只是供页面显示,后续表达式需要按约定名称读取。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
totalAmount/netAmount/taxAmount/deductTaxAmount |
|
|
|
offsetLoanAmount/payAmount/fundAccountId |
|
|
|
companyId/deptId/partnerType/partnerId/partnerName |
|
|
|
expenseSubjectCode/fundSubjectCode |
|
|
|
lines[].expenseTypeId/deptId/projectId/amount/netAmount/taxAmount/deductible/subjectCode |
|
payload顶层复制表头,同时保留header和lines。于是totalAmount和payload.header.totalAmount都可有来源,但二者不是允许随意冲突的两套口径。
下面这段真实代码同时给出三个重要事实:来源版本固定为1,来源主金额取报销总额,报销请求明确关闭即时生成。
源码摘录1:FinanceReimburseServiceImpl.java904—920行,连续原文节选;完整路径与文件SHA-256见来源审计。
payload.putAll(header);payload.put("header", header);payload.put("lines", lines);FinanceVoucherSourceReqDTO req = new FinanceVoucherSourceReqDTO();req.setSourceType(FinanceVoucherSourceTypeEnum.EXPENSE_CLAIM.getType());req.setSourceId(bill.getId());req.setSourceNo(bill.getBillCode());req.setSourceVersion(1);req.setCompanyId(bill.getCompanyId());req.setBookId(bill.getBookId());req.setBizDate(bill.getBillDate() != null ? bill.getBillDate() : LocalDate.now());req.setAmount(bill.getTotalAmount());req.setPayload(payload);req.setAutoGenerate(false);req.setRemark(StrUtil.blankToDefault(bill.getRemark(), "费用报销"));voucherSourceService.submitSource(req);}
入队时机,需要沿调用点核对
这份实现并不是所有报销一审批通过就立刻入队。
updateProcessStatus审批通过后先处理借款冲抵并计算待付:待付大于零时进入待付款;待付小于等于零时,处理票据和预算转实际并调用enqueueVoucher。
需要继续付款的路径,在addPaidAmount累计已付达到应付额后才调用enqueueVoucher;部分付款分支不做这一步。付款服务本身也提交PAYMENT来源,因此一次付款执行可能涉及两类业务事实。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
autoGenerate=false
|
|
|
精简ER:来源与结果必须接得回去
图3|按当前数据对象和schema整理的逻辑ER。连线表示应用关联,不宣称有物理外键或全部唯一约束;快照字段保存在JSON中。
来源队列的唯一键基线包含tenant_id + book_id + source_type + source_id + source_version + deleted。凭证保存sourceQueueId/templateId,把取数依据与选中的模板身份接回去。
但templateId不是模板内容版本。当前模板更新会替换模板明细,生成时读取当前模板;业务快照可重复读取,并不等于未来任意一次重试都使用同一份规则。
设计建议: 正式投产时将模板变更审批、版本快照、变更前后的试算样本一同纳入实施流程。本文没有把这些建议写成当前已交付的模板版本管理。
三、模板真正读什么:sourceAmount与payload要一起核对
当前执行代码把来源主金额注入sourceAmount,因此它确实参与缺省制证。
buildEnv先复制payload顶层,再将header中尚未出现的键补进去,最后注入来源元数据。相同业务键的优先级是:顶层payload优先于header;保留变量如sourceNo/sourceAmount随后由来源对象覆盖。
源码摘录2:FinanceVoucherTemplateServiceImpl.java373—388行,连续原文节选;完整路径与文件SHA-256见来源审计。
env.put("payload", source.getPayload() == null ? Map.of() : source.getPayload());env.put("sourceType", source.getSourceType());env.put("sourceId", source.getSourceId());env.put("sourceNo", source.getSourceNo());env.put("bizDate", source.getBizDate());env.put("period", source.getPeriod());env.put("sourceAmount", source.getAmount());// 单据未带部门时,用当前登录人部门填 DEPT 辅助核算,避免 5602 等费用科目生成失败if (env.get("deptId") == null) {Long deptId = SecurityFrameworkUtils.getLoginUserDeptId();if (deptId != null) {env.put("deptId", deptId);}}return env;}
这解释了一个容易被注释误导的地方:FinanceVoucherSourceDO.amount注释仍写“仅用于列表展示与核对”,但当前buildEnv实际将它暴露为sourceAmount,缺省报销及付款模板也使用这个变量。本文按执行链描述,不沿用只读字段注释得出的旧判断。
另一方面,amount这个普通变量并不自动等于sourceAmount。报销表头约定的是totalAmount,不能因为表单提示写了“例如amount”,就认为每种来源都提供amount。
|
|
|
|
|---|---|---|
sourceAmount |
|
|
totalAmount |
|
|
deductTaxAmount |
|
|
totalAmount - deductTaxAmount |
|
|
expenseSubjectCode |
|
|
deptId |
|
|
表达式能够算数,不代表它负责认定业务
当前求值使用Aviator,BOM声明版本5.4.3。服务把浮点字面量按BigDecimal解析,限制语法特性、可访问类和求值时间,并在取不到属性时允许返回nil。
这些设置的目标是纯取数计算,不是让模板调用任意Java能力,也不是本次完成了表达式沙箱安全审计。
模板保存时校验语法;但totalAmount拼成另一个合法变量名,语法仍可通过。金额求值为null后,渲染器会按零处理。因此,模板保存成功与业务字段齐全必须分开验收。
配置契约应列出每种来源的必需字段、类型和金额口径。缺关键字段时拒绝事实或给出明确错误,是建议补齐的产品契约,不能默认认为表达式引擎已经替业务做完了。
四、科目与辅助维度怎样映射,哪些细节不能想当然
当前渲染按模板行生成分录,不是自动遍历所有业务明细。
图4|本次只读打开P1操作学习账的SYS_EXPENSE_CLAIM既有编辑弹窗,未改值、未保存。两行均取sourceAmount:借方表达式expenseSubjectCode,贷方固定2241其他应付款;自动审核与自动过账显示“是”。这张配置图不代表本次执行了制证。
matchTemplate读取本账套、同来源类型的启用模板,按sort、ID排序。条件命中时取第一个;无条件模板先记为兜底,遍历完有条件规则后再采用;仍无兜底时尝试显式默认标记。
因此“sort小”并不是无条件模板永远先抢走全部业务。模板级条件决定选哪张模板,行条件决定选中模板里哪些分录参与,两者应分开配置。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
科目编码不是科目ID
expenseSubjectCode应返回“560203”这样的编码字符串。表达式不空但找不到科目时,立即抛异常,不继续走固定科目回退;空值且模板带固定ID时,才使用固定科目。
源码摘录4:FinanceVoucherTemplateServiceImpl.java456—474行,连续原文节选;完整路径与文件SHA-256见来源审计。
private FinanceSubjectDO resolveSubject(FinanceBookDO book, FinanceVoucherTemplateItemDO templateItem,Map<String, Object> env) {if (!FinanceTemplateSubjectModeEnum.isExpression(templateItem.getSubjectMode())) {return requireSubject(book, templateItem.getSubjectId());}String code = expressionService.evaluateString(templateItem.getSubjectExpr(), env);if (StrUtil.isNotBlank(code)) {FinanceSubjectDO subject = subjectMapper.selectByCode(book.getId(), StrUtil.trim(code));if (subject == null) {throw exception(VOUCHER_TEMPLATE_SUBJECT_NOT_FOUND, code);}return subject;}// 历史队列未平铺科目编码时,回退到缺省模板写入的固定科目if (templateItem.getSubjectId() != null) {return requireSubject(book, templateItem.getSubjectId());}throw exception(VOUCHER_TEMPLATE_SUBJECT_NOT_FOUND, "表达式返回空值");}
这一步只解决科目解析。正式保存时,凭证服务还核对账套、末级与启用状态;不能因表达式查到了科目,就说试算已经通过全部记账条件。
当前报销表头的resolveExpenseSubjectCode取第一笔映射成功的明细。虽然每个lines中都保存了subjectCode,缺省模板只有一个费用借方行,金额取整单主金额。
所以,住宿与交通映射不同科目时,不能把“两条明细均带科目”写成“自动生成两条费用分录”。 多费用、多部门、多项目需要明确分组契约及对应模板或扩展实现,并做专项验收。当前render没有业务明细自动展开循环。
辅助维度取值有顺序,也有边界
resolveAuxData只遍历科目绑定的auxTypes:先执行模板的idExpr/nameExpr,缺ID时再尝试约定字段。
|
|
|
|
|---|---|---|
|
|
deptId/deptName |
|
|
|
employeeId/borrowerId/partnerId/userId
|
|
|
|
projectId/projectName |
|
|
|
partnerId
|
|
凭证validateAndBuildAuxKey会检查科目声明的各辅助ID是否齐全,再构造组合键。辅助对象存在、有效、属于正确账套或业务范围,仍需结合主数据和权限专项验证。
这也是为什么“帮忙补当前登录人部门”只能解释技术取值,不能证明费用已经分到正确承担部门。
五、用一个独立案例复算拆税,不拿演示记录凑成功链
以下数字是独立假设,不对应图2或图5的真实记录。
假设只有一类费用、一个承担部门、一名员工,没有借款冲抵;报销总额1,060.00,净额1,000.00,税额60.00,企业财务已经确认本例60.00可抵扣。这里不根据税率或发票类别自动推断抵扣资格。
来源输入可抽象为:
{"sourceAmount": "1060.00","totalAmount": "1060.00","netAmount": "1000.00","taxAmount": "60.00","deductTaxAmount": "60.00","expenseSubjectCode": "660201","deptId": 103,"partnerId": 1001}
这是阅读示例,金额字符串方便精确展示;隔离执行时将金额构造成BigDecimal。sourceAmount是引擎注入变量,不是要求业务在payload里再复制一个同名字段。660201及下表其他科目必须由目标账套实际配置,本文不创建它们。
先明确希望得到的分录
|
|
|
|
|
|
|---|---|---|---|---|
|
|
|
totalAmount - deductTaxAmount |
|
|
|
|
|
deductTaxAmount |
|
|
|
|
|
sourceAmount |
|
|
复算结果:1,060.00 - 60.00 = 1,000.00;借方1,000.00 + 60.00 = 1,060.00,与贷方一致。
若本例60.00全部不可抵扣,把deductTaxAmount改为0:费用为1,060.00,税额分录为0而跳过,贷方仍为1,060.00。
这说明拆税时更重要的输入是经业务确认的可抵扣额。把netAmount无条件放进费用借方、同时只借可抵扣税额,可能漏掉不可抵扣部分。
本例是基于已有金额字段和模板行能力的配置方案,未保存到真实系统,也未执行真实来源试算或生成。当前缺省报销模板实际仍是总额费用借方、其他应付款贷方两行,不应把上表说成开箱默认效果。
单张平衡之后,还要检查来源组合
当前缺省报销与付款模板的设计方向分别是:
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
对于无冲抵、全额付款1,060的假设,两张凭证合起来应付相抵,费用只确认一次,资金只减少一次。不是让报销和付款两张模板都借费用、贷银行。
源码还有对旧缺省报销贷方的定向调整:仅命中默认标记且贷方仍是fundSubjectCode时,改挂其他应付款。因此也不能从模板备注“不会覆盖”推断系统永远不会调整任何旧缺省行。
但是图2展示的是500总额、200借款冲抵、300应付。若仅按上表生成“报销挂应付500”和“付款清应付300”,会残留200应付;它需要与借款核算一起说明冲抵如何结清。
这只是缺少冲抵分录时的算术检查,不是已确认运行库存在200错误余额。 本次没有串联该单的凭证、借款余额和实际付款,也不推断其他链路是否已经补记。
交付验收应把借款原放款、报销费用、冲抵和现金付款并排复算,核对费用、其他应收、其他应付与资金。只检查“每一张借贷平衡”,发现不了少记或重复确认。
六、两位小数、零金额与借贷平衡,谁在哪一层负责
金额先按分录行舍入,再累计借贷合计。
模板渲染时金额用BigDecimal,保留2位、HALF_UP;空值视为零,零金额行直接跳过;负金额会翻转借贷方向并取绝对值。
源码摘录3:FinanceVoucherTemplateServiceImpl.java405—428行,连续原文节选;完整路径与文件SHA-256见来源审计。
BigDecimal amount = expressionService.evaluateDecimal(templateItem.getAmountExpr(), env);amount = amount == null ? BigDecimal.ZERO : amount.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);previewItem.setAmount(amount);if (amount.compareTo(BigDecimal.ZERO) == 0) {// 金额为零的分录没有账务意义(如免税报销的税额行),直接不生成previewItem.setSkipReason("金额取数为 0");continue;}Integer direction = templateItem.getDirection();if (amount.compareTo(BigDecimal.ZERO) < 0) {// 取数为负说明业务方向与模板配置相反(如冲减折扣),翻方向比记负数更符合会计习惯direction = FinanceBalanceDirectionEnum.isDebit(direction)? FinanceBalanceDirectionEnum.CREDIT.getDirection(): FinanceBalanceDirectionEnum.DEBIT.getDirection();amount = amount.abs();previewItem.setDirection(direction);previewItem.setAmount(amount);}FinanceSubjectDO subject = resolveSubject(book, templateItem, env);previewItem.setSubjectCode(subject.getCode());previewItem.setSubjectName(StrUtil.blankToDefault(subject.getFullName(), subject.getName()));Map<String, Object> auxData = resolveAuxData(subject, templateItem, env);previewItem.setAuxData(auxData);
这里需要特别看执行代码:数据对象和页面有skipIfZero,但本次核对的render零值分支没有读取这个开关,实际遇零即跳过。不能把取消勾选描述成已经验证可保留零行。
负金额翻方向是普通模板渲染规则,也不等于红字冲销凭证的生命周期处理,两者不要混用。
舍入不是把最后一行偷偷改平
独立反例:两条借方分别取0.005,逐行舍入后各0.01,合计0.02;贷方取0.010,舍入后0.01,差0.01。
原始数学关系0.005 + 0.005 = 0.010成立,最终分录仍然不平。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
当前核对路径没有“自动补差一分钱”的通用逻辑。若业务需要尾差分配,应预先定义分配对象、顺序与记录依据,再专项实现和验收,不把差额悄悄塞给某个费用科目。
看一张已有结果,确认输出形态
图5|本次只读打开既有2026-08转-0001:借560203差旅费500、贷2241其他应付款500,辅助显示DEPT 103。来源FI504-2026090600008,与图2报销id=3不同,不能拼成同一单据制证成功链。页面的已过账状态属于既有数据,本次没有生成、审核或过账。
这个输出形态有助于核查科目、金额、辅助值和来源备注;“借贷平衡”只回答金额相等,不能证明税额处理、确认时点和重复来源组合都正确。
七、失败与重试:先分清重推事实,还是重新生成
重复提交同一来源版本,会返回已有来源,不会覆盖快照,也不会自动重新执行制证。
submitSource先按账套、来源类型、来源ID和版本查询,命中即返回。这对串行重复提交有明确行为;schema唯一键提供数据库约束基线,但并发冲突的返回语义仍要专项测试。
当前报销和付款入队调用都固定sourceVersion=1。单据内容变更后仍用同一身份重推,不能指望队列自动更新payload。业务版本递增、更正旧凭证和保留快照,应作为变更契约一起设计。
图6|当前主路径时序示意。实线表示调用顺序;本次没有执行这些写操作。autoGenerate=true是另一分支,当前报销和付款调用均传false。
三个开关,不要用一个“自动”概括
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
因此,当前模板也不能统一描述为“只生成暂存凭证”。generateVoucher创建凭证后,模板autoAudit=true时会调用autoAuditAndPost并传入autoPost;关闭相应配置时,结果状态才不同。
本篇只确认触发关系,不展开审核与过账账簿更新,更没有在本次环境点击这些操作。
不同入口的失败记录并不相同
来源服务有事务提交后自动尝试生成的分支。自动路径捕获Exception后调用自代理写失败;财务批量生成则每条通过自代理进入事务,捕获业务异常后记录失败原因。
源码摘录5:FinanceVoucherSourceServiceImpl.java347—364行,连续原文节选;完整路径与文件SHA-256见来源审计。
public FinanceVoucherBatchRespVO batchGenerateVoucher(Collection<Long> ids) {FinanceVoucherBatchRespVO respVO = new FinanceVoucherBatchRespVO();if (CollUtil.isEmpty(ids)) {return respVO;}for (Long id : ids) {try {// 逐条独立事务:一条模板没配好不该让整批生成失败self.generateVoucher(id, null);respVO.addSuccess();} catch (ServiceException ex) {FinanceVoucherSourceDO source = voucherSourceMapper.selectById(id);respVO.addFailure(id, source == null ? null : source.getSourceNo(), ex.getMessage());self.markVoucherFailed(id, ex.getMessage());}}return respVO;}
上面的批量代码只捕获ServiceException,不能扩写成“任何未知异常都记录失败并继续全部任务”。单条Controller直接调用generate,也没有在该入口统一捕获并更新失败原因。
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
自动生成安排在事务提交后,是为了避免生成异常把业务更新一起标记回滚;但它不是持久消息任务。业务提交后进程退出、提交回调中的事务资源行为、失败记录能否持久化,都需要独立集成验证。
本次也没有证明生成入口具备完整的并发抢占、行锁或状态条件更新。不要由来源入队唯一键直接推导出“同一来源并发点生成只会得到一张凭证”。
八、把配置合同变成可执行的验收清单
先交付来源样本与预期分录,再验按钮。 下面表格可直接作为研发、实施和财务共同评审的起点;“待验”不代表已测试通过。
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
本次实际验证了哪些内容
本篇在过程目录内用Java17重新编译当前FinanceExpressionServiceImpl,使用BOM对应的Aviator5.4.3执行金额、字符串科目、部门ID、缺失变量与条件表达式。
共17项检查通过,其中前13项调用原始表达式服务并校验返回或结果;后4项是自建金额舍入与冲抵残留模型。没有启动Spring上下文,没有调用模板render、制证服务、数据库或完整审批链。
源码片段为5段逐字摘录,每段不超过50行;完整路径、定位行号、文件SHA-256和代码核对结果随稿保存。三张真实系统截图分别覆盖业务单据、模板配置和既有分录,均为独立会话只读现成数据。
快速体验与采购核对
本地已启动环境可在财务菜单查看费用报销、凭证模板和凭证详情,先对照本文字段表读取已有记录。模板编辑弹窗能查看配置,正式环境的保存与生成应按企业权限和验收计划执行。
需要从源码启动时,按交付包的初始化与配置说明准备数据库、Redis、账套、科目和期间;后端Java17以yudao-server为单体入口,管理端主应用在ruoyi-office-vben/apps/web-antd,常用pnpm dev:antd。环境依赖和财务模块范围以实际交付为准。
商业源码按实际采购范围交付,公开仓库不代表包含全部财务模块。验收时除“能构建、能登录”,还应包含模板样本、来源契约、科目与辅助配置、错误处理和组合核算结果。
FAQ
1. 报销审批通过,是不是自动生成凭证了?
当前不是。仍需付款的报销先进入待付款分支;无需再付款或累计付款达到应付额时才提交来源,且报销调用autoGenerate=false。入队不等于生成,模板审核过账开关也不会在仅入队时执行。
2. 为什么模板写amount不一定能取到报销金额?
因为变量来自具体来源契约。当前报销表头提供totalAmount,引擎将来源主金额注入sourceAmount,并未自动创建通用amount别名;字段缺失可能返回nil,再被金额渲染当成零。
3. 有发票税额,缺省模板就会自动拆税吗?
不会据此推导。当前缺省报销是总额费用与其他应付款两行。拆税需明确经确认的可抵扣额、费用口径和模板行,本篇1060/1000/60属于独立配置案例。
4. 多条报销明细,会自动各生成一条分录吗?
当前render遍历模板行,没有自动展开lines。报销表头费用科目提示还会取第一条有映射的费用类型。多科目、多部门、多项目要单独设计并验证分组。
5. 修好模板后重推原单据,为什么未必重新生成?
同一来源身份与版本命中时直接返回已有记录,不覆盖payload、不重新触发生成。应区分“重推业务事实”和“对已保存来源重试生成”,改单还涉及版本及旧凭证更正策略。
来源与本次边界
依据当前本地报销、付款、来源服务、模板渲染、表达式服务、凭证组装以及schema基线核对;完整定位与图片记录见资料与配图来源。本篇未引用外部产品图片。
相关文章可继续阅读账套、科目与辅助核算建模、从业务单据到总账的模块划分。本篇聚焦转换契约,后续生命周期另文展开。
保留的限制:报销id=3历史流程不存在;P1学习账2026-08报表差额-500未定性、损益未结转阻断月结,本文未修复或重新验收。既有凭证与该报销不同记录,不能作为同一业务闭环。并发生成、模板变更复现、权限隔离、失败持久化与完整核算仍待专项验证。
本次只交付本地图文和验证材料,没有创建、保存或提交财务单据,没有付款、核销、生成、审核、过账、结转或改库。双端预览通过只表示文章布局与图片加载正常,不代表财务产品验收通过。
你们的自动制证最难落地的是金额口径、明细分组,还是来源变更后的历史解释?

