> ## Documentation Index
> Fetch the complete documentation index at: https://help.stralos.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Contractor copy and refresh

> How stralos-api-global copies a contractor company's suppliers, rates, settings and activities into a client company, and refreshes them later.

`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](https://help.stralos.app/api/global.md#contractor-copy-and-refresh) in the Global API reference. The Rates and Activities sides are described under [Rates contractor copy](https://help.stralos.app/internal/rates-service.md#contractor-copy) and [Activities contractor copy](https://help.stralos.app/internal/activities-service.md#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:

| Seq | Step | Service | Group |
| - | - | - | - |
| 1 | `global.settings` | Global | `companySettings` |
| 2 | `global.rate_categories` | Global | `rateCategories` |
| 3 | `global.suppliers` | Global | `suppliers` |
| 4 | `global.own_locations` | Global | `locations` |
| 5 | `rates.items_options` | Rates | `rates` |
| 6 | `rates.pricing_rules` | Rates | `rates` |
| 7 | `activities.library` | Activities | `activities` |

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`:

| `source` | Message shown on the refresh page |
| - | - |
| `copy` | "Prices set at your locations come across the same way you chose when you first brought them in." |
| `none` | "This company wasn't set up by copying from your company, so prices set at your own locations aren't brought across. Prices for all locations and supplier prices are." |

## 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.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.