dodil/erp-brick-manufacturing
DODIL ERP brick: manufacturing
src/brick_manufacturing/domain/settlement.py
"""Settlement (MFG-15): the order's WIP balance split into its variances, exactly.
Pure arithmetic over figures inventory already valued: nothing here reads a price. Material standard for what came out is
the planned quantity of each component, at the unit cost inventory valued the issue at, scaled to the output reported (so
normal roasting loss, which is in the BOM's planned yield, is NOT a variance; only output below plan is). The split always
sums to the balance: the category that takes the rounding is the last (activity). Scrap (MFG-14) is output that was MADE but never
received: it counts toward the standard material (so the material that burnt is not a usage variance) and its whole standard value is
expensed as abnormal scrap (IAS 2.16); only what is left of the balance is a variance.
"""
from dataclasses import dataclass
from decimal import Decimal
from brick_manufacturing.domain.values import money
@dataclass(frozen=True)
class Settled:
wip_debit: Decimal
wip_credit: Decimal
balance: Decimal
standard_material: Decimal
abnormal_scrap: Decimal
material_usage_variance: Decimal
activity_variance: Decimal
@property
def variance(self) -> Decimal:
"""What the approval threshold is compared with: the variances' absolute size, so +300 and -300 do not net to nothing."""
return abs(self.material_usage_variance) + abs(self.activity_variance) + abs(self.abnormal_scrap)
def settle(components: list, *, outputs_value: Decimal, activity_cost: Decimal, output_base: Decimal,
planned_output_base: Decimal, abnormal_scrap: Decimal = Decimal("0"),
scrap_base: Decimal = Decimal("0")) -> Settled:
"""components: [{planned_base, issued_base, issued_value}] per BOM component, sent movements only."""
issues_value = sum((c["issued_value"] for c in components), Decimal("0"))
ratio = (output_base + scrap_base) / planned_output_base if planned_output_base else Decimal("0")
std = Decimal("0")
for c in components:
if c["issued_base"] > 0:
std += c["planned_base"] * (c["issued_value"] / c["issued_base"]) * ratio
std = money(std, "the standard material")
debit, credit = money(issues_value + activity_cost, "the WIP debit"), money(outputs_value, "the WIP credit")
balance = money(debit - credit, "the WIP balance")
abnormal = money(abnormal_scrap, "the abnormal scrap")
usage = money(issues_value - std, "the material usage variance")
activity = balance - usage - abnormal
return Settled(debit, credit, balance, std, abnormal, usage, activity)