invoice/project.md
ilia 0cb8ad3d11 Document ledger reprints, optional dueDate, Limmud FSU invoice data
Add --print to regenerate PDFs from invoices.json without ledger writes.
Support optional dueDate on ledger rows (default remains invoice date + 14 days)
and persist dueDate when saving new production rows.

Data: limmud_fsu_canada client with invoiceFileName, cruise_event_av_it
product, 2026-LFSU01 sample invoice, Levkin contact fields in sender.json.

README and project.md describe CLI, schema, and May 2026 changelog.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-09 12:07:54 -04:00

9.2 KiB
Raw Blame History

Invoice Generator - Project Documentation

Project Overview

A Node.js command-line application for generating professional PDF invoices. The system uses a data-driven approach where all business information (senders, clients, products) is stored in JSON files, making it easy to maintain without code changes.

Architecture

Core Components

invoice/
├── index.js              # Main application entry point
├── data/                 # JSON data files
│   ├── sender.json       # Company/sender profiles
│   ├── client.json       # Client information
│   ├── products.json     # Product/service catalog
│   └── invoices.json     # Invoice ledger (auto-generated)
├── invoice/              # Generated PDF output
│   └── <sender_name>/    # Organized by sender
└── test/
    └── invoice.test.js   # Unit tests

Dependencies

Production:

  • easyinvoice (v3.0.47) - PDF generation library
  • inquirer (v8.2.4) - Interactive command-line prompts
  • yargs (v17.7.2) - Command-line argument parsing

Development:

  • jest (v29.7.0) - Testing framework

Data Flow

1. Data Loading

// Loads all JSON files from data/ directory
const senders = loadJson(FILES.SENDERS);
const clients = loadJson(FILES.CLIENTS);
const productsData = loadJson(FILES.PRODUCTS);

2. User Input Processing

  • Interactive Mode: Uses inquirer prompts for sender, client, and product selection
  • Non-Interactive Mode: Processes command-line arguments via yargs
  • Test Mode: Automatically selects first available options

3. Invoice Generation Process

graph TD
    A[Load Data] --> B[Validate Inputs]
    B --> C[Generate Preview PDF]
    C --> D[User Review]
    D --> E{Proceed?}
    E -->|Yes| F[Generate Production PDF]
    E -->|No| G[Keep Preview Only]
    F --> H[Save to Ledger]
    H --> I[Update invoices.json]

4. File Output

  • Preview: invoice/<sender>/<client>_<year>-DEV-PREVIEW.pdf
  • Production: invoice/<sender>/<client>_<year>-<sequence>.pdf

Key Functions

getNextInvoiceNumber(year, invoicesData)

  • Generates sequential invoice numbers (YYYY-0001, YYYY-0002...)
  • Resets numbering annually
  • Handles unordered invoice numbers correctly

createInvoice(invoiceData, invoiceFilePath)

  • Uses easyinvoice library to generate PDF
  • Creates directory structure if needed
  • Returns success/failure status

saveInvoiceRecord(invoiceData, invoiceProducts)

  • Calculates totals (subtotal, tax, grand total)
  • Appends invoice record to invoices.json
  • Persists dueDate from invoiceData.information.dueDate when set, for accurate future --print output
  • Maintains complete invoice history

Data Schema

Sender Schema

{
  "company": "string",
  "contactName": "string (optional)",
  "phone": "string (optional)",
  "email": "string (optional)",
  "address": "string",
  "zip": "string",
  "city": "string",
  "country": "string",
  "taxName": "string (optional)",
  "taxNumber": "string (optional)",
  "paymentInfo": "string (optional)"
}

Client Schema

{
  "company": "string",
  "address": "string",
  "zip": "string",
  "city": "string",
  "country": "string",
  "invoiceFileName": "string (optional)"
}

Product Schema

{
  "description": "string",
  "price": "number",
  "taxRate": "number (optional, default 0)"
}

Invoice Record Schema

{
  "number": "string (YYYY-0001 or custom e.g. 2026-LFSU01)",
  "date": "string (YYYY-MM-DD)",
  "dueDate": "string (YYYY-MM-DD, optional — see Due dates below)",
  "sender": "string (must match sender.json company name)",
  "client": "string (must match client.json company name)",
  "products": [
    {
      "description": "string",
      "price": "number",
      "quantity": "number",
      "taxRate": "number"
    }
  ],
  "subtotal": "number",
  "taxTotal": "number",
  "total": "number"
}

