{"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 rounded_cents, deduplicate_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        lines = invoice.get('lines', [])\n        if policy['rounding'] == 'per_line':\n            principal = sum(rounded_cents([line]) for line in lines)\n        else:\n            principal = rounded_cents(lines)\n        fee = invoice.get('fee_cents', 0)\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        events = deduplicate_events(\n            invoice.get('events', []), policy['duplicate_event']\n        )\n        if policy['refunds_first']:\n            events = sorted(events, key=lambda event: event['kind'] != 'refund')\n\n        for event in events:\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'] - invoice['created']\n                limit = policy['max_refund_age_days']\n                if age < 0 or age > limit:\n                    continue\n                if age == limit and not policy['inclusive_limit']:\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        if not active:\n            status = 'void'\n        elif refunded == charge:\n            status = 'refunded'\n        else:\n            status = 'open'\n        if policy['status_case'] == 'upper':\n            status = status.upper()\n        else:\n            status = status.lower()\n\n        result = {\n            'id': invoice['id'],\n            'charge': charge,\n            'refunded': refunded,\n            'balance': charge - refunded,\n            'status': status,\n        }\n        results.append((invoice['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': [item[1] for item in results]}\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\n\n\ndef rounded_cents(amounts):\n    terms = []\n    for amount in amounts:\n        parts = Decimal(amount).as_tuple()\n        coefficient = 0\n        for digit in parts.digits:\n            coefficient = coefficient * 10 + digit\n        if parts.sign:\n            coefficient = -coefficient\n        terms.append((coefficient, parts.exponent + 2))\n\n    scale = min([0] + [exponent for _, exponent in terms])\n    total = sum(\n        coefficient * 10 ** (exponent - scale)\n        for coefficient, exponent in terms\n    )\n    divisor = 10 ** (-scale)\n    whole, remainder = divmod(abs(total), divisor)\n    if remainder * 2 >= divisor:\n        whole += 1\n    return -whole if total < 0 else whole\n\n\ndef deduplicate_events(events, mode):\n    unique = {}\n    for event in events:\n        eid = event['eid']\n        if mode == 'last' or eid not in unique:\n            unique[eid] = event\n    return list(unique.values())\n"}}