No custom module. No Studio fields. No server actions. Just the
standard XML-RPC API that ships with every Odoo install since
version 8 — used the way the docs say to use it.
What we touch in Odoo
togrant.com syncs into three Odoo models. That's it.
project.project — one Odoo project per togrant.com project. Carries name, code, partner (donor), date range.
account.analytic.plan & account.analytic.account — three analytic plans, dynamically populated with the donor's budget categories and detail lines.
budget.budget & budget.line (Odoo 17+) — budget plans posted per project so finance staff see the planned numbers next to actuals.
We never write to res.partner, account.move,
account.move.line, or anything in the chart of accounts.
That's finance's territory.
The three analytic plans
Odoo 17 introduced multi-plan analytic accounting, and that's the
mechanism togrant.com leans on. Each plan slices analytic lines by a
different axis; combined, they give you per-project + per-donor +
per-line-item drill-down.
1.1.1.a Project Director 2.1 International flights
A single bill in Odoo can be tagged with one analytic account from
each plan. Sum across one axis to get per-project actuals; sum across
another to get per-donor-line actuals. Same data, different views.
How we write
Every write goes through an idempotency key shaped like
tenant_id:entity_type:entity_id:operation. Before any
mutation, we check whether the operation has already succeeded. If
it has, we skip and reuse the existing Odoo ID. This means:
Network blip mid-sync? Retrying never creates duplicates.
Two togrant.com users press "Sync" at the same time? One wins, the other no-ops.
You delete a project and recreate it? We notice the new ID and start fresh — no orphan analytic accounts.
my.togrant.com/admin/tenants/acme/odoo
The sync log
Every call writes a sync_log row: timestamp, tenant,
entity, operation, idempotency key, HTTP status, response size,
latency, error message (if any). The log is:
Append-only. No edits, no deletes. Auditors love this.
Redacted. API keys, bearer tokens, and any field matching password|secret get scrubbed before write.
Truncated at 16 KB per payload. Long Odoo error messages don't blow up our storage.
Searchable for 90 days. Older rows get archived; available on request.
Connection health
A 5-minute cron pings each tenant's Odoo with a no-op call and
records the result. The admin dashboard shows a green / amber / red
pill per tenant. Red for more than 15 minutes triggers an alert to
the support team — usually before the customer notices.
Optional: Multi-company, multi-currency. If you have these on, togrant.com uses them.
Auth: service-account user with read/write on Projects, Analytic Accounting, Budgets. No admin rights needed.
Older Odoo? The three-plan analytic structure landed in
Odoo 17. Earlier versions used a single analytic axis, which works for
some donor reporting but not for combined per-project + per-donor
drill-down. If you're on Odoo 16 or older, we recommend planning an
upgrade before deploying togrant.com — let's talk about timing.
What we don't do
To keep the integration boring (in a good way):
No journal entries. Finance keeps full control of the chart of accounts.
No supplier or customer creation in Odoo. We mirror donors as togrant.com records, not Odoo partners.
No fiscal-year configuration. We read your tenant's fiscal-year-start setting; we don't change Odoo's.
No payment processing. Bank reconciliation stays in Odoo.