dodil/erp-brick-inventory
DODIL ERP brick: inventory
src/brick_inventory/domain/units.py
"""Showing a quantity in the packs people handle: 5 PK24 and 6 C62 (94.5 LTR), the warehouse unit first.
A unit's factor is the base-unit quantity of ONE of it (numerator / denominator). Everything is exact `Fraction`; a remainder
that no listed unit divides is shown in the base unit.
"""
from decimal import Decimal
from fractions import Fraction
from brick_inventory.domain.values import fraction, round_half_up
def factor(row: dict) -> Fraction:
return Fraction(int(row["numerator"]), int(row["denominator"]))
def breakdown(quantity_base: Decimal, base_unit: str, units: list) -> list:
"""[(quantity, unit_code)] greedy over `units` (rows with unit_code, numerator, denominator, quantity_decimals), largest
first as given; whole counts only for a unit that takes no decimals; the rest in the base unit."""
left = fraction(quantity_base)
out = []
for u in units:
f = factor(u)
if f <= 0 or left <= 0 or f == 1 and u["unit_code"] == base_unit:
continue
whole = int(left // f)
if whole >= 1:
out.append((Decimal(whole), u["unit_code"]))
left -= whole * f
if left > 0 or not out:
out.append((round_half_up(left, 4).normalize() if left else Decimal(0), base_unit))
return out
def express(quantity_base: Decimal, row: dict) -> dict:
"""The quantity in one unit, exact: {quantity (to the unit's decimals), exact "n/d", unit_code}."""
q = fraction(quantity_base) / factor(row)
places = int(row.get("quantity_decimals") if row.get("quantity_decimals") is not None else 4)
return {"quantity": str(round_half_up(q, places)), "exact": f"{q.numerator}/{q.denominator}", "unit_code": row["unit_code"]}