From 699926cd76407be941c796960c17378eef9a79d6 Mon Sep 17 00:00:00 2001
From: rene
Date: Fri, 15 May 2026 12:06:05 +0200
Subject: [PATCH 1/3] Fix: Rechnung-Hinweistext auf AGB-konforme
Jahresbeitrags-Notiz umgestellt
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Alle drei Rechnungs-Einstiegspunkte (Admin-Upgrade-Button, automatische
Verlängerung via Scheduler, manuelles Upgrade via admin.py) erhalten jetzt
den einheitlichen Hinweis zum Jahresbeitrag gem. AGB ohne Rückerstattung.
---
backend/routes/admin.py | 2 +-
backend/scheduler.py | 2 +-
backend/static/js/pages/admin.js | 1 +
3 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/backend/routes/admin.py b/backend/routes/admin.py
index 4972bb6..c0ef2b7 100644
--- a/backend/routes/admin.py
+++ b/backend/routes/admin.py
@@ -1312,7 +1312,7 @@ async def _handle_upgrade_invoices(req: dict, new_tier_label: str):
""", (
inv_number, req["user_id"], req["name"], req["email"], billing_address,
description, period, price, price, price,
- f"Automatisch bei Upgrade von {req.get('old_tier','Standard')} auf {new_tier_label}.",
+ f"Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen. (Upgrade von {req.get('old_tier','Standard')} auf {new_tier_label})",
))
invoice_id = conn.execute("SELECT last_insert_rowid()").fetchone()[0]
conn.execute(
diff --git a/backend/scheduler.py b/backend/scheduler.py
index 8a7b24d..edb9963 100644
--- a/backend/scheduler.py
+++ b/backend/scheduler.py
@@ -258,7 +258,7 @@ async def _create_renewal_invoice_draft(user: dict, expires: date, tier_label: s
invoice_number, user["id"], user["name"], user["email"], billing_address,
description, period,
price, price, price,
- f"Automatisch erstellt — Abo läuft am {expires.strftime('%d.%m.%Y')} ab.",
+ f"Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen. (Automatisch erstellt, Ablauf: {expires.strftime('%d.%m.%Y')})",
))
conn.execute(
"INSERT INTO invoice_items (invoice_id, description, quantity, unit_price, total) VALUES (?,?,1,?,?)",
diff --git a/backend/static/js/pages/admin.js b/backend/static/js/pages/admin.js
index 46074b9..3a898ae 100644
--- a/backend/static/js/pages/admin.js
+++ b/backend/static/js/pages/admin.js
@@ -3645,6 +3645,7 @@ window.Page_admin = (() => {
recipient_address: address || '',
service_period: _period,
items: [{ description: tierItem.description, quantity: 1, unit_price: tierItem.unit_price }],
+ notes: 'Jahresbeitrag gem. AGB. Bei vorzeitiger Kündigung keine anteilige Rückerstattung; Zugang bleibt bis Laufzeitende bestehen.',
});
});
});
From 9a7f1008552fc654ae8468c24c38ba7d0043a9ab Mon Sep 17 00:00:00 2001
From: rene
Date: Fri, 15 May 2026 12:06:14 +0200
Subject: [PATCH 2/3] Legal: Widerrufs-Checkbox im Upgrade-Modal +
AGB-Abschnitt in Datenschutz
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Upgrade-Modal: Checkbox §356 Abs.4 BGB muss aktiv bestätigt werden,
"Anfrage senden" bleibt bis dahin deaktiviert
- Akzeptanz-Zeitstempel wird mit der upgradeRequest-Message mitgeschickt
- datenschutz.js: neuer Abschnitt "Abonnement & Kündigung" mit Laufzeit,
Verlängerung, Zahlung, Kündigung, Erstattung und Widerrufsrecht
---
backend/static/js/pages/datenschutz.js | 30 ++++++++++++++++++++++++++
backend/static/js/pages/settings.js | 22 ++++++++++++++++++-
2 files changed, 51 insertions(+), 1 deletion(-)
diff --git a/backend/static/js/pages/datenschutz.js b/backend/static/js/pages/datenschutz.js
index 425536f..368010c 100644
--- a/backend/static/js/pages/datenschutz.js
+++ b/backend/static/js/pages/datenschutz.js
@@ -238,6 +238,36 @@ window.Page_datenschutz = (() => {
style="${S.a}">www.lda.bayern.de
`)}
+ ${sec('Abonnement & Kündigung', `
+
+ Ban Yaro Pro und das Züchter-Paket sind Jahresabonnements mit einer Laufzeit von
+ 12 Monaten ab Freischaltung.
+
+
+ Laufzeit & Verlängerung: Das Abonnement läuft 12 Monate ab dem
+ Tag der Freischaltung. Nach Ablauf verlängert es sich auf unbestimmte Zeit mit einer
+ Kündigungsfrist von einem Monat zum Monatsende, sofern nicht vorher gekündigt wird
+ (§ 309 Nr. 9 BGB).
+
+
+ Zahlung: Der Jahresbeitrag (29 EUR für Pro, 49 EUR für
+ Züchter) wird einmalig für die gesamte Laufzeit im Voraus fällig.
+
+
+ Kündigung: Die Kündigung kann jederzeit in den Einstellungen der App
+ erfolgen. Der Zugang bleibt bis zum Ende der bezahlten Laufzeit erhalten.
+
+
+ Erstattung: Bei vorzeitiger Kündigung durch den Nutzer erfolgt keine
+ anteilige Rückerstattung des Jahresbeitrags. Der Zugang bleibt bis zum Ende der
+ bezahlten Laufzeit vollständig bestehen.
+
+
+ Widerrufsrecht: Da die Nutzung sofort nach Freischaltung beginnt und
+ der Nutzer dem ausdrücklich zustimmt, erlischt das 14-tägige Widerrufsrecht gemäß
+ § 356 Abs. 4 BGB mit Beginn der Nutzung.
+
`)}
+
${sec('Speicherdauer', `
Deine Daten werden vollständig gelöscht, sobald du deinen Account löschst —
diff --git a/backend/static/js/pages/settings.js b/backend/static/js/pages/settings.js
index 3cc8ac9..2cb9b49 100644
--- a/backend/static/js/pages/settings.js
+++ b/backend/static/js/pages/settings.js
@@ -313,6 +313,18 @@ window.Page_settings = (() => {
font-size:var(--text-xs);color:#c05000;line-height:1.6;margin-top:var(--space-2)">
💡 Tipp: Trag deine Rechnungsadresse im Profil ein — dann können wir die Rechnung vollständig ausstellen.
` : ''}
+
+
+
${breederForm}
`,
footer: `
@@ -330,6 +342,13 @@ window.Page_settings = (() => {
`
});
+ const widerrufBox = document.getElementById('widerruf-checkbox');
+ const sendBtn = document.getElementById('upgrade-request-send-btn');
+ if (sendBtn) sendBtn.disabled = true;
+ widerrufBox?.addEventListener('change', () => {
+ if (sendBtn) sendBtn.disabled = !widerrufBox.checked;
+ });
+
document.getElementById('upgrade-request-send-btn')?.addEventListener('click', async () => {
const btn = document.getElementById('upgrade-request-send-btn');
if (!btn) return;
@@ -363,7 +382,8 @@ window.Page_settings = (() => {
}
try {
- const res = await API.auth.upgradeRequest(tier);
+ const widerrufAt = new Date().toLocaleString('de-DE');
+ const res = await API.auth.upgradeRequest(tier, `[Widerrufsrecht akzeptiert am ${widerrufAt}]`);
UI.modal.close();
if (res.already) {
UI.toast.info('Deine Anfrage liegt bereits vor — wir melden uns bald.');
From db4d5cb1b6593cc2add7b3acf8b2b413d53c0696 Mon Sep 17 00:00:00 2001
From: rene
Date: Fri, 15 May 2026 12:07:13 +0200
Subject: [PATCH 3/3] Legal: Widerrufs-Checkbox, AGB-Abschnitt, Rechnungsnotiz
(SW by-v970)
---
backend/main.py | 2 +-
backend/static/js/app.js | 2 +-
backend/static/sw.js | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/backend/main.py b/backend/main.py
index 77526ee..b460a2b 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -408,7 +408,7 @@ async def serve_media(path: str, request: _Request):
raise _HE(404, "Nicht gefunden.")
return _media_response(filepath)
-APP_VER = "969" # muss mit APP_VER in app.js übereinstimmen
+APP_VER = "970" # muss mit APP_VER in app.js übereinstimmen
@app.get("/.well-known/assetlinks.json")
async def assetlinks():
diff --git a/backend/static/js/app.js b/backend/static/js/app.js
index 6082558..467c710 100644
--- a/backend/static/js/app.js
+++ b/backend/static/js/app.js
@@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
-const APP_VER = '969'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
+const APP_VER = '970'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VERSION = '1.6.0'; // ← semantische Version, wird bei make release gesetzt
const IS_STAGING = location.hostname === 'staging.banyaro.app';
// Cache-Bust-Parameter nach Update-Reload sofort entfernen
diff --git a/backend/static/sw.js b/backend/static/sw.js
index f814166..baa576f 100644
--- a/backend/static/sw.js
+++ b/backend/static/sw.js
@@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
-const CACHE_VERSION = 'by-v969';
+const CACHE_VERSION = 'by-v970';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten
const CACHE_API = 'ban-yaro-api-v1'; // API-Response-Cache