"""Which customer order a project or unit takes its customer, PO and dates from, when VISUAL has several. The one rule, in order: 1. a Clerical pin that names one of the project's orders (`pin`); 2. the base's own linked order, the first of `by_base[base]` (`base`); 3. the project order whose top jobs name the unit, open orders first (`unit_top_job`); 4. the project's first open order, status F, H or R (`project_open`); 5. the project's first order, the newest (`project_newest`). `derive` maps the chosen order to the fields a project or unit keeps: customer, po, order_date, ship_date. """ from __future__ import annotations from collections.abc import Mapping from dataclasses import dataclass from datetime import date, datetime from typing import Any from .types import Order OPEN_STATUSES = ("F", "H", "R") VIA = ("pin", "base", "unit_top_job", "project_open", "project_newest") FIELDS = ("customer", "po", "order_date", "ship_date") @dataclass(frozen=True) class Chosen: order: Order via: str # pin | base | unit_top_job | project_open | project_newest def _is_open(o: Order) -> bool: return (o.status or "").strip().upper() in OPEN_STATUSES def choose( *, unit_number: str, base: str, by_base: Mapping[str, list[Order]], by_project: Mapping[str, list[Order]], project: str, pin: str | None, ) -> Chosen | None: proj = list(by_project.get(project) or []) own = list(by_base.get(base) or []) if base else [] if pin: p = pin.strip().upper() for o in [*proj, *own]: if o.cust_order_id.strip().upper() == p: return Chosen(o, "pin") if own: return Chosen(own[0], "base") if unit_number: u = unit_number.upper() hits = [o for o in proj if any(t.upper() == u for t in o.top_jobs)] hits.sort(key=lambda o: not _is_open(o)) # stable: open first, the list's own order within if hits: return Chosen(hits[0], "unit_top_job") for o in proj: if _is_open(o): return Chosen(o, "project_open") if proj: return Chosen(proj[0], "project_newest") return None def derive(order: Order) -> dict[str, Any]: """customer <- customer_name, po <- customer_po_ref, order_date, ship_date <- promise or desired ship date.""" return { "customer": order.customer_name, "po": order.customer_po_ref, "order_date": order.order_date, "ship_date": order.promise_date or order.desired_ship_date, } def _iso(v: date | None) -> str | None: return v.isoformat() if v else None def order_ref(chosen: Chosen, at: datetime) -> dict[str, Any]: """The jsonb kept on projects.order_ref and units.order_ref.""" o = chosen.order return { "cust_order_id": o.cust_order_id, "via": chosen.via, "status": o.status, "customer_name": o.customer_name, "customer_po_ref": o.customer_po_ref, "order_date": _iso(o.order_date), "desired_ship_date": _iso(o.desired_ship_date), "promise_date": _iso(o.promise_date), "chosen_at": at.isoformat(), } def same_ref(a: Mapping[str, Any] | None, b: Mapping[str, Any] | None) -> bool: """Two order_refs are the same when everything but chosen_at matches.""" if a is None or b is None: return a is None and b is None return {k: v for k, v in a.items() if k != "chosen_at"} == {k: v for k, v in b.items() if k != "chosen_at"}