dodil/erp-brick-manufacturing

DODIL ERP brick: manufacturing

src/brick_manufacturing/domain/costing.py

"""The standard cost roll-up (MFG-06): a product's cost per base unit from its BOM, its routing and its components' prices.

(material + conversion - by-product value) / output, divided ONCE at the end; the material and conversion per unit shown beside it are
each rounded on their own, for variance analysis. A made component costs what ITS roll-up says; anything else costs what inventory
holds. Pure: the use case reads the prices and hands them in.
"""
from decimal import Decimal

from brick_manufacturing.domain.errors import RuleBroken
from brick_manufacturing.domain.values import q4


def cost_of(lines: list, output_base: Decimal, price_of, conversion: Decimal) -> dict:
    """lines: the BOM version's lines (dicts with kind, product_id, quantity_base). price_of(product_id) -> Decimal per base unit."""
    if output_base <= 0:
        raise RuleBroken("A BOM that makes nothing has no cost", gate="bom_valid")
    material = sum((Decimal(x["quantity_base"]) * price_of(x["product_id"]) for x in lines if x["kind"] == "component"), Decimal("0"))
    credit = sum((Decimal(x["quantity_base"]) * Decimal(x["nrv_unit_cost"] or 0) for x in lines if x["kind"] == "by_product"), Decimal("0"))
    return {"material": q4(material / output_base), "conversion": q4(conversion / output_base),
            "by_product_credit": q4(credit / output_base), "total": q4((material + conversion - credit) / output_base)}