# Quillbook — Product Requirements (v1.0)

Quillbook is an invoicing tool for small studios. Staff keep a customer directory in USD, EUR or GBP, draft and send invoices with line discounts, tax and shipping, record payments and credit notes, void invoices, and see what is owed on an Overview and an Aging report.

**Problem.** A studio owner needs to know, to the cent, who owes what and how late it is. Totals, statuses and receivable figures must agree on every screen, and money actions must be deliberate and reversible only where the product says so.

## Users
- **Owner** — See receivables at a glance, issue and send invoices, and be the only role able to void an invoice.
- **Bookkeeper** — Draft invoices, record payments and credit notes and chase overdue balances, without the ability to void.

## Goals
- Every invoice total follows one fixed formula, so the same invoice shows the same figures everywhere.
- An invoice status always reflects the money still owed.
- Receivable figures on the Overview, customer pages and Aging report reconcile to each other.
- Destructive actions need confirmation and respect role limits.

## Sign in and roles (AUTH)

Two demo roles, Owner and Bookkeeper, sign in with a username and password.

- **FR-AUTH-01** (Must) Valid credentials open the Overview and show who is signed in and with which role.
  - The sign-in screen is headed "Sign in to Quillbook" with Username and Password fields and a line listing the two demo accounts with their roles
  - After sign-in the heading "Overview" is shown and the sidebar shows the full name with the role, for example "Maren Holt" and "Owner"
  - The sidebar footer reads "Today is 19 Oct 2026"; all day counts are measured from that business date
- **FR-AUTH-02** (Should) Bad or missing credentials keep the user on the sign-in screen with a clear message.
  - A wrong password shows the red message "Incorrect username or password" and the heading stays "Sign in to Quillbook"
  - Signing in again with the right password opens the Overview and clears the message
  - Submitting both fields empty shows "Enter your username and password" without leaving the screen

## Overview (OVR)

A single screen of headline receivables figures and invoice counts by status.

- **FR-OVR-01** (Must) The Overview totals every unpaid balance in USD and splits out overdue, collected this month and drafts.
  - Outstanding receivables is $33,608.75 "across 17 open invoices"
  - Overdue $22,438.15; Collected in October +$4,500.00; Drafts (3) $3,650.80
  - A caption reads "67% of the outstanding balance is overdue."
  - Void invoices and drafts are never part of Outstanding receivables
- **FR-OVR-02** (Should) Invoices by status lists the six statuses with counts and a distinct chip colour each; a row opens the Invoices list filtered to that status.
  - Seeded counts: Draft 3, Sent 4, Partially paid 1, Paid 19, Overdue 12, Void 2
  - Draft and Void are grey, Sent blue, Partially paid amber, Paid green, Overdue red, each chip with a small dot before its label
  - Clicking the Overdue row opens Invoices filtered to Overdue with the subtitle "12 invoices shown"
- **FR-OVR-03** (Could) A thin bar under the receivables figures fills to the overdue share of the outstanding balance.
  - With the seeded books the red fill covers about 67% of the track and the rest is a pale track

## Invoice list (LST)

All invoices, newest first, with status chips, search and paging.

- **FR-LST-01** (Should) Status chips narrow the list and always show the counts for the full book.
  - Chips read All 41, Draft 3, Sent 4, Partially paid 1, Paid 19, Overdue 12, Void 2
  - The Overdue chip shows "12 invoices shown" with QB-1032 first and only red Overdue chips
  - The Void chip shows QB-1027 and QB-1026; the chip counts do not change when a filter is applied
- **FR-LST-02** (Should) The search box matches invoice numbers and customer names, case-insensitively, and an empty result shows a helpful empty state.
  - "halden" shows "3 invoices shown": QB-1036, QB-1021 and QB-1015
  - "QB-1029" leaves one row: Orchard Row Press, £3,200.00 total, £3,000.00 balance
  - A search with no match shows "No invoices match", the hint "Try another status or clear the search." and the subtitle "0 invoices shown"
