ThreadCount Community edition

Uniform stock management for healthcare linen rooms: the coordinator app, the phone counter and the staff app, for your own server. Built from f976bd5 on 2026-09-15. Licensed under the Functional Source License (FSL-1.1-ALv2).
This commit is contained in:
ThreadCount
2026-09-15 23:18:05 +10:00
commit c89010a4f1
424 changed files with 53588 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="320" viewBox="0 0 1280 320" role="img" aria-labelledby="t d">
<title id="t">ThreadCount</title>
<desc id="d">Uniform stock management for healthcare linen rooms. Community edition.</desc>
<rect width="1280" height="320" fill="#201e1d"/>
<!-- the mark: four rules stepping down, a stock count falling to its reorder point -->
<rect x="72" y="88" width="120" height="16" fill="#ec3013"/>
<rect x="72" y="128" width="96" height="16" fill="#f3f2f2"/>
<rect x="72" y="168" width="68" height="16" fill="#f3f2f2"/>
<rect x="72" y="208" width="44" height="16" fill="#f3f2f2"/>
<g font-family="Archivo, 'Helvetica Neue', Arial, sans-serif" fill="#f3f2f2">
<text x="240" y="150" font-size="84" font-weight="800" letter-spacing="-3">ThreadCount</text>
<text x="244" y="200" font-size="24" font-weight="500" fill="#b5b1af">Uniform stock management for healthcare linen rooms</text>
<text x="244" y="242" font-size="15" font-weight="800" letter-spacing="3" fill="#ec3013">COMMUNITY EDITION · RUN IT ON YOUR OWN SERVER</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+119
View File
@@ -0,0 +1,119 @@
# Writing the ThreadCount manual
The manual is the set of Markdown files under `docs/manual/<section>/<slug>.md`. The website
renders them at `/docs/<section>/<slug>` and the app renders the same files at
`/app/help/<section>/<slug>`, so one file serves both. The Community edition ships the folder.
## The one rule
**Every sentence must be true of the code as it is today.** Read the screen, the API route and
the library before you describe them. If you cannot verify a behaviour by reading the code, leave
it out. Never describe a feature that is planned, and never soften a limitation. Where a figure
comes from a constant (`lib/plan.ts`, `lib/sets.ts`), quote the constant's value.
## Front matter
```
---
title: Issue a garment
section: counter
order: 1
summary: Scan the garment, pick the person, record it. What is written, and what the counter refuses.
screen: Counter
role: Admin or Issuer
keywords: issue, scan, barcode, six sets, refusal, slip
---
```
- `section`: one of `start`, `counter`, `stock`, `people`, `reports`, `apps`, `account`,
`selfhost`, `reference`. Fixed list; the tree is built from it.
- `order`: position within the section, from 1.
- `summary`: one sentence, shown in the tree's search results and on the section index.
- `screen`: the app menu label or path the page is about, e.g. `Settings Data & audit log`. Omit if none.
- `role`: who can do it. One of `Admin`, `Admin or Issuer`, `Staff`, `Anyone`, `Self-hosting admin`.
- `keywords`: comma-separated words a coordinator would type into search. Include the words the
screens use AND the words a newcomer would use ("stocktake, count, audit").
## Body
Plain Markdown, this subset only:
- `## Heading` for sections. They are numbered automatically and listed in the rail. Four to
seven per page. No `#` or `###`.
- Paragraphs. Short. One idea each.
- `- item` bullet lists and `1. step` numbered lists (steps: bold the action first,
`1. **Scan the garment.** Then…`).
- Tables with `|`, a header row, then `|---|` then rows. Keep to four columns.
- Fenced code blocks with three backticks for commands and file contents.
- Callouts: a blockquote whose first line is a bold label.
`> **In plain terms** One sentence.` (navy: commentary)
`> **Careful** One sentence.` (red: a way to lose data or lock yourself out)
`> **In ThreadCount** …` is only for the guides; do not use it here.
- Inline `code` for settings, file names and values. Screen paths in code too: `Settings Data & audit log`.
- Links: `[text](/docs/stock/barcodes)` to other manual pages, `[text](/pricing)` to the site.
- **Bold** sparingly, never for whole sentences. No italics, no emoji, no headers inside callouts.
## Voice
- Australian English. Second person for the reader ("you issue"), the product by name or "it".
- Say what the screen does, then what it writes, then what it refuses and why. The
"What is written" table (Record · Change · Undo) belongs on every page about an action.
- Numbers as digits. Times as `07:40`. Dates as `13 Sep 2026`.
- No marketing, no adjectives about the product, no "simply", no "just". The manual explains.
- Sample data: the fictional **Riverside General** hospital, cost centres `RGH-4010` style,
suppliers **Northline Workwear** and **Harbour Embroidery** only, people like A. Hassan and
P. Nair, addresses at `example.com`. Never a real hospital, health service, supplier or person.
## Where to look
| Area | Read |
|---|---|
| Screens | `app/app/<screen>/page.tsx` and the components they import |
| Mutations | `app/api/mutate/route.ts` and `lib/ops.ts` (the `op` names and their checks) |
| Rules | `lib/sets.ts` (entitlement), `lib/plan.ts` (plans, limits), `lib/switches.ts` |
| Data | `prisma/schema.prisma` |
| Imports | `lib/csv.ts`, `app/api/import*`, `Settings Data & audit log` in `components/settings/DataAudit.tsx` |
| Phone apps | `app/m/**` (counter app), `app/my/**` (staff app), `lib/nativescan.ts` |
| Self-hosting | `README.md`, `docker-compose.yml`, `.env.example`, `Dockerfile`, `lib/edition.ts` |
| Billing, SSO, users | `lib/billing.ts`, `components/PlanTab.tsx`, `lib/sso.ts`, `components/SsoSettings.tsx`, `components/TwoFactor.tsx` |
| What each email says | `lib/accountmail.ts`, `lib/approvallink.ts`, `lib/billing-mail.cjs` |
## Hard constraints
- Never name a real hospital, health service, government health department or uniform supplier,
not even as an example. Use Riverside General, Northline Workwear, Harbour Embroidery.
`scripts/check-identity.sh` and the manual linter both fail a page that does.
- No personal email addresses. Product addresses only: hello@, support@, privacy@,
security@threadcount.tech.
- Never copy a secret, key or token into a page.
- Read-only for these files, never edit them: `app/m/**`, `app/my/**`, `components/screens/**`,
`components/m.tsx`, `components/my.tsx`, `components/staffui.tsx`, `components/staffnav.tsx`,
`components/MAuth.tsx`, `components/MPerson.tsx`, `components/MScan.tsx`, `androidshell/**`,
`staffshell/**`.
- Write only inside your own section folder. Do not run git, the dev server or a build.
- Length: 400 to 900 words a page, 4 to 7 `##` sections. The CSV reference may run to 1400.
## Every page in the manual
Link only to these. A link to anything else is a broken link.
| Section | Slugs, in order |
|---|---|
| `start` | threadcount-in-one-page, set-up-in-an-afternoon, the-two-roles, your-first-order |
| `counter` | issue-a-garment, exchanges-and-returns, requests-from-staff, manager-approvals, pickup-call-list, delivery-rounds, slips-and-signatures |
| `stock` | catalogue-sizes-and-cuts, barcodes, reorder-levels, stocktakes, order-list, receiving-and-back-orders, suppliers |
| `people` | staff-register, groups-and-routes, entitlement-rule, managers, deactivating-and-deleting |
| `reports` | the-nine-reports, cost-centres, journal-export, month-end-pack |
| `apps` | counter-app, staff-app, scanning-and-browsers |
| `account` | users, two-factor, single-sign-on, plan-and-billing, export-and-backup, delete-an-account |
| `selfhost` | install, first-run, updating, backups, email, configuration-reference |
| `reference` | csv-templates, glossary, keyboard-and-scanner |
A manual link is `/docs/<section>/<slug>`. Site pages you may also link: `/pricing`, `/compare`,
`/security`, `/security/questionnaire`, `/sla`, `/privacy`, `/terms`, `/delete-account`,
`/changelog`, `/demo`, `/about#contact`, and the guides under `/guides/`.
## When you finish
Reply with the files written, then a short list of anything you could not verify in the code and
therefore left out. That list is how the manual's gaps get found, so be specific.
+73
View File
@@ -0,0 +1,73 @@
---
title: Delete an account
section: account
order: 6
summary: Delete your login or the whole facility, clear data without deleting anything else, remove a wearer's staff sign-in, and what stays behind.
screen: Settings People & sign-in
role: Admin or Issuer
keywords: delete account, close account, delete facility, remove login, wipe, wipe activity, start fresh, empty this facility, reset, empty facility, staff app access, remove access, privacy, erase data
---
## The two cases
`Delete my account` is the last heading on `Settings People & sign-in`. What it deletes depends on whether anyone else can still sign in.
- **Other active users remain.** Only your login goes. The facility and its records stay.
- **You are the last active user.** Your account and the whole facility go together.
The note beside the heading says which case you are in: `the facility and its records stay`, or `deletes the facility and everything in it`. The public page at [/delete-account](/delete-account) explains the same thing without signing in.
## Delete your login
1. **Press `Delete my account`.** The box asks `Delete your login?`
2. **Type your password.**
3. **Press `Delete my login`.** You are signed out and taken to the home page.
Your user row is deleted, not deactivated. Issues, stocktakes and slips you recorded keep the name they were stamped with.
If you are an admin and other users remain but no other active admin, it is refused: `You're the only admin left. Make someone else an admin first, or delete the whole facility.`
## Delete the facility
1. **Take a backup.** `Download a backup first` sits above the button. See [Export and backup](/docs/account/export-and-backup).
2. **Press `Delete my account and this facility`.**
3. **Type your password, and type the facility name exactly.**
4. **Press `Delete everything`.**
Every user, the catalogue and barcodes, the register, and every issue, return, order, delivery, stocktake, request, photo and signature are deleted at once. It cannot be undone. This needs an admin account.
| Record | Change | Undo |
|---|---|---|
| Your user | Deleted | None |
| Facility | Deleted with everything in it | None; a backup file can be restored into a new facility |
Deleting works in a read-only facility. In the demo the button is switched off.
> **Careful** Deleting the facility does not stop a card plan. Press `Stop at period end` on `Settings Plan` first.
## Clear data, keep the account
Two boxes on `Settings Data & audit log`, above the audit log, remove data and leave everyone signed in. Only admins see them.
| | Wipe recorded activity | Start fresh |
|---|---|---|
| Confirm | Type `WIPE`, press `Wipe activity` | Type `RESET`, press `Empty this facility`, then confirm |
| Deletes | Issues, orders, pickups, stocktakes, stock moves, approvals, alterations, hand-ins, staff-app requests and messages, waitlist, kit checks, damage reports, record queries, the ward notice, photos | Everything in Wipe, plus the catalogue, barcodes, stock levels, departments, suppliers, locations, the register and its staff sign-ins |
| Keeps | Catalogue, register, departments, suppliers, barcodes, locations, opening balances, users | Users, facility name and settings |
| Also | Stock adjustments and pre-loved counts set to 0; order and request numbers restart | Order, catalogue and request numbers restart |
Both happen at once and cannot be undone.
## Staff sign-ins
A wearer cannot delete their staff sign-in from the staff app. An admin opens their record in `People`, goes to the `Details & access` tab, and in the `Staff app` panel presses `Remove access`, then confirms. The sign-in is deleted and they are signed out. A new code lets them back in later.
Their register entry and issue history stay, because they are the facility's stock records. Removing those is covered in [Deactivating and deleting](/docs/people/deactivating-and-deleting). A wearer who would rather not ask their coordinator can write to privacy@threadcount.tech.
## What is kept
- **Your name on records,** when only your login was deleted.
- **Backup files you downloaded.** They are on your own device. Delete them yourself.
- **Nothing in the facility,** once the facility is deleted. Its rows and its photo files go together.
The [privacy policy](/privacy) describes the hosted service's own encrypted backups.
+71
View File
@@ -0,0 +1,71 @@
---
title: Export and backup
section: account
order: 5
summary: The one-file backup, CSV exports, the last-backup date, what a restore replaces and refuses, and whose backups are whose.
screen: Settings Data & audit log
role: Admin
keywords: backup, export, export backup, download, restore, import backup, JSON, CSV, export CSV, last backup, data, audit log, recovery, take my data, leave
---
## The backup file
`Export backup` on `Settings Data & audit log` downloads the whole facility as one JSON file, named for the date where your facility is, such as `threadcount-backup-2026-09-13.json`. `Download backup` on `Settings Plan` gets the same file. Only admins can take it.
The file holds the facility's settings and every record. That covers the catalogue, barcodes, stock and its movements, and locations. It also covers departments, suppliers, the register, manager approvals, issues, orders and their deliveries, pickups, stocktakes, hand-ins and cost changes. The staff app's side is in it too: requests and their messages, the waitlist, kit checks, damage reports, record queries and the ward notice. Photos and signatures are written into the file.
Left out on purpose:
- users and staff sign-ins, because those rows hold passwords;
- ThreadCount's own notes about your plan.
The newest photos travel, up to 2000 photos or 40 MB. Older ones are counted and left out, so the file stays small enough to restore. After `Export backup`, the line under the buttons says how many were left out. The records they belong to are all in the file, and the images stay on the server.
The backup still downloads when the facility is read-only.
## CSV exports
`Export CSV` appears on `People`, `Requests`, the `On hand` and `Locations` tabs of `Stock`, the order ledger at `Orders All orders`, and `Reports`. On the `Count` tab, each past stocktake in the history has its own `CSV` button. An order's own page has `CSV` under `More` at the top. In Settings it is on the supplier list, the departments and cost centres list, and the audit log. The files are for reading and for finance; see [Reports](/docs/reports/the-nine-reports).
Only the backup file restores a facility. A CSV export does not change the last-backup date.
## The last-backup date
The `Backup` heading on `Settings Data & audit log` shows `Last backup` with the date and how many days ago, or `No backup taken yet.` Issuers see this line too. Past 7 days, or with no backup at all, it is marked in red, and admins see `!` beside `Settings` in the menu.
Each download of the backup file, from either screen, sets the date.
## Restore a backup
1. **Open `Settings Data & audit log` and choose `Import backup`.**
2. **Pick a ThreadCount backup file.**
3. **Confirm.** The warning says the restore replaces all data in this facility, and that users are kept.
Everything in the facility is deleted and rebuilt from the file, facility settings included. The facility name, slips, groups, sets and time zone all come from the file. It keeps the users, single sign-on settings and plan. Staff sign-ins survive, re-attached to register entries by staff number.
| Record | Change | Undo |
|---|---|---|
| Facility | All records replaced by the file's | Restore an earlier file |
| Facility | Settings replaced by the file's | Restore an earlier file |
| Staff sign-ins | Re-attached by staff number | None needed |
A restore is refused when:
- the file is not a ThreadCount backup;
- the facility is read-only;
- the file holds more staff records than the plan allows, 60 on Hosted Small;
- the file exceeds a count limit, such as 20,000 staff or 300,000 issues;
- you have made 20 restores or imports in 10 minutes;
- the upload is over 60 MB.
Photos beyond 2000 in a file are dropped rather than refused, and the line under the buttons says how many. Keep that file, because those images exist only in it.
> **Careful** A restore replaces what is there now. Take a fresh backup first if today's records matter.
## Your backups and the service's
The `Backups` row on `Settings Plan` shows the hosted service's own nightly backups: 14 days on Hosted Small, 35 days on the other plans. What they commit to is in the [Service Level Agreement](/sla) and on [Security](/security).
No screen restores one facility from those backups. The only restore in the app is `Import backup` with a file you hold.
The [Terms](/terms) make exporting and keeping regular backups the facility's own responsibility. Store the file under your records policy; it holds the register. Self-hosted servers need a backup of their own, as described in [Backups](/docs/selfhost/backups).
+92
View File
@@ -0,0 +1,92 @@
---
title: Plan and billing
section: account
order: 4
summary: The hosted plans and prices, the trial, paying by card or invoice, grace and read-only, and every billing email.
screen: Settings Plan
role: Admin
keywords: plan, billing, price, pricing, trial, subscribe, card, invoice, receipt, tax, GST, ABN, monthly, yearly, cancel, stop, read-only, grace, grandfathered, health service, purchase order
---
## Plans and prices
Prices are in Australian dollars before tax, from `PRICES` and `PLANS` in `lib/plan.ts`.
| Plan | Price | Staff records | Backups kept |
|---|---|---|---|
| Hosted Small | Free | 60 | 14 days |
| Hosted Facility | $129 a month or $1,290 a year | No ceiling | 35 days |
| Health Service | $4,990 a year for 5 facilities, $890 each extra | No ceiling | 35 days |
On Hosted Small the 60 counts every staff record, deactivated ones included. Past it, adding a person is refused with `The register is full for this plan — 60 staff records.`
A facility inside a health service takes that plan, shows a `Health service` row, and is invoiced yearly against the health service's purchase order.
A facility created before plans went live is grandfathered. Its status reads `Free · everything included, for good`, and there is nothing to buy.
`Plan` is the last item in the `Settings` side list. It is listed for admins only, and not in the demo. The Community edition has no plans.
## The trial
When you create a facility, choose Hosted Small, or a 30-day Hosted Facility trial. Neither takes a card. During the trial the status reads `Trial` with the days left.
## Pay by invoice or card
`Request an invoice` sends ThreadCount a notice naming your facility and the plan chosen: `Hosted Facility · annual` or `Health Service · annual`. ThreadCount raises the invoice to the `Billing contact` and records the payment. You can ask up to 3 times a day. A renewal is requested the same way.
`Subscribe by card` appears only when card payments are set up on the server, and not while the plan is paid. It opens `Subscribe`, a page in four steps for Hosted Facility:
1. **Plan.** Monthly $129 or yearly $1,290.
2. **Business.** Legal name, address, city and country are required, and so is the postcode, except in Ireland, Hong Kong, the United Arab Emirates and Macao. Tax ID is optional. `Billing email` is your billing contact and cannot be edited here.
3. **Agreements.** Tick the Terms of Service, Privacy Policy and Service Level Agreement. The time of the ticks is recorded.
4. **Card.** Press `Continue to card`, type the card into Stripe's fields on the page, then `Pay`. The total includes tax, which Stripe works out from the country and address. Card numbers never pass through ThreadCount.
Paying during a trial charges that day, and the paid period starts then. The Plan screen shows the plan as paid once Stripe confirms the payment.
## The Plan screen
The `Plan` table shows `Status`, `Staff records` (such as `42 of 60`), `Backups`, the end date (`Trial ends`, `Paid until` or `Writable until`), `Paid by`, `Invoiced to` and `Billing contact`. `Save contact` changes the billing contact.
A facility that pays by card also gets `Billing`, with `Card on file`, `Next charge` and `Cadence`, and `Invoices`, each with a PDF link.
- **`Update card`** takes a new card on the same page. It is used for every later charge, and any unpaid invoice is charged straight away.
- **`Stop at period end`**, then **`Yes, stop it`**, stops the renewal at the end of the paid period. Nothing more is charged, and nothing is refunded. `Keep my plan` undoes it before that date.
| Record | Change | Undo |
|---|---|---|
| Facility | Billing contact | `Save contact` again |
| Facility | Business details and agreement times | Subscribe again |
| Card plan | Card replaced | `Update card` |
| Card plan | Set to stop at period end | `Keep my plan` |
## Grace, then read-only
When a trial or a paid period ends unpaid, 14 days of grace follow. The status reads `Ended · still writable`, with `Writable until`.
After grace the facility is read-only. Changes are refused with `Read-only: this facility's plan has lapsed.` Nothing is deleted. A failed card payment changes nothing until 14 days after `Paid until`.
Still working when read-only:
- signing in, reports, CSV exports, printing and the backup download;
- the billing contact and `Request an invoice`;
- your own name and password, and deleting your account.
Restoring a backup is refused. Writing starts again when a payment is recorded.
## Billing emails
These go to the billing contact, or to every active admin if none is set.
| Email | Sent |
|---|---|
| Trial started | When the facility is created on a trial |
| Five trial notes | On days 1, 3, 7, 21 and 28 of the trial |
| Days left on your trial | Once in the last 7 days, then the day before |
| Trial ended | The day the trial ends |
| Now read-only | The day grace runs out |
| Receipt | Each card payment above zero |
| Payment did not go through | A card payment fails, once a day at most |
| Card updated | A new card is saved |
| Plan will end, or has ended | A card plan is set to stop, and when it ends |
These trial and read-only emails are checked daily and sent once each. Facilities paying by card, grandfathered ones and health-service members do not get them.
+72
View File
@@ -0,0 +1,72 @@
---
title: Single sign-on
section: account
order: 3
summary: Connect your facility's identity provider, route people to it by email domain, make it optional or required, and keep a break-glass admin.
screen: Settings People & sign-in
role: Admin
keywords: single sign-on, SSO, SAML, OpenID Connect, OIDC, Microsoft Entra, Azure AD, Okta, Google Workspace, identity provider, IdP, email domain, required, break-glass, metadata, disconnect
---
## What it does
Single sign-on lets the people at your facility sign in with the work account they already have, through your own identity provider: Microsoft Entra, Okta, Google Workspace, or any provider that speaks SAML or OpenID Connect.
An admin sets it up for one facility under the `Single sign-on` heading on `Settings People & sign-in`. On a server with no sign-on service it says `Single sign-on isnt available on this server.` It is not offered in the demo. Users who are not admins see one line saying whether the facility uses it.
Single sign-on never creates an account. The address your identity provider returns must already belong to an active user at your facility, added as described in [Users and passwords](/docs/account/users). Anyone else is told that the address has no account at this facility yet.
## Connect your identity provider
1. **In your identity provider, add an application for ThreadCount.** The panel shows the two values it asks for: the ACS or redirect URL, ending in `/api/auth/sso/callback`, and the Entity ID.
2. **Give ThreadCount the provider's metadata.** Paste its `Metadata URL`, which must start with `https://`, or paste the XML into `…or metadata XML`.
3. **List your email domains,** comma-separated. A domain is the part after the @, such as `example.com`.
4. **Press `Connect single sign-on`.**
The metadata is handed to the sign-on service and not shown again. The panel then reads `Connected`, with the provider's name when it has one. A later change to the domains is saved with `Save domains`.
Domains are checked each time they are saved:
- 10 at most.
- Public mail services such as `gmail.com` and `outlook.com` are refused.
- A domain belongs to one facility. One already registered elsewhere is refused.
| Record | Change | Undo |
|---|---|---|
| Facility | Connected, domains saved | `Disconnect single sign-on…` |
| Facility | `Require` or `Staff may use it too` switched | Untick it |
| User | Marked break-glass | Untick it |
| Facility | Disconnected, every switch off | Connect again |
## Routing by email domain
ThreadCount has one Log in screen for every facility, so it routes by address. When someone types an address ending in one of your domains, the screen offers `Continue with` and your facility's name. That button takes them to your identity provider, and they come back signed in.
A sign-in through your identity provider asks for no ThreadCount two-factor code. A deactivated user is refused.
## Optional or required
Once connected, single sign-on is optional. The Log in screen offers the button first, and `Prefer your password?` lets a user sign in with a password instead.
Tick `Require single sign-on` and:
- a correct password is refused for every user except break-glass admins, and the Log in screen sends them to single sign-on;
- password reset emails stop, except for break-glass admins.
`Require single sign-on` is refused until at least one active admin is marked break-glass.
## The break-glass admin
Under `Break-glass admins`, tick each admin who keeps a working password for the day the identity provider is down. Only admins are listed. On the Log in screen they open `Break-glass admin? Sign in with a password instead`.
> **Careful** A break-glass admin is the only way in while the identity provider is down. Keep that password, and that admin's two-factor, working.
## Staff sign-ins
Tick `Staff may use it too` and wearers who already have a staff sign-in can use single sign-on. They type their address on the website's Log in screen and land in their own staff view.
This is web only. The ThreadCount Staff app keeps their password, and `Require single sign-on` does not turn staff passwords off. See [The staff app](/docs/apps/staff-app).
## Disconnect
Press `Disconnect single sign-on…`, then `Disconnect`, or `Keep it` to back out. The connection is removed from the sign-on service, and `Require` and `Staff may use it too` are switched off. Everyone goes back to signing in with a password.
+64
View File
@@ -0,0 +1,64 @@
---
title: Two-factor sign-in
section: account
order: 2
summary: Turn on codes from an authenticator app, keep the recovery codes, sign in with them, and recover from a lost phone.
screen: Settings People & sign-in
role: Admin or Issuer
keywords: two-factor, two-factor authentication, 2FA, MFA, authenticator, TOTP, code, six digits, recovery codes, lost phone, new phone, trust this computer, security
---
## What it does
With two-factor on, a correct password is not enough to sign in. The Log in screen also asks for the six-digit code from an authenticator app on your phone.
It is set for your own account, under the `Two-factor` heading on `Settings People & sign-in`. Nobody else can turn it on or off for you, and it is optional for every role. An admin with two-factor off sees a note there saying it is worth turning on.
It covers password sign-in. A sign-in through [single sign-on](/docs/account/single-sign-on) asks for no ThreadCount code, because your identity provider handles the second factor.
## Turn it on
1. **Open `Settings People & sign-in` and press `Set up two-factor`.** A QR code appears, with the key written out under `Or type it in`.
2. **Scan the QR code with any authenticator app,** or type the key into it.
3. **Type the code the app shows into `Code from the app` and press `Turn it on`.** Nothing changes until this code is accepted. `Cancel` leaves your sign-in as it was.
4. **Save the recovery codes.** Ten appear, each in the form `XXXXX-XXXXX`. `Copy all` puts them on the clipboard. Press `I have saved them` when they are safe.
The `Two-factor authentication` box then shows `On` and how many recovery codes are left.
| Record | Change | Undo |
|---|---|---|
| Your user | Two-factor on, 10 recovery codes made | `Turn off` |
| Your user | 10 new recovery codes, the old ones dead | None |
| Your user | Two-factor off, recovery codes deleted | Set it up again |
Each of these is also written to the audit log on `Settings Data & audit log`, which admins can read.
## Recovery codes
Recovery codes are stored hashed. They are shown once, when they are made, and nobody can read them back afterwards, ThreadCount included. Print them or keep them somewhere you can reach without your phone.
Each code works once, in place of a code from the app. When 2 or fewer are left, the box suggests a fresh set.
`New recovery codes` asks you to confirm with your password, then `Generate new codes` shows 10 new codes. Every earlier code stops working at that moment.
## Signing in
After a correct password the Log in screen asks for the code. `Use a recovery code instead` switches the box to take a recovery code.
- Tick `Trust this computer for 30 days` and that browser is not asked for a code for 30 days. Changing your password ends the trust.
- The code step accepts 10 attempts per account in 15 minutes, then answers `Too many attempts — try again in a few minutes.`
- If the step has expired, the screen says `That sign-in has expired. Start again.` Type your password again.
A password reset by email also asks for the code before it signs you in, so a reset is not a way around two-factor.
## A lost or replaced phone
1. **Sign in with a recovery code.**
2. **Open `Settings People & sign-in`, press `Turn off`, type your password and press `Turn it off`.**
3. **Press `Set up two-factor`** and scan the new QR code with the new phone. A new set of recovery codes comes with it.
If the old phone still works, do the same steps with a code from it instead of a recovery code.
There is no screen where an admin turns off two-factor for another user. The `Edit user` dialog has no two-factor control.
> **Careful** A user with no phone and no recovery codes cannot sign in from any screen. For an only admin, that means nobody can. Keep the codes, and keep a second admin.
+73
View File
@@ -0,0 +1,73 @@
---
title: Users and passwords
section: account
order: 1
summary: Add admins and issuers, change your own details and password, reset a forgotten password by email, and keep a second admin so the facility never locks.
screen: Settings People & sign-in
role: Admin or Issuer
keywords: users, add user, edit user, admin, issuer, coordinator, login, sign in, password, change password, forgot password, reset, deactivate, reactivate, second admin, locked out, your account, profile, name
---
## Who can sign in
The people who sign in to the web app and the counter are users. Each has a work email, a password and one of two roles, Admin or Issuer. What each role may do is on [The two roles](/docs/start/the-two-roles).
Users are not the people on the register. A wearer who signs in to the staff app has a staff sign-in attached to their entry. It is managed on their record in `People`, on the `Details & access` tab, not here.
`Settings People & sign-in` opens from the side list on `Settings`, or from your name at the foot of the menu. Everyone sees `Your account`, `Password`, `Two-factor`, `Single sign-on` and `Delete my account`. Only admins see `Users`, and `Deactivated users` once there is one.
## Add or change a user
1. **Press `Add user`** beside the `Users` heading, which counts how many can sign in.
2. **Fill in first name, last name, title and work email.**
3. **Choose the role.** `Issuer` is chosen until you change it.
4. **Set a password of at least 8 characters and press `Save`.**
ThreadCount does not email the new user. The dialog says so: hand the password over yourself. One email address holds one user account across the whole service, so an address already in use is refused with `That email already has an account`.
`Edit` on a user opens the same dialog. It changes name, title and role, and `New password (leave blank to keep)` sets a new password. The work email cannot be changed. A new password ends every session signed in with the old one.
`Deactivate`, in that dialog, asks you to confirm, then stops the login without deleting it. Records keep the name, and the user moves to `Deactivated users`, where `Reactivate` lets them sign in again. The button is not offered on your own account. A deactivated user who signs in is told the account has been deactivated.
| Record | Change | Undo |
|---|---|---|
| User | Added, with a role and a password | `Deactivate` |
| User | Name, title, role or password changed | `Edit` again |
| User | Deactivated, cannot sign in | `Reactivate` |
## Your own details and password
Under `Your account`, change your first name, last name and title, then press `Save my details`. They stamp every issue, stocktake and slip you record.
Under `Password`, type `Current password`, then `New password` and `Confirm`, and press `Change password`. The new one must be at least 8 characters, and the button stays off until the two new passwords match.
You stay signed in on the device you used. Every other session signed in with the old password ends. Neither your details nor your password can be changed in the demo.
## Reset a forgotten password by email
On the Log in screen, type your work email, then choose `Forgot your password?`. The screen says `Reset link sent` whatever the address, so it cannot be used to find out who has an account.
- The link works once and expires in an hour.
- Asking again cancels any earlier link.
- At most 4 reset emails go to one address in an hour.
- A deactivated account is sent nothing.
- At a facility that requires single sign-on, only break-glass admins are sent a link. See [Single sign-on](/docs/account/single-sign-on).
- If two-factor is on, the reset asks for your code before it signs you in.
A completed reset signs you in and ends every other session on the account.
## When the email never arrives
Nothing arrives if the server has no mail set up, or if the address on the account is wrong. The Log in screen says the same thing either way, so it cannot tell you which. Self-hosting admins set mail up as described in [Email](/docs/selfhost/email).
Another admin at your facility can set you a new password with `Edit` under `Users`. The address itself cannot be changed there.
If you are the only admin, the reset screen asks you to write to support from an address you control, naming the facility and the address on the account.
## Why a second admin matters
ThreadCount refuses to demote or deactivate the last active admin, with `Keep at least one active admin`. It does not make you add a second one.
With one admin, that person's inbox is the only way back in when the password is forgotten, and their phone is the only way past [two-factor](/docs/account/two-factor). If the last person who can sign in deletes their account, the facility goes with it. See [Delete an account](/docs/account/delete-an-account).
> **Careful** Add a second admin with a working address before you need one. It is the one way back in that needs no email and no support.
+88
View File
@@ -0,0 +1,88 @@
---
title: The counter app
section: apps
order: 1
summary: The linen room's phone app, on Android and in a phone browser. Choosing a server, signing in, the screens, and what it keeps on the phone.
screen: /m
role: Admin or Issuer
keywords: counter app, android, phone, mobile, play store, install, sign in, server, self-hosted, community, offline, camera, stocktake, count, issue
---
The counter app is the coordinator's side of ThreadCount on a phone. You sign in with the same Admin or Issuer account as the portal at `/app` and see the same records, served at `/m`.
## Two ways to open it
- **In a phone browser.** Go to `https://threadcount.tech/m`, or `https://your-host/m` on your own server. You don't need to install anything.
- **The Android app.** It is called **ThreadCount** on Google Play. It shows the same `/m` screens inside the app and adds the phone's own barcode scanner (see [Scanning and browsers](/docs/apps/scanning-and-browsers)).
There is no iOS app. On an iPhone or iPad, open `/m` in the browser.
## The first screen and choosing a server
The first time you open the Android app, the welcome screen shows **Sign in**, **Sign up** and a line reading `Server: threadcount.tech · Change`.
Most rooms use the hosted service and leave that line alone. A room running the Community edition on its own server changes it, once, on each phone:
1. **Tap `Server: threadcount.tech · Change`.** The `Which server?` screen opens.
2. **Choose `Self-hosted`** and type the server's address, for example `uniforms.example.com`.
3. **Tap `Check and use this server`.** The app checks the server's `/api/app-info` to confirm it is a ThreadCount server.
The app refuses an address that doesn't answer over https within 8 seconds, or that answers but isn't a ThreadCount server. Once the check passes, the app saves the address on that phone and opens that server from then on. Choosing `ThreadCount hosted` puts the phone back on `threadcount.tech`.
Once you have tapped `Sign in` or `Sign up` on that phone, the app skips the welcome screen and goes straight to `/m`.
## Signing in and signing up
`Sign in` asks for your work email and password. If your account has two-factor turned on, it then asks for the six-digit code from your authenticator app. A recovery code also works. `Forgot password` emails a reset link that works once and expires after an hour. See [Two-factor](/docs/account/two-factor).
`Sign up` creates a new facility. It doesn't add you to an existing one. The person who signs up becomes the new facility's first Admin. To add someone to an existing facility, an Admin adds them with `Add user` in `Settings People & sign-in` in the portal: see [Users](/docs/account/users).
## The screens
The bar at the bottom has four tabs.
| Tab | What it does |
|---|---|
| Home | Today's issued and returned counts, lines below par, days since the last count, recent activity |
| Count | Pick a shelf and count it by scanning; gaps go to a variance screen before the count is filed |
| Stock | On hand against par, worst first, with a reorder draft |
| Search | People and stock in one box, or scan a garment to find it |
Home also has `Issue to someone`, and `Deliveries, pickups, rounds and more` opens `More`:
- `Receive a delivery`
- `Pickup call list`
- `Delivery round`, with a signature on the phone
- `Reprint a label`
- `Variance over time`
- `Catalogue`, where an Admin can add and edit garments
- `Settings`
Open a person's record from Search or Issue to issue to them, take a return or swap a size. [Issue a garment](/docs/counter/issue-a-garment) and [Exchanges and returns](/docs/counter/exchanges-and-returns) say what each of those writes.
`More Settings` holds only what the phone controls:
- `Beep and buzz on a scan`, which applies to this device only
- `Reason required at`, the count gap that needs a reason. An Admin can change it; an Issuer can only see it.
- links to the privacy policy, the terms and account deletion
- `Sign out`
`People`, `Orders`, `Reports` and the facility's settings are in the portal at `/app`. On a phone, the portal's `More` sheet has a `Counter app` link that opens `/m`.
## What it keeps on the phone
The app keeps only these on the phone:
- your session
- any shelf count you have started but not filed. It is kept under your own account, so the next person on a shared phone never sees it. Filing the count deletes it, and signing out deletes all your open counts.
- the beep setting, the chosen server, and whether the welcome screen has been seen
Issues, returns, stock, the staff register and every other record stay on the server.
## Permissions and connection
The Android app asks for one permission, the camera, and uses it only to read barcodes. No picture from scanning is stored or sent.
The app works online only. With no connection it shows `No connection.` and a `Try again` button, and it won't let you issue or count anything. Nothing is queued to send later. A count you hadn't filed stays on the phone until you're back online.
> **Careful** Signing out deletes any shelf count you haven't filed. File it first, or you'll have to count that shelf again.
+87
View File
@@ -0,0 +1,87 @@
---
title: Scanning and browsers
section: apps
order: 3
summary: USB scanners, the barcode formats ThreadCount reads, which browsers can scan with the camera, the Android scanner, and what happens with a code it doesn't know.
role: Admin or Issuer
keywords: scan, scanner, barcode, usb, camera, counter, stock count, unknown barcode dialog, product lookup, chrome, edge, safari, firefox, iphone, ean, upc, code 128, unknown barcode, bind, label, print
---
ThreadCount reads a barcode from a USB scanner, the camera in a supported browser, or the scanner built into the Android counter app. Wherever you can scan, you can also type the code.
## USB scanners
A USB scanner that types the code and then presses Enter works with any browser. In the portal these boxes accept one:
| Screen | Box |
|---|---|
| `Stock Count` | `Scan to count +1` |
| `Counter`, in `Issue` mode | `Scan a garment, or type a name`, in `Add garments` |
| `Stock Scan to add` | `Scan with a USB scanner, or type the code and press Enter` |
| A garment's `Scan sizes` | The prompt names the size to scan next |
`Scan to add` and `Scan sizes` bind codes, and only Admins see them.
You can also scan with no box selected. The portal takes 4 or more keys, each within 35 ms of the last and ending in Enter, as a scan. A staff number opens that person at the `Counter`. A garment goes into the pickup when the counter has a person open, adds 1 on `Stock Count`, and otherwise opens the garment's page. See [Keyboard and scanner](/docs/reference/keyboard-and-scanner).
## Formats it reads
Camera scanning, in the browser and in the Android app, reads these formats only:
- EAN-13 and EAN-8
- UPC-A and UPC-E
- Code 128, Code 39 and Code 93
ITF and Codabar are left out: with no check digit, a partial read can pass as a different code. No 2D codes are read.
A USB scanner or a typed code isn't limited to that list. In `Scan to add`, a 13-, 12-, 8- or 14-digit code has its check digit tested. If it fails, you see `check digit doesn't match — the scan may have mis-read`.
## Camera scanning in a browser
In a browser, the camera reads barcodes with the browser's built-in barcode detector.
| Browser | Camera scanning |
|---|---|
| Chrome | Yes |
| Edge | Yes |
| Safari on iPhone or iPad | No |
| Firefox | No |
Where the camera opens but the browser can't read barcodes, the screen says `Live reading isn't supported here — type the code instead.` With no camera access at all, it says `This browser has no camera access — type the code instead.` On an iPhone, type the code or use a USB scanner.
## The Android scanner
Inside the **ThreadCount** Android app, the phone's own scanner reads the code. If the app can't load it, the browser detector is used.
When counting a shelf, scanning stays on until you press `Stop scanning`. The same code again within 0.9 seconds counts as the same garment still in view. `Beep and buzz on a scan` in `More Settings` turns the sound and vibration on or off for that phone.
The ThreadCount Staff app has no scanner.
## Codes ThreadCount doesn't know
A code is known once it is bound to one size of one garment. In the counter app, an unknown code gives:
- **Counting a shelf:** `isn't a garment ThreadCount knows. Bind it to a size first`. A known code from another shelf names that shelf instead.
- **Issuing:** `isn't a garment ThreadCount knows.` Nothing is added.
- **Search:** the code is put in the search box.
In the portal:
- **`Stock Count`:** the `Unknown barcode` dialog opens. An Admin binds the code there; an Issuer is told only an admin can bind it.
- **`Counter`:** an Admin gets the same dialog; an Issuer sees `No garment has` and the code.
- **Scanned with no box selected:** the search panel opens with `No person or garment has` and the code, and Admins get `Bind it to a garment`.
- **`Scan to add`:** you can start a new product from the code, or attach it to a size you already have.
With `Look up unknown barcodes in public databases` ticked in `Settings Catalogue & suppliers`, `Scan to add` first asks a public barcode database for a product name. With it off, nothing leaves the server.
A code can be on one size only. Binding one already on another garment is refused unless you choose to move it. See [Barcodes](/docs/stock/barcodes).
## Printing ThreadCount barcodes
For garments that arrived without a barcode, an Admin opens the garment from `Stock` and chooses `Generate barcodes` from its menu. Each size without a code gets one of ThreadCount's own. These are EAN-13 codes starting `29`, a prefix for use inside one business, with a real check digit. A size with a supplier's code keeps it.
`Print labels`, in the same menu, asks you to confirm the count, then prints one label per garment on hand for each size with a code, two to a row on A4. With none to print, the sheet says why. A 13-digit code with a valid check digit prints as EAN-13, and any other code prints as Code 128.
`Reprint a label` in the counter app prints 1 to 24 copies of one size's label. It only lists sizes with a barcode bound to them. It prints from a phone browser. Inside the Android app the button reads `Print it on the desktop`, because that app can't print.
> **Careful** A garment without a readable barcode drops out of every scanned count. Reprint worn labels before the next stocktake.
+76
View File
@@ -0,0 +1,76 @@
---
title: The staff app
section: apps
order: 2
summary: The app for people who wear the uniform. How a staff member gets a sign-in, what they see and can do, what managers get, and how access is removed.
screen: /my
role: Staff
keywords: staff app, details and access, wearer, nurse, self-service, code, slip, activation, sign in, kit, request, waitlist, damage, kit check, manager, approve, ward round, remove access
---
The staff app shows one staff member their own uniform record, and lets them ask the linen room for what they need. It is at `/my` in any browser. On Android it is also on Google Play as **ThreadCount Staff**, which opens the same screens. There is no iOS app.
A staff sign-in is not a coordinator account. It is attached to the person's entry on the register, and it cannot open `/app` or `/m`.
## Getting a sign-in
An Admin gives each person a one-time code from their record in the portal:
1. **Open the person in `People`**, go to the `Details & access` tab and press `Generate a code` in the `Staff app` panel.
2. **Press `Print the slip` or `Copy`.** The code is 12 characters in three groups and is shown once only.
3. **Give the slip to the person.** In the app they tap `I have a code` (or `First time? I have a code` on the sign-in screen).
4. **They enter the code, an email address and a password** of at least 8 characters, and tick the terms box if it is shown.
The code works once. It expires 14 days after it was printed (`SLIP_DAYS` in `lib/compute.ts`), and the panel says when it expires. `New code` replaces an unused code, and `Cancel the code` withdraws it. A person who already has a sign-in cannot be given a code until their access is removed.
The email address is theirs to choose and may be a personal one, but each sign-in needs its own.
A forgotten password cannot be reset by email: the linen room removes access and gives a new code.
| Record | Change | Undo |
|---|---|---|
| Staff record | A code and its print date are stored | `Cancel the code` |
| Staff account | Created with the email and a hashed password; the code is spent | `Remove access` |
## What a staff member sees and can do
The bar at the bottom has `Home`, `Kit`, `Orders` and `Messages`.
- **Home** shows the request furthest along (with its collection code once it is ready), how many garments they hold, and shortcuts.
- **Kit** lists what the linen room has recorded against them. `This isn't right` raises a query about the record.
- **Request** asks for one or more garments with a reason. The request goes to the manager recorded on their staff record for approval.
- **On the shelf** shows each size as `In stock`, `Low` or `None on shelf`, and says when it was last counted.
- **Waitlist** is offered for a size with none on the shelf, showing their place before they join. Accepting a held garment sends it to their manager as a request.
- **Report damage** can also request a replacement. The garment leaves their record only when handed in at the counter.
- **Kit check** is open only while the linen room is running one, and Home prompts for it if they hold garments and haven't answered yet. They confirm how many of each they still have.
- **Orders** lists their open and finished requests, each with a message thread to the linen room.
- **Your sign-in** changes their password. A new password signs them out on every other phone or browser.
## What they never see
- prices, costs or any payment
- stock numbers: the shelf is shown in words only
- anyone else's record, unless that person reports to them as a manager
They cannot delete their own account; only the linen room can remove access.
## Manager functions
A staff member becomes a manager when someone on the register names them as their manager. See [Managers](/docs/people/managers). They get:
- **Approvals.** Home shows how many requests are waiting. They can approve or decline each line, with a reason when they decline.
- **Approve by link.** The email about a new request links to `/my/approve`, which needs no sign-in. The link lasts 14 days and stops working once the request is decided or either person leaves the register.
- **Ward.** Who on their team holds what, with each person's set count.
- **Raise for your team.** A request typed for someone who reports to them. It goes to the manager's own manager for approval.
A person with `On the ward desk` ticked in the `Staff app` panel on their `Details & access` tab also gets `Ward round`, where bags delivered to their ward are signed for. Whoever signs is named on the order. See [Delivery rounds](/docs/counter/delivery-rounds).
## Permissions and connection
ThreadCount Staff asks for internet access and nothing else: no camera, location, contacts, files, photos or microphone. It has no scanner and works online only. With no connection it shows `No connection.` and nothing is saved to send later.
## Having access removed
On the person's `Details & access` tab, an Admin presses `Remove access` in the `Staff app` panel and confirms. The staff account is deleted and every session it had ends at once. Their register entry and issue history stay.
`Deactivate` in the `Register` panel on the same tab also stops their sign-in straight away, with the message `You're no longer on the register at this facility.` See [Deactivating and deleting](/docs/people/deactivating-and-deleting).
+69
View File
@@ -0,0 +1,69 @@
---
title: Delivery rounds
section: counter
order: 6
summary: Take waiting pickups to the wards and have the receiver sign on screen. Which bags go on the round, how signatures are stored and removed, and the ward round for staff requests.
screen: Today Deliver on the round
role: Admin or Issuer
keywords: delivery round, delivery rounds, round, deliver on the round, start the round, ward, deliver, trolley, signature, sign, received by, handover photo, proof, ward desk, ward clerk
---
ThreadCount has two rounds. `Delivery rounds` takes pickups, garments ordered in for a person, to the wards. Approved staff requests go on the ward round, signed for by the ward desk in the staff app.
## Which bags go on the round
`Today` lists bags for the round under `Deliver on the round`, one row per ward, with the names, the garment count and `signed for at the ward desk`. A pickup goes there when nobody has called about it and the person's ward has an active person with the ward-desk flag. Every other pickup stays on [Call to collect](/docs/counter/pickup-call-list).
`Start the round` opens `Delivery rounds` for that ward. The screen is not on the desktop menu; on a phone it is `Delivery rounds` in the `More` sheet.
## The rounds screen
`Delivery rounds` lists every pickup not yet picked up or delivered, whether or not it was put on the round, grouped by the ward on the person's staff record. A person with no ward recorded is listed under `Unknown`. Each ward panel shows its cost centre and how many are to deliver. With more than one ward, a `Ward` bar picks one or `All`.
Each row shows the days waiting, the person, their phone number, the garments and the order code. A pickup waiting 14 days or more is flagged.
## Handing over
1. **Press `Delivered — sign`** on the row.
2. **Type the receiver's name** in `Received by (name)`.
3. **Have them sign** in the `Signature` box. `Clear` wipes it.
4. **Press `Add handover photo`** if you want one.
5. **Press `Mark delivered`.**
The name, signature and photo are each optional. The counter app's round takes a name and a signature, with no photo ([Counter app](/docs/apps/counter-app)).
| Record | Change | Undo |
|---|---|---|
| Pickup | Picked up today, the name typed, the signature and photo linked, marked as delivered on a round | No screen reverses it |
| Issue | One row per line, as `Picked up` writes, marked signed when a signature was drawn | Record a return |
| Photo | The signature, and the handover photo if taken | See below |
Delivery refuses `Already handed over` when the pickup was picked up or delivered first, and `Size <size> is no longer on <garment> — fix the catalogue before marking this delivered`.
## How signatures are stored and removed
The signature is saved as a PNG image before the delivery is recorded. An image may be up to 700 KB. It is kept as a file on the server, and the database holds a pointer to it. A self-hosted server keeps the files in the folder named by `PHOTO_DIR`, or `.photos` in the app's folder when that is unset ([Configuration reference](/docs/selfhost/configuration-reference)).
No screen deletes a single signature. Images are removed in two cases:
- An image nothing refers to, such as a signature saved for a delivery that then failed, is deleted once it is more than 1 day old. The clean-up runs on about 1 image save in 20.
- Wiping the facility's activity or starting fresh removes every image the facility holds, under `Settings Data & audit log`.
A backup carries up to 2000 images, and up to 40 MB of them.
> **Careful** A wipe of activity removes every delivery signature at once, with no way to keep some.
## The ward round for staff requests
In the request queue, a request being picked has `Send on the ward round` ([Requests from staff](/docs/counter/requests-from-staff)). It refuses in two cases:
- `<first name> has no ward recorded, so there is no round to send this on. Hold it at the counter, or record their ward on the staff register first.`
- `Nobody on <ward> can sign for a round bag — that needs somebody with the ward-desk flag and their own staff-app account. Hold it at the counter instead, or set the flag on their staff record first.`
The ward-desk flag is `On the ward desk`, set by an Admin in the `Staff app` panel of a person's `Details & access` tab.
The wearer is emailed when they have an account and email is set up. Somebody on that ward signs for the bag in the staff app. No drawn signature is taken; the queue shows `Signed by <name>` with their role, and whether it has been collected from the ward. See [Staff app](/docs/apps/staff-app).
## Ward delivery notes
The paper that travels with a request bag is the delivery slip, `Uniform ward delivery`. Its footer is `Delivery slip footer` under `Settings Facility`, which reads `After hours deliveries are left with the manager or team leader on duty.` until changed. See [Slips and signatures](/docs/counter/slips-and-signatures).
@@ -0,0 +1,79 @@
---
title: Exchanges and returns
section: counter
order: 2
summary: The counter's Return, Swap a size and Hand in modes, and the pre-loved pool. What each writes and how it moves the sets a person holds.
screen: Counter
role: Admin or Issuer
keywords: return, returned, exchange, swap a size, wrong size, hand-in, hand in, pre-loved, preloved, rag, lost, written off, damaged, credit, receipt
---
Garments come back three ways, each a mode on `Counter` once a person is chosen. A return closes one issue line with a condition. A swap changes the size of the same garment. A hand-in takes back whatever the person brings and sorts it into the pre-loved pool or rags.
## Returns
`Return` mode lists every garment the person holds under `Holding`, with `Return` on each line. `Return` also sits against lines under `Holding now` in `Issue` mode, and on the person's record under `Uniform` and `History`.
1. **Press `Return`** on the line.
2. **Say how many are coming back** when the line is more than 1 garment. The rest stays out with the person.
3. **Take a photo if you want one.** `Photo the garment (damage evidence)` is optional.
4. **Press a condition:** `Returned Good`, `Returned Damaged`, `Lost` or `Written Off`.
Pressing the condition records the return. A garment issued from the shelf and returned `Returned Good` counts back into the shelf figure; the other three conditions do not. A pre-loved garment returned `Returned Good` goes back into the pre-loved pool instead. Today's returns are listed under `Returned today`.
Returns refuse `This issue has already been returned / written off` and `This garment was handed in on <date> — it's already back in the pool`.
## Swap a size
`Swap a size` mode lists each line the person holds.
1. **Choose the `New size`.** Each size shows how many are on the shelf, or in the pre-loved pool when the line was pre-loved.
2. **Set the quantity** when the line is more than 1 garment.
3. **Press `Swap`.**
A swap:
- marks the garments coming back `Returned - Good`, splitting the line when only part of it is swapped
- issues the new size today, condition `New` at today's catalogue cost, or `Pre-loved` at cost 0 from the pool when the original was pre-loved
- carries across `offGroup` and `offStyle` from the original line
- changes the top or pants size on the staff record when the garment is a top or pants
It makes no ceiling check. It refuses `Pick a different size`, `Not enough size <size> on the shelf`, `Not enough pre-loved size <size> in the pool`, `That garment has already been returned` and `That garment was handed in on <date>`. The counter app's `Exchange` screen does the same ([Counter app](/docs/apps/counter-app)).
## Hand-ins
`Hand in` mode has `Record a hand-in`, and lists earlier hand-ins with `Receipt`. A person's record has `Record hand-in` under `History`.
1. **Add each garment and size.** Tap a size again to add one.
2. **Mark each line** `Good` or `Rag`, and `Laundered` or `Unlaundered`.
3. **Tick `Credit the good garments back`** if the approval and the yearly report figure should be credited.
4. **Press `Record hand-in`**, or `Record & print receipt`.
ThreadCount matches each line to the person's issues of that garment and size that are not returned or handed in: new before pre-loved, newest first. Matched issues are stamped handed in today; part of a line is split off. Good lines join the pre-loved pool and rags are counted for disposal, whether or not they matched an issue.
Credit applies only to Good lines matched to new, not pre-loved, issues. The larger of the credited tops and credited pants is given back as sets to the person's manager's approvals, newest first.
A hand-in refuses only `Add at least one garment` and `Invalid hand-in line`.
`Hand-in without a person`, at the top of `Counter`, opens `Adjust quantity` on its `Pre-loved` mode. It adds garments to the pool and matches no issue.
A damage report from the staff app is cleared on the `Damage` tab of the request queue with `Handed in at the counter`. Clearing it does not return the garment ([Requests from staff](/docs/counter/requests-from-staff)).
## The pre-loved pool
The pool is a count per garment and size. It grows from Good hand-in lines, from pre-loved garments returned `Returned Good`, and from pre-loved garments given back in a swap. It shrinks when a pre-loved line is issued or swapped out.
A pre-loved issue costs the ward nothing and never comes off a manager's approval. It does count towards the ceiling. `Reports`, on its `People` view, shows `Pool today`. A count can cover the pool on its own ([Stocktakes](/docs/stock/stocktakes)).
## What is written
| Record | Change | Undo |
|---|---|---|
| Return | Return date today and the condition on the issue; photo linked | No screen reverses a return |
| Swap | Old line returned `Returned - Good`; new issue row today; staff record size | No screen reverses a swap |
| Hand-in | A hand-in with its lines; matched issues stamped handed in; pool up by the Good lines | No screen reverses a hand-in |
| Approval, on credit | Sets used go down | Issue the sets again |
A return, a swap or a hand-in matched to an issue takes those garments off the sets a person holds, credit ticked or not. A swap puts the new size on, so the count does not change. A hand-in line matching no issue changes nothing.
> **In plain terms** The credit tick is about approvals and reports; the hand-in itself is what makes room under the ceiling.
+65
View File
@@ -0,0 +1,65 @@
---
title: Issue a garment
section: counter
order: 1
summary: Choose the person, add the garments, pick where each comes from, record it. What is written, and what the counter refuses.
screen: Counter
role: Admin or Issuer
keywords: issue, issue stock, counter, scan, barcode, badge, six sets, ceiling, override, pre-loved, order in, usual sizes, repeat last, refusal, slip, hand out uniform
---
`Counter` serves one person at a time. You choose the person first, then the mode: `Issue`, `Return`, `Hand in` or `Swap a size`. This page covers `Issue`; the other three are on [Exchanges and returns](/docs/counter/exchanges-and-returns). The server repeats every check the screen makes.
## At the counter
1. **Find the person.** Type a name or staff number into `Find a person`, or scan their badge. Up to 8 active people are listed, each with `Tops` and `Pants` meters. Enter picks an exact staff-number match, or else the first row. A badge scanned anywhere in the portal opens the counter with that person.
2. **Check the person panel.** It shows their group, ward, cost centre, cut and usual sizes, then how they get uniform: `FTE table`, `Starting kit` or `Manager approval`, and what the manager has signed.
3. **Add garments.** Scan into `Scan a garment, or type a name`, use `Scan with the camera`, or tap one of the usual chips. Typing 2 or more characters lists matching garments with a size strip; ones outside the person's group or cut are listed under `Other garments`. Tap a size again for one more.
4. **Choose a source for each line** in `This pickup`: `Shelf`, `Pre-loved` (shown when the pool holds some) or `Order in`. A line that could come from the shelf or the pool reads `Pick a source` until you choose.
5. **Read the line under the pickup.** It says `After this she holds 4 of 6 sets`, and names each reason the pickup needs an override.
6. **Set `Sets off the signed form`** when the person has a manager's approval with sets left. It starts at the larger of the tops and the pants in the pickup, not counting pre-loved lines.
7. **Print the `Collection slip`** if you need one, then press `Record issue`, or Ctrl+Enter (Cmd+Enter on a Mac).
`Repeat last · <date>` refills the pickup with every unreturned line from the person's most recent issue date, as shelf lines. `Change person` or Esc starts again; Esc does nothing while the pickup has lines in it.
An unknown barcode opens `Unknown barcode` for an Admin ([Barcodes](/docs/stock/barcodes)). An Issuer sees `No garment has <code>.`
## What is written
| Record | Change | Undo |
|---|---|---|
| Issue, from the shelf | One row per line: today's date, condition `New`, today's catalogue cost | Record a return |
| Issue, pre-loved | Condition `Pre-loved`, cost 0; the pool goes down | Return it as `Returned Good` |
| Replenishment draft | Shelf lines added to the supplier's draft order | Edit the draft on `Orders` |
| Order, for `Order in` | One order per supplier, status `Ordered`, for this person | Received lines join the [pickup call list](/docs/counter/pickup-call-list) |
| Manager's approval | Sets used go up, oldest approval first | A credited hand-in gives sets back |
An override is stamped only on the rows it applies to: `override` for the ceiling, `offGroup` for a garment outside the staff group, `offStyle` for the wrong cut. For `Order in` lines it is written into the order's note.
## What the counter refuses
`Record issue` stays disabled while a line has no source, a line asks for more than the shelf or pool holds (`Not enough on the shelf`, `Not enough pre-loved`), the person is inactive, or a reason needs the override tick. The server refuses the same cases:
| Refusal | Why |
|---|---|
| `This staff member is inactive — reactivate them on their profile first` | The record is inactive |
| `Not enough on the shelf for <garment> <size>` | Shelf below the pickup |
| `Not enough pre-loved <garment> <size> in the pool` | Pool below the pickup |
| `<garment> is discontinued` | The garment is archived |
| `A. Hassan is holding 6 tops and 5 pairs. That would be 7 tops and 5 pairs, and the most anyone holds is 6 sets — 6 tops and 6 pairs. Hand a top in to make room, or a coordinator can record an override.` | Past the ceiling |
| `<garment> is for <groups> — <name> is in <group>. Tick the coordinator override to issue it anyway.` | Outside their staff group |
| `<garment> is the <cut> cut — <name> is set to <style>. Tick the coordinator override to issue it anyway.` | Not their cut |
A refusal ending `refresh and try again` means somebody changed the shelf, the pool, the approval or the person's holdings while you were serving. Refresh and record again.
## The ceiling and the override
The ceiling is sets held at any time: 6 unless the facility has set its own figure under `Settings Issuing rules`. Tops and pants are counted separately. Garments outside a set have a ceiling of the same number, counted in garments. Holdings include garments on order, waiting to collect, and in approved request bags. Pre-loved garments count. See [The entitlement rule](/docs/people/entitlement-rule).
One tick, `Record as an override`, answers the ceiling, the staff group and the cut together. It clears when you change the person or anything in the pickup.
> **In plain terms** A hand-in makes room; an override records that somebody chose to go past the rule.
## On a phone
The counter app has its own issue screen, recorded through the same server check. See [Counter app](/docs/apps/counter-app).
+75
View File
@@ -0,0 +1,75 @@
---
title: Manager approvals
section: counter
order: 4
summary: One manager on each staff record approves that person's requests and signs their order form. Setting the manager, deciding in the app or by email, self-approval, and recording a signed form.
screen: People a person Details & access
role: Admin or Issuer
keywords: manager, approval, approve, decline, approver, email link, self-approved, signed form, order form, record a signed form, sets, FTE, credit slip, delegate, reports to
---
Each staff record names one manager. That person approves the wearer's requests in the staff app and signs their paper order form.
## One manager per record
The manager is set in the `Manager` panel on a person's `Details & access` tab. Search the register by name or staff number and choose; it saves at once. Only an Admin can set, change or remove it; an Issuer sees the name, or `None set.`
- Anyone may be their own manager. The panel then shows `Self-approved`.
- An inactive person is not offered, and the server refuses one: `That manager is no longer active on the register.`
- A manager whom others still name cannot be deactivated: `<n> people still name <first name> as their manager, and a request can't be sent to somebody who is off the register. Give them a new manager first.`
The same tab has `Whose requests <first name> approves`. An Admin can add somebody with `Add somebody who reports to them`, which asks `Change <first name>s record` first because it rewrites that person's manager, or take somebody off with `Remove from list`.
Without a manager, the staff app refuses to raise a request: `Your manager isn't set yet — the linen room has to record who approves your requests.`
A request is addressed to the manager recorded when it was raised. Changing the manager later does not move it; re-address it in the request queue ([Requests from staff](/docs/counter/requests-from-staff)).
## Deciding in the app
The manager opens the request in the staff app and decides each garment. A declined garment needs one of 3 reasons: `Over allowance`, `Not needed right now` or `Wrong item for the role`. Every garment must be decided before sending.
When at least 1 garment is approved the request goes to the linen room; when none is, it is declined.
## Deciding by email
When a request is raised, its manager is emailed `Uniform request from <name>`, or `Uniform request for <name>` when somebody raised it on the wearer's behalf, provided they have a staff-app account and email is set up. The link opens a page showing the request. Nothing is decided until the manager chooses on that page.
- The link lasts 14 days.
- It works once.
## Self-approval and the raise rule
Anybody may approve a request for their own uniform. It is never recorded as an ordinary approval: the history line names it as their own request, marked self-approved.
Nobody approves a request they raised for somebody else:
- Re-addressing to the raiser is refused: `<first name> raised this request, so it can't be sent back for <first name> to approve. Pick somebody else.`
- Sending a request to its wearer is refused unless they are set as their own manager.
- A decision by the raiser is refused: `You raised this request, so somebody else has to approve it — ask the linen room to re-address it.`
## Recording a signed order form
The `Approval` panel on a person's `Uniform` tab shows their route (`FTE table`, `Starting kit` or `Manager approval`), `Signed by the manager` and `Drawn`.
1. **Press `Print a new one`**, and have the manager sign it ([Slips and signatures](/docs/counter/slips-and-signatures)). `Order form` on the counter's person panel prints the same form.
2. **Press `Record a signed form`, then enter** `Sets`, `FTE`, `Date signed` and any `Note`. A date after today reads `After today: check the year.` and cannot be recorded.
3. **Press `Photo the signed form`** if you want the sheet on file.
4. **Press `Record approval`.** It needs an active manager set: `Set their manager first.`
The approval is recorded under the manager in the `Manager` panel. When the sets are above what the FTE table proposes, ThreadCount writes a sentence saying so after your note, and previews it under `Recorded as:` before you record.
`View the form` opens the photo. Sets come off at the [counter](/docs/counter/issue-a-garment) under `Sets off the signed form`.
Each approval is listed on the `History` tab under `Previous order forms`, reading `<n> of <m> left` or `Fully collected`, with `Signed form`, `Print the form` and `Credit slip`. An Admin can remove an approval with `×`.
## What is written
| Record | Change | Undo |
|---|---|---|
| Staff record | The manager | An Admin changes or removes it |
| Request and its lines | Each line approved or declined with a reason; status; a history line | None |
| Approval | Date signed, manager's name as signed, link to the manager, sets, FTE, note, photo; sets used starts at 0 | An Admin removes it |
The name on an approval is a copy. It still reads as signed after the manager is renamed or leaves.
> **In plain terms** The manager on the record is the only approver, on screen and on paper.
+65
View File
@@ -0,0 +1,65 @@
---
title: Pickup call list
section: counter
order: 5
summary: Garments ordered in for a person wait in Call to collect on Today until they are picked up. Days waiting, called, the slip and picked up.
screen: Today
role: Admin or Issuer
keywords: pickup, call list, call to collect, awaiting pickup, collection, collected, picked up, contacted, mark called, ring, phone, waiting, fortnight, order in
---
The call list is the `Call to collect` group on `Today`. It holds every pickup that has arrived and has not been picked up or delivered, except the bags going out on a delivery round.
## How a line joins it
A pickup starts as an order for a staff member, raised with `Order in` on [Counter](/docs/counter/issue-a-garment), or with `Order for a person` on `Orders` ([The To order list](/docs/stock/order-list)).
When the delivery is received, each line goes to `Shelf` or `Pickup`. Pickup lines become one pickup for that person; shelf lines go into stock. A short delivery is split off as a back order. See [Receiving and back orders](/docs/stock/receiving-and-back-orders).
The garments count towards the person's ceiling from the day they are ordered.
## Call to collect or the round
A pickup goes to `Deliver on the round` instead when nobody has called about it and the person's ward has an active person with the ward-desk flag. Pressing `Mark called` on a person's record moves it back to `Call to collect`. See [Delivery rounds](/docs/counter/delivery-rounds).
A group with nothing in it is not shown. When the whole queue is empty, `Today` reads `Nothing in the queue.`
## Reading the list
Rows are sorted by days waiting, longest first. Days waiting runs from the received date to today.
Each row shows the days waiting, the person's name and phone number (a call link where the device can dial), each garment with size and quantity, the order code, and `contacted` once called.
A pickup waiting 14 days or more is flagged. Those pickups count towards the `overdue` figure in the `Today` heading. The rail badge shows everything in the queue, not the overdue figure; an overdue pickup only changes the badge's colour.
## Called, slip, picked up
1. **Ring them, then press `Mark called`.** The button goes and the row reads `contacted`. No screen unmarks it.
2. **Print the `Slip`** if the bag needs one. It carries the name, ward, number of garments, the order's reference (or its code), the date received, and a tick against `Phone` under `Staff notified` once called. `Date notified` prints blank. It does not list the garments.
3. **Press `Picked up`** when they collect. The row leaves the list.
The same pickups are under `Waiting for her`, `him` or `them` on a person's `Uniform` tab, with `Mark called` and `Picked up`. The search panel lists them under `Waiting`, with `Picked up`.
When two people work the list at once, a refusal is shown above the rows. Pressing `Picked up` on a pickup already collected changes nothing.
## What is written
| Record | Change | Undo |
|---|---|---|
| Pickup | Contacted | No screen clears it |
| Pickup | Picked up today | No screen reverses it |
| Issue | One row per line: today's date, condition `New`, today's catalogue cost, the order code, marked as collected | Record a return from the person's record |
Picking up makes no ceiling check and stamps no ceiling override. It stamps `offGroup` or `offStyle` on a garment outside the person's staff group or uniform style.
It refuses one case: `Size <size> is no longer on <garment> — fix the catalogue before marking this picked up`. Put the size back on the garment, then press `Picked up` again.
## Hold period
A pickup has no hold period. Nothing expires: it stays until it is picked up or delivered. The 14-day flag marks it and does nothing else.
A staff request held at the counter carries a `Held until` note ([Requests from staff](/docs/counter/requests-from-staff)), and a waitlist offer is held for 48 hours. Neither applies to pickups.
> **In plain terms** The list is a queue of phone calls, and nothing leaves it on its own.
The counter app's `Pickups` screen works the same list. See [Counter app](/docs/apps/counter-app).
@@ -0,0 +1,84 @@
---
title: Requests from staff
section: counter
order: 3
summary: Staff ask for uniform in the staff app, their manager decides each garment, the linen room picks the bag. Where requests appear, the queue, codes, messages and the waitlist.
screen: Requests
role: Admin or Issuer
keywords: request, ward request, staff request, requests queue, approve, declined, pick, start picking, bag, collection code, hold, messages, waitlist, needs an approver, withdraw, re-address
---
A request is one ask for one person, raised in the staff app and approved by that person's manager before the linen room can act.
## What staff can ask for
- Up to 10 lines, and between 1 and 20 of each garment. The same garment and size asked for twice becomes one line.
- A reason, from `Worn out`, `Damaged`, `Lost` and `Extra for shifts`, and a note of up to 400 characters.
- Only garments for their own staff group, or for every group, and in their uniform style.
A person with no manager recorded cannot raise one. Each request gets a code such as `R-0042`. See [Staff app](/docs/apps/staff-app) and [Manager approvals](/docs/counter/manager-approvals).
## Where requests appear
- **Today.** `Pick for approved requests` lists approved requests. A row reads `in stock` with `Start picking`, or `none on the shelf` with `Order it in`, which opens `New order` for that person with the short lines filled in.
- **A person's record.** The `Requests` tab lists theirs, `Open` or `All`, with `Open in the queue`.
- **The queue.** The `Requests` button on `People` opens every request. On a phone it is `Requests` in the `More` sheet.
| Queue tab | Holds |
|---|---|
| `To do` | Approved and not yet handed over |
| `Needs an approver` | Waiting, with nobody asked to approve |
| `Open` | Everything not yet finished, waiting ones greyed |
| `All` | Every loaded request |
| `Record queries`, `Damage`, `Kit check & waitlist` | The staff app's other reports |
The queue loads the latest 400 requests; older ones are on the person's record. When any request has no approver, a line reads `<n> with no approver` with `Address them`. `Export CSV` writes the tab on screen.
Opening a request lists every line with its decision. A declined line is struck through with its reason and is never picked.
## The bag
1. **Press `Start picking`.**
2. **Hold it at the counter or send it on the ward round.** `Hold at the counter` takes an optional `Held until` note, such as `Fri 6pm`. `Send on the ward round` is covered in [Delivery rounds](/docs/counter/delivery-rounds).
3. **Match the code at the counter.** Holding gives the bag a 4-digit collection code, different from every other bag waiting at the counter.
4. **Press `Collected`** when the person takes it.
Holding at the counter emails the person with the code and the held-until note, when they have a staff-app account and email is set up. The note is text; nothing expires on it.
`Collected` checks the shelf and refuses `Not enough <garment> <size> on the shelf to hand over — count the shelf or order it in first.` It writes an issue for each approved line and adds the garments to the supplier's replenishment draft.
A request moves only forward. A stale screen gets `A request that is "<status>" can't move to "<status>".` or `Somebody else moved that request just now — reopen it.`
`Collection slip`, `Delivery slip` and `Print order form` print the approved lines ([Slips and signatures](/docs/counter/slips-and-signatures)).
## Messages
Each request has its own messages. You reply in `Reply to this order` and press `Send`. Replies are named `(linen room)`. ThreadCount sends no email when you write a reply. The request's `History` lists every step, who took it and when.
## Stuck requests
A request waiting on nobody, or on a manager who will never answer, has two ways out:
- **Re-address it.** Choose a name and press `Ask them` or `Re-address`. People without a staff-app account are listed under `Cant be asked — no staff-app account`. The person who raised it is left out. The wearer is listed only when set as their own manager, and choosing them is a self-approval.
- **Withdraw it.** `Withdraw it` declines the request with the reason `Withdrawn — no approver available` and emails the person when they have an account.
Deactivating a person closes their waiting requests.
## The waitlist
Staff join a waitlist for a garment and size in the staff app. The `Kit check & waitlist` tab lists who is waiting under `Waiting for a size`.
When the size arrives, press `Its in — offer it`. The garment is held for 48 hours, and the person is emailed when they have an account. Accepting in the staff app raises a request, which still needs their manager. After 48 hours the row reads `Hold lapsed — offer to the next person`.
An offer is refused when the garment is now outside the person's staff group or uniform style, with a message ending `Take them off this waitlist instead.`
Starting a kit check and closing it are Admin only.
## What is written
| Record | Change | Undo |
|---|---|---|
| Request | Status, collection code, held-until note, a history line per step | None; it only moves forward |
| Issue | One row per approved line on `Collected` | Record a return |
| Message | Your reply, with your name | None |
| Waitlist entry | Offered time | None |
@@ -0,0 +1,78 @@
---
title: Slips and signatures
section: counter
order: 7
summary: Every document the counter and a person's record print, where each prints from, what it carries, and which signatures ThreadCount records.
screen: Counter
role: Admin or Issuer
keywords: slip, print, printing, collection slip, delivery slip, order form, credit slip, hand-in receipt, access slip, signature, signed, mark signed, receipt, receipts to sign, paper
---
Each document opens in a new window and starts the browser's print dialog. If nothing opens, allow pop-ups for ThreadCount.
## The documents
| Document | Printed from | Paper |
|---|---|---|
| Collection slip | `Counter` (`This pickup`), `Today` (`Slip` in `Call to collect`), the request queue | A4 |
| Delivery slip | The request queue | A4 |
| Order form | `Counter` (`Order form`), a person's `Uniform` and `History` tabs, the request queue | A4 |
| Credit slip | `Counter` (`Credit slip`), a person's `History` tab | A5 landscape |
| Hand-in receipt | The hand-in dialog, `Counter` in `Hand in` mode, a person's `History` tab | A5 landscape |
| Access slip | A person's `Details & access` tab | A5 landscape |
Garment labels and the supplier order sheet print from `Stock` and `Orders`: see [Barcodes](/docs/stock/barcodes) and [The To order list](/docs/stock/order-list).
## Collection and delivery slips
Both carry the facility logo, or `Organisation name on slips`, from `Settings Facility`. Both carry `Staff name`, and `Collection code` beside it for a request held at the counter.
When garments are passed, `Garments — tick each one as it goes in the bag` lists them. Up to 10 lines print; the rest show as `+<n> more lines — see the request in ThreadCount.`
| Slip | Fields | Signature |
|---|---|---|
| `Uniform ready for collection` | Ward / Department, Date received, PO / Order no., Garments, Staff notified, Date notified | `Collected by (signature)`, `Date collected` |
| `Uniform ward delivery` | Ward / Department, Deliver to, Garments, PO / Order no., Date received, Requested by, Delivered by, Date / time | `Received on ward by (name + sign)` |
The footer is `Collection slip footer` or `Delivery slip footer` from `Settings Facility`.
What each screen fills in:
- **Counter.** The shelf and pre-loved lines in the pickup, marked `(pre-loved)` where they are; `Order in` lines are left off. The button works only when the pickup could be recorded and has a shelf or pre-loved line.
- **Request queue.** The approved lines, with the cut named unless unisex. `PO / Order no.` is the request code. A request on the ward round prints a delivery slip, any other a collection slip. A waiting or declined request has no slip.
- **Today.** Name, ward, garment count, the order reference, date received, and a `Phone` tick once called. No garment list.
## Order form
The form the manager signs. It prints three ways:
- From `Counter` or `Print a new one` on the `Uniform` tab, with the person's details and blank garment rows.
- From `Print order form` in the request queue, or `Print the form` on a request row under `Previous order forms`, with the garments written on. A request still waiting prints with the manager's block blank.
- From `Print the form` on a recorded approval, as a copy. It prints what was recorded, and `Signature (on the signed sheet)` stays empty.
It has a `Staff member signature` line, a `Manager / financial delegate approval` block, and a block for the linen room including `Date ordered`, `PO number`, `Invoice number`, `Stock received` and `Date collected`. See [Manager approvals](/docs/counter/manager-approvals).
## Credit slip, hand-in receipt and access slip
- **Credit slip.** `Uniform Credit`, the sets left on one approval (`2 of 5 sets remaining`), the person, who approved it and when, the FTE and notes. It tells the person to bring the slip or their payroll number to collect the rest. On `Counter` it prints the open approval.
- **Hand-in receipt.** Each garment, size, quantity and condition, `unlaundered` where marked, how many went to the pre-loved pool and to rag disposal, and whether the allowance was credited ([Exchanges and returns](/docs/counter/exchanges-and-returns)).
- **Access slip.** `Print the slip` after `Generate a code`: the one-time code a person uses to claim their staff-app account, with the steps ([Staff app](/docs/apps/staff-app)).
## Signatures
ThreadCount does not read ink. What it records:
| Where | What is recorded | By |
|---|---|---|
| An issue | The signed tick | You, after the slip is signed |
| A delivery on `Delivery rounds` | The drawn signature as an image, the receiver's name, and the signed tick on the issues | The receiver, on screen |
| A request bag on the ward round | The signer's name, role and time; no image | The ward desk, in the staff app |
| A manager's approval | A photo of the signed order form, if taken | You, when recording it |
The signed tick is `Mark signed` or `Signed` under `Holding` in the counter's `Return` and `Hand in` modes, and `signed` or `not signed` on a person's `Uniform` and `History` tabs.
`Today`'s month-end panel shows `<n> receipts to sign` when any issue lacks the tick, with `Chase`, which opens `People` filtered to `Receipts to sign`.
How drawn signatures and photos are stored and removed is on [Delivery rounds](/docs/counter/delivery-rounds).
> **In plain terms** Paper carries the signature; ThreadCount carries a tick, a name, or a picture of the paper.
@@ -0,0 +1,81 @@
---
title: Deactivating and deleting
section: people
order: 5
summary: Deactivate someone who has left and keep their history, delete only a record entered by mistake, remove staff-app access, and handle a privacy request.
screen: People a person Details & access
role: Admin
keywords: deactivate, inactive, leaver, left, delete staff, remove, reactivate, history, finance, remove access, staff app account, register panel, privacy, personal information
---
## Deactivate or delete
Someone who has left is deactivated. Their record stays, and so does everything recorded against it.
Delete removes the record itself. It is for a record entered by mistake, and it is refused for anyone with history.
Both are in the `Register` panel on the person's `Details & access` tab, which only an Admin sees.
## Deactivate
`Deactivate` is refused while anyone else still active names them as manager, and the refusal says how many. Give those people a new manager first. Somebody who is only their own manager is not held up by this.
When it goes through:
- Every request for them still waiting on approval is closed as declined, with the reason that they are no longer on the register and a timeline entry. The panel says how many were closed. Nobody is emailed about it.
- The counter refuses to issue to them, and they drop out of the counter's `Find a person` list.
- A request cannot be raised for them at the counter or in the staff app.
- Their staff-app sign-in is refused with `You're no longer on the register at this facility. Ask the linen room.`
- An emailed approval link about them, or sent to them as a manager, shows no details and decides nothing.
- They leave the `People` counts and the `Show` filter's lists, and are hidden unless `Include inactive` is chosen.
`Reactivate` puts them back. Requests closed at deactivation stay closed and have to be raised again.
## What stays for finance
A deactivated record keeps its issues, orders, approvals, hand-ins, alterations, requests and notes. Each issue keeps the cost it was issued at, so reports and the [journal export](/docs/reports/journal-export) go on including it.
## Delete
`Delete` appears only when the person has no issues and no orders, and asks for a confirmation. The server then checks everything else, and refuses if the record has any of:
- issues, orders, manager approvals, alterations or hand-ins;
- requests for them or raised by them;
- waitlist places, kit-check answers, damage reports or queries;
- a staff-app account.
The refusal lists what was found and says to deactivate instead.
> **Careful** A delete cannot be undone.
## Remove staff-app access
The `Staff app` panel on the same tab manages their account. It is read-only for an Issuer.
- **Remove access** deletes the account after a confirmation. Every session it had ends at once and any code is cleared. The register entry and history stay. To sign in again they need a new code.
- **Cancel the code** withdraws a code that has not been used. A code also stops working 14 days after it was generated.
Removing access does not deactivate the person. See [the staff app](/docs/apps/staff-app).
## Privacy requests
ThreadCount has no screen that exports or erases one person's information in a single step. What it has:
- The staff app shows a person their own record, and a query they raise there about it reaches the request queue under `Record queries`.
- The `People` export, searched to one name, gives that person's register row without notes or start date.
- The backup holds the whole facility. See [export and backup](/docs/account/export-and-backup).
- The `Note` panel on the `Uniform` tab can be edited or cleared by an Admin.
- A record with history cannot be deleted; it can be deactivated and its access removed.
The [privacy policy](/privacy) tells staff to ask their coordinator to remove their access, and says their register entry and history stay because the facility needs them for its own records. Anyone who would rather not ask their coordinator can write to privacy@threadcount.tech, and the request is routed through the facility's own privacy process.
Deleting a whole facility is on [delete an account](/docs/account/delete-an-account).
## What is written
| Record | Change | Undo |
|---|---|---|
| Deactivate | The record marked inactive; waiting requests declined. | `Reactivate`. Closed requests stay closed. |
| Delete | The record removed. | None. |
| Remove access | The staff-app account deleted, any code cleared. | Generate a new code. |
| Cancel the code | The code cleared. | Generate a new code. |
+83
View File
@@ -0,0 +1,83 @@
---
title: The entitlement rule
section: people
order: 3
summary: Six sets held at any time, for every group, with no financial year. How sets held are counted, how hand-ins make room, and where overrides are recorded.
screen: Settings Issuing rules
role: Admin or Issuer
keywords: entitlement, six sets, ceiling, most anyone holds, cap, allowance, sets held, over the ceiling, override, hand-in, credit, yearly figure, financial year
---
## The rule
Nobody holds more than the ceiling: 6 sets unless the facility sets its own. It limits what a person holds at any one time, and it is the same for every staff group and every route. There is no financial year in it and nothing resets in July.
A set is one top and one pair of trousers. The ceiling applies to each half, so 6 sets means at most 6 tops and at most 6 pairs. Somebody holding 6 tops and 2 pairs can take another pair, not another top.
Garments that are part of no set, such as fleeces and jackets, have their own ceiling of the same number, counted in garments.
Past the ceiling, the next garment comes only after a hand-in or on a coordinator's override.
## How sets held are counted
A garment's type decides which half it is.
| Half | Types |
|---|---|
| Top | `Shirt`, `Polo`, `Tunic`, `Scrub top`, `Blouse` |
| Trousers | `Pants`, `Trousers`, `Cargo pants`, `Shorts`, `Skort`, `Skirt` |
A garment with no type is judged by its name. A type typed in by hand that is not on the list counts toward no set, so pick from the list.
What a person holds is the total of:
- everything issued to them and not handed in or returned, pre-loved garments included;
- open orders placed for them, less what has already arrived;
- pickups waiting for them at the counter;
- request lines a manager has approved that nobody has collected.
`People`, the person's record and the `Counter` all use this count. The record's meters show tops and pants against the ceiling, with how many are still on order or waiting.
## At the counter
While you build a pickup, the `This pickup` panel says what the person will hold afterwards. A pickup that would take them past either ceiling lists the reason, such as `Past 6 sets — holds 6 tops and 4 pairs`, with the tick `Record as an override`. The server refuses the issue unless the override is ticked, and its refusal says what they hold and how much of it is still to come.
See [issue a garment](/docs/counter/issue-a-garment).
## Where overrides are recorded
- Each issue row that went past the ceiling is stamped as an override, pre-loved rows included. It shows as an `Override` tag in the person's `Holding` list and `Issue history`.
- For garments ordered in, the order's notes say the person is past the ceiling and name the coordinator who recorded the override.
- `People` counts everyone above the ceiling under `Over the ceiling`, and their row's status is `OVER`.
- `Reports People` lists them under `Exceptions` as `Past 6 sets on an override`.
The stamp is written only when the ceiling was actually passed. Garments outside a person's staff group or uniform style carry their own separate stamps.
## Hand-ins
`Record hand-in` on the person's `History` tab, or `Record a hand-in` in the counter's `Hand in` mode, makes room straight away. Each garment is marked `Good` or `Rag`. Matched issues are marked handed in and stop counting toward what the person holds, whether or not credit is ticked. Where only part of an issue line comes back, the line is split.
- `Good` garments join the pre-loved pool. `Rag` garments are counted for disposal.
- `Credit the good garments back` gives sets back to the manager's approvals, newest first, and credits the yearly figure. Only good garments matched to new issues earn credit; pre-loved garments do not.
- `Record & print receipt` prints the hand-in receipt as well.
Returns and size swaps are on [exchanges and returns](/docs/counter/exchanges-and-returns).
## The facility's own figures
| Field | Where | What it does |
|---|---|---|
| `Most anyone holds` | `Settings Issuing rules` | The rule on this page. Nought saves as 6. Fractions are rounded down. |
| `Starting kit` | `Settings Issuing rules` | The first-day kit for groups on the starting kit. |
| `Yearly figure for reports` | `Reports People` | What a year's drawing is measured against. It never limits the counter. A person's own figure is set on their record; groups on the FTE table are not measured. |
Only an Admin can change these. An Issuer sees them read-only.
## What is written
| Record | Change | Undo |
|---|---|---|
| Issue with override | Issue rows stamped as an override, or a note on the order. | Return or hand in the garment. The stamp stays. |
| Hand-in | A hand-in record, matched issues marked handed in, the pool updated. | None on screen. |
| Credit tick | Approval balances and the yearly figure credited. | None on screen. |
| Change the ceiling | The facility's ceiling. | Set it back. |
+80
View File
@@ -0,0 +1,80 @@
---
title: Staff groups and routes
section: people
order: 2
summary: Name your staff groups, put each on the FTE table, the starting kit or manager approval on the route board, and restrict garments by group.
screen: Settings Issuing rules
role: Admin
keywords: staff groups, groups, routes, route board, fte table, starting kit, manager approval, initial kit, rename group, remove group, garment groups, outside group, drag
---
## Name your groups
Staff groups are the facility's own names, on the board `Staff groups and how they get uniform` under `Settings Issuing rules`. A new facility has none, so everyone is on manager approval.
- **Add** a name in `New group name`, then `Add group`. A new group starts on manager approval.
- **Rename** from the group's menu. It changes the name on the list, its route, every staff record filed under it and every garment tagged for it. Renaming onto a name already in use is refused. Changing only the case or spacing is allowed.
- **Remove** from the group's menu. It is refused while any active staff member is filed under the group.
A staff import adds nothing to the list. Groups on the register but not on the list appear under `On the register, not on the list`, each as an `Add` button with a head count. Their staff are on manager approval until the group is added and moved to another route.
An Issuer sees the board but cannot change it.
## The three routes
The board has three columns: `FTE table`, `Starting kit` and `Manager approval`. Each group is a chip in one of them. To change a group's route:
1. **Drag the chip** to another column, or
2. **Open the chip's menu** and pick `Move to …`, or
3. **Press `Alt+←` or `Alt+→`** with the chip focused to move it one column.
A group cannot be on two routes; the server refuses the save. If a restored backup carries a group on both, it is read as being on the FTE table.
Every route stops at the same ceiling of sets held (see [the entitlement rule](/docs/people/entitlement-rule)).
## The FTE table
The person's `Combined FTE` proposes their initial kit:
| FTE | Sets proposed |
|---|---|
| `1.0`, `0.9` | 5 |
| `0.8`, `0.7` | 4 |
| `0.6`, `0.5` | 3 |
| `0.4`, `0.3` | 2 |
| `0.2`, `0.1` | 1 |
| `Casual` | None. The form names 1, 2 or 3 and leaves the number to the manager. |
A fraction not on the table, such as `0.75`, is read by the band it falls in. With no FTE recorded, no kit is proposed and `People` lists the person under `No FTE`.
The proposal is not a limit. A manager may sign for more, and the signed form is recorded with a sentence added, such as `Above the FTE table: 5 sets at 0.6 FTE, where the table proposes 3. Approved by P. Nair.` A casual signed for more than 3 gets one too. A signature does not lift the ceiling.
## The starting kit
`Starting kit`, in sets on day one, is set under `Settings Issuing rules`: 3 unless the facility sets its own. Nought saves as 3 and fractions are rounded down. A figure above the ceiling stops at the ceiling, and the screen says so. After day one, more is issued as needed up to the ceiling. Nothing has to be handed back first.
## Manager approval
There is no starting kit. The person's manager approves sets, on a signed order form recorded on their record or on a request in the staff app.
> **In plain terms** The counter refuses on the ceiling, the staff group and the uniform style. It does not refuse an issue because no approval is on file.
The `Approval` panel on the person's `Uniform` tab names their route. For the starting kit it counts every garment ever issued to them that was not pre-loved or returned in good condition. A hand-in does not give it back.
## Garments restricted by group
Each catalogue garment is tagged for one or more staff groups, or for all groups when it has no tag. See [catalogue, sizes and cuts](/docs/stock/catalogue-sizes-and-cuts).
- The staff app refuses a request for a garment outside the person's group. The one exception is a damage replacement for a garment they already hold.
- A request raised at the counter is refused the same way, and so is an order for the person.
- Issuing one at the counter needs `Record as an override` ticked. Each issue row is stamped as outside the group; for a garment ordered in, the order's notes say so instead.
- `Reports People` lists these under `Exceptions` as `Outside their staff group on an override`.
## What is written
| Record | Change | Undo |
|---|---|---|
| Add group | The facility's group list. | Remove it while nobody active is filed under it. |
| Move a group | The facility's FTE-table and starting-kit lists. | Move it back. |
| Rename | The list, the route, staff records and garment tags. | Rename it back. |
| Remove | The group comes off the list and off its route. | Add it back and move it to its route again. |
+70
View File
@@ -0,0 +1,70 @@
---
title: Managers
section: people
order: 4
summary: The one manager on each record approves that person's requests and signs their order forms. Self-approval, raising for others, email links and changing a manager.
screen: People a person Details & access
role: Admin
keywords: manager, approver, approval, sign, order form, signed form, self-approved, own manager, reports to, email link, needs an approver, re-address, reassign
---
## One manager, both jobs
Each record names at most one manager, in the `Manager` panel on the person's `Details & access` tab. That person approves their requests in the staff app, and a signed paper order form for them is recorded as signed by that manager.
1. **Type a name or staff number.** Only active people on the register are offered.
2. **Pick the name.** It saves straight away.
`Change` picks someone else. `Remove` clears it after a confirmation. Only an Admin can set or change a manager; an Issuer sees the name, or `None set.`. Setting a manager who is inactive is refused. The import's `manager` column sets the same field, by staff number.
Nobody can raise a request, in the staff app or at the counter, without a manager set. A manager who is no longer active counts as none: the request is refused with `The recorded manager is no longer on the register.`
The panel beside it lists whose requests the person approves. `Add somebody who reports to them` changes that other person's record, after a confirmation naming who they are being moved from. `Remove from list` clears that person's manager.
## What a manager signs
In the staff app, a manager approves or declines each garment, or the whole request. A decline needs a reason from a fixed list, and the wearer is emailed the decision if they have an account and the server sends mail. See [manager approvals](/docs/counter/manager-approvals) and [the staff app](/docs/apps/staff-app).
The linen room cannot approve on a manager's behalf.
On paper, `Record a signed form` in the `Approval` panel of the `Uniform` tab takes `Sets`, `FTE`, `Date signed` (not after today), a `Note` and `Photo the signed form`. `Record approval` stays disabled until an active manager is set: with none recorded the form says `Set their manager first.`, and with an inactive one it says `Their manager is inactive: set a new one first.` It records the manager's name linked to their record. An Admin or Issuer can record one; only an Admin can remove one, with `×` under `Previous order forms` on the `History` tab.
## Self-approval
Anyone may be recorded as their own manager. The `Manager` panel then shows `(themselves)` and a `Self-approved` tag.
- A request they decide for themselves is written into its timeline as a self-approval, and `Previous order forms` tags it.
- A signed form they approved for themselves is tagged `Self-approved` on their record.
- In the staff app's approvals, their own requests are set apart under `Your own request`.
## Raising for somebody else
A manager can raise a request in the staff app for the people who report to them. Nobody approves a request they raised for somebody else, so it goes to the raiser's own manager instead. If there is nobody above, or the raiser is their own manager, it is created with no approver and waits in the request queue under `Needs an approver`.
A request raised at the counter goes to the person's own manager.
The linen room addresses a waiting request from the queue, with the approver select and `Ask them` or `Re-address`. Sending it to the person who raised it is refused. Sending it to the person it is for is allowed only if they are recorded as their own manager.
## Email links
When a request is addressed to a manager who has a staff-app account, and the server is set up to send mail, the manager is emailed a link to approve or decline it.
- The link lasts 14 days, and works only while the request is still waiting. Once a decision is made, both links in the email stop working.
- Opening the link shows the request. The decision is made by pressing a button on that page, not by opening the link.
- If the manager or the wearer is no longer on the register, the page shows no details and decides nothing.
- A request for the manager's own uniform says `Your own uniform`.
A manager with no staff-app account is not emailed. The request still waits for them.
## Changing a manager
New requests go to the new manager. Requests already waiting stay addressed to the old manager until the linen room re-addresses them. A manager cannot be deactivated while anyone else active still names them; give those people a new manager first. See [deactivating and deleting](/docs/people/deactivating-and-deleting).
## What is written
| Record | Change | Undo |
|---|---|---|
| Set or change a manager | The person's manager. | `Change` or `Remove`. |
| Record a signed form | An approval with sets, FTE, date, signer, note and photo. | `×` on the approval (Admin). |
| Re-address a request | The request's manager, and a timeline entry. | Re-address again while it waits. |
| Approve or decline | Request and garment statuses, and a timeline entry. | None. |
+74
View File
@@ -0,0 +1,74 @@
---
title: The staff register
section: people
order: 1
summary: The People screen: what a record holds, how to add, import, filter and export the register, and what each tab of a person's record shows.
screen: People
role: Admin
keywords: staff register, people, add a person, import, roster, csv, payroll number, staff number, sizes, fte, uniform style, missing, approver, export, record, details and access
---
## What a record holds
Each person on the register is one record. `Add a person` at the top of `People` opens the `Add staff member` form. Only an Admin sees it.
| Field | Notes |
|---|---|
| `Staff number` | Required and unique in the facility. It cannot be changed after the record is saved, because issue history and reports are keyed to it. |
| `First name`, `Last name` | Both required. |
| `Phone`, `Staff group`, `Department` | The group decides the person's route (see [groups and routes](/docs/people/groups-and-routes)). The department is their ward and decides the cost centre unless an override is set. |
| `Top size`, `Pants size` | One of each. |
| `Uniform style` | `Men's`, `Women's`, `Either`, or not set. Not set offers every style, as `Either` does. |
| `Combined FTE` | `1.0` down to `0.1`, or `Casual`. Only read for groups on the FTE table. |
| `Yearly report figure (garments)` | Used by reports only; the counter never refuses on it. Groups on the FTE table are not measured. |
| `Cost centre override`, `Start date`, `Notes` | Optional. |
The manager and `On the ward desk` are set on the record's `Details & access` tab. Past the plan's staff limit, a new record is refused with `The register is full for this plan`.
## Import from a spreadsheet
`Import the register` opens `Settings Data & audit log` with the `Staff register` import chosen. Its columns are `num,first,last,phone,group,dept,cc,manager,fte,style,top,pants,ccoverride,ent,start,notes`.
- A row with no `num` or no `first` is skipped.
- A row whose staff number is already on the register updates that record. Only cells with something in them are written; a blank cell keeps what is there.
- `dept` with `cc` creates the department if it does not exist.
- `manager` is the manager's staff number, not their name. Managers are linked after every row is in, so a manager can appear anywhere in the file. A number that matches nobody is reported.
- An `fte`, `start` or `style` cell that cannot be read is reported and left blank. Dates are written `YYYY-MM-DD`.
- At most 20,000 rows at a time. New rows past the plan's staff limit are skipped and the refusal is reported once.
An import adds no group names to `Settings Issuing rules`. The template is on [CSV templates](/docs/reference/csv-templates).
## Filter and export
The search box, `Name, number or ward`, sits beside a `Group` select and a `Show` select. `Show` offers `Everyone`, `Missing something`, `No approver`, `No FTE`, `No sizes`, `No staff app`, `No uniform style`, `Receipts to sign` and `Over the ceiling`; every option but `Everyone` shows its count in brackets. When anyone is inactive, a segment switches between `Active` and `Include inactive`.
`Missing something` counts only gaps that stop something working: no approver (none, or one no longer active), no FTE (FTE-table groups only) and no sizes. Inactive records have no gaps.
The line under the filters counts the whole active register, whatever the search. Each row's `Status` is `OK`, `AT LIMIT` (a full half of a set), `OVER` or `Inactive`, and `Missing` names the gaps.
`Export CSV` downloads the rows on screen. The staff import reads its headers back, so a ward's list can go to its manager, come back with `Manager number` filled in, and be imported again. `Approver name (reference only)` is ignored on import. Notes and start dates are left out of the file.
`Requests` opens the [request queue](/docs/counter/requests-from-staff).
## A person's record
Click a name to open it. The top shows their details, their manager and meters for tops and pants against the ceiling, with `Open at the counter` and, for an Admin, `Edit details`.
| Tab | What is on it |
|---|---|
| `Uniform` | `Holding`, with a signed or not signed toggle and `Return` on each line. `Waiting for` pickups, the `Approval` panel and the `Note`. |
| `Requests` | Their requests, `Open` or `All`, and `Open in the queue`. |
| `History` | `Issue history`, `Hand-ins`, `Alterations`, `Previous order forms` and the orders placed for them. |
| `Details & access` | `Details`, `Manager`, whose requests they approve, `Staff app`, and the `Register` panel with `Deactivate` and `Delete`. |
An Issuer can read every tab, and can record a signed form, a hand-in or an alteration. Details, the note, the manager, the FTE and staff-app access are Admin only.
## What is written
| Record | Change | Undo |
|---|---|---|
| Add a person | A new staff record. | Delete it while it has no history. |
| `Edit details`, `Combined FTE` select | The fields on the record. The FTE saves as soon as it changes. | Edit again. The staff number cannot change. |
| Import | Records created or updated, managers linked, departments created. | None. Import a corrected file, or edit by hand. |
Every change is listed in the audit log under `Settings Data & audit log`.
+119
View File
@@ -0,0 +1,119 @@
---
title: CSV templates
section: reference
order: 1
summary: The six import files, the columns each one reads, how a row is matched to a record, and what every importer message means.
screen: Settings Data & audit log
role: Admin
keywords: csv, import, template, import the register, data and audit log, spreadsheet, columns, headers, upload, catalogue, staff register, departments, barcodes, reorder levels, opening balances, bulk load
---
## Load order and the basics
Under `Settings Data & audit log`, in `Import from CSV`, an Admin picks a file kind, presses `Download template` for `threadcount-<kind>-template.csv` (header row plus one example row), fills it in, saves it as CSV and presses `Import CSV`. `Import the register` on `People` opens the same place with the staff register already chosen.
Load in this order, because later files look up what earlier ones created:
1. **Departments & cost centres** (`depts`)
2. **Catalogue** (`catalog`)
3. **Staff register** (`staff`)
4. **Supplier barcodes** (`barcodes`)
5. **Opening balances** (`opening`)
6. **Reorder levels** (`reorder`)
For every file:
- Headers are matched ignoring case, spaces and punctuation, so `Staff no.` reads as `staffno`. Unknown columns are ignored.
- Cells are trimmed and cut at 400 characters. Blank lines are skipped.
- The apostrophe ThreadCount's exports put before `=`, `+`, `-`, `@` or a space is removed, so an exported file imports back unchanged.
- On a re-import a blank cell keeps what the record holds. An import cannot clear a field.
## Departments and catalogue
**Departments & cost centres.** Matched by exact name. The row order becomes the list's order.
| Column | Required | Rule | Example |
|---|---|---|---|
| `dept` | Yes | Also `department`, `name`, `ward`. | `Harbour Ward` |
| `cc` | No | Also `costcentre`, `costcenter`, `code`. Blank keeps the existing one. | `RGH-4010` |
**Catalogue.** Updates the garment with the same `item` and gender, and the same `sku` when given; otherwise creates one.
| Column | Required | Rule | Example |
|---|---|---|---|
| `item` | Yes | Also `name`, `itemname`, `garment`. | `Scrub Top` |
| `gender` | No | Starts `m`: men's. Starts `f` or `w`: women's. Anything else or blank: Unisex. | `Women's` |
| `sku` | No | Also `code`, `productcode`. | `NW-ST-220` |
| `supplier` | No | Also `vendor`. A new name is added to the supplier directory; a known one, in any case, takes the directory's spelling. | `Northline Workwear` |
| `cost` | No | Also `unitcost`, `price`. Non-digits dropped. Blank or 0 keeps the existing cost. | `31.50` |
| `group` | No | Also `staffgroup`. Several separated by a pipe; `All` is every group. Blank: All when new, unchanged on update. | `Midwife` |
| `sizes` | New garments | Also `size`. Separated by a pipe, `,`, `;` or `/`. An update adds new sizes and removes none. | `S/M/L/XL` |
| `notes` | No | Also `note`. | `Navy` |
| `type` | No | Not in the template. Also `producttype`, `garmenttype`. Decides top, trousers or neither. | `Scrub top` |
## Staff register
Updates the person with the same staff number, or creates one. Managers are linked after every row is written, so a manager can sit anywhere in the file. A row without a staff number or first name is skipped.
| Column | Required | Rule | Example |
|---|---|---|---|
| `num` | Yes | Also `staffnumber`, `staffno`, `number`, `payroll`, `payrollnumber`, `id`. | `00400127` |
| `first` | Yes | Also `firstname`, `given`. A `name` or `fullname` column is split at the first space if first and last are blank. | `Amira` |
| `last` | No | Also `lastname`, `surname`, `family`. | `Hassan` |
| `phone` | No | Also `mobile`, `contact`. | `0400 000 000` |
| `group` | No | Also `staffgroup`, `classification`. Not checked against `Settings Issuing rules`. | `Registered Nurse` |
| `dept` | No | Also `department`, `ward`, `wardunit`. | `Harbour Ward` |
| `cc` | No | Also `costcentre`, `costcenter`, `departmentcostcentre`. With `dept`, creates a missing department. Never changes an existing one. | `RGH-4010` |
| `manager` | No | Also `managernum`, `managernumber`, `approver`, `reportsto`. A staff number, not a name. | `00400019` |
| `fte` | No | Also `combinedfte`, `totalcombinedfte`, `totalfte`, `employmentfraction`, `fraction`. Above 0, or `Casual`. | `0.8` |
| `style` | No | Also `uniformstyle`, `uniform`, `cut`, `gender`. `m`, `male`, `men's`: Men's. `f`, `w`, `female`, `women's`, `ladies`: Women's. `either`, `both`, `any`, `all`, `unisex`: Either. | `Women's` |
| `top` | No | Also `topsize`, `shirt`, `shirtsize`. | `M` |
| `pants` | No | Also `pantsize`, `pant`, `trouser`. | `12` |
| `ccoverride` | No | Also `costcentreoverride`. | `RGH-4090` |
| `ent` | No | Also `entitlement`, `annualentitlement`. Whole number for the yearly report; limits nothing. | `10` |
| `start` | No | Also `startdate`, `commenced`. `YYYY-MM-DD`, or day first as `11/03/2024`. | `2026-02-02` |
| `notes` | No | Also `note`. | `Night shift` |
`Export CSV` on `People` writes headers that read back onto these columns. See [the staff register](/docs/people/staff-register) and [managers](/docs/people/managers).
## Barcodes, opening balances, reorder levels
Each row must match exactly one garment on whichever of `sku` (also `code`, `productcode`), `item` (also `name`, `itemname`, `garment`) and `gender` it fills in, then a `size` spelt as on the garment. Blank `gender` matches any. A header named `code` is read as the SKU.
| File | Column | Rule | Example |
|---|---|---|---|
| Supplier barcodes | `barcode` | Also `ean`, `code128`, `scan`. Blank skips the row. | `9300000000017` |
| Opening balances | `opening` | Also `qty`, `quantity`, `onhand`, `count`. Overwrites; blank leaves the size alone. | `14` |
| Opening balances | `reorder` | Optional. Also `reorderat`, `reorderlevel`. Not a number: sets 0. | `4` |
| Reorder levels | `reorder` | Also `reorderat`, `reorderlevel`, `level`. Overwrites; not a whole number skips the row. | `4` |
Negative figures are stored as 0. Every row these files write counts as created. See [barcodes](/docs/stock/barcodes) and [reorder levels](/docs/stock/reorder-levels).
## Messages and what to do
The result reads like `Catalogue: 12 created, 3 updated, 1 skipped.`, then up to 40 row messages. `Row 1` is the first row under the header, blank lines not counted; the two whole-file refusals give the file's line number.
| Message | Meaning | Fix |
|---|---|---|
| `Line N: a quote is opened and never closed…` | Nothing imported. | Fix the quote. |
| `Lines N: the wrong number of columns…` | Nothing imported; usually a stray `"`. | Fix those lines. |
| `No rows found — check the header row.` | Nothing under the header. | Save the right sheet. |
| `Row N: X has no sizes` | New garment skipped. | Add `sizes`. |
| `Row N: start date “X” isn't a date…` | Stored blank. | Write `YYYY-MM-DD`. |
| `Row N: FTE “X” isn't a fraction…` | Stored blank. | Write `0.75` or `Casual`. |
| `Row N: uniform style “X” isn't…` | Stored blank. | Use Men's, Women's or Either. |
| `Row N: no staff member with number X to be the manager` | Not linked. | Add or fix the manager's row. |
| `The register is full for this plan — 60 staff records…` | Said once; further new people skipped, updates applied. | See [plan and billing](/docs/account/plan-and-billing). |
| `Row N: no catalogue items match…` or `several…` | Skipped. | Fill in `item` or `gender`. |
| `Row N: size X not on Y` | Skipped. | Match the size's spelling. |
| `Row N: opening “X” isn't a number…` | Size left alone. | Write a whole number. |
| `X is already on Y · size Z…` | Code bound to another size, as a one-code-per-style list does. | One code per size. |
| `X is the generated code for Y…` | Refused. | Use the printed label's code. |
## Limits
- 20,000 rows a file: `Import at most 20,000 rows at a time`.
- 20 imports and backup restores together per person in 10 minutes: `Too many imports — wait a few minutes.`
- Over 60 MB: `Request too large`.
- An Issuer gets `Admin only`. A read-only facility gets the read-only message pointing to `Settings Plan`.
- Hosted Small holds 60 staff records (`PRICES.freeStaff`). Other plans and the Community edition have no ceiling.
+116
View File
@@ -0,0 +1,116 @@
---
title: Glossary
section: reference
order: 2
summary: The words ThreadCount's screens use, in alphabetical order, each with a line of meaning and the page that explains it.
role: Anyone
keywords: glossary, terms, definitions, words, meaning, vocabulary, what does it mean, audit log, to order, override
---
## AC
**Admin** — The [coordinator role](/docs/start/the-two-roles) that manages settings, the register, the catalogue, prices and suppliers.
**Audit log** — Every change in the facility, newest first, with who made it, under `Settings Data & audit log`. Only an Admin can read it. See [export and backup](/docs/account/export-and-backup).
**Back Order** — An order status for stock the supplier has not yet sent. See [receiving](/docs/stock/receiving-and-back-orders).
**Barcode** — The code bound to one size of one garment, so a scan finds that size. See [barcodes](/docs/stock/barcodes).
**Casual** — The FTE for someone with no fixed fraction. See [groups and routes](/docs/people/groups-and-routes).
**Catalogue** — The garments stocked, each with [sizes, SKU, supplier, cost, type and groups](/docs/stock/catalogue-sizes-and-cuts).
**Ceiling** — The most sets anyone may hold at once, 6 unless changed at `Most anyone holds` under `Settings Issuing rules`, the same for every group. See [the entitlement rule](/docs/people/entitlement-rule).
**Collection code** — The 4-digit code on a request's bag waiting at the counter. See [requests from staff](/docs/counter/requests-from-staff).
**Cost centre** — The finance code a department's issues are [charged to](/docs/reports/cost-centres).
**Cost centre override** — A [cost centre](/docs/reports/cost-centres) on one person's record, used instead of their department's.
## DG
**Damage report** — A garment reported damaged, listed under `Requests Damage` until it is [handed in](/docs/counter/exchanges-and-returns).
**Delivery round** — Bags taken to wards and signed for on screen, from `Deliver on the round` on `Today`. See [delivery rounds](/docs/counter/delivery-rounds).
**Department** — A ward or unit, with its [cost centre](/docs/reports/cost-centres), under `Settings Places & cost centres`.
**Draft** — An order not yet placed with the supplier, shown under its supplier in `To order` on `Orders`. See [the To order list](/docs/stock/order-list).
**Either** — The uniform style offered [both cuts](/docs/stock/catalogue-sizes-and-cuts).
**FTE** — A person's combined employment fraction, such as 0.8, or Casual. See [groups and routes](/docs/people/groups-and-routes).
**FTE table** — The [route](/docs/people/groups-and-routes) where FTE proposes the starting kit, from 5 sets at 1.0 down to 1 at 0.1.
**Grace** — The 14 days after a trial or paid period ends, when changes are still accepted. See [plan and billing](/docs/account/plan-and-billing).
**Grandfathered** — A facility created before plans existed, [hosted free](/docs/account/plan-and-billing) with everything.
**Group** — A staff group, such as Registered Nurse. Each is on one [route](/docs/people/groups-and-routes), and garments are tagged for the groups that wear them.
## HM
**Hand-in** — Garments given back, in `Hand in` mode on `Counter`: good ones join the pre-loved pool, rags are counted for disposal. See [exchanges and returns](/docs/counter/exchanges-and-returns).
**Issuer** — The [coordinator role](/docs/start/the-two-roles) that issues stock, files stock counts and receives deliveries, and cannot edit a price or delete a past issue.
**Items (FY)** — What someone has drawn since 1 July, a column under `Reports People`. It feeds the [reports](/docs/reports/the-nine-reports) and never limits the counter.
**Kit check** — A round, started by an Admin under `Requests Kit check & waitlist`, asking staff in the staff app to confirm what they hold. See [the staff app](/docs/apps/staff-app).
**Manager** — The person, recorded by staff number, who [approves someone's requests](/docs/people/managers) and signs their order form.
**Manager approval** — The route with no starting kit, where the manager [approves each set](/docs/counter/manager-approvals).
## NR
**Needs an approver** — A request nobody has been asked to decide, listed under that heading on `Requests`. See [requests from staff](/docs/counter/requests-from-staff).
**Opening balance** — The count a size starts from, set in `Adjust quantity` or by CSV. See [stocktakes](/docs/stock/stocktakes).
**Override** — An issue past the ceiling, outside the person's group or not their uniform style, recorded by ticking `Record as an override` on `Counter`. See [issue a garment](/docs/counter/issue-a-garment).
**Pre-loved** — Good handed-in garments, reissued at $0, which count toward the ceiling like new ones. See [exchanges and returns](/docs/counter/exchanges-and-returns).
**Rag** — A handed-in garment counted for [disposal](/docs/counter/exchanges-and-returns).
**Read-only** — After grace: reports, exports, printing and the backup still work and nothing is deleted, but most changes are refused. See [plan and billing](/docs/account/plan-and-billing).
**Reorder level** — The count at or below which a size joins `To order` on `Orders`. See [reorder levels](/docs/stock/reorder-levels).
**Request** — Garments asked for in the staff app, worked through on `Today`, on the person's record and on `Requests`. See [requests from staff](/docs/counter/requests-from-staff).
**Route** — How a group's garments are decided: FTE table, Starting kit or Manager approval. See [groups and routes](/docs/people/groups-and-routes).
## S
**Self-approved** — A request approved by someone who is [their own manager](/docs/people/managers).
**Set** — One top and one pair of trousers; the garment's type decides which half it is. See [the entitlement rule](/docs/people/entitlement-rule).
**Slip** — A printed collection or delivery slip, or a staff app code slip. See [slips and signatures](/docs/counter/slips-and-signatures).
**Starting kit** — The [route](/docs/people/groups-and-routes) giving sets on the first day, 3 by default, then more as needed.
**Stock on hand** — The live count of each size on the shelf, under `Stock On hand`. See [stocktakes](/docs/stock/stocktakes).
**Stock take** — A count of the shelf or the pre-loved pool against the system figure, done on `Stock Count` with a `Mode` of `Normal` or `Blind`. See [stocktakes](/docs/stock/stocktakes).
## TZ
**To order** — On `Orders`, for Admins: every size at or below its reorder level, suggested at twice the level less what is on hand and on order, grouped by supplier. See [the To order list](/docs/stock/order-list).
**Trial** — 30 days for a new hosted facility, no card taken. See [plan and billing](/docs/account/plan-and-billing).
**Uniform style** — The cut a person is offered, Men's, Women's or Either, plus everything unisex; blank offers every style. See [the staff register](/docs/people/staff-register).
**Variance** — The difference between a count and the system figure on `Stock Count`. See [stocktakes](/docs/stock/stocktakes).
**Waitlist** — People waiting for a size not on the shelf, under `Requests Kit check & waitlist`; an offer holds it for 48 hours. See [the staff app](/docs/apps/staff-app).
**Ward desk**`On the ward desk` on a staff record, letting that person sign for bags a round leaves at their ward. See [delivery rounds](/docs/counter/delivery-rounds).
**Ward Requests** — The old name of `Requests`, reached from `People`. See [requests from staff](/docs/counter/requests-from-staff).
@@ -0,0 +1,93 @@
---
title: Keyboard and scanner
section: reference
order: 3
summary: Setting up a USB barcode scanner, where a scan goes on each screen, the formats ThreadCount reads and prints, the keys the screens answer to, and how printing works.
screen: Search or scan, Counter, Stock Count
role: Admin or Issuer
keywords: usb scanner, keyboard wedge, enter, suffix, barcode, badge, ean-13, code 128, keyboard, shortcut, slash, ctrl k, search, escape, tab, print, labels, slip, pop-up
---
## Setting up a USB scanner
ThreadCount has no scanner driver. A USB scanner in keyboard mode (keyboard wedge) types the code and ends it with Enter.
1. **Set the scanner to keyboard mode.** Most are out of the box; the scanner's own manual has the setup barcode.
2. **Set the suffix to Enter (carriage return).** With a Tab suffix nothing is recorded.
3. **Scan a staff badge from `Today`.** `Counter` opens with that person chosen.
With no field selected, the portal treats at least 4 characters, each within 35 milliseconds of the last and ending in Enter, as a scan. While a field is selected, a dialog is open or the search panel is open, the portal does not watch for scans: the keys go wherever the cursor already is.
## Where a scan goes
With no field selected:
| What was scanned | Where you are | What happens |
|---|---|---|
| A staff number | Any screen | `Counter` opens with that person |
| A garment barcode | `Counter`, with a person chosen | That size is added to the pickup, in `Issue` mode |
| A garment barcode | `Stock Count` | 1 is added to that size's count |
| A garment barcode | Anywhere else | The garment's page opens at that size |
| A code nobody has | Any screen | Search opens with `No person or garment has <code>.` |
Admins also get `Bind it to a garment` in that search panel. Scan fields that act on Enter:
- `Counter`, `Find a person`: an exact staff number, or else the first match.
- `Counter`, `Scan a garment, or type a name`: adds the size to the pickup.
- `Stock Count`, `Scan to count +1`: selected when the tab opens.
- `Scan to add` on `Stock` (Admin): finds the garment, or offers to add it.
- `Scan sizes` on a garment's page (Admin): binds the code to the size it asks for.
An unknown code in the `Counter` box opens `Unknown barcode` for an Admin; an Issuer sees `No garment has <code>.` On `Stock Count` the dialog opens for everyone, but only an Admin can bind. See [barcodes](/docs/stock/barcodes).
## Formats
| Where | Formats |
|---|---|
| USB scanner | Whatever the scanner types. A binding keeps up to 64 characters. |
| Camera, in the browser and the counter app | EAN-13, EAN-8, UPC-A, UPC-E, Code 128, Code 39, Code 93 |
| Printed labels | EAN-13 for a valid 13-digit code, Code 128 for anything else |
`Generate barcodes` on a garment's page gives each size with no code an EAN-13 starting `29`, the GS1 range for use inside one organisation.
At a window 780 pixels wide or narrower, a `SCAN` button sits at the bottom right. On `Counter` and `Stock Count` it opens that screen's camera; anywhere else it opens search with the camera running. Which browsers can use the camera is on [scanning and browsers](/docs/apps/scanning-and-browsers).
## Keys the screens answer to
| Key | Where | Does |
|---|---|---|
| / or Ctrl+K (Cmd+K on a Mac) | Any portal screen, outside a field or dialog | Opens search |
| Up and Down arrows, Enter | Search panel | Move through results; open the highlighted one |
| Shift+Enter | Search panel, on a person | Opens their record instead of `Counter` |
| Escape | Search panel, any dialog | Closes it |
| Tab, Shift+Tab | Search panel, any dialog | Moves round its controls without leaving it |
| Escape | `Counter`, with a person chosen | Changes person, unless the pickup has lines or a match list is open |
| Ctrl+Enter (Cmd+Enter) | `Counter`, `Issue` mode | Records the issue |
| Down arrow | `Counter` scan box, `Manager` search | Moves to the first match |
| Alt+Left, Alt+Right | `Settings Issuing rules`, a group's menu button | Moves the group one route back; one route on |
| Enter, Escape | Renaming a group | Saves; cancels |
| Enter | `New group name`, `New supplier`, `Add a size`, a size's barcode field | Adds or saves |
## Keys in this manual
The manual has its own keys, on the website at `/docs` and in the app at `/app/help`.
| Key | Where | Does |
|---|---|---|
| / | A manual page, when you are not typing in a field | Puts the cursor in the manual's search box |
| Up and Down arrows | The search results | Move between results |
| Enter | The search results | Opens the highlighted page |
| Escape | The search box | Closes the results |
Manual pages also carry `Print` and `Copy link` buttons.
## Printing
Printing uses the browser's own print dialog. Two routes lead there.
- **A print page** opens the dialog after half a second and keeps a `Print` button for another try. The order form (`Order form` on `Counter`, `Print order form` on a request, `Print the form` on a person's `History` tab), the supplier order sheet (`Print` after ordering, `Order sheet` on an order) and labels (`Print labels (N)` in a garment page's menu) open in a new tab. Collection and delivery slips open in a pop-up window: `Collection slip` on `Counter`, `Slip` under `Call to collect` on `Today`, and `Collection slip` or `Delivery slip` on a request.
- **A print window** carries the document itself: `Print` on each `Reports` panel, `Month-end pack`, `Sheet` on a supplier's `To order` panel, `Print order` on an order, `Print count sheet` on `Stock Count`, `Credit slip`, and the hand-in `Receipt`. If the browser blocks it, ThreadCount says `Pop-up blocked — allow pop-ups for ThreadCount to print.`
The label sheet holds 6 labels to an A4 page. From a garment's page it prints one label per garment on hand for each size with a code, after you confirm the number. From the counter app, a single code prints 1 to 24 copies, 6 unless changed.
> **Careful** The counter app cannot print. A print page opened there says `This app can't print. Open this page in a browser — on the computer at the counter — to print it.`
+62
View File
@@ -0,0 +1,62 @@
---
title: Cost centres
section: reports
order: 2
summary: How an issue reaches a cost centre, why each issue keeps the price of the day it went out, and what moving a person between wards does to the reports.
screen: Settings Places & cost centres
role: Admin
keywords: cost centre, cost center, department, ward, override, price, unit cost, re-price, valuation, move staff, transfer, unallocated, places
---
## Where the cost centre comes from
A cost centre belongs to a department or ward, and a person belongs to a ward. When the reports group an issue, they look up the person it was issued to and use, in this order:
1. **Their cost centre override**, if one is set on their record.
2. **Their ward's cost centre**, from `Settings Places & cost centres`.
If neither gives a code, the issue shows under an em dash in `Reports Spend By cost centre` and under `UNALLOCATED` on the journal. Finance cannot post that line, so the Journal panel is marked; see [Journal export](/docs/reports/journal-export).
## Setting departments and codes
`Settings Places & cost centres` has a `Departments & cost centres` table with the columns `Department / ward`, `Cost centre` and `Staff`. An Admin edits the name and the code in place, and each change saves as you type; for an Issuer the boxes are disabled. To add one, enter a name such as `Ward 4A` under `New department / ward` and a code such as `RGH-4010` under `Cost centre`, then press `Add`. The button stays disabled until both are filled.
- A name already on the list is refused, whatever its capitals, including when you rename a department to it.
- Renaming a department moves its staff and any orders filed under the old name to the new name.
- A department with staff on it cannot be removed; the `×` only appears when its `Staff` count is 0.
- Two departments may share a code. The journal then gives them one line.
- `Export CSV` downloads the table.
The same section links to `Stock Locations`, where rooms, shelves and bays are kept.
## The override on a record
Open the person under `People`, then `Details & access`. The `Cost centre` row shows the code in use followed by `override` or `from ward`. An Admin presses `Edit details` and picks from `Cost centre override`, which lists the codes already on your departments, or `None (from ward)`. See [Staff register](/docs/people/staff-register).
## The price on the day
When a garment is issued, the issue stores the garment's catalogue cost at that moment as its unit cost. This holds at the counter, when a picked request is handed over, on a pickup, and when a delivery goes straight to the person who ordered it. An issue from the pre-loved pool stores $0.
In an exchange, the garment that came back keeps its original unit cost, and the replacement stores the catalogue cost on the day of the exchange. If the original came from the pre-loved pool, the replacement is pre-loved too and stores $0. A partial return splits the row, and both halves keep the unit cost. See [Exchanges and returns](/docs/counter/exchanges-and-returns).
Every issued figure on `Reports` is quantity times that stored unit cost. An issue with no stored cost falls back to today's catalogue cost.
## Why nothing is re-priced
Changing a garment's cost in the catalogue records the old and new cost with your name. It does not touch any issue, so a journal finance has already posted totals the same after a supplier raises its prices.
Three figures are not price-on-the-day. Valuation and Shrinkage on `Reports Stock`, and value saved on `Reports People Pre-loved`, use the current catalogue cost, so they move when a cost changes.
## Moving a person between wards
The cost centre is not stored on the issue. The reports work it out from the person's record each time the screen draws. When you change a person's ward or override, every issue they have ever had moves to the new cost centre, including months already closed. Changing a department's code moves the issues of everyone on it who has no cost centre override.
> **Careful** Changing a ward, an override or a department's code re-files that history in every month, so a reprinted journal for a posted month will no longer match what finance posted.
If a person transfers at month end, print the month-end pack and export the journal first, then change the record. Unit costs stay as they were.
| Record | Change | Undo |
|---|---|---|
| Department | Name and cost centre; a rename also renames it on staff and orders | Edit the boxes back |
| Staff record | Ward and cost centre override | Edit the record back |
| Catalogue cost | New cost and a cost change entry; issues untouched | Enter the old cost |
+70
View File
@@ -0,0 +1,70 @@
---
title: Journal export
section: reports
order: 3
summary: One debit line per cost centre for the month, its exact CSV layout, where the GL account is set, and how to trace a line back to the issues.
screen: Reports Spend Journal
role: Admin or Issuer
keywords: journal, GL, general ledger, account code, finance, debit, CSV, export, posting, month end, reconcile, cost centre, unallocated, spend
---
## What the journal is
The `Journal` panel on `Reports Spend` turns the month's issues into one debit line per cost-centre code. It starts from the `By cost centre` table, drops cost centres with no items this month, and adds together wards that share a code. Lines are sorted by debit, largest first, and a `TOTAL` row closes the table.
The journal counts what `Issued value` counts: issues dated in the month, less garments returned as `Returned - Good`, less issues from the pre-loved pool, each at the unit cost stored when it was issued. Its total equals `Issued value` for the month. See [Spend, Stock and People reports](/docs/reports/the-nine-reports).
The export has debit lines only. There is no credit line and no date column.
## The columns
Press `Export journal CSV` on the Journal panel. The `Export CSV` button in the page header downloads the whole monthly report instead. The journal file is `threadcount-journal-YYYY-MM.csv`, for example `threadcount-journal-2026-08.csv`.
| Column | Holds |
|---|---|
| Cost Centre | The code, or `UNALLOCATED` |
| Department | The ward name; several are joined with ` / ` |
| GL Account | The GL account from Settings, the same on every line |
| Description | The description prefix, a space, then the month |
| Items | Items issued, a whole number |
| Debit | The value to 2 decimal places, with no dollar sign or commas |
The first row is the header and the last is `TOTAL`, with the item count, the debit total and the middle columns blank:
```
"Cost Centre","Department","GL Account","Description","Items","Debit"
"RGH-4010","Ward 4A","631020","Uniform issues August 2026",42,"1386.00"
"RGH-5090","Ward 5C / Theatres","631020","Uniform issues August 2026",17,"602.50"
"TOTAL","","","",59,"1988.50"
```
Text is wrapped in double quotes, and so is the debit. Text that begins with a space, `=`, `+`, `-` or `@` gets a leading apostrophe, so a spreadsheet does not read it as a formula. The file is UTF-8 with a byte-order mark.
## The GL account and description
An Admin sets both under `Settings Facility`, in `Finance & reports`:
- `GL account`, up to 40 characters. While it is blank, the journal shows `—` in its place.
- `Journal description prefix`, up to 120 characters. While it is blank, the journal uses `Uniform issues`.
The month follows the prefix as a full month name and year, so `Uniform issues` becomes `Uniform issues August 2026`. There is one GL account per facility, and the Journal panel's head shows it after `GL`.
## Choosing the period
The journal covers one calendar month, the one in `Reporting month` at the top of `Reports`. There is no custom date range. An issue belongs to the month of its date, which is the day in the facility's `Time zone`, under `Settings Facility`, when it was recorded.
`Print` on the Journal panel prints the same table on A4, headed `End-of-month journal` and the month.
## UNALLOCATED lines
An issue to someone with no cost centre override, whose ward has no code, lands on `UNALLOCATED`. The Journal panel is then marked with the footer `UNALLOCATED = no cost centre`. Once a stocktake has been filed that month, step 3 of the month-end strip shows how many issues are affected, and pressing that number scrolls to the Journal. Until then the step reads `Ready after the count`. Set the person's ward or override on their record, and the line moves to the right code the next time the screen draws. See [Cost centres](/docs/reports/cost-centres).
## Reconciling a line
1. **Pick the month and open `Spend`.**
2. **Press the cost-centre code on the Journal line.** A dialog titled `Issues behind` the code and the month lists the issues: date, staff, item, size, quantity, unit cost and value, with a total that equals the line's debit.
3. **Press `Export CSV` in the dialog** to give finance the detail. The file is named like `threadcount-cost-centre-rgh-4010-2026-08.csv`. Its first line names the cost centre and the month, then come the columns `Date, Staff, Item, Size, Qty, Unit cost, Value` and a `TOTAL` row.
The detail is recounted from the same issues as the line, so the two agree. Both follow each person's cost centre as it is now, not as it was in that month.
> **Careful** Moving a person to another ward re-files their past issues, so export the journal before you change anyone's ward at month end.
+68
View File
@@ -0,0 +1,68 @@
---
title: Month-end pack
section: reports
order: 4
summary: One printed document for finance with the month's summary figures, cost centres, journal and top stock, plus shrinkage, exceptions and approvals when there are any.
screen: Reports
role: Admin or Issuer
keywords: month-end pack, month end, EOM, finance pack, print, PDF, summary, journal, shrinkage, exceptions, approvals, monthly routine, close, month-end steps
---
## What the pack is
`Month-end pack` is a button in the header of `Reports`, and the fourth step of the month-end strip below it. Both print the same A4 document for the month in `Reporting month`, titled `Month-end pack` and the month, for example `Month-end pack — August 2026`. The line under the title gives the facility, the location, today's date and the coordinator.
The pack is print only. The strip's step reads `PDF + journal CSV + valuation`, but pressing it prints the one document and downloads no file. The journal CSV comes from the Journal panel on `Spend`; see [Journal export](/docs/reports/journal-export). Printing writes nothing.
## What it contains
Four sections are always there:
1. **Summary.** The six figures below.
2. **Cost centre summary.** One row per journal line: code, department, items and value, with a `TOTAL` row.
3. **Journal.** One debit per cost centre, headed with the GL account: code, description and debit.
4. **Top stock.** The 10 garments with the most items issued in the month, with quantity and value.
Three sections appear only when they have rows:
- **Shrinkage.** Each stocktake filed from 1 July to the end of the month: date, counted by, variances, net units and net value.
- **Staff exceptions.** Staff, cost centre and the flag, as on `Reports People Exceptions`.
- **Uncollected manager's approvals.** Staff, approved by, and remaining sets.
The pack leaves out supplier spend, pre-loved, the totals by staff group and staff member, the financial year table and the valuation line by line. Print those from their panels; see [Spend, Stock and People reports](/docs/reports/the-nine-reports).
## The summary figures
| Figure | Counted as |
|---|---|
| Issued value | Month's issues at the unit cost stored on each |
| Items issued | Items in those issues |
| Supplier orders placed | Value of orders dated in the month, less drafts, cancelled orders and back orders; delivered lines at receipt cost, the rest at catalogue cost |
| Stock on hand value | Units on hand today at current catalogue cost |
| Shrinkage (FY to end of month) | Net stocktake variance at current catalogue cost |
| Stocktakes counted (FY) | Stocktakes filed from 1 July to the end of the month |
Issues exclude garments returned as `Returned - Good` and issues from the pre-loved pool. Stock on hand value and the approvals are always as at today, even when you print a past month.
## Running and printing it
1. **Open `Reports` and pick the month.**
2. **Press `Month-end pack`.** A new window opens with the document and the print dialog.
3. **Print from the dialog.** If nothing opens, allow pop-ups for ThreadCount and press the button again.
A pack for a closed month can be printed again at any time. Issued figures use the unit cost each issue was stored with, and year-to-date figures stop at the end of that month. The cost centre on each line follows each person's record as it is today; see [Cost centres](/docs/reports/cost-centres).
Today always shows a `Month-end` panel for the current month, with `Deliveries booked in`, `Stock take filed` and `Journal ready`. A row of receipts to sign, with a `Chase` link, appears only when receipts are unsigned. Its unallocated link and its `Month-end pack` button both open `Reports`; neither prints.
## A monthly routine
In the first days of the new month, pick last month on `Reports` and work along the strip:
1. **Check `Deliveries booked in`.** Book in anything overdue from `Orders All orders`.
2. **Check `Stock take filed`.** A stocktake counts in the month it was filed, so file the month's count before the month ends.
3. **Clear `Journal`.** If it shows a number unallocated, press it to jump to the Journal, then set the person's ward or cost centre override on their record; see [Staff register](/docs/people/staff-register).
4. **Open the `People` tab of `Reports`.** Read Exceptions, and follow up Approvals outstanding; see [Manager approvals](/docs/counter/manager-approvals).
5. **Print the month-end pack**, then press `Export journal CSV` and send both to finance.
6. **Only then move anyone between wards.** A move re-files that person's past issues.
Because stock on hand value is as at today, print the pack early in the month for a figure close to month end. See [Stocktakes](/docs/stock/stocktakes).
+66
View File
@@ -0,0 +1,66 @@
---
title: Spend, Stock and People reports
section: reports
order: 1
summary: The three Reports tabs, the month-end strip, what each panel counts and leaves out, the period it covers, and how to print or export it.
screen: Reports
role: Admin or Issuer
keywords: reports, spend, stock, people, finance, cost centre, journal, top stock, valuation, shrinkage, exceptions, supplier spend, approvals outstanding, pre-loved, month, month-end, print, CSV, export
---
## How the screen works
`Reports` has a month select (`Reporting month`), `Export CSV` and `Month-end pack` in its header. Below them sit the month-end strip and three tabs: `Spend`, `Stock` and `People`. The select lists the current month, every month with an issue or an order in it, and the 5 months before the one chosen. Every panel reads the chosen month, except Valuation, Approvals outstanding and the `Pool today` part of Pre-loved, which describe today.
`Export CSV` in the header downloads the main export of the tab you are on: the monthly report on Spend (cost centres, staff groups, staff, suppliers and the financial year in one file), Valuation on Stock, and Exceptions on People. Most panels carry their own `CSV` and `Print`. `Print` opens an A4 page in a new window, headed with the facility, location, today's date and the coordinator.
Old report links such as `?tab=journal` still open the tab that now holds that report.
## The month-end strip
The strip has four steps for the chosen month:
1. **Deliveries booked in.** `All in`, or the number overdue, linked to `Orders All orders`.
2. **Stock take filed.** The date of the latest stocktake filed in the month, or `Not yet` with `Start`, which opens `Stock Count`. Counts of the pre-loved pool do not count.
3. **Journal.** `Ready after the count` until a stocktake is filed, then the number of issues with no cost centre, or `Ready`.
4. **Month-end pack.** Prints the pack; see [Month-end pack](/docs/reports/month-end-pack).
## The panels at a glance
| Panel (tab) | A row is | Period |
|---|---|---|
| By cost centre, By staff group, By staff member (Spend) | A cost centre and ward, a group, a person | Month |
| Journal (Spend) | One cost-centre code | Month |
| Financial year (Spend) | A month | 1 July to the end of the month |
| Valuation (Stock) | A garment with stock on hand | Today |
| Shrinkage (Stock) | A filed stocktake | Financial year to the end of the month |
| Top stock (Stock) | A garment, top 15 | Month, and the financial year |
| Supplier spend (Stock) | A supplier | Month |
| Exceptions, Pre-loved (People) | A flagged person; a pool issue or hand-in | Month |
| Approvals outstanding (People) | An approval with sets remaining | Today |
## What counts as issued
An issue counts when its date falls in the month and the garment has not come back as `Returned - Good`. A garment returned in any other condition still counts. Issues from the pre-loved pool are left out of every issued figure and reported only on Pre-loved.
Every year-to-date figure stops at the end of the chosen month, so a closed month reprints with the figures it first had. Each issue is valued at the unit cost stored on it when it went out; see [Cost centres](/docs/reports/cost-centres).
## Spend
Three figures head the tab: `Issued value` against the month before, `Garments issued` with how many were pre-loved, and `Ordered from suppliers` with the number of orders. Below them sit `By cost centre`, `By staff group`, `By staff member` and `Issued value · last 6 months`, whose bars change the month. A row in `By cost centre` opens the issues behind it. `Journal` and `Financial year` close the tab; see [Journal export](/docs/reports/journal-export).
## Stock
Valuation counts units on hand for every garment and size today, at the current catalogue cost. Garments with no units are left out, and a negative size counts as 0; the panel says how many sizes are negative. The pre-loved pool is not in it.
Shrinkage lists every stocktake filed from 1 July to the end of the month, with lines counted, variances, net units and net value at current catalogue cost. See [Stocktakes](/docs/stock/stocktakes).
Top stock ranks the 15 garments with the most items issued in the month. Supplier spend totals orders dated in the month that were placed; drafts, cancelled orders and back orders are left out. A delivered line is valued at its receipt cost, an undelivered line at catalogue cost.
## People
Exceptions names a person who, in the month, was issued garments on an override past the sets ceiling, outside their staff group or not in their uniform style, or whose items reach `Exception threshold (items/month)` in `Settings Facility`. The threshold is 10 when blank or 0; the ceiling is `Most anyone holds` in `Settings Issuing rules`. Override rows come first. `Items (FY)` is a tally, and nobody is flagged on it; see [The entitlement rule](/docs/people/entitlement-rule).
Approvals outstanding lists every manager's approval with sets not yet collected. Pre-loved lists pool issues with value saved at catalogue cost, hand-ins split into good and rag, and the pool today at $0.
The line `Yearly figure for reports` shows garments per person, and an Admin changes it with `Change`. The counter does not refuse on it.
+126
View File
@@ -0,0 +1,126 @@
---
title: Backups and restore
section: selfhost
order: 4
summary: Nightly database dumps and photo copies from the compose file's own services and volumes, the in-app export as a second copy, and how to restore and test a restore.
role: Self-hosting admin
keywords: backup, restore, pg_dump, pg_restore, database, photos, signatures, volume, cron, export, import, disaster recovery, test restore
---
## What has to be kept
A Community instance keeps its data in two Docker volumes. Back up both, together.
| Volume | Mounted at | Holds |
|---|---|---|
| `threadcount_db` | `/var/lib/postgresql/data` in `db` | Every record: catalogue, stock, staff, issues, orders, requests, users |
| `threadcount_photos` | `/data/photos` in `app` | Signatures and damage photographs |
The names come from `docker-compose.yml`: the project is named `threadcount`, and its volumes are `db` and `photos`.
Photos are not in the database. Each one is a file at `<facility id>/<photo id>.jpg` (or `.png`) under `/data/photos`, and the database holds only that path. A database dump without the photos restores every record but none of the signatures or photographs.
## Nightly dumps
The checkout includes `docker/backup.sh`. Run it from the checkout and give it a destination directory:
```sh
cd /srv/threadcount
docker/backup.sh /srv/backups/threadcount
```
Each run writes a dated directory, for example `2026-09-15-033001`, containing:
- `threadcount.dump`: the database as a custom-format `pg_dump`, taken inside `db` as the `threadcount` user
- `photos.tgz`: the photos directory copied out of `app`
It keeps the newest 14 directories and deletes older ones. Run it nightly from cron:
```
30 3 * * * cd /srv/threadcount && docker/backup.sh /srv/backups/threadcount >> /var/log/threadcount-backup.log 2>&1
```
> **Careful** A backup on the same disk as the volumes is lost with them. Copy the backup directory to another machine.
## The export as a second copy
An admin can download the whole facility as one JSON file from `Settings Data & audit log` with Export backup. It needs no access to the server's command line, and it can be imported on another server.
| Part | In the export |
|---|---|
| All facility records | Yes |
| Photos | The newest ones only: at most 2000 photos and 40 MB of image data. The screen says how many older photos were left out. |
| Users (admins and issuers) | No |
| Staff app logins | No. A restore keeps the existing logins and re-attaches them by staff number. |
Import backup on the same screen asks you to confirm, then replaces all of that facility's data with the file's. Users are kept. See [export and backup](/docs/account/export-and-backup).
Because it leaves out older photos and users, the export is a second copy, not a replacement for the dump.
The Backup line on that screen shows the date of the last export. For admins, `Settings` in the menu shows `!` when that export is more than 7 days old or has never been taken. A run of `docker/backup.sh` does not change that date.
## Restore from a dump
This replaces everything on the server with the backup. Choose the backup directory first; these steps use `2026-09-15-033001`.
1. **Extract the photos.**
```sh
cd /srv/backups/threadcount/2026-09-15-033001
tar -xzf photos.tgz
```
2. **Stop the stack and remove both volumes.**
```sh
cd /srv/threadcount
docker compose down
docker volume rm threadcount_db threadcount_photos
```
3. **Start only the database, then load the dump.**
```sh
docker compose up -d db
docker compose exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < /srv/backups/threadcount/2026-09-15-033001/threadcount.dump
```
4. **Start everything.** `migrate` runs first and finds nothing to apply that the dump does not already have.
```sh
docker compose up -d
```
5. **Put the photos back and give them to the app's user.**
```sh
docker compose cp /srv/backups/threadcount/2026-09-15-033001/photos app:/data/
docker compose exec -u root app chown -R threadcount:threadcount /data/photos
```
Restore onto the release the dump was taken from, or a later one. Migrations only go forwards.
> **Careful** `docker volume rm` deletes the live data. Take a fresh backup first if the current data might still be needed.
## Test a restore
A backup that has never been restored is not proven. Restore into a second, separate stack on the same server, under another project name and port, so the live instance is untouched:
```sh
cd /srv/threadcount
APP_PORT=3100 docker compose -p tcrestore up -d db
docker compose -p tcrestore exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < /srv/backups/threadcount/2026-09-15-033001/threadcount.dump
APP_PORT=3100 docker compose -p tcrestore up -d --build
curl -fsS http://127.0.0.1:3100/api/health
```
Then check what came back:
- **Sign in.** Forward the port with `ssh -L 3100:127.0.0.1:3100` and open `http://localhost:3100` on your own machine.
- **Compare the counts.** The line at the top of `Settings Data & audit log` gives active staff, garments, issues and orders. Check it against the live instance.
When you are done, remove the test stack and its volumes:
```sh
docker compose -p tcrestore down -v
```
@@ -0,0 +1,93 @@
---
title: Configuration reference
section: selfhost
order: 6
summary: Every environment variable, whether it is required, its default and what it does, with the hosted-only ones marked.
role: Self-hosting admin
keywords: configuration, environment, variables, env, .env, settings, reference, session secret, smtp, turnstile, signups disabled, app port, edition, hosted only
---
## How settings are read
Settings go in `.env` in the checkout. `docker-compose.yml` passes the whole file to the `app` container, then sets five values itself, and those win over `.env`:
| Variable | Set by compose to |
|---|---|
| `DATABASE_URL` | The bundled `db` service, using `POSTGRES_PASSWORD` |
| `EDITION` | `community` |
| `PHOTO_DIR` | `/data/photos` |
| `PORT` | `3000` |
| `HOSTNAME` | `0.0.0.0` |
Variables starting `NEXT_PUBLIC_` are compiled into the app when the image is built. The build does not read `.env`: compose passes only `NEXT_PUBLIC_SITE_URL` and `NEXT_PUBLIC_TURNSTILE_SITEKEY` into it. Any other `NEXT_PUBLIC_` variable in `.env` has no effect on a Docker install. A change to either of those two needs `docker compose up -d --build`; any other change needs `docker compose up -d`.
The repository's `.env.example` lists the variables the Community edition reads. The tables below also list the ones only threadcount.tech uses, marked hosted only, so you know they can stay blank.
## Required
| Variable | Default | What it does |
|---|---|---|
| `SESSION_SECRET` | `change-me` | Signs every session cookie. The server refuses to start with the placeholder or with none. Make one with `openssl rand -base64 48`. |
| `POSTGRES_PASSWORD` | none | The bundled database's password. Compose refuses to start without it. |
| `NEXT_PUBLIC_SITE_URL` | `http://localhost:3000` | The address people open. Every link in an email is built from it. Build-time. |
| `EDITION` | none | `community`. Turns off plans, the staff ceiling, the demo and reporting to ThreadCount, and makes Turnstile optional. |
| `DATABASE_URL` | set by compose | The Postgres connection. Required outside compose. |
## Mail
See [email](/docs/selfhost/email).
| Variable | Default | What it does |
|---|---|---|
| `SMTP_HOST` | none | Mail server. Mail is on only when this, `SMTP_USER` and `SMTP_PASS` are all set. |
| `SMTP_PORT` | `587` | `465` connects with TLS from the start. |
| `SMTP_USER` | none | Mail server login |
| `SMTP_PASS` | none | Mail server password |
| `SMTP_FROM` | `SMTP_USER` | The From line |
| `CONTACT_TO` | none | Hosted only. Where the website's contact form is delivered. |
## Switches and the server
| Variable | Default | What it does |
|---|---|---|
| `SIGNUPS_DISABLED` | open | `1` hides facility sign-up and refuses the sign-up endpoint. |
| `APP_PORT` | `3000` | The port on the server the app is published on. Compose reads it; the container always listens on 3000. |
| `PHOTO_DIR` | set by compose | Where signatures and photos are written. Back it up with the database. |
| `TURNSTILE_SECRET` | none | Cloudflare Turnstile on sign-in, sign-up and password reset. Enforced only when set. |
| `NEXT_PUBLIC_TURNSTILE_SITEKEY` | none | Turnstile's site key. Build-time. Set both or neither. |
| `TURNSTILE_OPTIONAL` | none | `1` lets production run without Turnstile. For local tests; a Community instance does not need it. |
| `DB_POOL_MAX` | the driver's default | Local development only. `1` makes every database call wait for the one before. Never set it on a server. |
## Documents, errors, statistics and chat
These are all `NEXT_PUBLIC_` variables, so as the first section explains, they have no effect on a Docker install today.
| Variable | Default | What it does |
|---|---|---|
| `NEXT_PUBLIC_TERMS_URL` | none | Your terms page, linked from the staff sign-in and account screens. With none, no link shows. |
| `NEXT_PUBLIC_PRIVACY_URL` | none | Your privacy notice, linked the same way |
| `NEXT_PUBLIC_GLITCHTIP_DSN` | none | Your own GlitchTip or other Sentry-protocol address for error reports. With none, nothing is reported. |
| `NEXT_PUBLIC_RELEASE` | none | A label stamped on error reports; also shown as `version` at `/api/app-info` when there is no `COMMUNITY_VERSION` file |
| `NEXT_PUBLIC_UMAMI_SRC` | none | Your own Umami tracker script. With none, no statistics are sent. |
| `NEXT_PUBLIC_UMAMI_SITE_ID` | none | Umami site id |
| `NEXT_PUBLIC_UMAMI_APP_ID` | none | Umami id for the app |
| `NEXT_PUBLIC_CHATWOOT_URL` | none | Your own Chatwoot. With this and the token set, a chat widget shows in the coordinator app. |
| `NEXT_PUBLIC_CHATWOOT_TOKEN` | none | Chatwoot website token |
## Hosted only
Nothing in the Community edition reads these. Leave them blank.
| Variable | Used on threadcount.tech for |
|---|---|
| `JACKSON_URL`, `JACKSON_API_KEY` | Single sign-on for facilities |
| `CF_ACCESS_TEAM_DOMAIN`, `CF_ACCESS_AUD` | Sign-in to ThreadCount's own staff tools |
| `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, `STRIPE_PRICE_MONTHLY`, `STRIPE_PRICE_ANNUAL`, `NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY` | Card payments |
| `INVOICE_ENTITY`, `INVOICE_ABN` | Who invoices hosted plans |
| `PLANS_LIVE` | Forcing plans on. Ignored when `EDITION=community`. |
| `DEMO_DISABLED`, `DEMO_RESET_TOKEN` | The public demo facility |
| `ANDROID_APP_FINGERPRINTS_COUNTER`, `ANDROID_APP_FINGERPRINTS_STAFF` | Android App Links for ThreadCount's own Play apps |
| `LISTMONK_URL`, `LISTMONK_LIST_UUID` | The product-update mailing list |
| `CHATWOOT_URL`, `CHATWOOT_API_TOKEN`, `CHATWOOT_ACCOUNT_ID`, `CHATWOOT_INBOX_ID` | Filing contact-form messages in the helpdesk |
threadcount.tech's own settings file has four more variables, for its internal administration. The Community edition does not contain that code.
+73
View File
@@ -0,0 +1,73 @@
---
title: Email
section: selfhost
order: 5
summary: The SMTP settings, every email the product sends, what each screen does when no mail is configured, and how to test it.
role: Self-hosting admin
keywords: email, mail, smtp, password reset, approval link, notifications, ready to collect, supplier order, orders, order and email, no-reply, from address, test email
---
## The settings
Email is optional. It is set with five variables in `.env`:
| Setting | Required for mail | What it does |
|---|---|---|
| `SMTP_HOST` | Yes | Your mail server's hostname |
| `SMTP_PORT` | No | Defaults to `587`. On `465` the connection uses TLS from the start; on any other port it starts plain and upgrades if the server offers it. |
| `SMTP_USER` | Yes | The login for the mail server |
| `SMTP_PASS` | Yes | The password for that login |
| `SMTP_FROM` | No | The From line, e.g. `"ThreadCount <no-reply@example.health>"`. If blank, `SMTP_USER` is used. |
Mail counts as configured only when `SMTP_HOST`, `SMTP_USER` and `SMTP_PASS` are all set. A mail server that needs no login cannot be used.
These are read while the app runs, not when it is built, so a change needs a restart but no rebuild:
```sh
docker compose up -d
```
Links in emails are built from `NEXT_PUBLIC_SITE_URL`, which is compiled in. If the links are wrong, fix that setting and rebuild with `docker compose up -d --build`.
## What is sent
| Email | Sent to | When |
|---|---|---|
| Facility set up | The person who signed up | A facility is created |
| Password reset | An admin or issuer | They ask on the sign-in page. The link works once and expires in 1 hour. |
| Uniform request needing approval | The manager, with a link to approve | A request is raised in the staff app, or re-addressed to another manager from the queue at `/app/requests` |
| Decision | The person the request is for | A manager approves or declines, or someone uses Withdraw it in the requests queue |
| Ready to collect, or coming on the round | The person the request is for | The request is held at the counter or sent on the ward round from the requests queue |
| Waitlist offer | The person on the waitlist | A garment is offered to them, with the time it is held until |
| Supplier order | The supplier's email under `Settings Catalogue & suppliers` | An admin uses Order and email on `Orders`, or Email supplier on the order's page. Replies go to that admin's address. |
Staff notices go only to people whose staff app login has an email address. See [the staff app](/docs/apps/staff-app) and [requests from staff](/docs/counter/requests-from-staff).
When an admin adds a user, the product does not email them their password.
## With no mail configured
Nothing is sent, and the log records `[mail] no SMTP configured — not sending:` followed by the subject. The work itself is still recorded.
- **Sign-up.** The facility is created. The screen says no mail is configured, so the address has not been checked.
- **Requests and decisions.** The request is raised and the decision is recorded. When someone raises a request in the staff app, the screen says their manager hasn't been emailed. After a decision on the approval-link page, it says the wearer hasn't been emailed. A decision made in the staff app does not say whether the wearer was emailed. The requests queue in the coordinator app does not show whether anyone was emailed.
- **Supplier orders.** The order is still raised, but it is not emailed. The screen says "Email is not set up on this server — print the order instead."
- **Password reset.** No link is sent, but the sign-in page still shows "Reset link sent". The only way back in is another admin setting a new password under `Settings People & sign-in`.
> **Careful** Without mail, a facility whose only admin forgets their password cannot get back in through the product. Keep a second admin.
## When sending fails
If mail is configured but the server refuses a message, the action that caused the email still succeeds and `[mail] send failed:` is logged with the reason. Emailing a supplier order is the exception: the screen says "The email could not be sent — try again in a minute, or print the order." and the order is not marked as emailed. Print and CSV stay on the order.
## Test it
1. **Restart after setting the variables.** Run `docker compose up -d`.
2. **Ask for a password reset for your own address.** On the sign-in page, enter your email and use the forgot-password link.
3. **Check the inbox and the log.**
```sh
docker compose logs app | grep '\[mail\]'
```
If a reset email arrives and there is no `[mail]` line, mail works. At most 4 reset emails go to one address in an hour. Check that the link in the email starts with your own address, not `http://localhost:3000`.
+83
View File
@@ -0,0 +1,83 @@
---
title: First run
section: selfhost
order: 2
summary: The first sign-up creates the facility and its admin; then groups, data, a second admin, closing sign-ups, and what the Community edition leaves out.
screen: Settings
role: Self-hosting admin
keywords: first run, sign up, create account, facility, admin, staff groups, routes, import, csv, checklist, today, second admin, signups disabled, community, hosted, differences
---
## Create the facility
There is no default username or password. Whoever creates an account first creates a facility and becomes its admin.
1. **Open your address.** `/` sends you to the sign-in page at `/auth`.
2. **Choose to create the facility's account.** Sign-up is three steps.
3. **Step 1, You.** Your first and last name, your work email and a password of at least 8 characters. The email is where a password reset goes, so it has to be right.
4. **Step 2, Your facility.** The facility name, plus two optional answers. The setting (hospital, aged care or community) chooses a starting list of staff groups. The state sets the time zone that counts and month-end use.
5. **Step 3, Confirm.** You are signed in as the facility's admin. Open ThreadCount on the next screen takes you to `Today`, where the Welcome to ThreadCount checklist lists six first steps, each with a button while it is not done.
| Record | Change | Undo |
|---|---|---|
| Facility | Created with your facility name | Delete the account under `Settings People & sign-in` |
| User | You, role Admin, title Uniform Coordinator | Edit under `Settings People & sign-in` |
The address you typed is not verified. If mail is set up, a welcome note goes to it. If it is not, the screen says so and asks you to make sure the address is right.
## Staff groups first
Open `Settings Issuing rules` and find Staff groups and how they get uniform. It is a board with one column per route: FTE table, Starting kit and Manager approval. If you chose a setting at sign-up, a starting list is there; drag a group to another column, or use its menu to rename or remove it. Otherwise the board says "No staff groups yet — everyone is on manager approval." Add a group with New group name and Add group.
Each group takes one route to its kit. The routes are explained in [groups and routes](/docs/people/groups-and-routes).
## Load your data
`Settings Data & audit log` has Import from CSV. Choose the kind of file (Catalogue, Staff register, Departments & cost centres, Supplier barcodes, Reorder levels or Opening balances), use Download template for its columns, then Import CSV. Re-importing updates matching rows. The columns are in [CSV templates](/docs/reference/csv-templates).
For admins, Import the register on `People` opens this screen with Staff register already chosen. The first two checklist steps on `Today` open this screen too.
## Add a second admin
Do this before you sign out.
1. **Open `Settings People & sign-in`.** Users comes after Two-factor, and only admins see it.
2. **Choose Add user.** Enter the first and last name, an optional title, the role (Admin or Issuer), the work email and a password of at least 8 characters.
3. **Hand the password over yourself.** The screen says so: passwords set here are not emailed.
An admin can later set a new password for another user with Edit on the same list, in New password (leave blank to keep). That is the way back in when mail is not configured: the forgot-password form sends nothing without mail, so a facility whose one admin forgets their password has nobody who can reset it. See [users](/docs/account/users).
## Close sign-ups
Once your facility exists, stop anyone else creating one on your server:
```sh
# in .env
SIGNUPS_DISABLED=1
```
```sh
docker compose up -d
```
With `SIGNUPS_DISABLED=1` the sign-in page no longer offers to create a facility, and the sign-up endpoint answers `403` with "New facility sign-ups are closed." Nothing in the product can reopen sign-ups while the variable is set.
> **Careful** While sign-ups are open, any visitor to your address can create their own facility on your database.
## What differs from hosted
The Community edition is built from the same code, with the parts that belong to threadcount.tech removed before release. Your instance has the coordinator app at `/app`, the phone counter at `/m` and the staff app at `/my`, in full.
| Area | Hosted | Community |
|---|---|---|
| Plans, staff ceiling, trials | Yes | None. Every facility has everything, with no limit on staff records. |
| Single sign-on | Yes | Not included. Password plus an authenticator code. |
| Health-service organisations | Yes | Not included |
| Card payments | Yes | Not included |
| Public website, guides, pricing, legal pages, demo | Yes | Not included. `/` goes to sign-in. |
| Cloudflare Turnstile | Required | Optional. Without it the per-address limits stand alone. |
| Error reports and usage statistics | Sent to ThreadCount | Sent nowhere |
| Android App Links | Yes | Not included |
| Email | ThreadCount's mail server | Yours, or none |
The published Android apps can still use your server: on the app's first screen, change the server, pick Self-hosted and enter your hostname. See [the counter app](/docs/apps/counter-app).
+101
View File
@@ -0,0 +1,101 @@
---
title: Install the Community edition
section: selfhost
order: 1
summary: What the server needs, the four settings that must be set, starting it with Docker Compose, the HTTPS proxy in front, and how to tell it is up.
role: Self-hosting admin
keywords: install, self-host, docker, compose, community edition, server, port, proxy, caddy, https, health check, requirements
---
## What you need
The Community edition runs as three containers from one `docker-compose.yml`. Before you start you need:
- A Linux server with Docker and Docker Compose. The README suggests 2 CPU and 2 GB of memory to start.
- A hostname pointing at that server, for example `uniforms.example.health`.
- An HTTPS reverse proxy in front of it (Caddy, nginx or Traefik).
HTTPS is not optional. The app runs with `NODE_ENV=production`, and in production the session cookie is marked `Secure`. A browser on another machine will not send that cookie over plain HTTP, so nobody can sign in.
The three services are:
| Service | Image | What it does |
|---|---|---|
| `db` | `postgres:16-alpine` | The database, in the named volume `db` |
| `migrate` | built locally | Runs `npx prisma migrate deploy` once, then exits |
| `app` | built locally | The product, on port 3000, photos in the named volume `photos` |
## Clone and set the four settings
```sh
git clone https://github.com/pricehq/threadcount-community.git
cd threadcount-community
cp .env.example .env
```
Open `.env` and set these four. The rest can stay blank.
| Setting | What to put |
|---|---|
| `SESSION_SECRET` | A long random string: `openssl rand -base64 48`. The server refuses to start while it still says `change-me`. |
| `POSTGRES_PASSWORD` | Any long password. Compose refuses to start without it. |
| `NEXT_PUBLIC_SITE_URL` | The address people will type: `https://uniforms.example.health`. |
| `EDITION` | `community` |
`NEXT_PUBLIC_SITE_URL` is compiled into the app when the image is built, and it is the base of every link in an email (password resets, approval links). If it is blank the build uses `http://localhost:3000`. If you change it later you must rebuild.
> **Careful** The image build does not read `.env`: only `NEXT_PUBLIC_SITE_URL` and `NEXT_PUBLIC_TURNSTILE_SITEKEY` are passed in as build arguments. Other `NEXT_PUBLIC_` values in `.env` do not reach a Docker install. See [the configuration reference](/docs/selfhost/configuration-reference).
## Build and start
```sh
docker compose up -d --build
```
The first start builds the image, which takes a few minutes. Compose then starts `db` and waits for `pg_isready`, runs `migrate` to create the schema, and starts `app` only once `migrate` has finished without an error.
Watch progress with:
```sh
docker compose ps
docker compose logs -f app
```
If the app stops at once with `Refusing to start:`, the message names the setting that is missing or unsafe. In the Community edition it checks two: `DATABASE_URL` and `SESSION_SECRET`. `DATABASE_URL` is filled in by `docker-compose.yml`, so you do not set it.
## The port and the proxy
Inside the container the app always listens on 3000. On the server it is published on `APP_PORT`, which defaults to `3000`. Change `APP_PORT` in `.env` if 3000 is taken.
Point your proxy at that port. A Caddyfile for Caddy is:
```
uniforms.example.health {
reverse_proxy 127.0.0.1:3000
}
```
Docker publishes the port on every interface of the server, not only `127.0.0.1`. Firewall it, or set `APP_PORT=127.0.0.1:3000` so only a proxy on the same machine can reach it.
The sign-in, sign-up and reset limits count per address. The app reads the address from the last entry of the `X-Forwarded-For` header, then from `X-Real-IP`. Caddy sets `X-Forwarded-For` for you. If neither header arrives, every visitor counts as the same address, and one person's failed attempts count against everyone.
## Check it is up
The app answers `/api/health` without signing in. It asks the database a question and replies:
| Answer | Meaning |
|---|---|
| `200` with `{"ok":true}` | The app is serving and the database answers |
| `503` with `{"ok":false}` | The app is running but cannot reach the database |
```sh
curl -fsS https://uniforms.example.health/api/health
```
The image runs the same check itself every 30 seconds, so `docker compose ps` shows `app` as `healthy` once it passes.
`/api/app-info` is also public. It returns `"product": "threadcount"`, `"edition": "community"` and the release in `version`, which is read from the `COMMUNITY_VERSION` file. The Android apps call it before they will point at your server.
## Next
Open `https://uniforms.example.health`. The address `/` sends you to `/auth`. Go on to [first run](/docs/selfhost/first-run) to create the facility, then set up [email](/docs/selfhost/email) and [backups](/docs/selfhost/backups) before anyone relies on it.
+79
View File
@@ -0,0 +1,79 @@
---
title: Update to a new release
section: selfhost
order: 3
summary: Back up, fetch the release, rebuild, and let the migration run before the new app starts. Why a plain git pull does not work.
role: Self-hosting admin
keywords: update, upgrade, release, new version, git pull, fetch, reset, rebuild, migrations, schema, changelog, version
---
## How releases are published
Each Community release is one commit with no history behind it. It is exported from ThreadCount's own code and the parts that belong only to threadcount.tech are removed. Before it is pushed, the result must type-check, build with `EDITION=community` and pass a smoke test. The new commit replaces the previous one on the `main` branch.
The commit message names the date it was built. The same date and the source commit are written to the `COMMUNITY_VERSION` file in the checkout, in this form:
```
community 2026-09-15 b36d739
```
A running server reports that line as `version` at `/api/app-info`.
## Back up first
Before every update, take a database dump and a copy of the photos, as in [backups](/docs/selfhost/backups):
```sh
docker/backup.sh /srv/backups/threadcount
```
Migrations only go forwards. `prisma migrate deploy` applies new migrations and has no step that undoes one, so the way back to an older release is to restore the dump you took before updating.
## Fetch the release
Because each release replaces the last commit rather than adding to it, `git pull` refuses to merge the two. Fetch, then move your checkout to the new commit:
```sh
cd /srv/threadcount
git fetch origin
git reset --hard origin/main
cat COMMUNITY_VERSION
```
`.env` is ignored by git, so `git reset --hard` leaves it alone.
> **Careful** `git reset --hard` throws away any change you made to a file in the repository, `docker-compose.yml` included. Keep local settings in `.env`, or copy your changes somewhere before you reset.
## Rebuild and start
```sh
docker compose up -d --build
```
What happens, in order:
1. **The images are rebuilt.** The build takes `NEXT_PUBLIC_SITE_URL` and `NEXT_PUBLIC_TURNSTILE_SITEKEY` from `.env` again.
2. **`db` is checked.** Compose waits until Postgres answers `pg_isready`.
3. **`migrate` runs.** It runs `npx prisma migrate deploy` against the bundled database and applies any migration the database does not have yet.
4. **`app` starts.** Compose starts the new app only if `migrate` exited successfully.
If a migration fails, `app` is not started. Read what went wrong with:
```sh
docker compose logs migrate
```
Then check the app is healthy:
```sh
docker compose ps
curl -fsS https://uniforms.example.health/api/health
```
## What changed
The Community edition has no changelog of its own. The [changelog](/changelog) on threadcount.tech lists what changed for the people who use ThreadCount, newest first. It is written for the hosted service, so entries about plans, card payments, single sign-on or the website do not apply to your server.
## Phones after an update
The Android apps ask your server at `/api/app-info` for the oldest app version it still works with, given in `minApp`. A phone running an older app is told to update rather than failing. Phones using `/m` or `/my` in a browser get the new version the next time the page loads.
@@ -0,0 +1,77 @@
---
title: Set up in an afternoon
section: start
order: 2
summary: From creating the facility to the first garment issued at the counter, in the order the Getting set up checklist on Today asks for it.
screen: Today
role: Admin
keywords: sign up, signup, create account, new facility, setup, onboarding, checklist, getting set up, welcome, plan, trial, import, opening stock, opening balance, first issue
---
## Create the facility
Sign-up is three steps.
1. **You.** First name, last name, work email and a password of at least 8 characters. Password resets go to that email, so it has to be right.
2. **Your facility.** The facility name, plus two optional answers. `Setting` (Hospital, Aged care, Community health or Other) chooses a starting list of staff groups. `State or territory` sets the time zone that counts and month-end are read in, and nothing else.
3. **Plan**, or **Confirm** when the server is not offering plans. Where the server publishes terms or a privacy policy, the create button stays off until you tick that you agree.
The plan step offers two choices. Neither takes a card.
| Plan | Price | Limit |
|---|---|---|
| Hosted Small | Free | Up to 60 staff records |
| Hosted Facility | 30-day trial | Then $1,290 a year |
Health Service is not offered at sign-up. A Community edition server has no plans and no staff-record ceiling.
## What is written
| Record | Change | Undo |
|---|---|---|
| Facility | Created with your name as coordinator, the plan you chose, any seeded staff groups and the time zone | See [Delete an account](/docs/account/delete-an-account) |
| User | You, as `Admin`, with the title Uniform Coordinator | Edit under `Settings People & sign-in` |
| Email | A welcome note to the address you typed | None |
Sign-up is refused for an email that already has an account, and after 5 sign-ups from one connection in an hour. `Open ThreadCount` then takes you to `Today`, where the checklist is titled `Welcome to ThreadCount`.
## Staff groups
Staff groups are under `Settings Issuing rules`, on the board headed `Staff groups and how they get uniform`. It has three columns, one per route: `FTE table`, `Starting kit` and `Manager approval`. An Admin drags a group between columns, or opens its menu for `Move to …`, `Rename` and `Remove`. Type a name in `New group name` and press `Add group` to add one.
`Rename` moves every staff record filed under the group to the new name, along with garments tagged for it, and keeps its route. A group with active people filed under it cannot be removed. The rules for each route are on [Groups and routes](/docs/people/groups-and-routes).
## Load your data
`Settings Data & audit log` has `Import from CSV`, which takes a spreadsheet saved as CSV. The `Import the register` button on `People` opens it with the staff register chosen.
1. **Choose what you are importing** from the list.
2. **Press `Download template`** and fill it in.
3. **Press `Import CSV`** and choose the file. Re-importing updates matching rows.
Import is shown to Admins only, and each user is limited to 20 imports in 10 minutes. Import the staff register and the catalogue first. The columns each template takes are on [CSV templates](/docs/reference/csv-templates). Add suppliers under `Settings Catalogue & suppliers`, with their order email and `Lead time (days)`.
## Opening stock and the first issue
Set reorder levels on each garment's page, which opens from `Stock On hand`. In the `Sizes` panel, the `Reorder at` column has minus and plus buttons for each size. To set one level across many garments, tick them on `On hand` and use `Set reorder`.
Then press `Adjust quantity` at the foot of `On hand` and choose `Opening balance`, which overwrites a line's opening balance and is for start-up only. Only an Admin can set an opening balance or a reorder level.
When the shelf is loaded, open `Counter`, find someone on the register and issue a garment. [Issue a garment](/docs/counter/issue-a-garment) walks through the counter.
## The setup checklist
`Today` shows a checklist, titled `Getting set up`, of six things a new facility does once.
| Step | Button |
|---|---|
| Add staff, or import the register | `Settings Data` |
| Add garments | `Settings Data` |
| Set reorder levels | `Stock` |
| Record opening stock | `Stock` |
| Issue a garment | `Counter` |
| Bind a barcode or print labels | `Stock` |
Each tick comes from the records, not from a click. For example, the first step is ticked once the staff register has a row in it. Every step still to do has a button to the screen that does it.
The checklist disappears once all six are ticked, or once the facility is 60 days old and three or more are ticked. An Admin can hide it sooner with `Dismiss`, and it stays hidden for the whole facility. It does not appear in the phone app.
+66
View File
@@ -0,0 +1,66 @@
---
title: The two roles
section: start
order: 3
summary: What an Admin can do that an Issuer cannot, as the server enforces it, and what staff see when they sign in to the staff app.
screen: Settings People & sign-in
role: Anyone
keywords: roles, permissions, admin, issuer, access, user, users, staff sign-in, staff app, activation code, who can
---
## Two roles, checked on the server
Every user of the linen-room app is either `Admin` or `Issuer`. The server checks the role on every change, so hiding a button is not the only protection. If an Issuer attempts a change they are not allowed to make, the server refuses it with `Admin only`, whichever screen it came from.
Your name and role are at the foot of the menu, or at the bottom of the `More` sheet on a phone. A sign-in lasts 14 days, or 30 days if you tick to stay signed in on that computer. Changing a password ends every older sign-in for that user.
## What each role can do
An Admin can do everything an Issuer can, plus everything in the Cannot column. The one thing an Admin cannot do is remove or demote the last active Admin.
| Role | Can | Cannot |
|---|---|---|
| Issuer | Issue, return, swap sizes and take hand-ins on `Counter` | Add, edit or delete catalogue items, sizes or barcodes |
| Issuer | Work requests, pickups and delivery rounds | Change reorder levels, supplier codes or catalogue prices |
| Issuer | File a count on `Stock Count`, receive stock, add to the pre-loved pool | Set on hand, adjust or write off stock, or set opening balances |
| Issuer | Create an order with `New order` or `Order for a person`, and receive deliveries | Raise the `To order` list, email a supplier or cancel an order |
| Issuer | Read `People` and `Reports` | Add, edit or delete staff, departments or locations, or generate staff-app codes |
| Issuer | Read `Settings`, where the fields are disabled | Change settings, staff groups, suppliers, users or the plan |
| Issuer | Change their own details and password | Read the audit log, import CSV, restore a backup, wipe or reset data |
On `Orders`, an Issuer sees `Recent orders` where an Admin sees `To order`. The `Mark ordered` and `Order sheet` buttons on an order, and `Mark shipped` in its menu, are shown to Admins only.
## Managing users
Admins see the `Users` list under `Settings People & sign-in`, with `Add user` and an `Edit` button on each row. The user dialog's `Role` sets `Admin` or `Issuer`. The facility must always have at least one active Admin, so the server will not demote or remove the last one and replies `Keep at least one active admin`. See [Users](/docs/account/users).
## Staff sign-ins
Staff on the register are not users, and they never have either role. A staff member signs in to the staff app, at `/my`, with a separate account.
1. **An Admin generates a code** on the person's record, under `People Details & access`, in the `Staff app` panel. It is twelve characters in three groups, and `Print the slip` prints it.
2. **The staff member activates it** at the staff sign-in with `First time? I have a code`, then chooses an email and password. A code generated 14 or more days ago is refused as expired.
3. **After that they sign in** at the staff sign-in, or through the ordinary sign-in on the website.
A staff sign-in lasts 30 days. It is a different kind of session from a user's, so it cannot open the linen-room app. A user's session shows nothing in the staff app either.
## What staff can see
The staff app menu is `Home`, `Kit`, `Orders` and `Messages`. In it, staff can:
- see what they hold and raise a request for garments
- send messages about their own requests, report damage and raise a dispute
- join, leave or accept a waitlist place, and answer a kit check when one is open
- change their own password
Two extra views come from the register, not from a role. If someone is named as another person's manager, they also see approvals. If someone is marked `On the ward desk`, they see the delivery round for their ward.
## What is written
| Record | Change | Undo |
|---|---|---|
| User role | `Admin` or `Issuer`, set under `Settings People & sign-in` | Set it back, as long as one active Admin remains |
| Staff activation code | Generated on the person's record, stamped with the date | `Cancel the code`; it expires after 14 days anyway |
| Staff account | Created when the code is used | `Remove access` on the record, which ends its sign-ins |
If a staff member is deactivated on the register, they lose the staff app at once. When they try to sign in they are told `You're no longer on the register at this facility. Ask the linen room.`
@@ -0,0 +1,78 @@
---
title: ThreadCount in one page
section: start
order: 1
summary: What ThreadCount records, the screens in the menu, the search and scan bar, who signs in, and where to find each first-month task.
role: Anyone
keywords: overview, introduction, menu, screens, today, counter, stock, orders, people, search, scan, what is threadcount, getting started, help, manual, docs
---
## What it records
ThreadCount records uniform stock for one facility and keeps three things in step.
- **Who holds what.** Every garment issued, returned, swapped or handed in is written against a person on the staff register, so each person's holding is always current.
- **The shelf, by size.** Each garment in the catalogue has sizes, and each size has its own on-hand figure, reorder level and supplier code. Issues take stock off the shelf. Deliveries and stock counts put it back or correct it.
- **What to order.** Sizes at or below their reorder level are gathered on `Orders` into one panel per supplier, raised as orders, and received against when the delivery arrives.
All three come from the same records. A garment issued at the counter changes the person's holding and the shelf figure. If the size drops to its reorder level, it appears under `To order`.
## The menu
The menu down the left side lists the screens in this order.
| Screen | What it is for |
|---|---|
| `Today` | The queue: the setup checklist, bags to call people about, the delivery round, approved requests to pick, deliveries due and counts due, with the month-end steps beside it. |
| `Counter` | Find the person first, then `Issue`, `Return`, `Hand in` or `Swap a size`. |
| `Stock` | Three tabs: `On hand`, `Count` and `Locations`. Each garment has its own page. |
| `Orders` | `To order` by supplier, `On the way`, and the ledger of all orders. |
| `People` | The staff register and each person's record, with the tabs `Uniform`, `Requests`, `History` and `Details & access`. |
| `Reports` | Three tabs, `Spend`, `Stock` and `People`, under the month-end steps. |
| `Settings` | Facility, Issuing rules, Catalogue & suppliers, Places & cost centres, People & sign-in, Data & audit log, and Plan. |
Some screens are not on the menu. `Delivery rounds` lights `Today`. The full request queue is opened with the `Requests` button on `People`, and lights `People`. The audit log is at the foot of `Settings Data & audit log`. Your name and role are at the foot of the menu, with `Sign out`.
On a narrow screen the bar along the bottom holds `Today`, `Counter`, `Stock` and `More`. `More` holds Orders, People, Requests, Delivery rounds, Reports, Settings, Help and Counter app. A `SCAN` button opens the camera.
## Search and scan
The bar across the top reads `Search or scan: a person, a garment, an order`. Press `/`, or Ctrl+K (⌘K on a Mac), to open it from anywhere except a field or a dialog.
- **A person.** Type a name or staff number. Enter opens them at the counter, and Shift+Enter opens their record.
- **A garment.** Type a name or SKU to open its page on `Stock`, or type its barcode and press Enter.
- **An order.** Type the order code, the supplier's reference, the invoice number or the supplier.
A barcode scanner works without opening the bar. Scanning a staff number opens that person at the counter. Scanning a garment adds it to the pickup when the counter has a person open, counts it on `Stock Count`, and otherwise opens the garment's page.
## Who signs in
The people who run the linen room sign in as users, and each user has one of two roles.
- **Admin** sets the facility up and makes the changes that affect everyone: the catalogue, reorder levels, the staff register, users, and raising the `To order` list with suppliers.
- **Issuer** works the counter: issues, returns, requests, stock counts and receiving deliveries.
The person who creates the facility is its first Admin. Staff who wear the uniform are not users. They sign in to the staff app with a separate account that shows their own record and the requests they are part of. [The two roles](/docs/start/the-two-roles) sets out what each can do.
## Where the manual lives
The same pages appear in three places.
- On the website, under `/docs`.
- In the app, under `Help`. Every screen has a `?` beside its title that opens the page about that screen. On a phone, `Help` is also in the `More` sheet.
- In the Community edition's source repository, `pricehq/threadcount-community`, in the `docs/manual` folder.
## How do I…
| Task | Page |
|---|---|
| Load the staff register and catalogue from a spreadsheet | [CSV templates](/docs/reference/csv-templates) |
| Issue a garment at the counter | [Issue a garment](/docs/counter/issue-a-garment) |
| Set the level at which a size is reordered | [Reorder levels](/docs/stock/reorder-levels) |
| Count the shelf | [Stocktakes](/docs/stock/stocktakes) |
| Order from a supplier | [Your first order](/docs/start/your-first-order) |
| Book in a delivery that arrived short | [Receiving and back orders](/docs/stock/receiving-and-back-orders) |
| Give a colleague a login | [Users](/docs/account/users) |
| Get staff onto the staff app | [Staff app](/docs/apps/staff-app) |
| Close off the month for finance | [Month-end pack](/docs/reports/month-end-pack) |
| Take a backup of everything | [Export and backup](/docs/account/export-and-backup) |
+68
View File
@@ -0,0 +1,68 @@
---
title: Your first order
section: start
order: 4
summary: From the To order list on Orders to a delivery on the shelf, including what happens when it arrives short.
screen: Orders
role: Admin
keywords: order, ordering, purchase order, supplier, reorder, raise, to order, order list, order and email, on the way, csv, email supplier, receive, delivery, back order, invoice
---
## Before you start
The `To order` list depends on two things you set up once.
- **Reorder levels.** A size appears on the list when its on-hand figure is at or below its level. The same sizes show under `At reorder` on `Stock On hand`. See [Reorder levels](/docs/stock/reorder-levels).
- **Suppliers** under `Settings Catalogue & suppliers`: `Account no.`, `Order email` and `Lead time (days)`. Each garment names its supplier. See [Suppliers](/docs/stock/suppliers).
Enter each size's supplier code in the `Ordering` panel on the garment's page. A line with no code shows `no code`, which links to that garment.
## To order
On `Orders`, an Admin sees `To order`, with one panel per supplier in A to Z order. Each heading gives the lead time and order email, or `no email on file`. Issuers see `Recent orders` instead.
Each line is a size at or below its reorder level. The table shows `Code`, `Garment`, `On hand`, `Reorder`, `On order`, `Per week`, `Order` and `Cost`, with a button at the end of each line to remove it. The suggested quantity is twice the reorder level, less what is on hand and what is already on order, and never below 0. A line marked `runs out before this arrives` will run out before a delivery ordered today would arrive.
- Change any quantity with the stepper, or remove a line with `×`.
- `Add a line` adds any garment and size.
- If you have already placed the order on the supplier's website, type their order number into `Supplier order no.`. It is optional.
- `Sheet` prints the panel as it stands, before anything is raised.
The supplier's draft orders are listed under the table, each tagged `staff` for a person's order or `draft` for a stock draft, with its own `Supplier order no.` box. A person's order is never merged into the shelf's order.
## Order and email
Press `Order and email`. If the supplier has no order email, the button reads `Order` instead.
1. **The stock lines become one order** marked `Ordered`, with the supplier's order number as its reference. Lines left at zero are dropped.
2. **Each draft is raised with its lines unchanged** and marked `Ordered`.
3. **Each raised order is emailed** to the supplier, when the button said `Order and email`.
4. **The panel lists the raised orders**, with a count such as `1 order raised` beside the supplier name, each with `Print`, `CSV` and `Email`. Press `Done` to close it.
`Print` opens the A4 order sheet with the supplier's product codes. `CSV` downloads Supplier code, Description, Size, Qty and Unit cost. `Email` sends it again. An email is refused if the supplier has no email address, if email is not set up on the server, or if the order is a draft or cancelled.
## What is written
| Record | Change | Undo |
|---|---|---|
| Order | One new order for stock, `Ordered`, with a code like `ORD-2026-0001` and an expected date the supplier's lead time away, or 14 days if none is set | An Admin can `Cancel order` while it is Draft, Ordered, Shipped or Back Order |
| Draft order | Status changed to `Ordered` | Cancel, as above |
| Order | The time it was emailed or printed | None |
## Receive the delivery
Raised orders move to `On the way`, overdue ones first. When the boxes arrive, press `Receive` there, or open the order and press `Receive delivery`. Orders overdue or due within 2 days also appear under `Receive` on `Today`. An Admin can choose `Mark shipped` from the order's menu, though it is not required.
1. **Enter the invoice number and arrival date.** A note and `Photo the invoice` are optional.
2. **Enter what arrived on each line.** Each line starts at the quantity still outstanding. `Scan items off the box (camera)` adds one for each scan.
3. **Choose the destination.** `Shelf` puts the garments into stock. On an order for a staff member, `Pickup` puts them on the call list instead.
4. **Check the cost.** If the invoiced cost is not the catalogue cost, choose `Keep`, or an Admin can choose `Update catalogue cost`.
5. **Press `Receive`.**
The server refuses a line where more arrived than was outstanding, and refuses a delivery where nothing arrived.
## Back orders
Anything that did not arrive goes on a back order. The original order is marked `Received`. A new order with status `Back Order` is created for the shortfall. It keeps the same supplier reference and has the note `Back order — short on` followed by the original order's code. The original order links to it under `Back order`, and the new order's heading reads `back order of` and the original code.
When the rest arrives, receive the back order the same way. A received delivery cannot be reversed, so an Admin corrects the shelf with `Adjust quantity` instead. Every order, drafts included, is on the ledger at `Open the ledger`. There is more detail on [Receiving and back orders](/docs/stock/receiving-and-back-orders) and [The To order list](/docs/stock/order-list).
+62
View File
@@ -0,0 +1,62 @@
---
title: Barcodes
section: stock
order: 2
summary: Binding the supplier's barcode to each size, dealing with an unknown code once, printing ThreadCount's own labels, and the formats the scanners read.
screen: Stock a garment
role: Admin
keywords: barcode, scan, bind, unbind, unknown barcode, label, print labels, generate barcode, scan sizes, scan to add, EAN-13, Code 128, scanner, GTIN
---
## Supplier barcodes
Each size of a garment carries one barcode, and a code belongs to one size in the facility. Most garments arrive with the supplier's code on the swing tag, and that is the code to bind.
1. **Open the garment.** `Stock On hand`, then the garment's name.
2. **Press Scan sizes.** The dialog starts on the first size with no barcode.
3. **Scan each size's label.** After each scan it moves to the next unbound size. **Scan** or **Re-scan** on a row points it at that size instead.
4. **Add a size while scanning.** Type it into `Size that isn't on the item yet`, then scan. The size goes on the end of the list with its code.
Codes can also be bound in bulk from `Settings Data & audit log` with the Supplier barcodes template.
## An unknown code
On `Stock Count`, a code that matches nothing opens the `Unknown barcode` dialog. An Admin picks the garment, then clicks the size, and the code is bound from then on. An Issuer sees the code and is told only an admin can bind it. **New product from this barcode** (Admin) opens `Add catalogue item` with the code waiting for its size.
A scanner used outside any field on another screen opens the search panel with `No person or garment has` the code. Admins get **Bind it to a garment** there, which opens the same dialog.
**Scan to add**, at the top of `Stock` (Admin), works through a pile of garments: a known code offers **Open product**; an unknown one offers a new product or a binding to a garment you already have.
The dialogs check the digits. A code that fails its check digit is flagged as a possible mis-read but can still be bound, and so can a code of a non-standard length.
With `Settings Catalogue & suppliers Look up unknown barcodes in public databases` ticked, `Scan to add` sends the barcode number, and nothing else, to UPCitemdb, then Open Products Facts, and shows any product name found. Only valid EAN-13, UPC-A, EAN-8 and GTIN-14 codes are looked up, up to 120 an hour per facility. It is off by default.
## Typed, generated and moved codes
The garment page's `Sizes` panel has a barcode box on each row (Admin). Type or scan the code and press Enter or **Save**. Codes may carry letters. To take a code off, clear the box and confirm, or press **×**.
**Generate barcodes**, in the garment page's menu, gives every size without a code one of ThreadCount's own; **Generate** on a row does one size. Sizes with a supplier code keep it. A generated code is a 13-digit EAN-13 starting `29`, the GS1 range for use inside one business, with a real check digit. Numbers come from a counter held for the facility, so two people labelling at once can't be given the same one.
A code already on another garment is refused with where it is. Confirm the move, or press **Move … onto size … anyway** in `Scan sizes`, and the code leaves the old size for this one. Binding a code that is another garment's internal `93` number is refused.
## Printing labels
**Print labels (N)**, in the garment page's menu, prints one label for every garment on hand across the sizes that carry a barcode. It asks first, then opens an A4 sheet, 2 labels across, in a new tab.
Each label carries the garment name, size, SKU, and the bars with the code. A valid EAN-13 prints as EAN-13; any other code prints as Code 128. A code too long to print at a scannable size is flagged above the sheet. With no size labelled, or nothing on hand, the sheet says which instead of printing.
## Accepted formats
- The camera reads EAN-13, EAN-8, UPC-A, UPC-E, Code 128, Code 39 and Code 93. QR codes are ignored.
- A scanner that types the code and presses Enter works in the scan boxes. See [keyboard and scanner](/docs/reference/keyboard-and-scanner).
- A bound code can be up to 64 characters.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Bind a code (scan, type or pick) | Barcode record: code, garment, size position | Clear the box, or press × |
| Move a code | The same record now points at the new size | Move it back |
| Generate | Barcode record marked generated; the facility's number counter moves on | Unbind; the number is not handed out again |
| Remove a size | Its barcode is deleted with it | Bind the code again |
| Print labels | Nothing | Nothing to undo |
@@ -0,0 +1,68 @@
---
title: Catalogue, sizes and cuts
section: stock
order: 1
summary: The On hand list, how a garment is entered once, why a size is a position in its list, what a cut is, and where garments live.
screen: Stock On hand
role: Admin
keywords: catalogue, garment, product, add garment, on hand, inventory, sizes, size run, cut, gender, men's, women's, unisex, staff groups, discontinue, duplicate, delete, price history, locations, shelf
---
## The On hand list
`Stock On hand` lists every garment with a strip of its sizes and what each holds. The segment narrows it to `All`, `At reorder`, `Out`, `On order` or `No barcode`; `Filter garments`, `Group` and `Supplier` narrow it further. Click a size to open `Adjust quantity`, or the name to open the garment's page. **Export CSV** downloads every size.
Admins can tick garments for the bulk bar: `Discontinue`, `Reinstate`, `Delete`, `Set supplier`, `Set group`, `Set reorder` and `Apply price`.
## Adding a garment
A garment is one item in one colour: a name, a cut, a SKU, a supplier, a unit cost, a product type, staff groups, notes and a list of sizes. Stock, barcodes, reorder levels, supplier codes and shelf places are held per size.
1. **Press Add garment** at the top of `Stock`.
2. **Enter the garment.** The supplier list comes from `Settings Catalogue & suppliers`.
3. **Add the sizes.** Type a size and press Enter, or pick from a run: `XS 5XL`, `6 24` or `72 117`.
4. **Give each size its barcode and opening stock.** Both are optional.
5. **Press Add to catalogue.** It is refused without a name, a size and a cost (0 is a cost), or when one barcode is on two sizes.
The product type decides whether a garment counts as a top or trousers, half a set under the [entitlement rule](/docs/people/entitlement-rule), and the dialog says which. Many garments come in at once from `Settings Data & audit log` with the Catalogue template (see [CSV templates](/docs/reference/csv-templates)).
## Cuts and staff groups
A garment's cut is `Unisex`, `Men's` or `Women's`, set in `Gender`. A men's or women's garment shows `(M)` or `(W)` after its name. A person's uniform style is set on their record in [People](/docs/people/staff-register). In an import, a gender starting with `m` is men's, one starting with `f` or `w` is women's, and anything else is unisex.
The staff groups tick-list says which groups a garment is for. **An empty list means every group.** Other groups can't request it, and need the override at the counter. In an import, groups are separated by `|`, and a blank group column on a re-import leaves a garment's groups as they were.
## A size is a position
ThreadCount records a size by its position in the size list, not by its name. Every issue, count, movement, barcode and reorder level points at a position.
- A new size goes on the end, from `Add a size` in the garment page's `Sizes` panel, or from **Scan sizes**. It starts with no barcode.
- Once anything is recorded against a garment, its sizes can't be reordered.
- **Remove** on a size row is refused if that size has an issue, stock on hand, an open supplier order, a stock movement, a stocktake, a hand-in, a ward request, somebody waiting for it, a kit check answer or a record query. The refusal names which.
- Removing a size deletes its reorder level and barcode and moves every later size down one. A garment's last size can't be removed.
## Locations
`Stock Locations` holds where garments live. A location is a `Room`, `Shelf`, `Bay`, `Laundry` or `External`, and can sit inside another. An Admin adds one with `New location`, `Kind` and `Inside`. **×** removes it: the locations inside move up a level and its sizes become unplaced. An Issuer sees the list read-only.
Anyone places a size with the `Location` select on the garment's page, and a count can then be scoped to that location (see [stocktakes](/docs/stock/stocktakes)).
## Discontinue, duplicate, delete
- **Discontinue**, from the garment page's menu or the bulk bar, takes a garment out of counts and reorder flags. Its stock still counts in the value on hand. **Reinstate** brings it back.
- **Duplicate** copies the type, gender, supplier, cost, notes, sizes and reorder levels, but no barcodes or stock. It is refused when a garment of that name already exists for the same groups.
- **Delete**, on the bulk bar, removes garments with nothing recorded against them. Anything with history or stock on hand is discontinued instead.
A change of unit cost, from **Edit garment**, `Apply price`, the order page or a delivery, adds an entry to `Price history`. Past issues keep their recorded price.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Add to catalogue | New garment; barcodes; opening stock; a price history entry when the cost is above 0 | Delete while nothing is recorded, otherwise Discontinue |
| Change unit cost | Garment cost; a price history entry | Change it back |
| Add a size | Size appended | Remove, while nothing is recorded |
| Remove a size | Size, reorder level and barcode deleted; later sizes move down | Add it again, at the end |
| Discontinue | Garment marked discontinued | Reinstate |
| Duplicate | New garment with sizes and reorder levels | Delete |
| Remove a location | Location deleted; its sizes unplaced | Add it and re-place the sizes |
+68
View File
@@ -0,0 +1,68 @@
---
title: The To order list
section: stock
order: 5
summary: What to order, one panel per supplier and net of what is already on order, raised and emailed in one press, then the orders on the way and the ledger of every order.
screen: Orders
role: Admin
keywords: order list, to order, purchase order, reorder, supplier order, raise, order and email, supplier code, order sheet, print, CSV, email supplier, supplier order number, on the way, ledger, all orders, order history
---
## The Orders screen
`Orders` has three columns. Admins see `To order` on the left; an Issuer sees `Recent orders` there instead, the latest 10. The middle column, `On the way`, lists placed orders still open, overdue first, each with **Receive** (see [receiving](/docs/stock/receiving-and-back-orders)). `This month` counts the orders placed and received this month with their value, and **Open the ledger** goes to every order.
**New order** and **Order for a person**, at the top, are open to everyone. Both make a draft.
## What appears in To order
`To order` has one panel per supplier, A to Z, for every supplier with a size at [reorder](/docs/stock/reorder-levels) or a draft with lines. The panel heading shows the supplier's lead time and order email, or `no email on file`, and the number of lines and their value at catalogue cost.
Each size at reorder is a line, with a suggested quantity of twice the level, less what is on hand, less what is on order, never below 0. On order counts every open order and draft, so a quantity already on a draft is not suggested again. The supplier is the garment's, or the first in `Settings Catalogue & suppliers` when it has none.
The columns are `Code`, `Garment`, `On hand`, `Reorder`, `On order`, `Per week`, `Order` and `Cost`. A size with no supplier code shows `no code`, linked to the garment's page. A size tagged `runs out before this arrives` is sorted to the top.
- The `Order` stepper changes a quantity; **×** takes a line off.
- **Add a line** adds any current garment and size, with a quantity.
- Edits stay on this screen until the panel is ordered.
## Drafts in the panel
Each of the supplier's drafts with lines is listed under the table, tagged `staff` when it is for a person or `draft` when it is for stock, with its own `Supplier order no.` box. A draft for a staff member is raised as its own order and never merged into the stock order, so each person's order stays separate from the shelf's.
## Ordering
The foot of the panel has `Supplier order no.` for the stock order: the number the supplier gives when the order is placed on their site. **Sheet** prints the panel as it stands.
**Order and email**, or **Order** when the supplier has no order email, raises the panel:
- The stock lines become a new order for stock, status `Ordered`, with the supplier order number as its reference and an expected date of today plus the supplier's lead time, or 14 days when none is set. Lines at 0 are dropped.
- Each draft becomes `Ordered`, taking its supplier order number if one was typed.
- If anything is refused, for example a draft somebody else has already placed, nothing is raised.
- With an order email on file, each order is then emailed, and the panel says `Sent to` the address or why it wasn't.
The panel then lists the orders raised, each with **Print**, **CSV** and **Email**, until **Done**.
## The sheet, the CSV and the email
**Print** opens the A4 purchase order (Admin): the order number and supplier reference; the facility, its location and organisation; the supplier with contact, phone and email; the date and expected date; the account number; the person it was ordered for, with department and cost centre; a row per line with the supplier code; and spaces for who ordered and the date placed. Where a size has no supplier code, the garment's SKU is used. Opening the sheet records when the order was printed. The same sheet is **Order sheet** on the order's own page.
**CSV** downloads the order number, supplier and supplier order number, then `Supplier code`, `Description`, `Size`, `Qty` and `Unit cost` per line.
**Email** sends the order to the supplier's order email. The subject reads like `Purchase order ORD-2026-0042 (NW-48211) — Riverside General`, with the lines, the account, the delivery location and an estimated value ex tax, asking the supplier to quote the order number on the invoice. It is refused for a draft, a cancelled order, a supplier with no order email, and a server with no email set up.
## The ledger
`Orders All orders` lists every order. Search by order number, reference, invoice, tracking, supplier, person or garment, and filter by `Supplier`, `Garment`, `From`, `To` and `All`, `Draft`, `Open` or `Received`. `Open` means placed and not yet received or cancelled. **Export CSV** downloads the orders shown.
A garment's page lists every order line for it under `Orders`, and every change of unit cost under `Price history`.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Order, stock lines | New order: stock, `Ordered`, reference, expected date, lines | Cancel order on the order page (Admin) |
| Order, a draft | Status `Draft` to `Ordered`; reference if typed | Cancel order (Admin) |
| Print sheet | The order's printed time | Nothing to undo |
| Email | Email to the supplier; the order's emailed time | None; the supplier has it |
| Sheet, CSV | Nothing | Nothing to undo |
@@ -0,0 +1,69 @@
---
title: Receiving and back orders
section: stock
order: 6
summary: Booking a delivery in against its order, recording the invoice, splitting short lines to a back order, and sending garments to the shelf or a pickup.
screen: Orders an order Receive delivery
role: Admin or Issuer
keywords: receive, receive delivery, delivery, goods received, invoice, back order, short delivery, partial delivery, pickup, shelf, invoiced cost, docket, on the way, overdue
---
## Before a delivery can be received
A delivery is received from three places: **Receive** on a row of `Orders On the way`, **Receive delivery** on the order's own page, and **Receive delivery** in the `Receive` group on `Today`, which lists orders due within 2 days or overdue.
**Receive delivery** appears on an order that is `Ordered`, `Shipped` or `Back Order`. A draft has to be marked ordered first, and a received or cancelled order can't be received again. **Mark shipped**, in the order page's menu (Admin), is optional and changes nothing about receiving.
The order page's `Units received` figure shows how many units have arrived against how many were ordered.
## Ticking the lines
The `Receive delivery` dialog lists each line of the order:
- `Outstanding`: ordered less already received.
- `Arrived`: filled in with the outstanding figure. Change it to what is in the box.
- `Destination`: `Shelf`, or `Pickup` when the order is for a staff member. A staff member's order starts on `Pickup`; an order for stock has only `Shelf`.
- `Invoiced cost`: the unit cost on the invoice, filled in with the catalogue cost.
**Scan items off the box (camera)** adds 1 to a line for each garment scanned, and names a garment that isn't on the order.
An arrived figure above the outstanding one is refused. Book surplus in with `Adjust quantity Receive` from `Stock On hand`, which records it as received without an order. At least one line needs a quantity.
## Invoice, date and price
1. **Enter the invoice number.** It is kept on the delivery and on the order.
2. **Check the arrival date.** It starts on today in the facility's time zone.
3. **Add a note** if something was wrong, and **Photo the invoice** if you want it on file.
4. **Check any price flag.** When an invoiced cost differs from the catalogue, the line shows both. **Keep** leaves the catalogue alone; **Update catalogue cost** (Admin) changes it and adds a price history entry.
5. **Press Receive.**
On the order page, a line whose delivered units were invoiced at a different price is marked `invoice price`.
## Short lines become a back order
Receiving closes the order as `Received`, whatever arrived. Anything short is moved onto a new order:
- Status `Back Order`, for the same supplier, person, cost centre and supplier reference.
- Its note reads `Back order — short on` and the original order number.
- Its expected date is today plus the supplier's lead time, or 14 days when none is set.
The two orders link to each other: the back order's page says `back order of` the original, and the original lists its back orders under `Lines`. A back order is received the same way, and anything short on it goes to another back order. Cancel one the supplier won't fill with **Cancel order** (Admin).
## Where the garments go
- **Shelf** lines are added to stock on hand for their sizes straight away.
- **Pickup** lines are not put on the shelf. They become one pickup for the staff member, which appears on `Today` under `Call to collect` or `Deliver on the round`. See the [pickup call list](/docs/counter/pickup-call-list).
The order's `History` panel adds a `Delivery received` entry with the invoice number, each line and where it went, and an **Invoice photo** button when one was taken. A received order's details are locked.
> **Careful** There is no undo for a delivery once received; a wrong quantity is corrected on the shelf with Adjust quantity.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Receive | A delivery record (date, invoice, note, photo) with a line per arrived size (quantity, destination, invoiced cost); the order set to `Received` with its invoice and received date, the note added to its notes | None; correct stock with Adjust quantity |
| Short lines | New `Back Order` order linked to the original | Cancel order (Admin) |
| Shelf lines | Stock on hand for those sizes goes up | Adjust quantity (Admin) |
| Pickup lines | A pickup for the staff member with those lines | None from this dialog |
| Update catalogue cost | Catalogue unit cost; a price history entry | Change the cost on the garment page |
+62
View File
@@ -0,0 +1,62 @@
---
title: Reorder levels
section: stock
order: 3
summary: A reorder level per size or the facility default, when a size is flagged, where the flags show, and how the forecast suggests a level.
screen: Stock a garment
role: Admin
keywords: reorder level, par, minimum, low stock, at reorder, out of stock, flagged, replenishment, forecast, suggested reorder, lead time, weeks of cover, runs out
---
## Where a level comes from
Every size has a reorder level. It is the size's own figure if one has been set, and the facility default otherwise. The default is `Default reorder level` under `Settings Catalogue & suppliers`, which starts at `3`.
An Admin sets a size's own level in any of these places:
- The **** and **+** stepper in the `Reorder at` column of the `Sizes` panel, on the garment's page.
- **Set reorder** on the bulk bar of `Stock On hand`, which sets one level on every size of the ticked garments.
- **Use** beside a forecast suggestion in the garment page's `Ordering` panel (below).
- The Reorder levels template in `Settings Data & audit log`, or the optional `reorder` column of Opening balances. See [CSV templates](/docs/reference/csv-templates).
- **Duplicate** copies a garment's levels to the copy.
A level can't go below 0. Once a size has its own level, no screen returns it to the default; set it to the default figure instead. An Issuer sees the levels but can't change them.
## When a size is flagged
A size is at reorder when what is on hand is at or below its level. A size is left out while nothing has ever happened to it: no opening stock, no adjustment, no level of its own and no movement. Discontinued garments are never flagged.
The same rule drives every place a flag shows:
- `Stock On hand`: the `At reorder` segment and its count, a low or out mark on the size in the strip, and a `N low` or `N out` tag on the garment.
- The garment page: `Reorder` or `Out` in each size's `Status`.
- The rail: the `Stock` badge counts garments with any size at reorder.
- `Today`: the `Runs out before a delivery` panel lists sizes at reorder that are out, or forecast to run out before a delivery, 6 at most.
- `Orders`: the To order list, where they become lines to order. See [the To order list](/docs/stock/order-list).
On the phone, the count's variance screen says how many lines will fall below par once the count commits: lines that were above their reorder level before the count and are at or below it after. Committing opens the Reorder screen only when there is at least one such line. Otherwise the screen reads "Nothing falls below par when this commits, so there is nothing to reorder." and committing returns to the home screen. Committing drafts no order itself.
## The forecast's suggested level
The garment page's `Ordering` panel shows, for each size, a usage figure and a suggested level. It is worked out from issues already recorded:
1. **Usage.** Garments of that size issued in the last 13 weeks, averaged per week. If none were issued in 13 weeks, the last 26 are used. Pre-loved issues are not counted.
2. **Lead time.** The supplier's lead time in days divided by 7, or 2 weeks when none is set.
3. **Suggested level.** Weekly usage × (lead time in weeks + 2), rounded up.
The figure reads like `Suggested 12 · 3.1 wk cover · ~4/wk`: the suggestion, how many weeks the shelf lasts at that rate, and the weekly rate. With no issues in 26 weeks it reads `no usage yet` and nothing is suggested.
A `runs out before delivery` tag appears when the weeks of cover are fewer than the lead time. On the To order list the same size reads `runs out before this arrives`, which is also shown for a size that is out and has any usage.
A suggestion writes nothing. **Use** appears when the suggestion differs from the current level, and sets it.
> **In plain terms** The suggestion covers the lead time plus 2 weeks at the recent rate of issue.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| / +, Use | That size's reorder level | Set it again |
| Set reorder (bulk) | The level on every size of each ticked garment | Set it again per size |
| Default reorder level | Facility setting, read by every size without its own | Change it back |
| A flag or a suggestion | Nothing; both are worked out on screen | Nothing to undo |
+68
View File
@@ -0,0 +1,68 @@
---
title: Stocktakes
section: stock
order: 4
summary: Counting the shelf or a location at the desk or on a phone, blind counts, variance in garments and dollars, the reasons a large gap needs, and what filing writes.
screen: Stock Count
role: Admin or Issuer
keywords: stocktake, stock take, count, audit, stock count, blind count, variance, shrinkage, count sheet, reason, missing, condemned, laundry, file count, apply adjustments, location, shelf, pre-loved
---
## Counting at the desk
`Stock Count` lists every size of every current garment with the figure ThreadCount holds (`System`) and a `Counted` box.
1. **Set the scope.** `Filter garments` by name, SKU, size or barcode, pick a `Group`, or pick a `Location` to count only the sizes placed there and inside it. `Show Uncounted` hides lines already counted.
2. **Count.** Each scan into `Scan to count +1` adds 1 to its line; **Camera** does the same. Or type the figure into `Counted`.
3. **Watch the progress.** The bar reads `N% counted · N of N`, and the line below gives the variances, their dollar value and how many need a reason.
4. **Zero uncounted** sets every uncounted line in the scope to 0, for a count where the rest of the shelf is empty.
The tally is kept in this browser for the signed-in person until it is filed or cleared, and `Saved tally · last entry` says when it was last touched. **Clear counts** starts again. A code that matches nothing opens the [unknown barcode](/docs/stock/barcodes) dialog. A code for a size outside the chosen location is not counted.
`Pool Shelf` counts stock on hand. `Pool Pre-loved` counts the pool of handed-in garments instead, carried at nil value. The screen can be reached from `Today`, where `Counts due` lists locations never counted or last counted 30 or more days ago.
## Blind counts and the count sheet
`Mode Blind` hides the `System` figures and the variance and dollar figures, so the counter can't see the expected number. A counted line shows a tick under `Counted?`. A line that needs a reason still asks for one.
**Print count sheet** prints the lines in scope, grouped by garment, with the size, the bound barcode, the system figure and an empty box. In blind mode the system figure is left off and the sheet is marked `BLIND COUNT`.
## Variance and reasons
A line's variance is counted less system. The dollar figure is each variance times the garment's unit cost, added up; in the pre-loved pool it is nil.
A gap as large as `Settings Issuing rules A count gap needs a reason at` or larger, over or short, must carry a reason: `At laundry`, `Condemned`, `Missing` or `Other`. The setting starts at `5` and is never treated as less than 1. Lines still owing a reason are kept at the top of the list whatever the filter hides, and the file button stays off until each has one. The server refuses the whole count if one arrives without.
## Filing the count
The button reads **File count** when every counted line matched and **Apply adjustments** when some didn't. Issuers can file a count as well as Admins.
- Only counted lines are sent. Uncounted lines are left as they are.
- Every counted line is filed, matching or not, so each size's `Last counted` date on the garment page is right.
- Each variance moves stock on hand by the difference. In the pre-loved pool, the pool is set to the counted figure.
- The system figure is read again on the server when the count is filed.
- A count scoped to a location files with that location, and is refused if a line is not placed under it.
`Stocktake history`, below, lists filed counts newest first: date, who counted, pool, location, lines, variances, and the net change in garments and dollars. **Variances** shows each gap with its reason; **CSV** downloads that count's variance lines with unit cost and variance value.
## Counting on a phone
In the [counter app](/docs/apps/counter-app), the count lists every location with garments placed on it, plus `Not on a shelf yet`. Sizes are placed from the garment's page, and locations are made in `Stock Locations`.
1. **Pick the location.** Each line shows counted against expected.
2. **Scan.** Each scan adds 1. A code placed on another shelf is refused with that shelf's name. **Undo** takes 1 off the current line; **Type a count instead** is for a label that won't scan.
3. **Finish count.** The variance screen lists lines that don't match, with **Recount** and the reasons for large gaps.
4. **Commit count.**
The counter app also charts each size's gap across the last 6 shelf counts.
> **Careful** A phone commit files every line on that location, and a line nobody scanned is filed as 0.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Counting (desk or phone) | Nothing on the server; the tally is kept on the device | Clear counts |
| File count / Apply adjustments / Commit count | A stocktake: date, who, lines counted, variances, shelf or pool, location if scoped; a line per counted size with system, counted and reason | No undo; a filed count stays in history |
| A variance on the shelf | Stock on hand moved by the difference | Count again, or Adjust quantity (Admin) |
| A variance in the pool | The pre-loved pool set to the counted figure | Count again, or Adjust quantity Pre-loved |
+64
View File
@@ -0,0 +1,64 @@
---
title: Suppliers
section: stock
order: 7
summary: The supplier directory, the contact, account, order email and lead time each supplier carries, the supplier's code for each size, and where each is used.
screen: Settings Catalogue & suppliers
role: Admin
keywords: supplier, vendor, supplier directory, lead time, account number, order email, contact, supplier code, product code, remove supplier, set supplier
---
## The supplier directory
`Settings Catalogue & suppliers` lists every supplier the facility buys from under `Suppliers`, each in its own panel with the number of products and orders that use it.
1. **Type the name** into `New supplier`, for example `Northline Workwear`.
2. **Press Add supplier**, or Enter. A name already on the list, in any mix of capitals, is refused.
A supplier is also added when a garment is saved or bulk-changed with a supplier name the directory doesn't hold. The match ignores capitals, and the garment is stored with the directory's spelling, so `northline workwear` finds `Northline Workwear`.
A supplier's name can't be changed once added. **Export CSV** downloads the supplier, contact, phone, account number, order email, lead time, and the number of products and orders for each.
## Each supplier's details
| Field | What it holds |
|---|---|
| `Contact` | Who to ask for |
| `Phone` | The supplier's phone number |
| `Account no.` | The facility's account with the supplier |
| `Order email` | Where purchase orders are emailed; blank means print or CSV only |
| `Lead time (days)` | Days from ordering to delivery; blank means none set |
Changes save as you type. An order email that isn't an email address is refused, and it is stored in lower case. An Issuer sees the fields but can't change them.
## Where the details are used
- **Lead time** sets the expected delivery date: in `New order` when the supplier is picked, on orders raised from [To order](/docs/stock/order-list), and on back orders. With none set, those expect delivery in 14 days. The forecast reads it in weeks, and assumes 2 weeks when none is set (see [reorder levels](/docs/stock/reorder-levels)).
- **Lead time and order email** head each supplier's panel in `Orders To order`, which shows `no email on file` when there is none.
- **Contact, phone, order email and account number** print on the A4 purchase order, and the account number goes into the email.
- **Order email** is the only address **Order and email**, **Email** and **Email supplier** send to. Without it, To order offers **Order** alone, and emailing an order is refused with a note to add an address.
## The supplier's code for each size
Suppliers number each size and colour of a garment separately. Record that code on the garment's page, in the `Ordering` panel's `Supplier code` column, against each size (up to 60 characters). It saves when you leave the box. An Issuer sees the codes but can't change them.
The code is printed against the line on the A4 purchase order, in the order CSV and in the order email, so it can be keyed into the supplier's own site. Where a size has none, the sheet, the email and the CSV from To order use the garment's SKU; the order page's CSV has a separate `SKU` column. On To order a missing code shows as `no code`, linked back to the garment's page.
The supplier code is separate from the barcode: it is what the supplier calls the size, and the [barcode](/docs/stock/barcodes) is what a scanner reads.
## Changing and removing a supplier
A garment's supplier is changed on its page with **Edit garment**, or for several at once with `Set supplier` on the bulk bar of `Stock On hand`. Changing a garment's supplier doesn't change existing orders. An order's supplier can be changed in the order page's `Order details` until it is received or cancelled.
**×** on a supplier's panel removes it, and appears only when no product and no order uses it. The server refuses the removal otherwise.
> **In plain terms** A supplier that has ever been used stays in the directory, so its orders keep their contact and account details.
## What is written
| Action | Record · Change | Undo |
|---|---|---|
| Add supplier | Supplier record with its name | Remove it while nothing uses it |
| Edit a detail | That field on the supplier | Edit it again |
| Remove | Supplier record deleted | Add it again and re-enter the details |
| Supplier code | The code on that size of the garment | Edit or clear it |
+127
View File
@@ -0,0 +1,127 @@
# Self-hosting ThreadCount (Community edition)
The Community edition is the hosted product with `EDITION=community` in its environment. This
page says what that changes, how to run it, how to keep it, and what is not included.
## What `EDITION=community` changes
The Community edition is the product a single room uses, and nothing that belongs to the hosted
service. Its source is an export of the hosted code with the hosted-only parts left out.
| | Hosted (threadcount.tech) | Community |
|---|---|---|
| Coordinator app (`/app`), phone counter (`/m`), staff app (`/my`) | Yes | Yes, in full |
| Plans, staff ceiling, read-only for non-payment | Yes | None. Every facility behaves as a grandfathered one. |
| Website, pricing, guides, legal pages | Yes | Not included. `/` goes to sign-in. |
| Demo facility | Yes | Not included |
| Health-service layer (organisations, shared catalogue) | Yes | Not included |
| Single sign-on (SAML/OIDC) | Yes | Not included. Password plus authenticator code. |
| Card payments | Where configured | Not included |
| Cloudflare Turnstile on sign-in and sign-up | Required | Optional. Set both keys to enforce it; without it the per-address rate limits stand alone. |
| Error reports | To ThreadCount's own GlitchTip, scrubbed | Nowhere, unless you set `NEXT_PUBLIC_GLITCHTIP_DSN` to your own |
| Usage statistics | To ThreadCount's own Umami, scrubbed | Nowhere. The tracker only mounts on `*.threadcount.tech`. |
| Transactional email | ThreadCount's SMTP | Your SMTP, or none |
Your instance is yours, and so is the privacy statement your staff read. The product's screens
link to the terms and the privacy notice from a few places (the staff sign-in, account screens);
set `NEXT_PUBLIC_TERMS_URL` and `NEXT_PUBLIC_PRIVACY_URL` to your own documents before you build,
or they point at threadcount.tech's.
## Running it
`docker-compose.yml` runs three services: `db` (Postgres 16, in a named volume), `migrate` (a
one-shot `prisma migrate deploy` that runs before the app on every `up`), and `app` (Next.js's
standalone server, port 3000, photographs in a second named volume). Put TLS in front of port
3000. A minimal Caddyfile:
```
uniforms.example.health {
reverse_proxy 127.0.0.1:3000
}
```
The image is built on your machine, not pulled, because `NEXT_PUBLIC_SITE_URL` and the optional
Turnstile site key are compiled into the browser bundle. If you change either, rebuild:
`docker compose up -d --build`.
### The first facility
Open the hostname. Create an account; that creates a facility and makes you its administrator.
Under Settings, name your staff groups first (nothing works until a facility has them), then load
the catalogue, departments, staff register, barcodes and opening stock from CSV under Settings
Data. Add a second administrator under Settings Account Users before you sign out — a
forgotten password with one admin and no SMTP locks the facility.
Then set `SIGNUPS_DISABLED=1` in `.env` and `docker compose up -d`. Your instance is now one
facility, and nobody else can create another.
## Upgrading
Each release replaces the repository's history rather than adding to it, so a plain `git pull`
refuses to merge. Fetch and move to the release instead. Your `.env` is not tracked and stays put.
```sh
git fetch origin
git reset --hard origin/main
docker compose up -d --build
```
`migrate` applies any new schema migrations before the new app starts; the old app keeps
serving until then. Take a backup first (below). Releases that need more than that say so in
their notes.
## Backups
`docker/backup.sh <directory>` writes one directory per run: the database as a custom-format
`pg_dump` and the photographs as a tarball, keeping the last fourteen. Run it nightly from cron:
```
30 3 * * * cd /srv/threadcount && docker/backup.sh /srv/backups/threadcount >> /var/log/threadcount-backup.log 2>&1
```
Copy that directory somewhere off the machine. A backup on the same disk as the database is not a
backup.
Separately, an administrator can download the whole facility as one JSON file from Settings
Data at any time, and restore it into a fresh instance from the same screen. That file is the
portable form; the `pg_dump` is the fast one.
### Restoring
```sh
docker compose down
docker volume rm threadcount_db threadcount_photos # only if you mean to replace everything
docker compose up -d db
docker compose exec -T db pg_restore -U threadcount -d threadcount --clean --if-exists < 2026-09-13-033001/threadcount.dump
docker compose up -d
docker compose cp 2026-09-13-033001/photos app:/data/ # after extracting photos.tgz
```
## The Android apps
The two Play apps (ThreadCount, the counter; ThreadCount Staff) can point at your server. On the
app's first screen tap "Server: threadcount.tech · Change", choose Self-hosted, and enter your
hostname. The app checks it over HTTPS (it calls `/api/app-info`, which every ThreadCount server
answers), saves it on the phone, and opens your counter or staff screens from then on. The choice is
per phone; "Change" is always on the first screen, and the sign-in screens show which server they
are talking to.
Two things stay tied to threadcount.tech's domain and do not carry over: emailed approval links
opening straight in the staff app (they open in the browser instead, and work there), and saved
passwords shared between the website and the app. The phone must be able to reach your server over
HTTPS with a certificate it trusts; a self-signed certificate is refused.
Without the apps, the same screens work in a phone's browser: `https://your-host/m` for the
counter (camera scanning works in Chrome and Edge) and `https://your-host/my` for staff.
## Environment reference
`.env.example` lists every variable the application reads, with a comment on each. The ones that
matter for a Community instance are `SESSION_SECRET`, `POSTGRES_PASSWORD`, `NEXT_PUBLIC_SITE_URL`,
`EDITION`, `NEXT_PUBLIC_TERMS_URL`, `NEXT_PUBLIC_PRIVACY_URL`, the `SMTP_*` group, and
`SIGNUPS_DISABLED`. Leave the Turnstile and GlitchTip variables empty unless you run those yourself.
## Getting help
Open an issue on the repository. If you would rather someone else ran it, that is what
[threadcount.tech/pricing](https://threadcount.tech/pricing) is for.