[18.0][ADD] account_statement_import_online_ramp - #950
Open
dnplkndll wants to merge 1 commit into
Open
Conversation
New OCA online-statement provider that imports Ramp corporate-card transactions via the Ramp Developer API. A Ramp organization has one credit line shared across all cards, billed as a single monthly statement and paid via one ACH transfer. The module follows that accounting reality: one Ramp credit line maps to one Odoo bank-type journal, regardless of how many cards are issued under it. Key features ============ * OAuth2 client_credentials auth — the module mints and caches a short-lived bearer keyed on the provider record. Refreshes transparently on expiry or HTTP 401. * Concurrent-safe token refresh — SELECT ... FOR UPDATE on the provider row plus a post-lock cache re-check ensures two parallel cron runs on the same provider can't mint two separate bearers. * Transient-failure retry — urllib3 Retry adapter mounted on https://: 3 tries with exponential backoff on 502/503/504. Applied to both the token mint and the data-fetch session. * SSRF / bearer-leak guard on pagination — Ramp's `page.next` URL is validated to be HTTPS on the configured Ramp host (hostname comparison, port- and case-tolerant) before being followed, so a tampered response cannot redirect the bearer. * Bounded pagination — fetch loop caps at 1000 pages with an explicit UserError if Ramp's cursor never terminates. * State filter — transactions in DECLINED, PENDING_INITIATION, or ERROR are skipped before producing statement lines. * Sign convention — Ramp returns positive amounts on spend; the module flips the sign so spend lands as a negative line on the credit-line liability journal (standard Odoo bank-statement convention). * raw_data preservation — the full Ramp transaction payload including card_id, user_id, sk_category_name, accounting_categories is stored on each statement line for downstream automation. 35 mocked-HTTP tests covering: service registration, single/multi- page cursor pagination (and max-pages safeguard), mapping, sign flip, state filtering, OAuth2 token cache hit/miss/refresh, missing-token and missing-expires_in edge cases, 401 → refresh → retry, network errors, missing-credentials guard, SSRF cursor guard (cross-env, port-tolerant, case-insensitive, user-authority bypass).
dnplkndll
force-pushed
the
18.0-add-account-statement-import-online-ramp
branch
from
May 12, 2026 13:38
92e7415 to
3aa1064
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a new online-statement provider that imports
Ramp corporate-card transactions via the
Ramp Developer API, mirroring
the existing Plaid and Mercury providers.
Ramp is a US corporate-card and spend-management platform. A Ramp
organization has one credit line shared across all cards, billed as a
single monthly statement and paid via one ACH transfer. The module
follows that accounting reality: one Ramp credit line maps to one
Odoo bank-type journal, regardless of how many cards are issued under
it.
Endpoints used
POST /developer/v1/token— OAuth2 client_credentials. Mints ashort-lived bearer cached on the provider record; refreshed on
expiry or HTTP 401.
GET /developer/v1/transactions— paginated card-transaction feedfiltered by date range; cursor pagination via
page.next.Key features
Plaid Link). Closer in shape to the Mercury provider.
SELECT ... FOR UPDATEon theprovider row + post-lock cache re-check, so two parallel cron runs
on the same provider don't mint two separate bearers.
urllib3.Retryadapter mounted onhttps://with 3 tries, exponential backoff, retrying on 502/503/504.Applied to both token mint and data fetch.
page.nextURL is validated to be HTTPS on the configured Ramphost (
.hostnamecomparison, port- and case-tolerant, user-authoritybypass rejected) before being followed.
explicit
UserErrorif Ramp's cursor never terminates.DECLINED,PENDING_INITIATION, orERRORare skipped at fetch time;CLEAREDandPENDINGare kept.module flips the sign so spend lands as a negative line on the
credit-line liability journal (standard Odoo bank-statement
convention).
(including
card_id,user_id,sk_category_name,accounting_categories) is stored on each line so downstreamautomation can use it without a second API call.
Tests
35 mocked-HTTP tests, all green locally:
Coverage:
partner_namefallbackpersist-to-record, default-expires-in, null-expires-in,
missing-access-token, missing-credentials, rejected-credentials
host, plain HTTP, cross-env (sandbox↔prod),
user@authority bypassEnd-to-end smoke test
Module installed cleanly into a fresh Odoo 18 DB, provider configured
against a real Ramp production account, OAuth2 token minted
successfully (after fixing initial client_id/secret swap), and the
/transactionsrequest returned a well-formed (empty) result set foran org with no card spend yet. Re-pull once real transactions accrue.
License
AGPL-3, matching the sibling Plaid and Mercury providers.
Checklist
pre-commit run --all-filesclean (ruff, ruff-format,prettier, pylint_odoo, whool-init, oca-gen-addon-readme).
readme/fragments provided;README.rstauto-generated.OCA 18.0 → 19.0 migration wiki:
no
self._cr/_uid/_context, nopytz, nourljoin,no deprecated
SUPERUSER_IDimport, nogroups_idXML,no demo-data dependency in tests, no
odoo-test-helper,no
migrations/directory. Already on the 19.0 trajectory.[18.0][ADD]commit (squashed from review iteration).Configuration screenshot
The "Ramp Configuration" group only appears when Service = Ramp. Client Secret is rendered as a password field (masked). Demo values shown above; no real credentials.