Skip to content

Vendease Integration Server

Overview

The Vendease Integration Server is a Node.js / TypeScript middleware service whose job is to keep two Vendease platforms in sync with the Odoo ERP. The package.json describes it as "a robust middleware service for Odoo integration and Vendease E-Procurement platform", and the README adds that the service exists to bridge invoicing data between the in-house systems and Odoo. In practice it sits between three sides:

  • the in-house E-Procurement platform (src/eproc/, src/services/core/) that produces invoices, products, sales orders and payment requests;
  • the in-house Marketplace / "PIB" platform (referenced from src/app/marketplace); and
  • the Odoo ERP instance, accessed through XML-RPC via the node-odoo and odoo-xmlrpc client libraries.

A second responsibility, layered on top of the Odoo bridge, is to push business records into the CRM (src/CRM/) and to act as a glue layer for the VTP ("vendor / location / pricing") spreadsheets that get loaded into Odoo (src/vtp/). The HTTP surface itself is small — the bulk of the work runs as NATS event consumers, scheduled cron jobs, and one-shot hydration runs that backfill historical data.

The repository targets Node.js 14.18.1 (pinned in both package.json and the Dockerfile) and ships TypeScript compiled to ./build before being executed by Node.

Architecture & Technology

The runtime layout is straightforward Express on top of a long-lived process that also owns background workers:

  • HTTP APIsrc/app.ts builds an Express app with helmet, cors, JSON and URL-encoded body parsing, a global error handler, and mounts a single versioned router at /api/v1 (src/routes/index.ts). The mounted areas cover product synchronisation, job control, invoice submission and inbound webhooks. Request logging and a generic 500 handler are wired in app.ts.
  • Process bootstrapsrc/index.ts is the entry point. It first calls odooConnection.connect(), logs successful Odoo authentication, then starts the HTTP listener on PORT (default 9900) and triggers setup.runHydration(). The cron scheduler and NATS / DB-event bindings are present but currently commented out in setup.ts, so day-to-day execution relies on the hydration path plus the HTTP routes.
  • Setup orchestrationsrc/setup.ts exposes connectNats(), connectDbEventListeners(), runHydration() and connectJobs(). The NATS client is node-nats-streaming; outages are surfaced through a Slack notifier (src/notifications). Hydration delegates to eProcrementJOB.hydrate() in src/services/core/jobs.
  • Odoo client layersrc/model/ contains a thin per-entity wrapper around the XML-RPC client. The wrappers mirror Odoo models the service has to write to: account-invoice, attachment, currency, employee, expense, partner, product, purchase-order, sale-order, stock-move, uom, warehouse and a top-level odoo.model.ts with shared CRUD helpers. actions.ts and config/ group reusable operations and configuration.
  • Domain servicessrc/services/ contains the business logic that consumes records from the source systems and pushes them through the model wrappers. The core/ subtree mirrors the Odoo entity set (company, country, invoice, payment-request, product, sales-order, plus a jobs/ runner). Sibling folders cover specialised flows: api-key, company (with a dedicated co.odoo.service.ts), csv for spreadsheet ingestion, expense, payment-jornal, product, product-category, and warehouse.
  • Event consumptionsrc/events/ holds NATS listeners (listener.ts, queue-group.ts) and per-domain handlers under company/ and invoice/. Database-driven listeners live alongside the app/eproc and app/marketplace modules and are spun up from setup.connectDbEventListeners.
  • Background work — two parallel mechanisms exist: src/cron-jobs/ ships a Bull-backed queue (queue.ts, queue-names.ts, consumer.ts, runner.ts, jobs.ts) using Redis as the broker, and src/app/scheduler exposes a CronScheduler that uses node-cron/cron for time-based jobs.
  • Cachingsrc/cache/ and src/redis/ provide both Redis-backed and on-disk caches (per-entity caches such as cacheCompany, cacheInvoice, cacheProduct); the disk caches are flushed at startup by setup.cleanups().
  • Persistence — Sequelize is configured (the repo ships an .sequelizerc file and src/database/) with a pg driver. The service keeps its own bookkeeping tables (job state, sync ledgers) separate from the Odoo instance.
  • External integrationsaws-sdk, googleapis, @google-apps/chat, google-auth-library are pulled in for object storage, Google Sheets / Drive based data feeds and chat notifications. Slack webhooks back the operational alerts in src/notifications.
  • CRM bridgesrc/CRM/ holds clients for Vendease's CRM (company, contact, country, deals, procurement, user, plus a shared http/ layer), used to mirror partners and deals out of Odoo.
  • VTP loaderssrc/vtp/ ships an xlsx-based loader plus several reference spreadsheets (commercial supplier prices, location costs, product data dumps) and an image-upload.ts for product photo handling.

