# TASK: BizHub Price Scraper + QuickBooks Sync
**Priority:** MEDIUM
**Agent:** engineer
**Filed:** 2026-02-21
**Filed by:** Doug (via Claude Code session)
**Schedule:** Weekly (Sunday night) or on-demand
---
## Overview
Scrape Topcon BizHub dealer portal for current list prices on all Topcon parts
in QuickBooks, compute FarmTech buy price (38% off list), and update QBO.
## Prerequisites
- Playwright + Chromium must be installed: `pip install playwright && playwright install chromium`
- BizHub credentials in `/data/Sandbox/QuickBooks/bizhub/config.json`
- QBO tokens valid in `/data/Sandbox/QuickBooks/data/qb_tokens.json`
## Step 1: Refresh QBO Items List
Pull all items from QBO to get the latest part numbers. This generates the
scrape target list.
```bash
cd /data/Sandbox/QuickBooks
python3 -X utf8 -c "
import json, re
from qb_client import QuickBooksClient
qb = QuickBooksClient()
all_items = []
for item_type in ['Service', 'Inventory', 'NonInventory']:
start = 1
while True:
result = qb.query(f\"SELECT * FROM Item WHERE Type = '{item_type}' STARTPOSITION {start} MAXRESULTS 1000\")
items = result.get('QueryResponse', {}).get('Item', [])
if not items:
break
all_items.extend(items)
if len(items) < 1000:
break
start += 1000
with open('bizhub/qbo_all_items.json', 'w') as f:
json.dump(all_items, f, indent=2)
print(f'Pulled {len(all_items)} QBO items')
# Build Topcon parts list (BizHub-compatible formats only)
topcon = []
seen = set()
for item in all_items:
name = item.get('Name', '')
if name in seen:
continue
# BizHub product code formats
if re.match(r'^[12]\d{6}-\d{2}$', name) or re.match(r'^[12]\d{5}-\d{2,3}$', name):
seen.add(name)
topcon.append(name)
elif re.match(r'^AG[AB]\d{3,}', name):
seen.add(name)
topcon.append(name)
elif name.upper() in ['CROP SPEC', 'CROPSPEC', 'X35', 'X25', 'AGI-4', 'AGS-2', 'CL-55', 'CL-20']:
seen.add(name)
topcon.append(name)
topcon.sort()
with open('bizhub/topcon_parts_list.txt', 'w') as f:
for p in topcon:
f.write(p + '\n')
print(f'Topcon parts to scrape: {len(topcon)}')
"
```
## Step 2: Run BizHub Scraper
Scrape all Topcon parts. Uses `--resume` to skip already-scraped parts.
Takes ~90 minutes for full catalog (~600 parts).
```bash
cd /data/Sandbox/QuickBooks/bizhub
python3 -X utf8 bizhub_scraper.py --file topcon_parts_list.txt --resume
```
**Strategy:** Direct URL lookup (`/topcon/product/{PART_NUMBER}`) first,
then search fallback. 38% dealer discount applied to list price.
## Step 3: Generate Price Report
Compare BizHub prices with QBO. Review before syncing.
```bash
cd /data/Sandbox/QuickBooks/bizhub
python3 -X utf8 bizhub_to_qbo.py --report
```
## Step 4: Sync to QuickBooks (Dry Run First)
```bash
# Preview changes
python3 -X utf8 bizhub_to_qbo.py --dry-run
# Apply cost updates only (PurchaseCost = buy price)
python3 -X utf8 bizhub_to_qbo.py --sync --cost-only
```
## Pricing Rules
- **List price** = what BizHub shows on the product page (MSRP)
- **Buy price** = list price * 0.62 (FarmTech gets 38% off list)
- **QBO PurchaseCost** = buy price (what FarmTech pays Topcon)
- **QBO UnitPrice** = sell price (what FarmTech charges customers) - DO NOT overwrite with list price unless Doug says to
## DO NOT
- Do NOT update UnitPrice (sell price) without explicit approval
- Do NOT place orders on BizHub (implies acceptance of PAS relationship)
- Do NOT run more than once per week (rate limiting / courtesy)
- Do NOT modify config.json credentials
- If QBO auth expires, notify Doug — do NOT re-auth automatically