工会报销导出修改
This commit is contained in:
+3
-3
@@ -3,7 +3,7 @@ package com.budwk.app.zhgh.dayofficework.unionReimburse.service;
|
|||||||
import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse;
|
import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/** 工会报销两类审批单共用的导出服务。 */
|
/** 工会报销活动及慰问审批单共用的导出服务。 */
|
||||||
public interface UnionReimburseExportService {
|
public interface UnionReimburseExportService {
|
||||||
/**
|
/**
|
||||||
* @param id 当前登录人已提交的报销单ID,不接受其他人的单据或待提交单据
|
* @param id 当前登录人已提交的报销单ID,不接受其他人的单据或待提交单据
|
||||||
@@ -14,13 +14,13 @@ public interface UnionReimburseExportService {
|
|||||||
/**
|
/**
|
||||||
* 汇总页所有状态的报销单均可打印,必须具有汇总权限并符合所属工会数据范围。
|
* 汇总页所有状态的报销单均可打印,必须具有汇总权限并符合所属工会数据范围。
|
||||||
* @param id 汇总页选中的报销单 ID,可为待提交或其他申请状态
|
* @param id 汇总页选中的报销单 ID,可为待提交或其他申请状态
|
||||||
* @return 与我的报销导出使用相同模板生成的完整 PDF 字节数组,无权限或生成失败时抛出业务异常
|
* @return 按慰问类型选择模板生成的完整 PDF 字节数组,活动沿用原模板;无权限或生成失败时抛出业务异常
|
||||||
*/
|
*/
|
||||||
byte[] exportCollectPdf(String id);
|
byte[] exportCollectPdf(String id);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param form 已加载发票明细的报销单,支付明细为空时兼容旧主表字段
|
* @param form 已加载发票明细的报销单,支付明细为空时兼容旧主表字段
|
||||||
* @return 两份模板通用的平铺字段及 payments、invoices 循环行
|
* @return 活动及慰问模板通用的平铺字段及 payments、invoices 循环行
|
||||||
*/
|
*/
|
||||||
Map<String, Object> buildExportData(UnionReimburse form);
|
Map<String, Object> buildExportData(UnionReimburse form);
|
||||||
}
|
}
|
||||||
|
|||||||
+35
-5
@@ -16,6 +16,7 @@ import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburse;
|
|||||||
import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburseInvoiceDetail;
|
import com.budwk.app.zhgh.dayofficework.unionReimburse.model.UnionReimburseInvoiceDetail;
|
||||||
import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseExportService;
|
import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseExportService;
|
||||||
import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService;
|
import com.budwk.app.zhgh.dayofficework.unionReimburse.service.UnionReimburseService;
|
||||||
|
import com.budwk.app.zhgh.staffbenefit.condolence.model.CondolenceType;
|
||||||
import com.deepoove.poi.XWPFTemplate;
|
import com.deepoove.poi.XWPFTemplate;
|
||||||
import com.deepoove.poi.config.Configure;
|
import com.deepoove.poi.config.Configure;
|
||||||
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
|
||||||
@@ -49,6 +50,13 @@ import java.util.*;
|
|||||||
public class UnionReimburseExportServiceImpl implements UnionReimburseExportService {
|
public class UnionReimburseExportServiceImpl implements UnionReimburseExportService {
|
||||||
public static final String ACTIVITY_TEMPLATE = "union_reimburse_activity_pdf";
|
public static final String ACTIVITY_TEMPLATE = "union_reimburse_activity_pdf";
|
||||||
public static final String CONDOLENCE_TEMPLATE = "union_reimburse_condolence_pdf";
|
public static final String CONDOLENCE_TEMPLATE = "union_reimburse_condolence_pdf";
|
||||||
|
// 使用慰问类型业务编码匹配模板,避免类型名称调整后选择错误;编码与现有申请页一致。
|
||||||
|
private static final Map<String, String> COLLECT_CONDOLENCE_TEMPLATES = Map.of(
|
||||||
|
"A", "union_reimburse_condolence_marriage_pdf",
|
||||||
|
"D", "union_reimburse_condolence_birth_pdf",
|
||||||
|
"E", "union_reimburse_condolence_hospital_pdf",
|
||||||
|
"G", "union_reimburse_condolence_member_death_pdf",
|
||||||
|
"H", "union_reimburse_condolence_relative_death_pdf");
|
||||||
@Inject
|
@Inject
|
||||||
private UnionReimburseService unionReimburseService;
|
private UnionReimburseService unionReimburseService;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -62,7 +70,7 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
|||||||
return renderDocument(loadExportForm(id, false, false), true, false);
|
return renderDocument(loadExportForm(id, false, false), true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 汇总打印支持所有状态,并严格按汇总权限和所属工会范围读取,复用我的报销 PDF 模板。 */
|
/** 汇总打印支持所有状态,按汇总权限和所属工会范围读取,并按慰问类型选择模板。 */
|
||||||
@Override
|
@Override
|
||||||
public byte[] exportCollectPdf(String id) {
|
public byte[] exportCollectPdf(String id) {
|
||||||
return renderDocument(loadExportForm(id, true, true), true, true);
|
return renderDocument(loadExportForm(id, true, true), true, true);
|
||||||
@@ -103,10 +111,32 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
|||||||
return form;
|
return form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 两份模板共用字段和循环行配置;collectPrint 仅为汇总打印补充编号二维码。 */
|
/**
|
||||||
|
* 汇总页五类慰问使用独立模板,其余类型和我的报销沿用原模板。
|
||||||
|
* @param form 已通过数据权限校验的报销单,使用慰问类型 ID 读取业务编码
|
||||||
|
* @param collectPrint 是否为汇总页打印,只有该入口启用分类模板
|
||||||
|
* @return 系统导出模板管理中的模板编码;类型关联缺失时提示核对,不猜测类型名称
|
||||||
|
*/
|
||||||
|
private String resolveTemplateCode(UnionReimburse form, boolean collectPrint) {
|
||||||
|
if (!"UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject())) {
|
||||||
|
return ACTIVITY_TEMPLATE;
|
||||||
|
}
|
||||||
|
if (!collectPrint) {
|
||||||
|
return CONDOLENCE_TEMPLATE;
|
||||||
|
}
|
||||||
|
// 兼容旧申请将类型 ID 保存在 condolenceType 的情况,不回写历史数据。
|
||||||
|
String typeId = StrUtil.blankToDefault(form.getCondolenceTypeId(), form.getCondolenceType());
|
||||||
|
CondolenceType type = StrUtil.isBlank(typeId) ? null : dao.fetch(CondolenceType.class, typeId);
|
||||||
|
if (type == null || StrUtil.isBlank(type.getCode())) {
|
||||||
|
throw new BaseException("慰问类型不存在或编码为空,请核对后打印");
|
||||||
|
}
|
||||||
|
// 五类之外的既有慰问继续使用通用模板;已匹配的专用模板缺失时由模板加载器明确报错。
|
||||||
|
return COLLECT_CONDOLENCE_TEMPLATES.getOrDefault(type.getCode(), CONDOLENCE_TEMPLATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 模板共用字段和循环行配置;汇总打印按慰问类型选模板并补充编号二维码。 */
|
||||||
private byte[] renderDocument(UnionReimburse form, boolean asPdf, boolean collectPrint) {
|
private byte[] renderDocument(UnionReimburse form, boolean asPdf, boolean collectPrint) {
|
||||||
String templateCode = "UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject())
|
String templateCode = resolveTemplateCode(form, collectPrint);
|
||||||
? CONDOLENCE_TEMPLATE : ACTIVITY_TEMPLATE;
|
|
||||||
Path docx = null;
|
Path docx = null;
|
||||||
Path pdf = null;
|
Path pdf = null;
|
||||||
try {
|
try {
|
||||||
@@ -114,7 +144,7 @@ public class UnionReimburseExportServiceImpl implements UnionReimburseExportServ
|
|||||||
if (asPdf) pdf = Files.createTempFile("union_reimburse_", ".pdf");
|
if (asPdf) pdf = Files.createTempFile("union_reimburse_", ".pdf");
|
||||||
// 活动和慰问模板的支付标记均位于表头,支付明细统一循环下一行。
|
// 活动和慰问模板的支付标记均位于表头,支付明细统一循环下一行。
|
||||||
// 活动模板的发票标记也位于表头,循环下一行;慰问保留原有的当前行配置。
|
// 活动模板的发票标记也位于表头,循环下一行;慰问保留原有的当前行配置。
|
||||||
boolean invoiceOnSameRow = CONDOLENCE_TEMPLATE.equals(templateCode);
|
boolean invoiceOnSameRow = "UNION_REIMBURSE_PROJECT_1".equals(form.getReimburseProject());
|
||||||
Configure config = Configure.builder()
|
Configure config = Configure.builder()
|
||||||
.bind("payments", new LoopRowTableRenderPolicy(false))
|
.bind("payments", new LoopRowTableRenderPolicy(false))
|
||||||
.bind("invoices", new LoopRowTableRenderPolicy(invoiceOnSameRow)).build();
|
.bind("invoices", new LoopRowTableRenderPolicy(invoiceOnSameRow)).build();
|
||||||
|
|||||||
+18
-6
@@ -125,7 +125,7 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
/**
|
/**
|
||||||
* 保存、提交及重新提交共用支付信息校验,并在同一事务内保存申请和发票明细。
|
* 保存、提交及重新提交共用支付信息校验,并在同一事务内保存申请和发票明细。
|
||||||
* @param unionReimburse 申请表单,paymentDetails 为支付方式、金额及对私收款资料组成的明细列表
|
* @param unionReimburse 申请表单,paymentDetails 为支付方式、金额及对私收款资料组成的明细列表
|
||||||
* @param stateId 1 表示保存草稿,2 表示提交;均要求支付信息完整、发票必填且通过服务端验真
|
* @param stateId 1 表示保存草稿,2 表示提交;均要求支付信息完整,发票选填,上传后须通过服务端验真
|
||||||
* @return Result 成功或失败结果,msg 包含失败明细的行号及原因
|
* @return Result 成功或失败结果,msg 包含失败明细的行号及原因
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@@ -134,7 +134,7 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
if (unionReimburse == null) {
|
if (unionReimburse == null) {
|
||||||
return Result.error("报销数据不能为空");
|
return Result.error("报销数据不能为空");
|
||||||
}
|
}
|
||||||
// 保存、提交及重提统一要求发票完整,不能通过慰问类型或草稿状态跳过。
|
// 发票选填;保存、提交及重提均对已上传的发票检查完整性。
|
||||||
Result invoiceResult = validateInvoiceDetails(unionReimburse);
|
Result invoiceResult = validateInvoiceDetails(unionReimburse);
|
||||||
if (invoiceResult != null) {
|
if (invoiceResult != null) {
|
||||||
return invoiceResult;
|
return invoiceResult;
|
||||||
@@ -669,7 +669,7 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有报销类型由必填发票明细自动汇总金额和张数。
|
* 有发票时汇总票面金额和张数;无发票时按已校验的支付明细汇总金额,张数为零。
|
||||||
*/
|
*/
|
||||||
private void fillInvoiceSummary(UnionReimburse unionReimburse) {
|
private void fillInvoiceSummary(UnionReimburse unionReimburse) {
|
||||||
double totalMoney = 0D;
|
double totalMoney = 0D;
|
||||||
@@ -684,6 +684,14 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
invoiceFileCount++;
|
invoiceFileCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 无发票申请从服务端已校验的支付明细计算金额,不能信任前端传入的报销总额。
|
||||||
|
if (Lang.isEmpty(unionReimburse.getInvoiceDetails())) {
|
||||||
|
BigDecimal paymentTotal = BigDecimal.ZERO;
|
||||||
|
for (cn.hutool.json.JSONObject detail : unionReimburse.getPaymentDetails()) {
|
||||||
|
paymentTotal = paymentTotal.add(new BigDecimal(detail.get("money").toString()));
|
||||||
|
}
|
||||||
|
totalMoney = paymentTotal.setScale(2, RoundingMode.HALF_UP).doubleValue();
|
||||||
|
}
|
||||||
unionReimburse.setMoney(BigDecimal.valueOf(totalMoney).setScale(2, RoundingMode.HALF_UP).doubleValue());
|
unionReimburse.setMoney(BigDecimal.valueOf(totalMoney).setScale(2, RoundingMode.HALF_UP).doubleValue());
|
||||||
unionReimburse.setInvoiceNumber(String.valueOf(invoiceFileCount));
|
unionReimburse.setInvoiceNumber(String.valueOf(invoiceFileCount));
|
||||||
}
|
}
|
||||||
@@ -754,6 +762,10 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
*/
|
*/
|
||||||
private Result validatePaymentAmountLimit(UnionReimburse form) {
|
private Result validatePaymentAmountLimit(UnionReimburse form) {
|
||||||
List<UnionReimburseInvoiceDetail> invoices = form.getInvoiceDetails();
|
List<UnionReimburseInvoiceDetail> invoices = form.getInvoiceDetails();
|
||||||
|
// 无发票时不设票面金额上限,支付明细仍须通过必填及正金额校验。
|
||||||
|
if (Lang.isEmpty(invoices)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
BigDecimal invoiceTotal = BigDecimal.ZERO.setScale(2);
|
BigDecimal invoiceTotal = BigDecimal.ZERO.setScale(2);
|
||||||
if (invoices != null) {
|
if (invoices != null) {
|
||||||
for (int i = 0; i < invoices.size(); i++) {
|
for (int i = 0; i < invoices.size(); i++) {
|
||||||
@@ -821,11 +833,11 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 保存及提交前检查发票完整性,每条明细必须且只能包含一张可定位的发票文件。
|
* 发票列表允许为空;已有明细必须且只能包含一张可定位的发票文件。
|
||||||
*/
|
*/
|
||||||
private Result validateInvoiceDetails(UnionReimburse unionReimburse) {
|
private Result validateInvoiceDetails(UnionReimburse unionReimburse) {
|
||||||
if (Lang.isEmpty(unionReimburse.getInvoiceDetails())) {
|
if (Lang.isEmpty(unionReimburse.getInvoiceDetails())) {
|
||||||
return Result.error("请至少维护一条发票明细");
|
return null;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < unionReimburse.getInvoiceDetails().size(); i++) {
|
for (int i = 0; i < unionReimburse.getInvoiceDetails().size(); i++) {
|
||||||
UnionReimburseInvoiceDetail detail = unionReimburse.getInvoiceDetails().get(i);
|
UnionReimburseInvoiceDetail detail = unionReimburse.getInvoiceDetails().get(i);
|
||||||
@@ -1596,7 +1608,7 @@ public class UnionReimburseServiceImpl extends BaseServiceImpl<UnionReimburse> i
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 区分活动类与慰问类的基本信息校验;所有类型的发票均为必填。
|
* 区分活动类与慰问类的基本信息校验;所有类型的发票均为选填。
|
||||||
*/
|
*/
|
||||||
private boolean needInvoiceDetails(UnionReimburse unionReimburse) {
|
private boolean needInvoiceDetails(UnionReimburse unionReimburse) {
|
||||||
return unionReimburse != null && !"UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject());
|
return unionReimburse != null && !"UNION_REIMBURSE_PROJECT_1".equals(unionReimburse.getReimburseProject());
|
||||||
|
|||||||
+35
-11
@@ -438,7 +438,7 @@ layout("/layouts/platform.html"){
|
|||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
<el-form-item label="报销金额" prop="money">
|
<el-form-item label="报销金额" prop="money">
|
||||||
<div class="invoice-field-title">报销金额</div>
|
<div class="invoice-field-title">报销金额</div>
|
||||||
<el-input v-model="formData.money" placeholder="自动汇总发票金额" readonly></el-input>
|
<el-input v-model="formData.money" placeholder="有发票按发票汇总,无发票按支付信息汇总" readonly></el-input>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="12">
|
<el-col :span="12">
|
||||||
@@ -587,11 +587,11 @@ layout("/layouts/platform.html"){
|
|||||||
<div>
|
<div>
|
||||||
<div class="invoice-toolbar-title">发票明细列表</div>
|
<div class="invoice-toolbar-title">发票明细列表</div>
|
||||||
<div class="invoice-summary-tip">
|
<div class="invoice-summary-tip">
|
||||||
所有报销类型均须上传发票并通过验真;每条明细对应一张发票图片或单页 PDF,金额和张数自动汇总。
|
发票选填;无发票时按支付信息汇总报销金额。上传后须通过验真,每条明细对应一张发票图片或单页 PDF。
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<el-tag type="info">发票必须验真</el-tag>
|
<el-tag type="info">发票选填,上传须验真</el-tag>
|
||||||
<el-button @click="openInvoiceDialog('add')" plain type="primary"
|
<el-button @click="openInvoiceDialog('add')" plain type="primary"
|
||||||
style="margin-left: 12px">新增发票
|
style="margin-left: 12px">新增发票
|
||||||
</el-button>
|
</el-button>
|
||||||
@@ -943,6 +943,15 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
// 无发票时支付明细决定报销金额,支付行增删或金额变化后同步更新汇总。
|
||||||
|
"formData.paymentDetails": {
|
||||||
|
handler() {
|
||||||
|
if (!(this.formData.invoiceDetails || []).length) {
|
||||||
|
this.rebuildInvoiceSummary()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
"detailForm.invoiceFiles": {
|
"detailForm.invoiceFiles": {
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
const file = newVal && newVal.length ? newVal[0] : null
|
const file = newVal && newVal.length ? newVal[0] : null
|
||||||
@@ -1221,7 +1230,14 @@ layout("/layouts/platform.html"){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.$set(row, "historyData", accounts)
|
this.$set(row, "historyData", accounts)
|
||||||
this.$set(row, "bankAccountId", selectedId)
|
// 新建户名的选项必须先完成渲染,再更新选中值,避免 el-select 将临时账户ID直接显示为户名。
|
||||||
|
this.$nextTick(() => {
|
||||||
|
// 重复输入、账户请求回填或切换付款人会替换选项列表;旧回调不得覆盖最新选择。
|
||||||
|
if (row.historyData !== accounts) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.$set(row, "bankAccountId", selectedId)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
// 选中框只显示户名,下拉内容展示开户行和尾号,同名多卡仍按账户ID选择。
|
// 选中框只显示户名,下拉内容展示开户行和尾号,同名多卡仍按账户ID选择。
|
||||||
bankAccountLabel(account) {
|
bankAccountLabel(account) {
|
||||||
@@ -1545,17 +1561,24 @@ layout("/layouts/platform.html"){
|
|||||||
fileCount = fileCount + 1
|
fileCount = fileCount + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 所有类型都按必填发票汇总,不再使用无票慰问金额兜底。
|
// 无发票时按支付明细汇总,使用整数分避免小数累加误差。
|
||||||
|
if (!invoiceDetails.length) {
|
||||||
|
let paymentCents = 0
|
||||||
|
const paymentDetails = this.formData.paymentDetails || []
|
||||||
|
paymentDetails.forEach(item => {
|
||||||
|
const cents = this.moneyToCents(item && item.money)
|
||||||
|
if (cents !== null) {
|
||||||
|
paymentCents += cents
|
||||||
|
}
|
||||||
|
})
|
||||||
|
totalMoney = paymentCents / 100
|
||||||
|
}
|
||||||
this.$set(this.formData, "money", totalMoney ? totalMoney.toFixed(2) : "0.00")
|
this.$set(this.formData, "money", totalMoney ? totalMoney.toFixed(2) : "0.00")
|
||||||
this.$set(this.formData, "invoiceNumber", String(fileCount))
|
this.$set(this.formData, "invoiceNumber", String(fileCount))
|
||||||
},
|
},
|
||||||
validateInvoiceDetailsBeforeSubmit() {
|
validateInvoiceDetailsBeforeSubmit() {
|
||||||
const invoiceDetails = this.formData.invoiceDetails || []
|
const invoiceDetails = this.formData.invoiceDetails || []
|
||||||
// 保存、提交及重提均要求至少一张发票,包括慰问类申请。
|
// 发票可不上传;已经添加的发票仍须填写完整并沿用验真流程。
|
||||||
if (!invoiceDetails.length) {
|
|
||||||
this.$message.warning("请至少维护一条发票明细")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for (let i = 0; i < invoiceDetails.length; i++) {
|
for (let i = 0; i < invoiceDetails.length; i++) {
|
||||||
const currentDetail = invoiceDetails[i]
|
const currentDetail = invoiceDetails[i]
|
||||||
if (!currentDetail.invoiceFiles || currentDetail.invoiceFiles.length === 0) {
|
if (!currentDetail.invoiceFiles || currentDetail.invoiceFiles.length === 0) {
|
||||||
@@ -1675,7 +1698,8 @@ layout("/layouts/platform.html"){
|
|||||||
if (!Number.isSafeInteger(invoiceCents) || !Number.isSafeInteger(paymentCents)) {
|
if (!Number.isSafeInteger(invoiceCents) || !Number.isSafeInteger(paymentCents)) {
|
||||||
return "金额合计超出可处理范围,请检查填写金额"
|
return "金额合计超出可处理范围,请检查填写金额"
|
||||||
}
|
}
|
||||||
if (paymentCents > invoiceCents) {
|
// 无发票时只校验支付金额本身;有发票才限制支付合计不得超过票面总额。
|
||||||
|
if (invoices.length && paymentCents > invoiceCents) {
|
||||||
return "支付金额合计不能超过发票总额(支付合计:" + (paymentCents / 100).toFixed(2)
|
return "支付金额合计不能超过发票总额(支付合计:" + (paymentCents / 100).toFixed(2)
|
||||||
+ "元,发票总额:" + (invoiceCents / 100).toFixed(2) + "元)"
|
+ "元,发票总额:" + (invoiceCents / 100).toFixed(2) + "元)"
|
||||||
}
|
}
|
||||||
|
|||||||
+27
-9
@@ -761,10 +761,11 @@ layout("/layouts/platform_h5.html"){
|
|||||||
</van-action-sheet>
|
</van-action-sheet>
|
||||||
</van-cell-group>
|
</van-cell-group>
|
||||||
<van-cell-group class="form-section" v-if="formData.reimburseProject">
|
<van-cell-group class="form-section" v-if="formData.reimburseProject">
|
||||||
<div class="form-section__title">电子发票</div>
|
<div class="form-section__title">电子发票(选填)</div>
|
||||||
|
<div class="invoice-tip">无发票时按支付信息汇总报销金额;上传的发票仍须通过验真。</div>
|
||||||
<van-field label="报销金额"
|
<van-field label="报销金额"
|
||||||
v-model="formData.money"
|
v-model="formData.money"
|
||||||
placeholder="自动汇总发票金额"
|
placeholder="有发票按发票汇总,无发票按支付信息汇总"
|
||||||
readonly></van-field>
|
readonly></van-field>
|
||||||
<van-field label="发票张数"
|
<van-field label="发票张数"
|
||||||
v-model="formData.invoiceNumber"
|
v-model="formData.invoiceNumber"
|
||||||
@@ -1167,6 +1168,15 @@ layout("/layouts/platform_h5.html"){
|
|||||||
// 监听报销类别发生变化,报销项目联动改变
|
// 监听报销类别发生变化,报销项目联动改变
|
||||||
// 修复 watch 中的监听器
|
// 修复 watch 中的监听器
|
||||||
watch: {
|
watch: {
|
||||||
|
// 无发票时支付明细决定报销金额,支付行增删或金额变化后同步更新汇总。
|
||||||
|
"formData.paymentDetails": {
|
||||||
|
handler() {
|
||||||
|
if (!(this.formData.invoiceDetails || []).length) {
|
||||||
|
this.rebuildInvoiceSummary()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true
|
||||||
|
},
|
||||||
"detailForm.invoiceFiles": {
|
"detailForm.invoiceFiles": {
|
||||||
handler(newVal) {
|
handler(newVal) {
|
||||||
const file = newVal && newVal.length ? newVal[0] : null
|
const file = newVal && newVal.length ? newVal[0] : null
|
||||||
@@ -1854,17 +1864,24 @@ layout("/layouts/platform_h5.html"){
|
|||||||
fileCount = fileCount + 1
|
fileCount = fileCount + 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 所有类型都按必填发票汇总,不再使用无票慰问金额兜底。
|
// 无发票时按支付明细汇总,使用整数分避免小数累加误差。
|
||||||
|
if (!invoiceDetails.length) {
|
||||||
|
let paymentCents = 0
|
||||||
|
const paymentDetails = this.formData.paymentDetails || []
|
||||||
|
paymentDetails.forEach(item => {
|
||||||
|
const cents = this.moneyToCents(item && item.money)
|
||||||
|
if (cents !== null) {
|
||||||
|
paymentCents += cents
|
||||||
|
}
|
||||||
|
})
|
||||||
|
totalMoney = paymentCents / 100
|
||||||
|
}
|
||||||
this.$set(this.formData, "money", totalMoney ? totalMoney.toFixed(2) : "0.00")
|
this.$set(this.formData, "money", totalMoney ? totalMoney.toFixed(2) : "0.00")
|
||||||
this.$set(this.formData, "invoiceNumber", String(fileCount))
|
this.$set(this.formData, "invoiceNumber", String(fileCount))
|
||||||
},
|
},
|
||||||
validateInvoiceDetailsBeforeSubmit() {
|
validateInvoiceDetailsBeforeSubmit() {
|
||||||
const invoiceDetails = this.formData.invoiceDetails || []
|
const invoiceDetails = this.formData.invoiceDetails || []
|
||||||
// 与 PC 一致:草稿、提交和重新提交均要求所有类型提供发票。
|
// 发票可不上传;已经添加的发票仍须填写完整并沿用验真流程。
|
||||||
if (!invoiceDetails.length) {
|
|
||||||
this.$toast.fail("请至少维护一条发票明细")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for (let i = 0; i < invoiceDetails.length; i++) {
|
for (let i = 0; i < invoiceDetails.length; i++) {
|
||||||
const currentDetail = invoiceDetails[i]
|
const currentDetail = invoiceDetails[i]
|
||||||
if (!currentDetail.invoiceFiles || currentDetail.invoiceFiles.length === 0) {
|
if (!currentDetail.invoiceFiles || currentDetail.invoiceFiles.length === 0) {
|
||||||
@@ -2167,7 +2184,8 @@ layout("/layouts/platform_h5.html"){
|
|||||||
if (!Number.isSafeInteger(invoiceCents) || !Number.isSafeInteger(paymentCents)) {
|
if (!Number.isSafeInteger(invoiceCents) || !Number.isSafeInteger(paymentCents)) {
|
||||||
return "金额合计超出可处理范围,请检查填写金额"
|
return "金额合计超出可处理范围,请检查填写金额"
|
||||||
}
|
}
|
||||||
if (paymentCents > invoiceCents) {
|
// 无发票时只校验支付金额本身;有发票才限制支付合计不得超过票面总额。
|
||||||
|
if (invoices.length && paymentCents > invoiceCents) {
|
||||||
return "支付金额合计不能超过发票总额(支付合计:" + (paymentCents / 100).toFixed(2)
|
return "支付金额合计不能超过发票总额(支付合计:" + (paymentCents / 100).toFixed(2)
|
||||||
+ "元,发票总额:" + (invoiceCents / 100).toFixed(2) + "元)"
|
+ "元,发票总额:" + (invoiceCents / 100).toFixed(2) + "元)"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user