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-odooandodoo-xmlrpcclient 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 API —
src/app.tsbuilds an Express app withhelmet,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 inapp.ts. - Process bootstrap —
src/index.tsis the entry point. It first callsodooConnection.connect(), logs successful Odoo authentication, then starts the HTTP listener onPORT(default9900) and triggerssetup.runHydration(). The cron scheduler and NATS / DB-event bindings are present but currently commented out insetup.ts, so day-to-day execution relies on the hydration path plus the HTTP routes. - Setup orchestration —
src/setup.tsexposesconnectNats(),connectDbEventListeners(),runHydration()andconnectJobs(). The NATS client isnode-nats-streaming; outages are surfaced through a Slack notifier (src/notifications). Hydration delegates toeProcrementJOB.hydrate()insrc/services/core/jobs. - Odoo client layer —
src/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,warehouseand a top-levelodoo.model.tswith shared CRUD helpers.actions.tsandconfig/group reusable operations and configuration. - Domain services —
src/services/contains the business logic that consumes records from the source systems and pushes them through the model wrappers. Thecore/subtree mirrors the Odoo entity set (company,country,invoice,payment-request,product,sales-order, plus ajobs/runner). Sibling folders cover specialised flows:api-key,company(with a dedicatedco.odoo.service.ts),csvfor spreadsheet ingestion,expense,payment-jornal,product,product-category, andwarehouse. - Event consumption —
src/events/holds NATS listeners (listener.ts,queue-group.ts) and per-domain handlers undercompany/andinvoice/. Database-driven listeners live alongside theapp/eprocandapp/marketplacemodules and are spun up fromsetup.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, andsrc/app/schedulerexposes aCronSchedulerthat usesnode-cron/cronfor time-based jobs. - Caching —
src/cache/andsrc/redis/provide both Redis-backed and on-disk caches (per-entity caches such ascacheCompany,cacheInvoice,cacheProduct); the disk caches are flushed at startup bysetup.cleanups(). - Persistence — Sequelize is configured (the repo ships an
.sequelizercfile andsrc/database/) with apgdriver. The service keeps its own bookkeeping tables (job state, sync ledgers) separate from the Odoo instance. - External integrations —
aws-sdk,googleapis,@google-apps/chat,google-auth-libraryare pulled in for object storage, Google Sheets / Drive based data feeds and chat notifications. Slack webhooks back the operational alerts insrc/notifications. - CRM bridge —
src/CRM/holds clients for Vendease's CRM (company,contact,country,deals,procurement,user, plus a sharedhttp/layer), used to mirror partners and deals out of Odoo. - VTP loaders —
src/vtp/ships anxlsx-based loader plus several reference spreadsheets (commercial supplier prices, location costs, product data dumps) and animage-upload.tsfor 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/eventstranslate 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 bus —
node-nats-streamingis the wire protocol for cross-service events (configured vianats_cluster_id,nats_client_id,nats_url). Consumers register under a queue group (events/queue-group.ts). - HTTP webhooks —
/api/v1/webhooksaccepts inbound callbacks;/api/v1/jobsand/api/v1/productsgive operators the ability to trigger or replay a sync. - Spreadsheets — bulk price / location data from the VTP files in
src/vtp/and CSVs handled bysrc/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. - CRM —
src/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-sdkfor 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 port9900, runsnpm start.sec.yaml— a KubernetesSecretmanifest for thecosteasy-stagingnamespace holding the TLS certificate and key forcosteasy-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, withcore/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 theenginesfield).npm run start:dev— clean, build, then run the compiled output undernodemon(config innodemon.json).npm run build—tsc -p tsconfig.jsonafter wiping./build.npm start/npm run prod:build— production-style startup; the latter chainsclean,buildandstart.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 withts-jest, configured inpackage.jsonand usingsrc/test/setup.ts.npm run install:common— re-installs the in-housevendease-commonpackage, which provides the shared logger,ServerResponse,ServerErrorHandler, NATS event base classes and similar utilities.
Container build:
Dockerfileusesnode:14.18.1, copies the repo, runsnpm install, exposes9900and runsnpm start(which itself rebuilds before booting).infra/carries deployment manifests for the Kubernetes cluster;sec.yamlprovisions 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#enginesand theDockerfilepin Node 14, which is past upstream end-of-life. Many of the runtime dependencies (express4,axios1,googleapis144,aws-sdk2.1692) still install cleanly there, but any future upgrade has to be planned around this floor. - Hydration runs at every boot —
src/index.tscallssetup.runHydration()unconditionally on successful Odoo connection. The hydrate routine inservices/core/jobsis 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 code —
setup.connectJobs()andCronScheduler.start()are commented out insetup.ts/src/index.ts, and only the eProcurement DB event listener is wired inconnectDbEventListeners(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
closeevent by callingprocess.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,/invoicesand/webhooksdirectly under/api/v1with only the globalhelmet,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 files —
companies.jsonandproducts.jsonat 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.xlsxand the datedproduct-data-21-10-2025*.xlsxare 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 repository —
sec.yamlships 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 startup —
setup.cleanups()deletes the cache directories for invoices, companies and products on every boot, with each step wrapped in atry/catchthat 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
READMElinks to Google Docs and Sheets for the Odoo integration documentation and model mapping. The PDFs indocs/are snapshots of those external sources; they may drift from the live Odoo schema if not refreshed.