← Back to all projects
Blocked PREP (Mission Control executes phases, Cole reviews UAT gates) Accounts Payable Automation Created 2026-03-31

Plan: Payable Schedule — Fabric Outlet + Shiplap


Objective

Build an automated payable schedule that scrapes vendor invoice emails from Gmail 2x/day, parses PDF attachments, and writes structured payables data to a Google Sheet register — covering both Fabric Outlet and Shiplap vendor invoices.


Context

  • Vendor invoices (Moda, Riley Blake, Free Spirit, Wilmington, Kaufman, etc.) arrive via email as PDF attachments
  • Currently fragmented — no centralized payable register
  • All email accounts are Gmail under Cole's Google Workspace domain (Google Admin access confirmed)
  • Output should be a Google Sheet with payable register (vendor, invoice #, amount, due date, line items)
  • LaunchAgent should run 2x/day (9am + 3pm)
  • Encrypted secrets available via scripts/secrets.py

Email infrastructure approach (agreed):

  1. Cole creates a dedicated inbox (e.g. invoices@fabricoutlet.com) in Google Admin
  2. Cole adds a Gmail routing rule: emails from vendor domains auto-copied to that inbox
  3. Cole authorizes that inbox via OAuth (~5 min) — saves refresh token to secrets.env.enc
  4. Mission Control reads only that dedicated inbox — no personal inboxes touched

Key files to check first:

  • ~/ai-projects-local/shared-knowledge/secrets.env.enc — check for existing Gmail credentials
  • ~/ai-projects-local/mission-control/docs/integrations-status.md — Gmail API status
  • ~/ai-projects-local/mission-control/tasks/lessons.md — any prior Gmail API patterns

Pre-Requisite: Cole Action Required (YOURS)

Before Mission Control can start Phase 1, Cole must:

  1. Confirm where invoices currently land — which Gmail accounts in the domain are receiving vendor invoices (Moda, Riley Blake, Free Spirit, Wilmington, Kaufman, etc.)
  2. Create dedicated inbox in Google Admin Console: - Go to admin.google.com → Directory → Users → Add new user - Suggested: invoices@[yourdomain].com
  3. Add routing rule in Google Admin Console: - Google Admin → Apps → Google Workspace → Gmail → Routing - Add rule: "Recipient matches" existing invoice recipients → also deliver to invoices@yourdomain.com - OR: "Sender domain matches" vendor domains → deliver to invoices@yourdomain.com
  4. Authorize Gmail API for the new inbox: - Go to console.cloud.google.com → project river-engine-486318-p8 - Enable Gmail API if not already enabled - Create OAuth 2.0 credentials (or reuse existing from Google Sheets MCP setup) - Run auth flow logged in as invoices@yourdomain.com - Scopes needed: gmail.readonly + gmail.send
  5. Update this plan with the confirmed inbox address and note that pre-reqs are done

Estimated time: 20-30 minutes total Then: Update this plan status to "Ready to execute" and Mission Control picks up Phase 1


Phase 1: Gmail API Setup + Vendor Email Detection

DISPATCH tasks:

  1. Check secrets.env.enc for any existing Gmail OAuth credentials (GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET, GMAIL_REFRESH_TOKEN)
  2. If missing: generate a setup guide for Cole to authorize Gmail API via Google Cloud Console (project: river-engine-486318-p8 already exists — reuse it). Scope needed: gmail.readonly
  3. Write scripts/gmail_api_client.py — authenticates via OAuth, stores token in ~/.config/gmail-mcp/token.json
  4. Write scripts/payable_vendor_detector.py — scans inbox/all mail for emails from known vendor domains: - @moda.com, @modafabrics.com - @rileyblakedesigns.com - @freespirit.com - @wilmingtonfabrics.com - @robertkaufman.com - Plus: any email with subject containing "invoice", "remittance", "shipping notice", "order confirmation" from fabric/quilting suppliers
  5. Test: print last 20 matching emails (sender, subject, date, has_attachment)

Acceptance Criteria:

  • Script authenticates without errors
  • At least 5 real vendor emails detected from the last 90 days
  • Output shows sender domain, subject, date, attachment filename

[STOP — Ready for UAT: Phase 1] Cole reviews: do the detected emails look right? Any missing vendor domains? Any false positives?


Phase 2: PDF Invoice Parsing

DISPATCH tasks:

  1. Install pdfplumber and pypdf2 in the mission-control venv if not present
  2. Download 3-5 sample PDF attachments from the detected vendor emails (Phase 1 output)
  3. Write scripts/invoice_parser.py with vendor-specific parsers: - parse_riley_blake(pdf) — extract: invoice #, PO #, ship date, line items (SKU, description, qty, price), total - parse_moda(pdf) — same fields - parse_generic(pdf) — fallback: extract any tables + amounts using pdfplumber table detection
  4. For each parser: print extracted fields as JSON to verify accuracy
  5. Handle edge cases: multi-page invoices, invoices with no machine-readable text (flag as needs_manual_review)

Acceptance Criteria:

  • At least 2 vendor-specific parsers working
  • Generic fallback handles any remaining vendors
  • Extracted JSON includes: vendor, invoice_number, po_number, invoice_date, due_date, line_items[], total_amount, currency
  • Flagging works for unreadable PDFs

[STOP — Ready for UAT: Phase 2] Cole reviews: sample parsed output for 3 real invoices. Do the numbers match what's on the PDF?


Phase 3: Google Sheets Payable Register

DISPATCH tasks:

  1. Create (or identify existing) Google Sheet for payable register — name: "Payable Schedule — FO + Shiplap"
  2. Define schema: - Sheet tabs: FO Payables, Shiplap Payables, All Payables - Columns: Date Received | Vendor | Invoice # | PO # | Invoice Date | Due Date | Amount | Status | Store | Source Email | Notes
  3. Write scripts/payable_sheet_writer.py — uses Google Sheets MCP or direct API to: - Check for duplicates (match on Invoice # + Vendor before writing) - Append new rows to correct tab based on store - Mark All Payables as a combined view (formula-based or written directly)
  4. Test: write 3 parsed invoices to the sheet, verify deduplication works

Acceptance Criteria:

  • Sheet exists and is correctly structured
  • 3 test invoices written without duplicates
  • Re-running the writer does not create duplicate rows
  • Correct tab routing (FO vs Shiplap based on email recipient or vendor logic)

[STOP — Ready for UAT: Phase 3] Cole reviews: open the Google Sheet. Does it look right? Correct columns, correct data, no duplicates?


Phase 4: Full Pipeline + LaunchAgent Automation

DISPATCH tasks:

  1. Write scripts/payable_pipeline.py — orchestrates full flow: - Check Gmail for new vendor emails since last run (use stored last_run_timestamp) - Download PDF attachments to /tmp/payable_invoices/ - Parse each PDF - Write new invoices to Google Sheet - Update last_run_timestamp - Log results to ~/cron-logs/payable-pipeline.log - Slack alert if any invoices flagged needs_manual_review
  2. Add Gmail credentials to secrets.env.enc
  3. Write LaunchAgent plist: com.missioncontrol.payablepipeline.plist - Schedule: 9:00am + 3:00pm daily - Points to ~/bin/cron-payable-pipeline.sh wrapper
  4. Write the cron wrapper script (follows iCloud sandbox pattern from infrastructure.md)
  5. Load and test LaunchAgent with launchctl load
  6. Run manually once and verify end-to-end

Acceptance Criteria:

  • Pipeline runs without errors
  • New invoices appear in Google Sheet within minutes of manual trigger
  • LaunchAgent fires on schedule
  • Slack alert fires for any unreadable PDFs

[STOP — Ready for UAT: Phase 4 — Final] Cole reviews: let pipeline run for 2-3 days. Check Google Sheet — are real invoices populating? Any missing vendors? Any parsing errors in logs?


Follow-Up Tasks (post-UAT)

[FOLLOW_UP] YOURS | Enable Payable Schedule for Production | Confirm pipeline is stable and Google Sheet is shared with accounting | priority=2, rationale="Production go-live after 3-day UAT soak"

Execution Log

(Mission Control updates this as phases complete)