- **FR-LST-03** (Could) The list shows 15 rows per page and pages forward and back.
  - Page 1 reads "Showing 1–15 of 41", starts at QB-1041 and has Previous disabled
  - Next shows "Showing 16–30 of 41" starting at QB-1026, then "Showing 31–41 of 41" ending at QB-1001 with Next disabled
- **FR-LST-04** (Should) Overdue, Void and Paid invoices are visibly distinct, with days late shown for overdue ones.
  - An invoice due 19 Sep 2026 shows "30 days late" under its due date, a red Overdue chip, and "30 days past due" on its detail page
  - A void invoice has its number and total struck through, an em dash for Balance and a grey Void chip; its detail shows a VOID stamp and Balance due $0.00 with no Record payment button
  - A fully paid invoice shows a green PAID stamp, "Paid" equal to the total and Balance due $0.00, with its payment listed with date, method and reference

## Invoice editor (INV)

Create, edit, send, duplicate and delete invoices with live totals.

- **FR-INV-01** (Must) A new invoice with several lines, a discount, tax and shipping gets the right totals and can be saved and sent.
  - The editor says "Amounts are in USD. Shipping is not taxed."; choosing a customer shows "<Customer> pays in USD on Net 15 terms." and sets the due date from the issue date plus the terms (3 Nov 2026 for Net 15)
  - A line of 2 at 150.00 with Tax 10% shows $300.00; a line of 10 at 4.50 with Tax 0% and 10% line discount shows $40.50
  - With invoice discount 10% and Shipping 12.00 the summary reads Subtotal $340.50; Discount -$34.05; Tax 10% $27.00; Shipping $12.00; Total $345.45
  - Save and send opens the next number (QB-1042) with the toast "QB-1042 saved and sent", a blue Sent chip and "Due 3 Nov 2026"
- **FR-INV-02** (Must) A draft can be edited with live totals and then sent, which locks it.
  - A draft shows Edit, Send invoice, Duplicate, Print and Delete draft and no Record payment
  - Changing Shipping from 25.00 to 40.00 on a draft total of $1,410.80 updates the Total to $1,425.80 before saving
  - After Save and send the toast is "QB-1040 saved and sent", the chip is blue Sent, and Edit and Delete draft are gone while Record payment, Issue credit note and Void invoice appear
- **FR-INV-03** (Should) An invoice cannot be saved without a customer and nothing the user typed is lost.
  - Save draft with no customer shows the red message "Choose a customer" above the Details card; the typed line description is kept
- **FR-INV-04** (Should) The due date may not be earlier than the issue date; equal dates are accepted.
  - A due date of 18 Oct 2026 with issue date 19 Oct 2026 shows "Due date cannot be before the issue date" and saves nothing
  - A due date equal to the issue date saves with the toast "QB-1042 saved as draft"
  - For a EUR customer the editor shows euro symbols and the hint names EUR and the customer terms
- **FR-INV-05** (Should) Duplicate copies lines and discounts into a new draft dated today.
  - The toast is "Created draft QB-1042"; the header shows a grey Draft chip, the customer, "issued 19 Oct 2026" and a DRAFT stamp
  - Totals equal the source invoice and a draft has no "Balance due" row
  - The due date is today plus the customer terms (18 Nov 2026 for Net 30)
  - The list then reads "42 invoices shown" with the new draft first and chip counts All 42, Draft 4
- **FR-INV-06** (Should) A draft is deleted only after a confirm step and the counts update.
  - The dialog is "Delete draft QB-1039?" with "The draft is removed permanently. This cannot be undone."
  - Escape closes the dialog and keeps the draft
  - Confirming shows the toast "Draft deleted", returns to the list and the counts read All 40 and Draft 2
