From 1583bd7c0b98629bfabcfd6e61051138de9495f1 Mon Sep 17 00:00:00 2001 From: alexph-dev Date: Wed, 6 May 2026 17:55:26 +0700 Subject: [PATCH] docs: add QBO report export domain skill --- .../domain-skills/qbo/report-export.md | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 agent-workspace/domain-skills/qbo/report-export.md diff --git a/agent-workspace/domain-skills/qbo/report-export.md b/agent-workspace/domain-skills/qbo/report-export.md new file mode 100644 index 00000000..4105a740 --- /dev/null +++ b/agent-workspace/domain-skills/qbo/report-export.md @@ -0,0 +1,157 @@ +# QuickBooks Online Custom Report PDF Export + +Field-tested on QBO custom reports in a logged-in Chromium-family browser on 2026-05-06. + +## Fast Path + +For clean QBO custom-report PDFs, use the visible QBO export flow, then save the PDF blob directly: + +1. Open the saved custom report. +2. Set the visible date inputs, then click `refresh-report`. +3. Set the required report expansion state. +4. Click QBO report toolbar `Export` -> `Export to PDF`. +5. Wait for the generated `blob:https://qbo.intuit.com/` PDF target. +6. Fetch that blob from the QBO page origin and write the bytes locally. +7. Validate with `pdftotext` when report content matters. + +Do not use CDP `Page.printToPDF` for QBO reports. It prints the surrounding QBO shell, not the clean report body. Do not use the native `Save as PDF` sheet unless the QBO PDF blob is unavailable. + +## Browser Attachment + +- Use the real logged-in browser profile that has the QBO session. +- If that session is in a non-default Chromium browser, start it with a known remote-debugging port and pass `BU_CDP_URL` or `BU_CDP_WS` to Browser Harness. +- Computer Use is only a fallback for native OS sheets, visual confirmation, or unexpected QBO UI blockers. It is not part of the normal fast path. +- After a browser restart, QBO may restore a stale report tab. Always read the visible report period before exporting. + +Example attachment pattern for a dedicated browser endpoint: + +```bash +BU_NAME=qbo BU_CDP_URL=http://127.0.0.1:9223 browser-harness -c 'print(page_info())' +``` + +## Report State + +- Custom reports list route: `https://qbo.intuit.com/app/customreports`. +- Saved custom report builder routes look like `/app/report/builder?rptId=sbg:&type=user`. +- Date fields are normal visible inputs. Their format follows the QBO/user locale; one tested locale used `dd.mm.yyyy`. +- After setting dates, click the report toolbar `refresh-report`. +- Verify report readiness from visible text: ` report is ready`. +- QBO can move toolbar buttons when the report width/state changes. Locate `Export` from the current screenshot/DOM instead of reusing old coordinates. + +## Compact Menu + +Use the report toolbar `Compact` dropdown. It commonly contains: + +- `Compact View` +- `Normal View` +- `Expand All` +- `Collapse All` + +For a summary PDF with one visible grouping layer, choose `Collapse All`, then expand the top report group manually. Verify summary rows are visible and transaction-detail rows are not. + +For a detailed PDF, choose `Expand All` and verify transaction rows are visible. + +Useful post-state check: + +```python +rows = js(""" +return Array.from(document.querySelectorAll("table tr, [role=row], div")) + .map(r => r.innerText.trim().replace(/\s+/g, " ")) + .filter(Boolean) + .slice(0, 200) +""") +``` + +## PDF Export + +- QBO `Export` -> `Export to PDF` produces the clean report PDF. +- This path may not fire a normal Chromium `Browser.downloadProgress` event, so `Browser.setDownloadBehavior` alone is not enough. +- QBO opens an in-page PDF modal using Chromium's built-in PDF viewer. The actual report PDF appears as a child `blob:https://qbo.intuit.com/` target. +- The modal's `Save as PDF` button can open a native OS save sheet. Avoid that by fetching the blob directly. +- Fetching the blob too early can return no value; wait briefly after clicking `Export to PDF`. + +Blob-save template: + +```python +import base64, json, os, time + +def latest_qbo_pdf_blob(): + blobs = [ + t["url"] + for t in cdp("Target.getTargets")["targetInfos"] + if t.get("url", "").startswith("blob:https://qbo.intuit.com/") + ] + return blobs[-1] if blobs else None + +def save_qbo_pdf_blob(out_path, timeout=30): + deadline = time.time() + timeout + blob = None + while time.time() < deadline: + blob = latest_qbo_pdf_blob() + if blob: + break + wait(0.5) + if not blob: + raise RuntimeError("QBO PDF blob did not appear") + + wait(2) # let the PDF viewer finish loading the blob + meta = js(f""" + return fetch({json.dumps(blob)}).then(async r => {{ + const b = await r.blob(); + const ab = await b.arrayBuffer(); + const u8 = new Uint8Array(ab); + let s = ""; + for (let i = 0; i < u8.length; i += 0x8000) {{ + s += String.fromCharCode(...u8.subarray(i, i + 0x8000)); + }} + window.__qbo_pdf_b64 = btoa(s); + return {{ + ok: r.ok, + status: r.status, + type: b.type, + size: b.size, + b64len: window.__qbo_pdf_b64.length + }}; + }}); + """) + if not meta or meta.get("type") != "application/pdf": + raise RuntimeError(f"unexpected QBO blob metadata: {meta}") + + parts = [] + for i in range(0, meta["b64len"], 8000): + parts.append(js(f"return window.__qbo_pdf_b64.slice({i},{i + 8000})")) + + out_dir = os.path.dirname(out_path) + if out_dir: + os.makedirs(out_dir, exist_ok=True) + pdf = base64.b64decode("".join(parts)) + with open(out_path, "wb") as f: + f.write(pdf) + return {"path": out_path, "size": len(pdf), "blob": blob} +``` + +End-to-end outline: + +```python +# 1. Use screenshot/DOM to click report toolbar Export. +# 2. Click menu item "Export to PDF". +# 3. Save the latest QBO PDF blob. +save_qbo_pdf_blob("/abs/output/report.pdf") +``` + +## Validation + +Use local PDF tools for high-confidence runs: + +```bash +pdfinfo /abs/output/report.pdf +pdftotext /abs/output/report.pdf - +``` + +Checks that catch common failures: + +- PDF header starts with `%PDF`. +- `pdftotext` includes the expected report title and date range. +- Summary exports include the expected summary rows. +- Detailed exports include transaction-date/detail rows. +- The text does not include QBO shell/navigation labels, which indicates `Page.printToPDF` was used accidentally.