feat(invoice):基本完成发票相关数据库操作。

This commit is contained in:
徐涛 2023-06-16 13:49:00 +08:00
parent ce2bb160e1
commit db52a140b1
4 changed files with 591 additions and 0 deletions

View File

@ -1,5 +1,12 @@
package model package model
import (
"electricity_bill_calc/tools"
"electricity_bill_calc/types"
"github.com/shopspring/decimal"
)
type InvoiceTitle struct { type InvoiceTitle struct {
Name string `json:"name"` Name string `json:"name"`
USCI string `json:"usci"` USCI string `json:"usci"`
@ -8,3 +15,31 @@ type InvoiceTitle struct {
Bank string `json:"bank"` Bank string `json:"bank"`
Account string `json:"account"` Account string `json:"account"`
} }
type InvoiceCargo struct {
Name string `json:"name"`
Price decimal.Decimal `json:"price"`
Unit string `json:"unit"`
Quantity decimal.Decimal `json:"quantity"`
TaxRate decimal.Decimal `json:"taxRate"`
Tax decimal.Decimal `json:"tax"`
Total decimal.Decimal `json:"total"`
}
type Invoice struct {
InvoiceNo string `json:"invoiceNo"`
Park string `json:"parkId" db:"park_id"`
Tenement string `json:"tenementId" db:"tenement_id"`
InvoiceType *string `json:"type" db:"type"`
Info InvoiceTitle `json:"invoiceInfo" db:"invoice_info"`
Cargos []InvoiceCargo `json:"cargos"`
TaxRate decimal.Decimal `json:"taxRate" db:"tax_rate"`
TaxMethod int16 `json:"taxMethod" db:"tax_method"`
Total decimal.Decimal `json:"total" db:"total"`
IssuedAt types.DateTime `json:"issuedAt" db:"issued_at"`
Covers []string `json:"covers"`
}
func (i Invoice) Type() string {
return tools.DefaultOrEmptyStr(i.InvoiceType, "")
}

129
model/report.go Normal file
View File

@ -0,0 +1,129 @@
package model
import (
"electricity_bill_calc/types"
"github.com/shopspring/decimal"
)
type ReportIndex struct {
Id string `json:"id"`
Park string `json:"parkId" db:"park_id"`
Period types.DateRange `json:"period"`
Category int16 `json:"category"`
MeterType int16 `json:"meter04kvType" db:"meter_04kv_type"`
PricePolicy int16 `json:"pricePolicy"`
BasisPooled int16 `json:"basisPooled"`
AdjustPooled int16 `json:"adjustPooled"`
LossPooled int16 `json:"lossPooled"`
PublicPooled int16 `json:"publicPooled"`
Published bool `json:"published"`
PublishedAt *types.DateTime `json:"publishedAt" db:"published_at"`
Withdraw int16 `json:"withdraw"`
LastWithdrawAppliedAt *types.DateTime `json:"lastWithdrawAppliedAt" db:"last_withdraw_applied_at"`
LastWithdrawAuditAt *types.DateTime `json:"lastWithdrawAuditAt" db:"last_withdraw_audit_at"`
Status *int16 `json:"status"`
Message *string `json:"message"`
CreatedAt types.DateTime `json:"createdAt" db:"created_at"`
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
}
type ReportSummary struct {
ReportId string `json:"reportId" db:"report_id"`
OverallArea decimal.Decimal `json:"overallArea" db:"overall_area"`
Overall ConsumptionUnit `json:"overall"`
ConsumptionFee decimal.NullDecimal `json:"consumptionFee" db:"consumption_fee"`
Critical ConsumptionUnit `json:"critical"`
Peak ConsumptionUnit `json:"peak"`
Flat ConsumptionUnit `json:"flat"`
Valley ConsumptionUnit `json:"valley"`
Loss decimal.NullDecimal `json:"loss"`
LossFee decimal.NullDecimal `json:"lossFee" db:"loss_fee"`
LossProportion decimal.NullDecimal `json:"lossProportion" db:"loss_proportion"`
AuthorizeLoss *ConsumptionUnit `json:"authorizeLoss" db:"authorize_loss"`
BasicFee decimal.Decimal `json:"basicFee" db:"basic_fee"`
BasicPooledPriceConsumption decimal.NullDecimal `json:"basicPooledPriceConsumption" db:"basic_pooled_price_consumption"`
BasicPooledPriceArea decimal.NullDecimal `json:"basicPooledPriceArea" db:"basic_pooled_price_area"`
AdjustFee decimal.Decimal `json:"adjustFee" db:"adjust_fee"`
AdjustPooledPriceConsumption decimal.NullDecimal `json:"adjustPooledPriceConsumption" db:"adjust_pooled_price_consumption"`
AdjustPooledPriceArea decimal.NullDecimal `json:"adjustPooledPriceArea" db:"adjust_pooled_price_area"`
LossDilutedPrice decimal.NullDecimal `json:"lossDilutedPrice" db:"loss_diluted_price"`
TotalConsumption decimal.Decimal `json:"totalConsumption" db:"total_consumption"`
FinalDilutedOverall decimal.NullDecimal `json:"finalDilutedOverall" db:"final_diluted_overall"`
}
type ReportPublicConsumption struct {
ReportId string `json:"reportId" db:"report_id"`
MeterId string `json:"parkMeterId" db:"park_meter_id"`
Overall ConsumptionUnit `json:"overall"`
Critical ConsumptionUnit `json:"critical"`
Peak ConsumptionUnit `json:"peak"`
Flat ConsumptionUnit `json:"flat"`
Valley ConsumptionUnit `json:"valley"`
LossAdjust ConsumptionUnit `json:"lossAdjust"`
ConsumptionTotal decimal.Decimal `json:"consumptionTotal" db:"consumption_total"`
LossAdjustTotal decimal.Decimal `json:"lossAdjustTotal" db:"loss_adjust_total"`
FinalTotal decimal.Decimal `json:"finalTotal" db:"final_total"`
}
type ReportDetailedPublicConsumption struct {
MeterDetail
ReportPublicConsumption
}
type ReportPooledConsumption struct {
ReportId string `json:"reportId" db:"report_id"`
MeterId string `json:"parkMeterId" db:"park_meter_id"`
Overall ConsumptionUnit `json:"overall"`
Critical ConsumptionUnit `json:"critical"`
Peak ConsumptionUnit `json:"peak"`
Flat ConsumptionUnit `json:"flat"`
Valley ConsumptionUnit `json:"valley"`
PooledArea decimal.Decimal `json:"pooledArea" db:"pooled_area"`
Diluted []NestedMeter `json:"diluted"`
}
type ReportDetailedPooledConsumption struct {
MeterDetail
ReportPooledConsumption
PublicPooled int16 `json:"publicPooled"`
}
type ReportDetailNestedMeterConsumption struct {
Meter MeterDetail `json:"meter"`
Consumption NestedMeter `json:"consumption"`
}
type ReportTenement struct {
ReportId string `json:"reportId" db:"report_id"`
Tenement string `json:"tenementId" db:"tenement_id"`
Detail Tenement `json:"tenementDetail" db:"tenement_detail"`
Period types.DateRange `json:"calcPeriod" db:"calc_period"`
Overall ConsumptionUnit `json:"overall"`
Critical ConsumptionUnit `json:"critical"`
Peak ConsumptionUnit `json:"peak"`
Flat ConsumptionUnit `json:"flat"`
Valley ConsumptionUnit `json:"valley"`
BasicFeePooled decimal.Decimal `json:"basicFeePooled" db:"basic_fee_pooled"`
AdjustFeePooled decimal.Decimal `json:"adjustFeePooled" db:"adjust_fee_pooled"`
LossFeePooled decimal.Decimal `json:"lossFeePooled" db:"loss_fee_pooled"`
FinalPooled decimal.Decimal `json:"finalPooled" db:"final_pooled"`
FinalCharge decimal.Decimal `json:"finalCharge" db:"final_charge"`
Invoice []string `json:"invoice" db:"invoice"`
Meters []NestedMeter `json:"meters" db:"meters"`
Pooled []NestedMeter `json:"pooled" db:"pooled"`
}
type ReportTask struct {
Id string `json:"id"`
LastModifiedAt types.DateTime `json:"lastModifiedAt" db:"last_modified_at"`
Status int16 `json:"status"`
Message *string `json:"message"`
}
type SimplifiedTenementCharge struct {
ReportId string `json:"reportId" db:"report_id"`
Period types.DateRange `json:"period"`
TotalConsumption decimal.Decimal `json:"totalConsumption" db:"total_consumption"`
FinalCharge decimal.Decimal `json:"finalCharge" db:"final_charge"`
}

400
repository/invoice.go Normal file
View File

@ -0,0 +1,400 @@
package repository
import (
"context"
"electricity_bill_calc/cache"
"electricity_bill_calc/config"
"electricity_bill_calc/global"
"electricity_bill_calc/logger"
"electricity_bill_calc/model"
"electricity_bill_calc/types"
"errors"
"fmt"
"strings"
"github.com/doug-martin/goqu/v9"
_ "github.com/doug-martin/goqu/v9/dialect/postgres"
"github.com/georgysavva/scany/v2/pgxscan"
"github.com/jackc/pgx/v5"
"github.com/shopspring/decimal"
"go.uber.org/zap"
)
type _InvoiceRepository struct {
log *zap.Logger
ds goqu.DialectWrapper
}
var InvoiceRepository = _InvoiceRepository{
log: logger.Named("Repository", "Invoice"),
ds: goqu.Dialect("postgres"),
}
// 查询指定园区中符合条件的发票
func (ir _InvoiceRepository) ListInvoice(pid *string, startDate, endDate *types.Date, keyword *string, page uint) ([]*model.Invoice, int64, error) {
ir.log.Info("查询指定园区的发票。", zap.Stringp("Park", pid), logger.DateFieldp("StartDate", startDate), logger.DateFieldp("EndDate", endDate), zap.Stringp("Keyword", keyword), zap.Uint("Page", page))
cacheCondition := []string{
cache.NullableStringKey(pid),
fmt.Sprintf("%d", page),
cache.NullableStringKey(keyword),
cache.NullableConditionKey(startDate),
cache.NullableConditionKey(endDate),
}
if invoices, total, err := cache.RetrievePagedSearch[[]*model.Invoice]("invoice", cacheCondition...); err != nil && invoices != nil && len(*invoices) > 0 {
ir.log.Info("从缓存中获取到了符合条件的发票记录。", zap.Int("Count", len(*invoices)), zap.Int64("Total", total))
return *invoices, total, nil
}
ctx, cancel := global.TimeoutContext()
defer cancel()
invoiceQuery := ir.ds.
From(goqu.T("invoice").As("i")).
Join(goqu.T("tenement").As("t"), goqu.On(goqu.I("i.tenement_id").Eq(goqu.I("t.id")))).
Select("i.*")
countQuery := ir.ds.
From(goqu.T("invoice").As("i")).
Select(goqu.COUNT("*"))
if pid != nil && len(*pid) > 0 {
invoiceQuery = invoiceQuery.Where(goqu.I("t.park_id").Eq(*pid))
countQuery = countQuery.Where(goqu.I("t.park_id").Eq(*pid))
}
if keyword != nil && len(*keyword) > 0 {
pattern := fmt.Sprintf("%%%s%%", *keyword)
invoiceQuery = invoiceQuery.Where(goqu.Or(
goqu.I("i.invoice_no").ILike(pattern),
goqu.I("t.full_name").ILike(pattern),
goqu.I("t.short_name").ILike(pattern),
goqu.I("t.abbr").ILike(pattern),
goqu.I("t.contact_name").ILike(pattern),
goqu.I("t.contact_phone").ILike(pattern),
goqu.L("t.invoice_info->>'usci'").ILike(pattern),
))
countQuery = countQuery.Where(goqu.Or(
goqu.I("i.invoice_no").ILike(pattern),
goqu.I("t.full_name").ILike(pattern),
goqu.I("t.short_name").ILike(pattern),
goqu.I("t.abbr").ILike(pattern),
goqu.I("t.contact_name").ILike(pattern),
goqu.I("t.contact_phone").ILike(pattern),
goqu.L("t.invoice_info->>'usci'").ILike(pattern),
))
}
var queryRange = types.NewEmptyDateTimeRange()
if startDate != nil {
queryRange.SetLower(startDate.ToBeginningOfDate())
}
if endDate != nil {
queryRange.SetUpper(endDate.ToEndingOfDate())
}
invoiceQuery = invoiceQuery.Where(goqu.L("i.issued_at <@ ?", queryRange))
countQuery = countQuery.Where(goqu.L("i.issued_at <@ ?", queryRange))
startRow := (page - 1) * config.ServiceSettings.ItemsPageSize
invoiceQuery = invoiceQuery.
Order(goqu.I("i.issued_at").Desc()).
Offset(startRow).
Limit(config.ServiceSettings.ItemsPageSize)
var (
invoices []*model.Invoice = make([]*model.Invoice, 0)
total int64
)
querySql, queryArgs, _ := invoiceQuery.Prepared(true).ToSQL()
if err := pgxscan.Select(ctx, global.DB, &invoices, querySql, queryArgs...); err != nil {
ir.log.Error("查询发票记录失败。", zap.Error(err))
return invoices, 0, err
}
countSql, countArgs, _ := countQuery.Prepared(true).ToSQL()
if err := pgxscan.Get(ctx, global.DB, &total, countSql, countArgs...); err != nil {
ir.log.Error("查询发票记录数失败。", zap.Error(err))
return invoices, 0, err
}
var relationName string
if pid != nil && len(*pid) > 0 {
relationName = fmt.Sprintf("invoice:%s", *pid)
} else {
relationName = "invoice"
}
cache.CachePagedSearch(invoices, total, []string{relationName}, "invoice", cacheCondition...)
return invoices, total, nil
}
// 查询指定商户未开票的核算记录,改记录将只包括商户整体核算,不包括商户各个表计的详细
func (ir _InvoiceRepository) ListUninvoicedTenementCharges(tid string) ([]*model.SimplifiedTenementCharge, error) {
ir.log.Info("查询指定商户的未开票核算记录", zap.String("Tenement", tid))
cacheConditions := []string{
tid,
}
if records, err := cache.RetrieveSearch[[]*model.SimplifiedTenementCharge]("uninvoiced_tenement_charge", cacheConditions...); err == nil && records != nil && len(*records) > 0 {
ir.log.Info("从缓存中获取到了符合条件的未开票核算记录。", zap.Int("Count", len(*records)))
return *records, nil
}
ctx, cancel := global.TimeoutContext()
defer cancel()
chargeSql, chargeArgs, _ := ir.ds.
From(goqu.T("report_tenement").As("t")).
Join(goqu.T("report").As("r"), goqu.On(goqu.I("t.report_id").Eq(goqu.I("r.id")))).
Select(
goqu.I("t.report_id"),
goqu.I("r.period"),
goqu.L("(t.overall->>'amount')::numeric").As("amount"),
goqu.I("t.final_charge"),
).
Where(
goqu.I("t.tenement_id").Eq(tid),
goqu.I("t.invoice").IsNull(),
).
Prepared(true).ToSQL()
var charges []*model.SimplifiedTenementCharge
if err := pgxscan.Select(ctx, global.DB, &charges, chargeSql, chargeArgs...); err != nil {
ir.log.Error("查询未开票核算记录失败。", zap.Error(err))
return charges, err
}
cache.CacheSearch(charges, []string{fmt.Sprintf("uninvoiced_tenement_charge:%s", tid)}, "uninvoiced_tenement_charge", cacheConditions...)
searchKey := cache.AssembleSearchKey("uninvoiced_tenement_charge", cacheConditions...)
for _, charge := range charges {
cache.CacheRelation(fmt.Sprintf("report:%s", charge.ReportId), cache.STORE_TYPE_KEY, searchKey)
}
return charges, nil
}
// 更新指定核算中指定商户的开票状态以及对应发票号。
// 如果给定了发票号,那么指定记录状态为已开票,如果给定的发票号为`nil`,啊么指定记录为未开票。
func (ir _InvoiceRepository) UpdateTenementInvoicedState(tx pgx.Tx, ctx context.Context, rid, tid string, invoiceNo *string) error {
ir.log.Info("更新指定核算中指定商户的开票状态和记录", zap.String("Report", rid), zap.String("Tenement", tid), zap.Stringp("InvoiceNo", invoiceNo))
updateSql, updateArgs, _ := ir.ds.
Update(goqu.T("report_tenement")).
Set(goqu.Record{
"invoice": invoiceNo,
}).
Where(
goqu.I("report_id").Eq(rid),
goqu.I("tenement_id").Eq(tid),
).
Prepared(true).ToSQL()
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
ir.log.Error("更新核算记录的开票状态失败。", zap.Error(err))
return err
}
return nil
}
// 查询指定发票的详细记录信息
func (ir _InvoiceRepository) GetInvoiceDetail(invoiceNo string) (*model.Invoice, error) {
ir.log.Info("查询指定发票的详细信息", zap.String("InvoiceNo", invoiceNo))
if invoice, err := cache.RetrieveEntity[model.Invoice]("invoice", invoiceNo); err == nil && invoice != nil {
ir.log.Info("从缓存中获取到了符合条件的发票记录。")
return invoice, nil
}
ctx, cancel := global.TimeoutContext()
defer cancel()
invoiceSql, invoiceArgs, _ := ir.ds.
From(goqu.T("invoice")).
Select("*").
Where(goqu.I("invoice_no").Eq(invoiceNo)).
Prepared(true).ToSQL()
var invoice model.Invoice
if err := pgxscan.Get(ctx, global.DB, &invoice, invoiceSql, invoiceArgs...); err != nil {
ir.log.Error("查询发票记录失败。", zap.Error(err))
return nil, err
}
cache.CacheEntity(invoice, []string{fmt.Sprintf("invoice:%s", invoiceNo)}, "invoice", invoiceNo)
return &invoice, nil
}
// 获取指定商户的简化核算记录
func (ir _InvoiceRepository) GetSimplifiedTenementCharges(tid string, rids []string) ([]*model.SimplifiedTenementCharge, error) {
ir.log.Info("查询庄园商户的简化核算记录", zap.String("Tenement", tid), zap.Strings("Reports", rids))
cacheConditions := []string{
tid,
strings.Join(rids, ":"),
}
if records, err := cache.RetrieveSearch[[]*model.SimplifiedTenementCharge]("simplified_tenement_charge", cacheConditions...); err == nil && records != nil && len(*records) > 0 {
ir.log.Info("从缓存中获取到了符合条件的简化核算记录。", zap.Int("Count", len(*records)))
return *records, nil
}
ctx, cancel := global.TimeoutContext()
defer cancel()
chargeSql, chargeArgs, _ := ir.ds.
From(goqu.T("report_tenement").As("t")).
Join(goqu.T("report").As("r"), goqu.On(goqu.I("t.report_id").Eq(goqu.I("r.id")))).
Select(
goqu.I("t.report_id"),
goqu.I("r.period"),
goqu.L("(t.overall->>'amount')::numeric").As("amount"),
goqu.I("t.final_charge"),
).
Where(
goqu.I("t.tenement_id").Eq(tid),
goqu.I("t.report_id").In(rids),
).
Prepared(true).ToSQL()
var charges []*model.SimplifiedTenementCharge
if err := pgxscan.Select(ctx, global.DB, &charges, chargeSql, chargeArgs...); err != nil {
ir.log.Error("查询简化核算记录失败。", zap.Error(err))
return charges, err
}
cache.CacheSearch(charges, []string{fmt.Sprintf("tenement:%s", tid)}, "simplified_tenement_charge", cacheConditions...)
searchKey := cache.AssembleSearchKey("simplified_tenement_charge", cacheConditions...)
for _, charge := range charges {
cache.CacheRelation(fmt.Sprintf("report:%s", charge.ReportId), cache.STORE_TYPE_KEY, searchKey)
}
return charges, nil
}
// 查询发票号码对应的商户 ID
// ! 这个方法不能被加入缓存,这个方法存在的目的就是为了清除缓存。
func (ir _InvoiceRepository) GetInvoiceBelongs(invoiceNo string) ([]string, error) {
ir.log.Info("查询发票号码对应的商户 ID", zap.String("InvoiceNo", invoiceNo))
ctx, cancel := global.TimeoutContext()
defer cancel()
tenementSql, tenementArgs, _ := ir.ds.
From(goqu.T("invoice")).
Select("tenement_id").
Where(goqu.I("i.invoice_no").Eq(invoiceNo)).
Prepared(true).ToSQL()
var tenementIds []string
if err := pgxscan.Select(ctx, global.DB, &tenementIds, tenementSql, tenementArgs...); err != nil {
ir.log.Error("查询发票号码对应的商户 ID 失败。", zap.Error(err))
return tenementIds, err
}
return tenementIds, nil
}
// 删除指定的发票记录
func (ir _InvoiceRepository) Delete(tx pgx.Tx, ctx context.Context, invoiceNo string) error {
ir.log.Info("删除指定的发票记录", zap.String("InvoiceNo", invoiceNo))
deleteSql, deleteArgs, _ := ir.ds.
Delete(goqu.T("invoice")).
Where(goqu.I("invoice_no").Eq(invoiceNo)).
Prepared(true).ToSQL()
if _, err := tx.Exec(ctx, deleteSql, deleteArgs...); err != nil {
ir.log.Error("删除发票记录失败。", zap.Error(err))
return err
}
return nil
}
// 删除指定发票记录与指定核算记录之间的关联
func (ir _InvoiceRepository) DeleteInvoiceTenementRelation(tx pgx.Tx, ctx context.Context, invoiceNo string) error {
ir.log.Info("删除指定发票记录与指定核算记录之间的关联", zap.String("InvoiceNo", invoiceNo))
updateSql, updateArgs, _ := ir.ds.
Update(goqu.T("report_tenement")).
Set(goqu.Record{
"invoice": nil,
}).
Where(goqu.I("invoice").Eq(invoiceNo)).
Prepared(true).ToSQL()
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
ir.log.Error("删除发票记录与核算记录之间的关联失败。", zap.Error(err))
return err
}
return nil
}
// 确认发票的归属
func (ir _InvoiceRepository) IsBelongsTo(invoiceNo, uid string) (bool, error) {
ir.log.Info("确认发票的归属", zap.String("InvoiceNo", invoiceNo), zap.String("User", uid))
ctx, cancel := global.TimeoutContext()
defer cancel()
querySql, queryArgs, _ := ir.ds.
From(goqu.T("invoice").As("i")).
Join(goqu.T("park").As("p"), goqu.On(goqu.I("p.id").Eq(goqu.I("i.park_id")))).
Select(goqu.COUNT("i.*")).
Where(
goqu.I("i.invoice_no").Eq(invoiceNo),
goqu.I("p.user_id").Eq(uid),
).
Prepared(true).ToSQL()
var count int64
if err := pgxscan.Get(ctx, global.DB, &count, querySql, queryArgs...); err != nil {
ir.log.Error("查询发票归属失败", zap.Error(err))
return false, err
}
return count > 0, nil
}
// 创建一条新的发票记录
func (ir _InvoiceRepository) Create(pid, tid, invoiceNo string, invoiceType *string, amount decimal.Decimal, issuedAt types.DateTime, taxMethod int16, taxRate decimal.Decimal, cargos *[]*model.InvoiceCargo, covers *[]string) error {
ir.log.Info("记录一个新的发票", zap.String("Park", pid), zap.String("Tenement", tid), zap.String("Invoice", invoiceNo))
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, err := global.DB.Begin(ctx)
if err != nil {
ir.log.Error("开启事务失败。", zap.Error(err))
return err
}
tenemenetSql, tenementArgs, _ := ir.ds.
From(goqu.T("tenement").As("t")).
LeftJoin(goqu.T("park_building").As("b"), goqu.On(goqu.I("t.building").Eq(goqu.I("b.id")))).
Select(
"t.*", goqu.I("b.name").As("building_name"),
).
Where(goqu.I("t.id").Eq(tid)).
Prepared(true).ToSQL()
var tenement model.Tenement
if err := pgxscan.Get(ctx, global.DB, &tenement, tenemenetSql, tenementArgs...); err != nil {
ir.log.Error("查询商户信息失败。", zap.Error(err))
tx.Rollback(ctx)
return err
}
if tenement.InvoiceInfo == nil {
ir.log.Error("尚未设定商户的发票抬头信息")
tx.Rollback(ctx)
return errors.New("尚未设定商户的发票抬头信息")
}
createSql, createArgs, _ := ir.ds.
Insert(goqu.T("invoice")).
Cols(
"invoice_no", "park_id", "tenement_id", "invoice_type", "amount", "issued_at", "tax_method", "tax_rate", "cargos", "covers",
).
Vals(goqu.Vals{
invoiceNo, pid, tid, invoiceType, amount, issuedAt, taxMethod, taxRate, cargos, covers,
}).
Prepared(true).ToSQL()
if _, err := tx.Exec(ctx, createSql, createArgs...); err != nil {
ir.log.Error("创建发票记录失败。", zap.Error(err))
tx.Rollback(ctx)
return err
}
updateSql, updateArgs, _ := ir.ds.
Update(goqu.T("report_tenement")).
Set(goqu.Record{
"invoice": invoiceNo,
}).
Where(
goqu.I("tenement_id").Eq(tid),
goqu.I("report_id").In(*covers),
).
Prepared(true).ToSQL()
if _, err := tx.Exec(ctx, updateSql, updateArgs...); err != nil {
ir.log.Error("更新核算记录的开票状态失败。", zap.Error(err))
tx.Rollback(ctx)
return err
}
err = tx.Commit(ctx)
if err != nil {
ir.log.Error("提交事务失败。", zap.Error(err))
tx.Rollback(ctx)
return err
}
for _, rid := range *covers {
cache.AbolishRelation(fmt.Sprintf("report:%s", rid))
}
cache.AbolishRelation(fmt.Sprintf("invoice:%s", pid))
cache.AbolishRelation("invoice")
return nil
}

27
vo/invoice.go Normal file
View File

@ -0,0 +1,27 @@
package vo
import (
"electricity_bill_calc/model"
"electricity_bill_calc/types"
"github.com/shopspring/decimal"
)
type InvoiceResponse struct {
No string `json:"no" copier:"InvoiceNo"`
Tenement string `json:"tenement"`
Title model.InvoiceTitle `json:"title" copier:"Info"`
IssuedAt types.DateTime `json:"issuedAt"`
Amount decimal.Decimal `json:"amount" copier:"Total"`
TaxMethod int16 `json:"taxMethod"`
TaxRate decimal.Decimal `json:"taxRate"`
InvoiceType string `json:"invoiceType" copier:"Type"`
}
type InvoiceCreationForm struct {
Park string `json:"park"`
Tenement string `json:"tenement"`
TaxMethod int16 `json:"taxMethod"`
TaxRate decimal.NullDecimal `json:"taxRate"`
Covers []string `json:"covers"`
}