AGB-Kopie: Quelle docs/rechtliches/agb.md wortgleich mit der Website (Fassung 2026-09-30), Tests gegen AGB.tsx
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
445d234695
commit
edccd4d8af
2 files changed
+226
No files matched your search
@@ -0,0 +1,149 @@
|
||||
# Allgemeine Geschäftsbedingungen
|
||||
|
||||
**Fassung 2026-09-30** · Wortgleiche Kopie der Allgemeinen Geschäftsbedingungen auf webklar.com/agb
|
||||
|
||||
## Kaufvertrag – WEBklar (Modularer Projektvertrag)
|
||||
|
||||
**zwischen**
|
||||
|
||||
WEBklar (im Folgenden „Anbieter“)
|
||||
|
||||
**und**
|
||||
|
||||
Kunde laut Angebot (im Folgenden „Kunde“)
|
||||
|
||||
## 1. Vertragsgegenstand
|
||||
|
||||
(1) Gegenstand dieses Vertrages ist die Erbringung der im Angebot definierten Leistungen.
|
||||
|
||||
(2) Der Vertrag besteht aus diesem Grundvertrag sowie den ausgewählten Leistungsmodulen.
|
||||
|
||||
(3) Maßgeblich ist das jeweils angenommene Angebot von WEBklar.
|
||||
|
||||
## 2. Leistungsart
|
||||
|
||||
(1) Sämtliche Leistungen von WEBklar stellen Dienst- und Entwicklungsleistungen dar.
|
||||
|
||||
(2) Ein bestimmter wirtschaftlicher, technischer oder rechtlicher Erfolg wird nicht geschuldet.
|
||||
|
||||
(3) WEBklar erbringt keinen laufenden Betrieb, sofern dieser nicht explizit vereinbart wurde.
|
||||
|
||||
## 3. Leistungsmodul A – Webseite (einmalig)
|
||||
|
||||
(aktiv, wenn im Angebot enthalten)
|
||||
|
||||
(1) WEBklar erstellt eine individuelle Webseite gemäß Angebot.
|
||||
|
||||
(2) Die Umsetzung erfolgt nach den vom Kunden gelieferten Inhalten und Vorgaben.
|
||||
|
||||
(3) Zusätzliche Leistungen wie Domain, Hosting, Wartung oder SEO sind nicht Bestandteil, sofern sie nicht gesondert beauftragt wurden.
|
||||
|
||||
(4) Der Kunde ist nicht berechtigt, Änderungen am Quellcode selbst vorzunehmen.
|
||||
|
||||
(5) Änderungen erfolgen ausschließlich durch WEBklar gegen gesonderte Vergütung.
|
||||
|
||||
## 4. Leistungsmodul B – Automatisierung / Virtualisierung (einmalig)
|
||||
|
||||
(aktiv, wenn im Angebot enthalten)
|
||||
|
||||
(1) WEBklar entwickelt individuelle Automatisierungen, Apps oder Virtualisierungssysteme.
|
||||
|
||||
(2) Die Leistung stellt eine reine Entwicklungsleistung dar.
|
||||
|
||||
(3) Optional kann eine Beratungsleistung Bestandteil des Projektes sein.
|
||||
|
||||
(4) Ein laufender Betrieb, Monitoring oder Wartung ist nicht geschuldet, außer dies wurde explizit vereinbart.
|
||||
|
||||
(5) Der Kunde entscheidet über Inhalte, Daten und Prozesse und trägt dafür die rechtliche Verantwortung.
|
||||
|
||||
## 5. Leistungsmodul C – Hosting
|
||||
|
||||
(aktiv, wenn im Angebot enthalten)
|
||||
|
||||
(1) Hosting bietet WEBklar als Hosting-Abo an. Für das Hosting-Abo und das Wartungs-Abo gelten die Vertragsbedingungen für die WEBklar-Abos mit Leistungsbeschreibungen; sie gehen diesen AGB vor.
|
||||
|
||||
(2) Bestehende Hosting-Verträge mit jährlicher Laufzeit behalten die bei ihrem Abschluss vereinbarten Bedingungen. Abweichend davon können Verbraucher solche Verträge nach einer automatischen Verlängerung jederzeit mit einer Frist von einem Monat kündigen.
|
||||
|
||||
(3) WEBklar ist berechtigt, externe Anbieter (z. B. Rechenzentren) einzusetzen.
|
||||
|
||||
## 6. Quellcode und Eigentum
|
||||
|
||||
### 6.1 Webseite
|
||||
|
||||
(1) Der Kunde erhält ein einfaches, zeitlich unbegrenztes Nutzungsrecht an der fertigen Webseite.
|
||||
|
||||
(2) Der Quellcode der Webseite wird nur auf ausdrückliche Anfrage und nach Vereinbarung herausgegeben.
|
||||
|
||||
(3) Ohne Vereinbarung verbleibt der Quellcode bei WEBklar und wird archiviert.
|
||||
|
||||
### 6.2 Apps, Automatisierungen und Backend
|
||||
|
||||
(1) Der Quellcode von Apps, Automatisierungen und Backend-Systemen verbleibt vollständig bei WEBklar.
|
||||
|
||||
(2) Eine Herausgabe erfolgt ausschließlich nach gesonderter schriftlicher Vereinbarung.
|
||||
|
||||
(3) Der Kunde erhält lediglich Zugriff auf die Bedienoberfläche bzw. das Frontend.
|
||||
|
||||
## 7. Mitwirkungspflichten des Kunden
|
||||
|
||||
(1) Der Kunde stellt alle benötigten Inhalte, Daten und Freigaben rechtzeitig bereit.
|
||||
|
||||
(2) Verzögerungen durch fehlende Mitwirkung gehen nicht zu Lasten von WEBklar.
|
||||
|
||||
(3) WEBklar ist nicht verpflichtet, rechtliche Prüfungen der Inhalte vorzunehmen.
|
||||
|
||||
## 8. Abnahme
|
||||
|
||||
(1) Nach Fertigstellung wird dem Kunden die Leistung zur Abnahme bereitgestellt.
|
||||
|
||||
(2) Erfolgt innerhalb von 14 Tagen keine Rückmeldung, gilt die Leistung als abgenommen.
|
||||
|
||||
(3) Nach Abnahme sind nur noch kostenpflichtige Änderungen möglich.
|
||||
|
||||
## 9. Vergütung und Zahlung
|
||||
|
||||
(1) Die Vergütung ergibt sich aus dem Angebot.
|
||||
|
||||
(2) Wird nach Aufwand abgerechnet, erfasst WEBklar die Arbeitszeit je Arbeitsschritt in Schritten von 15 Minuten; angefangene 15 Minuten werden aufgerundet. Wirkt eine weitere Person unterstützend mit, rechnet WEBklar für sie die Hälfte der Arbeitszeit des Arbeitsschritts an, aufgerundet auf volle 15 Minuten; bei mehreren unterstützenden Personen gilt das für jede. Jeder Arbeitsschritt und jede Unterstützung ist im Kundenportal einsehbar.
|
||||
|
||||
(3) Projektleistungen sind nach Vereinbarung fällig.
|
||||
|
||||
(4) Für die Abos gelten die Vertragsbedingungen für die WEBklar-Abos.
|
||||
|
||||
## 10. Zahlungsverzug
|
||||
|
||||
(1) Bei Zahlungsverzug erfolgen bis zu zwei Mahnungen.
|
||||
|
||||
(2) Danach ist WEBklar berechtigt:
|
||||
|
||||
- Leistungen zu sperren
|
||||
- Verzugszinsen zu berechnen
|
||||
- den Vertrag außerordentlich zu kündigen
|
||||
|
||||
## 11. Haftung
|
||||
|
||||
(1) WEBklar haftet unbeschränkt bei Vorsatz und grober Fahrlässigkeit, bei Verletzung von Leben, Körper oder Gesundheit, nach dem Produkthaftungsgesetz und soweit WEBklar eine Garantie übernommen hat.
|
||||
|
||||
(2) Bei leicht fahrlässiger Verletzung einer wesentlichen Vertragspflicht haftet WEBklar nur für den vertragstypischen, vorhersehbaren Schaden. Wesentlich sind Pflichten, deren Erfüllung die ordnungsgemäße Durchführung des Vertrags überhaupt erst ermöglicht und auf deren Einhaltung der Kunde regelmäßig vertrauen darf. Im Übrigen ist die Haftung für leichte Fahrlässigkeit ausgeschlossen.
|
||||
|
||||
(3) Der Kunde bewahrt Kopien der Inhalte und Daten auf, die er WEBklar übergibt oder selbst einpflegt. Für den Verlust solcher Inhalte und Daten haftet WEBklar außer in den Fällen von Absatz 1 nur in Höhe des Aufwands, der für ihre Wiederherstellung nötig gewesen wäre, wenn der Kunde diese Kopien aufbewahrt hätte.
|
||||
|
||||
(4) Diese Regeln gelten auch zugunsten der Mitarbeiter und Beauftragten von WEBklar.
|
||||
|
||||
## 12. Kündigung
|
||||
|
||||
(1) Laufzeitverträge können zum Ende der jeweiligen Laufzeit gekündigt werden. Laufzeit und Kündigung der Abos richten sich nach den Vertragsbedingungen für die WEBklar-Abos.
|
||||
|
||||
(2) Das Recht zur außerordentlichen Kündigung bleibt unberührt.
|
||||
|
||||
## 13. Referenzen
|
||||
|
||||
WEBklar darf das Projekt nur nach ausdrücklicher Zustimmung des Kunden als Referenz verwenden.
|
||||
|
||||
## 14. Schlussbestimmungen
|
||||
|
||||
(1) Es gilt deutsches Recht.
|
||||
|
||||
(2) Gerichtsstand ist der Sitz von WEBklar, soweit zulässig.
|
||||
|
||||
(3) Sollten einzelne Bestimmungen unwirksam sein, bleibt der Vertrag im Übrigen wirksam.
|
||||
@@ -349,3 +349,80 @@ test('belehrung_gleich_funktionen: Belehrung und Formular sind wörtlich die Bel
|
||||
assert.equal(absaetze.join('\n\n'), widerrufsbelehrungFunktion())
|
||||
assert.deepEqual(B.abschnitte.map((a) => a.ueberschrift), ['Widerrufsrecht', 'Folgen des Widerrufs'])
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------------------------------------- AGB-Kopie
|
||||
|
||||
const AGB_MD = 'docs/rechtliches/agb.md'
|
||||
// Quelle der Website-AGB auf dem Server (Repository webklar.com). Fehlt die Datei (anderer Rechner, Container), entfällt nur der Vergleich mit der Website.
|
||||
const AGB_WEBSITE = process.env.WEBKLAR_AGB_QUELLE || '/home/webklar/apps/webklar.com/build/src/pages/AGB.tsx'
|
||||
// SHA-256 des normierten Klartexts der Website-AGB vom 30.09.2026 (Commit 72f278b der Website; Titelblock und Nr. 1 bis 14). Ändert sich der Text der Kopie,
|
||||
// braucht sie eine neue Fassung: AGB_FASSUNG hochziehen, agb.md ändern, Seite und PDF neu erzeugen, diesen Hash eintragen.
|
||||
const AGB_TEXT_SHA256 = 'f3374352ef97e6050bb0908c1b4abd806f759851b5b85fa9d030512d46b36659'
|
||||
|
||||
/** Textknoten der Kopie: alles nach der Fassungszeile, ohne Überschriftszeichen, Aufzählungszeichen, Absatznummern „(1)“ und Fettdruck. */
|
||||
function agbKnotenAusMarkdown(md) {
|
||||
const zeilen = md.replace(/\r/g, '').split('\n')
|
||||
const start = zeilen.findIndex((z) => z.startsWith('**Fassung '))
|
||||
assert.ok(start > 0, 'agb.md: Fassungszeile fehlt')
|
||||
return zeilen
|
||||
.slice(start + 1)
|
||||
.filter((z) => z.trim() !== '')
|
||||
.map((z) => z.replace(/^#{1,3} /, '').replace(/^- /, '').replace(/^\(\d+\) /, '').replace(/\*\*/g, '').replace(/\s+/g, ' ').trim())
|
||||
}
|
||||
|
||||
/** Textknoten der Website-Seite AGB.tsx: vom Seitentitel bis zum Ende des Vertragstexts, ohne JSX-Kommentare und Markup. */
|
||||
function agbKnotenAusWebsite(tsx) {
|
||||
const von = tsx.indexOf('<h1 className="wk-seite__titel">')
|
||||
const bis = tsx.indexOf('</article>')
|
||||
assert.ok(von > 0 && bis > von, 'AGB.tsx: Aufbau hat sich geändert (Seitentitel oder article nicht gefunden)')
|
||||
return tsx
|
||||
.slice(von, bis)
|
||||
.replace(/\{\/\*[\s\S]*?\*\/\}/g, ' ')
|
||||
.replace(/\{" "\}/g, ' ')
|
||||
.replace(/<[^>]*>/g, '\n')
|
||||
.split('\n')
|
||||
.map((z) => z.replace(/\s+/g, ' ').trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
const agbText = (knoten) => norm(knoten.join(' '))
|
||||
/** Die erste Stelle, an der zwei Texte auseinanderlaufen, mit etwas Umgebung (für die Fehlermeldung). */
|
||||
const ersterUnterschied = (a, b) => {
|
||||
let i = 0
|
||||
while (i < a.length && i < b.length && a[i] === b[i]) i += 1
|
||||
return `Kopie: „…${a.slice(Math.max(0, i - 40), i + 60)}…“, Website: „…${b.slice(Math.max(0, i - 40), i + 60)}…“`
|
||||
}
|
||||
|
||||
test('agb_fassung: AGB_FASSUNG ist der 30.09.2026, der Tag der letzten Textänderung der Website-AGB (Commit 72f278b); die Kopie trägt dieselbe Fassung', () => {
|
||||
assert.equal(AGB_FASSUNG, '2026-09-30')
|
||||
assert.ok(lies(AGB_MD).startsWith(`# Allgemeine Geschäftsbedingungen\n\n**Fassung ${AGB_FASSUNG}**`))
|
||||
})
|
||||
|
||||
test('agb_md_aufbau: vierzehn Abschnitte in Reihenfolge; die Verweise der Auftragsbestätigung auf Nr. 7, 8 und 9 treffen', () => {
|
||||
const md = lies(AGB_MD)
|
||||
const abschnitte = md.split('\n').filter((z) => /^## \d+\. /.test(z)).map((z) => z.replace(/^## /, ''))
|
||||
assert.deepEqual(abschnitte.map((z) => Number.parseInt(z, 10)), Array.from({ length: 14 }, (_, i) => i + 1))
|
||||
const titel = Object.fromEntries(abschnitte.map((z) => [Number.parseInt(z, 10), z.replace(/^\d+\. /, '')]))
|
||||
assert.equal(titel[7], 'Mitwirkungspflichten des Kunden')
|
||||
assert.equal(titel[8], 'Abnahme')
|
||||
assert.equal(titel[9], 'Vergütung und Zahlung')
|
||||
assert.ok(T.mitwirkung.includes('Nr. 7 und Nr. 8'))
|
||||
assert.ok(T.aufwand({ satz: '100,00 €' }).includes('(AGB Nr. 9)'))
|
||||
const nr9 = md.slice(md.indexOf('## 9. '), md.indexOf('## 10. '))
|
||||
assert.ok(nr9.includes('in Schritten von 15 Minuten') && nr9.includes('die Hälfte der Arbeitszeit'), 'Nr. 9 enthält die Regel, auf die die Auftragsbestätigung verweist')
|
||||
// der Titelblock der Website steht als Überschrift und Parteien, die Verweise auf die Abos bleiben Text
|
||||
assert.ok(md.includes('## Kaufvertrag – WEBklar (Modularer Projektvertrag)'))
|
||||
assert.ok(md.includes('**zwischen**') && md.includes('**und**'))
|
||||
})
|
||||
|
||||
test('agb_text_pin: der normierte Text der Kopie hat den festgehaltenen SHA-256 (keine versehentliche Änderung eines Rechtstextes)', () => {
|
||||
const hash = sha256(agbText(agbKnotenAusMarkdown(lies(AGB_MD))))
|
||||
assert.equal(hash, AGB_TEXT_SHA256, `Der Text der AGB-Kopie hat sich geändert (Hash jetzt ${hash}). Änderungen nur mit neuer Fassung: AGB_FASSUNG hochziehen, Seite und PDF neu erzeugen.`)
|
||||
})
|
||||
|
||||
test('agb_text_gleich_website: die Kopie ist wortgleich mit der Seite AGB.tsx der Website (Titelblock und Nr. 1 bis 14)', (t) => {
|
||||
if (!existsSync(AGB_WEBSITE)) return t.skip(`Website-AGB nicht erreichbar: ${AGB_WEBSITE}`)
|
||||
const website = agbText(agbKnotenAusWebsite(readFileSync(AGB_WEBSITE, 'utf8')))
|
||||
const kopie = agbText(agbKnotenAusMarkdown(lies(AGB_MD)))
|
||||
assert.ok(kopie === website, `Die AGB-Kopie weicht von den AGB der Website ab (${ersterUnterschied(kopie, website)}). Ist die Website neu, braucht die Kopie eine neue Fassung (AGB_FASSUNG, agb.md, Seite, PDF, Hash).`)
|
||||
assert.equal(sha256(website), AGB_TEXT_SHA256)
|
||||
})
|
||||
Reference in new issue
Block a user