Skip to content

Zugferd-Refactoring#570

Merged
dspangenberg merged 2 commits into
mainfrom
develop
Jul 4, 2026
Merged

Zugferd-Refactoring#570
dspangenberg merged 2 commits into
mainfrom
develop

Conversation

@dspangenberg

@dspangenberg dspangenberg commented Jul 4, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • Neue Funktionen

    • ZUGFeRD-Optionen werden jetzt dynamisch anhand der verfügbaren Einstellungen angezeigt.
    • Die Rechnungsmaske zeigt die ZUGFeRD-Rechnung-Checkbox nur noch, wenn diese Funktion aktiviert ist.
  • Verbesserungen

    • Beim Erstellen, Bearbeiten, Wiederholen und Duplizieren von Rechnungen werden die ZUGFeRD-Einstellungen konsistenter übernommen.
    • Beim Wechsel eines Kontakts werden passende ZUGFeRD-Angaben automatisch ergänzt.
  • Bug Fixes

    • Toast-Benachrichtigungen verhalten sich nun konsistenter während des Ladens und lassen sich entsprechend besser schließen.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@dspangenberg, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ddc8cb98-1aac-45af-bf27-f252dca6336a

📥 Commits

Reviewing files that changed from the base of the PR and between 7a43969 and 535ea73.

📒 Files selected for processing (2)
  • resources/js/Components/twc-ui/sonner.tsx
  • resources/js/Pages/App/Invoice/InvoiceForm.tsx

Walkthrough

Diese PR ersetzt fest kodierte ZUGFeRD-Standardwerte durch ZugferdSettings-gesteuerte Werte in Controller und Model, reicht is_zugferd_enabled durch Create/Edit-Seiten an InvoiceForm durch, das die Checkbox nun bedingt rendert und zugferd_route_id nur bei Vorhandensein setzt. Zusätzlich wird die isDismissible-Logik der Toast-Komponente angepasst.

Changes

ZUGFeRD-Settings-Integration

Layer / File(s) Summary
Backend: ZugferdSettings in Controller und Model
app/Http/Controllers/App/InvoiceController.php, app/Models/Invoice.php
Import von ZugferdSettings; is_zugferd wird bei create, createRecurringInvoice und duplicateInvoice anhand is_enabled statt fest true gesetzt; is_zugferd_enabled wird an Views übergeben.
Frontend: Prop-Weitergabe
resources/js/Pages/App/Invoice/InvoiceCreate.tsx, resources/js/Pages/App/Invoice/InvoiceDetailsEditBaseData.tsx
Props-Interfaces erweitert um is_zugferd_enabled: boolean, Wert wird destrukturiert und an InvoiceForm weitergereicht.
InvoiceForm: bedingtes Rendering
resources/js/Pages/App/Invoice/InvoiceForm.tsx
Neues Prop is_zugferd_enabled; zugferd_route_id wird nur bei vorhandenem Kontaktwert gesetzt; ZUGFeRD-Checkbox wird nur bei aktivierter Einstellung angezeigt.

Toast Dismiss-Verhalten

Layer / File(s) Summary
isDismissible-Berechnung anpassen
resources/js/Components/twc-ui/sonner.tsx
toast() und toast.promise() berechnen isDismissible nun abhängig von isLoading/isDismissible statt eines festen Werts.

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

Possibly related PRs

  • dspangenberg/opsc#489: Ändert Validierungsregeln für dieselben ZUGFeRD-Felder (is_zugferd, zugferd_profile, zugferd_route_id).
  • dspangenberg/opsc#492: Ändert ebenfalls die is_zugferd-Setzung in InvoiceController.php und Invoice.php, aber mit festem true statt settings-gesteuert.
  • dspangenberg/opsc#565: Stellt die ZugferdSettings-Infrastruktur bereit, auf der diese PR aufbaut.

Poem

Im Rechnungsbau, ganz flauschig-fein,
steuert nun ZUGFeRD sich selbst allein. 🐰
Kein festes „true“ mehr, nur Settings-Licht,
die Checkbox hüpft nur, wenn's Flag verspricht.
Toast bleibt stehen, bis er darf verschwinden –
so lässt sich Karottencode leicht verbinden. 🥕

🚥 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 benennt den betroffenen Bereich und passt zur ZUGFeRD-bezogenen Überarbeitung, auch wenn er die konkreten Änderungen nicht detailliert beschreibt.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch develop

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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
resources/js/Components/twc-ui/sonner.tsx (1)

171-187: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Build-Fehler: props existiert nicht in toast.promise.

In Zeile 183 wird props.isDismissible referenziert, obwohl im Scope von toast.promise nur options destrukturiert wird (Zeile 172: title, message, loading, success, error, duration – kein isDismissible). Das bestätigt auch der Build-Fehler aus der statischen Analyse: „Cannot find name 'props'". Zusätzlich fehlt isDismissible als Feld in PromiseToastOptions (Zeile 134-142), sodass selbst nach Korrektur der Variable das Feld nicht existiert.

