diff --git a/controller/report.go b/controller/report.go index 5e31558..2c694ac 100644 --- a/controller/report.go +++ b/controller/report.go @@ -37,6 +37,7 @@ func InitializeReportController(router *gin.Engine) { router.POST("/report/:rid/publish", security.EnterpriseAuthorize, publishReport) router.GET("/reports", security.MustAuthenticated, searchReports) router.GET("/report/:rid", security.MustAuthenticated, fetchReportPublicity) + router.POST("/report/:rid/calculate", security.EnterpriseAuthorize, calculateReport) } func ensureReportBelongs(c *gin.Context, result *response.Result, requestReportId string) bool { @@ -461,3 +462,17 @@ func fetchReportPublicity(c *gin.Context) { } result.Success("已经取得指定公示报表的发布版本。", tools.ConvertStructToMap(publicity)) } + +func calculateReport(c *gin.Context) { + result := response.NewResult(c) + requestReportId := c.Param("rid") + if !ensureReportBelongs(c, result, requestReportId) { + return + } + err := service.CalculateService.ComprehensivelyCalculateReport(requestReportId) + if err != nil { + result.Error(http.StatusInternalServerError, err.Error()) + return + } + result.Success("指定公示报表中的数据已经计算完毕。") +} diff --git a/model/end_user_detail.go b/model/end_user_detail.go index 7e665d7..09c9ccc 100644 --- a/model/end_user_detail.go +++ b/model/end_user_detail.go @@ -36,7 +36,7 @@ type EndUserDetail struct { AdjustValley decimal.Decimal `xorm:"numeric(14,2) not null default 0" json:"adjustValley"` Overall decimal.NullDecimal `xorm:"numeric(14,2)" json:"overall"` OverallFee decimal.NullDecimal `xorm:"numeric(14,2)" json:"overallFee"` - OverallProporttion decimal.Decimal `xorm:"numeric(16,15) not null default 0" json:"-"` + OverallProportion decimal.Decimal `xorm:"numeric(16,15) not null default 0" json:"-"` Critical decimal.NullDecimal `xorm:"numeric(14,2)" json:"critical"` CriticalFee decimal.NullDecimal `xorm:"numeric(18,8)" json:"criticalFee"` Peak decimal.NullDecimal `xorm:"numeric(14,2)" json:"peak"` diff --git a/model/publicity.go b/model/publicity.go index e8ae92f..b02830f 100644 --- a/model/publicity.go +++ b/model/publicity.go @@ -26,7 +26,7 @@ type PaidPart struct { type EndUserOverallPart struct { Overall decimal.Decimal `json:"overall"` OverallPrice decimal.Decimal `json:"overallPrice"` - OverallFee decimal.Decimal `json:"overallFee"` + OverallFee decimal.Decimal `json:"consumptionFee"` Critical decimal.NullDecimal `json:"critical"` CriticalPrice decimal.NullDecimal `json:"criticalPrice"` CriticalFee decimal.NullDecimal `json:"criticalFee"` @@ -107,7 +107,7 @@ type Publicity struct { EndUser EndUserOverallPart `json:"endUserSum"` Loss LossPart `json:"loss"` PublicConsumptionOverall PublicConsumptionOverallPart `json:"public"` - OtherCollections OtherShouldCollectionPart `json:"other"` + OtherCollections OtherShouldCollectionPart `json:"others"` Maintenance MaintenancePart `json:"maintenance"` EndUserDetails []EndUserSummary `json:"endUser"` } diff --git a/model/report_summary.go b/model/report_summary.go index f13156a..b229aeb 100644 --- a/model/report_summary.go +++ b/model/report_summary.go @@ -76,7 +76,11 @@ func (s ReportSummary) Validate() (bool, error) { func (s *ReportSummary) CalculatePrices() { s.ConsumptionFee = decimal.NewNullDecimal(s.OverallFee.Sub(s.BasicFee).Sub(s.AdjustFee)) - s.OverallPrice = decimal.NewNullDecimal(s.ConsumptionFee.Decimal.Div(s.Overall).RoundBank(8)) + if s.Overall.GreaterThan(decimal.Zero) { + s.OverallPrice = decimal.NewNullDecimal(s.ConsumptionFee.Decimal.Div(s.Overall).RoundBank(8)) + } else { + s.OverallPrice = decimal.NewNullDecimal(decimal.Zero) + } if s.Critical.GreaterThan(decimal.Zero) { s.CriticalPrice = decimal.NewNullDecimal(s.CriticalFee.Div(s.Critical).RoundBank(8)) } else { diff --git a/service/calculate.go b/service/calculate.go new file mode 100644 index 0000000..0e294ba --- /dev/null +++ b/service/calculate.go @@ -0,0 +1,245 @@ +package service + +import ( + "electricity_bill_calc/exceptions" + "electricity_bill_calc/global" + "electricity_bill_calc/model" + + "github.com/shopspring/decimal" + "xorm.io/builder" + "xorm.io/xorm/schemas" +) + +type _CalculateService struct{} + +var CalculateService _CalculateService + +func (_CalculateService) ComprehensivelyCalculateReport(reportId string) (err error) { + // 资料准备 + var reportIndex = new(model.Report) + has, err := global.DBConn.ID(reportId).NoAutoCondition().Get(reportIndex) + if err != nil || !has { + return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表", err) + } + var summary = new(model.ReportSummary) + has, err = global.DBConn.ID(reportId).NoAutoCondition().Get(summary) + if err != nil || !has { + return exceptions.NewNotFoundErrorFromError("未找到指定的公示报表概览", err) + } + var maintenanceFeeRecords = make([]model.WillDilutedFee, 0) + err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&maintenanceFeeRecords) + if err != nil { + return exceptions.NewNotFoundErrorFromError("未能获取到公示报表对应的待摊薄费用信息", err) + } + var endUserDetails = make([]*model.EndUserDetail, 0) + err = global.DBConn.Where(builder.Eq{"report_id": reportId}).Find(&endUserDetails) + if err != nil { + return exceptions.NewNotFoundErrorFromError("未获取到公示报表对应的终端用户抄表信息", err) + } + + // 综合计算 + summary.CalculatePrices() + + // 计算维护费总计 + maintenanceFeeTotal := decimal.NewFromInt(0) + for _, m := range maintenanceFeeRecords { + maintenanceFeeTotal = maintenanceFeeTotal.Add(m.Fee) + } + + // 计算终端用户信息与概览中的合计 + summary.PublicConsumption = decimal.NewNullDecimal(decimal.Zero) + summary.PublicConsumptionCritical = decimal.NewNullDecimal(decimal.Zero) + summary.PublicConsumptionPeak = decimal.NewNullDecimal(decimal.Zero) + summary.PublicConsumptionFlat = decimal.NewNullDecimal(decimal.Zero) + summary.PublicConsumptionValley = decimal.NewNullDecimal(decimal.Zero) + summary.CustomerConsumption = decimal.NewNullDecimal(decimal.Zero) + summary.CustomerConsumptionCritical = decimal.NewNullDecimal(decimal.Zero) + summary.CustomerConsumptionPeak = decimal.NewNullDecimal(decimal.Zero) + summary.CustomerConsumptionFlat = decimal.NewNullDecimal(decimal.Zero) + summary.CustomerConsumptionValley = decimal.NewNullDecimal(decimal.Zero) + for _, eu := range endUserDetails { + eu.OverallFee = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + eu.CriticalFee = decimal.NewNullDecimal( + eu.Critical.Decimal.Mul(summary.CriticalPrice.Decimal).RoundBank(2), + ) + eu.PeakFee = decimal.NewNullDecimal( + eu.Peak.Decimal.Mul(summary.PeakPrice.Decimal).RoundBank(2), + ) + eu.FlatFee = decimal.NewNullDecimal( + eu.Flat.Decimal.Mul(summary.FlatPrice.Decimal).RoundBank(2), + ) + eu.ValleyFee = decimal.NewNullDecimal( + eu.Valley.Decimal.Mul(summary.ValleyPrice.Decimal).RoundBank(2), + ) + if eu.IsPublicMeter && eu.WillDilute { + summary.PublicConsumption.Decimal = summary.PublicConsumption.Decimal.Add(eu.Overall.Decimal) + summary.PublicConsumptionCritical.Decimal = summary.PublicConsumptionCritical.Decimal.Add(eu.Critical.Decimal) + summary.PublicConsumptionPeak.Decimal = summary.PublicConsumptionPeak.Decimal.Add(eu.Peak.Decimal) + summary.PublicConsumptionFlat.Decimal = summary.PublicConsumptionPeak.Decimal.Add(eu.Flat.Decimal) + summary.PublicConsumptionValley.Decimal = summary.PublicConsumptionValley.Decimal.Add(eu.Valley.Decimal) + } else { + summary.CustomerConsumption.Decimal = summary.CustomerConsumption.Decimal.Add(eu.Overall.Decimal) + summary.CustomerConsumptionCritical.Decimal = summary.CustomerConsumptionCritical.Decimal.Add(eu.Critical.Decimal) + summary.CustomerConsumptionPeak.Decimal = summary.CustomerConsumptionPeak.Decimal.Add(eu.Peak.Decimal) + summary.CustomerConsumptionFlat.Decimal = summary.CustomerConsumptionPeak.Decimal.Add(eu.Flat.Decimal) + summary.CustomerConsumptionValley.Decimal = summary.CustomerConsumptionValley.Decimal.Add(eu.Valley.Decimal) + } + } + + // 计算户表总电费和公共总电费以及相应的摊薄 + summary.CustomerConsumptionFee = decimal.NewNullDecimal( + summary.CustomerConsumption.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.CustomerConsumptionCriticalFee = decimal.NewNullDecimal( + summary.CustomerConsumptionCritical.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.CustomerConsumptionPeakFee = decimal.NewNullDecimal( + summary.CustomerConsumptionPeak.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.CustomerConsumptionFlatFee = decimal.NewNullDecimal( + summary.CustomerConsumptionFlat.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.CustomerConsumptionValleyFee = decimal.NewNullDecimal( + summary.CustomerConsumptionValley.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.PublicConsumptionFee = decimal.NewNullDecimal( + summary.PublicConsumption.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.PublicConsumptionCriticalFee = decimal.NewNullDecimal( + summary.PublicConsumptionCritical.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.PublicConsumptionPeakFee = decimal.NewNullDecimal( + summary.PublicConsumptionPeak.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.PublicConsumptionFlatFee = decimal.NewNullDecimal( + summary.PublicConsumptionFlat.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + summary.PublicConsumptionValleyFee = decimal.NewNullDecimal( + summary.PublicConsumptionValley.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(2), + ) + if summary.Overall.Abs().GreaterThan(decimal.Zero) { + summary.PublicConsumptionProportion = decimal.NewNullDecimal( + summary.PublicConsumption.Decimal.Div(summary.Overall).RoundBank(15), + ) + } + + // 计算线损 + summary.Loss = decimal.NewNullDecimal( + summary.Overall.Sub(summary.PublicConsumption.Decimal).Sub(summary.CustomerConsumption.Decimal), + ) + summary.LossFee = decimal.NewNullDecimal( + summary.Loss.Decimal.Mul(summary.OverallPrice.Decimal).RoundBank(8), + ) + if summary.Overall.Abs().GreaterThan(decimal.Zero) { + summary.LossProportion = decimal.NewNullDecimal( + summary.Loss.Decimal.Div(summary.Overall).RoundBank(15), + ) + } + if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { + summary.LossDilutedPrice = decimal.NewNullDecimal( + summary.LossFee.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(8), + ) + } + + // 计算基本电费和调整电费等的摊薄 + if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { + summary.BasicDilutedPrice = decimal.NewNullDecimal( + summary.BasicFee.Div(summary.CustomerConsumption.Decimal).RoundBank(8), + ) + summary.AdjustDilutedPrice = decimal.NewNullDecimal( + summary.AdjustFee.Div(summary.CustomerConsumption.Decimal).RoundBank(8), + ) + summary.MaintenanceDilutedPrice = decimal.NewNullDecimal( + maintenanceFeeTotal.Div(summary.CustomerConsumption.Decimal).RoundBank(8), + ) + summary.PublicConsumptionDilutedPrice = decimal.NewNullDecimal( + summary.PublicConsumptionFee.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(8), + ) + } + + // 计算摊薄总计 + summary.FinalDilutedOverall = decimal.NewNullDecimal( + summary.BasicFee. + Add(summary.AdjustFee). + Add(summary.PublicConsumptionFee.Decimal). + Add(maintenanceFeeTotal). + Add(summary.LossFee.Decimal), + ) + + // 计算终端用户的全部摊薄内容 + for _, eu := range endUserDetails { + if eu.IsPublicMeter && eu.WillDilute { + // 计算需要摊薄的公共表计的摊薄内容 + if summary.PublicConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { + eu.OverallProportion = eu.Overall.Decimal.Div(summary.PublicConsumption.Decimal).RoundBank(15) + } else { + eu.OverallProportion = decimal.Zero + } + } else { + // 计算户表表计的摊薄内容 + if summary.CustomerConsumption.Decimal.Abs().GreaterThan(decimal.Zero) { + eu.OverallProportion = eu.Overall.Decimal.Div(summary.CustomerConsumption.Decimal).RoundBank(15) + } else { + eu.OverallProportion = decimal.Zero + } + eu.BasicFeeDiluted = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.BasicDilutedPrice.Decimal).RoundBank(2), + ) + eu.AdjustFeeDiluted = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.AdjustDilutedPrice.Decimal).RoundBank(2), + ) + eu.LossDiluted = decimal.NewNullDecimal( + summary.Loss.Decimal.Mul(eu.OverallProportion).RoundBank(8), + ) + eu.LossDiluted = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.LossDilutedPrice.Decimal).RoundBank(8), + ) + eu.MaintenanceFeeDiluted = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.MaintenanceDilutedPrice.Decimal).RoundBank(8), + ) + eu.PublicConsumptionDiluted = decimal.NewNullDecimal( + eu.Overall.Decimal.Mul(summary.PublicConsumptionDilutedPrice.Decimal).RoundBank(8), + ) + eu.FinalDiluted = decimal.NewNullDecimal( + eu.BasicFeeDiluted.Decimal. + Add(eu.AdjustFeeDiluted.Decimal). + Add(eu.LossFeeDiluted.Decimal). + Add(eu.MaintenanceFeeDiluted.Decimal). + Add(eu.PublicConsumptionDiluted.Decimal). + RoundBank(2), + ) + eu.FinalCharge = decimal.NewNullDecimal( + eu.OverallFee.Decimal.Add(eu.FinalDiluted.Decimal).RoundBank(2), + ) + } + } + + // 向数据库保存报表概况以及终端用户摊薄结果 + tx := global.DBConn.NewSession() + if err = tx.Begin(); err != nil { + return + } + defer tx.Close() + + _, err = tx.ID(reportId).Update(summary) + if err != nil { + tx.Rollback() + return + } + for _, eu := range endUserDetails { + _, err = tx.ID(schemas.NewPK(eu.ReportId, eu.ParkId, eu.MeterId)).Update(eu) + if err != nil { + tx.Rollback() + return + } + } + + err = tx.Commit() + if err != nil { + tx.Rollback() + return + } + return +}