feat(invoice):基本完成发票相关数据库操作。
This commit is contained in:
parent
ce2bb160e1
commit
db52a140b1
|
@ -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
129
model/report.go
Normal 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
400
repository/invoice.go
Normal 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
27
vo/invoice.go
Normal 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"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user