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):
- Cole creates a dedicated inbox (e.g.
invoices@fabricoutlet.com) in Google Admin - Cole adds a Gmail routing rule: emails from vendor domains auto-copied to that inbox
- Cole authorizes that inbox via OAuth (~5 min) — saves refresh token to
secrets.env.enc - 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:
- Confirm where invoices currently land — which Gmail accounts in the domain are receiving vendor invoices (Moda, Riley Blake, Free Spirit, Wilmington, Kaufman, etc.)
- Create dedicated inbox in Google Admin Console:
- Go to admin.google.com → Directory → Users → Add new user
- Suggested:
invoices@[yourdomain].com - 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 toinvoices@yourdomain.com - 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 asinvoices@yourdomain.com- Scopes needed:gmail.readonly+gmail.send - 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:
- Check
secrets.env.encfor any existing Gmail OAuth credentials (GMAIL_CLIENT_ID,GMAIL_CLIENT_SECRET,GMAIL_REFRESH_TOKEN) - 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 - Write
scripts/gmail_api_client.py— authenticates via OAuth, stores token in~/.config/gmail-mcp/token.json - 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 - 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:
- Install
pdfplumberandpypdf2in the mission-control venv if not present - Download 3-5 sample PDF attachments from the detected vendor emails (Phase 1 output)
- Write
scripts/invoice_parser.pywith 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 - For each parser: print extracted fields as JSON to verify accuracy
- 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:
- Create (or identify existing) Google Sheet for payable register — name: "Payable Schedule — FO + Shiplap"
- 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 - 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 - MarkAll Payablesas a combined view (formula-based or written directly) - 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:
- Write
scripts/payable_pipeline.py— orchestrates full flow: - Check Gmail for new vendor emails since last run (use storedlast_run_timestamp) - Download PDF attachments to/tmp/payable_invoices/- Parse each PDF - Write new invoices to Google Sheet - Updatelast_run_timestamp- Log results to~/cron-logs/payable-pipeline.log- Slack alert if any invoices flaggedneeds_manual_review - Add Gmail credentials to
secrets.env.enc - Write LaunchAgent plist:
com.missioncontrol.payablepipeline.plist- Schedule: 9:00am + 3:00pm daily - Points to~/bin/cron-payable-pipeline.shwrapper - Write the cron wrapper script (follows iCloud sandbox pattern from infrastructure.md)
- Load and test LaunchAgent with
launchctl load - 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)
~/ai-projects/mission-control/plans/payable-schedule.md