The Express layer therefore acts as the synchronous front door (mostly webhooks and operator endpoints), while the bulk of synchronisation runs in the background workers and listeners that share the same process.

How It Fits With Other Systems

Inputs feeding the service:

  • E-Procurement Postgres database — the canonical source of invoices, products, payment requests and company / branch records. Listeners in src/app/eproc/events translate inserts and updates into Odoo writes.
  • Marketplace ("PIB") database — a parallel source for marketplace invoices and supporting records, listened to from src/app/marketplace.
  • NATS streaming busnode-nats-streaming is the wire protocol for cross-service events (configured via nats_cluster_id, nats_client_id, nats_url). Consumers register under a queue group (events/queue-group.ts).
  • HTTP webhooks/api/v1/webhooks accepts inbound callbacks; /api/v1/jobs and /api/v1/products give operators the ability to trigger or replay a sync.
  • Spreadsheets — bulk price / location data from the VTP files in src/vtp/ and CSVs handled by src/services/csv.

Outputs the service writes to:

  • Odoo — primary target. Every entity in src/model/ wraps a specific Odoo model; invoices, sales orders, purchase orders, partners, products, warehouses, stock moves, units of measure, currencies and attachments are all created or updated via XML-RPC.
  • CRMsrc/CRM/ mirrors selected entities (companies, contacts, deals, procurement records) into the CRM platform.
  • Slack / Google Chat — operational notifications when NATS drops, jobs fail, or hydrations finish (src/notifications, data-migration-google-chat/).
  • Object storage — AWS S3 via aws-sdk for attachments and image uploads (src/vtp/image-upload.ts).

Dependence direction: the service consumes events and reads data from E-Procurement, Marketplace and CRM, and writes the bulk of its output into Odoo (with secondary writes back into CRM). It is therefore the single authoritative bridge between Vendease's transactional platforms and the ledger held in Odoo.

Repository Layout

Top-level files:

  • package.json, tsconfig.json, nodemon.json, .eslintrc — TypeScript build, lint and watch configuration.
  • Dockerfile — Node 14.18.1 base image, exposes port 9900, runs npm start.
  • sec.yaml — a Kubernetes Secret manifest for the costeasy-staging namespace holding the TLS certificate and key for costeasy-staging-api.vendease.com. It is checked into the repo and is used by cluster tooling rather than the application itself.
  • infra/ — additional infrastructure manifests.
  • companies.json, products.json — empty placeholder JSON files at the repository root (both 0 bytes); they appear to be slots for one-off seed data and currently carry no content.
  • seeders/ — directory exists but is empty; reserved for seed scripts.
  • docs/ — three PDF references (Model mapping.pdf, Odoo integration.pdf, Odoo-Eprocuremnt Interface.pdf) shipped as the authoritative mapping between source-system fields and Odoo fields.
  • public/, logs/, coverage/, build/, node_modules/ — generated / runtime artefacts.

The application source lives under src/:

  • src/index.ts, src/app.ts, src/setup.ts — process entry, Express bootstrap and orchestration.
  • src/config/ — environment loader (Env), Odoo connection factory.
  • src/routes/ — HTTP endpoints grouped by capability area: products, jobs, invoices and inbound webhooks.
  • src/controllers/, src/middleware/ — request handlers and Express middleware.
  • src/services/, src/services/core/ — domain services, with core/ aligned to the Odoo entity set.
  • src/model/ — XML-RPC wrappers per Odoo model.
  • src/events/, src/app/eproc/, src/app/marketplace/ — NATS and database-event listeners.
  • src/cron-jobs/, src/app/scheduler/ — Bull queue and time-based scheduler.
  • src/cache/, src/redis/ — Redis and on-disk caches.
  • src/database/ — Sequelize models / repository for the service's own bookkeeping tables.
  • src/CRM/ — CRM client modules.
  • src/vtp/, src/eproc/ — input feeds (spreadsheets, e-procurement helpers).
  • src/notifications/ — Slack / Google Chat alerting.
  • src/webhook/, src/auth/, src/errors/, src/exceptions/, src/middleware/ — supporting concerns.
  • src/test/, src/test_data.ts, src/products-with-issues.ts — Jest setup, fixtures and ad-hoc diagnostic scripts.

