Skip to content

Zugferd-Einstellungen vor Aktivierung prüfen (WIP)#571

Merged
dspangenberg merged 1 commit into
mainfrom
develop
Jul 4, 2026
Merged

Zugferd-Einstellungen vor Aktivierung prüfen (WIP)#571
dspangenberg merged 1 commit into
mainfrom
develop

Conversation

@dspangenberg

@dspangenberg dspangenberg commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Neue Funktionen

    • Beim Aktivieren von ZUGFeRD wird jetzt vorab geprüft, ob die nötigen Angaben vollständig sind.
    • Fehlende Voraussetzungen verhindern das Aktivieren und zeigen eine verständliche Fehlermeldung an.
  • Fehlerbehebungen

    • Fehler beim Umschalten von ZUGFeRD werden jetzt direkt als Toast angezeigt.
    • Die Anzeige von Zahlungsbedingungen wurde angepasst, um unerwartete Inhalte zu vermeiden.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Vor der ZUGFeRD-Aktivierung wird nun eine Plausibilitätsprüfung (checkStettings) durchgeführt, die bei fehlender Umsatzsteuer-ID die Aktivierung mit Fehlermeldung abbricht. Zusätzlich entfällt die Befüllung von paypentTerm aus payment_deadline, und das Frontend zeigt bei Fehlern einen Error-Toast an.

Changes

ZUGFeRD Aktivierungsprüfung und Fehler-Feedback

Layer / File(s) Summary
Service: Einstellungsprüfung und Zahlungsbedingungen
app/Services/ZugferdService.php
Neue Methode checkStettings() validiert die Umsatzsteuer-ID des Verkäufer-Kontakts; getPaymentInformation() befüllt $paypentTerm nicht mehr aus payment_deadline.
Controller: Aktivierung mit Prüfung absichern
app/Http/Controllers/App/Setting/ZugferdSettingController.php
Import der ZugferdService-Facade; enable() prüft per checkStettings() und bricht bei Fehler mit withErrors ab, statt direkt zu aktivieren.
Frontend: Fehler-Toast bei fehlgeschlagener Aktivierung
resources/js/Pages/App/Setting/ZugferdSetting/ZugferdSettingEdit.tsx
router.put erhält einen onError-Callback, der einen Error-Toast mit der ersten Fehlermeldung zeigt.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ZugferdSettingEdit
  participant ZugferdSettingController
  participant ZugferdService

  ZugferdSettingEdit->>ZugferdSettingController: router.put (enable)
  ZugferdSettingController->>ZugferdService: checkStettings()
  ZugferdService-->>ZugferdSettingController: true oder Fehlerstring
  alt Fehler
    ZugferdSettingController-->>ZugferdSettingEdit: redirect back withErrors
    ZugferdSettingEdit->>ZugferdSettingEdit: onError Toast anzeigen
  else Erfolg
    ZugferdSettingController-->>ZugferdSettingEdit: is_enabled aktualisiert
    ZugferdSettingEdit->>ZugferdSettingEdit: onSuccess Toast anzeigen
  end
Loading

Possibly related PRs

  • dspangenberg/opsc#493: Beide PRs ändern getPaymentInformation() in ZugferdService.php bezüglich Zahlungsbedingungslogik.
  • dspangenberg/opsc#562: Beide PRs greifen in dieselbe Service-Datei ein und ändern getPaymentInformation()/verwandte ZUGFeRD-Logik.
  • dspangenberg/opsc#567: Beide PRs betreffen ZUGFeRD-Settings-Felder (Kontakt/VAT), auf denen die neue checkStettings()-Prüfung basiert.

Poem

Ein Häschen prüft die Steuer-ID,
bevor der Rechnungslauf gedeiht 🐇
Fehlt sie, dann kommt der rote Ton,
kein stiller Fail, ein Toast als Lohn!
ZUGFeRD hoppelt jetzt ganz sicher voran. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Der Titel beschreibt die Hauptänderung korrekt: ZUGFeRD-Einstellungen werden vor der Aktivierung geprüft.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 PHPStan (2.2.2)

PHPStan was skipped because the config uses disallowed bootstrapFiles, bootstrapFile, or includes directives.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
app/Http/Controllers/App/Setting/ZugferdSettingController.php (1)

33-36: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Fehlerübergabe via withErrors($result) erzeugt untypischen numerischen Schlüssel.

$result ist ein einzelner String. RedirectResponse::withErrors() castet dies intern zu (array) $result, wodurch eine MessageBag mit dem numerischen Schlüssel 0 statt einem semantischen Feldnamen entsteht (z. B. seller_contact_id). Laut Inertia-Dokumentation werden Validierungsfehler normalerweise feldbasiert (errors.first_name) bereitgestellt – die numerische Schlüsselung ist unüblich und function­iert nur, weil das Frontend exakt errors[0] erwartet (siehe ZugferdSettingEdit.tsx). Das koppelt beide Seiten eng an eine implizite Konvention statt an einen aussagekräftigen Schlüssel.

♻️ Vorschlag mit semantischem Fehlerschlüssel
-        $result = ZugferdService::checkStettings();
-        if ($result !== true) {
-            return redirect()->back()->withErrors($result);
-        }
+        $result = ZugferdService::checkStettings();
+        if ($result !== true) {
+            return redirect()->back()->withErrors(['zugferd' => $result]);
+        }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Http/Controllers/App/Setting/ZugferdSettingController.php` around lines
33 - 36, The error handling in ZugferdSettingController’s settings check uses
withErrors() with a plain string from ZugferdService::checkStettings(), which
produces a numeric MessageBag key instead of a semantic field name. Update the
redirect in this controller to pass the error under a meaningful key that
matches the failing setting (for example the relevant contact or field
identifier), and keep the frontend contract in ZugferdSettingEdit.tsx aligned
with that named error key rather than relying on errors[0].
app/Services/ZugferdService.php (1)

234-234: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Methodenname enthält Tippfehler.

checkStettings statt checkSettings – wird konsistent in Facade und Controller weiterverwendet, ist aber dauerhaft im öffentlichen API-Namen sichtbar.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Services/ZugferdService.php` at line 234, The public method name has a
typo and should be corrected from checkStettings to checkSettings in
ZugferdService, and all call sites that reference this API in the Facade and
Controller should be updated to use the new name consistently. Keep the behavior
unchanged, but ensure the renamed method remains aligned across the service and
any dependent references so the public API no longer exposes the misspelling.
resources/js/Pages/App/Setting/ZugferdSetting/ZugferdSettingEdit.tsx (1)

78-85: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Fragiler Zugriff errors[0] mit Doppel-Cast, kein Fallback.

Der Zugriff errors[0] as unknown as string funktioniert nur, weil das Backend zufällig einen numerischen Schlüssel 0 liefert (siehe ZugferdSettingController::enable()). Laut Inertia-Doku sind Validierungsfehler normalerweise feldbasiert (z. B. errors.first_name); ändert sich künftig die Fehlerstruktur (z. B. echte Feld-Validierung), zeigt der Toast undefined an, da kein Fallback existiert. Der doppelte Type-Cast (as unknown as string) deutet zudem darauf hin, dass der eigentliche Typ von errors nicht direkt zu string kompatibel ist und hier Typsicherheit umgangen wird.

🛡️ Vorschlag mit Fallback
         onError: (errors) =>{
           toast({
             id: toastId,
             type: 'error',
             title: 'ZUGFeRD konnte nicht aktiviert werden.',
-            message: errors[0] as unknown as string
+            message: Object.values(errors)[0] ?? 'ZUGFeRD konnte nicht aktiviert werden.'
           })
         },

Based on learnings, dieses Fehler-Schema hängt eng mit dem Backend-Vertrag aus ZugferdSettingController::enable() zusammen – eine Anpassung dort (semantischer Schlüssel statt 0) sollte hier entsprechend nachgezogen werden.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@resources/js/Pages/App/Setting/ZugferdSetting/ZugferdSettingEdit.tsx` around
lines 78 - 85, Der Toast in `ZugferdSettingEdit` greift fragil über `errors[0]
as unknown as string` auf einen numerischen Fehlerkey zu und hat keinen
Fallback. Ersetze den Direktzugriff im `onError`-Handler durch eine robuste
Auswertung der von Inertia gelieferten Fehlerstruktur (z. B. erstes vorhandenes
Feld/erstes Message-Objekt) und zeige einen Default-Text an, wenn keine
Fehlermeldung vorhanden ist; entferne dabei den Doppel-Cast und halte die Typen
im Handler passend zu `errors` sauber.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/Services/ZugferdService.php`:
- Around line 234-246: `ZugferdService::checkStettings()` assumes
`Contact::find($this->settings->seller_contact_id)` always returns a model, but
`seller_contact_id` can be null so `$contact` may be null and `$contact->vat_id`
can fatal. Update `checkStettings()` to guard against a missing contact before
accessing `vat_id`, returning an appropriate validation message when no contact
is configured or found. Use the existing `ZugferdSettings` and `Contact` lookup
in `checkStettings()` as the place to add the null check.

---

Nitpick comments:
In `@app/Http/Controllers/App/Setting/ZugferdSettingController.php`:
- Around line 33-36: The error handling in ZugferdSettingController’s settings
check uses withErrors() with a plain string from
ZugferdService::checkStettings(), which produces a numeric MessageBag key
instead of a semantic field name. Update the redirect in this controller to pass
the error under a meaningful key that matches the failing setting (for example
the relevant contact or field identifier), and keep the frontend contract in
ZugferdSettingEdit.tsx aligned with that named error key rather than relying on
errors[0].

In `@app/Services/ZugferdService.php`:
- Line 234: The public method name has a typo and should be corrected from
checkStettings to checkSettings in ZugferdService, and all call sites that
reference this API in the Facade and Controller should be updated to use the new
name consistently. Keep the behavior unchanged, but ensure the renamed method
remains aligned across the service and any dependent references so the public
API no longer exposes the misspelling.

In `@resources/js/Pages/App/Setting/ZugferdSetting/ZugferdSettingEdit.tsx`:
- Around line 78-85: Der Toast in `ZugferdSettingEdit` greift fragil über
`errors[0] as unknown as string` auf einen numerischen Fehlerkey zu und hat
keinen Fallback. Ersetze den Direktzugriff im `onError`-Handler durch eine
robuste Auswertung der von Inertia gelieferten Fehlerstruktur (z. B. erstes
vorhandenes Feld/erstes Message-Objekt) und zeige einen Default-Text an, wenn
keine Fehlermeldung vorhanden ist; entferne dabei den Doppel-Cast und halte die
Typen im Handler passend zu `errors` sauber.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1452c149-6e04-4051-a189-8d67eb210010

📥 Commits

Reviewing files that changed from the base of the PR and between 30bc081 and a78dc99.

📒 Files selected for processing (3)
  • app/Http/Controllers/App/Setting/ZugferdSettingController.php
  • app/Services/ZugferdService.php
  • resources/js/Pages/App/Setting/ZugferdSetting/ZugferdSettingEdit.tsx

Comment on lines +234 to +246
public function checkStettings(): string|bool
{
$this->settings = app(ZugferdSettings::class);

$contact = Contact::with(['mails', 'phones'])->find($this->settings->seller_contact_id);

if (! $contact->vat_id) {
return 'Umsatzsteuer-ID fehlt';
}

return true;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Null-Pointer-Risiko: $contact kann null sein.

seller_contact_id ist laut ZugferdSettings (app/Settings/ZugferdSettings.php:20) nullable (?int). Ist die Einstellung (noch) nicht gesetzt, liefert Contact::find(null) null zurück (Eloquent wandelt where('id', '=', null) intern in whereNull, was nie trifft). Der nachfolgende Zugriff $contact->vat_id löst dann einen fatalen Fehler aus – genau in dem Szenario, das die Plausibilitätsprüfung eigentlich sauber abfangen soll (fehlende Konfiguration vor Aktivierung).

🐛 Vorgeschlagener Fix
     public function checkStettings(): string|bool
     {
         $this->settings = app(ZugferdSettings::class);

         $contact = Contact::with(['mails', 'phones'])->find($this->settings->seller_contact_id);

-        if (! $contact->vat_id) {
+        if (! $contact || ! $contact->vat_id) {
             return 'Umsatzsteuer-ID fehlt';
         }

         return true;
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public function checkStettings(): string|bool
{
$this->settings = app(ZugferdSettings::class);
$contact = Contact::with(['mails', 'phones'])->find($this->settings->seller_contact_id);
if (! $contact->vat_id) {
return 'Umsatzsteuer-ID fehlt';
}
return true;
}
public function checkStettings(): string|bool
{
$this->settings = app(ZugferdSettings::class);
$contact = Contact::with(['mails', 'phones'])->find($this->settings->seller_contact_id);
if (! $contact || ! $contact->vat_id) {
return 'Umsatzsteuer-ID fehlt';
}
return true;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/Services/ZugferdService.php` around lines 234 - 246,
`ZugferdService::checkStettings()` assumes
`Contact::find($this->settings->seller_contact_id)` always returns a model, but
`seller_contact_id` can be null so `$contact` may be null and `$contact->vat_id`
can fatal. Update `checkStettings()` to guard against a missing contact before
accessing `vat_id`, returning an appropriate validation message when no contact
is configured or found. Use the existing `ZugferdSettings` and `Contact` lookup
in `checkStettings()` as the place to add the null check.

@dspangenberg dspangenberg merged commit 07614db into main Jul 4, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant