Node.js/Express + SQLite + Vanilla JS app for managing employee commissions on telecom activations (Telekom, Vodafone, O2, 1&1). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
225 lines
9.6 KiB
Markdown
225 lines
9.6 KiB
Markdown
# CLAUDE.md
|
|
|
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
|
|
## Project Overview
|
|
|
|
This is a **Provisions-Tracker** (Commission Tracker) application for managing employee commissions on telecommunications activations ("Schaltungen"). Built as a Node.js/Express backend with SQLite database and vanilla JavaScript frontend.
|
|
|
|
**Purpose**: Track commissions for telecom providers (Telekom, Vodafone, O2, 1&1), including tariffs, customer data, documents, and complex commission calculations.
|
|
|
|
## Development Commands
|
|
|
|
### Running the Application
|
|
```bash
|
|
# Production mode
|
|
npm start
|
|
|
|
# Development mode with auto-restart
|
|
npm run dev
|
|
|
|
# Install dependencies
|
|
npm install
|
|
```
|
|
|
|
The application runs on `http://localhost:3000` by default (configurable via `PORT` environment variable).
|
|
|
|
### Database
|
|
- SQLite database file: `provision.db` (auto-created on first run)
|
|
- Database initialization happens automatically via `initDatabase()` in [database.js](database.js)
|
|
- Backup file exists: `provision.db.backup`
|
|
- No migrations system - uses `CREATE TABLE IF NOT EXISTS` and `ALTER TABLE` with error handling
|
|
|
|
## Architecture Overview
|
|
|
|
### Three-Tier Structure
|
|
|
|
**Backend** ([server.js](server.js) - ~1987 lines)
|
|
- Express.js REST API with ~40+ endpoints
|
|
- Handles business logic, commission calculations, and database operations
|
|
- File upload handling via Multer (10MB limit)
|
|
- CORS enabled for development
|
|
|
|
**Database** ([database.js](database.js) - 288 lines)
|
|
- SQLite with 12+ tables
|
|
- Key tables: `anbieter` (providers), `tarife` (tariffs), `schaltungen` (connections/commissions), `dokumente` (documents)
|
|
- Complex commission system with multiple rule types (fixed/percentage)
|
|
- Includes soft-delete tracking (`geloeschte_schaltungen`)
|
|
|
|
**Frontend** ([public/](public/))
|
|
- Single-page app: [index.html](public/index.html), [script.js](public/script.js) (~4000+ lines)
|
|
- Tab-based navigation (Erfassung, Übersicht, Provisionen, Admin, Gelöschte)
|
|
- Preloading strategy for fast tab switching
|
|
- Drag-and-drop file uploads
|
|
|
|
### Key Database Tables and Relationships
|
|
|
|
```
|
|
anbieter (providers)
|
|
├─→ tarife (tariffs) - provision_euro, mitarbeiter_provision
|
|
│ ├─→ provisionsregeln (tariff-specific commission rules)
|
|
│ └─→ tarifoptionen_tarife (M2M link to options)
|
|
├─→ anbieter_provisionsregeln (global provider commission rules)
|
|
└─→ mitarbeiter_prozentsaetze (employee percentage rates per provider)
|
|
|
|
schaltungen (main commissions table)
|
|
├─→ anbieter_id, tarif_id (foreign keys)
|
|
├─→ dokumente (uploaded documents)
|
|
├─→ schaltungen_tarifoptionen (M2M selected tariff options)
|
|
└─→ hardware_ek, kundenverkaufspreis_netto (hardware pricing)
|
|
|
|
tarifoptionen_new (tariff options - new structure)
|
|
└─→ tarifoptionen_tarife (M2M relationship to tarifs)
|
|
|
|
tarifoptionen (old structure - kept for migration compatibility)
|
|
```
|
|
|
|
### Commission Calculation System
|
|
|
|
The app supports multiple commission calculation methods with complex fallback logic:
|
|
|
|
1. **Tariff-specific rules** (`provisionsregeln` table)
|
|
- Fixed amount or percentage
|
|
- Employee commission (verkaeufer_provision)
|
|
|
|
2. **Global provider rules** (`anbieter_provisionsregeln` table)
|
|
- Applied when no tariff-specific rule exists
|
|
|
|
3. **Direct tariff amounts** (fallback)
|
|
- `tarife.provision_euro` - company commission
|
|
- `tarife.mitarbeiter_provision` - employee commission
|
|
|
|
4. **Tariff options** (`tarifoptionen`)
|
|
- Additional commission adjustments per activation
|
|
- Can be positive or negative (e.g., rebates)
|
|
- Many-to-many relationship (options can apply to multiple tariffs)
|
|
|
|
**API endpoint**: `GET /api/schaltungen/:id/provision` - calculates total commission for a specific activation.
|
|
|
|
### Migration Notes
|
|
|
|
The system includes an in-place migration from old tarifoptionen structure (one-to-many with tarifs) to new structure (many-to-many via `tarifoptionen_new` and `tarifoptionen_tarife`). Migration runs automatically on startup via `migrateToNewTarifoptionen()` in [database.js:228-266](database.js#L228-L266).
|
|
|
|
## API Endpoint Categories
|
|
|
|
### Anbieter (Providers)
|
|
- `GET /api/anbieter` - List all providers
|
|
- `POST /api/anbieter` - Add provider
|
|
- `DELETE /api/anbieter/:id` - Delete provider (with dependency checks)
|
|
- `GET /api/anbieter/:id/provisionsregeln` - Get global commission rules
|
|
- `POST /api/anbieter-provisionsregeln` - Add global rule
|
|
- `PUT /api/anbieter-provisionsregeln/:id` - Update global rule
|
|
|
|
### Tarife (Tariffs)
|
|
- `GET /api/tarife` - List all tariffs
|
|
- `GET /api/tarife/:anbieter_id` - List tariffs for provider
|
|
- `GET /api/tarif/:id` - Get single tariff
|
|
- `POST /api/tarife` - Add tariff
|
|
- `PUT /api/tarife/:id` - Update tariff
|
|
- `DELETE /api/tarife/:id` - Delete tariff (with dependency checks)
|
|
- `GET /api/tarife/:id/provisionsregeln` - Get tariff commission rules
|
|
- `POST /api/provisionsregeln` - Add tariff rule
|
|
- `PUT /api/provisionsregeln/:id` - Update tariff rule
|
|
- `PUT /api/tarife/:id/mitarbeiter-provision` - Update employee commission
|
|
|
|
### Schaltungen (Commissions/Activations)
|
|
- `GET /api/schaltungen` - List all activations
|
|
- `POST /api/schaltungen` - Create activation
|
|
- `PUT /api/schaltungen/:id` - Update activation
|
|
- `DELETE /api/schaltungen/:id` - Delete activation (soft delete with reason tracking)
|
|
- `GET /api/schaltungen/:id/provision` - Calculate commission for activation
|
|
- `GET /api/provisionen` - Get commission overview/report
|
|
- `GET /api/geloeschte-schaltungen` - List deleted activations
|
|
|
|
### Dokumente (Documents)
|
|
- `POST /api/schaltungen/:id/dokumente` - Upload document (multipart/form-data)
|
|
- `GET /api/schaltungen/:id/dokumente` - List documents for activation
|
|
- `GET /api/dokumente/:id/download` - Download document
|
|
- `DELETE /api/dokumente/:id` - Delete document (removes file from disk)
|
|
|
|
### Tarifoptionen (Tariff Options)
|
|
- `GET /api/tarifoptionen` - List all options (old structure)
|
|
- `GET /api/tarifoptionen/multi` - List all options (new structure)
|
|
- `POST /api/tarifoptionen/multi` - Add option (new structure)
|
|
- `PUT /api/tarifoptionen/multi/:id` - Update option (new structure)
|
|
- `DELETE /api/tarifoptionen/multi/:id` - Delete option (new structure)
|
|
- `GET /api/schaltungen/:id/tarifoptionen` - Get options for activation
|
|
- `POST /api/schaltungen/:id/tarifoptionen` - Link option to activation
|
|
- `DELETE /api/schaltungen/:schaltung_id/tarifoptionen/:tarifoption_id` - Unlink option
|
|
|
|
### Mitarbeiter (Employee Settings)
|
|
- `GET /api/anbieter/:id/mitarbeiter-prozentsaetze` - Get employee percentages for provider
|
|
- `POST /api/mitarbeiter-prozentsaetze` - Add percentage rate
|
|
- `DELETE /api/mitarbeiter-prozentsaetze/:id` - Delete percentage rate
|
|
|
|
## Important Implementation Details
|
|
|
|
### File Uploads
|
|
- Storage: `uploads/` directory (created automatically)
|
|
- Naming: `{timestamp}-{random}.{extension}`
|
|
- Limit: 10MB per file
|
|
- Supported types: PDF, JPG, JPEG, PNG, DOC, DOCX (enforced client-side)
|
|
- Files are physically deleted when document is removed
|
|
|
|
### Frontend State Management
|
|
- **Preloading**: App preloads all tab data in background after 1 second delay for faster navigation
|
|
- **Tab switching**: Uses cached data when available (`showTab()` checks if data exists)
|
|
- Global variables: `currentSchaltungId`, `uploadedFiles`
|
|
- Message system: Toast notifications via `showMessage()` overlay
|
|
|
|
### Database Connection Pattern
|
|
**Singleton Pattern** - A single persistent database connection is used throughout the application lifecycle:
|
|
```javascript
|
|
const db = getDatabase(); // Returns singleton instance
|
|
// ... do work ...
|
|
// NO db.close() - connection persists
|
|
```
|
|
- Connection is opened once on startup and reused
|
|
- Foreign keys are enabled after initialization (`PRAGMA foreign_keys = ON`)
|
|
- Busy timeout set to 3000ms for concurrent access handling
|
|
- Graceful shutdown closes connection on SIGINT/SIGTERM
|
|
|
|
### Delete Operations
|
|
- **Soft delete**: Schaltungen are moved to `geloeschte_schaltungen` table with reason and timestamp
|
|
- **CASCADE delete**: Deleting a Schaltung automatically removes:
|
|
- Associated documents from database (via `ON DELETE CASCADE`)
|
|
- Document files from filesystem (handled in endpoint)
|
|
- Linked tarifoptionen (via `ON DELETE CASCADE`)
|
|
- **Hard delete**: Individual documents physically removed from filesystem when deleted
|
|
- **Dependency checks**: Providers and Tariffs check for usage before deletion
|
|
|
|
### Language
|
|
- **UI/Database**: German (Anbieter, Tarife, Schaltungen, etc.)
|
|
- **Code**: Mixed German/English
|
|
- **Comments**: Mostly English in code, German in UI strings
|
|
|
|
## Testing Data
|
|
|
|
Default data seeded on first run:
|
|
- **Providers**: Telekom, Vodafone, O2, 1&1
|
|
- **Tariffs**: 10 example tariffs with commission amounts
|
|
- **Employee percentages**: 8-10% per provider
|
|
- **Tariff options**: 5 example options including rebates
|
|
- **Global rules**: Example O2 (fixed 20€) and 1&1 (12% percentage) rules
|
|
|
|
## Common Development Patterns
|
|
|
|
### Adding a New API Endpoint
|
|
1. Add route in [server.js](server.js) following existing patterns
|
|
2. Use `getDatabase()` to open connection
|
|
3. Always call `db.close()` in callbacks
|
|
4. Return JSON with proper error handling
|
|
5. Update frontend in [script.js](script.js) with corresponding API call
|
|
|
|
### Adding a Database Table
|
|
1. Add `CREATE TABLE IF NOT EXISTS` in [database.js](database.js) `initDatabase()`
|
|
2. Add within `db.serialize()` block
|
|
3. Consider foreign key constraints
|
|
4. Add seed data with `INSERT OR IGNORE` if needed
|
|
|
|
### Modifying Commission Logic
|
|
- Main calculation logic is in `GET /api/schaltungen/:id/provision`
|
|
- Fallback order: provisionsregeln → anbieter_provisionsregeln → direct tariff amounts
|
|
- Always recalculate when tariff/provider/options change
|
|
- Employee commission calculated separately from company commission
|