enhance(calculate): 完善计算部分

This commit is contained in:
2023-08-07 15:15:11 +08:00
84 changed files with 3173 additions and 5398 deletions

View File

@@ -0,0 +1,33 @@
package calculate
import (
"electricity_bill_calc/model"
"fmt"
"sync/atomic"
)
func CheckMeterArea(report *model.ReportIndex, meters []*model.MeterDetail) (bool, error) {
anyAreaOptions := report.BasisPooled == model.POOLING_MODE_AREA ||
report.AdjustPooled == model.POOLING_MODE_AREA ||
report.PublicPooled == model.POOLING_MODE_AREA ||
report.LossPooled == model.POOLING_MODE_AREA
if anyAreaOptions {
var meterWithoutArea int32
for _, m := range meters {
if (m.MeterType == model.METER_INSTALLATION_TENEMENT || m.MeterType == model.METER_INSTALLATION_POOLING) &&
m.Area == nil {
atomic.AddInt32(&meterWithoutArea, 1)
}
}
if meterWithoutArea != 0 {
return false, fmt.Errorf("园区中有 %d 个表计没有设置面积,无法进行按面积摊薄。", meterWithoutArea)
}
return true, nil
}
return false, nil
}

View File

@@ -0,0 +1,10 @@
package calculate
import "electricity_bill_calc/model/calculate"
// / 合并所有的表计
type Key struct {
Code string
TenementID string
}
type MeterMap map[Key]calculate.Meter

View File

@@ -9,13 +9,6 @@ import (
"github.com/shopspring/decimal"
)
// / 合并所有的表计
type Key struct {
Code string
TenementID string
}
type MeterMap map[Key]calculate.Meter
func CollectMeters(tenements []calculate.PrimaryTenementStatistics, poolings []calculate.Meter, publics []calculate.Meter) (MeterMap, error) {
meters := make(MeterMap)
// Collect tenement meters

View File

@@ -1,72 +0,0 @@
package calculate
import (
"electricity_bill_calc/global"
"electricity_bill_calc/repository"
"github.com/doug-martin/goqu/v9"
)
type _ModService struct {
ds goqu.DialectWrapper
}
var ModService = _ModService{
ds: goqu.Dialect("postgres"),
}
func mainCalculateProcess(rid string) error {
// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。
err := CalculateEnabledArea(tenementreports, &summary)
// 计算基本电费分摊、调整电费分摊、电费摊薄单价。
err = CalculatePrices(&summary)
// 收集目前所有已经处理的表计,统一对其进行摊薄计算。
collectMeters, err := CollectMeters(tenementreports, poolingmetersreports, parkmetersreports)
meters, err := collectMeters, err
if err != nil {
return err
}
// 根据核算报表中设置的摊薄内容,逐个表计进行计算
CalculateBasicPooling(report, summary, meters)
CalculateAdjustPooling(report, summary, meters)
CalculateLossPooling(report, summary, meters)
// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。
CalculateTenementConsumptions(meters)
CalculateTenementPoolings(report, summary, meters, metersrelations)
// 计算商户的合计电费信息,并归总与商户相关联的表计记录
tenementCharges, err := CalculateTenementCharge(tenementReports, summary, meters, meterRelations)
if err != nil {
// 处理错误
}
// 从此处开始向数据库保存全部计算结果。
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, _ := global.DB.Begin(ctx)
err = repository.CalculateRepository.ClearReportContent(tx, report.Id)
if err != nil {
tx.Rollback(ctx)
return err
}
err = SaveSummary(tx, summary)
if err != nil {
tx.Rollback(ctx)
return err
}
err = SavePublics(tx, report, meters)
if err != nil {
tx.Rollback(ctx)
return err
}
err = SavePoolings(tx)
if err != nil {
tx.Rollback(ctx)
return err
}
err = SaveTenements(tx)
if err != nil {
tx.Rollback(ctx)
return err
}
tx.Commit(ctx)
}

37
service/calculate/park.go Normal file
View File

@@ -0,0 +1,37 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"time"
)
func MetersParkCalculate(report model.ReportIndex, periodStart time.Time,
periodEnd time.Time, meterDetail []*model.MeterDetail,
summary calculate.Summary) ([]calculate.Meter, error) {
parkMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_PARK)
if err != nil {
return nil, err
}
lastTermParkMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_PARK)
if err != nil {
return nil, err
}
parkMeterReadings = append(parkMeterReadings, lastTermParkMeterReadings...)
var parkMetersReports []calculate.Meter
for _, meter := range meterDetail {
if meter.MeterType == model.METER_INSTALLATION_PARK {
parkMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, parkMeterReadings, *meter, summary)
if err != nil {
return nil, err
}
parkMetersReports = append(parkMetersReports, parkMetersReport)
}
}
return parkMetersReports, nil
}

View File

