"""A unit's measures (PRD-28): weights, volume and dimensions, per ONE of the unit, each in a unit of the right kind. Exact decimals;
an unset measure is None (unknown), never 0."""
from decimal import Decimal
from typing import Optional
from brick_product.domain.errors import RuleBroken
LIMIT = Decimal("1e12") # NUMERIC(19,6): a measure is below a trillion
WEIGHTS, VOLUMES, DIMENSIONS = ("gross_weight", "net_weight"), ("volume",), ("length", "width", "height")
# (the measures, the unit column that states them, the kind that unit must be)
GROUPS = ((WEIGHTS, "weight_unit_code", "mass"), (VOLUMES, "volume_unit_code", "volume"), (DIMENSIONS, "dimension_unit_code", "length"))
def check(values: dict, kinds: dict) -> None:
"""`values`: the whole row (measure fields, None for unknown). `kinds`: unit_code -> kind for every unit named. Raises RuleBroken
naming what is wrong: a negative or oversize number, a measure with no unit, a unit of the wrong kind, a net above the gross."""
for fields, unit_field, kind in GROUPS:
given = [f for f in fields if values.get(f) is not None]
unit: Optional[str] = values.get(unit_field)
for f in given:
v = values[f]
if not (Decimal(0) <= v < LIMIT):
raise RuleBroken(f"{f} is zero or more, and below 1,000,000,000,000")
if v.as_tuple().exponent < -6:
raise RuleBroken(f"{f} has at most 6 decimal places")
if given and not unit:
raise RuleBroken(f"give the unit of {', '.join(given)} ({unit_field}): a unit of {kind}")
if unit and kinds.get(unit) != kind:
raise RuleBroken(f"{unit} is a unit of {kinds.get(unit) or 'unknown kind'}, not of {kind}: {unit_field} must be a unit of {kind}")
g, n = values.get("gross_weight"), values.get("net_weight")
if g is not None and n is not None and n > g:
raise RuleBroken(f"the net weight ({n}) is above the gross weight ({g})")