Running, Building & Deploying

Local workflow comes from the README and package.json:

  • npm install — install dependencies (Node 14.18.1 is required by the engines field).
  • npm run start:dev — clean, build, then run the compiled output under nodemon (config in nodemon.json).
  • npm run buildtsc -p tsconfig.json after wiping ./build.
  • npm start / npm run prod:build — production-style startup; the latter chains clean, build and start.
  • npm run lint, npm run lint-and-fix, npm run prettier-format — formatting and lint hygiene.
  • npm test, npm run test:ci, npm run test:dev — Jest with ts-jest, configured in package.json and using src/test/setup.ts.
  • npm run install:common — re-installs the in-house vendease-common package, which provides the shared logger, ServerResponse, ServerErrorHandler, NATS event base classes and similar utilities.

Container build:

  • Dockerfile uses node:14.18.1, copies the repo, runs npm install, exposes 9900 and runs npm start (which itself rebuilds before booting).
  • infra/ carries deployment manifests for the Kubernetes cluster; sec.yaml provisions the staging TLS secret used by the ingress.

Runtime configuration is environment-driven. src/config reads variables through an Env loader; the codebase references at minimum port, nats_cluster_id, nats_client_id, nats_url, plus credentials for Odoo, Postgres, Redis, AWS, Google APIs, CRM and Slack. Local .env, .env.prod and .env.staging files exist for that purpose; their contents are not documented here.

There is no separate migration command — Sequelize migrations (if any) are expected to be applied through the standard sequelize-cli flow given the presence of .sequelizerc.

Operational Notes & Risks

  • Pinned Node 14.18.1 — both package.json#engines and the Dockerfile pin Node 14, which is past upstream end-of-life. Many of the runtime dependencies (express 4, axios 1, googleapis 144, aws-sdk 2.1692) still install cleanly there, but any future upgrade has to be planned around this floor.
  • Hydration runs at every bootsrc/index.ts calls setup.runHydration() unconditionally on successful Odoo connection. The hydrate routine in services/core/jobs is expected to be idempotent; restarts in the wrong window can otherwise add load on Odoo and on the source databases.
  • Listeners and cron currently disabled in codesetup.connectJobs() and CronScheduler.start() are commented out in setup.ts / src/index.ts, and only the eProcurement DB event listener is wired in connectDbEventListeners (the marketplace listener is commented out). Whatever is supposed to react to NATS events or scheduled triggers depends on these being re-enabled.
  • Single bridge to Odoo — because the service is the only writer for many Odoo entities (invoices, sales orders, partners), a hard outage stops the ledger from receiving updates. The NATS reconnect handler responds to a close event by calling process.exit(), which relies on the process supervisor (Kubernetes / Docker) to restart it.
  • Operator endpoints carry no auth in routes/index.ts — the router mounts /products, /jobs, /invoices and /webhooks directly under /api/v1 with only the global helmet, cors, body parser and error handler in front. Any access control is enforced inside the per-route handlers (src/auth/, src/middleware/); the wiring should be checked before exposing the service publicly.
  • Empty seed filescompanies.json and products.json at the repository root are present but empty (0 bytes). They look like seed-file placeholders that have been intentionally cleared; before treating them as inputs they need to be populated.
  • Bundled spreadsheets in src/vtp/commerical-supplier-price.xlsx, location-cost.xlsx and the dated product-data-21-10-2025*.xlsx are committed into the source tree. They are read at runtime, so updates ship via repository commits rather than out-of-band uploads.
  • TLS secret in repositorysec.yaml ships base64-encoded certificate and private-key material for the staging hostname. It belongs in a secret store; while it lives in the repo the keys must be treated as compromised the moment a repo clone leaves a controlled environment.
  • Disk cache cleanup at startupsetup.cleanups() deletes the cache directories for invoices, companies and products on every boot, with each step wrapped in a try/catch that swallows the error. A failed cleanup is silently logged and the service continues.
  • Slack notifier is the primary alert channel — failures in NATS are sent to Slack from setup.connectNats. If the Slack webhook is misconfigured, outages can go unnoticed even though they are recorded by the local logger.
  • Documentation is mostly external — the README links to Google Docs and Sheets for the Odoo integration documentation and model mapping. The PDFs in docs/ are snapshots of those external sources; they may drift from the live Odoo schema if not refreshed.