# Company document branding and invoicing

Company OS has one resolved document profile per company. It is the source of
truth for branded letters, invoices, reimbursement lines, and invoice-related
agent work.

## Resolution

Every company starts with an adapted copy of the platform default:

`config/company-os-document-brand-profile.v1.json`

The baseline is the Gestion Velora system used for the SDC Enticy invoices:
bilingual French/English labels, CAD, Letter portrait layout, the Gestion
Velora wordmark and contact block, GST 5%, QST 9.975%, Net 30, 2% monthly late
fees, $60/hour after-hours work with a three-hour minimum, and explicit
reimbursement lines. Company OS keeps those structural rules while replacing
the identity with the company's known name and supplied branding. If company
identity is unavailable, the unadapted platform profile remains the fallback.

## Passport setup choice

When a new company is created, Passport asks the operator to choose one
starting point:

1. Use the adapted Gestion Velora baseline (recommended). Company OS keeps the
   invoice structure and replaces the identity with the company's supplied
   display name, optional legal name, invoice name, billing contact, and
   website.
2. Share an existing invoice or template. The file is uploaded through the
   existing Passport evidence endpoint and remains non-authoritative reference
   evidence while it is reviewed.
3. Describe a preferred format. The notes are stored as a pending request for
   an authorized profile change.

The first option is ready immediately. The second and third options do not
silently change the active profile; an authorized person must approve a
complete governed profile override before the requested format becomes active.

If a company supplies another approved invoice format, store a complete adapted
profile as a governed company override. The override is stored under
`company_profiles.metadata.document_brand_profile`; unrelated company metadata
is preserved. The active profile is returned by:

`GET /api/company/companies/{company_id}/document-profile`

The same resolved profile is injected into
`GET /api/company/tasks/{task_id}/llm-context` as `document_profile` and as the
`document_profile` context section. Agents therefore see the active format
without relying on a chat instruction or a local file guess.

## CLI

Read the active profile:

```bash
company-os company profile show
company-os company invoice-format show
```

Create a company override from a JSON file. The file must use the versioned
`company_os.document_brand_profile.v1` schema and contain `brand` and
`invoice_format` objects:

```bash
company-os company profile set --file PROFILE.json --dry-run
company-os company profile set --file PROFILE.json --confirm
```

For an invoice-only adaptation, the CLI preserves the current brand and accepts
either a full profile, an object containing `invoice_format`, or a standalone
`company_os.invoice_format.v1` object:

```bash
company-os company invoice-format set --file INVOICE_FORMAT.json --dry-run
company-os company invoice-format set --file INVOICE_FORMAT.json --confirm
```

`--dry-run` performs the read and validation path, then returns the planned
governed `PUT` without writing. Mutations still require the existing Company OS
principal, policy scope, confirmation, governance receipt, and independent
readback controls.

The CLI accepts the endpoint response itself as an input file, so an operator
can run `company profile show > PROFILE.json`, edit it, and submit it again.
Do not put credentials, tokens, passwords, or other secrets in a document
profile; the server rejects those field names.

## Rendering branded PDFs

Invoices and board minutes render to PDF from the resolved profile with
[pdfcn](https://github.com/shadcn-labs/pdfcn) components on the Forme (WASM)
engine, in `web/company-console`. The source record's `schema` selects the
document:

```bash
company-os company profile show > PROFILE.json
npm --prefix web/company-console run render:company-document -- \
  --profile PROFILE.json --source RECORD.json --out document.pdf [--logo logo.png]
```

Every PDF prints short SHA-256 digests of the profile and the source record
(canonical JSON), and the command prints the full digests, as the pack's
document-governance policy requires. When something is missing, the document
shows a visible review box instead of a guessed value.

### Invoices — `company_os.invoice_document.v1`

Sample: `web/company-console/scripts/fixtures/company-invoice.sample.json`.
Lines are `service`, `after_hours` (billed at the profile rate with its
minimum hours), or `reimbursement`. GST and QST are computed on the same
pre-tax base and the due date comes from the `NET n` label. Review items: a
reimbursement without a receipt, tax registration numbers absent from the
profile, a line marked tax-exempt. Drafts carry a `BROUILLON · DRAFT`
watermark.

### Board minutes (procès-verbal) — `company_os.board_minutes_document.v1`

Sample: `web/company-console/scripts/fixtures/company-board-minutes.sample.json`.
Follows the [board-minutes handbook](../../packs/gestion-velora-property-management/BOARD_MINUTES_HANDBOOK.md):

- resolutions keep their status (`adopted`, `proposed`, `rejected`,
  `deferred`) with mover, seconder, vote, and `source_ref`;
- quorum is counted from attendees (`present` and `proxy`); a stated count
  that disagrees, an unstated requirement, or a missed quorum is flagged;
- the action ledger prints a missing owner or due date as
  `Inconnu · Unknown` and flags it;
- `unresolved` items (legal interpretation, missing evidence, …) get their own
  section; decisions or commitments without a listed source are flagged;
- `status: "adopted"` requires `adoption.date` and `adoption.reference`.
  Drafts state that they are not adopted and that distribution needs explicit
  approval; adoption never implies distribution approval;
- `confidential: true` marks the masthead and every footer.

### Fonts

The profile's `brand.typography` chooses the families: `family` (both roles),
or `body_family` and `display_family`, plus an optional `title_weight`
(a `title_style` containing "light" means 300). A profile without typography
uses the Gestion Velora baseline, Calibri. Invoices alone can use other
families through `invoice_format.typography` (same keys); the procès-verbal
and other documents always use `brand.typography`. Gestion Velora Inc. sets
Liberation Mono / Liberation Sans there, the typefaces of its invoices. Set it
in the company's own profile, not in the platform baseline: the server copies
`invoice_format` into every company it adapts. Try other families without editing
the profile with `--body-font` and `--display-font`.

Families resolve through the font library, in order: bundled
(`public/fonts/pdf/`), Vercel (Geist, Geist Mono), fonts installed on the
computer (Microsoft Office, LibreOffice, user and system fonts — read in place,
never copied into the repository), then 67 curated Google Fonts downloaded
once into `~/.cache/company-os/fonts`. A family available nowhere falls back
to its metric-compatible open substitute (Calibri → Carlito, Cambria →
Caladea, Arial/Helvetica → Arimo, Times → Tinos, Courier/Liberation Mono →
Cousine, Georgia → Gelasio), then to Inter. The render summary reports the
family actually embedded and any substitution, so a document rendered on a
machine without Office says so.

```bash
npm --prefix web/company-console run fonts -- list [--category sans|serif|mono|display] [--origin system]
npm --prefix web/company-console run fonts -- install --all     # cache Google Fonts for offline use
npm --prefix web/company-console run fonts -- specimen --out fonts.pdf
```

Only fonts that draw Latin text with French accents are listed, and only
TrueType-flavoured files: Forme silently replaces woff2 and CFF `.otf` fonts.

### Code layout

Blocks live in `components/pdf/blocks/company-invoice/`,
`company-board-minutes/`, and `company-shared/`; models in
`lib/company-documents/`; the profile-to-theme mapping in
`lib/pdf-themes/company-profile.ts`. Brand fonts (Inter, Playfair Display, OFL)
are in `public/fonts/pdf/`. The other files under `components/pdf/`, `lib/`,
and `types/` are pdfcn registry copies — update them with
`npx shadcn add @pdfcn/forme/<item> --overwrite`, not by hand.
Tests: `npm --prefix web/company-console run test:company-documents`.
