--- name: invoices description: Generate, file, and track client invoices as PDFs. Use when the user wants an invoice created ("invoice Acme Co 450 for the March photo shoot", "bill Northside Dental for last week", "make my invoice for this client"), wants payment status changed ("mark 2026-001 paid"), asks about invoice history ("what's unpaid?", "what did I bill Acme this year?"), or wants old invoice PDFs filed. Builds the PDF locally with headless Microsoft Edge, files it under the skill's output folder by year, and updates ledger.csv. --- # Invoices Local, free invoice system for a freelancer or small business. No accounts, no network, no subscription. The user always sends invoices themselves. This skill NEVER sends, posts, or emails an invoice anywhere. ## Paths `` below means the folder this SKILL.md sits in. - DATA root: `\output` - Ledger (single source of truth): `DATA\ledger.csv` - Client profiles: `DATA\clients.csv` - Finished PDFs: `DATA\\` - Template: `\template.html` - PDF renderer: `\scripts\make-invoice.ps1` - Dashboard generator: `\scripts\make-dashboard.ps1` (no arguments needed) - Dashboard view: `DATA\dashboard.html` - Filled-HTML staging: the session scratchpad directory To keep invoices somewhere else, change `$DataRoot` at the top of both scripts to the same full path. ## Before the first invoice `template.html` ships with five fill-once placeholders: `{{YOUR_NAME}}`, `{{YOUR_ADDRESS}}`, `{{YOUR_EMAIL}}`, `{{YOUR_PHONE}}`, and `{{PAYMENT_INSTRUCTIONS}}`. If any of them are still in the file, ask the user for those details, write them into `template.html`, and say the file is now set up. This happens once, not per invoice. ## Creating an invoice 1. Read `clients.csv` and `ledger.csv` fresh from disk every time. Never trust remembered state from earlier in a session. 2. Resolve the client fuzzily against `clients.csv` (a partial name like "acme" matches "Acme Co"). Unknown client: ask for the bill-to name once, then add a profile row. 3. Invoice number = `-NNN` where NNN is (highest NNN for the current year in the ledger) + 1, zero-padded to 3 (2026-001, 2026-002, and so on). The document shows it as `# 2026-001`. Numbers are never reused, including voided ones. Numbering restarts at 001 each new year. Create `DATA\\` when it does not exist yet. 4. Dates: - Issue date = today. - Billing period: from the user's request. If they give none and the client has a cadence, infer the next window from that client's latest non-void PeriodEnd in the ledger, and state the inference clearly in the final report. Cadence `biweekly`: start = PeriodEnd + 1 day, end = start + 13 days. Cadence `10-business-days`: start = first weekday after PeriodEnd, end = the 10th weekday counting from start (Sat and Sun excluded; holidays count as work days unless the user says otherwise). Cadence `none`: no period at all. - Due date from the client's DueRule: `period_start` = first day of the period; `on_receipt` = issue date; `net_15` / `net_30` = issue date plus that many days. 5. Amounts and line items: from the request, else the client's defaults in `clients.csv`. If neither exists, ASK. Never guess or invent an amount, a rate, or a payment instruction. Multiple line items are supported (one `` per item). 6. Fill every per-invoice `{{PLACEHOLDER}}` in the template. Leave the five fill-once identity placeholders alone; by this point they hold the user's real details. - Dates formatted like `Mar 3, 2026`. Currency like `$1,200.00`. - `{{PERIOD_ROW}}`: `Service Period:Mar 3 – Mar 14, 2026`, or empty string when there is no period. - Item rows: `DESCRIPTION1$1,200.00$1,200.00` - `{{NOTES_BLOCK}}`: the whole notes div when there is a note for this invoice, or empty string when there is not: `
Notes:
TEXT
` - HTML-escape `&`, `<`, `>` in all user text. 7. Write the filled HTML to the scratchpad, then render: `powershell -ExecutionPolicy Bypass -File "" -HtmlPath "" -PdfPath "\"` A relative `-PdfPath` lands under DATA. Pass a full path to override. 8. Filename, exactly: `INV-YYYY-NNN Client Name (Mon D - Mon D).pdf`, for example `INV-2026-001 Acme Co (Mar 3 - Mar 14).pdf`. No billing period: use the issue date, `INV-2026-002 Acme Co (Mar 20).pdf`. 9. Verify before reporting: read the produced PDF and check the number, names, dates, amounts, and notes against what was intended. Fix and re-render if anything is off. 10. Ledger write: copy `ledger.csv` to `ledger.backup.csv` FIRST, then append the row. Quote any field containing a comma. Status starts as `unpaid`. 11. Regenerate the dashboard: `powershell -ExecutionPolicy Bypass -File ""`. This runs after EVERY ledger write (create, revise, void, mark paid) so dashboard.html is never stale. 12. Open the PDF (`Invoke-Item`) and report: number, client, period, amount, saved path, plus any inference made in step 4. ## Ledger File: `DATA\ledger.csv` Columns: `Number,Client,IssueDate,DueDate,PeriodStart,PeriodEnd,Description,Amount,Status,PaidDate,File` Statuses: `unpaid`, `paid`, `void`. Dates in the ledger are ISO (2026-03-03). Amount is a bare number, no currency symbol and no thousands separator (1200.00). File is the path relative to DATA. ## Client profiles File: `DATA\clients.csv` Columns: `Client,DueRule,Cadence,DefaultRate,DefaultQty,DefaultDescription,DefaultNotes` - `DueRule`: `on_receipt`, `period_start`, `net_15`, or `net_30` - `Cadence`: `none`, `biweekly`, or `10-business-days` - The Default columns may be blank. Blank means ask. ## Commands - "show my dashboard" / "update my invoice dashboard": run the dashboard generator, then `Invoke-Item` dashboard.html. All dashboard math lives in the script. Never hand-compute totals into the HTML, and never edit dashboard.html directly (it is a generated view of ledger.csv). - "mark 2026-001 paid": back up the ledger, set Status=paid and PaidDate=today on that row. - "what's unpaid?": list unpaid rows with number, client, amount, due date, and days overdue if past due. - "what did I bill this year?": sum and list from the ledger. - Revise ("redo 2026-003, make it 20 hours"): SAME number, regenerate, overwrite the PDF, update the ledger row in place. No new number for corrections. - Void ("void 2026-004"): Status=void. The PDF stays on disk and the number is never reused. - Standing-rate changes ("Acme is 20 hours a week now"): update that client's row in clients.csv so future defaults are right. Never touch invoices already generated. ## Rules - The ledger is the only counter. No separate counter file may be created. - Back up ledger.csv before EVERY write to it, no exceptions. - Keep documents grayscale and minimal. No logos, colors, or emojis unless the user asks for them. - Invoices from before this skill was installed keep their original numbers if the user backfills them into the ledger. - Never invent an amount, a rate, a due date rule, or payment details. Ask instead.