SAP CAP backend on Node.js and HANA Cloud automating business travel in SAP Concur: policy checks, per diem, travel agency, ServiceNow and payroll integration. Includes a routing service on Cloud Foundry for distances in km, with no paid APIs.

Backend application built for an Italian banking group ranked in the top five by total assets, extending SAP Concur with the reimbursement rules set by the national labour agreement and internal travel policy. The system consumes lifecycle events for travel requests and expense reports (Submitted, Approved, Cancelled, Rejected), applies the checks that standard Concur configuration cannot express, calculates the daily travel allowance and propagates results to Concur, the travel agency, ServiceNow and payroll files. It has no user interface: it is an OData V4 service layer invoked by SAP Integration Suite (Cloud Integration).
The group manages business travel in SAP Concur, but its reimbursement rules cannot be expressed in standard configuration: the daily allowance is built in thirds (lunch, dinner, overnight) on rates that depend on the destination city and the employee's contractual grade, is offset against actual meal expenses only for domestic trips, is withheld below a minimum number of travel days, and drops by 15% past the thirty-first or forty-sixth consecutive day. On top of that come the compliance checks — short-distance trips, on-site missions, high-risk countries, overlap with recorded absences — which back-office staff had to verify manually, one expense report at a time.
Work performed under a non-disclosure agreement: the client cannot be named and the code is not public. The downstream systems were not negotiable: attendance data lives in PeopleFocus, a SOAP service reachable only through Cloud Connector, and the travel agency accepts nothing but a proprietary XML envelope with descending document versioning. The actions are called by webhooks, so an unhandled error must not trigger retry storms. Several queries had to work around real SAP HANA limits (LIMIT not allowed in correlated subqueries, NCLOB columns for long payloads).
SAP Cloud Application Programming Model (CAP) application on Node.js, deployed to SAP BTP Cloud Foundry as an MTA archive (service, HANA deployer, approuter), secured with XSUAA.
Everything starts in Concur: webhooks on travel request and expense report events call Cloud Integration, which forwards the call to the CAP service untransformed. From there the logic lives entirely in CAP, which queries Concur for the details of the case, PeopleFocus for overlap with recorded absences, ServiceNow for operational data, and produces the XML envelope for the travel agency. Agency confirmations come back through a second Cloud Integration flow and update the cases in the database.
Outbound
SAP Concur --webhook--> Integration Suite --OData V4--> CAP service (Node.js)
|
+----------------+----------------+---------------+
v v v v
SAP HANA Cloud Concur REST API PeopleFocus ServiceNow
(28 entities) (v3/v4/v5) (SOAP, Cloud
Connector)
Inbound
CAP service --XML envelope--> Integration Suite --> travel agency
travel agency --confirmations--> Integration Suite --> CAP service --> SAP HANA Cloud
TRAVEL schema;.hdbprocedure) for bulk upserts, where row-by-row round-trips did not hold up;run-trace.js) posted to Cloud Integration for monitoring: outcome, severity, counters, per-request steps;assert-based self-checks, runnable with plain node, covering the calculation rules.Business travel used to run on a platform outside the SAP landscape, with a lower level of automation: the reimbursement rules set by the national labour agreement and internal policy still ended up under manual back-office review, one expense report at a time. The programme therefore had two goals: bring the whole process back inside the SAP perimeter, alongside the group's other systems, and let the system enforce the rules that until then depended on human checking.
The engine now calculates the allowance across all four scenarios — single-day and multi-day, domestic and foreign — and applies on its own the checks back-office staff used to run by hand: short-distance trips, on-site missions, high-risk countries, continuity between consecutive trips, the long-trip reduction, overlap with recorded absences. The approval, cancellation and rejection flows and the handover of the document to the travel agency are implemented end to end. The system has been delivered and is currently in user acceptance testing at the client.
Mileage reimbursement needs the real driving distance between two addresses. The options available — Google Distance Matrix and an equivalent service in the SAP ecosystem — are both metered, with thresholds that the travel volume of a banking population of several thousand people clears without effort: a recurring cost for every distance calculated, on a function the client did not want to make dependent on a paid service.
I proposed and built the alternative. OSRM is an open-source routing engine running on OpenStreetMap data: I validated it locally, built a Docker image carrying only the Italian map extract — the only perimeter in scope — and deployed it as a service on Cloud Foundry, next to the application. Distance calculation therefore stays inside the client's own infrastructure, with no calls to external services and no per-call cost, backed by a persistent coordinate cache that avoids recomputing recurring routes.
I have owned the CAP component as its only developer since late February 2026, with end-to-end technical ownership — data model, services, integrations and releases on BTP — working from the functional specifications maintained by the project analyst. The scope has grown in successive phases, from the first per-diem calculation scenarios through to the travel agency integration and the inbound confirmation flow.
The rules that are easiest to get wrong — continuity between consecutive trips, the long-trip reduction, header-versus-segment date alignment — are covered by runnable checks: a regression surfaces immediately, without having to replay a real travel request.