HANDOFF: target project invoice

Scope
Only invoice requirements are authoritative. Ignore all archived records for other projects. Updates 1–5 have been applied in order; the settings below are current. No later update or question is available.

Current settings
{
  "currency": "CAD",
  "rounding": "per_line",
  "min_invoice_cents": 10,
  "duplicate_event": "first",
  "refunds_first": true,
  "max_refund_age_days": 30,
  "inclusive_limit": true,
  "fee_refundable": false,
  "refund_overflow": "cap",
  "void_is_tombstone": true,
  "status_case": "lower",
  "order": "id"
}

Fixed interface and input
Implement evaluate(payload) returning:
{invoices:[{id,charge,refunded,balance,status}]}

Invoices have unique id, integer created day, currency, lines containing nonnegative decimal-dollar strings, nonnegative fee_cents, and ordered events. Each event has eid, kind (refund/void/restore), day, and nonnegative cents.
Do not mutate payload. Empty input returns {invoices:[]}.

Current processing requirements
1. Filter out invoices whose currency is not CAD.
2. Convert each line to integer cents using exact decimal ROUND_HALF_UP, rounding each line before summing. This sum is principal.
3. charge = principal + fee_cents.
4. Skip invoices with charge < 10 cents; charge equal to 10 is retained.
5. Deduplicate events by eid, keeping the first occurrence and its insertion position.
6. After deduplication, stably move refunds ahead of all non-refund events. Preserve relative order within each group; do not sort events by day.
7. Begin each invoice active, with refunded = 0.
8. A void makes the invoice inactive and permanently prevents subsequent restore. Earlier refunds are never undone.
9. A restore cannot reactivate an invoice after a void under the current tombstone setting.
10. Accept refunds only while active and when 0 <= event.day - created <= 30. Day 30 is eligible; negative ages and ages above 30 are not.
11. Refund ceiling = principal only. Fees are not refundable.
12. Cap each eligible refund at the remaining unrefunded ceiling; do not reject an oversized event in full.
13. Final status precedence:
    - "void" if inactive;
    - otherwise "refunded" if refunded == charge;
    - otherwise "open".
    Status strings are lowercase.
14. balance = charge - refunded.
15. Sort retained output invoices by id. Return only id, charge, refunded, balance, status.

Important interactions / exceptions
- Refunds-first processing means refunds are considered before void/restore events even if a void appeared earlier in the original event list.
- Refund eligibility uses event day relative to created, not processing position.
- A void does not reverse refunds already accepted.
- A positive nonrefundable fee can leave an active invoice "open" even after all principal is refunded, because "refunded" requires refunded == charge.
- An inactive invoice is "void" even if refunded == charge.
- Minimum invoice filtering uses total charge including fee, not principal alone.

Other fixed contract branches
The contract also defines alternatives should future target updates select them:
- rounding="invoice": round the exact sum of lines once.
- duplicate_event="last": replace the stored event value while retaining that eid's first insertion position.
- refunds_first=false: preserve deduplicated event order.
- inclusive_limit=false: maximum refund age is exclusive.
- fee_refundable=true: ceiling includes fee.
- refund_overflow="reject": reject the whole event if it exceeds remaining ceiling.
- void_is_tombstone=false: restore re-enables refunds after void.
- order="created": sort by (created,id).
- status_case controls output case.

Unresolved / not specified
- No outstanding choice among the listed settings; all 12 have current target values.
- Programming language, validation/error behavior for invalid inputs, id type/comparison details, and handling of missing payload fields are not specified.
- Do not infer those details or any defaults from unrelated archived projects.
- Await the next target update or question; it may supersede individual settings while leaving others unchanged.