invoice-generator

Featured

Generate professional invoices in PDF from line items.

Category: Utility Tier: Highly useful within a category Source: Newly authored Updated: 2026-07-20

What it does

Generate professional invoices in PDF from line items.

How an agent uses it

  • A user asks to "make an invoice", "bill my client", or "generate an invoice PDF".
  • A user provides services/products sold and wants a formal document with totals.
  • A user needs recurring or one-off invoices with consistent branding.
  • A user wants tax (VAT/GST/sales tax) applied and shown as its own line.
  • A user works across currencies and needs symbols + formatting handled correctly.

Do **not** use this skill for: quotes/estimates (no tax committed yet — still fine

to adapt, but name it clearly), receipts for already-paid cash sales, or timesheets

without a billing step. For those, adjust the document title and clarify status.

What you get

Install this skill and your Hermes agent can generate professional invoices in pdf from line items. No manual setup, no scripts to run — the agent handles it.

Install command

hermes skills install https://raw.githubusercontent.com/THEROCKSSS/hermes-skills-portfolio/main/skills/invoice-generator/SKILL.md
View SKILL.md on GitHub
---
name: invoice-generator
description: Use when a user wants a clean, branded PDF invoice generated from structured line items — client details, itemized line items, tax, totals, and currency — for one-off billing, recurring invoices, or multi-currency clients.
version: 1.0.0
author: Hermes Agent
license: MIT
metadata:
  hermes:
    tags: [invoice, pdf, billing, fpdf2, line-items]
    related_skills: [markdown-to-pdf, resume-builder]
---

# invoice-generator

## Overview

Turn structured line items into a polished, print-ready PDF invoice. The agent
collects client and item details, validates the math, and renders a professional
document the user can send to a customer or file for accounting.

## When to Use

- A user asks to "make an invoice", "bill my client", or "generate an invoice PDF".
- A user provides services/products sold and wants a formal document with totals.
- A user needs recurring or one-off invoices with consistent branding.
- A user wants tax (VAT/GST/sales tax) applied and shown as its own line.
- A user works across currencies and needs symbols + formatting handled correctly.

Do **not** use this skill for: quotes/estimates (no tax committed yet — still fine
to adapt, but name it clearly), receipts for already-paid cash sales, or timesheets
without a billing step. For those, adjust the document title and clarify status.

## Input Format

Collect the invoice as a single YAML block. Keep field names stable so the
generator script can parse them without guessing.

```yaml
invoice:
  number: INV-2026-0042
  issue_date: 2026-07-20
  due_date: 2026-08-19
  currency: USD
  tax_rate: 0.20          # 20% VAT/GST; 0 if tax-exempt
  notes: "Payment due within 30 days. Bank transfer preferred."
  sender:
    name: Alex
    company: Amano Studio LLC
    email: billing@amano.studio
    address: "12 Birch Lane, Suite 4\nPortland, OR 97201"
    tax_id: "US-EIN 84-1234567"
  client:
    name: Jordan Reeves
    company: Reeves & Co.
    email: accounts@reeves.co
    address: "88 Market Street\nLondon EC2M 1AP, UK"
  items:
    - description: "Brand identity design"
      quantity: 1
      unit_price: 2400.00
    - description: "Logo suite (primary + secondary)"
      quantity: 2
      unit_price: 350.00
    - description: "Business card layout"
      quantity: 3
      unit_price: 90.00
```

### Field rules

- `currency`: ISO 4217 code (`USD`, `EUR`, `GBP`, `JPY`, ...). Drives the symbol.
- `tax_rate`: decimal fraction (0.20 = 20%). Multiply after subtotal.
- `quantity` × `unit_price` = line total. All money values are decimals.
- `address` may contain `\n` for line breaks.
- Missing `due_date` → default to `issue_date` + 30 days.
- Missing `number` → fall back to `INV-<YYYY>-<seq>` (see Numbering).

## Template

A clean, professional invoice has a clear visual hierarchy:

1. **Header row** — Sender name/logo on the left, "INVOICE" title + number on the right.
2. **Party block** — "From" (sender) and "Bill To" (client) side by side.
3. **Meta row** — Issue date, due date, currency, tax ID.
4. **Line-item table** — columns: Description | Qty | Unit Price | Line Total.
5. **Totals block** — Subtotal, Tax, **Total Due** (right-aligned, emphasized).
6. **Footer** — notes, payment terms, thank-you line.

Design cues that read as "professional":
- One accent color (deep navy or slate), generous whitespace, 10–11pt body text.
- Right-align all numeric columns; use thousands separators.
- Bold the Total Due and separate it with a thin rule.
- Avoid clip-art, gradients, and decorative fonts.

## PDF Generation

Use `fpdf2` (pure Python, no system libraries) so the skill runs anywhere Python does.

Install once:

```bash
pip install fpdf2
```

Minimal generation function (embed/call this from the agent's runtime):

```python
from fpdf import FPDF
from datetime import date, timedelta

SYMBOLS = {"USD": "$", "EUR": "€", "GBP": "£", "JPY": "¥", "CAD": "C$", "AUD": "A$"}

def money(value, currency):
    sym = SYMBOLS.get(currency, f"{currency} ")
    # JPY has no decimal places by convention
    dec = 0 if currency == "JPY" else 2
    return f"{sym}{value:,.{dec}f}"

def build_invoice(data, out_path="invoice.pdf"):
    inv = data["invoice"]
    cur = inv.get("currency", "USD")
    tax_rate = float(inv.get("tax_rate", 0) or 0)
    items = inv.get("items", [])

    subtotal = sum(float(i["quantity"]) * float(i["unit_price"]) for i in items)
    tax = subtotal * tax_rate
    total = subtotal + tax

    pdf = FPDF(format="A4", unit="mm")
    pdf.add_page()
    pdf.set_auto_page_break(auto=True, margin=15)
    pdf.set_margins(15, 15, 15)

    # Header
    pdf.set_font("Helvetica", "B", 20)
    pdf.cell(0, 10, inv["sender"]["name"], ln=True)
    pdf.set_font("Helvetica", "", 10)
    pdf.cell(0, 6, inv["sender"].get("company", ""), ln=True)
    pdf.ln(2)

    pdf.set_xy(140, 15)
    pdf.set_font("Helvetica", "B", 16)
    pdf.cell(0, 10, "INVOICE", ln=True)
    pdf.set_font("Helvetica", "", 10)
    pdf.set_x(140)
    pdf.cell(0, 6, f"Number: {inv.get('number', 'N/A')}", ln=True)
    pdf.set_x(140)
    pdf.cell(0, 6, f"Issue: {inv.get('issue_date', date.today().isoformat())}", ln=True)

    pdf.ln(6)

    # Parties
    y = pdf.get_y()
    pdf.set_font("Helvetica", "B", 10)
    pdf.cell(90, 6, "From", ln=0)
    pdf.cell(90, 6, "Bill To", ln=True)
    pdf.set_font("Helvetica", "", 10)
    for key in ("name", "company", "email", "address"):
        s = str(inv["sender"].get(key, "")).replace("\n", "  |  ")
        c = str(inv["client"].get(key, "")).replace("\n", "  |  ")
        pdf.cell(90, 6, s[:48], ln=0)
        pdf.cell(90, 6, c[:48], ln=True)

    pdf.ln(4)

    # Items table
    pdf.set_font("Helvetica", "B", 10)
    pdf.set_fill_color(230, 232, 240)
    pdf.cell(100, 8, "Description", border=1, fill=True)
    pdf.cell(20, 8, "Qty", border=1, fill=True, align="R")
    pdf.cell(35, 8, "Unit Price", border=1, fill=True, align="R")
    pdf.cell(35, 8, "Total", border=1, fill=True, align="R", ln=True)
    pdf.set_font("Helvetica", "", 10)
    for i in items:
        lt = float(i["quantity"]) * float(i["unit_price"])
        pdf.cell(100, 8, str(i["description"])[:60], border=1)
        pdf.cell(20, 8, str(i["quantity"]), border=1, align="R")
        pdf.cell(35, 8, money(float(i["unit_price"]), cur), border=1, align="R")
        pdf.cell(35, 8, money(lt, cur), border=1, align="R", ln=True)

    # Totals
    pdf.ln(2)
    pdf.set_font("Helvetica", "B", 11)
    pdf.cell(155, 8, "Subtotal", align="R")
    pdf.cell(35, 8, money(subtotal, cur), align="R", ln=True)
    if tax_rate:
        pdf.cell(155, 8, f"Tax ({int(tax_rate*100)}%)", align="R")
        pdf.cell(35, 8, money(tax, cur), align="R", ln=True)
    pdf.set_font("Helvetica", "B", 13)
    pdf.cell(155, 9, "Total Due", align="R")
    pdf.cell(35, 9, money(total, cur), align="R", ln=True)

    # Notes
    if inv.get("notes"):
        pdf.ln(6)
        pdf.set_font("Helvetica", "", 9)
        pdf.multi_cell(0, 5, inv["notes"])

    pdf.output(out_path)
    return out_path
```

The agent should:
1. Parse the YAML the user provided (or assemble it from a conversation).
2. Call `build_invoice(data, "invoice.pdf")`.
3. Return the saved PDF path to the user.

## Numbering

- Prefer an explicit `number` from the user (e.g. `INV-2026-0042`).
- If absent, generate `INV-<YYYY>-<seq>` where `<seq>` is a zero-padded sequence
  based on the year (start at 0001, increment per invoice).
- Keep numbers unique and sequential; do not reuse or skip ranges silently.
- For drafts, prefix with `DRAFT-` and strip it on finalization.

## Tax Calculation

- `tax = subtotal × tax_rate`. Apply **after** the subtotal, never per line, unless
  the jurisdiction requires line-level tax (then sum line taxes).
- Show tax as its own line with the rate in parentheses: `Tax (20%)`.
- `total = subtotal + tax`. Never round the subtotal before computing tax if the
  jurisdiction rounds tax on the gross — when in doubt, compute tax on the exact
  subtotal and round only the displayed value.
- Tax-exempt? Set `tax_rate: 0` and omit the tax line (or label it `Tax (0% — exempt)`).
- If the client is in another tax jurisdiction, surface that to the user before
  assuming a rate — reverse-charge rules may apply.

## Multi-currency

- Always store ISO 4217 codes. Map to display symbols via the `SYMBOLS` dict above;
  extend it as needed.
- **JPY, KRW** conventionally show **no decimals** — handle `dec = 0`.
- Format with thousands separators (`1,234.56`) and right-align in tables.
- If the invoice is in a non-sender currency, note the exchange basis in `notes`
  (e.g. "Converted at 1 EUR = 1.08 USD on 2026-07-20").
- Never mix currencies within one invoice; convert first, then bill in one currency.

## Common Pitfalls

1. **Float rounding.** Money math in floats is fine for display, but round only at
   presentation. Accumulate subtotal as a precise sum.
2. **Missing client address** breaks the layout — default to the email if address
   is absent, and warn the user.
3. **Long descriptions** overflow the cell — truncate to ~60 chars or use
   `multi_cell` for wrapping.
4. **Page breaks.** Set `auto_page_break` (done above) so long item lists don't clip.
5. **Special characters** (€, é, &). `fpdf2` core fonts are latin-1; for full
   Unicode, add a TTF font via `pdf.add_font(...)` or sanitize to ASCII.
6. **Date formats.** Store ISO `YYYY-MM-DD`; format for display per locale only at
   render time, never in the source data.
7. **Duplicate numbers.** Check the sequence before assigning a generated number.
8. **Not saved where the user expects.** Return the absolute output path and confirm
   the filename so the user can find the PDF.

## Verification Checklist

- [ ] `subtotal + tax == total`, recomputed independently and matching what's printed on the PDF
- [ ] Currency symbol and decimal places match the ISO 4217 code (0 decimals for JPY/KRW, else 2)
- [ ] Invoice number is unique — checked against prior invoices, not blindly incremented
- [ ] Client/sender address and long line-item descriptions render without truncation or overflow
- [ ] The absolute output path of the generated PDF was returned and confirmed to the user

## Install / Source

Public portfolio (Alex):
https://github.com/THEROCKSSS/hermes-skills-portfolio/blob/main/skills/invoice-generator/SKILL.md
# invoice-generator



A public [Hermes](https://github.com/THEROCKSSS/hermes-skills-portfolio) skill that turns

structured line items into a clean, professional **PDF invoice** — with client

details, itemized rows, tax, totals, and multi-currency support.



> "Agent + skill = the user gets a branded invoice PDF without touching a

> spreadsheet."



## Why



Invoicing is repetitive, error-prone, and easy to get wrong on tax and totals.

This skill standardizes the input, validates the math, and renders a

print-ready document every time.



## Install



Point Hermes at the skill file:



```

https://github.com/THEROCKSSS/hermes-skills-portfolio/blob/main/skills/invoice-generator/SKILL.md

```



Or copy the `invoice-generator/` folder into your Hermes skills directory.



The PDF engine uses [`fpdf2`](https://pypi.org/project/fpdf2/) (pure Python):



```bash

pip install fpdf2

```



## Usage



Tell the agent what you sold. Provide the details as a YAML block, or just

describe the job and let the agent assemble it:



```yaml

invoice:

  number: INV-2026-0042

  issue_date: 2026-07-20

  currency: USD

  tax_rate: 0.20

  sender:

    name: Alex

    company: Amano Studio LLC

    email: billing@amano.studio

  client:

    name: Jordan Reeves

    company: Reeves & Co.

    email: accounts@reeves.co

  items:

    - description: "Brand identity design"

      quantity: 1

      unit_price: 2400.00

    - description: "Logo suite"

      quantity: 2

      unit_price: 350.00

```



The agent returns a saved `invoice.pdf` path.



## Features



- **Clean template** — header, From/Bill-To blocks, itemized table, totals.

- **Tax handling** — subtotal × rate, shown as its own line; exempt-aware.

- **Numbering** — explicit or auto `INV-<YYYY>-<seq>`.

- **Multi-currency** — ISO 4217 codes, correct symbols, JPY/KRW zero-decimal.

- **Safe math** — precise accumulation, rounding only at display.



## What it is not



Quotes/estimates, cash receipts, and timesheets without a billing step are

out of scope — adapt the title and status if you reuse it for those.



## License



MIT — free to use, fork, and ship in your own portfolio.



---



Part of the [hermes-skills-portfolio](https://github.com/THEROCKSSS/hermes-skills-portfolio)

by Alex.