# Afton Electric QBO Cleanup — Canonical State
**Last updated:** 2026-05-22 end-of-session
**Pilot client:** Afton Electric, LLC (Sole Proprietor, QBO Advanced, Realm via "afton" client slug)

This is the canonical state file. Update it (don't append) when something changes.

---

## TL;DR

| Problem area | $ impact | State | Next action owner |
|---|---|---|---|
| Customer master data (null names, dups) | — | 58/61 nulls fixed; 3 failed (Amy Turner, Nelle, Afton LLC); 24 phone-in-name + 15 merge pairs pending | Jimmie (UI merges) + Ledger (3 failed) |
| Duplicate invoice/payment pairs | $21,058.24 phantom revenue (24 pairs 2024, 8 pairs 2025) | **Untouched.** Void approach FAILED today; need redesign via Credit Memos | Ledger + Rex |
| Undeposited Funds mess | $562K, 394 payments | Untouched | Jimmie (manual Make Deposits against RBFCU) |
| $800K forced rec adjustments | $800,070 across 5 JEs | Staged, blocked on UF cleanup | Ledger |
| AR aging — true delinquent | ~$45-55K | 3 priority collection cases identified, no calls made | Jimmie (relationship work) |
| Books audit cleanliness | — | Slightly noisier than 2026-05-20 (11 customers have voided recovery-invoice clutter, fully documented in PrivateNotes) | n/a — acceptable cost |

**Overall progress vs "perfect close": ~5-10%.**

---

## What happened 2026-05-22

### Step 1 — Customer null cleanup (Riv script)
- 61 sparse updates queued via `afton_customer_cleanup.py`
- 58 executed cleanly (Pending → Executed via worker)
- **3 still Failed:**
  - Amy Turner Id 1614 — duplicate-name conflict (another "Amy Turner" exists)
  - Nelle Id 1325 — duplicate with Id 1335 "Nelle" (both nulls; one cleaned, other can't because of name collision)
  - Afton Electric, LLC Id 951 — stale SyncToken (Jimmie edited concurrently in UI; likely already fixed, needs re-check)
- Garbage record Id 594 (literally "null" displayname) — not queued, awaits decision

### Step 4 — Duplicate-payment voids attempt (FAILED)
**Original plan from 2026-05-20 checkpoint:** void 40 duplicate payments to clean up the $562K UF dup-portion.

**Pre-flight discovery (2026-05-22):** Every "duplicate payment" had a paired duplicate INVOICE behind it. Scanned all 35 candidate pairs:
- 32 are clean dup pairs (paired pmt + paired inv, both duplicates of "keep" set)
- 3 are mis-flagged (not true duplicates): Mike Fischer 17649/17650, Jean Autrey 18914/18916, Mark Braunstein 19511/19513

**Tax-period split:**
- 24 pairs ($16,128.00) in 2024 — closed period, owner filed return
- 8 pairs ($4,930.24) in 2025 — open, owner on extension
- Mike Stegall pair confirmed 2025

**Track A attempt (8 open-period 2025 pairs + 3 originally-pending = 11 pairs):**
- Queued 16 pmt+inv voids + 3 paired inv voids = 22 queue rows (ids 63-65, 77-95)
- Result: **all 11 invoices voided successfully; ZERO payments voided.**
- Failure reasons:
  - 9 payments processed via QBO Merchant Services (credit card) — API rejects with "credit card amount" validation error. Only voidable in UI.
  - 2 payments already grouped into Deposit transactions — API rejects with "Deposited Transaction cannot be changed". Must edit Deposit first in UI.
- **Result: 11 customers in orphan-credit state**, $5,935.24 of phantom AR credits

**Recovery from Track A failure:**
- Recreated 11 invoices via `afton_recover_orphan_credits.py` → queue ids 96-106
- Worker double-fired due to Jimmie running desktop shortcut + my separate --row-id loop
- Net effect: 22 recovery invoices created instead of 11 (one set each)
- Jimmie voided the 11 extras manually in QBO UI
- Mike Stegall #2515 had auto-tax applied incorrectly ($980.75 vs $906); Jimmie fixed in UI to $906

**Current state per the 11 Track A customers:**
| Customer | Voided original inv | Voided extra-recovery inv | Alive recovery inv | Unapplied pmt |
|---|---|---|---|---|
| Debra Hazle | 21341 ($0) | 27031 ($0) | 27036 #2505 ($1082.50) | 21342 ($1082.50) |
| Ralph Marcantonio | 21513 ($0) | 27032 ($0) | 27040 #2509 ($1199.00) | 21514 ($1199.00) |
| Mike Frontz | 21535 ($0) | 27033 ($0) | 27044 #2513 ($620.00) | 21538 ($620.00) |
| Kassandra Gesse | 21556 ($0) | 27034 ($0) | 27045 #2514 ($225.00) | 21557 ($225.00) |
| Amy Erwin | 21333 ($0) | 27035 ($0) | 27046 #2515 ($375.00) | 22214 ($375.00) |
| Scott Ferguson | 22529 ($0) | 27037 ($0) | 27047 #2516 ($232.74) | 22530 ($232.74) |
| Dan Malone | 23657 ($0) | 27038 ($0) | 27048 #2517 ($290.00) | 23658 ($290.00) |
| Mike Stegall | 24807 ($0) | 27039 ($0) | 27049 #2518 ($906.00) | 24809 ($906.00) |
| Linda Hughes | 24043 ($0) | 27041 ($0) | 27050 #2519 ($481.00) | 24046 ($481.00) |
| Joyce Yannuzzi | 25518 ($0) | 27042 ($0) | 27051 #2520 ($344.00) | 25521 ($344.00) |
| Bena Videsa | 25568 ($0) | 27043 ($0) | 27052 #2521 ($180.00) | 25569 ($180.00) |

**Outstanding manual action for Jimmie:** for each of those 11 customers, open in QBO UI → apply the unapplied payment to the alive recovery invoice via Receive Payment. After done: customer balance → $0, books back to pre-attempt state for these 11.

---

## Critical learnings (captured in auto-memory)

- `reference-qbo-dup-payment-invoice-pattern` — always pull linked invoices before voiding a "dup payment"
- `reference-qbo-void-api-limitations` — CC and deposited payments unvoidable via API; void approach creates orphan-credit trap
- `reference-qbo-autoreminder-empirical-check` — check DeliveryInfo timestamps, not Preferences API
- `feedback_one_question_at_a_time` (existing) — confirmed correct, surface ONE decision at a time

---

## Open work — prioritized

| Pri | Item | $ leverage | Effort | Owner | Blocked by |
|---|---|---|---|---|---|
| 1 | Apply 11 unapplied payments to recovery invoices (close out today's recovery) | $5,935 | 11 quick UI clicks | Jimmie | — |
| 2 | Rex conversation on 2024 tax treatment (3 options: amend / current-period JE / GAAP-strict) | $16,128 | 1 conversation | Jimmie + Rex | — |
| 3 | Redesign 32-pair cleanup using **Credit Memos** instead of voids | $21,058 cleanup | Script + queue + execute | Ledger | #2 outcome |
| 4 | Make Deposits — match remaining ~$540K UF payments to RBFCU statements | unblocks $800K reversal | Multi-hour UI | Jimmie | — |
| 5 | Reverse $800K forced rec adjustments | $800,070 | 5 JEs via queue | Ledger | #4 done |
| 6 | Resolve 3 failed Step 1 cleanups | tiny | ~10 min | Ledger | — |
| 7 | 15 manual customer merges (Step 3) + 24 phone-in-name dups | cosmetic | 30-60 min UI | Jimmie | — |
| 8 | Review 3 mis-flagged "dup" pairs (Mike Fischer, Jean Autrey, Mark Braunstein) | $1-2K | small | Ledger | — |
| 9 | 3 priority collection calls (Corbin, Jimmy Martinson, La Golondrina) + Jake's $2,240 apply | $30K+ AR | calls | Jimmie | — |
| 10 | Riv: fix worker double-fire on Sheets 429 quota errors | infra debt | Riv work | Riv | — |

---

## Source files

| Path | Purpose |
|---|---|
| `Team/Riv/j2_qbo_mcp/server.py` | j2-qbo MCP server (20 read + 8 write tools) |
| `Team/Riv/j2_qbo_mcp/worker.py` | Queue worker (supports `--once --row-id N` for single-row mode) |
| `Team/Riv/j2_qbo_mcp/write_queue.json` | Queue state (entries up to id 108 as of 2026-05-22) |
| `Team/Riv/j2_qbo_mcp/afton_customer_cleanup.py` | Step 1 script — 61 null-name updates |
| `Team/Riv/j2_qbo_mcp/afton_payment_voids.py` | Step 4 ORIGINAL plan — pmt-only voids (now superseded) |
| `Team/Riv/j2_qbo_mcp/afton_open_period_pair_voids.py` | Step 4 Track A — pmt+inv voids (failed, kept for audit) |
| `Team/Riv/j2_qbo_mcp/afton_recover_orphan_credits.py` | Recovery script — recreated 11 invoices |
| `Owner Inbox/afton_customer_cleanup_report.md` | Step 1 results report |
| `Owner Inbox/afton_payment_voids_report.md` | Step 4 original-plan dry-run report |
| `Owner Inbox/afton_open_period_pair_voids_report.md` | Track A queue report |
| `Owner Inbox/afton_payment_pairs_invoice_analysis.json` | **CANONICAL** — full pmt+inv topology analysis for all 35 original pairs (use this to design Credit Memo approach) |
| `Owner Inbox/afton_session_2026-05-22_summary.md` | Owner-facing summary of today |