🐛 Vorschlag zur Behebung
 interface PromiseToastOptions<T = unknown> {
   type?: ToastVariant
   title?: string
   message: string
   loading?: string
   success?: string | ((data: T) => string)
   error?: string | ((error: unknown) => string)
   duration?: number
+  isDismissible?: boolean
 }
 toast.promise = <T,>(promise: Promise<T>, options: PromiseToastOptions<T>) => {
-  const { type, title, message, loading, success, error, duration } = options
+  const { type, title, message, loading, success, error, duration, isDismissible } = options
   const id = crypto.randomUUID()

   sonnerToast.custom(
     id => (
       <Toast
         id={id}
         title={title}
         message={loading ?? message}
         type={type ?? 'default'}
         isLoading
-        isDismissible={!loading && !props.isDismissible}
+        isDismissible={isDismissible ?? false}
       />
     ),
     { id, duration: Infinity }
   )
🤖 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/Components/twc-ui/sonner.tsx` around lines 171 - 187,
`toast.promise` references an undefined `props` symbol and also relies on an
`isDismissible` field that is missing from `PromiseToastOptions`. Update
`toast.promise` in `sonner.tsx` to read dismissibility from the destructured
options (or another valid local source) instead of `props`, and add
`isDismissible` to `PromiseToastOptions` if it should be configurable there.
Ensure the `Toast` call in `toast.promise` uses the corrected dismissible flag
and keeps the existing loading/message behavior intact.

Source: Linters/SAST tools

🤖 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 `@resources/js/Components/twc-ui/sonner.tsx`:
- Line 158: The isDismissible prop in the Sonner wrapper is being inverted, so
explicit caller values are ignored in favor of the opposite boolean. Update the
logic in sonner.tsx where isDismissible is passed so props.isDismissible is used
directly, and only fall back to a loading-based default when the prop is not
provided. Keep the fix localized to the Sonner component/props handling around
isDismissible.

In `@resources/js/Pages/App/Invoice/InvoiceForm.tsx`:
- Around line 110-113: The contact-change sync in InvoiceForm is leaving a stale
zugferd_route_id in the form when the newly selected contact has no Leitweg-ID.
Update the existing useEffect logic around selectedContact so zugferd_route_id
is always synchronized on every contact change, just like invoice_contact_id,
dunning_block, payment_deadline_id, and tax_id, and explicitly clear it to an
empty value when selectedContact.zugferd_route_id is missing.

---

Outside diff comments:
In `@resources/js/Components/twc-ui/sonner.tsx`:
- Around line 171-187: `toast.promise` references an undefined `props` symbol
and also relies on an `isDismissible` field that is missing from
`PromiseToastOptions`. Update `toast.promise` in `sonner.tsx` to read
dismissibility from the destructured options (or another valid local source)
instead of `props`, and add `isDismissible` to `PromiseToastOptions` if it
should be configurable there. Ensure the `Toast` call in `toast.promise` uses
the corrected dismissible flag and keeps the existing loading/message behavior
intact.
🪄 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: 1c0b6e99-49c6-4fde-b5ed-93a589c32f5d

📥 Commits

Reviewing files that changed from the base of the PR and between 544c329 and 7a43969.

📒 Files selected for processing (6)
  • app/Http/Controllers/App/InvoiceController.php
  • app/Models/Invoice.php
  • resources/js/Components/twc-ui/sonner.tsx
  • resources/js/Pages/App/Invoice/InvoiceCreate.tsx
  • resources/js/Pages/App/Invoice/InvoiceDetailsEditBaseData.tsx
  • resources/js/Pages/App/Invoice/InvoiceForm.tsx

<Toast
id={id}
isLoading={props.isLoading}
isDismissible={!props.isLoading && !props.isDismissible}

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline =="
ast-grep outline resources/js/Components/twc-ui/sonner.tsx --view expanded || true

echo "== relevant lines =="
nl -ba resources/js/Components/twc-ui/sonner.tsx | sed -n '130,210p'

echo "== search isDismissible usages =="
rg -n "isDismissible" resources/js -S || true

Repository: dspangenberg/opsc

Length of output: 1119


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file size =="
wc -l resources/js/Components/twc-ui/sonner.tsx

echo "== slice around line 158 =="
sed -n '145,175p' resources/js/Components/twc-ui/sonner.tsx | cat -n

echo "== slice around line 183 =="
sed -n '175,195p' resources/js/Components/twc-ui/sonner.tsx | cat -n

echo "== all isDismissible occurrences with context =="
grep -n -C 2 "isDismissible" resources/js/Components/twc-ui/sonner.tsx || true

Repository: dspangenberg/opsc

Length of output: 2861


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Toast component section =="
sed -n '72,130p' resources/js/Components/twc-ui/sonner.tsx | cat -n

echo "== promise toast section =="
sed -n '175,220p' resources/js/Components/twc-ui/sonner.tsx | cat -n

Repository: dspangenberg/opsc

Length of output: 3855


isDismissible nicht negieren

!props.isLoading && !props.isDismissible kehrt einen expliziten Wert um: true wird zu false, false wird im Nicht-Ladezustand zu true. Der Aufruferwert sollte direkt gelten; nur ohne Angabe sollte der Ladezustand den Default bestimmen.

🐛 Vorschlag zur Behebung
-        isDismissible={!props.isLoading && !props.isDismissible}
+        isDismissible={props.isDismissible ?? !props.isLoading}
📝 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
isDismissible={!props.isLoading && !props.isDismissible}
isDismissible={props.isDismissible ?? !props.isLoading}
🤖 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/Components/twc-ui/sonner.tsx` at line 158, The isDismissible
prop in the Sonner wrapper is being inverted, so explicit caller values are
ignored in favor of the opposite boolean. Update the logic in sonner.tsx where
isDismissible is passed so props.isDismissible is used directly, and only fall
back to a loading-based default when the prop is not provided. Keep the fix
localized to the Sonner component/props handling around isDismissible.

Comment thread resources/js/Pages/App/Invoice/InvoiceForm.tsx Outdated
@dspangenberg dspangenberg merged commit 30bc081 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