{"files":{"main.py":"from core import run\nfrom policy import POLICY\n\n\ndef evaluate(payload):\n    return run(payload, POLICY)\n","core.py":"from helpers import principal_cents, ordered_events\n\n\ndef run(payload, policy):\n    results = []\n    for invoice in payload.get('invoices', []):\n        if invoice['currency'] != policy['currency']:\n            continue\n\n        principal = principal_cents(invoice['lines'], policy['rounding'])\n        fee = invoice['fee_cents']\n        charge = principal + fee\n        if charge < policy['min_invoice_cents']:\n            continue\n\n        ceiling = principal + (fee if policy['fee_refundable'] else 0)\n        refunded = 0\n        active = True\n        tombstoned = False\n        created = invoice['created']\n\n        for event in ordered_events(invoice.get('events', []), policy):\n            kind = event['kind']\n            if kind == 'void':\n                active = False\n                if policy['void_is_tombstone']:\n                    tombstoned = True\n            elif kind == 'restore':\n                if not tombstoned:\n                    active = True\n            elif kind == 'refund' and active:\n                age = event['day'] - created\n                limit = policy['max_refund_age_days']\n                eligible = age >= 0 and (\n                    age < limit or (policy['inclusive_limit'] and age == limit)\n                )\n                if not eligible:\n                    continue\n                amount = event['cents']\n                remaining = ceiling - refunded\n                if amount > remaining:\n                    if policy['refund_overflow'] == 'reject':\n                        continue\n                    amount = remaining\n                refunded += amount\n\n        status = 'void' if not active else ('refunded' if refunded == charge else 'open')\n        if policy['status_case'] == 'upper':\n            status = status.upper()\n        result = {\n            'id': invoice['id'],\n            'charge': charge,\n            'refunded': refunded,\n            'balance': charge - refunded,\n            'status': status,\n        }\n        results.append((created, result))\n\n    if policy['order'] == 'id':\n        results.sort(key=lambda item: item[1]['id'])\n    else:\n        results.sort(key=lambda item: (item[0], item[1]['id']))\n    return {'invoices': [result for _, result in results]}\n","policy.py":"POLICY = {\n    'currency': 'USD',\n    'duplicate_event': 'first',\n    'fee_refundable': True,\n    'inclusive_limit': True,\n    'max_refund_age_days': 30,\n    'min_invoice_cents': 10,\n    'order': 'id',\n    'refund_overflow': 'cap',\n    'refunds_first': True,\n    'rounding': 'per_line',\n    'status_case': 'lower',\n    'void_is_tombstone': True,\n}\n","helpers.py":"from fractions import Fraction\n\n\ndef round_half_up(value):\n    return (2 * value.numerator + value.denominator) // (2 * value.denominator)\n\n\ndef principal_cents(lines, rounding):\n    amounts = [Fraction(line) * 100 for line in lines]\n    if rounding == 'per_line':\n        return sum(round_half_up(amount) for amount in amounts)\n    return round_half_up(sum(amounts, Fraction(0)))\n\n\ndef ordered_events(events, policy):\n    unique = {}\n    for event in events:\n        eid = event['eid']\n        if eid not in unique or policy['duplicate_event'] == 'last':\n            unique[eid] = event\n    ordered = list(unique.values())\n    if policy['refunds_first']:\n        ordered.sort(key=lambda event: event['kind'] != 'refund')\n    return ordered\n"}}