Skip to content

Bexio expense sync: only handle purchases with status=pending - #29

Merged
tompluess merged 1 commit into
mainfrom
expense-sync-pending-only
Aug 21, 2026
Merged

tompluess merged 1 commit into
mainfrom
expense-sync-pending-only

Conversation

@tompluess

Copy link
Copy Markdown
Owner

What

/api/bexio-expense-sync now processes a Moco purchase only when its status is pending. Anything else — an archived purchase above all — is skipped silently.

Why

Moco purchases move pending → archived once the operator has filed them. An archived purchase is closed business and must not be (re-)pushed to Bexio, but every later edit of one still fires a Purchase:update webhook at us.

How

New first gate in sync(), ahead of the "Review pending" tag check:

status = _purchase_status(body)
if status and status != "pending":
    logger.info("expense sync: skipped (status=%s) source_id=%s", status, body.get("id"))
    return {"skipped": "status_not_pending", "status": status}

Three decisions worth calling out:

  • Silent. INFO log, no Telegram — those update webhooks repeat, so alerting would be pure noise. The endpoint returns its usual 200 ok=true so Moco stops retrying (same shape as the other routine skips).
  • pending-only, not archived-only, so a status Moco grows later can't be silently treated as payable. _purchase_status lowercases and trims first.
  • A body with no status still syncs. All five live-webhook fixtures carry status: "pending", but the OCR auto-release hand-off feeds get_purchase output into the same sync(); silently dropping a real bill because some shape omitted the field is the worse failure than syncing one purchase too many.

Tests

Four new cases in tests/test_bexio_expense_sync_service.py — archived skips with zero Bexio/Moco I/O and no Telegram, the casing/other-status sweep, pending still syncing end-to-end, and a status-less body still syncing. Full suite: 691 passed.

CLAUDE.md's service bullet updated in the same commit.

🤖 Generated with Claude Code

https://claude.ai/code/session_014tmj9oSBu9dBe7yBoS4TTs

Moco purchases move pending -> archived once the operator has filed
them. An archived purchase is closed business and must not be (re-)pushed
to Bexio, but every later edit of one still fires a Purchase:update
webhook at us.

Gate on `status` as the very first check in `sync()`, ahead of the
"Review pending" tag: anything that isn't `pending` returns
`{"skipped": "status_not_pending", "status": ...}` with an INFO log and
no Telegram alert (the webhooks repeat, so alerting would be noise), and
the endpoint's usual 200/ok=true stops Moco retrying.

The gate is pending-only rather than archived-only so a status Moco grows
later can't be silently treated as payable. A body carrying no `status`
at all still syncs: every live webhook has the field, and dropping a real
bill because some shape omitted it is the worse failure than syncing one
purchase too many.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014tmj9oSBu9dBe7yBoS4TTs
@vercel

vercel Bot commented Aug 21, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
moco-skyr-functions Ready Ready Preview Aug 21, 2026 4:52pm
vercel-functions Ready Ready Preview Aug 21, 2026 4:52pm

@tompluess
tompluess merged commit 08b5111 into main Aug 21, 2026
4 checks passed
@tompluess
tompluess deleted the expense-sync-pending-only branch August 21, 2026 16:53

This branch was successfully deployed

2 active deployments
Preview – vercel-functions — b64d42a5 Deployed Aug 21, 2026 by vercel[bot]
Preview – moco-skyr-functions — b64d42a5 Deployed Aug 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant