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

325 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```javascript
// 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
```mermaid
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
```json
{
"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
```json
{
"company": "string",
"address": "string",
"zip": "string",
"city": "string",
"country": "string",
"invoiceFileName": "string (optional)"
}
```
### Product Schema
```json
{
"description": "string",
"price": "number",
"taxRate": "number (optional, default 0)"
}
```
### Invoice Record Schema
```json
{
"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
```bash
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)
```bash
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
```bash
--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