Add VO management, tariff-option assignment, and 2-step schaltung wizard

VOs (Vertriebspartner-Nummern)
- New vos table (one row per VO, scoped to a provider). Migrations
  add tarife.vo_id and schaltungen.vo_id columns (nullable so legacy
  rows survive).
- New /api/vos CRUD (list, create, update, delete with use-checks).
- Admin: new "VOs (Vertriebspartner)" sub-tab. List/create with
  provider dropdown; edit via prompt; delete refuses while used.
- /api/tarife?vo_id=X filter for fetching tariffs of a specific VO.

Tariff editor
- Add-tariff form requires a VO; new dependent VO dropdown that
  populates after the provider is chosen.
- Edit-tariff modal now shows the VO dropdown and a checkbox grid of
  all anbieter-wide tarifoptionen. Saving the dialog applies the
  selection via the existing tarifoptionen_tarife table.
- New GET /api/tarife/:id/tarifoptionen-multi returns the option set
  scoped to the tariff's provider, each annotated with whether it is
  currently assigned to this tariff.
- POST/PUT /api/tarife accept vo_id (required for create, validated
  against the provider) and an optional tarifoption_ids array.

Schaltung wizard
- The Schaltung-erfassen tab is now a 2-step wizard:
  step 1 picks a provider from a button row (mirrors the screenshot
  the user shared); step 2 shows a 2-column form mirroring the
  Aktivierungs-/Vertragsdaten layout.
- Step 2 cascades: VO -> tariffs filtered by VO -> tarifoptionen
  filtered by tariff. Erfasser is autofilled from the logged-in user
  (read-only).
- Removed customer fields that the user does not need (Kundennummer,
  Kennwort/Service-PIN never existed; Rufnummer is now optional both
  in the form and in the API validation).
- After saving, the wizard resets to step 1 ready for the next entry.

Schema
- tarife: vo_id added.
- schaltungen: vo_id added; rufnummer is nullable in new DBs.
  Existing DBs may still have NOT NULL on rufnummer; the schaltungen
  route now sends an empty string instead of NULL to remain
  compatible.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kerimatilganandClaude Opus 4.7 committed 2026-05-15 16:36:22 +02:00
1 parent ea3a3fc12c
commit f2ab23f261
7 files changed
+768 -212

No files matched your search

+22 -2
View File
@@ -74,19 +74,35 @@ function initDatabase() {
UNIQUE(anbieter_id, gueltig_ab)
)`);
// Vertriebspartner-Nummern (VOs) per Anbieter. Each tariff is assigned to
// exactly one VO (nullable for legacy rows that pre-date this column).
db.run(`CREATE TABLE IF NOT EXISTS vos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
anbieter_id INTEGER NOT NULL,
nummer TEXT NOT NULL,
bezeichnung TEXT,
aktiv INTEGER NOT NULL DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (anbieter_id) REFERENCES anbieter (id),
UNIQUE(anbieter_id, nummer)
)`);
// Tarife
db.run(`CREATE TABLE IF NOT EXISTS tarife (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
anbieter_id INTEGER,
vo_id INTEGER,
provision_euro DECIMAL(10,2) DEFAULT 0,
mitarbeiter_provision DECIMAL(10,2) DEFAULT 0,
beschreibung TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (anbieter_id) REFERENCES anbieter (id),
FOREIGN KEY (vo_id) REFERENCES vos (id),
UNIQUE(name, anbieter_id)
)`);
addColumnIfMissing(db, 'tarife', 'mitarbeiter_provision DECIMAL(10,2) DEFAULT 0');
addColumnIfMissing(db, 'tarife', 'vo_id INTEGER');
// Tariff-specific commission rules. basis_euro: base amount for percent calculation.
db.run(`CREATE TABLE IF NOT EXISTS provisionsregeln (
@@ -124,7 +140,9 @@ function initDatabase() {
)`);
addColumnIfMissing(db, 'anbieter_provisionsregeln', 'basis_euro DECIMAL(10,2)');
// Schaltungen
// Schaltungen. rufnummer is nullable on new installs (the application
// also accepts NULL on existing DBs with NOT NULL columns since we just
// pass empty string when missing). vo_id captures which VO was used.
db.run(`CREATE TABLE IF NOT EXISTS schaltungen (
id INTEGER PRIMARY KEY AUTOINCREMENT,
schaltungs_id TEXT NOT NULL UNIQUE,
@@ -133,8 +151,9 @@ function initDatabase() {
vorname TEXT NOT NULL,
nachname TEXT NOT NULL,
firma TEXT,
rufnummer TEXT NOT NULL,
rufnummer TEXT,
anbieter_id INTEGER NOT NULL,
vo_id INTEGER,
tarif_id INTEGER NOT NULL,
hardware TEXT,
hardware_ek DECIMAL(10,2),
@@ -145,6 +164,7 @@ function initDatabase() {
)`);
addColumnIfMissing(db, 'schaltungen', 'hardware_ek DECIMAL(10,2)');
addColumnIfMissing(db, 'schaltungen', 'kundenverkaufspreis_netto DECIMAL(10,2)');
addColumnIfMissing(db, 'schaltungen', 'vo_id INTEGER');
// Dokumente (note: ON DELETE CASCADE only applied to new DBs - existing DBs
// handled via explicit DELETE in server.js)