Files
go-cart-actor/cmd/cart/projection_overlay.go
T
mats aa8b2bdedc
Build and Publish / BuildAndDeployAmd64 (push) Failing after 5s
Build and Publish / BuildAndDeployArm64 (push) Failing after 6s
all the refactor
2026-06-28 17:51:52 +02:00

98 lines
4.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
messages "git.k6n.net/mats/go-cart-actor/proto/cart"
"git.k6n.net/mats/platform/catalog"
)
// taxClassToBp maps the canonical TaxClass string identifiers published on
// catalog.Projection to basis-points-of-a-percent (rate × 100) — the single
// platform-wide rate scale used by AddItem.Tax (int32 basis points), the
// mutation registry, and the tax.Provider Compute formula.
//
// Values reflect the standard tariff names used by the Nordic configuration.
// Per-country / per-tenant numeric resolution (e.g. Finland 25.5%, Ireland
// 13.5%) is delegated to platform/tax and is a follow-up: this static map
// gets the common case right (2500 / 1200 / 600 / 0) and lets the cache path
// ship while the platform/tax lookup is wired into the cart pool server.
var taxClassToBp = map[string]int{
"standard": 2500,
"reduced": 1200, // common reduced rate (e.g. SE/NO food)
"lowered": 600, // super-reduced
"zero": 0, // zero-rated (export, healthcare, ...)
"exempt": 0, // similarly untaxed, separate nominal class
}
// resolveTaxBp returns the basis-point rate for a TaxClass string, falling
// back to platform "standard" (25%) when the class is unrecognised. The
// default matches the grain's existing behaviour (AddItem falls back to 2500
// when m.Tax == 0 — see pkg/cart/mutation_add_item.go).
func resolveTaxBp(class string) int {
if bp, ok := taxClassToBp[class]; ok {
return bp
}
return 2500
}
// ApplyProjectionOverlay overlays the cache's authoritative catalog facts onto
// an AddItem message that was just produced by a PRODUCT_BASE_URL HTTP fetch.
//
// Authoritative-from-cache fields (these WIN over the HTTP-fetched value when
// both are present, because the bus is the live stream):
//
// Price (bus has post-class-overrides; product service has static)
// Tax (TaxClass → bp via resolveTaxBp; only when TaxClass is set —
// an empty TaxClass means the producer didn't classify,
// so we leave the HTTP-fetched rate intact rather than
// quietly swapping in a default 2500 and miscategorising)
// Title (canonical, post-trim)
// Image (canonical)
// InventoryTracked (only flips to true; false is the zero value in proto
// and may be unset on the wire, so we don't second-guess)
// DropShip (only flips to true; same reasoning)
//
// The HTTP-fetched values remain authoritative for fields the cache does not
// carry: SellerId / SellerName (marketplace split), Stock (per docs/inventory-
// shape.md stock is queried synchronously from inventory, not cached),
// OrgPrice / Discount (not yet on the projection schema), the dynamic
// ExtraJson product data (configurator options — width/height used by
// accessory child-pricing), and the catalog uint32 ItemId (the product
// service's integer id which messages.AddItem.ItemId dedupes on — Projection
// only carries the opaque string ID, so a cache-only build path is blocked
// and the HTTP fetch is preserved).
//
// Tombstoned SKUs (catalog.projection_published with deleted=true) → the
// caller checks idx.IsDeleted(sku) before this helper and short-circuits.
func ApplyProjectionOverlay(msg *messages.AddItem, p catalog.Projection) {
if msg == nil {
return
}
// Price: positive bus values override HTTP. A zero PriceIncVat is treated
// as "no value" rather than "free".
if p.PriceIncVat.Int64() > 0 {
msg.Price = p.PriceIncVat.Int64()
}
// Tax: only override when the bus has classified the SKU. An empty TaxClass
// means the producer had nothing to say about VAT; the HTTP rate is more
// trustworthy in that case than a guessed 2500.
if p.TaxClass != "" {
msg.Tax = int32(resolveTaxBp(p.TaxClass))
}
// Display fields: only override if non-empty so the HTTP-fanned value
// remains in place when the cache hasn't populated them yet.
if p.Title != "" {
msg.Name = p.Title
}
if p.Image != "" {
msg.Image = p.Image
}
// Flags: only flip to true (positive boolean signals). A false is the
// proto-zero value and not worth overriding.
if p.InventoryTracked {
msg.InventoryTracked = true
}
if p.DropShip {
msg.DropShip = true
}
}