corrections

package
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 2, 2026 License: Apache-2.0 Imports: 14 Imported by: 0

Documentation

Overview

Package corrections is the arithmetic and the rendering of a correction run: the amounts one metering and rating pass produced per key, the differences between two such passes, and the credit note every affected project is handed for them.

The package is pure. It reads nothing and writes nothing, the way rating and statements read and write nothing, so a period is diffed from the results a caller already holds. The key the amounts are diffed by is decision D6's: the cloud, platform, resource type and resource id of the resource, the project that owned it, and the dimension it was billed on. The grouping of the deltas into documents is the one statements.Build applies to rated records, over deltas instead: a project's own resources are its line items, and every project attributed to it arrives as a related cost beside them.

Adjustments are neither a resource nor a dimension, so the adjustment records of the two passes are diffed on a key of their own: the statement they were applied to, the relation they came from, and the type, the scope and the rate of the element. Their differences render on the credit note beside the rated deltas, and the note's total is what the correction settles after them (roadmap/05-phase-5-commercial-pricing.md, WP 5.3).

The orchestration is elsewhere. The run row that carries the correction, the period lock it runs under, and the writing of the deltas and the credit notes live in internal/engine/runs.

The normative specification is roadmap/03-phase-3-metering-rating.md, WP 3.9.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AdjustmentAmounts

func AdjustmentAmounts(sts []statements.Statement) map[AdjustmentKey]AdjustmentAmount

AdjustmentAmounts sums what the adjustments of one pass came to, per key. A statement carries its applied adjustments beside its total, and two elements of one relation that agree on type, scope and rate are summed under one key.

A pass whose statements carry no adjustments yields an empty map, which is what a period that adjusted nothing is diffed as: every key of the other side becomes a delta.

func Amounts

func Amounts(usage []metering.ResourceUsage, rated rating.Result) (map[Key]decimal.Decimal, error)

Amounts sums what one pass rated, per key. usage and rated are index-aligned per resource the way rating produced them: record j of a rated resource rates draft j of the same resource in usage, and the project that draft carried owns the amounts of the record. A rated resource that usage does not hold, and one whose record count differs from its draft count, are errors rather than sums built from whatever lines up: an amount filed under the wrong project is a delta against a line that never existed.

Every amount was rounded where it was rated, and the sums here are never rounded again (roadmap/00-conventions.md section 6). A dimension that cost nothing is summed all the same, so the two passes are diffed over every dimension they were held against rather than over the ones that cost money.

A pass with no rated resources yields an empty map, which is what a period that billed nothing is diffed as: every key of the other side becomes a delta.

Types

type AdjustmentAmount

type AdjustmentAmount struct {
	RelationType, RelationTarget string
	// RateValue is AdjustmentKey.Rate as the decimal it was rendered from, which
	// is what a credit-note line writes.
	RateValue decimal.Decimal
	Amount    decimal.Decimal
}

AdjustmentAmount is one side of the adjustment diff: what the records under one key add up to, the relation they came from and the rate they were applied at. The relation and the rate travel with the amount so a delta names them without the statements being read again.

type AdjustmentChange

type AdjustmentChange struct {
	Type           string       `json:"type"`
	RelationType   string       `json:"relation_type"`
	RelationTarget string       `json:"relation_target"`
	RelationID     string       `json:"relation_id"`
	Scope          string       `json:"scope"`
	Rate           money.Rate   `json:"rate"`
	Old            money.Amount `json:"old"`
	New            money.Amount `json:"new"`
	Delta          money.Amount `json:"delta"`
}

AdjustmentChange is one adjustment on the credit note: the relation it came from, what the run being corrected applied, what the correction applied, and the difference the project is credited or debited for. The field order is the order it is marshalled in.

type AdjustmentDelta

type AdjustmentDelta struct {
	AdjustmentKey
	RelationType, RelationTarget string
	// RateValue is AdjustmentKey.Rate as a decimal, the way AdjustmentAmount
	// carries it.
	RateValue       decimal.Decimal
	Old, New, Delta decimal.Decimal
}

AdjustmentDelta is one non-zero difference between the adjustments of the two passes. Delta is New minus Old, the way Delta.Delta is, and it carries the sign of the amounts it is taken between: a discount the correction applies less of comes out positive, which is money the project owes back.

func DiffAdjustments

func DiffAdjustments(old, current map[AdjustmentKey]AdjustmentAmount) []AdjustmentDelta

DiffAdjustments is what changed between the adjustments of the two passes: one AdjustmentDelta per key they disagree on, sorted by statement key, relation id, the application order of the type (adjustment.TypeOrder), scope and rate. A key one side does not hold counts as zero there, so an adjustment the correction no longer applies is credited back whole and one it applies for the first time is charged whole.

A key the two sides agree on is left out, one both of them applied at zero included. Passes that adjusted the same, two empty ones included, yield nil rather than an empty slice.

type AdjustmentKey

type AdjustmentKey struct {
	StatementKey, RelationID, Type, Scope, Rate string
}

AdjustmentKey is what the adjustment records of two passes are diffed by: the statement the adjustment was applied to, the relation it came from, and the type, the scope and the rate of the element. Two elements of one relation that agree on all three are one adjustment as far as the diff goes, because nothing else on the line tells them apart.

Rate is the six-place text money.Rate renders, the scale the adjustments schema admits, because a decimal is not a map key.