@@ -60,9 +60,13 @@ func SavePoolings(tx pgx.Tx, report model.ReportIndex, meters MeterMap, relation
if err != nil {
return err
}
tx.Commit(ctx)
return nil
}
func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []*calculate.TenementCharge) error {
func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.PrimaryTenementStatistics, tc []calculate.TenementCharge) error {
ctx, cancel := global.TimeoutContext()
defer cancel()
var ts []model.Tenement
for _, r := range tenement {
ts = append(ts, r.Tenement)
@@ -71,5 +75,7 @@ func SaveTenements(tx pgx.Tx, report model.ReportIndex, tenement []calculate.Pri
if err != nil {
return err
}
tx.Commit(ctx)
return nil
}

111
service/calculate/pooled.go Normal file
View File

@@ -0,0 +1,111 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"github.com/shopspring/decimal"
"time"
"unsafe"
)
//核算园区中的全部公摊表计的电量用量
func PooledMetersCalculate(report *model.ReportIndex, periodStart time.Time,
periodEnd time.Time, meterDetails []*model.MeterDetail,
summary calculate.Summary) ([]calculate.Meter, error) {
poolingMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_POOLING)
if err != nil {
return nil, err
}
lastTermPoolingMeterReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_POOLING)
if err != nil {
return nil, err
}
poolingMeterReadings = append(poolingMeterReadings, lastTermPoolingMeterReadings...)
var poolingMetersReports []calculate.Meter
for _, meter := range meterDetails {
poolingMetersReport, err := determinePublicMeterConsumptions(meter.Code, periodStart, periodEnd, poolingMeterReadings, *meter, summary)
if err != nil {
return nil, err
}
poolingMetersReports = append(poolingMetersReports, poolingMetersReport)
}
return poolingMetersReports, nil
}
// 确定指定非商户表计在指定时间段内的全部电量
func determinePublicMeterConsumptions(meterId string, periodStart time.Time,
periodEnd time.Time, readings []model.MeterReading,
meterDetail model.MeterDetail, summary calculate.Summary) (calculate.Meter, error) {
startReading, err := DeterminePublicMeterStartReading(meterId, periodStart, meterDetail.DetachedAt.Time, readings)
if err != nil {
return calculate.Meter{}, err
}
endReading, err := DeterminePublicMeterEndReading(meterId, periodEnd, meterDetail.DetachedAt.Time, readings)
if err != nil {
return calculate.Meter{}, err
}
overall, err := ComputeOverall(*startReading, *endReading, summary)
if err != nil {
return calculate.Meter{}, err
}
critical, err := ComputeCritical(*startReading, *endReading, summary)
if err != nil {
return calculate.Meter{}, err
}
peak, err := ComputePeak(*startReading, *endReading, summary)
if err != nil {
return calculate.Meter{}, err
}
flat, err := ComputeFlat(*startReading, *endReading, summary)
if err != nil {
return calculate.Meter{}, err
}
valley, err := ComputeValley(*startReading, *endReading, summary)
if err != nil {
return calculate.Meter{}, err
}
return calculate.Meter{
Code: meterId,
Detail: meterDetail,
CoveredArea: meterDetail.Area.Decimal,
LastTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{
Ratio: startReading.Ratio,
Overall: startReading.Overall,
Critical: startReading.Critical,
Peak: startReading.Peak,
Flat: startReading.Flat,
Valley: startReading.Valley,
})),
CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(&model.Reading{
Ratio: endReading.Ratio,
Overall: endReading.Overall,
Critical: endReading.Critical,
Peak: endReading.Peak,
Flat: endReading.Flat,
Valley: endReading.Valley,
})),
Overall: overall,
Critical: critical,
Peak: peak,
Flat: flat,
Valley: valley,
AdjustLoss: model.ConsumptionUnit{},
PooledBasic: model.ConsumptionUnit{},
PooledAdjust: model.ConsumptionUnit{},
PooledLoss: model.ConsumptionUnit{},
PooledPublic: model.ConsumptionUnit{},
SharedPoolingProportion: decimal.Decimal{},
Poolings: nil,
}, nil
}

105
service/calculate/shared.go Normal file
View File

@@ -0,0 +1,105 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/types"
"errors"
"fmt"
"time"
)
// 确定指定非商户表计的起始读数
func DeterminePublicMeterStartReading(meterId string, periodStart time.Time,
attachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) {
periodBeginning := types.Date{Time: periodStart}.ToBeginningOfDate()
if len(meterReadings) <= 0 {
return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId))
}
var minReading types.DateTime
for _, reading := range meterReadings {
if reading.ReadAt.Before(minReading.Time) {
minReading = reading.ReadAt
}
}
startTimes := []time.Time{
minReading.Time,
periodBeginning.Time,
ShiftToAsiaShanghai(attachedAt),
}
if len(startTimes) < 0 {
return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的起始时间", meterId))
}
var startReading []model.MeterReading
for _, reading := range meterReadings {
readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC())
for _, startTime := range startTimes {
if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) {
startReading = append(startReading, reading)
break
}
}
}
if len(startReading) <= 0 {
return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的起始读数", meterId))
}
var startReadings *model.MeterReading
for _, readings := range startReading {
if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) {
startReadings = &readings
}
}
return startReadings, nil
}
// 确定指定非商户表计的结束读数
func DeterminePublicMeterEndReading(meterId string, periodEnd time.Time,
detachedAt time.Time, meterReadings []model.MeterReading) (*model.MeterReading, error) {
periodEnding := types.Date{Time: periodEnd}.ToEndingOfDate()
if len(meterReadings) <= 0 {
return nil, errors.New(fmt.Sprintf("表计的抄表记录数据不足%s", meterId))
}
var minReading types.DateTime
for _, reading := range meterReadings {
if reading.ReadAt.Before(minReading.Time) {
minReading = reading.ReadAt
}
}
startTimes := []time.Time{
minReading.Time,
periodEnding.Time,
ShiftToAsiaShanghai(detachedAt),
}
if len(startTimes) < 0 {
return nil, errors.New(fmt.Sprintf("无法确定表计 {%s} 的计量的终止时间", meterId))
}
var startReading []model.MeterReading
for _, reading := range meterReadings {
readingAt := ShiftToAsiaShanghai(reading.ReadAt.UTC())
for _, startTime := range startTimes {
if reading.Meter == meterId && readingAt.After(startTime) || readingAt.Equal(startTime) {
startReading = append(startReading, reading)
break
}
}
}
if len(startReading) <= 0 {
return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的终止读数", meterId))
}
var startReadings *model.MeterReading
for _, readings := range startReading {
if startReadings == nil || readings.ReadAt.Before(startReadings.ReadAt.Time) {
startReadings = &readings
}
}
return startReadings, nil
}

View File

