Documentation
¶
Overview ¶
Package adjustments resolves the pricing adjustments a project's relations carry and applies them to the rated amounts of its statement. New parses the graph of one run, Adjust applies it to one statement. Both are pure: the package does no I/O.
The resolution is a breadth-first walk from the statement's project over the outgoing adjustment-carrying relations, bounded by the depth the caller configures (decision D6). Every relation is visited once, so a relation two paths reach adjusts once, and a cycle among the relations ends on the visited set. The relations are taken as given: the caller loads the ones whose validity overlaps the period, and each of them applies to the whole period (decision D4).
The collected adjustments apply in the order surcharge, discount, project discount, kickback (decision D3), ties broken by relation id and then by the position of the element in its relation's array. A surcharge is computed on the base, so two surcharges add rather than compound. A discount and a project discount are computed on the running net, so they stack multiplicatively. A kickback is computed on the running net and leaves it alone: it is what a partner is owed rather than what the customer pays.
The "running net per scope partition" of the roadmap's pseudocode is read here as one running amount per (platform, resource type) bucket of the bases. An adjustment covers the buckets its scope matches (decision D5, through adjustment.ScopeMatches), and its line is rounded once, by money.Round2 (decision D7). That rounded amount is then apportioned back over the buckets it covers, every bucket but the last taking its own rounded share and the last taking the remainder, so the shares sum to the line exactly and every bucket stays a two-place amount.
A kickback whose relation target is not a partner is skipped, and New reports it as WarningKickbackTargetNotPartner. A commission owed to a project that is not a partner entity is a mistake in the registry rather than an intent to bill, and the run says so instead of paying it.
The normative specification is roadmap/05-phase-5-commercial-pricing.md, WP 5.3.
Index ¶
Constants ¶
const WarningKickbackTargetNotPartner = "adjustment_kickback_target_not_partner"
WarningKickbackTargetNotPartner marks a relation whose kickbacks were not applied because its target is not a partner.
Variables ¶
This section is empty.
Functions ¶
func New ¶
func New(relations []source.Relation, projects []source.Project, depth int) (*Adjuster, []Warning, error)
New parses the adjustments of every relation once per run, rather than once per statement. The relations are expected in ascending id order, the order source.Snapshot.Relations returns them in, and the edges keep it.
A depth below 1 is an error: a walk that does not take its first level reads no relation at all, and asking for that is a mistake rather than a way to turn adjustments off (an empty relation type list is that).
A relation whose pricing_adjustments member the schema refuses is refused as well, because a relation whose stored array cannot be read must not bill as though it carried nothing. It is refused by the Adjust of the first statement whose walk reaches that relation rather than here: project_relations.metadata is a free-form document one tenant writes, and a stale one of a project this run bills nothing from would otherwise fail every run and every tick of the whole deployment. Metadata without the member is an edge that carries nothing.
Both ends of every relation have to be among the projects, which the registry's foreign keys and the snapshot already hold; the check is here because a line names its target's external id.
The kickbacks of a relation whose target is not a partner are dropped, one warning per relation, and the other adjustments of that relation stay. The warnings come back in the order the relations were given.
Types ¶
type Adjuster ¶
type Adjuster struct {
// contains filtered or unexported fields
}
Adjuster holds the parsed adjustment graph of one run.
func (*Adjuster) Adjust ¶
Adjust applies to one project's rated amounts what the walk from it collects. A project no relation names, or a project whose relations carry nothing, comes back as its base cost with no lines. The one error is a relation the walk reaches whose stored adjustments could not be read, which New kept for this call: such a statement is not billed at all rather than billed as though the relation carried nothing.
The bases are summed per bucket, and an adjustment applies to the buckets its scope covers. Bases of more than one platform are what an attributed project's related costs bring in, and they are adjusted along with the project's own.
type Base ¶
type Base struct {
// Platform and ResourceType are what an adjustment's scope is matched
// against.
Platform, ResourceType string
// Amount is what the line was rated at.
Amount decimal.Decimal
}
Base is one rated line of a statement, own or related: what the scoped sums are built from.
type Line ¶
type Line struct {
Type string `json:"type"`
RelationType string `json:"relation_type"`
// RelationTarget is the external id of the relation's target, which is the
// beneficiary of a kickback.
RelationTarget string `json:"relation_target"`
RelationID string `json:"relation_id"`
Scope string `json:"scope"`
Description string `json:"description,omitempty"`
Rate money.Rate `json:"rate"`
// Base is the amount the rate was applied to.
Base money.Amount `json:"base"`
// Amount is signed: a discount is negative.
Amount money.Amount `json:"amount"`
}
Line is one applied adjustment as the document renders it and a record stores it. The field order is the order it is marshalled in.
type Outcome ¶
type Outcome struct {
// BaseCost is the sum of the bases, the total Phase 3 rated.
BaseCost decimal.Decimal
// Lines holds every collected adjustment in application order, a zero one
// included, so the document shows everything that reached the statement. It
// is nil when the walk collected nothing.
Lines []Line
// NetCost is BaseCost plus every non-kickback amount: what the customer
// pays.
NetCost decimal.Decimal
// KickbackTotal is the sum of the kickback amounts, which NetCost does not
// hold.
KickbackTotal decimal.Decimal
}
Outcome is what the adjustments of one statement come to.
type Warning ¶
type Warning struct {
// Code is WarningKickbackTargetNotPartner.
Code string `json:"code"`
// RelationID is the relation whose kickbacks were dropped, as text.
RelationID string `json:"relation_id"`
// TargetPlatform is the platform of that relation's target, which is what
// the kickbacks were dropped over.
TargetPlatform string `json:"target_platform"`
// TargetID is the external id of that relation's target.
TargetID string `json:"target_id"`
}
Warning is one finding of the resolution. It is JSON-tagged because the run writes the warnings into runs.stats verbatim.