Skip to main content
stralos-api-global runs the copy and refresh orchestrator for contractor companies. A copy brings chosen parts of a contractor’s home company into a client company. A refresh later brings ticked changes across, one way only, from home to the client company. The orchestrator lives in src/contractor-copy/ and its routes in src/routes/contractor-companies.ts. For the endpoint contract, see Contractor copy and refresh in the Global API reference. The Rates and Activities sides are described under Rates contractor copy and Activities contractor copy. Every job runs in the client company’s session. The caller must be a contractor person whose token came through an active link from the source company, and an admin of that contractor company.

Job steps

A job is a fixed list of seven steps, stored as contractor_copy_job_steps rows in the client company: A step that does not run is recorded as skipped when the job is created:
  • global.settings runs when companySettings is ticked.
  • global.rate_categories and global.suppliers run when their own group or rates is ticked.
  • global.own_locations always runs on a copy. On a refresh it runs when rates is ticked or a new location is ticked in apply.
  • The rates steps run when rates is ticked, and activities.library when activities is ticked. Ticking activities also ticks rates.
global.own_locations must run on a rates refresh even when no new location is ticked. It builds the map from each home location to the client company’s copy of it, found by provenance. Without that map, the Rates service cannot place a location price and skips it as not_chosen_location. Rows are matched by provenance (copied_from_org_id and copied_from_id), so re-running a step does not duplicate data.

Slices and leases

A job advances in slices so that it fits inside the API Lambda’s time limit without a separate worker. The request that starts a job runs steps until a 15-second budget is spent. Each later poll (GET /org/contractor-copy-jobs/:jobId) or resume continues from the first unfinished step. A slice takes a 45-second lease on the job. A poll that finds a live lease returns the job without advancing it, so two pollers never run the same job at once. If a slice dies, its lease expires and the next poll picks the job up. The rates and activities steps work in pages of 300 rows and save their page cursor and ID maps after each page, so a slice that stops mid-step loses at most the current page. Only one queued or running job may exist per client company, enforced by a partial unique index. A failed job can be resumed from its failed step. A finished job is never re-run; start a new job for another copy or refresh.

Location prices

A home company’s own prices can be set for all locations or for one location. On a copy, every home location comes across, and locationMap chooses which home location’s prices each copied location takes, or none. The Rates service writes a location price only where the map says that location takes it. A refresh never trusts a locationMap in its request. refreshLocationMap() in src/contractor-copy/runner.ts resolves the map, and createJob() stores it on the refresh job’s selection:
  • If the client company has a successful copy from this contractor, the newest copy’s stored map applies. A copy with no stored map is treated as an empty map, which means every copied location took its own prices.
  • If there is no successful copy, for example because the company invited the contractor rather than being created and copied into, every home location maps to none. Only all-locations prices and supplier prices refresh.
The refresh preview resolves the map the same way and reports which case applies in locationRates:

Refresh preview

POST /org/contractor-refresh/preview runs the Global steps in preview mode and asks the Rates and Activities services for a dry-run diff. For each group it returns changed rows (the home value beside the client company’s value) and new rows, plus home locations added since the copy under the locations group. Rows the client company created itself never appear, and a location price from a home location nobody chose is never offered.