# Stream a Fergus company

> Land a Fergus company's jobs, quotes, invoices, timesheets and schedule in your warehouse every hour, read with a personal access token, with nothing to approve.

A Fergus stream lands a Fergus company's jobs, quotes, invoices, timesheets, schedule and customer lists in your warehouse, as often as every hour. Each run reads the quotes that changed since the last run and the recent days of timesheets and bookings again. Jobs, stock on hand and the lists are read whole once a day.

Fergus is a **published source**, like [Bite](https://docs.sanda-os.com.au/streams/bite): sanda wrote the code that reads it and runs it as its own, so there is no connector for sam to write or for anyone to approve. An Admin or Full User generates a personal access token in Fergus, you type it in once, choose what to read, and set a schedule.

:::note
Fergus is in the catalogue once it is switched on for your console. Fergus's API terms ask that its data is used only in ways Fergus has approved, so sanda switches Fergus on only under a written agreement with Fergus that covers landing a company's data in your warehouse, your analysis of it, and sam's answers over it. If **Choose a source** doesn't list Fergus, ask sanda support.
:::

:::note
Fergus's terms don't allow its data, or anything made from it, to be sent to an advertising network. A push to an advertising network refuses anything that reads a Fergus stream's tables, or a view or model built on them, however many steps away. See [what a push never does](https://docs.sanda-os.com.au/streams/out#what-a-push-never-does).
:::

## Before you start

- **An Admin or Full User generates the token.** In Fergus only Admin and Full Users can generate a personal access token, and it reads with that user's permissions: if Fergus doesn't let that user edit employee rates, the token reads users' pay rates and charge-out rates empty. In sanda, only owners and admins connect a company and make a stream on it, because sanda reads it on the whole workspace's behalf.
- **One stream per company.** Fergus's API terms allow each company 500 API calls an hour, shared with every other app the company has connected. So a company has one Fergus stream, which runs at most every 60 minutes. Add what you want to read to that stream rather than making another.
- **A token lasts a year.** Fergus ends a token 365 days after it is made or last refreshed. Refresh it in Fergus before then: refreshing adds another 365 days, and brings back one that has already ended. Fergus also turns a token off when its user is removed or loses Admin or Full User access.

## Connect a company

:::steps
1. **Generate a token in Fergus.** As an Admin or Full User, go to **Settings · Integrations**, select **Fergus API**, and press **Generate PAT**. Copy the token: Fergus shows it only once. Fergus gives each user one token, so if yours is already used elsewhere, ask another Admin or Full User to generate one.
2. **Open Pull.** Go to **Data · Streams**, open the **Pull** tab and press the **Fergus** card under **Choose a source**. The new pull's editor opens on **Connect a Fergus company** when none is connected yet. Once a company is connected, **Connect a company** beside Fergus under **Connected** adds another.
3. **Paste the token** into **Personal access token**. **Name** is optional: leave it empty to name it **Fergus ·** and the company's name in Fergus.
4. **Press Check and connect.** sanda reads the company's details from Fergus with the token before it keeps anything, so a mistyped or ended token is a sentence under the form, and nothing is stored. The company is listed under Fergus in **Connected**.
:::

sanda keeps the token encrypted, and it is never shown again, in the console or to sam. **Type it again**, beside the company under **Connected**, replaces the token (after you generate a new one), and every stream on it carries on. A token for a different company is refused there, because a stream keeps the company it reads. Connecting a company that is already connected is refused too: type its token again instead. **Disconnect** forgets the token, and the company's stream waits until it is typed again. The token itself stays valid in Fergus until you delete it there.

## Make a stream

:::steps
1. **Start it.** Press the **Fergus** card under **Choose a source**, or **New pull** and then **Fergus**.
2. **Choose when it runs.** **When it runs** comes first. A Fergus stream runs at most every 60 minutes, so the faster choices are locked, with the reason beside them. See [when a stream runs](https://docs.sanda-os.com.au/streams/schedules).
3. **Name it.** Its tables are named after it: a stream called `fergus` lands jobs in `raw.fergus__jobs`.
4. **Choose the company**, and, if your workspace has more than one warehouse, the warehouse it lands in. Both are chosen once.
5. **Choose what it reads.** Everything is ticked. Untick what you don't need.
6. **Check its settings.** **Read history from** is where the first run starts reading time entries and calendar events: leave it empty to read the last 365 days. **Days to read again** is how many days of time entries and calendar events each run reads again (30 to start with). **Days ahead** is how far ahead the calendar is read (90 to start with). **Pay rates** says whether pay rates land.
7. **Press Save pull.** Saving reads nothing yet: press **Run now**, or wait for its schedule.
:::

## What it reads

| Tick | Lands in | Each run reads |
|---|---|---|
| **Jobs** | `raw.<stream>__jobs` | The whole list, once a day |
| **Quotes** | `raw.<stream>__quotes` | The quotes that changed since the last run |
| **Customer invoices** | `raw.<stream>__customer_invoices` | The whole list, once a day |
| **Time entries** | `raw.<stream>__time_entries` | The most recent days again (**Days to read again** says how many) |
| **Stock on hand** | `raw.<stream>__stock_on_hand` | The whole list, once a day |
| **Calendar events** | `raw.<stream>__calendar_events` | From **Days to read again** days back to **Days ahead** days ahead |
| **Enquiries** | `raw.<stream>__enquiries` | The whole list, once a day |
| **Customers** | `raw.<stream>__customers` | The whole list, once a day |
| **Sites** | `raw.<stream>__sites` | The whole list, once a day |
| **Contacts** | `raw.<stream>__contacts` | The whole list, once a day |
| **Users** | `raw.<stream>__users` | The whole list, once a day |
| **Company** | `raw.<stream>__company` | The company's own details, once a day |

Something the token's user can't see in Fergus is passed over for the run, which says so and reads everything else (see [when something goes wrong](#when-something-goes-wrong)).

### Quotes

Each run asks Fergus for the quotes changed since the newest change the last run saw, less 10 minutes, so a quote changed while the last run was reading isn't missed. Every version of a quote is a row of its own. The first run reads every quote the company has, whatever **Read history from** says, and a long history can take more than one run. A quote deleted in Fergus keeps its row, because Fergus's API doesn't say it went.

### Jobs and stock on hand

Jobs and stock on hand (the materials put on a job's phases) are read whole once a day, oldest first. Fergus can leave the time a job or a stock line last changed empty, and doesn't say where it lists those when asked for the newest changes first, so reading only what changed could miss them. Reading the whole list lands every one. The first run reads every job and stock line, whatever **Read history from** says.

sanda asks Fergus for jobs on hold and archived jobs too. Fergus doesn't delete jobs: it archives them, so an archived job keeps its row, with `archived` true. A stock line Fergus no longer lists is marked `_deleted_at` and kept.

### Time entries

Each run reads every time entry from **Days to read again** days before the day the last run read up to, through today, because an unlocked timesheet still changes after the day it was worked. A time entry deleted in Fergus within those days is marked `_deleted_at` and kept. The first run reads from **Read history from**, and a long history can take more than one run.

### Calendar events

Each run reads the schedule a week at a time, Monday to Sunday as Fergus's calendar shows it, from **Days to read again** days back to **Days ahead** days ahead, because bookings move and new ones are added for the weeks to come. An empty week is read and passed. A booking removed from the schedule within those days is marked `_deleted_at` and kept. Each booking is keyed by its id and its start, so a booking that repeats has a row for each start Fergus lists.

### The lists

Jobs, stock on hand, customer invoices, enquiries, customers, sites, contacts, users and the company are read whole once a day, whatever the stream's schedule, because each read spends the company's allowance. **Load everything again** reads them on its next run anyway. A row Fergus no longer lists is marked `_deleted_at` and kept, and reports and answers stop counting it.

Unless it's asked for a status, Fergus lists only the enquiries in `TODO`, and it doesn't say which users it lists. So a run asks for each status in turn: enquiries in `TODO`, `CONTACTED`, `JOBCREATED` and `REJECTED`, users who are `active`, `disabled` and `invited`.

### Left out

- **Job costs.** Fergus gives a job's costs, billed amounts and hours one job per request, which the company's hourly allowance can't carry for every job. sanda doesn't read them.
- **Job phases and their costs**, for the same reason: one request per job, or per phase.
- **Invoice line items**: one request per invoice.
- **Price books, tasks, notes and attachments.**
- **Purchase orders, supplier invoices and payments**: Fergus's API doesn't offer them. An invoice's amount paid, paid date and status are on **Customer invoices**.

## What lands in your warehouse

Every field becomes a column, and the whole record is kept in `_record`, as for every [pull](https://docs.sanda-os.com.au/streams/in#where-the-records-land). A few things are worth knowing when you model them:

- **Times on time entries are the company's own clock.** A time entry's `startTime`, `endTime` and `unchargedTimeStart` are the date and time of day as Fergus shows them on the timesheet. Fergus's API doesn't name a time zone for them, so they land as times without one. Times on calendar events carry their zone.
- **Hours and money are numbers**, as Fergus sends them: `paidDuration` and `unchargedTimeDuration` in hours, rates an hour, totals and tax in the company's currency. An invoice's `dueDays` is text, because Fergus gives either a number of days or a rule such as `20th_of_next_month`.
- **Ids are numbers**, as Fergus sends them, except the company's `guid`. The `jobId` on a quote, an invoice, a time entry, a stock line or a calendar event names a row of jobs, once jobs have been read: a job made since the day's read of jobs lands with the next one.
- **Nested details are JSON**: a job's `customer`, `siteAddress`, `mainContact` and `activeQuote`, a quote's `sections` and `totals`, and a customer's contacts and addresses each land as a JSON column of their own.
- **People's contact details land**: names, email addresses, phone numbers and addresses, on jobs, quotes, invoices, time entries, calendar events, enquiries, customers, sites, contacts, users and the company. A time entry names the person who worked it, beside their pay rate, and a calendar event the person booked, often with a customer's name in its title. The editor says so under each.
- **Pay rates** land on users and time entries unless **Pay rates** says to leave them out. Then `payRate` is taken off before anything lands, and is in neither the column nor `_record`. A token whose user Fergus doesn't let edit employee rates reads users' pay rates and charge-out rates empty either way.

## Change a stream

**Edit**, on the stream's page, changes everything but the company and the warehouse. Something you take off keeps its table, with what it landed. A new **Days to read again** or **Days ahead** takes effect on the next run. A new **Read history from** date takes effect the next time everything is read from the start: press **Load everything again** on the stream's page. Leaving pay rates out takes effect for what is read after the change, so press **Load everything again** to read the rest again without them.

## Remove Fergus data

To stop sanda reading a company and take its data out:

:::steps
1. **Delete the pull.** **Delete pull**, at the foot of the stream's page, stops it reading.
2. **Disconnect the company.** Press **Disconnect** beside it under **Connected** on the **Pull** tab, and sanda forgets the token. Delete the token in Fergus too, under **Settings · Integrations · Fergus API**, and it stops working everywhere.
3. **Ask for its tables to go.** A deleted pull's tables stay in your warehouse, with everything they landed, as every pull's do. Ask sanda support to remove them, naming the stream.
:::

## When something goes wrong

| What you see | What to do |
|---|---|
| Fergus refused the personal access token sanda reads the company with | The token ended, was deleted, or its user was removed or lost Admin or Full User access. The stream waits, rather than failing again and again. Refresh the token in Fergus, or generate another, and press **Type it again** beside the company under **Connected** |
| Fergus wouldn't let this personal access token read something, so the run read everything else | The token's user can't see it in Fergus. Generate the token as an Admin, type it again, and the next run reads it |
| This company's Fergus API allowance is nearly used up, so sanda leaves the rest to its other apps until it resets | Nothing to do. The run landed what it read and stopped, and runs before the allowance resets are skipped. The next run after it carries on from the last page that landed |
| Fergus says this company's API allowance is used up until it resets | Nothing to do, as above. If it happens often, another app is spending most of the company's allowance |
| sanda waits until a time before asking Fergus again, so nothing was read | A run that started while the company rests is skipped. The first run after that time reads |
| Fergus allows one stream per company | The company already has a stream. Add what you want to read to it |
| That personal access token is for another company | A stream keeps the company it reads. Connect the other company as its own, with a stream of its own |
| Fergus asked sanda to slow down | Nothing to do: the run waits as Fergus asks, then carries on |
| Fergus didn't answer, or answered with an error of its own | Nothing to do: the run tries again, and the next run carries on from the last page that landed |

A run that stopped early says why on the stream's page, and the overview lists it under **Needs attention**. A failed run counts toward the stream pausing itself, as any stream's does; a run that stopped for Fergus's allowance, or was skipped while the company rests, does not. See [runs](https://docs.sanda-os.com.au/streams/runs).

## Limits

- A run makes at most 0.1 requests a second, so it stays under the 500 API calls an hour Fergus's terms allow each company and the 100 requests a minute its API allows, which the company shares with every other app it has connected. Each request asks for up to 100 records.
- When Fergus says 50 calls or fewer are left, the run lands that page and steps aside until the allowance resets. When Fergus asks for a wait longer than 5 minutes, the run stops there, and later runs skip until then.
- **Days to read again** is at most 90 days, and **Days ahead** at most 365.
- One run lands at most 20,000,000 records, so a long history is several runs.

A Fergus stream is billed like any pull: a charge for each run that does its work, and the rows it lands past what your edition includes. See [what streams cost](https://docs.sanda-os.com.au/streams#what-streams-cost).

:::links
- [Pull](https://docs.sanda-os.com.au/streams/in): Every way records come into your warehouse on a stream.
- [When a stream runs](https://docs.sanda-os.com.au/streams/schedules): How often, within which hours, on which days.
- [Runs](https://docs.sanda-os.com.au/streams/runs): What each run did, and what to do when one fails.
:::
