Multi-property tax tracking for Eric's real estate portfolio. Form F (agricultural), rental depreciation, estimated tax calculations.
| Property | Type | State | Notes |
|---|---|---|---|
| 162 James Ave, Atherton CA | Primary Residence | CA | Property tax, mortgage interest |
| 3553 Old Mission Rd, Traverse City MI | Farm/Vineyard | MI | Form F agricultural deductions |
| 3469 Old Mission Rd, Traverse City MI | Rental | MI |
| Managed by Soper Services |
| 100 Main St, Los Altos CA | Rental | CA | Rental income/depreciation |
gog CLI) — scan for tax-related emails (1099s, K-1s, property tax bills, vendor invoices)1Ua45Dw38A32RMo2L6IBc6NoC-hyQXGs4rZb_7uymLzg)2025_Tax_Automation_Comprehensive_Guide.md in workspacescripts/legacy/ (see integration section below)Due dates for federal and CA/MI estimated tax payments:
memory/cron-state.json for last ES reminder sent.slack-teams-hub skill).import subprocess, json, re
from datetime import datetime, timedelta
def scan_rental_emails(days_back=30):
"""Scan Gmail for rental income notifications using gog CLI."""
since = (datetime.now() - timedelta(days=days_back)).strftime("%Y/%m/%d")
queries = [
f"from:soper subject:rent after:{since}", # Soper Services (3469 Old Mission)
f"subject:rental payment subject:los altos after:{since}", # 100 Main St
]
results = []
for q in queries:
# Use gog CLI for Gmail search
out = subprocess.run(
["gog", "gmail", "search", "--query", q, "--format", "json"],
capture_output=True, text=True
)
if out.returncode == 0:
messages = json.loads(out.stdout)
for msg in messages:
results.append({
"date": msg.get("date"),
"subject": msg.get("subject"),
"from": msg.get("from"),
"snippet": msg.get("snippet", ""),
})
return results
def append_rental_to_sheet(property_name, amount, date, category="Rental Income"):
"""Append a rental income/expense row to the tax tracking sheet."""
sheet_id = "1Ua45Dw38A32RMo2L6IBc6NoC-hyQXGs4rZb_7uymLzg"
row_data = f"{date},{property_name},{category},{amount}"
subprocess.run([
"gog", "sheets", "append", "--spreadsheet-id", sheet_id,
"--range", "Transactions!A:D", "--values", row_data
], check=True)
Track expenses by vendor for Michigan Form F (Schedule of Farm Income/Loss):
FARM_VENDORS = ["agrivine", "manigold", "ginops"]
def scan_farm_invoices(year=2026):
"""Scan Gmail for farm vendor invoices."""
results = []
for vendor in FARM_VENDORS:
out = subprocess.run(
["gog", "gmail", "search", "--query",
f"from:{vendor} OR subject:{vendor} after:{year}/01/01 before:{year}/12/31",
"--format", "json"],
capture_output=True, text=True
)
if out.returncode == 0:
messages = json.loads(out.stdout)
for msg in messages:
results.append({"vendor": vendor, "date": msg.get("date"), "subject": msg.get("subject")})
return results
Rental properties use straight-line depreciation over 27.5 years (residential):
Track in the Google Sheet under a "Depreciation" tab. Update annually or when capital improvements are made.
gog auth login — see skills/google-oauth-reauth/SKILL.md.scripts/legacy/):
subscription_tracker.py — subscription cost tracking (overlaps with expense categorization)tax_email_scan.py, check-tax-emails.py, scan-tax-emails.py — prior Gmail scanning implementations; reference for query patterns but use the workflow above for new runsadd-tax-emails-to-sheet.py — prior sheet integration; patterns reusablecreate_farm_tax_doc.py — Form F document generationsubscription_tracker.py writes to the same subscription sheet)memory/incidents.jsonl.