Skip to content

RealUnit-Mail PR #3598 — Folge-Themen für späteren PR #3654

@TaprootFreak

Description

@TaprootFreak

Folge-Punkte aus dem Code-Review von #3598. Wurden bewusst nicht im PR mitgemacht, um den initialen Mail-Stand für RealUnit zügig live zu bringen. In einem separaten PR nachzuziehen.

F1: disabledMailContexts-Schema einführen

Aktuell ad-hoc-Filter in jedem Service:

if (entity.wallet?.name === REALUNIT_WALLET_NAME) {
  await this.repo.update(...entity.somethingMail());
  continue;
}

Sieben Mal verteilt (3× buy-crypto, 3× buy-fiat, 1× limit-request).

Sauber: Eine deklarative Wallet-Spalte mit MailContext[] (granularer als bestehender disabledMailTypes auf MailContextType). Ähnliches Pattern wie Wallet.disabledMailTypes.

Vorteil: jeder neue Mail-Trigger braucht keine Service-Anpassung mehr; pro Wallet ein DB-Record.

F2: confirmSentMail() für nicht-versendete Mails ist semantisch falsch

Skip-Logic markiert mit confirmSentMail() / pendingMail() / chargebackMail()mailSendDate wird gesetzt, obwohl keine Mail rausging.

Folgen:

  • Audit/Reporting wird falsch (aussagt "Mail wurde gesendet")
  • Etwaige spätere Resend-Logik weiß nicht, dass es ein Skip war
  • Nicht klar erkennbar in Logs, ob es ein Skip oder ein echter Send war

Sauber: Ein separates mailSkipped-Feld auf den Entities (BuyCrypto / BuyFiat / LimitRequest) oder ein Skip-Log.

F3: Fehlende Test-Coverage

Null neue Tests für die DFX-API-weiten Änderungen:

  • MailFactory.createUserV2Mail — wallet-aware translation, welcome-Insert, forced-lang, body-injection-Reihenfolge
  • MailFactory.createPersonalMail — analog
  • RealUnit-Opt-out-Filter (10 amlReasons in pendingBuyCrypto/pendingBuyFiat, plus chargeback / chargebackUnconfirmed / limit-request)

Sauber: Mindestens einen Integration-Test pro Mail-Pfad mit / ohne RealUnit-Wallet.

F4: Hardcoded WordPress-CDN-URL für RealUnit-Logo

In realunit.hbs ist das Logo als Image-Src hartcodiert:

https://realunit.ch/wp-content/uploads/2023/06/realunit-logo-tagline-full-color-rgb.svg

Risiko: WordPress-Media-Library kann den Pfad ändern (z. B. bei Migration), Logo bricht.

Sauber: Logo-URL per Wallet-Config setzen (Config.mail.wallet.RealUnit.logoUrl), oder das SVG inline ins Template einbetten.

F5: Preview-Script-Status klären

scripts/generate-realunit-previews.js (~600 Zeilen JS) ist im PR mitcommitted. Dient als Review-Tool für DAS und das Compliance-Team.

Frage: Soll's bleiben (für künftige Anpassungen jederzeit ein neuer Export möglich) oder als lokales Tool unter .gitignore?

Falls bleiben: Folge-Aufgabe — README in scripts/ mit Beschreibung wie es ausgeführt wird.

F6: Wallet-Body-Text-Mechanismus auch für PersonalMail

MailFactory.getWalletBodyTexts(...) wird aktuell nur in createUserV2Mail aufgerufen, nicht in createPersonalMail. Konsistenz-Lücke. Falls eine RealUnit-Mail je per MailType.PERSONAL gehen würde (aktuell limit-request für RealUnit deaktiviert, also kein konkretes Problem), würde der Body-Override nicht funktionieren.

Sauber: Den getWalletBodyTexts-Aufruf auch in createPersonalMail einbauen.

F7: Mail-Text-Polish

Aus dem Review von DAS / RealUnit-Compliance noch ausstehend (kann auch direkt von DAS kommen):

  • sell-processing: Salutation und Body doppeln "wird verarbeitet"
  • buy-crypto-completed Body: "Sie sollten die Anzahl Token sowie den aktuellen Wert sehen" — "sollten" konjunktiv-unsicher
  • buy-crypto-completed Title: "Transfer Ihrer RealUnit-Token" generisch, klare Variante: "Kauf RealUnit-Token erfolgreich"
  • kyc-success: Salutation und Body doppeln "abgeschlossen"
  • kyc-missing-data: "fehlen noch Daten, die Sie noch ergänzen müssen" — "noch" zweimal
  • pending-merge-incomplete line2: "im vorangegangenen E-Mail" — Genus-Inkonsistenz (sollte "in der vorangegangenen Mail")
  • fiat-input-currency-exchange: Reihenfolge umkehren — der Hinweis "In der RealUnit App stehen Ihnen sowohl ein CHF- als auch ein EUR-Bankkonto zur Verfügung" ist die handlungsrelevanteste Information und sollte zuerst kommen
  • pending-video-ident: Stornier-Option in line5 fragwürdig (DFX betreut RealUnit eh telefonisch); plus "DFX AG" zweimal erwähnt
  • added-address Body: sehr knapp, fehlt Kontext zur Account-Zusammenlegung — Kunde könnte erschrecken ("Welche Adresse?")
  • recommendation-mail: doppelter Button-Konflikt (registration_button "Klick hier" + hardcoded "App öffnen") — App-öffnen passt nicht für Empfänger ohne App
  • ref-reward: Body besteht nur aus "Vielen Dank für Ihre Weiterempfehlung der RealUnit App!" — keine Info zur Prämie

F8: Klärung mit DAS — Mail-Setup-Schluss

Im DAS-Doc v1.0 endet eine Liste mit "Prüfen ob vorhanden:" (unausgefüllt). Klärung, ob die jetzigen Mails das Mail-Setup vollständig abdecken oder zusätzliche Trigger erforderlich sind. Comment dazu wartet auf Antwort: #3598 (comment)

F9: walletName-Parameter-Threading durch die Translation-Chain

In der MailFactory wurde walletName?: string an translate(), translateParams(), getMailAffix() und mapMailAffix() als optionaler Parameter angehängt und durch alle vier Ebenen durchgereicht. Funktioniert, ist aber unsauberes Plumbing.

Sauber: Ein Translation-Context-Objekt ({ lang, walletName }) einmal oben in createUserV2Mail/createPersonalMail aufbauen und durch den Stack reichen — oder direkt einen Translator-Helper-Klasse pro Mail-Build, die Lang + Wallet-Override kapselt.

Aufgefallen in @davidleomay's PR-Review: #3598 (review)

F10: createPersonalMail ignoriert forcedLang und walletName bei der Translation

MailFactory.createPersonalMail nutzt:

  • lang = userData.language.symbol — kein Fallback auf walletMailConfig.forcedLang
  • this.translate(title, lang) — kein walletName-Argument; das Subject wird also nie aus mail-<wallet>.json gelesen

createUserV2Mail macht beides korrekt. Inkonsistenz.

Aktuell kein Live-Bug: Die einzige RealUnit-relevante PersonalMail (LIMIT_REQUEST) ist für RealUnit deaktiviert. Aber sobald je eine RealUnit-Mail über MailType.PERSONAL läuft, würde sie nicht in DE rendern und den Subject aus dem DFX-Default ziehen.

Fix: in createPersonalMail analog zu createUserV2Mail:

  1. walletName + walletMailConfig ableiten
  2. lang = walletMailConfig?.forcedLang ? ... : userData.language.symbol.toLowerCase()
  3. this.translate(title, lang, undefined, walletName) aufrufen

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions