# Holdfast Invoice (v1)

Phone-first static web app for Canadian construction trades. **Fieldledger** product. Owner is a silent partner in New Brunswick.

This is not tax or legal advice. Holdfast is not affiliated with the Canada Revenue Agency. No CRA branding. Confirm holdback, GST/HST, PST and QST with your contract, your accountant, and the statute.

CAD. Canadian English. No fake testimonials. No fake user counts.

## Open locally

No build step. No backend.

```bash
cd fieldledger/holdfast
python3 -m http.server 8080
```

Then open <http://127.0.0.1:8080/> on your phone or laptop.

The public site is <https://holdfastinvoice.ca/>.

- Landing + holdback calculator: `index.html`
- Invoice app: `app.html`
- 2026 rates cheat sheet: `rates.html`
- Sources: `SOURCES.md`

You can also open `index.html` as a file, but some browsers restrict IndexedDB / service workers on `file://`. Use the tiny local server if invoices will not save.

After the first load, `sw.js` caches the app so it works in the truck.

## What v1 does

- Progress invoice: legal name, address, optional GST/HST number, sequential `INV-0001`, job/site address **and** billing address, labour vs materials, contract value, draw #, previously billed, this-draw amount.
- Province pack: holdback % + release window from the verified table. You pick the province.
- **Tax on the GROSS before holdback.** Shown: this-draw gross, tax, holdback withheld, amount due now, holdback due on DATE (invoice date + window).
- Holdback **release** invoice on the same job later.
- Interac e-Transfer block on the PDF (email + optional auto-deposit name). No card processing.
- PDF via vendored `html2pdf.js` (`vendor/html2pdf.bundle.min.js`) so it works offline.
- Jobs and invoices in **IndexedDB** in this browser.

## What v1 does not

Taking payment from your customers, quotes, change orders, timesheets, T5018, T2125, bank sync, AI writing, US retainage, QuickBooks, claiming CRA-approved. Polar checkout URL is live for **$29 CAD/month**; the paid flag is still local-only (no backend).

## Free vs paid

**Free:** 2 invoices. PDF footer says **Holdfast**.

**Paid unlock (local only):** unlimited invoices, no footer, logo upload.

- Query param: `app.html?paid=1`
- Or localStorage key: `holdfast_paid` = `1`

Polar checkout is live for **$29 CAD/month**. The paid flag is still local-only (no backend). Polar does not flip the unlock; use `?paid=1` or localStorage `holdfast_paid=1` on this phone after you pay.

Lock again from the Unlock screen, or delete the localStorage key.

## Math (product rule)

```
this-draw gross   = labour + materials this draw
tax               = GST/HST (and QST / PST if on) on the GROSS
holdback withheld = holdback% × GROSS
amount due now    = gross + tax − holdback
holdback due later = the withheld (pre-tax) dollars
holdback due date  = invoice date + provincial window (days)
```

The calculator on the homepage uses **the same function** as the invoice (`js/math.js`). If they ever disagree, that is a bug.

CRA remittance on the holdback *slice* of tax may wait until the holdback is paid or the period expires (Excise Tax Act s. 168(7)). Holdfast still **prints** tax on the full gross. See `SOURCES.md`. Confirm with your accountant.

The printed holdback date is a **reminder from the invoice date**. Real statutory clocks usually start from substantial performance, completion, abandonment, termination, or a certificate.

## Worked examples (QA)

Self-test: in a browser console on any Holdfast page, `HF_MATH.selfTest()`. Or:

```bash
node -e 'global.window=global; require("./js/data.js"); require("./js/math.js"); console.log(HF_MATH.selfTest())'
```

Must print `{ ok: true, ... }`.

### 1. $40,000 New Brunswick job — draw 1 (full this-draw)

Province pack: **10% holdback, 60 days, 15% HST** (*Construction Remedies Act* ss. 34, 47). Invoice date **26 August 2026**.

| | |
|---|---|
| This-draw gross | $40,000.00 |
| HST 15% on the gross | $6,000.00 |
| Holdback withheld 10% | $4,000.00 |
| **Amount due now** | **$42,000.00** |
| Holdback due later | $4,000.00 |
| Reminder date | 25 October 2026 (60 days) |

Calculator sentence: *Holdback on $40,000 in New Brunswick = $4,000, HST is $6,000, you invoice $42,000 now, $4,000 in 60 days.*

Crown bonded NB (s. 34(4)): holdback 5% = $2,000; due now = $40,000 + $6,000 − $2,000 = **$44,000.00**.

### 2. Ontario draw 1 — $18,000 labour + $7,000 materials

Province pack: **10% holdback, 60 days, 13% HST**.

| | |
|---|---|
| This-draw gross | $25,000.00 |
| HST 13% | $3,250.00 |
| Holdback 10% | $2,500.00 |
| **Amount due now** | **$25,750.00** |
| Holdback due later | $2,500.00 |

### 3. Nova Scotia $10,000 this-draw

HST **14%** (CRA, since 2025-04-01). Holdback 10%, 60 days.

| | |
|---|---|
| Gross | $10,000.00 |
| HST 14% | $1,400.00 |
| Holdback | $1,000.00 |
| **Amount due now** | **$10,400.00** |

## Verified vs unverified provinces (2026-08-26)

| | Holdback | Window | Status |
|---|---|---|---|
| New Brunswick | 10% (5% Crown bonded) | 60 d | **VERIFIED** |
| Nova Scotia | 10% | 60 d | **VERIFIED** |
| PEI | 15% / 20% if ≤ $15,000 | 60 d | **VERIFIED** |
| Newfoundland and Labrador | 10% | 30 d | **VERIFIED** |
| Ontario | 10% | 60 d (+ 2026 annual release) | **VERIFIED** |
| British Columbia | 10% | 55 d (lien file 45 d) | **VERIFIED** |
| Alberta | 10% | 60 d (90 d oil/gas & concrete) | **VERIFIED** |
| Saskatchewan | 10% | 40 d | **VERIFIED** (%); $50k threshold UNVERIFIED |
| Manitoba | 7.5% | 60 d default | **% VERIFIED**; window **caution** (was 40 d in 2015 SCC; 2024 lien period 60 d) |
| Quebec | none statutory | 30 d hypothec reminder | **VERIFIED** (no %) |
| Yukon / NWT / Nunavut | type it | type it | **UNVERIFIED** |

GST/HST: CRA calculator, page modified 2025-04-01. NB/NL/PE 15% HST, NS 14%, ON 13%, GST 5% elsewhere; QC GST+QST; BC/SK/MB GST+PST.

Unverified territories will not invent 10%. The app leaves % and days blank until you type them.

## Files

```
holdfast/
  index.html          landing + calculator
  app.html            invoice app
  rates.html          2026 cheat sheet
  css/app.css
  js/data.js          province packs
  js/math.js          shared math (calculator = invoice)
  js/db.js            IndexedDB
  js/app.js
  js/landing.js
  vendor/html2pdf.bundle.min.js
  sw.js
  manifest.webmanifest
  SOURCES.md
  README.md
```

## Privacy

Everything stays in this browser. Fieldledger does not receive your invoices. Clearing site data deletes jobs.

Dated 26 August 2026.
