feat: 发票管理

This commit is contained in:
2026-07-27 11:34:31 +08:00
parent 2d8a31a3cc
commit 5bbfba353d
10 changed files with 1081 additions and 0 deletions
@@ -0,0 +1,77 @@
package com.budwk.app.zhgh.pointsmall.invoice.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
@IocBean
@Ok("json:full")
@At("/platform/zhgh/points-mall/invoice")
public class PointsMallInvoiceController {
@Inject
private PointsMallInvoiceService pointsMallInvoiceService;
@At("")
@Ok("beetl:/platform/zhgh/points-mall/invoice/index.html")
@SaCheckPermission("points.mall.invoice")
public void index() {
}
@At
@SaCheckPermission("points.mall.invoice")
public Result pageData(PointsMallInvoiceMainPageParam param) {
return Result.success(pointsMallInvoiceService.mainPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result infoPage(PointsMallInvoiceInfoPageParam param) {
return Result.success(pointsMallInvoiceService.infoPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result infoDetail(@Param("id") String id) {
return Result.success(pointsMallInvoiceService.infoDetail(id));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result orderPage(PointsMallInvoiceOrderPageParam param) {
return Result.success(pointsMallInvoiceService.orderPage(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result orderTotalPoints(PointsMallInvoiceOrderPageParam param) {
return Result.success(pointsMallInvoiceService.orderTotalPoints(param));
}
@At
@SaCheckPermission("points.mall.invoice")
public Result supplierList() {
return Result.success(pointsMallInvoiceService.listSuppliers());
}
@At
@SLog(tag = "积分商城发票管理", msg = "发起开票申请")
@SaCheckPermission("points.mall.invoice")
public Result applyInvoice(PointsMallInvoiceApplyParam param) {
try {
return Result.success(pointsMallInvoiceService.applyInvoice(param));
} catch (IllegalArgumentException e) {
return Result.error(e.getMessage());
}
}
}
@@ -0,0 +1,128 @@
package com.budwk.app.zhgh.pointsmall.invoice.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_invoice_info")
@Comment("积分商城发票信息")
public class PointsMallInvoiceInfo extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("发票主表ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
private String mainId;
@Column
@Comment("发票号码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceId;
@Column
@Comment("发票代码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceCode;
@Column
@Comment("发票日期")
private Date invoiceDate;
@Column
@Comment("发票裸价")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceNakeAmount;
@Column
@Comment("发票税率")
@ColDefine(type = ColType.FLOAT, width = 10, precision = 4)
private BigDecimal invoiceTaxRate;
@Column
@Comment("发票税额")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceTaxAmount;
@Column
@Comment("价税合计")
@ColDefine(type = ColType.FLOAT, width = 18, precision = 2)
private BigDecimal invoiceAmount;
@Column
@Comment("发票类型")
private Integer invoiceType;
@Column
@Comment("电子发票地址")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String url;
@Column
@Comment("发票Base64")
@ColDefine(type = ColType.TEXT)
private String imageEncode;
@Column
@Comment("文件类型")
@ColDefine(type = ColType.VARCHAR, width = 20)
private String fileType;
@Column
@Comment("校验码")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String checkCode;
@Column
@Comment("发票地址")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String invoiceAddress;
@Column
@Comment("开户银行")
@ColDefine(type = ColType.VARCHAR, width = 200)
private String invoiceBank;
@Column
@Comment("银行账号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceAccount;
@Column
@Comment("联系电话")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String invoiceContact;
@Column
@Comment("开票内容")
@ColDefine(type = ColType.VARCHAR, width = 500)
private String invoiceContent;
@Column
@Comment("纳税人识别号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String taxIdNumber;
@Column
@Comment("备注")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String remark;
@Column
@Comment("开票人")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String biller;
}
@@ -0,0 +1,47 @@
package com.budwk.app.zhgh.pointsmall.invoice.models;
import com.budwk.app.base.model.BaseModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.nutz.dao.entity.annotation.*;
import org.nutz.dao.interceptor.annotation.PrevInsert;
import java.io.Serializable;
@Data
@EqualsAndHashCode(callSuper = true)
@Table("points_mall_invoice_main")
@Comment("积分商城发票主表")
public class PointsMallInvoiceMain extends BaseModel implements Serializable {
@Name
@Column
@Comment("ID")
@ColDefine(type = ColType.VARCHAR, width = 32)
@PrevInsert(uu32 = true)
private String id;
@Column
@Comment("结算单号")
@ColDefine(type = ColType.VARCHAR, width = 100)
private String settlementId;
@Column
@Comment("开票结果")
private Integer bSuccess;
@Column
@Comment("可开票订单列表")
@ColDefine(type = ColType.TEXT)
private String sucOrderIds;
@Column
@Comment("无法开票订单列表")
@ColDefine(type = ColType.TEXT)
private String failOrderIds;
@Column
@Comment("开票失败原因")
@ColDefine(type = ColType.VARCHAR, width = 1000)
private String failMsg;
}
@@ -0,0 +1,23 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import lombok.Data;
@Data
public class PointsMallInvoiceApplyParam {
private String supplierId;
private String orderIds;
private String startDate;
private String endDate;
private String invoiceDate;
private String invoiceTitle;
private String invoiceCode;
private String invoiceAddress;
private String invoiceContact;
private Integer invoiceType;
private String invoiceContent;
private String remark;
private String bankAccount;
private String registeredAddress;
private String bankName;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceInfoPageParam extends PageForm {
private String mainId;
private String invoiceId;
private String invoiceCode;
private String invoiceDate;
private Integer invoiceType;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceMainPageParam extends PageForm {
private String settlementId;
private Integer bSuccess;
private String sucOrderIds;
private String failOrderIds;
private String failMsg;
}
@@ -0,0 +1,16 @@
package com.budwk.app.zhgh.pointsmall.invoice.param;
import com.budwk.app.base.param.PageForm;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Data
@EqualsAndHashCode(callSuper = true)
public class PointsMallInvoiceOrderPageParam extends PageForm {
private String supplierId;
private String orderId;
private String startDate;
private String endDate;
private Integer invoiceStatus;
}
@@ -0,0 +1,31 @@
package com.budwk.app.zhgh.pointsmall.invoice.service;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import org.nutz.dao.entity.Record;
import java.math.BigDecimal;
import java.util.List;
public interface PointsMallInvoiceService extends BaseService<PointsMallInvoiceMain> {
Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param);
Pagination<Record> infoPage(PointsMallInvoiceInfoPageParam param);
Record infoDetail(String id);
Pagination<Record> orderPage(PointsMallInvoiceOrderPageParam param);
BigDecimal orderTotalPoints(PointsMallInvoiceOrderPageParam param);
String applyInvoice(PointsMallInvoiceApplyParam param);
List<PointsMallSupplier> listSuppliers();
}
@@ -0,0 +1,218 @@
package com.budwk.app.zhgh.pointsmall.invoice.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceInfo;
import com.budwk.app.zhgh.pointsmall.invoice.models.PointsMallInvoiceMain;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceApplyParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceInfoPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceMainPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.param.PointsMallInvoiceOrderPageParam;
import com.budwk.app.zhgh.pointsmall.invoice.service.PointsMallInvoiceService;
import com.budwk.app.zhgh.pointsmall.order.models.PointsMallOrderSub;
import com.budwk.app.zhgh.pointsmall.supplier.models.PointsMallSupplier;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
import org.nutz.dao.sql.Sql;
import org.nutz.ioc.loader.annotation.IocBean;
import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;
@IocBean(args = {"refer:dao"})
public class PointsMallInvoiceServiceImpl extends BaseServiceImpl<PointsMallInvoiceMain> implements PointsMallInvoiceService {
public PointsMallInvoiceServiceImpl(Dao dao) {
super(dao);
}
@Override
public Pagination<PointsMallInvoiceMain> mainPage(PointsMallInvoiceMainPageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getSettlementId())) {
cnd.and("settlementId", "like", "%" + param.getSettlementId() + "%");
}
if (param.getBSuccess() != null) {
cnd.and("bSuccess", "=", param.getBSuccess());
}
if (StrUtil.isNotBlank(param.getSucOrderIds())) {
cnd.and("sucOrderIds", "like", "%" + param.getSucOrderIds() + "%");
}
if (StrUtil.isNotBlank(param.getFailOrderIds())) {
cnd.and("failOrderIds", "like", "%" + param.getFailOrderIds() + "%");
}
if (StrUtil.isNotBlank(param.getFailMsg())) {
cnd.and("failMsg", "like", "%" + param.getFailMsg() + "%");
}
cnd.desc("createdAt");
return listPage(param.getPageNumber(), param.getPageSize(), PointsMallInvoiceMain.class, cnd);
}
@Override
public Pagination<Record> infoPage(PointsMallInvoiceInfoPageParam param) {
Cnd cnd = Cnd.where("delFlag", "=", false);
if (StrUtil.isNotBlank(param.getMainId())) {
cnd.and("mainId", "=", param.getMainId());
}
if (StrUtil.isNotBlank(param.getInvoiceId())) {
cnd.and("invoiceId", "like", "%" + param.getInvoiceId() + "%");
}
if (StrUtil.isNotBlank(param.getInvoiceCode())) {
cnd.and("invoiceCode", "like", "%" + param.getInvoiceCode() + "%");
}
if (StrUtil.isNotBlank(param.getInvoiceDate())) {
cnd.and("invoiceDate", "=", DateUtil.parse(param.getInvoiceDate()));
}
if (param.getInvoiceType() != null) {
cnd.and("invoiceType", "=", param.getInvoiceType());
}
cnd.desc("createdAt");
Sql sql = Sqls.create("select * from points_mall_invoice_info $condition");
sql.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@Override
public Record infoDetail(String id) {
if (StrUtil.isBlank(id)) {
return null;
}
Sql sql = Sqls.create("select * from points_mall_invoice_info where id=@id and delFlag=0");
sql.params().set("id", id);
return (Record) dao().execute(sql.setCallback(Sqls.callback.record())).getResult();
}
@Override
public Pagination<Record> orderPage(PointsMallInvoiceOrderPageParam param) {
Cnd cnd = invoiceOrderCnd(param, "o");
cnd.desc("o.orderCompleteTime").desc("o.orderCreateTime");
Sql sql = Sqls.create("select o.*, s.supplierName from points_mall_order_sub o left join points_mall_supplier s on o.supplierId = s.supplierId and s.delFlag = 0 $condition");
sql.setCondition(cnd);
return listPageMap(param.getPageNumber(), param.getPageSize(), sql);
}
@Override
public BigDecimal orderTotalPoints(PointsMallInvoiceOrderPageParam param) {
Sql sql = Sqls.create("select ifnull(sum(ifnull(pointsPrice,0) - ifnull(refund,0)), 0) as totalPoints from points_mall_order_sub $condition");
sql.setCondition(invoiceOrderCnd(param, ""));
Record record = (Record) dao().execute(sql.setCallback(Sqls.callback.record())).getResult();
return record == null || record.get("totalPoints") == null ? BigDecimal.ZERO : new BigDecimal(record.get("totalPoints").toString());
}
@Override
public String applyInvoice(PointsMallInvoiceApplyParam param) {
checkApplyParam(param);
PointsMallInvoiceOrderPageParam query = new PointsMallInvoiceOrderPageParam();
query.setSupplierId(param.getSupplierId());
query.setOrderId(param.getOrderIds());
query.setStartDate(param.getStartDate());
query.setEndDate(param.getEndDate());
query.setInvoiceStatus(0);
List<PointsMallOrderSub> orders = dao().query(PointsMallOrderSub.class, invoiceOrderCnd(query, ""));
if (CollUtil.isEmpty(orders)) {
throw new IllegalArgumentException("无符合条件的订单数据");
}
List<String> orderIds = orders.stream().map(PointsMallOrderSub::getOrderId).collect(Collectors.toList());
String settlementId = System.currentTimeMillis() + RandomUtil.randomNumbers(6);
PointsMallInvoiceMain invoice = new PointsMallInvoiceMain();
invoice.setSettlementId(settlementId);
invoice.setBSuccess(0);
invoice.setSucOrderIds(String.join(",", orderIds));
invoice.setFailOrderIds("");
invoice.setFailMsg("");
insert(invoice);
PointsMallInvoiceInfo info = new PointsMallInvoiceInfo();
info.setMainId(invoice.getId());
info.setInvoiceId(settlementId);
info.setInvoiceCode(param.getInvoiceCode());
info.setInvoiceDate(DateUtil.parse(param.getInvoiceDate()));
info.setInvoiceType(param.getInvoiceType());
info.setInvoiceAmount(orderTotalPoints(query));
info.setInvoiceNakeAmount(info.getInvoiceAmount());
info.setInvoiceAddress(param.getInvoiceAddress());
info.setInvoiceContact(param.getInvoiceContact());
info.setInvoiceContent(param.getInvoiceContent());
info.setTaxIdNumber(param.getInvoiceCode());
info.setRemark(param.getRemark());
info.setInvoiceBank(param.getBankName());
info.setInvoiceAccount(param.getBankAccount());
dao().insert(info);
dao().update(PointsMallOrderSub.class, Chain.make("invoiceStatus", 2), Cnd.where("supplierId", "=", param.getSupplierId()).and("orderId", "in", orderIds));
return "开票申请已生成,结算单号:" + settlementId;
}
@Override
public List<PointsMallSupplier> listSuppliers() {
return dao().query(PointsMallSupplier.class, Cnd.where("delFlag", "=", false).asc("sortCode"));
}
private Cnd invoiceOrderCnd(PointsMallInvoiceOrderPageParam param, String alias) {
Cnd cnd = Cnd.where(field(alias, "delFlag"), "=", false)
.and(field(alias, "orderState"), "=", 5)
.and(field(alias, "reconciliationStatus"), "=", 1);
if (StrUtil.isNotBlank(param.getSupplierId())) {
cnd.and(field(alias, "supplierId"), "=", param.getSupplierId());
}
if (param.getInvoiceStatus() != null) {
cnd.and(field(alias, "invoiceStatus"), "=", param.getInvoiceStatus());
}
if (StrUtil.isNotBlank(param.getOrderId())) {
List<String> orderIds = StrUtil.splitTrim(param.getOrderId(), ",");
if (orderIds.size() > 1) {
cnd.and(field(alias, "orderId"), "in", orderIds);
} else {
cnd.and(field(alias, "orderId"), "like", "%" + param.getOrderId() + "%");
}
}
if (StrUtil.isNotBlank(param.getStartDate())) {
cnd.and(field(alias, "orderCompleteTime"), ">=", DateUtil.beginOfDay(DateUtil.parse(param.getStartDate())));
}
if (StrUtil.isNotBlank(param.getEndDate())) {
cnd.and(field(alias, "orderCompleteTime"), "<=", DateUtil.endOfDay(DateUtil.parse(param.getEndDate())));
}
return cnd;
}
private String field(String alias, String field) {
return StrUtil.isBlank(alias) ? field : alias + "." + field;
}
private void checkApplyParam(PointsMallInvoiceApplyParam param) {
if (param == null) {
throw new IllegalArgumentException("开票参数不能为空");
}
if (StrUtil.isBlank(param.getSupplierId())) {
throw new IllegalArgumentException("供应商不能为空");
}
if (StrUtil.isBlank(param.getOrderIds())) {
throw new IllegalArgumentException("订单不能为空");
}
if (StrUtil.isBlank(param.getStartDate()) || StrUtil.isBlank(param.getEndDate())) {
throw new IllegalArgumentException("订单完成日期不能为空");
}
if (StrUtil.isBlank(param.getInvoiceDate())) {
throw new IllegalArgumentException("开票日期不能为空");
}
if (StrUtil.isBlank(param.getInvoiceCode())) {
throw new IllegalArgumentException("纳税人识别号不能为空");
}
if (StrUtil.isBlank(param.getInvoiceAddress())) {
throw new IllegalArgumentException("发票地址不能为空");
}
if (StrUtil.isBlank(param.getInvoiceContact())) {
throw new IllegalArgumentException("联系电话不能为空");
}
}
}
@@ -0,0 +1,509 @@
<!--#
layout("/layouts/platform.html"){
#-->
<div id="app" v-cloak>
<el-card shadow="never">
<search @search="doSearch">
<search-item label="结算单号">
<el-input clearable placeholder="请输入结算单号" v-model="pageForm.settlementId"></el-input>
</search-item>
<search-item label="开票结果">
<el-select clearable placeholder="请选择开票结果" style="width: 100%" v-model="pageForm.bSuccess">
<el-option label="成功" :value="0"></el-option>
<el-option label="失败" :value="1"></el-option>
</el-select>
</search-item>
<search-item label="成功订单">
<el-input clearable placeholder="请输入订单ID" v-model="pageForm.sucOrderIds"></el-input>
</search-item>
<search-item label="失败订单">
<el-input clearable placeholder="请输入订单ID" v-model="pageForm.failOrderIds"></el-input>
</search-item>
</search>
</el-card>
<el-card class="mt10" shadow="never">
<table-tool label="发票管理">
<el-button @click="openApplyDialog" size="mini" type="success">开票申请</el-button>
<el-button @click="openOrderDialog" size="mini" type="primary">可开票订单</el-button>
</table-tool>
<el-table :data="tableData" border class="vi-table" size="small" style="width: 100%" v-loading="tableLoading">
<el-table-column :index="indexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="结算单号" min-width="180" prop="settlementId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="开票结果" min-width="110">
<template slot-scope="{row}">
<el-tag :type="invoiceResultType(row.bSuccess)" size="mini">{{ invoiceResultName(row.bSuccess) }}</el-tag>
</template>
</el-table-column>
<el-table-column align="center" label="成功订单" min-width="220" prop="sucOrderIds" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="失败订单" min-width="220" prop="failOrderIds" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="失败原因" min-width="220" prop="failMsg" show-overflow-tooltip></el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="220">
<template slot-scope="{row}">
<el-button @click="openInfoDialog(row)" size="mini" type="primary">发票明细</el-button>
<el-button @click="openOrderDialog(row.sucOrderIds)" size="mini">成功订单</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="pageForm.pageNumber"
:page-size="pageForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="pageForm.totalCount"
@current-change="pageChange"
@size-change="sizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
</el-card>
<el-dialog :close-on-click-modal="false" title="开票申请" :visible.sync="applyDialogVisible" top="5vh" width="720px">
<el-form :model="applyForm" label-width="120px" size="small">
<el-form-item label="供应商" required>
<el-select clearable filterable placeholder="请选择供应商" style="width: 100%" v-model="applyForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单完成日期" required>
<el-date-picker
end-placeholder="结束日期"
range-separator="至"
start-placeholder="开始日期"
style="width: 100%"
type="daterange"
value-format="yyyy-MM-dd"
v-model="applyDateRange"
></el-date-picker>
</el-form-item>
<el-form-item label="订单ID" required>
<el-input placeholder="多个订单ID用英文逗号分隔" type="textarea" v-model="applyForm.orderIds"></el-input>
</el-form-item>
<el-form-item label="开票日期" required>
<el-date-picker placeholder="请选择开票日期" style="width: 100%" type="date" value-format="yyyy-MM-dd" v-model="applyForm.invoiceDate"></el-date-picker>
</el-form-item>
<el-form-item label="发票抬头">
<el-input clearable placeholder="请输入发票抬头" v-model="applyForm.invoiceTitle"></el-input>
</el-form-item>
<el-form-item label="纳税人识别号" required>
<el-input clearable placeholder="请输入纳税人识别号" v-model="applyForm.invoiceCode"></el-input>
</el-form-item>
<el-form-item label="发票地址" required>
<el-input clearable placeholder="请输入发票地址" v-model="applyForm.invoiceAddress"></el-input>
</el-form-item>
<el-form-item label="联系电话" required>
<el-input clearable placeholder="请输入联系电话" v-model="applyForm.invoiceContact"></el-input>
</el-form-item>
<el-form-item label="发票类型">
<el-select clearable placeholder="请选择发票类型" style="width: 100%" v-model="applyForm.invoiceType">
<el-option label="全电专票" :value="9"></el-option>
<el-option label="全电普票" :value="10"></el-option>
</el-select>
</el-form-item>
<el-form-item label="开票内容">
<el-input clearable placeholder="请输入开票内容" v-model="applyForm.invoiceContent"></el-input>
</el-form-item>
<el-form-item label="开户银行">
<el-input clearable placeholder="请输入开户银行" v-model="applyForm.bankName"></el-input>
</el-form-item>
<el-form-item label="银行账号">
<el-input clearable placeholder="请输入银行账号" v-model="applyForm.bankAccount"></el-input>
</el-form-item>
<el-form-item label="备注">
<el-input placeholder="请输入备注" type="textarea" v-model="applyForm.remark"></el-input>
</el-form-item>
<el-form-item label="订单积分合计">
<span>{{ orderTotalPoints }}</span>
<el-button @click="loadOrderTotalPoints" size="mini" style="margin-left: 12px">计算</el-button>
</el-form-item>
</el-form>
<span slot="footer">
<el-button @click="applyDialogVisible = false">取消</el-button>
<el-button @click="submitApply" :loading="applyLoading" type="primary">提交申请</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="可开票订单" :visible.sync="orderDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="供应商">
<el-select clearable filterable placeholder="供应商" style="width: 180px" v-model="orderForm.supplierId">
<el-option :key="item.supplierId" :label="item.supplierName" :value="item.supplierId" v-for="item in supplierList"></el-option>
</el-select>
</el-form-item>
<el-form-item label="订单ID">
<el-input clearable placeholder="多个订单ID用英文逗号分隔" v-model="orderForm.orderId"></el-input>
</el-form-item>
<el-form-item label="完成日期">
<el-date-picker
end-placeholder="结束日期"
range-separator="至"
start-placeholder="开始日期"
style="width: 260px"
type="daterange"
value-format="yyyy-MM-dd"
v-model="orderDateRange"
></el-date-picker>
</el-form-item>
<el-form-item label="开票状态">
<el-select clearable placeholder="开票状态" style="width: 130px" v-model="orderForm.invoiceStatus">
<el-option label="未开票" :value="0"></el-option>
<el-option label="已开票" :value="1"></el-option>
<el-option label="开票中" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="orderSearch" type="primary">查询</el-button>
<el-button @click="orderReset">重置</el-button>
<el-button @click="useOrdersForApply" type="success">用于开票</el-button>
</el-form-item>
</el-form>
<el-table :data="orderList" @selection-change="orderSelectionChange" border size="small" style="width: 100%" v-loading="orderLoading">
<el-table-column align="center" type="selection" width="50"></el-table-column>
<el-table-column :index="orderIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="订单ID" min-width="170" prop="orderId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="供应商" min-width="150" prop="supplierName" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="用户ID" min-width="120" prop="userId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="完成时间" min-width="160" prop="orderCompleteTime"></el-table-column>
<el-table-column align="center" label="订单总金额" min-width="110" prop="totalPrice"></el-table-column>
<el-table-column align="center" label="积分金额" min-width="110" prop="pointsPrice"></el-table-column>
<el-table-column align="center" label="退款积分" min-width="110" prop="refund"></el-table-column>
<el-table-column align="center" label="开票状态" min-width="100">
<template slot-scope="{row}">
<el-tag :type="invoiceStatusType(row.invoiceStatus)" size="mini">{{ invoiceStatusName(row.invoiceStatus) }}</el-tag>
</template>
</el-table-column>
</el-table>
<el-pagination
:current-page="orderForm.pageNumber"
:page-size="orderForm.pageSize"
:page-sizes="[10, 20, 30, 50]"
:total="orderForm.totalCount"
@current-change="orderPageChange"
@size-change="orderSizeChange"
layout="total, sizes, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="orderDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
<el-dialog :close-on-click-modal="false" title="发票明细" :visible.sync="infoDialogVisible" top="5vh" width="90%">
<el-form :inline="true" size="small" @submit.native.prevent>
<el-form-item label="发票号码">
<el-input clearable placeholder="发票号码" v-model="infoForm.invoiceId"></el-input>
</el-form-item>
<el-form-item label="发票代码">
<el-input clearable placeholder="发票代码" v-model="infoForm.invoiceCode"></el-input>
</el-form-item>
<el-form-item label="发票日期">
<el-date-picker clearable placeholder="发票日期" type="date" value-format="yyyy-MM-dd" v-model="infoForm.invoiceDate"></el-date-picker>
</el-form-item>
<el-form-item>
<el-button @click="infoSearch" type="primary">查询</el-button>
</el-form-item>
</el-form>
<el-table :data="infoList" border size="small" style="width: 100%" v-loading="infoLoading">
<el-table-column :index="infoIndexMethod" align="center" label="序号" type="index" width="70"></el-table-column>
<el-table-column align="center" label="发票号码" min-width="150" prop="invoiceId" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发票代码" min-width="130" prop="invoiceCode" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="发票日期" min-width="120" prop="invoiceDate"></el-table-column>
<el-table-column align="center" label="发票类型" min-width="110">
<template slot-scope="{row}">{{ invoiceTypeName(row.invoiceType) }}</template>
</el-table-column>
<el-table-column align="center" label="价税合计" min-width="110" prop="invoiceAmount"></el-table-column>
<el-table-column align="center" label="税额" min-width="100" prop="invoiceTaxAmount"></el-table-column>
<el-table-column align="center" label="纳税人识别号" min-width="160" prop="taxIdNumber" show-overflow-tooltip></el-table-column>
<el-table-column align="center" label="电子发票地址" min-width="220" prop="url" show-overflow-tooltip></el-table-column>
</el-table>
<el-pagination
:current-page="infoForm.pageNumber"
:page-size="infoForm.pageSize"
:total="infoForm.totalCount"
@current-change="infoPageChange"
layout="total, prev, pager, next"
style="margin-top: 16px; text-align: right"
></el-pagination>
<span slot="footer">
<el-button @click="infoDialogVisible = false">关闭</el-button>
</span>
</el-dialog>
</div>
<script nonce="${cspNonce!}">
new Vue({
el: "#app",
mixins: [initTableMixins],
data() {
return {
supplierList: [],
tableData: [],
tableLoading: false,
pageForm: {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
settlementId: null,
bSuccess: null,
sucOrderIds: null,
failOrderIds: null,
failMsg: null
},
applyDialogVisible: false,
applyLoading: false,
applyDateRange: [],
orderTotalPoints: 0,
applyForm: this.emptyApplyForm(),
orderDialogVisible: false,
orderLoading: false,
orderDateRange: [],
orderList: [],
orderSelections: [],
orderForm: this.emptyOrderForm(),
infoDialogVisible: false,
infoLoading: false,
infoList: [],
infoForm: this.emptyInfoForm()
}
},
methods: {
emptyApplyForm() {
return {
supplierId: null,
orderIds: null,
startDate: null,
endDate: null,
invoiceDate: null,
invoiceTitle: null,
invoiceCode: null,
invoiceAddress: null,
invoiceContact: null,
invoiceType: 10,
invoiceContent: null,
remark: null,
bankAccount: null,
registeredAddress: null,
bankName: null
}
},
emptyOrderForm() {
return {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
supplierId: null,
orderId: null,
startDate: null,
endDate: null,
invoiceStatus: 0
}
},
emptyInfoForm() {
return {
pageNumber: 1,
pageSize: 10,
totalCount: 0,
mainId: null,
invoiceId: null,
invoiceCode: null,
invoiceDate: null,
invoiceType: null
}
},
indexMethod(index) {
return index + 1 + (this.pageForm.pageNumber - 1) * this.pageForm.pageSize
},
orderIndexMethod(index) {
return index + 1 + (this.orderForm.pageNumber - 1) * this.orderForm.pageSize
},
infoIndexMethod(index) {
return index + 1 + (this.infoForm.pageNumber - 1) * this.infoForm.pageSize
},
doSearch() {
this.pageForm.pageNumber = 1
this.pageData()
},
async pageData() {
this.tableLoading = true
const resp = await this.$axios.post(loc() + "/pageData", this.pageForm)
this.tableLoading = false
if (resp.code === 0) {
this.tableData = resp.data.list || []
this.pageForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
pageChange(val) {
this.pageForm.pageNumber = val
this.pageData()
},
sizeChange(val) {
this.pageForm.pageSize = val
this.pageForm.pageNumber = 1
this.pageData()
},
async loadSuppliers() {
const resp = await this.$axios.post(loc() + "/supplierList")
if (resp.code === 0) {
this.supplierList = resp.data || []
}
},
openApplyDialog() {
this.applyForm = this.emptyApplyForm()
this.applyDateRange = []
this.orderTotalPoints = 0
this.applyDialogVisible = true
},
syncApplyDateRange() {
this.applyForm.startDate = this.applyDateRange && this.applyDateRange.length ? this.applyDateRange[0] : null
this.applyForm.endDate = this.applyDateRange && this.applyDateRange.length ? this.applyDateRange[1] : null
},
async loadOrderTotalPoints() {
this.syncApplyDateRange()
if (!this.applyForm.supplierId || !this.applyForm.orderIds) {
this.notifyWarning("请先选择供应商并填写订单ID")
return
}
const resp = await this.$axios.post(loc() + "/orderTotalPoints", {
supplierId: this.applyForm.supplierId,
orderId: this.applyForm.orderIds,
startDate: this.applyForm.startDate,
endDate: this.applyForm.endDate,
invoiceStatus: 0
})
if (resp.code === 0) {
this.orderTotalPoints = resp.data || 0
} else {
this.notifyWarning(resp.msg)
}
},
async submitApply() {
this.syncApplyDateRange()
this.applyLoading = true
const resp = await this.$axios.post(loc() + "/applyInvoice", this.applyForm)
this.applyLoading = false
if (resp.code === 0) {
this.notifySuccess(resp.data || "开票申请成功")
this.applyDialogVisible = false
this.pageData()
} else {
this.notifyWarning(resp.msg)
}
},
openOrderDialog(orderIds) {
this.orderDialogVisible = true
this.orderForm = this.emptyOrderForm()
this.orderDateRange = []
if (typeof orderIds === "string" && orderIds) {
this.orderForm.orderId = orderIds
this.orderForm.invoiceStatus = null
}
this.orderPageData()
},
syncOrderDateRange() {
this.orderForm.startDate = this.orderDateRange && this.orderDateRange.length ? this.orderDateRange[0] : null
this.orderForm.endDate = this.orderDateRange && this.orderDateRange.length ? this.orderDateRange[1] : null
},
orderSearch() {
this.orderForm.pageNumber = 1
this.orderPageData()
},
orderReset() {
this.orderForm = this.emptyOrderForm()
this.orderDateRange = []
this.orderPageData()
},
async orderPageData() {
this.syncOrderDateRange()
this.orderLoading = true
const resp = await this.$axios.post(loc() + "/orderPage", this.orderForm)
this.orderLoading = false
if (resp.code === 0) {
this.orderList = resp.data.list || []
this.orderForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
orderPageChange(val) {
this.orderForm.pageNumber = val
this.orderPageData()
},
orderSizeChange(val) {
this.orderForm.pageSize = val
this.orderForm.pageNumber = 1
this.orderPageData()
},
orderSelectionChange(rows) {
this.orderSelections = rows || []
},
useOrdersForApply() {
if (!this.orderSelections.length) {
this.notifyWarning("请先选择订单")
return
}
const first = this.orderSelections[0]
this.applyForm = this.emptyApplyForm()
this.applyForm.supplierId = first.supplierId
this.applyForm.orderIds = this.orderSelections.map((item) => item.orderId).join(",")
this.applyForm.invoiceDate = new Date().toISOString().slice(0, 10)
this.applyDateRange = [this.orderForm.startDate, this.orderForm.endDate].filter(Boolean)
this.orderDialogVisible = false
this.applyDialogVisible = true
this.loadOrderTotalPoints()
},
openInfoDialog(row) {
this.infoForm = this.emptyInfoForm()
this.infoForm.mainId = row.id
this.infoDialogVisible = true
this.infoPageData()
},
infoSearch() {
this.infoForm.pageNumber = 1
this.infoPageData()
},
async infoPageData() {
this.infoLoading = true
const resp = await this.$axios.post(loc() + "/infoPage", this.infoForm)
this.infoLoading = false
if (resp.code === 0) {
this.infoList = resp.data.list || []
this.infoForm.totalCount = resp.data.totalCount || 0
} else {
this.notifyWarning(resp.msg)
}
},
infoPageChange(val) {
this.infoForm.pageNumber = val
this.infoPageData()
},
invoiceResultName(value) {
return Number(value) === 0 ? "成功" : "失败"
},
invoiceResultType(value) {
return Number(value) === 0 ? "success" : "danger"
},
invoiceStatusName(value) {
const map = { 0: "未开票", 1: "已开票", 2: "开票中" }
return map[value] || "-"
},
invoiceStatusType(value) {
const map = { 0: "info", 1: "success", 2: "warning" }
return map[value] || "info"
},
invoiceTypeName(value) {
const map = { 9: "全电专票", 10: "全电普票" }
return map[value] || "-"
}
},
created() {
this.loadSuppliers()
this.pageData()
}
})
</script>
<!--#
}
#-->