dodil/erp-brick-tax
DODIL ERP brick: tax
src/brick_tax/domain/errors.py
"""The refusals a rule makes. The use cases turn them into a 409 with these words, through both doors.
A refused transition returns its unmet gates (shared.yaml conventions.readiness): `GateUnmet` carries them as
{gate, met, reason, fix}, so a screen, an agent and a selling brick all read the same list and never re-derive a rule.
"""
import json
from dataclasses import dataclass
@dataclass(frozen=True)
class Gate:
"""One precondition, answered: a stable id, whether it is met, why not (in the customer's words), and the fix."""
gate: str
met: bool
reason: str
fix: str
def as_dict(self) -> dict:
return {"gate": self.gate, "met": self.met, "reason": self.reason, "fix": self.fix}
class RuleBroken(Exception):
"""A business rule said no. The message is written for the person who tried."""
class GateUnmet(RuleBroken):
"""A transition refused because gates are unmet. `gates` are only the unmet ones, each with met=False."""
def __init__(self, gates):
self.gates = list(gates)
super().__init__(self.gates[0].reason if self.gates else "refused")
def text(self) -> str:
"""The refusal as one string both doors can carry: the first reason, then every unmet gate as JSON."""
return f"{self.gates[0].reason} — unmet gates: {json.dumps([g.as_dict() for g in self.gates])}"
def unmet(gate: str, reason: str, fix: str) -> GateUnmet:
return GateUnmet([Gate(gate, False, reason, fix)])