- E-Mail-Aenderung durch Berater wird erst nach Bestaetigung durch den Kunden
aktiv (Link an die neue Adresse, Portal kunde/email-bestaetigen); danach
ziehen alle Eintraege des Kunden und seine Abos mit. Admins aendern direkt.
- Kundenhoheit: E-Mail eines Beraterkontos oder eines Kunden eines anderen
Beraters wird abgelehnt (beim Speichern und bei der Bestaetigung).
- Fix CustomerPriority::entryExists: aelteste gepruefte Zuordnung gilt,
Eintraege in Pruefung (is_like) dienen nicht mehr als Vorlage.
- "Berater zuweisen" voreingestellt auf alle Bestellungen des Kunden.
- Neue Admin-Seite Kundenhoheit: offene Pruefungen, Kunden bei mehreren
Beratern, abgelehnte/offene E-Mail-Aenderungen; Zaehler im Menue.
- Neue Tabelle shopping_user_email_changes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Abschnitt V (DE/EN/ES/FR): neuer Absatz zum Schutz von Kontaktformular
und Registrierung mit Cloudflare Turnstile (Daten, Zweck, Art. 6 Abs. 1
lit. f DSGVO, USA-Uebermittlung) sowie Rate-Limit und 30-Tage-Protokoll
abgewiesener Anfragen
- Stand auf 09.10.2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kontaktformular: keine Mail mehr an die eingegebene Adresse (wurde als
Spam-Versender missbraucht); Anfrage nur an Kontakt-Adresse + Berater (BCC),
Reply-To = Absender; Eingaben in der Mail escaped
- Kontakt + Registrierung: Cloudflare Turnstile statt reCAPTCHA (Schluessel
aus dem Code in die .env), Rate-Limits, Honeypot + Mindest-Ausfuellzeit,
Namensregel (lateinische Schrift, max. 50 Zeichen), Form Requests
- Registrierung: Partner-Nummer nur fuer aktive Berater (vorher ungeprueft)
- CRM: POST /register abgeschaltet (legte ohne Captcha Benutzer an)
- Passwort-Mindestlaenge systemweit 8 Zeichen ueber Password::defaults()
- Log abgefangener Anfragen: storage/logs/form-spam-*.log
- Doku und Protokoll: dev/2026-10-09-spam/README.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 50 EUR je Neupartner bleiben; die Top 3 (500/300/200 EUR) ermittelt
jetzt der Umsatz der Neupartner (wie Montenegro, ohne Einmalpunkte/Abos)
- Gleichstand bei Umsatzwertung: vorn liegt, wessen letzte gezaehlte
Umsatzbuchung frueher kam
- Challenge-Seite: Praemie und Top-3-Darstellung auch bei Umsatzwertung,
Ranglisten-Hinweis auf Umsatz (DE/EN/ES/FR)
- Seeder: neue Texte und Bedingungen (Rangliste, Steuerpflicht,
vergünstigter Einstieg bis 31.10.), gleicht Wertung beim Einspielen ab
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- page_script in invoice, delivery, credit und cancellation ans Ende des
body verschoben (wirkte nur auf Seite 1), Beschriftung uebersetzt
- Gutschriften rendern mit enable_php wie Rechnungen
- Lieferschein: Schluessel pdf.delivery_note_no ohne ueberzaehligen Punkt
- Tests: Position des Skripts, alle pdf.*-Schluessel in allen Sprachen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Patch-/Minor-Updates (guzzle, commonmark, flysystem, excel,
phpspreadsheet, phpseclib, laravel/framework 11.x)
- barryvdh/laravel-dompdf 3.1 (dompdf 3.1.6), Test fuer den PDF-Weg
inkl. eingebettetem PHP und Briefpapier
- Rueckport des Laravel-12.60-Fixes gegen CRLF in der email-Regel
- Protokoll: Ergebnis composer audit, offene Punkte Laravel 12 und
PDF-Seitenzahlen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- sentry/sentry-laravel 4.28; config/sentry.php gehaertet (keine PII,
keine Request-Bodys, before_send ErrorReportScrubber)
- Handler: Integration::captureUnhandledException, CommandNotFoundException
nicht melden
- ReportErrorSignals (global): toter interner Link (nach Pfad wegen
Catch-all-Route), 403/429 fuer Angemeldete, 419; gedrosselt
- Browser: selbst gehostetes SDK (esbuild-Bundle 11.5.0), Loader ohne
Build-Schritt und ohne Regex-Lookbehind, in 7 Layouts mit area-Tag
- phpunit.xml: DSNs und ntfy-URL leer, CACHE_DRIVER=array (Tests schrieben
bisher in den lokalen Datei-Cache)
- dev/glitch-tip/README.md mit Instanz, Org, Projekt-IDs, API-Auswertung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- dev/betrieb/2026-10-08-protokoll-umstellung.md: Ergebnis, Releases,
Aufraeumen ab 22.10.2026 (Server, Mac, Git-Server), offene Punkte
- Kernel: abo:send-execution-reminder ist bewusst aktiv (veralteten
Kommentar entfernt)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- deploy.sh bricht ab, wenn APP_ENV nicht production ist
- dev/app-bak (altes Code-Backup) geloescht
- README Abschnitt H: Historie neu geschrieben
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_ide_helper*, .phpstorm.meta.php werden lokal erzeugt (php artisan ide-helper:*).
Eine fruehere Version von _ide_helper war 10 GB gross.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
packages.php und services.php erzeugt Laravel selbst; nach composer install
--no-dev unterscheiden sie sich auf Live vom lokalen Stand (Dev-Pakete) und
blockierten deploy.sh. Anleitung: Fallstricke zu Admin-Uebersetzungen, SFTP
und erzeugten Dateien.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- HealthProbe: Datenbank, Cache, Queue (sync / database / Horizon),
fehlgeschlagene Jobs (24 h), Scheduler-Herzschlag, Plattenplatz
- /health auf jeder Domain, ohne web-Gruppe, nur mit MONITORING_HEALTH_TOKEN
- monitoring:heartbeat minuetlich im Scheduler
- Sysadmin -> Systemstatus (my.mivita.care/sysadmin/status) mit Version,
Umgebung und zuletzt fehlgeschlagenen Jobs
- Anleitung Abschnitt G
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Beim Umstieg auf Git-Deploy mit dem Live-Server abgeglichen:
- resources/lang/de/website.php (Text Online-Streitbeilegung) und
resources/lang/es.json (DIV): im Admin auf Live geaendert
- web/templates partner, ueber-uns, erreichbarkeit: lagen nur auf Live,
ueber SiteController erreichbar, /partner im Footer verlinkt
- Migrationen create_dhl_shipments, create_user_invoice_credits,
create_users_sales_volumes: auf Live ausgefuehrt, fehlten im Repository
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- storage/fonts (dompdf-Font-Cache, schreibt die App selbst) und
storage/reports (CSV mit E-Mail-Adressen): nicht mehr versioniert, ignoriert
- storage/oauth-*.key: standen trotz /storage/*.key im Index
- _static/: stand trotz .gitignore im Index (63 MB Altbestand)
- public/phpinfo.php entfernt (oeffentlich aufrufbar)
Lokale Dateien bleiben liegen. Auf Live direkt auf den Tag mit diesem
Commit umstellen, sonst loescht der Checkout die Dateien dort.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Supervisor bedient alle Queues der Jobs (high-priority, default, dhl-*),
production mit 3 statt 10 Prozessen
- Redis-Queue mit after_commit: der Stripe-Paid-Pfad plant den
Rechnungsnummer-Sync innerhalb der Transaktion ein
- /horizon nur fuer System-Admins (admin >= 4)
- horizon:snapshot alle 5 Minuten, queue:prune-failed nach 30 Tagen
- deploy.sh: QUEUE_WORKER (horizon | none), Anleitung Abschnitt F
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- dev/betrieb/deploy.sh: Tag auschecken, composer, Caches leeren (bewusst
kein route:/config:cache wegen hostabhaengiger Routen und env()-Aufrufen),
horizon:terminate, FPM-Reload, Worker-Pruefung am Ende
- dev/betrieb/README.md: Einrichtung Server, Umstellung des Live-Verzeichnisses,
Release, Deploy, Rollback
- config('app.version') aus RELEASE, php artisan about --only=mivita
- monitoring:queue-probe prueft nach dem Deploy, ob Horizon Jobs abholt
- Vorlage dev/git-deploy-monitoring aus Pressekonto
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bisher per .gitignore ausgeschlossen und nur per SFTP auf Live. Fuer den
Git-Deploy gehoert der Theme-Build ins Repository. Entfernt: Demos, Tests,
Quellen, ungenutzte Bibliotheken und die PHP-Beispielskripte von
slim-image-cropper (lagen oeffentlich im Webroot). 1292 -> 390 Dateien,
31 MB -> 13 MB. RELEASE (schreibt deploy.sh) wird ignoriert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seit das Herkunftsfeld im Checkout freiwillig ist, fehlte der Block bei
Kundenbestellungen ohne Angabe ganz. Jetzt steht dort grau "keine Angabe".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kunden loeschen (Rueckmeldung "es wird nichts geloescht"):
- DSGVO-geloeschte Eintraege blieben mit Name in den Listen stehen; Kunde
61009 wurde deshalb achtmal geloescht, bei Eintrag 45013 wurde die E-Mail
danach ueber ein offenes Bearbeiten-Formular zurueckgeschrieben
- neue Spalte shopping_users.gdpr_deleted_at (Bestand per Migration markiert)
- Admin-Kundenliste blendet geloeschte Kunden aus (Filter: ausblenden /
mit anzeigen / nur geloeschte), Berater-Kundenliste immer
- Bearbeiten/Speichern im Admin und beim Berater gesperrt; ShoppingUser
verweigert das Zurueckschreiben von E-Mail und Telefon
- Modal und Erfolgsmeldung (de/en/es/fr) sagen klar, dass Name und
Rechnungsadresse wegen der Aufbewahrungspflicht bleiben
- gdpr:repair-customers [--execute] zieht protokollierte Loeschungen nach
- DSGVO-Loeschung prueft Superadmin serverseitig; Berater duerfen eine
Kunden-E-Mail nur beim eigenen Kunden nachtragen
- setFakerMail setzt faker_mail bei geloeschten Eintraegen nicht zurueck
Checkout: "Wie bist du auf uns aufmerksam geworden?" ist kein Pflichtfeld
mehr und wird farbig hervorgehoben.
Deploy: php artisan migrate && php artisan gdpr:repair-customers --execute
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Berater ohne UserShop liefen bei Kundenbestellungen (abo-ot-customer) in
"Attempt to read property id on null". Shops werden jetzt bei Freischaltung
und bei Bedarf automatisch angelegt (Name aus Vor-/Nachname, bei Belegung
-2, -3, ...). Nachtrag fuer Bestandsberater: php artisan user:ensure-shops.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kundenentscheid 25.09.2026 zu den Regeln 4 bis 7:
- Firmenkonto (mivita.company_user_id) steht in keiner Rangliste und
bekommt keine Praemie; getrackt wird weiter, damit ein ueber den
Firmenlink registrierter Partner nach dem Umhaengen mit umzieht
- Bedingungen Punkt 4 (de/en/es/fr): auch Kunden, die Berater werden,
zaehlen; massgeblich ist der aktuell hinterlegte Sponsor
- Test: Anmeldung ueber den Firmenlink, Umhaengen, Wertung beim neuen
Sponsor
- Doku: Regeln 4 bis 7 entschieden
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Kundenentscheid 25.09.2026:
- Storno: Ist jede bezahlte Starterpaket-Bestellung eines Neupartners
storniert (txaction = cancelled), bekommt das Tracking cancelled_at,
eine Storno-Logzeile nimmt die Einmalpunkte zurueck. Nachtlauf und
Neuaufbau markieren; "Meine Berechnung" zeigt den Partner durchgestrichen
mit "Storniert am ..., zaehlt nicht".
- Gezaehlt wird fuer alle, einen Rang bekommen nur bestaetigte Teilnehmer;
Export und Summen nur fuer sie. Unbestaetigte sehen "Deine Praemie nach
Bestaetigung", im Admin "Teilnahme nicht bestaetigt, keine Auszahlung".
- Datenschutz: andere Berater sehen Unbestaetigte in der Rangliste nicht
mehr (auch nicht anonym), VIP-Konten schon, der eigene Eintrag bleibt.
- Teilnahmebedingungen Punkt 2 und 7 und Hinweistexte in de/en/es/fr.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Der Dry-Run auf Produktion (25.09.) listet einen Berater, der ohne Sponsor
registriert wurde (m_sponsor und pre_sponsor leer). terminate() haette ihn
jede Nacht mit "kein aktiver Sponsor" uebersprungen. Pausieren und harter
Cut nehmen jetzt den naechsten aktiven Sponsor ueber m_sponsor/pre_sponsor
und sonst das Firmenkonto (ohne Mail).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A1-A4 und E1 live seit 23.09., B2, A07, Abo-Warnung und E1-Nachtraege
live seit 25.09., Settings auf 0, Montenegro beendet. Offen: erster
user:cleanup-Lauf, Seeder fuer den Challenge-Start 09.10.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Qualifikationsbeginn 09.10.2026 statt 01.10. (Kundenentscheid 25.09.),
Beschreibung und Teilnahmebedingungen nennen den 9. Oktober
- Name, Untertitel, Beschreibung und Bedingungen als trans_* auf
Englisch, Spanisch und Franzoesisch
- Seeder gleicht bei bestehender Challenge Texte, Uebersetzungen und den
Beginn ab; alle anderen Admin-Werte bleiben
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Die Recruiting Challenge erfasste Eigen- und Kundenabos mit 0 Punkten
(Live-Kopie: 175 Eigenabos). Sie tauchten in Details und Admin auf und
brachten Berater ohne Neupartner in die Rangliste.
- incentives.include_abos (Standard an); Migration schaltet Incentives
ohne jede Abo-Wertung aus (0 Abo-Punkte, 0 Mindestabos, ohne Umsatzpunkte)
- aus: kein Abo-Tracking in Tracker, Neuaufbau und Reparatur;
incentive:calculate raeumt vorhandene Abo-Zeilen ab
- Admin: Checkbox im Formular, Abo-Punkte/Mindestabos werden dann auf 0
gesetzt; Abo-Spalte und -Bereiche in Admin, Details und Teilnahme-Hinweis
entfallen
- Seeder der Recruiting Challenge setzt den Schalter aus
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Aktuelle Punkte / Meine Statistik / Team-Punkte: Farblegende unter der
Bestellliste entfaellt (Kundenwunsch 24.09.)
- Home: "Level & Provision laut Prognose" nennt jetzt "Stand Monatsende";
die Prognose rechnet schon bis zum letzten Liefertag, nur der Titel
sagte noch "Stand 25."
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- September-Plan: B2, E1 (Gleichstand, Zahlungsdatum), A07 und die
Abo-Warnung nachgetragen; Audit-Stand A07 erledigt.
- Bisher nur im Arbeitsbaum: Entwicklungsplan und Kundenbrief vom 07.09.
samt PDF, Tickets vom 08.09., Log-Analyse 26.08., Ledger-Plan,
August-Anerkennungen. E-Mail-Adressen von Kunden und Beratern durch
Platzhalter bzw. Namen ersetzt.
Nicht versioniert (personenbezogene Daten): Kundenliste CSV/XLSX und die
Produktions-Logs in dev/2026-08-26-logs/*.log.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
user:make_abo_order nimmt nur Abos mit next_date = heute; faellt ein Lauf
aus, bleibt ein Abo stillschweigend liegen (Abo 39: Termin 25.03.2026).
abo:check-overdue meldet taeglich um 05:30 alle aktiven Abos mit Status
okay und verstrichenem Termin per Mail an mivita (Link ins Admin-Abo) und
ins abo_order-Log. Es wird bewusst nichts nachgebucht; --no-mail nur fuer
die Ausgabe.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Entwicklungsplan September, B2 (Protokoll 08.09., Rueckmeldung 14.09., R3):
- Ablauftag + 2 Kalendertage (Nachtlauf): Team, Kunden, Kundenabos und
Portal-Logins gehen umkehrbar an den naechsten aktiven Sponsor, jede
Umhaengung in consultant_suspension_transfers protokolliert. Kurzinfo
an den Sponsor ohne Listen, keine Mail an das Firmenkonto.
- Zahlung bis Tag 27: Berater aktiv, Team und Kundendaten zurueck (nur
was noch beim Sponsor liegt); Umsaetze der Zwischenzeit bleiben dort.
- Tag 28: was noch am Berater haengt geht endgueltig an den Sponsor,
Berater wird dessen Kunde (ohne Duplikat) und geloescht; Listen-Mail
mit Vorname, Nachname, Telefon, E-Mail an jeden haltenden Sponsor.
- Cron, Zahlungseingang und Admin-Ablaufdatum laufen ueber
ConsultantSuspensionService; user:cleanup --dry-run listet beide Stufen.
- Mail 35 und Dashboard nennen Pausentag und Frist; Fristen in
config/mivita.php (cleanup_suspend_days, cleanup_delete_days).
- Nebenbefunde: Kernel referenziert UserCleanUp, user:restore aus der
User-Bereinigung laeuft nur noch einmal.
- Testschema: shopping_users.auth_user_id nullable wie auf Produktion
(Migration aendert nur, wo die Spalte noch NOT NULL ist).
- Uebersicht fuer die Kundin: dev/_tickets/2026-09-23-mitgliedschaft-
ablauf-uebersicht.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
paylineContext() nahm die gesamte Downline, lineByUserId() und die
Team-Statistik den Provisionsbaum. "Teampartner/Abos in der Payline" und
die Payline-Prognose zaehlten damit auch Berater ohne Mitgliedsebene oder
Abrechnungskonto samt ihrem Unterbaum, deren Abo-Punkte nie in die
Provision eingehen. An der Live-Kopie betrifft das 102 von 162 Beratern
mit Payline. Nach dem Deploy cache:clear (payline_context_*, 6 h).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Rangliste: bei gleichen Punkten bestaetigte Teilnahme vor anonym, danach
wer den Stand zuerst erreicht hat (letzter zaehlender Neupartner bzw.
Neuabo laut Tracking-Tabellen), zuletzt die Teilnehmer-ID. Vorher
entschied bei Gleichstand die Datenbankreihenfolge
(Teilnahmebedingungen Recruiting-Challenge, Punkt 6).
- Neupartner zaehlen fuer Incentives ab ihrer ersten Kontozahlung
(users.partner_paid_at), wie in der Statistik (Kundenentscheid
16.09.2026), nicht ab dem Bestelldatum: Live-Tracking, naechtliche
Reparatur und Neuaufbau nutzen dieselbe Regel
(IncentiveTracker::newPartnerCountedAt). Wer am 29.09. bestellt und am
01.10. zahlt, zaehlt in der Challenge.
Montenegro ist abgeschlossen; gegen die Live-Kopie tauschen dort nur
Plaetze ab 81, und der naechtliche Lauf entfernt keine bestehenden
Trackings.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Seit 68d230d fehlten in es/fr je 25 Keys der Admin-Maske fuer manuelle
Zahlungseingaenge; LocalizationSyncTest und FrenchLocalizationTest waren
deshalb dauerhaft rot und haetten neue Luecken nicht mehr gemeldet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Admin-Bereich "Newsletter-Listen" (Plan dev/_tickets/entwicklungsplan-
newsletter-listen.md): Berater und Kunden nach Filtern zusammenstellen,
als Liste speichern und wieder laden, Export als XLSX/CSV. Jeder Export
wird protokolliert (newsletter_list_exports).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ticket dev/_tickets/2026-09-08.md #1: neuer Punkt "Berater uebertragen" im
Superadmin (/admin/user/transfer). Vorschau zeigt Firstline, Downline,
geparkte Sponsor-Bezuege, Kunden, Kundenabos und Portal-Logins; nach
Bestaetigung wird alles in einer Transaktion umgehaengt und wie die
User-Bereinigung protokolliert (user_clean_up_logs,
shopping_user_member_logs, SysLog). Bestellungen, Umsaetze und
Provisionsdaten bleiben beim bisherigen Berater. Ziel unterhalb der
Firstline der Quelle wird abgelehnt (Kreis im Baum).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Ticket dev/_tickets/2026-09-08.md #2:
- "Kunde loeschen" oeffnet ein Modal statt direkt per GET zu loeschen. Es
zeigt alle Eintraege desselben Kunden (Kundennummer oder E-Mail, ohne
Platzhalter-Adressen) mit Anzahl Bestellungen.
- Ohne Bestellungen: Komplett-Loeschung (anonymisiert und soft-geloescht,
Portal-Logins und Stripe-Kunden entfernt, Zahlungs-Tokens inaktiver Abos
geleert). Mit Bestellungen nur DSGVO-Loeschung (Superadmin); die arbeitet
jetzt ebenfalls auf der E-Mail-Gruppe und entfernt Portal-Logins.
- Bei aktivem Abo oder Berater-Datensatz wird abgelehnt.
- Die alte "Loeschung" (CustomerRepository::deleteCustomer) entfaellt.
- Fehlermeldungen nutzen alert-error; alert-danger zeigte kein Layout an.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Die Pakete stehen im Plan als umgesetzt und sind live, der Controller- und
View-Teil lag aber nur im Arbeitsbaum (Sprachkeys und Model-Methoden sind
seit 5a38f4a/685f7a9 versioniert):
- K5: Abo-Bestellungen in Punkteuebersicht, Dashboard-Punkten und eigener
Statistik tuerkis mit Zusatz "(Abo)", Legende darueber.
- K4: Summe der Payline-Abos ("149 + 391 = 540 Abos gesamt").
- Home: payline_is_career_fallback, solange die Monatsquali aussteht
(Kundenentscheid 01.09.); die View wertet das Kennzeichen schon aus.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Eine spaeter stornierte Rechnung fehlte im Export ihres Rechnungsmonats,
weil cancellation = true auch an der Originalrechnung steht. Sie bleibt
jetzt als Erloes im Rechnungsmonat; die Stornierung wird im Monat des
Stornobelegs gegengebucht (nur Belege ohne cancellation_id).
- Pruefliste je Buchungsstapel als CSV (Button im Steuerberater-Bereich):
Belege, die sich ohne Zusatzinformation nicht abstimmen lassen (doppelte
Rechnungsnummern, Stornos ausserhalb des Systems).
- Korrespondenz und offene Differenzen mit dem Steuerberater unter
dev/2026-07-17-steuern/.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- shopping_orders.shipped_at (+ Herkunft live/dhl/geschaetzt): gesetzt im
Model, sobald der Versandstatus auf versendet wechselt, egal an welcher
Stelle der Status gesetzt wird. sales:backfill-shipped-at traegt
Altbestellungen aus dem DHL-Label nach oder schaetzt.
- SalesVolumeService rechnet Absatzmengen je Produkt wahlweise nach
Versand (inkl. externer/API-Bestellungen, Stornos im Monat des
Stornobelegs) oder nach Rechnung. Sets werden fuer die zweite Liste in
Einzelartikel aufgeloest.
- Admin-Seite: Umschalter Versand/Rechnung, Datenqualitaet (Anteil
geschaetzter Versanddaten), Export Produkte und Einzelartikel.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Produkte mit 7,5 Punkten wurden an drei Stellen zu ganzen Zahlen:
user_abo_one_time_items (PHP-Cast int, 7,5 -> 7), homeparty_user_order_items
(MySQL rundete auf 8) und shopping_collect_orders (Summe der Shop-API).
Die Migration zieht die drei Spalten auf DECIMAL(10,2) nach (Nachtrag zu
2026_01_22), Cast und Abo-Snapshot rechnen mit float.
points:report-decimal-diff wertet die Differenzen je Berater und Monat aus
(nur lesend), points:repair-decimal korrigiert den laufenden Monat und
zieht die Monatssummen nach.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Slim::storeCroppedImage() vertraute dem vom Client gemeldeten Typ und
uebernahm die Endung aus dem Dateinamen; "shell.php" mit Typ image/png
landete als .php im public-Disk. Jetzt entscheidet
getimagesizefromstring() ueber die Bytes, die Endung kommt aus dem
geprueften Typ.
- Foto loeschen war ein GET-Link und liess sich per fremdem <img src>
ausloesen (CSRF); jetzt POST-Formular mit Token.
- Ohne Konto (users.account_id) bricht Upload/Loeschen mit 404 ab statt
mit einem Fatal.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>