dodil/erp-brick-inventory

DODIL ERP brick: inventory

src/brick_inventory/application/common.py

"""Who may see what, and the shapes the answers share."""
from decimal import Decimal
from typing import Optional

from erp_platform.bricks import Refused

from brick_inventory.application.unit import can

READ, VALUE, AVAILABILITY = "inv:stock:read", "inv:valuation:read", "inv:availability:read"
MONEY_FIELDS = ("value_amount", "unit_cost", "average_cost", "average_cost_after", "value_after", "value_change", "price_difference",
                "standard_price", "consumption_value", "price", "amount", "total_value", "cost")


def need_read(caller: dict) -> None:
    """Stock is seen by whoever holds inv:stock:read; to anyone else a warehouse, bin, lot or move is a 404, never a 403."""
    if not can(caller, READ):
        raise Refused(404, "not found")


def sees_value(caller: dict) -> bool:
    return can(caller, VALUE)


def without_values(obj):
    """A caller without inv:valuation:read never sees a cost or a value in any answer."""
    if isinstance(obj, dict):
        return {k: without_values(v) for k, v in obj.items() if k not in MONEY_FIELDS}
    if isinstance(obj, list):
        return [without_values(v) for v in obj]
    return obj


def shaped(caller: dict, obj):
    return obj if sees_value(caller) else without_values(obj)


def need(caller: dict, permission: str, *, as_404: bool = False) -> None:
    if not can(caller, permission):
        if as_404:
            raise Refused(404, "not found")
        raise Refused(403, f"missing permission: {permission}")


def num(d) -> Optional[str]:
    """A quantity or amount as the exact string it leaves as (no float); None stays None."""
    if d is None:
        return None
    return format(Decimal(d), "f")


def qstr(d) -> Optional[str]:
    """A quantity without trailing zeros: '144', '0.75'."""
    if d is None:
        return None
    s = format(Decimal(d), "f")
    return s.rstrip("0").rstrip(".") if "." in s else s


def capacity_value(v, what: str):
    """A bin's capacity: empty means unlimited; otherwise a quantity above zero with at most four places."""
    from brick_inventory.domain.values import check_quantity
    if v in (None, "", "null"):
        return None
    return check_quantity(v, what)