- **FR-INV-07** (Must) Invoice totals follow one formula: line amounts after line discount, then the invoice discount, then tax on the discounted amount rounded per tax-rate group, then shipping (untaxed).
  - Total = Sub-total - invoice discount + tax + shipping, with tax computed on the discounted amount of each rate group
  - A 10% invoice discount on lines totalling $1,399.80 gives Discount -$139.98, Tax 10% $125.98, Shipping $25.00 and Total $1,410.80
  - A sent invoice with a 5% line discount shows Sub-total $3,852.25; Discount (5%) -$192.61; Tax 10% $137.96; Total $3,797.60; Balance due $3,797.60
  - A line with a 5% line discount shows "Line discount 5% (-$42.75)"

## Payments and status (PAY)

Recording payments and keeping the invoice status in step with the balance.

- **FR-PAY-01** (Must) An invoice is Paid only when its balance is zero; any invoice with money still owed keeps a non-Paid status.
  - An invoice of $1,800.00 with $600.00 paid is amber "Partially paid" with Balance due $1,200.00
  - Seeded chip counts stay Partially paid 1, Paid 19, Overdue 12
  - A part-payment never turns an invoice green Paid
- **FR-PAY-02** (Must) Recording a part-payment lowers the balance and moves a Sent invoice to Partially paid.
  - The dialog "Record payment" says "Balance due $2,000.00 on QB-1035"
  - Paying 500.00 by Bank transfer closes the dialog with "Payment of $500.00 recorded"
  - The chip becomes amber "Partially paid", Balance due $1,500.00 and the Payments card lists +$500.00 dated 19 Oct 2026
  - The Partially paid chip then lists QB-1035 and QB-1033
- **FR-PAY-03** (Should) A payment above the balance is refused; paying exactly the balance settles the invoice.
  - 285.01 against a $285.00 balance shows "Payment of $285.01 exceeds the balance due of $285.00" inside the dialog
  - "Use full balance" fills 285.00 and the payment shows "Payment of $285.00 recorded"; the chip is green Paid with a PAID stamp and Balance due $0.00

## Credit notes (CRN)

Credit notes reduce what a customer owes and keep their own numbering.

- **FR-CRN-01** (Must) A credit note reduces the invoice balance and is listed with its own number and reason.
  - Balance due = Total - payments - credit notes; a $3,200.00 invoice with credit notes leaving $3,000.00 shows Balance due $3,000.00 in the preview and in the side card
  - The dialog reads "Reduces the balance of QB-1037 ($285.00 due)."; issuing $10.00 shows "Credit note of $10.00 issued"
  - Credit notes -$10.00 appears in the totals, Balance due is $275.00, and the side card lists "CN-0005 · -$10.00" with the reason "Menu typo"
- **FR-CRN-02** (Should) A credit note needs a reason.
  - Submitting with the reason empty shows "A reason is required for a credit note" and issues nothing

## Voiding invoices (VOID)

Owner-only cancellation of an invoice that should no longer be owed.

- **FR-VOID-01** (Must) Voiding needs a confirm step; cancel changes nothing; a voided invoice drops out of receivables.
  - The dialog "Void QB-1035?" explains the customer will no longer owe it and that voiding cannot be undone
  - Cancel closes the dialog and the chip stays Sent
  - Confirming shows "QB-1035 voided", a grey Void chip, a VOID stamp and Balance due $0.00
  - Outstanding receivables falls by $2,000.00 to $31,608.75, the status list shows Void 3 and Sent 3, and the Aging report drops the customer row and totals $31,608.75
- **FR-VOID-02** (Should) Only the Owner can void; for a Bookkeeper the action is disabled with an explanation.
  - The Void invoice button is greyed out with the tooltip "Only owners can void invoices"
  - Clicking it opens no dialog and the status does not change
- **FR-VOID-03** (Must) Void invoices owe nothing: they are excluded from Outstanding, Overdue, customer balances and the Aging report.
  - With the two seeded void invoices, Outstanding is $33,608.75 and Overdue $22,438.15
  - A customer whose only other invoice is void shows Outstanding $310.00 and Overdue $310.00 for the one invoice still owed
  - Aging 31–60 totals $7,245.00 and the grand total is $33,608.75

## Customers (CUS)

A directory of customers with a billing currency and payment terms.

- **FR-CUS-01** (Should) A customer can be added and then invoiced in its own currency and terms.
  - The Customers page reads "13 customers", sorted by name; the Add customer dialog has Name, Contact, City, Email, Currency and Payment terms
  - Adding shows the toast "<Name> added" and the subtitle "14 customers"; the new customer shows 0 invoices and an em dash under Outstanding
  - New invoice from that customer preselects it with the hint "pays in GBP on Net 60 terms." and a due date 60 days after 19 Oct 2026 (18 Dec 2026)
- **FR-CUS-02** (Could) Customer names are unique.
  - Adding an existing name shows a message of the form A customer named "<Name>" already exists inside the dialog, which stays open
- **FR-CUS-03** (Should) A customer page summarises what they owe in their own currency and links to their invoices; customers with invoices cannot be deleted.
  - A USD customer with Net 30 shows Outstanding $3,435.00, Overdue $2,235.00 ("Needs a reminder") and Invoices 4 ("0 in draft")
  - Delete is disabled with the tooltip "Customers with invoices cannot be deleted"
  - Clicking an invoice row opens that invoice
  - An EUR customer shows Outstanding €7,000.00 and Overdue €3,400.00; customer balances do not use the aging buckets
  - A GBP customer shows Outstanding £1,425.00

## Aging report (RPT)

Unpaid balances grouped by days past due, converted to USD.

- **FR-RPT-01** (Must) The Aging report buckets every unpaid balance by days past due, converts to USD and adds up to the Overview figure.
  - The page says "Amounts in USD (EUR 1.08, GBP 1.27)" and "Where the unpaid $33,608.75 sits"
  - Buckets: Current $11,170.60; 1–30 days $9,675.00; 31–60 days $7,245.00; 61–90 days $3,948.15; 90+ days $1,570.00
  - The Total row repeats those buckets and totals $33,608.75
  - A EUR customer with €3,600.00 current and €3,400.00 in 31–60 shows $3,888.00 and $3,672.00, total $7,560.00 (EUR at 1.08)
  - Each customer row shows an em dash in buckets where it owes nothing
- **FR-RPT-02** (Must) Bucket edges are inclusive: an invoice exactly 30 days past due is in 1–30; 31 days goes to 31–60.
  - An invoice due 19 Sep 2026 (30 days before the business date) shows $1,150.00 under 1–30 and an em dash under 31–60
  - The Total row shows 1–30 $9,675.00 and 31–60 $7,245.00

## Keyboard shortcuts (KEY)

Shortcuts for search and new invoices, and Escape to dismiss dialogs.

- **FR-KEY-01** (Could) "/" focuses the search box, "n" opens a new invoice, and Escape closes any dialog without acting.
  - On Invoices, "/" moves the cursor into "Search number or customer"
  - Outside any field, "n" opens the editor headed "New invoice"
  - Escape on the Issue credit note dialog closes it and issues nothing

## Non-functional requirements

- **NFR-01** (Should) At phone width (390px) the sidebar becomes a menu sheet and pages do not scroll sideways.
  - The top bar shows a menu button, the logo and a plus button
  - The menu lists Overview, Invoices (with count 41), Customers and Aging report
  - The invoice table shows Invoice, Due, Total and Status only, with the customer name under each number
- **NFR-02** (Must) Money uses the customer currency symbol, thousands separators and exactly two decimals; Overview and Aging report figures are always in USD.
  - USD $, EUR €, GBP £; negative adjustments are prefixed with "-"

## Glossary and rules

- **Balance due** — Total minus payments minus credit notes; zero for a void invoice.
- **Overdue** — Unpaid, past its due date on the business date of 19 Oct 2026.
- **Net N** — Payment terms: the due date is N days after the issue date.

## Out of scope

- Real payment processing or email delivery
- Live exchange rates
- Multi-user concurrency; each browser tab keeps its own copy of the books