Due dates: When you run node index.js --print <number>, the PDF due date is taken from dueDate if present. Otherwise it defaults to date plus 14 days. New production invoices still compute due as “today + 14” in the UI flow; saveInvoiceRecord stores that value on the ledger row as dueDate when present so reprints stay consistent. You can edit dueDate in invoices.json for any row (for example, payment terms through end of month).

Command-Line Interface

Available Commands

npm start                    # Interactive mode
npm run generate            # Non-interactive mode
npm test                    # Run unit tests
npm run test-invoice        # Test mode (dev only)

Reprinting from the ledger (no prompts, no ledger changes)

node index.js --print <invoice-number>

Looks up invoice-number in data/invoices.json, resolves sender and client by matching company strings to sender.json / client.json, and writes invoice/<senderKey>/<filename>_<number>.pdf. The clients optional invoiceFileName is used as the filename prefix when set (for example limmud_fsuc_2026-LFSU01.pdf).

Command-Line Options

--interactive               # Force interactive mode
--print <invoice-number>    # PDF only from invoices.json; does not append ledger
--sender <key>             # Sender key from sender.json
--client <key>             # Client key from client.json
--products <key1> <key2>   # Product keys from products.json
--test                     # Test mode (uses first available options)
--help                     # Show help

Testing Strategy

Unit Tests (test/invoice.test.js)

  • Invoice Numbering: Tests sequential numbering logic
  • Year Reset: Verifies numbering resets annually
  • Unordered Numbers: Handles gaps in invoice sequence

Test Coverage

  • getNextInvoiceNumber() function
  • Edge cases for invoice numbering
  • Year boundary scenarios

Error Handling

File System Errors

  • Missing JSON files trigger helpful error messages
  • Invalid JSON syntax is caught and reported
  • Directory creation failures are handled gracefully

Data Validation

  • Invalid sender/client/product keys are rejected
  • Missing required fields trigger validation errors
  • Tax rate calculations handle missing values

PDF Generation Errors

  • easyinvoice failures are caught and logged
  • File write errors are handled with user feedback

Configuration Options

Currency

  • Default: CAD (Canadian Dollar)
  • Set in commonData.settings.currency

Tax Configuration

  • Per-product tax rates in products.json
  • Tax name customization in sender.json
  • Automatic tax calculations

Payment Information

  • Custom payment instructions in sender.paymentInfo
  • Rendered in invoice footer
  • Supports HTML formatting

File Organization

Input Files (data/)

  • sender.json: Company profiles and tax information
  • client.json: Client contact details
  • products.json: Service/product catalog with pricing
  • invoices.json: Auto-generated invoice ledger

Output Files (invoice/)

  • Organized by sender name
  • Preview files marked with -DEV-PREVIEW
  • Production files with sequential numbering
  • Automatic directory creation

Development Workflow

Adding New Data

  1. Edit appropriate JSON file in data/
  2. Use interactive mode to test: npm start
  3. Verify preview generation
  4. Create production invoice if satisfied

Testing Changes

  1. Run unit tests: npm test
  2. Test invoice generation: npm run test-invoice
  3. Verify file output structure
  4. Check ledger updates

Code Modifications

  • Main logic in index.js
  • Test cases in test/invoice.test.js
  • Data validation in main function
  • Error handling throughout

Security Considerations

  • No external API calls
  • Local file system only
  • No sensitive data in code
  • JSON validation for data integrity
  • Error boundaries prevent crashes

Performance

  • Minimal dependencies
  • Synchronous file operations
  • No database overhead
  • Fast PDF generation
  • Efficient JSON parsing

Changelog (May 2026)

  • --print: Regenerate PDFs from data/invoices.json without modifying the ledger.
  • Optional ledger dueDate: Overrides the default “invoice date + 14 days” when printing; stored on new rows when production creates information.dueDate.
  • Data: Client limmud_fsu_canada (Limmud FSU Canada) with invoiceFileName for PDF naming; product cruise_event_av_it ($500 + 13% HST); sample invoice 2026-LFSU01 (Levkin Inc. → Limmud FSU Canada).
  • Sender: Optional contactName, phone, and email on levkin in sender.json (and schema support for other senders).

Future Enhancements

Potential Features

  • Multiple currency support
  • Invoice templates
  • Email integration
  • Export to accounting software
  • Invoice status tracking
  • Payment reminders

Technical Improvements

  • TypeScript migration
  • Database integration
  • Web interface
  • API endpoints
  • Docker containerization