dodil/erp-brick-sales-order
DODIL ERP brick: sales order
src/brick_sales_order/infrastructure/ai_steps.py
"""AI steps (the AI-step contract): a step only SUGGESTS. Its output lands in so_suggestion and reaches an order only
when a person accepts it; the owner holds each step's mode (off | suggest) under so:settings:controls, and turning a
step on is recorded as the owner's consent (who, when) in the dated so_settings row.
The brick never calls a model itself. The step runner (the capability's provider, or the order inbox's poller) reads the
PO and hands its output to `ai_order_from_po` / `ai_check_po`; with no provider the fallback is the rep's own work:
the PO attached to an empty draft, or comparing by eye. Tests never call a model.
"""
from brick_sales_order.domain.values import AI_KEYS
AI_STEPS = {
"ai_order_from_po": {
"mode": "off", "modes_allowed": ["off", "suggest"], "safe_for_auto": False,
"fallback": "the PO is attached to an empty draft for the rep",
"benefit": "a customer's PO by email becomes a checked draft in seconds instead of ten minutes of typing",
"audit": "so_suggestion: model, the PO's object key, the output, and who accepted, changed or rejected what, when",
},
"ai_check_po": {
"mode": "off", "modes_allowed": ["off", "suggest"], "safe_for_auto": False,
"fallback": "the rep compares by eye",
"benefit": "a PO that quietly changes a price or a quantity is caught before confirmation, not at invoicing",
"audit": "so_suggestion, as for ai_order_from_po",
},
}
def mode_on(settings, step: str, day) -> str:
"""The step's mode in force on a date: the owner's dated control row, else off."""
return settings.value_on(AI_KEYS[step], day) or "off"