Files
geograsim/App/Don_t_Deploy/2026-08-26-lizenz-vertrieb.sql
T
Adminator 96a73e22b2 Lizenz-Vertrieb: Chargen-Vergabe, Storno, Verlaengerung, Mail, Einloeselink
Erweitert das Lizenzmodul um eine Vertriebs-/Vergabe-Schicht ueber dem
bestehenden Einloese-Flow (non-breaking, alle Alt-Codes + generate/redeem
/assign unveraendert).

Schema (Migration 2026-08-26-lizenz-vertrieb.sql, additiv):
- license_customers (Besteller: Kundennummer/Email/Name)
- license_batches (Bestellung/Charge: externe Bestellnummer, Typ, Menge,
  Gueltigkeit, Sammel-Einloesetoken, Storno)
- licenses += batch_id, issued_at, valid_from/until (pro Code -> Verlaengerung),
  canceled_at/reason; code auf VARCHAR(32) (laengere Codes)
- license_mails (Mail-Archiv, unabh. vom IMAP), license_redeem_attempts
  (Brute-Force-Schutz), license_events (Audit-Log)

Backend (php/api/licenses.php):
- issue_batch: N frische 100-Bit-Codes in Transaktion, sofort vergeben,
  an Kunde/Bestellung/Gueltigkeit gebunden; sticky Default Gueltig-bis
- Uebersicht (view=batches mit Aggregat ausgegeben/storniert/netto/eingeloest
  /abgelaufen), batch_detail, Kunden-Autocomplete, sales_default
- cancel_batch/cancel_license, extend_batch/extend_license
- send_batch_mail (Server-Treiber via Mailer, archiviert, Anhang bei >40)
- redeem_charge (Sammel-Einloeselink)
- Brute-Force-Bremse + Audit + Storno-Ablehnung im redeem/redeem_batch

UI (admin-licenses.html): Ausgabe-Formular (Bestaetigung bei Grossmengen),
Ergebnis mit Copy/Download(txt+csv)/Mail-Composer/Sammellink, Bestellungs-
Uebersicht mit Filtern + Detail-Modal (Storno/Verlaengern/Verlauf/Mails).

Einloese-Landingpage (einloesen.html + pages/einloesen.php): ?charge=Token
(alle auf einmal) und ?code=CODE, mit Lehrer-Login-Gate.

Lib: Database Transaktionen; Mailer sendWithAttachments + renderPlainBody.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-26 01:39:14 +02:00

104 lines
5.2 KiB
SQL

-- =====================================================================
-- Lizenz-Vergabe / Vertrieb (2026-08-26)
-- Additiv & non-breaking: bestehende `licenses`-Zeilen (batch_id NULL) und
-- die Flows generate/redeem/assign bleiben unveraendert funktionsfaehig.
-- Neu: Kunden, Chargen (Bestellungen), Mail-Archiv, Storno, Verlaengerung,
-- Brute-Force-Schutz + Audit-Log, laengere Codes, Charge-Einloeselink.
-- =====================================================================
-- 1) Kunden (Besteller) ------------------------------------------------
CREATE TABLE IF NOT EXISTS license_customers (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_no VARCHAR(40) NULL, -- INTERNE Kundennummer (optional)
email VARCHAR(190) NULL,
name VARCHAR(190) NULL, -- Besteller / Verlag / Schule
note VARCHAR(255) NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_customer_no (customer_no),
KEY idx_email (email),
KEY idx_name (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 2) Chargen (Bestellungen) -------------------------------------------
CREATE TABLE IF NOT EXISTS license_batches (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
customer_id INT UNSIGNED NULL,
order_no VARCHAR(120) NULL, -- EXTERNE Bestellnummer (Marktplatz/Ministerium), Freitext -> geht in die Mail
sales_type VARCHAR(30) NOT NULL DEFAULT 'privat', -- marktplatz | direktvertrieb | privat (spaeter erweiterbar)
qty INT UNSIGNED NOT NULL DEFAULT 0,
default_valid_from DATE NULL,
default_valid_until DATE NULL,
note VARCHAR(255) NULL,
redeem_token CHAR(40) NULL, -- Sammel-Einloeselink der ganzen Charge
created_by INT UNSIGNED NULL, -- admin_users.id
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
canceled_at TIMESTAMP NULL,
canceled_reason VARCHAR(255) NULL,
UNIQUE KEY uq_redeem_token (redeem_token),
KEY idx_customer (customer_id),
KEY idx_order (order_no),
KEY idx_type (sales_type),
KEY idx_created (created_at),
CONSTRAINT fk_batch_customer FOREIGN KEY (customer_id) REFERENCES license_customers(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 3) licenses erweitern ------------------------------------------------
-- code auf 32 Zeichen verbreitern (laengere Codes), Vertriebsfelder ergaenzen.
ALTER TABLE licenses
MODIFY COLUMN code VARCHAR(32) NOT NULL,
ADD COLUMN batch_id INT UNSIGNED NULL AFTER school_year,
ADD COLUMN issued_at TIMESTAMP NULL AFTER redeemed_at,
ADD COLUMN valid_from DATE NULL AFTER issued_at,
ADD COLUMN valid_until DATE NULL AFTER valid_from,
ADD COLUMN canceled_at TIMESTAMP NULL AFTER valid_until,
ADD COLUMN canceled_reason VARCHAR(255) NULL AFTER canceled_at,
ADD KEY idx_batch (batch_id),
ADD KEY idx_valid_until (valid_until),
ADD KEY idx_issued (issued_at),
ADD CONSTRAINT fk_license_batch FOREIGN KEY (batch_id) REFERENCES license_batches(id) ON DELETE SET NULL;
-- 4) Mail-Archiv / Outbox (unabhaengig vom IMAP-Postausgang) -----------
CREATE TABLE IF NOT EXISTS license_mails (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
batch_id INT UNSIGNED NULL,
to_email VARCHAR(190) NOT NULL,
subject VARCHAR(255) NOT NULL,
body_text MEDIUMTEXT NULL, -- gerenderter Klartext (kopierbar + Archiv)
body_html MEDIUMTEXT NULL,
provider VARCHAR(20) NOT NULL DEFAULT 'server', -- server | ses
status VARCHAR(12) NOT NULL DEFAULT 'queued', -- queued | sent | failed
error VARCHAR(255) NULL,
created_by INT UNSIGNED NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
sent_at TIMESTAMP NULL,
KEY idx_batch (batch_id),
KEY idx_status (status),
CONSTRAINT fk_mail_batch FOREIGN KEY (batch_id) REFERENCES license_batches(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 5) Brute-Force-Schutz + Audit beim Einloesen -------------------------
CREATE TABLE IF NOT EXISTS license_redeem_attempts (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
ip VARBINARY(16) NULL, -- INET6_ATON(...)
teacher_id INT UNSIGNED NULL,
code_tried VARCHAR(32) NULL,
ok TINYINT(1) NOT NULL DEFAULT 0,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_ip_time (ip, created_at),
KEY idx_teacher_time (teacher_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- 6) Audit-Log fuer Chargen/Codes -------------------------------------
CREATE TABLE IF NOT EXISTS license_events (
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
batch_id INT UNSIGNED NULL,
license_id INT UNSIGNED NULL,
event VARCHAR(30) NOT NULL, -- issued|extended|canceled|mailed|redeemed
detail VARCHAR(255) NULL,
admin_id INT UNSIGNED NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
KEY idx_batch (batch_id),
KEY idx_license (license_id),
KEY idx_event (event)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;