Skip to content

[Invoices]: Dashboard widget for Recurring Invoices #127

Description

@nielsdrost7

SMART Plan — [Invoices] Dashboard widget: Recurring Invoice summary

Note: The original body contained implementation instructions for InvoicePlane v1 (CodeIgniter). v2 uses Laravel 11 + Filament v4 — the v2 approach is described below.

Specific

Add a Recurring Invoice Summary Filament widget to the company panel dashboard. The widget shows a compact table with three rows: projected totals for last year, this year, and next year, based on existing RecurringInvoice records scoped to the current company.

Measurable

  • A "Recurring Invoices" widget appears on the company dashboard.
  • The widget shows three rows: last year / this year / next year (projected).
  • Each row shows the total amount for that period, scoped to the current company.
  • Widget is hidden when no RecurringInvoice records exist.

Achievable

  1. Create Modules/Invoices/Filament/Company/Widgets/RecurringInvoiceSummaryWidget.php extending Filament\Widgets\StatsOverviewWidget or a custom table widget.
  2. The widget queries RecurringInvoice::forCompany() grouped by year.
  3. Register the widget in the company panel provider or via the Invoices module's service provider.

Relevant

Recurring invoices represent predictable revenue. Surfacing a year-over-year summary on the dashboard lets admins spot trends without running a report.

Time-bound

Sprint 5.


Arrange · Act · Assert

// Modules/Invoices/Tests/Feature/DashboardTest.php

#[Test]
public function it_renders_the_recurring_invoice_summary_widget(): void
{
    /* Arrange */
    RecurringInvoice::factory()->for($this->company)->create([
        'amount'      => 1000,
        'next_run_at' => now()->startOfYear()->addMonths(2),
    ]);

    /* Act */
    $component = Livewire::actingAs($this->user)
        ->test(\Modules\Invoices\Filament\Company\Widgets\RecurringInvoiceSummaryWidget::class);

    /* Assert */
    $component->assertSuccessful();
    $component->assertSee((string) now()->year);
}

Activity

  1. added this to the v2.0.0-alpha milestone on Nov 1, 2025
  2. changed the title [-][FEATURE] Dashboard needs RecurringInvoices widget[/-] [+][RecurringInvoices]: Dashboard needs RecurringInvoices widget[/+] on Nov 1, 2025
  3. nielsdrost7 commented on Jan 2, 2026

    @nielsdrost7
    CollaboratorAuthor

    @coderabbitai make pull-request with AAA test Scenario's: Arrange, Act, Assert. And also make sure you test edge cases

  4. coderabbitai commented on Jan 2, 2026

    @coderabbitai
    Contributor

    Rate Limit Exceeded

    @nielsdrost7 have exceeded the limit for the number of chat messages per hour. Please wait 0 minutes and 9 seconds before sending another message.

  5. changed the title [-][RecurringInvoices]: Dashboard needs RecurringInvoices widget[/-] [+][Invoices]: Dashboard widget for Recurring Invoices[/+] on Jun 13, 2026
  6. nielsdrost7 commented on Jul 24, 2026

    @nielsdrost7
    CollaboratorAuthor

    Test Suggestions

    Added to support future implementation of this request. The issue body contains a complete SMART Plan.

    The issue already specifies a "Recurring Invoice Summary" widget that projects totals for last/current/next year. Beyond the basic AAA test in the body, consider these edge cases:

    • PHPUnit: it_hides_the_widget_when_no_recurring_invoices_exist()
    #[Test]
    public function it_hides_the_widget_when_no_recurring_invoices_exist(): void
    {
        /* Arrange */
        // No RecurringInvoice records created
    
        /* Act */
        $component = Livewire::actingAs($this->user)
            ->test(RecurringInvoiceSummaryWidget::class, ['tenant' => Str::lower($this->company->search_code)]);
    
        /* Assert */
        $component->assertNotVisible(); // or assertAttributeSet('visible', false);
    }
    • PHPUnit: it_groups_recurring_invoices_by_year_correctly()
    #[Test]
    public function it_groups_recurring_invoices_by_year_correctly(): void
    {
        /* Arrange */
        RecurringInvoice::factory()->for($this->company)->create([
            'amount'      => 500,
            'next_run_at' => now()->subYear()->startOfYear()->addMonths(2),
        ]);
        RecurringInvoice::factory()->for($this->company)->create([
            'amount'      => 1000,
            'next_run_at' => now()->startOfYear()->addMonths(2),
        ]);
        RecurringInvoice::factory()->for($this->company)->create([
            'amount'      => 750,
            'next_run_at' => now()->addYear()->startOfYear()->addMonths(2),
        ]);
    
        /* Act */
        $component = Livewire::actingAs($this->user)
            ->test(RecurringInvoiceSummaryWidget::class, ['tenant' => Str::lower($this->company->search_code)]);
    
        /* Assert */
        $component->assertSuccessful();
        $component->assertSee('500'); // Last year total
        $component->assertSee('1000'); // This year total
        $component->assertSee('750'); // Next year total
    }
    • Edge case: What does the widget show if there are recurring invoices with next_run_at dates spread across months within a single year? Does it only count invoices with next_run_at in the year range, or does it also include all-time recurring invoices?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrefinedRefined with SMART plan / test suggestions

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions