Skip to content

[IP-31]: complete the invoice list row action group (#31, #167, #201, #530, #48)#581

Draft
nielsdrost7 wants to merge 1 commit into
InvoicePlane:developfrom
underdogg-forks:feature/31-invoice-list-actions
Draft

[IP-31]: complete the invoice list row action group (#31, #167, #201, #530, #48)#581
nielsdrost7 wants to merge 1 commit into
InvoicePlane:developfrom
underdogg-forks:feature/31-invoice-list-actions

Conversation

@nielsdrost7

@nielsdrost7 nielsdrost7 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Completes the invoice list row action group. One branch, five issues, squashed into a single atomic commit.

What each issue got

#167 — Copy invoice

  • New copy row action (confirmation dialog), gated by duplicate-invoices.
  • InvoiceCopyService::copy() duplicates the invoice inside a DB transaction: all header fields, discount, numbering, template settings and line items are carried over; the copy is reset to Draft, invoice_number = null (drafts may have null numbers), invoiced_at = today, due +30 days, fresh url_key. Payments, creditinvoice_parent_id and is_read_only are deliberately not copied.
  • Cherry-picked from the original orphaned feature/167-copy-invoice work and adapted to the current develop structure (permission gate added, dead code removed).

#201 — Enter payment

  • New enter_payment row action, gated by create-payments, visible only for Sent, Viewed, Partially Paid and Overdue invoices (never Draft or Paid).
  • Modal is pre-filled with the invoice reference, the open balance (invoice_total minus the sum of the invoice's payments) and today's date; payment method is a required select.
  • PaymentService::enterInvoicePayment() records the payment in a transaction and syncs the invoice status: fully paid → Paid, partly paid Sent/Viewed → Partially Paid (Overdue stays Overdue until settled in full). The pre-existing PaymentService did not do any status syncing, so this is new service logic.

#530 — Email action disabled with tooltip

  • The send email action is now disabled (not hidden) with tooltip "This customer has no email address" when the invoice's customer has no email.
  • Relation::customer_email previously read a non-existent email column (always null); it now resolves from the primary contact's email communications, preferring the one flagged is_primary.

#31 — Status-aware action set

  • delete is hidden for Paid invoices (InvoiceService::deleteInvoice() throws for them anyway; now the action never shows). There is no Cancelled status in InvoiceStatus, so Paid is the only terminal state to guard.

#48 — Filter by invoice group

  • SelectFilter::make('numbering_id') on the invoices table; options are the Invoice-type numbering schemes of the current company (tenant-scoped via the BelongsToCompany global scope, matching the invoice form's numbering select). The numbering table has no active column, so all invoice numbering schemes are offered.

Closes

Closes #167 — Copy an invoice to generate a similar billing record
Closes #201 — Enter a payment for an invoice from the invoice list
Closes #530 — Disable "Email Invoice" action with tooltip when customer has no email address
Closes #31 — implement invoices options on invoice list
Closes #48 — Filter invoices by invoice group

Follow-up issues (not resolved here — reopen if auto-closed on merge)

Closes #200 — "download pdf" row action is still a stub (empty action closure + literal todo: modal description text). PR #575 implements PDF download on the invoice edit page via InvoiceService::generatePdf() — this list action should call the same service once merged, not duplicate PDF logic.

Deliberately untouched

The download pdf stub action is left as-is: the PDF service is being built on a separate branch (PR #575) and this PR must not conflict with it.

Validation

Upstream CI is dead, so validation was local (PHPUnit, sqlite in-memory), with a baseline comparison against origin/develop:

Run develop baseline this branch
Full suite 272 passed / 26 skipped / 9 incomplete / 0 failed 280 passed (272 + 8 new) / 26 skipped / 9 incomplete / 0 failed
New InvoiceListActionsTest 8 tests, 61 assertions, all passing

The pre-existing 9 incomplete / 26 skipped and the QuoteWasUpdatedListenerTest runner warning are identical to the develop baseline. All touched files pass Pint.

@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8677932d-2f8e-4ad6-8d0e-ce19e58c2816

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@nielsdrost7 nielsdrost7 self-assigned this Jul 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment