【凭证】凭证管理
凭证管理模块,由 yudao-module-fms 后端模块的 voucher 包实现,前端实现在 @/views/fms/voucher 目录。
凭证是 FMS 记录经济业务的核心单据。每张凭证由一张主表和多条分录组成,分录记录摘要、会计科目、辅助核算以及借方或贷方金额;账簿、报表和结账都以凭证分录作为期间发生额来源。日常账务围绕「录凭证 → 查凭证 → 审核 → 凭证汇总」形成闭环。
- 录凭证:填写凭证字、凭证号、日期、摘要、科目、辅助核算和借贷金额。
- 查凭证:按期间、凭证字、凭证号、摘要、科目、金额、制单人和审核状态查询,并执行审核、反审核、删除、打印、导入导出等操作。
- 凭证汇总表:按会计期间和科目级次汇总借方、贷方发生额,可打印、导出并跳转到科目明细账。
本文涉及表如下图所示:
# 1. 凭证
凭证,由 FmsVoucherController 提供接口(/fms/voucher)。凭证分录没有独立 Controller,随凭证保存、修改和删除,由 FmsVoucherServiceImpl 在同一事务中差量维护。
# 1.1 主表表结构
省略 creator/create_time/updater/update_time/deleted/tenant_id 等通用字段
CREATE TABLE `fms_voucher` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`account_set_id` bigint DEFAULT NULL COMMENT '账套编号',
`voucher_word_id` bigint DEFAULT NULL COMMENT '凭证字编号',
`reviewer_user_id` bigint DEFAULT NULL COMMENT '审核人后台用户编号',
`voucher_number` int DEFAULT NULL COMMENT '凭证号',
`voucher_time` datetime DEFAULT NULL COMMENT '凭证日期',
`voucher_period` int GENERATED ALWAYS AS (EXTRACT(YEAR_MONTH FROM `voucher_time`)) STORED COMMENT '凭证期间',
`attachment_urls` json DEFAULT NULL COMMENT '附件地址数组',
`attachment_count` int NOT NULL DEFAULT 0 COMMENT '附件数',
`debit_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '借方金额',
`credit_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '贷方金额',
`total` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '合计金额',
`status` int DEFAULT NULL COMMENT '状态',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='FMS 凭证';
① voucher_word_id 关联 fms_voucher_word 表的 id 字段,reviewer_user_id 关联 system_users 表的 id 字段,account_set_id 关联 fms_account_set 表的 id 字段。
② voucher_period 是根据 voucher_time 生成的 yyyyMM 期间字段。凭证号唯一性按「账套 + 凭证字 + 会计月份 + 凭证号」判断,不同月份可以重复使用同一凭证号。
③ attachment_urls 保存附件地址数组(最多 100 个),attachment_count 保存凭证上的附单据张数。附件可以在凭证保存后由 updateVoucherAttachments 独立维护,允许的附件类型由 FMS_VOUCHER_ATTACHMENT_FILE_TYPES 控制。
④ debit_amount、credit_amount、total 由后端根据分录汇总写入,前端不应直接修改主表合计。
⑤ 枚举 status 凭证状态(FmsVoucherStatusEnum)。详见 §1.3 状态流转。
该表包含一个子表:
fms_voucher_entry(凭证分录):在录凭证页面的分录表格中维护,一张凭证至少两条有效分录。
# 1.2 子表结构
CREATE TABLE `fms_voucher_entry` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
`account_set_id` bigint DEFAULT NULL COMMENT '账套编号',
`voucher_id` bigint DEFAULT NULL COMMENT '凭证编号',
`subject_id` bigint DEFAULT NULL COMMENT '科目编号',
`assist_combination_id` bigint DEFAULT NULL COMMENT '辅助核算组合编号',
`digest` varchar(500) DEFAULT NULL COMMENT '摘要内容',
`subject_code` varchar(64) DEFAULT NULL COMMENT '科目编码',
`subject_name` varchar(255) DEFAULT NULL COMMENT '会计科目内容',
`quantity` decimal(18,4) NOT NULL DEFAULT 0.0000 COMMENT '数量',
`unit_price` decimal(18,6) NOT NULL DEFAULT 0.000000 COMMENT '单价',
`debit_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '借方金额',
`credit_amount` decimal(18,2) NOT NULL DEFAULT 0.00 COMMENT '贷方金额',
`auxiliaries` json DEFAULT NULL COMMENT '辅助核算项目数组',
`sort` int DEFAULT NULL COMMENT '显示顺序',
PRIMARY KEY (`id`)
) ENGINE=InnoDB COMMENT='FMS 凭证分录';
① voucher_id 关联 fms_voucher 表的 id 字段,subject_id 关联 fms_subject 表的 id 字段,assist_combination_id 关联 fms_assist_combination 表的 id 字段。
② subject_code、subject_name 是保存时从当前科目复制的快照,digest 是分录摘要。凭证列表直接读取分录展示摘要和科目,不需要逐行再查科目名称。
③ 每条分录只能填写借方或贷方其中一侧,不能同时填写,也不能两边都为空;整张凭证至少两条有效分录,借方合计必须等于贷方合计。
④ 数量核算科目可以填写数量和单价,两者必须同时大于 0,且 数量 × 单价(按金额精度截取)等于该行金额;未启用数量核算的科目不能填写数量或单价。
⑤ 科目配置了辅助核算类别时,分录必须按科目配置顺序完整选择启用中的辅助项目。保存时写入项目名称快照,并由 FmsAuxiliaryCombinationService 复用或创建辅助核算组合。
auxiliaries JSON 字段结构
[
{
"type": 1,
"typeId": 12,
"itemId": 105,
"name": "杭州芋道科技"
}
]
① type 辅助核算类别(FmsAuxiliaryTypeEnum):1 客户、2 供应商、3 职员、4 项目、5 部门、6 存货、7 自定义。
② typeId 指向 fms_auxiliary_type,itemId 指向 fms_auxiliary_item,name 是项目名称快照。项目后续改名或停用,历史分录仍展示当时的名称。
③ 这份数组与 assist_combination_id 描述的是同一组核算维度:数组用于凭证自身展示,组合编号用于核算项目账簿的聚合查询,详见 《【设置】币别、科目、辅助核算、初始余额》。
# 1.3 状态流转
凭证生命周期由 FmsVoucherServiceImpl 控制。状态枚举 FmsVoucherStatusEnum 定义了 9 个值,当前管理后台只使用其中两个:
| 状态值 | 枚举 | 说明 | 可执行操作 |
|---|---|---|---|
0 | PENDING_REVIEW | 待审核(初始状态) | 编辑、审核、删除、维护附件、打印 |
1 | APPROVED | 已审核 | 反审核、打印、查询 |
状态流转说明
新建凭证 ──→ 待审核(0) ──审核──→ 已审核(1)
↑ │
└──── 反审核 ───────┘
- 新建(
createVoucher):Service 强制设置为待审核,创建请求不能自行传入状态。 - 审核 / 反审核(
updateVoucherReviewStatus):待审核与已审核之间双向流转,审核时记录reviewer_user_id。 - 编辑 / 删除(
updateVoucher/deleteVoucherList):已审核凭证必须先反审核才能编辑或删除。删除后会产生凭证断号,可通过整理凭证补齐。 - 期间锁定:凭证所在会计期间已结账时,新增、修改、删除、审核、反审核、移动和整理都会被
validatePeriodOpen拦截。 - 结账生成的凭证:由
validateClosingVoucherEditable保护,在凭证页面只读,不能编辑、审核或删除,只能通过结账模块重新生成,详见 《【结账】期末结账》。
代码中还保留 REJECTED、REVIEWING、WITHDRAWN、UNSUBMITTED、CREATED、DELETED、VOIDED 等枚举值,用于兼容更完整的审批状态模型,当前页面不会产生这些状态。
# 1.4 保存校验
保存、修改和批量操作均先校验账套成员权限,再锁定账套行,串行生成同一账套的凭证号。主要校验如下:
| 校验项 | 规则 | 失败场景 |
|---|---|---|
| 账套 | 当前用户必须拥有账套写权限 | 查看者尝试录入、审核或删除 |
| 会计期间 | 凭证日期所在期间必须开放 | 期间已结账 |
| 凭证字 | 必须属于当前账套 | 使用其他账套凭证字 |
| 凭证号 | 同账套、同凭证字、同月份不能重复,且必须为正整数 | 号段已存在或输入 0 |
| 分录数量 | 至少两条有效分录 | 只填写一行 |
| 科目 | 必须启用且为末级科目 | 选择停用科目或父级科目 |
| 金额 | 单行只能借或贷,整张凭证借贷平衡 | 同行两边都有金额或总额不平 |
| 数量和辅助 | 按科目配置满足数量金额关系和辅助项目完整性 | 缺少数量、单价或辅助项目 |
接口权限由菜单按钮控制,例如 fms:voucher:create、fms:voucher:update、fms:voucher:review、fms:voucher:delete、fms:voucher:print、fms:voucher:import、fms:voucher:export、fms:voucher:move 和 fms:voucher:tidy。
# 2. 录凭证
对应 [财务管理 -> 凭证管理 -> 录凭证] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/fms/voucher/create 目录。
# 2.1 新增
进入页面后,系统加载当前账套的凭证字、科目、辅助类别和当前会计期间,默认选择默认凭证字,并调用 getNextVoucherNumber 取得当前月份下一个凭证号。
表单包括凭证字、凭证号、凭证日期、附单据张数,以及分录表格中的摘要、会计科目、数量 / 单价、借方金额和贷方金额。摘要输入框聚焦时会沿用上一条摘要,也可以点击【摘要库】选择常用摘要;科目选择后,按科目配置显示辅助核算项目。
点击【保存】创建凭证,点击【保存并新增】保存后清空表单继续录入。保存前前端会检查凭证字、凭证号、日期、至少两条有效分录、每行摘要 / 科目 / 金额和借贷平衡,后端仍会再次完整校验。

# 2.2 辅助核算与科目余额
选中的末级科目配置了辅助核算类别时,分录行内会出现对应的项目下拉框。项目变更后,页面读取该辅助组合的余额并显示在分录下方;科目余额则按当前月份加载,方便录入时确认科目当前的借贷方向和金额。

# 2.3 摘要库与凭证模板
【摘要库】由 FmsDigestLibrary.vue 打开,读取当前账套的常用摘要并回填当前分录摘要。
【更多 -> 使用模板】由 FmsVoucherTemplateSelect.vue 打开模板选择;【更多 -> 保存为模板】先复用凭证分录校验,再由 FmsVoucherTemplateSaveForm.vue 保存模板。模板保存同样要求至少两条分录、科目可用、金额平衡、数量与辅助核算合法,详见 《【设置】凭证字、常用摘要、凭证模板》。

# 2.4 审核与反审核
保存后页面带上凭证编号,并显示【审核】【打印】【复制】等操作。
- 审核:待审核凭证点击【审核】,确认后状态变为已审核并记录审核人。
- 反审核:已审核凭证点击【反审核】,状态回到待审核,之后才可以编辑或删除。
- 删除:仅未审核、非结账生成且所在期间开放的凭证可以删除;删除会产生凭证断号,页面会二次确认。
- 复制:点击【复制】以
copyFrom打开新的录入页,只复制分录和基础内容,不复制凭证编号和主键,用户确认后重新保存。 - 上一张 / 下一张:从列表打开详情时会带上当前查询结果的编号列表,可使用页面两侧按钮浏览相邻凭证。

# 2.5 打印

点击【打印】打开 FmsVoucherPrintForm.vue,可设置 A4、B5 或自定义纸张、横向 / 纵向、纸张边距和字体大小,确认后通过隐藏 iframe 打印 HTML。打印设置按账套缓存,缓存键为 fmsVoucherPrintSetting:{accountSetId},不同账套互不影响。打印在浏览器端完成,不新增持久化表。
# 3. 查凭证
对应 [财务管理 -> 凭证管理 -> 查凭证] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/fms/voucher/list 目录。
# 3.1 列表
支持按会计期间、凭证字、凭证号、摘要、科目、金额范围、制单人和审核状态筛选。表格展示日期、凭证字号、附件数量、摘要、科目、借方金额、贷方金额、制单人、审核人和状态。
点击凭证字号进入录入页查看或编辑;查看者和结账生成凭证为只读,主管或会计在权限和状态允许时显示编辑。

# 3.2 附件
点击附件数量打开 FmsVoucherAttachmentForm.vue。待审核凭证且当前用户有写权限时可以上传、删除和保存附件;已审核、结账生成或查看者只显示附件,不允许修改。

# 3.3 批量操作
行操作支持审核、反审核、打印和删除;勾选多张凭证后支持批量审核、批量反审核和批量删除。批量审核会先过滤不符合当前状态的凭证,批量删除包含已审核凭证时前端直接提示,后端仍会校验凭证状态、期间和结账生成标记。
# 3.4 打印与导出
【更多 -> 打印凭证】读取当前选中凭证,未选择时读取当前筛选条件下全部数据,由 FmsVoucherPrintForm.vue 生成打印 HTML。
【更多 -> 打印列表】在新窗口预览当前筛选范围的列表;【更多 -> 导出】下载 凭证列表.xls,选中凭证时导出只包含选中数据。

# 3.5 导入
点击【更多 -> 导入凭证】打开 FmsVoucherImportForm.vue,按「上传文件 → 导入数据 → 导入完成」三步操作:
① 点击【下载《凭证导入模板》】,模板由 getVoucherImportTemplateData 按当前账套的科目和辅助核算生成。
② 按模板填写凭证日期、凭证字、凭证号、摘要、科目、金额、数量和辅助项目,上传一个 Excel 文件。
③ 点击【开始导入】,importVoucher 按日期 + 凭证字 + 凭证号分组,先校验每行科目、金额、辅助核算,再校验同组的分录数量、摘要和借贷平衡。
④ 导入完成后展示总分录数、成功 / 失败分录数、总凭证数和成功 / 失败凭证数;存在错误行时可下载错误数据文件,修正后继续导入。

# 3.6 移动与整理

【更多 -> 移动凭证】打开 FmsVoucherMoveForm.vue,选择期间、凭证字、原凭证号和目标号,把目标凭证移动到目标号之前,并将中间凭证号顺延。目标号必须小于原凭证号,期间必须开放,原凭证必须存在。
【更多 -> 整理凭证】打开 FmsVoucherTidyForm.vue,选择期间、凭证字(为空表示全部凭证字)、起始编号和整理方式。枚举 FmsVoucherTidyTypeEnum:
| 值 | 枚举 | 说明 |
|---|---|---|
1 | FILL_GAPS | 按凭证号顺次前移,补齐断号 |
2 | REORDER_BY_TIME | 按凭证日期重新顺次编号 |
两项操作都先锁定账套,使用临时负号批量更新再写入最终号码,避免唯一键冲突;操作只改变凭证号,不改变分录内容。

# 4. 凭证汇总表
对应 [财务管理 -> 凭证管理 -> 凭证汇总表] 菜单,对应 yudao-ui-admin-vue3 项目的 @/views/fms/voucher/statistics 目录。
# 4.1 查询与汇总
按会计期间范围、凭证字、凭证号范围和科目级次范围查询。getVoucherStatisticsList 先按凭证分录聚合末级科目的直接发生额,再沿科目树向上累加下级科目,返回科目编码、科目名称、级次、借方金额和贷方金额;页面底部按最小科目级次展示合计行。
查询校验包括:开始期间不能晚于结束期间;最小级次不能大于最大级次;最小凭证号不能大于最大凭证号;凭证字必须属于当前账套。

# 4.2 打印、导出与钻取
点击科目编码,在有 fms:ledger:detail:query 权限时跳转到科目明细账,并携带当前科目和期间范围,详见 《【账簿】账簿管理》。
点击【打印】由 FmsPrintPreview.vue 将当前表格渲染为打印预览;点击【导出】下载以期间命名的 Excel 文件。
