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>
325 lines
9.2 KiB
Markdown
325 lines
9.2 KiB
Markdown
# 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 client’s 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
|