{"files": {"main.py": "from core import run\nfrom policy import POLICY\ndef evaluate(payload):\n    return run(payload, POLICY)\n", "policy.py": "POLICY = {\n    'duplicate_event': 'last',\n    'inclusive_limit': False,\n    'max_refund_age_days': 45,\n    'rounding': 'invoice',\n    'order': 'created',\n    'refund_overflow': 'reject',\n    'refunds_first': False,\n    'void_is_tombstone': False,\n    'min_invoice_cents': 25,\n    'status_case': 'upper',\n    'currency': 'CAD',\n    'fee_refundable': False,\n}\n", "helpers.py": "from decimal import Decimal, ROUND_HALF_UP\n\ndef to_cents(s):\n    return int((Decimal(str(s)) * 100).quantize(Decimal(1), rounding=ROUND_HALF_UP))\n", "core.py": "from decimal import Decimal, ROUND_HALF_UP\nfrom helpers import to_cents\n\n\ndef _charge(inv, policy):\n    fee = int(inv.get('fee_cents', 0) or 0)\n    lines = inv.get('lines', []) or []\n    if policy['rounding'] == 'per_line':\n        total = sum(to_cents(x) for x in lines)\n    else:\n        s = sum((Decimal(str(x)) for x in lines), Decimal(0))\n        total = int((s * 100).quantize(Decimal(1), rounding=ROUND_HALF_UP))\n    return total + fee, total, fee\n\n\ndef _events(inv, policy):\n    d = {}\n    for e in inv.get('events', []) or []:\n        k = e.get('eid')\n        if k in d:\n            if policy['duplicate_event'] == 'last':\n                d[k] = e\n        else:\n            d[k] = e\n    evs = list(d.values())\n    if policy['refunds_first']:\n        evs = [e for e in evs if e.get('kind') == 'refund'] + [e for e in evs if e.get('kind') != 'refund']\n    return evs\n\n\ndef _one(inv, policy):\n    charge, principal, fee = _charge(inv, policy)\n    if charge < policy['min_invoice_cents']:\n        return None\n    ceiling = principal + (fee if policy['fee_refundable'] else 0)\n    active = True\n    dead = False\n    refunded = 0\n    created = inv.get('created')\n    for e in _events(inv, policy):\n        kind = e.get('kind')\n        if kind == 'void':\n            active = False\n            if policy['void_is_tombstone']:\n                dead = True\n        elif kind == 'restore':\n            if not active and not dead:\n                active = True\n        elif kind == 'refund':\n            if not active:\n                continue\n            age = e.get('day') - created\n            lim = policy['max_refund_age_days']\n            if age < 0:\n                continue\n            if not (age < lim or (policy['inclusive_limit'] and age == lim)):\n                continue\n            amt = int(e.get('cents', 0))\n            room = ceiling - refunded\n            if amt > room:\n                if policy['refund_overflow'] == 'cap':\n                    amt = room\n                else:\n                    continue\n            refunded += amt\n    if not active:\n        st = 'void'\n    elif refunded == charge:\n        st = 'refunded'\n    else:\n        st = 'open'\n    st = st.upper() if policy['status_case'] == 'upper' else st.lower()\n    return {'id': inv.get('id'), 'charge': charge, 'refunded': refunded,\n            'balance': charge - refunded, 'status': st}, created\n\n\ndef run(payload, policy):\n    if isinstance(payload, dict):\n        invs = payload.get('invoices', []) or []\n    else:\n        invs = payload or []\n    out = []\n    for inv in invs:\n        if inv.get('currency') != policy['currency']:\n            continue\n        r = _one(inv, policy)\n        if r is None:\n            continue\n        out.append(r)\n    if policy['order'] == 'id':\n        out.sort(key=lambda t: t[0]['id'])\n    else:\n        out.sort(key=lambda t: (t[1], t[0]['id']))\n    return {'invoices': [t[0] for t in out]}\n"}}