@@ -1,18 +1,99 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"errors"
"github.com/shopspring/decimal"
"fmt"
)
// / 计算已经启用的商铺面积和
// /
// / - `tenements`:所有商户的电量信息
// / - `summary`:核算报表的摘要信息
func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summary *calculate.Summary) error {
var areaMeters []calculate.Meter
// 计算已经启用的商铺面积和
func TotalConsumptionCalculate(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary) decimal.Decimal {
var areaMaters []calculate.Meter
for _, t := range tenements {
areaMaters = append(areaMaters, t.Meters...)
}
areaMaters = removeDuplicates(areaMaters)
var areaTotal float64
for _, m := range areaMaters {
areaTotal += m.Detail.Area.Decimal.InexactFloat64()
}
areaTotal += summary.OverallArea.InexactFloat64()
return decimal.NewFromFloat(areaTotal)
}
func removeDuplicates(meters []calculate.Meter) []calculate.Meter {
result := make([]calculate.Meter, 0, len(meters))
seen := make(map[string]bool)
for _, meter := range meters {
if !seen[meter.Code] {
seen[meter.Code] = true
result = append(result, meter)
}
}
return result
}
// 计算线损以及调整线损
func LossCalculate(report *model.ReportIndex, Public *[]calculate.Meter,
publicTotal *decimal.Decimal, summary *calculate.Summary) error {
summary.Loss = summary.Overall.Amount.Sub(summary.TotalConsumption)
var summaryAmount decimal.Decimal
if summary.Overall.Amount == decimal.Zero {
summaryAmount = decimal.NewFromFloat(1.0)
} else {
summaryAmount = summary.Overall.Amount
}
summary.LossProportion = summary.Loss.Div(summaryAmount)
var authorizedLossRate decimal.Decimal
//TODO: 2023.08.04 在此发现reportIndex结构体与数据库中的report表字段不对应缺少两个相应字段在此添加的如在其他地方有错误优先查找这里
if summary.LossProportion.InexactFloat64() > report.AuthorizedLossRate {
authorizedLossRate = summary.LossProportion
} else {
return errors.New(fmt.Sprintf("经过核算园区的线损率为:{%.8f} 核定线损率为:{%.8f}", summary.LossProportion.InexactFloat64(), authorizedLossRate.InexactFloat64()))
}
summary.AuthoizeLoss = model.ConsumptionUnit{
Amount: decimal.NewFromFloat(summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()),
Fee: decimal.NewFromFloat((summary.Overall.Amount.InexactFloat64() * authorizedLossRate.InexactFloat64()) * summary.Overall.Price.InexactFloat64()),
Price: summary.Overall.Price,
Proportion: authorizedLossRate,
}
differentialLoss := summary.LossDilutedPrice.Sub(summary.AuthoizeLoss.Amount)
if publicTotal.InexactFloat64() <= decimal.Zero.InexactFloat64() {
return errors.New("园区公共表计的电量总和为非正值,或者园区未设置公共表计,无法计算核定线损")
}
for _, meter := range *Public {
amountProportion := meter.Overall.Amount.InexactFloat64() / publicTotal.InexactFloat64()
adjustAmount := differentialLoss.InexactFloat64() * decimal.NewFromFloat(-1.0).InexactFloat64()
meter.AdjustLoss = model.ConsumptionUnit{
Amount: decimal.NewFromFloat(adjustAmount),
Fee: decimal.NewFromFloat(adjustAmount * summary.LossDilutedPrice.InexactFloat64()),
Price: summary.LossDilutedPrice,
Proportion: decimal.NewFromFloat(amountProportion),
}
}
return nil
}
// 计算已经启用的商铺面积和
func EnabledAreaCalculate(tenements *[]calculate.PrimaryTenementStatistics,
summary *calculate.Summary) (*decimal.Decimal, error) {
var areaMeters []calculate.Meter
for _, t := range *tenements {
areaMeters = append(areaMeters, t.Meters...)
}
// 去重
@@ -32,6 +113,7 @@ func CalculateEnabledArea(tenements []calculate.PrimaryTenementStatistics, summa
return &areaTotal, nil
}
// =================================================================================
// / 计算基本电费分摊、调整电费分摊以及电费摊薄单价。
// /
// / - `summary`:核算报表的摘要信息
@@ -52,4 +134,5 @@ func CalculatePrices(summary *calculate.Summary) error {
summary.AdjustPooledPriceArea = summary.AdjustFee.Div(summary.OverallArea)
}
return nil
}

View File

@@ -3,219 +3,456 @@ package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"errors"
"fmt"
"github.com/shopspring/decimal"
"math/big"
"sort"
"strings"
"time"
"unsafe"
)
// / 计算各个商户的合计信息,并归总与商户关联的表计记录
func CalculateTenementCharge(tenements []calculate.PrimaryTenementStatistics, summary calculate.Summary, meters MeterMap, relations []model.MeterRelation) ([]calculate.TenementCharge, error) {
tenementCharges := make([]calculate.TenementCharge, 0)
// 核算园区中的全部商户表计电量用电
func TenementMetersCalculate(report *model.ReportIndex,
PeriodStart time.Time, PeriodEnd time.Time,
meterDetails []*model.MeterDetail, summary calculate.Summary) ([]calculate.PrimaryTenementStatistics, error) {
tenements, err := repository.CalculateRepository.GetAllTenements(report.Id)
if err != nil {
fmt.Println("tenement 0", err)
return nil, err
}
tenementMeterRelations, err := repository.CalculateRepository.GetAllTenementMeterRelations(report.Park, PeriodEnd, PeriodStart)
if err != nil {
fmt.Println("tenement 1", err)
return nil, err
}
tenementMeterReadings, err := repository.CalculateRepository.GetMeterReadings(report.Id, model.METER_INSTALLATION_TENEMENT)
if err != nil {
fmt.Println("tenement 2", err)
return nil, err
}
lastPeriodReadings, err := repository.CalculateRepository.GetLastPeriodReadings(report.Id, model.METER_INSTALLATION_TENEMENT)
if err != nil {
fmt.Println("tenement 3", err)
return nil, err
}
var tenementReports []calculate.PrimaryTenementStatistics
for _, tenement := range tenements {
relatedMeters := make([]calculate.Meter, 0)
for _, meter := range tenement.Meters {
code := meter.Code
if meter.Detail.MeterType == model.METER_INSTALLATION_TENEMENT {
code = meter.Detail.Code
var meters []model.TenementMeter
for _, relation := range tenementMeterRelations {
if strings.EqualFold(relation.TenementId, tenement.Id) {
meters = append(meters, relation)
}
relatedMeter, ok := meters[Key{Code: code}]
if !ok {
return nil, errors.New("related meter not found")
}
relatedMeters = append(relatedMeters, relatedMeter)
}
// Calculate overall, critical, peak, flat, valley, etc.
//var overall, critical, peak, flat, valley model.ConsumptionUnit
basicPooled, adjustPooled, lossPooled, publicPooled := new(big.Rat), new(big.Rat), new(big.Rat), new(big.Rat)
lossAmount := new(big.Rat)
for _, meter := range relatedMeters {
overall.Add(overall, meter.Overall)
critical.Add(critical, meter.Critical)
peak.Add(peak, meter.Peak)
flat.Add(flat, meter.Flat)
valley.Add(valley, meter.Valley)
basicPooled.Add(basicPooled, meter.PooledBasic.Fee)
adjustPooled.Add(adjustPooled, meter.PooledAdjust.Fee)
lossAmount.Add(lossAmount, meter.AdjustLoss.Amount)
lossPooled.Add(lossPooled, meter.PooledLoss.Fee)
publicPooled.Add(publicPooled, meter.PooledPublic.Fee)
pt, err := determineTenementConsumptions(
tenement,
meters,
PeriodStart,
PeriodEnd,
tenementMeterReadings,
lastPeriodReadings,
meterDetails,
summary,
)
if err != nil {
return nil, err
}
// Update proportions and other data for related meters
for _, meter := range relatedMeters {
meter.Overall.Proportion = new(big.Rat).Quo(meter.Overall.Amount, overall.Amount)
meter.Critical.Proportion = new(big.Rat).Quo(meter.Critical.Amount, critical.Amount)
meter.Peak.Proportion = new(big.Rat).Quo(meter.Peak.Amount, peak.Amount)
meter.Flat.Proportion = new(big.Rat).Quo(meter.Flat.Amount, flat.Amount)
meter.Valley.Proportion = new(big.Rat).Quo(meter.Valley.Amount, valley.Amount)
meter.PooledBasic.Proportion = new(big.Rat).Quo(meter.PooledBasic.Fee, basicPooled)
meter.PooledAdjust.Proportion = new(big.Rat).Quo(meter.PooledAdjust.Fee, adjustPooled)
meter.PooledLoss.Proportion = new(big.Rat).Quo(meter.PooledLoss.Fee, lossPooled)
meter.PooledPublic.Proportion = new(big.Rat).Quo(meter.PooledPublic.Fee, publicPooled)
}
tenementCharges = append(tenementCharges, TenementCharges{
Tenement: tenement.Tenement.ID,
Overall: ConsumptionUnit{
Amount: overall.Amount,
Fee: new(big.Rat).Mul(overall.Amount, summary.Overall.Price),
Price: summary.Overall.Price,
Proportion: new(big.Rat).Quo(overall.Amount, summary.Overall.Amount),
},
Critical: ConsumptionUnit{
Amount: critical.Amount,
Fee: new(big.Rat).Mul(critical.Amount, summary.Critical.Price),
Price: summary.Critical.Price,
Proportion: new(big.Rat).Quo(critical.Amount, summary.Critical.Amount),
},
Peak: ConsumptionUnit{
Amount: peak.Amount,
Fee: new(big.Rat).Mul(peak.Amount, summary.Peak.Price),
Price: summary.Peak.Price,
Proportion: new(big.Rat).Quo(peak.Amount, summary.Peak.Amount),
},
Flat: ConsumptionUnit{
Amount: flat.Amount,
Fee: new(big.Rat).Mul(flat.Amount, summary.Flat.Price),
Price: summary.Flat.Price,
Proportion: new(big.Rat).Quo(flat.Amount, summary.Flat.Amount),
},
Valley: ConsumptionUnit{
Amount: valley.Amount,
Fee: new(big.Rat).Mul(valley.Amount, summary.Valley.Price),
Price: summary.Valley.Price,
Proportion: new(big.Rat).Quo(valley.Amount, summary.Valley.Amount),
},
Loss: ConsumptionUnit{
Amount: lossAmount,
Fee: new(big.Rat).Mul(lossPooled, summary.AuthorizeLoss.Price),
Price: summary.AuthorizeLoss.Price,
Proportion: new(big.Rat).Quo(lossAmount, summary.AuthorizeLoss.Amount),
},
BasicFee: basicPooled,
AdjustFee: adjustPooled,
LossPooled: lossPooled,
PublicPooled: publicPooled,
// ... 其他字段的初始化
})
tenementReports = append(tenementReports, pt)
}
return tenementCharges, nil
return tenementReports, nil
}
func calculateTenementCharge(
tenements []*PrimaryTenementStatistics,
summary *Summary,
meters MeterMap,
_relations []MeterRelation,
) ([]TenementCharges, error) {
var tenementCharges []TenementCharges
// TODO: 2023.08.02 此方法未完成此方法主要用于。确定指定商户在指定时间段内的所有表计读数(完成)
func determineTenementConsumptions(tenement model.Tenement,
relatedMeters []model.TenementMeter, periodStart time.Time,
periodEnd time.Time, currentTermReadings []model.MeterReading, lastPeriodReadings []model.MeterReading,
meterDetails []*model.MeterDetail, summary calculate.Summary) (calculate.PrimaryTenementStatistics, error) {
var meters []calculate.Meter
for _, meter := range relatedMeters {
startReading, err := determineTenementMeterStartReading(meter.MeterId, periodStart, ShiftToAsiaShanghai(tenement.MovedInAt.Time), meter, currentTermReadings, lastPeriodReadings)
if err != nil {
fmt.Println(err)
return calculate.PrimaryTenementStatistics{}, err
}
endReading, err := determineTenementMeterEndReading(meter.MeterId, periodEnd, ShiftToAsiaShanghai(tenement.MovedOutAt.Time), meter, currentTermReadings)
if err != nil {
fmt.Println(err)
return calculate.PrimaryTenementStatistics{}, err
}
detail, err := getMeterDetail(meterDetails, meter.MeterId)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
overall, err := ComputeOverall(*startReading, *endReading, summary)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
critical, err := ComputeCritical(*startReading, *endReading, summary)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
peak, err := ComputePeak(*startReading, *endReading, summary)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
flat, err := ComputeFlat(*startReading, *endReading, summary)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
valley, err := ComputeValley(*startReading, *endReading, summary)
if err != nil {
return calculate.PrimaryTenementStatistics{}, err
}
lastTermReading := model.Reading{
Ratio: startReading.Ratio,
Overall: startReading.Overall,
Critical: startReading.Critical,
Peak: startReading.Peak,
Flat: startReading.Flat,
Valley: startReading.Valley,
}
lastTermReadingPtr := &lastTermReading
currentTermReading := model.Reading{
Ratio: endReading.Ratio,
Overall: endReading.Overall,
Critical: endReading.Critical,
Peak: endReading.Peak,
Flat: endReading.Flat,
Valley: endReading.Valley,
}
currentTermReadingPtr := &currentTermReading
meter := calculate.Meter{
Code: meter.MeterId,
Detail: detail,
CoveredArea: decimal.NewFromFloat(detail.Area.Decimal.InexactFloat64()),
LastTermReading: (*calculate.Reading)(unsafe.Pointer(lastTermReadingPtr)),
CurrentTermReading: (*calculate.Reading)(unsafe.Pointer(currentTermReadingPtr)),
Overall: overall,
Critical: critical,
Peak: peak,
Flat: flat,
Valley: valley,
AdjustLoss: model.ConsumptionUnit{},
PooledBasic: model.ConsumptionUnit{},
PooledAdjust: model.ConsumptionUnit{},
PooledLoss: model.ConsumptionUnit{},
PooledPublic: model.ConsumptionUnit{},
SharedPoolingProportion: decimal.Decimal{},
Poolings: nil,
}
meters = append(meters, meter)
}
return calculate.PrimaryTenementStatistics{
Tenement: tenement,
Meters: meters,
}, nil
}
func getMeterDetail(meterDetails []*model.MeterDetail, code string) (model.MeterDetail, error) {
for _, detail := range meterDetails {
if detail.Code == code {
return *detail, nil
}
}
return model.MeterDetail{}, errors.New(fmt.Sprintf("表计 %s 的详细信息不存在", code))
}
// 确定指定表计的起始读数
func determineTenementMeterStartReading(meterId string, periodStart time.Time, tenementMovedInAt time.Time,
meterRelation model.TenementMeter, currentTermReadings []model.MeterReading,
lastPeriodReadings []model.MeterReading) (*model.MeterReading, error) {
var startTime time.Time
timeList := []time.Time{
periodStart,
tenementMovedInAt,
meterRelation.AssociatedAt.Time,
}
for _, t := range timeList {
if t.After(startTime) {
startTime = t
}
}
if startTime.IsZero() {
return nil, fmt.Errorf("无法确定表计 %s 的计量的起始时间", meterId)
}
var startReading *model.MeterReading
if startTime.Equal(periodStart) {
for _, reading := range lastPeriodReadings {
if reading.Meter == meterId {
if startReading == nil || reading.ReadAt.After(startReading.ReadAt.Time) {
startReading = &reading
}
}
}
} else {
for _, reading := range currentTermReadings {
readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time)
if reading.Meter == meterId && readingAt.After(startTime) {
if startReading == nil || readingAt.Before(startReading.ReadAt.Time) {
startReading = &reading
}
}
}
}
if startReading == nil {
return nil, errors.New("无法确定表计 " + meterId + " 的计量的起始读数")
}
return startReading, nil
}
// 确定指定表计的终止读书
func determineTenementMeterEndReading(meterId string, periodEnd time.Time,
TenementMovedOutAt time.Time, meterRelation model.TenementMeter,
currentTermReadings []model.MeterReading) (*model.MeterReading, error) {
var endTime time.Time
timeList := []time.Time{
periodEnd,
TenementMovedOutAt,
ShiftToAsiaShanghai(meterRelation.DisassociatedAt.Time),
}
for _, t := range timeList {
if t.After(endTime) {
endTime = t
}
}
if endTime.IsZero() {
return nil, fmt.Errorf("无法确定表计 %s 的计量的结束时间", meterId)
}
var endReading *model.MeterReading
for _, reading := range currentTermReadings {
readingAt := ShiftToAsiaShanghai(reading.ReadAt.Time)
if reading.Meter == meterId && readingAt.Before(endTime) {
if endReading == nil || readingAt.After(ShiftToAsiaShanghai(endReading.ReadAt.Time)) {
endReading = &reading
}
}
}
if endReading == nil {
return nil, errors.New(fmt.Sprintf("无法确定表计 %s 的计量的结束读数", meterId))
}
return endReading, nil
}
func ShiftToAsiaShanghai(t time.Time) time.Time {
location, _ := time.LoadLocation("Asia/Shanghai")
return t.In(location)
}
// 计算各个商户的合计信息,并归总与商户关联的表计记录
func TenementChargeCalculate(tenements []calculate.PrimaryTenementStatistics,
summary calculate.Summary, meters MeterMap) []calculate.TenementCharge {
result := make(map[string][]string)
for _, t := range tenements {
meterCodes := make([]string, len(t.Meters))
for i, m := range t.Meters {
meterCodes[i] = m.Code
meterCodes := make([]string, 0)
for _, m := range t.Meters {
meterCodes = append(meterCodes, m.Code)
}
relatedMeters := make([]*Meter, len(meterCodes))
for i, code := range meterCodes {
relatedMeter, ok := meters[Key{Code: code, TenementID: t.Tenement.ID}]
if !ok {
// 处理未找到相关表计的情况
continue
}
relatedMeters[i] = relatedMeter
}
var overall, critical, peak, flat, valley ConsumptionUnit
var basicPooled, adjustPooled, lossAmount, lossPooled, publicPooled decimal.Decimal
for _, meter := range relatedMeters {
overall.Amount = overall.Amount.Add(meter.Overall.Amount)
overall.Fee = overall.Fee.Add(meter.Overall.Fee)
critical.Amount = critical.Amount.Add(meter.Critical.Amount)
critical.Fee = critical.Fee.Add(meter.Critical.Fee)
peak.Amount = peak.Amount.Add(meter.Peak.Amount)
peak.Fee = peak.Fee.Add(meter.Peak.Fee)
flat.Amount = flat.Amount.Add(meter.Flat.Amount)
flat.Fee = flat.Fee.Add(meter.Flat.Fee)
valley.Amount = valley.Amount.Add(meter.Valley.Amount)
valley.Fee = valley.Fee.Add(meter.Valley.Fee)
basicPooled = basicPooled.Add(meter.PooledBasic.Fee)
adjustPooled = adjustPooled.Add(meter.PooledAdjust.Fee)
lossAmount = lossAmount.Add(meter.AdjustLoss.Amount)
lossPooled = lossPooled.Add(meter.PooledLoss.Fee)
publicPooled = publicPooled.Add(meter.PooledPublic.Fee)
}
// 反写商户表计的统计数据
for _, meter := range relatedMeters {
meter.Overall.Proportion = meter.Overall.Amount.Div(overall.Amount)
meter.Critical.Proportion = meter.Critical.Amount.Div(critical.Amount)
meter.Peak.Proportion = meter.Peak.Amount.Div(peak.Amount)
meter.Flat.Proportion = meter.Flat.Amount.Div(flat.Amount)
meter.Valley.Proportion = meter.Valley.Amount.Div(valley.Amount)
meter.PooledBasic.Proportion = meter.PooledBasic.Fee.Div(basicPooled)
meter.PooledAdjust.Proportion = meter.PooledAdjust.Fee.Div(adjustPooled)
meter.PooledLoss.Proportion = meter.PooledLoss.Fee.Div(lossPooled)
meter.PooledPublic.Proportion = meter.PooledPublic.Fee.Div(publicPooled)
}
// 构造并添加商户的合计信息
tenementCharges = append(tenementCharges, TenementCharges{
Tenement: t.Tenement.ID,
Overall: ConsumptionUnit{
Price: summary.Overall.Price,
Proportion: overall.Amount.Div(summary.Overall.Amount),
Amount: overall.Amount,
Fee: overall.Fee,
},
Critical: ConsumptionUnit{
Price: summary.Critical.Price,
Proportion: critical.Amount.Div(summary.Critical.Amount),
Amount: critical.Amount,
Fee: critical.Fee,
},
Peak: ConsumptionUnit{
Price: summary.Peak.Price,
Proportion: peak.Amount.Div(summary.Peak.Amount),
Amount: peak.Amount,
Fee: peak.Fee,
},
Flat: ConsumptionUnit{
Price: summary.Flat.Price,
Proportion: flat.Amount.Div(summary.Flat.Amount),
Amount: flat.Amount,
Fee: flat.Fee,
},
Valley: ConsumptionUnit{
Price: summary.Valley.Price,
Proportion: valley.Amount.Div(summary.Valley.Amount),
Amount: valley.Amount,
Fee: valley.Fee,
},
Loss: ConsumptionUnit{
Price: summary.AuthorizeLoss.Price,
Proportion: lossAmount.Div(summary.AuthorizeLoss.Amount),
Amount: lossAmount,
Fee: lossPooled,
},
BasicFee: basicPooled,
AdjustFee: adjustPooled,
LossPooled: lossPooled,
PublicPooled: publicPooled,
FinalCharges: overall.Fee.Add(basicPooled).Add(adjustPooled).Add(lossPooled).Add(publicPooled),
Submeters: relatedMeters,
Poolings: make([]Meter, 0), // TODO: Add pooling logic here
})
sort.Strings(meterCodes)
result[t.Tenement.Id] = meterCodes
}
var Key Key
var tc []calculate.TenementCharge
for tCode, meterCodes := range result {
relatedMeters := make([]calculate.Meter, 0)
for _, code := range meterCodes {
Key.Code = code + "_" + tCode
meter, ok := meters[Key]
if ok {
relatedMeters = append(relatedMeters, meter)
}
}
// 计算商户的合计电费信息
var overall model.ConsumptionUnit
var critical model.ConsumptionUnit
var peak model.ConsumptionUnit
var flat model.ConsumptionUnit
var valley model.ConsumptionUnit
return tenementCharges, nil
var basicPooled decimal.Decimal
var adjustPooled decimal.Decimal
var lossAmount decimal.Decimal
var lossPooled decimal.Decimal
var publicPooled decimal.Decimal
for _, meter := range relatedMeters {
overall.Amount.Add(meter.Overall.Amount)
overall.Fee.Add(meter.Overall.Fee)
critical.Amount.Add(meter.Critical.Amount)
critical.Fee.Add(meter.Critical.Fee)
peak.Amount.Add(meter.Peak.Amount)
peak.Fee.Add(meter.Peak.Fee)
flat.Amount.Add(meter.Flat.Amount)
flat.Fee.Add(meter.Flat.Fee)
valley.Amount.Add(meter.Valley.Amount)
valley.Fee.Add(meter.Valley.Fee)
basicPooled.Add(meter.PooledBasic.Fee)
adjustPooled.Add(meter.PooledAdjust.Fee)
lossAmount.Add(meter.PooledLoss.Amount)
lossPooled.Add(meter.PooledLoss.Fee)
publicPooled.Add(meter.PooledPublic.Fee)
// 反写商户表计的统计数据
meter.Overall.Proportion = func() decimal.Decimal {
if overall.Amount.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.Overall.Amount.Div(overall.Amount)
}()
meter.Critical.Proportion = func() decimal.Decimal {
if critical.Amount.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.Critical.Amount.Div(critical.Amount)
}()
meter.Peak.Proportion = func() decimal.Decimal {
if peak.Amount.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.Peak.Amount.Div(peak.Amount)
}()
meter.Flat.Proportion = func() decimal.Decimal {
if flat.Amount.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.Flat.Amount.Div(flat.Amount)
}()
meter.Valley.Proportion = func() decimal.Decimal {
if valley.Amount.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.Valley.Amount.Div(valley.Amount)
}()
meter.PooledBasic.Proportion = func() decimal.Decimal {
if basicPooled.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.PooledBasic.Fee.Div(basicPooled)
}()
meter.PooledAdjust.Proportion = func() decimal.Decimal {
if adjustPooled.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.PooledAdjust.Fee.Div(adjustPooled)
}()
meter.PooledLoss.Proportion = func() decimal.Decimal {
if lossPooled.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.PooledLoss.Fee.Div(lossPooled)
}()
meter.PooledPublic.Proportion = func() decimal.Decimal {
if publicPooled.Equal(decimal.Zero) {
return decimal.Zero
}
return meter.PooledPublic.Fee.Div(publicPooled)
}()
var OverallProportion decimal.Decimal
if summary.Overall.Amount == decimal.Zero {
OverallProportion = decimal.Zero
} else {
OverallProportion = decimal.NewFromFloat(overall.Amount.InexactFloat64() / summary.Overall.Amount.InexactFloat64())
}
var CriticalProportion decimal.Decimal
if summary.Critical.Amount == decimal.Zero {
CriticalProportion = decimal.Zero
} else {
CriticalProportion = decimal.NewFromFloat(critical.Amount.InexactFloat64() / summary.Critical.Amount.InexactFloat64())
}
var PeakProportion decimal.Decimal
if summary.Peak.Amount == decimal.Zero {
PeakProportion = decimal.Zero
} else {
PeakProportion = decimal.NewFromFloat(peak.Amount.InexactFloat64() / summary.Peak.Amount.InexactFloat64())
}
var FlatProportion decimal.Decimal
if summary.Flat.Amount == decimal.Zero {
FlatProportion = decimal.Zero
} else {
FlatProportion = decimal.NewFromFloat(flat.Amount.InexactFloat64() / summary.Flat.Amount.InexactFloat64())
}
var ValleyProportion decimal.Decimal
if summary.Valley.Amount == decimal.Zero {
ValleyProportion = decimal.Zero
} else {
ValleyProportion = decimal.NewFromFloat(valley.Amount.InexactFloat64() / summary.Valley.Amount.InexactFloat64())
}
tenementCharge := calculate.TenementCharge{
Tenement: tCode,
Overall: model.ConsumptionUnit{
Price: summary.Overall.Price,
Proportion: OverallProportion,
},
Critical: model.ConsumptionUnit{
Price: summary.Critical.Price,
Proportion: CriticalProportion,
},
Peak: model.ConsumptionUnit{
Price: summary.Overall.Price,
Proportion: PeakProportion,
},
Flat: model.ConsumptionUnit{
Price: summary.Overall.Price,
Proportion: FlatProportion,
},
Valley: model.ConsumptionUnit{
Price: summary.Overall.Price,
Proportion: ValleyProportion,
},
BasicFee: basicPooled,
AdjustFee: adjustPooled,
LossPooled: lossPooled,
PublicPooled: publicPooled,
FinalCharges: decimal.NewFromFloat(
overall.Fee.InexactFloat64() + basicPooled.InexactFloat64() +
adjustPooled.InexactFloat64() + lossPooled.InexactFloat64() +
publicPooled.InexactFloat64()),
Submeters: nil,
Poolings: nil,
}
tc = append(tc, tenementCharge)
}
}
return tc
}

141
service/calculate/utils.go Normal file
View File

@@ -0,0 +1,141 @@
package calculate
import (
"electricity_bill_calc/model"
"electricity_bill_calc/model/calculate"
"errors"
"fmt"
"github.com/shopspring/decimal"
)
// 计算两个读书之间的有功(总)电量
func ComputeOverall(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) {
start := startReading.Overall.InexactFloat64() * startReading.Ratio.InexactFloat64()
end := endReading.Overall.InexactFloat64() * endReading.Ratio.InexactFloat64()
if start > end {
return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("表计 {%s} 有功(总)开始读数 {%x} 大于结束读数 {%x}", startReading.Meter, start, end))
}
amount := end - start
var summaryAmount float64
if summary.Overall.Amount == decimal.Zero {
summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64()
} else {
summaryAmount = summary.Overall.Amount.InexactFloat64()
}
return model.ConsumptionUnit{
Amount: decimal.NewFromFloat(amount),
Fee: decimal.NewFromFloat(amount * summary.Overall.Price.InexactFloat64()),
Price: decimal.NewFromFloat(summary.Overall.Price.InexactFloat64()),
Proportion: decimal.NewFromFloat(amount / summaryAmount),
}, nil
}
//计算两个读书之间的尖峰电量
func ComputeCritical(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) {
start := startReading.Critical.InexactFloat64() * startReading.Ratio.InexactFloat64()
end := endReading.Critical.InexactFloat64() * endReading.Ratio.InexactFloat64()
if start > end {
return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("尖峰开始读数 {%x} 大于结束读数 {%x}", start, end))
}
amount := end - start
var summaryAmount float64
if summary.Critical.Amount.Equal(decimal.Zero) {
summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64()
} else {
summaryAmount = summary.Critical.Amount.InexactFloat64()
}
return model.ConsumptionUnit{
Amount: decimal.NewFromFloat(amount),
Fee: decimal.NewFromFloat(amount * summary.Critical.Amount.InexactFloat64()),
Price: decimal.NewFromFloat(summary.Critical.Price.InexactFloat64()),
Proportion: decimal.NewFromFloat(amount / summaryAmount),
}, nil
}
// 计算两个读数之间的峰电量
func ComputePeak(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) {
start := startReading.Peak.InexactFloat64() * startReading.Ratio.InexactFloat64()
end := startReading.Peak.InexactFloat64() * endReading.Ratio.InexactFloat64()
if start > end {
return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("峰开始读数 {%x} 大于结束读数 {%x}", start, end))
}
amount := end - start
var summaryAmount float64
if summary.Peak.Amount.Equal(decimal.Zero) {
summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64()
} else {
summaryAmount = summary.Peak.Amount.InexactFloat64()
}
return model.ConsumptionUnit{
Amount: decimal.NewFromFloat(amount),
Fee: decimal.NewFromFloat(amount * summary.Peak.Price.InexactFloat64()),
Price: decimal.NewFromFloat(summary.Peak.Price.InexactFloat64()),
Proportion: decimal.NewFromFloat(amount / summaryAmount),
}, nil
}
//计算两个读数之间的平电量
func ComputeFlat(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) {
start := startReading.Flat.InexactFloat64() * startReading.Ratio.InexactFloat64()
end := endReading.Flat.InexactFloat64() * endReading.Ratio.InexactFloat64()
if start > end {
return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("平开始读数 {%x} 大于结束读数 {%x}", start, end))
}
amount := end - start
var summaryAmount float64
if summary.Flat.Amount.Equal(decimal.Zero) {
summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64()
} else {
summaryAmount = summary.Flat.Amount.InexactFloat64()
}
return model.ConsumptionUnit{
Amount: decimal.NewFromFloat(amount),
Fee: decimal.NewFromFloat(amount * summary.Flat.Price.InexactFloat64()),
Price: decimal.NewFromFloat(summary.Flat.Price.InexactFloat64()),
Proportion: decimal.NewFromFloat(amount / summaryAmount),
}, nil
}
//计算两个读数之间的谷电量
func ComputeValley(startReading model.MeterReading, endReading model.MeterReading, summary calculate.Summary) (model.ConsumptionUnit, error) {
start := startReading.Valley.InexactFloat64() * startReading.Ratio.InexactFloat64()
end := endReading.Valley.InexactFloat64() * endReading.Ratio.InexactFloat64()
if start > end {
return model.ConsumptionUnit{}, errors.New(fmt.Sprintf("谷开始读数 {%x} 大于结束读数 {%x}", start, end))
}
amount := end - start
var summaryAmount float64
if summary.Valley.Amount.Equal(decimal.Zero) {
summaryAmount = decimal.NewFromFloat(1.0).InexactFloat64()
} else {
summaryAmount = summary.Valley.Amount.InexactFloat64()
}
return model.ConsumptionUnit{
Amount: decimal.NewFromFloat(amount),
Fee: decimal.NewFromFloat(amount * summary.Valley.Price.InexactFloat64()),
Price: decimal.NewFromFloat(summary.Valley.Price.InexactFloat64()),
Proportion: decimal.NewFromFloat(amount / summaryAmount),
}, nil
}

View File

@@ -0,0 +1,168 @@
package calculate
import (
"electricity_bill_calc/global"
"electricity_bill_calc/model/calculate"
"electricity_bill_calc/repository"
"fmt"
)
func MainCalculateProcess(rid string) {
report, err := repository.ReportRepository.GetReportIndex(rid)
if err != nil {
fmt.Println("1", err.Error()+"指定报表不存在")
return
}
reportSummary, err := repository.ReportRepository.RetrieveReportSummary(rid)
if err != nil {
fmt.Println("2", err.Error()+"指定报表的基本电量电费数据不存在")
return
}
summary := calculate.FromReportSummary(reportSummary, report)
periodStart := report.Period.SafeLower()
periodEnd := report.Period.SafeUpper()
meterDetails, err := repository.MeterRepository.AllUsedMetersInReport(report.Id)
if err != nil {
fmt.Println("3", err)
return
}
meterRelations, err := repository.CalculateRepository.GetAllPoolingMeterRelations(report.Park, periodStart.Time)
if err != nil {
fmt.Println("4", err)
return
}
_, err = CheckMeterArea(report, meterDetails)
if err != nil {
fmt.Println("5", err)
return
}
// 寻找每一个商户的所有表计读数,然后对分配到各个商户的表计读数进行初步的计算.
tenementReports, err := TenementMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("6", err)
return
}
// 取得所有公摊表计的读数,以及公摊表计对应的分摊表计
poolingMetersReports, err := PooledMetersCalculate(report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("7", err)
return
}
// 获取所有的物业表计,然后对所有的物业表计电量进行计算。
parkMetersReports, err := MetersParkCalculate(*report, periodStart.Time, periodEnd.Time, meterDetails, summary)
if err != nil {
fmt.Println("8", err)
return
}
// 计算所有表计的总电量
parkTotal := TotalConsumptionCalculate(tenementReports, summary)
// 计算线损以及调整线损
err = LossCalculate(report, &parkMetersReports, &parkTotal, &summary)
if err != nil {
fmt.Println("9", err)
return
}
// 计算所有已经启用的商铺面积总和,仅计算所有未迁出的商户的所有表计对应的商铺面积。
_, err = EnabledAreaCalculate(&tenementReports, &summary)
if err != nil {
fmt.Println("10", err)
return
}
err = CalculatePrices(&summary)
if err != nil {
fmt.Println("11", err)
return
}
//===========================================================================
// 计算基本电费分摊、调整电费分摊、电费摊薄单价。
err = CalculatePrices(&summary)
// 收集目前所有已经处理的表计,统一对其进行摊薄计算。
meters, err := CollectMeters(tenementReports, poolingMetersReports, parkMetersReports)
if err != nil {
fmt.Println("12", err)
return
}
// 计算商户的合计电费信息,并归总与商户相关联的表计记录
tenementCharges := TenementChargeCalculate(tenementReports, summary, meters)
// 根据核算报表中设置的摊薄内容,逐个表计进行计算
err = CalculateBasicPooling(report, &summary, &meters)
if err != nil {
fmt.Println("13", err)
return
}
err = CalculateAdjustPooling(*report, summary, meters)
if err != nil {
fmt.Println("14", err)
return
}
err = CalculateLossPooling(*report, summary, meters)
if err != nil {
fmt.Println("15", err)
return
}
// 计算所有商户类型表计的全周期电量,并根据全周期电量计算共用过同一表计的商户的二次分摊比例。
_, err = CalculateTenementConsumptions(meters)
if err != nil {
fmt.Println("16", err)
return
}
err = CalculateTenementPoolings(*report, summary, meters, meterRelations)
if err != nil {
fmt.Println("17", err)
return
}
// 计算商户的合计电费信息,并归总与商户相关联的表计记录
tenementCharges = TenementChargeCalculate(tenementReports, summary, meters)
// 从此处开始向数据库保存全部计算结果。
ctx, cancel := global.TimeoutContext()
defer cancel()
tx, _ := global.DB.Begin(ctx)
err = repository.CalculateRepository.ClearReportContent(tx, report.Id)
if err != nil {
tx.Rollback(ctx)
fmt.Println("18", err)
}
err = SaveSummary(tx, summary)
if err != nil {
tx.Rollback(ctx)
fmt.Println("19", err)
}
err = SavePublics(tx, *report, meters)
if err != nil {
tx.Rollback(ctx)
fmt.Println("20", err)
}
err = SavePoolings(tx, *report, meters, meterRelations)
if err != nil {
tx.Rollback(ctx)
fmt.Println("21", err)
}
err = SaveTenements(tx, *report, tenementReports, tenementCharges)
if err != nil {
tx.Rollback(ctx)
fmt.Println("22", err)
}
tx.Commit(ctx)
}