type BuildResult

type BuildResult struct {
	// Statements holds one entry per project that gets a credit note, sorted by
	// Key.
	Statements []statements.Statement
	// Unregistered holds one entry per project id no registry row matched,
	// sorted by cloud and then by project id.
	Unregistered []statements.UnregisteredProject
}

BuildResult is what one rendering pass produced: the credit notes the correction is settled from, and the project ids they were billed under that the registry does not hold.

func BuildCreditNotes

func BuildCreditNotes(
	periodFrom, periodTo time.Time,
	correctsRunID uuid.UUID,
	currency string,
	deltas []Delta,
	adjustmentDeltas []AdjustmentDelta,
	projects []source.Project,
	res attribution.Resolution,
) (BuildResult, error)

BuildCreditNotes renders one credit note per project that owns at least one delta. The grouping is the one statements.Build applies to a period's rated records, over the deltas instead: a project's own resources are its line items, and every project attributed to it arrives as a related cost beside them. A note is stored under the key its project's statement is stored under (statements.Key), so a credit note lands where the document it corrects did.

Of the resolution only Attributed is read, as in statements.Build. A project it does not name is credited under itself, and a project id the registry does not hold is credited standalone under that raw id and named in BuildResult.Unregistered.

An adjustment delta lands on the note of the statement key it names, and creates that note where no rated delta did. Such a note shows what its lines came to before the adjustments, the changes themselves, and what they come to after them; its total is the net delta. A note no adjustment delta reached renders as it does without adjustments at all.

A note's total is the sum of its line items and its related costs, adjusted by the changes it carries, so a project that is owed money reads a negative total. Correcting nothing is not an error: without deltas of either kind there is nothing to credit and no document comes back.

type Change

type Change struct {
	Old   money.Amount `json:"old"`
	New   money.Amount `json:"new"`
	Delta money.Amount `json:"delta"`
}

Change is one dimension of one resource on the note: what the run being corrected billed, what the correction rated, and the difference the project is credited or debited for.

type CreditNote

type CreditNote struct {
	BillingPeriod statements.BillingPeriod `json:"billing_period"`
	ProjectID     string                   `json:"project_id"`
	Platform      string                   `json:"platform"`
	CorrectsRunID string                   `json:"corrects_run_id"`
	LineItems     []LineItem               `json:"line_items"`
	RelatedCosts  []RelatedCost            `json:"related_costs"`
	// BaseDelta is what the line items and the related costs add up to before
	// the adjustments, NetDelta what they come to after them, and KickbackDelta
	// what a partner's commission changed by beside the net rather than as part
	// of it. The four members are nil on a note no adjustment delta reached,
	// whose bytes hold none of them. Total is the net delta where they are
	// there, which is what the correction settles.
	//
	// None of them carries a currency of its own: every amount on the note is in
	// the currency Currency names, the way Total already renders.
	BaseDelta     *money.Amount      `json:"base_delta,omitempty"`
	Adjustments   []AdjustmentChange `json:"adjustments,omitempty"`
	NetDelta      *money.Amount      `json:"net_delta,omitempty"`
	KickbackDelta *money.Amount      `json:"kickback_delta,omitempty"`
	Total         money.Amount       `json:"total"`
	Currency      string             `json:"currency"`
}

CreditNote is one project's credit note: the deltas the correction credits or debits it for, and the run they correct. The field order is the order the document is marshalled in.

type Delta

type Delta struct {
	Key
	Old, New, Delta decimal.Decimal
}

Delta is one non-zero difference between the two passes. Delta is New minus Old: a credit is negative and a debit positive, so usage that turned out smaller than the finalized run billed is money owed back.

func Diff

func Diff(old, current map[Key]decimal.Decimal) []Delta

Diff is what changed between the two passes: one Delta per key they disagree on, sorted by cloud, platform, resource type, resource id, project id, and dimension. A key one side does not hold counts as zero there, so a resource the correction no longer bills is credited its whole amount and one it bills for the first time is debited for it.

A key the two sides agree on is left out, one both of them rated at zero included: a delta of 0.00 is nothing to credit and nothing to invoice. Passes that agree on everything, two empty ones included, yield nil rather than an empty slice.

type Key

type Key struct {
	Cloud, Platform, ResourceType, ResourceID, ProjectID, Dimension string
}

Key is what the amounts of two passes are diffed by (D6): the resource an amount was rated for, the project that owned the resource while it ran, and the dimension it was billed on. Two amounts under one key are the same line of the same invoice, one rated before the late events arrived and one after.

type LineItem

type LineItem struct {
	ResourceType string            `json:"resource_type"`
	ResourceID   string            `json:"resource_id"`
	Platform     string            `json:"platform"`
	Dimensions   map[string]Change `json:"dimensions"`
	Total        money.Amount      `json:"total"`
}

LineItem is one resource's deltas as one project is credited or debited for them: one entry per dimension the two passes disagree on, and what they add up to.

type RelatedCost

type RelatedCost struct {
	RelationType string       `json:"relation_type"`
	ProjectID    string       `json:"project_id"`
	Platform     string       `json:"platform"`
	LineItems    []LineItem   `json:"line_items"`
	Total        money.Amount `json:"total"`
}

RelatedCost is one attributed project's deltas on the credit note of the project they are billed under: the type of the edge that claimed it, who it is, and the same line items it would carry standalone.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL