Skip to content

[Settings]: Company panel — manage Invoice Groups #236

Closed

Description

@nielsdrost7

SMART Plan — As an Admin, in the company Panel I want to be able to view, edit, delete Invoice Groups

Specific

Ensure a NumberingResource exists in the Company panel (/{tenant}) that is fully functional for view, edit, and delete operations scoped to the current tenant. The context notes that this resource may already exist at Modules/Core/Filament/Company/Resources/Numberings/NumberingResource.php — if so, verify it exposes edit and delete. If absent, create it.

Measurable

  • A customer_admin can navigate to Invoice Groups in the company panel.
  • The list page shows only Numbering records belonging to the current tenant company.
  • Edit saves changes correctly; delete removes the record; create adds a new scoped record.
  • A customer role cannot access the resource.

Achievable

  1. Check if NumberingResource exists at Modules/Core/Filament/Company/Resources/Numberings/NumberingResource.php; if not, create it following the admin panel version as a template.
  2. Ensure EditNumbering and DeleteAction are included in the resource pages / table actions.
  3. Scope all queries via BelongsToCompany global scope on the Numbering model.
  4. Register the resource in CompanyPanelProvider if not already registered — Modules/Core/Providers/CompanyPanelProvider.php
  5. Add role guard so only customer_admin / elevated roles can access.

Relevant

Invoice Groups define the numbering format for a company's documents (e.g. INV-2026-0001). Company admins must be able to manage these independently to meet their own document numbering requirements without raising a support ticket to the platform admin.

Time-bound

Sprint 6.


Arrange · Act · Assert

// Modules/Core/Tests/Feature/Company/NumberingResourceTest.php

#[Test]
public function customer_admin_can_edit_a_numbering_in_company_panel(): void
{
    /* Arrange */
    $numbering = Numbering::factory()->for($this->company)->create(['prefix' => 'INV-']);

    /* Act */
    $response = $this->testLivewire(EditNumbering::class, ['record' => $numbering->id])
        ->fillForm(['prefix' => 'BILL-'])
        ->call('save');

    /* Assert */
    $response->assertHasNoErrors();
    $this->assertDatabaseHas('numberings', ['id' => $numbering->id, 'prefix' => 'BILL-']);
}

#[Test]
public function customer_admin_can_delete_a_numbering_in_company_panel(): void
{
    /* Arrange */
    $numbering = Numbering::factory()->for($this->company)->create();

    /* Act */
    $this->testLivewire(ListNumberings::class)
        ->callTableAction(DeleteAction::class, $numbering)
        ->assertHasNoErrors();

    /* Assert */
    $this->assertDatabaseMissing('numberings', ['id' => $numbering->id]);
}

Activity

  1. coderabbitai commented on Dec 28, 2025

    @coderabbitai
    Contributor

    📝 CodeRabbit Plan Mode

    Generate an implementation plan and prompts that you can use with your favorite coding agent.

    • Create Plan
    Examples

    🔗 Similar Issues

    Possible Duplicates

    Related Issues

    👤 Suggested Assignees

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. added this to the v2.0.0-alpha milestone on Dec 28, 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

    Coding Plan

    Approach

    Implement complete CRUD functionality for Invoice Groups in the Company Panel, allowing company administrators to organize and manage groups of invoices. Create comprehensive test coverage using AAA pattern to verify all operations and edge cases.

    Observations

    This is a multi-tenant Laravel application using Filament for admin panels. The Company Panel serves as the primary tenant-scoped interface where company users manage their invoices and related entities. The codebase follows a modular structure with modules in Modules/{Module}/ containing Models, Migrations, Filament Resources, and Tests. All company-scoped entities use the BelongsToCompany trait for automatic query scoping. Tests follow the AAA (Arrange-Act-Assert) pattern with explicit comments, extend from AbstractCompanyPanelTestCase, and use factories inheriting from AbstractFactory for test data generation. Authorization is enforced through middleware and the multi-tenant Filament panel configuration.

    Assumptions
    Assumption 1: Invoice Group Purpose and Structure

    Options Considered:

    • Complex grouping with hierarchy, billing rules, and consolidation logic
    • Simple categorical grouping with name, description, and optional invoice relationships

    Chosen Option: 2

    Rationale: Keep invoice groups simple and lightweight - a name, optional description, and optional relationship to invoices. This aligns with the "sub page" designation in the checklist and follows the principle of building only what's needed.

    Assumption 2: Invoice Group Deletion Behavior

    Options Considered:

    • Cascade delete - deleting group deletes all associated invoices
    • Soft delete group and nullify invoice relationships
    • Prevent deletion if group contains invoices

    Chosen Option: 2

    Rationale: Soft delete the group and set invoice_group_id to NULL for associated invoices. This is the safest approach that preserves invoice data while allowing flexible reorganization, consistent with the soft delete pattern used elsewhere in the codebase.

    Assumption 3: Create Operation Inclusion

    Options Considered:

    • Implement only view/edit/delete as specified
    • Include create operation as standard CRUD practice

    Chosen Option: 2

    Rationale: Include create operation. Standard Filament resource patterns include full CRUD, and users need a way to create groups to then edit/delete them.

    💡 User Tips

    Regenerate the plan with different choices with @coderabbitai <feedback>.

    Implementation Steps

    Phase 1: Database Schema and Model Layer

    Establish the foundational data structures for invoice groups with proper multi-tenant scoping and relationships.

    Task 1: Create Invoice Groups Table Migration
    • Create migration file in Modules/Invoices/Database/Migrations/ following the timestamp naming convention
    • Add columns: id, company_id (foreign key), name (string, required), description (text, nullable), deleted_at (for soft deletes), timestamps
    • Add foreign key constraint for company_id referencing companies table with cascade on delete
    • Add index on company_id for query performance
    • Add unique constraint on (company_id, name) to prevent duplicate group names within a company
    Task 2: Add Invoice Group Foreign Key to Invoices Table
    • Create migration file in Modules/Invoices/Database/Migrations/ to alter invoices table
    • Add invoice_group_id column (nullable foreign key)
    • Add foreign key constraint referencing invoice_groups.id with SET NULL on delete
    • Add index on invoice_group_id for efficient relationship queries
    Task 3: Create InvoiceGroup Model
    • Create model file in Modules/Invoices/Models/
    • Extend Eloquent Model and use BelongsToCompany, SoftDeletes traits
    • Define fillable properties: name, description, company_id
    • Add relationship method invoices() returning hasMany relationship to Invoice model
    • Add relationship method company() returning belongsTo relationship to Company model
    • Define casts for timestamps and dates
    Task 4: Update Invoice Model with Group Relationship
    • Add invoice_group_id to the @Property docblock in Modules/Invoices/Models/Invoice.php
    • Add invoiceGroup() method returning belongsTo relationship to InvoiceGroup model
    • Add invoice_group_id to fillable array if not already included
    🤖 Prompt for AI agents
    Create the database schema and model layer for invoice groups with multi-tenant
    scoping. This phase establishes the foundational data structure.
    
    Create invoice_groups table migration:
    - Add columns: id, company_id (FK), name (string, required), description (text,
    nullable), deleted_at, timestamps
    - Foreign key company_id → companies (cascade on delete)
    - Index on company_id
    - Unique constraint on (company_id, name)
    
    Alter invoices table migration:
    - Add invoice_group_id (nullable FK)
    - Foreign key invoice_group_id → invoice_groups.id (SET NULL on delete)
    - Index on invoice_group_id
    
    Create InvoiceGroup model:
    - Use BelongsToCompany and SoftDeletes traits
    - Fillable: name, description, company_id
    - Relationships: invoices() hasMany, company() belongsTo
    
    Update Invoice model:
    - Add invoice_group_id to @property docblock
    - Add invoiceGroup() belongsTo relationship
    - Add invoice_group_id to fillable
    

    Phase 2: Filament Resource for Company Panel

    Build the admin interface for invoice group management in the company-scoped panel following established Filament resource patterns.

    Task 1: Create InvoiceGroupResource
    • Create resource file in Modules/Invoices/Filament/Company/Resources/InvoiceGroups/
    • Configure resource with InvoiceGroup model
    • Set navigation icon and group under "Invoices" section
    • Define table() method with columns: name, description, invoice count (using relationship count), created_at, updated_at
    • Add filters for searching by name and date ranges
    • Add bulk delete action respecting soft deletes
    • Add row actions for view, edit, and delete with confirmation modals
    Task 2: Create Form Schema
    • Create form schema file in Modules/Invoices/Filament/Company/Resources/InvoiceGroups/Schemas/
    • Define text input for name (required, max 255 characters)
    • Define textarea for description (nullable, helpful text explaining the group's purpose)
    • Add validation rules: unique name within company scope
    • Ensure company_id is automatically set via BelongsToCompany trait
    Task 3: Create Resource Pages
    • Create page classes in Modules/Invoices/Filament/Company/Resources/InvoiceGroups/Pages/
    • ListInvoiceGroups page: display table with search and filters
    • CreateInvoiceGroup page: form for creating new groups
    • EditInvoiceGroup page: form for editing existing groups
    • ViewInvoiceGroup page: display group details with related invoices list using Filament relation manager or custom table
    Task 4: Add Invoice Group Selection to Invoice Forms
    • Update invoice form schema in Modules/Invoices/Filament/Company/Resources/Invoices/Schemas/InvoiceForm.php
    • Add Select field for invoice_group_id (nullable, searchable)
    • Populate options from InvoiceGroup::query()->pluck('name', 'id') scoped to current company
    • Position field logically in the form (likely near invoice number or status)
    🤖 Prompt for AI agents
    Build the Filament admin interface for invoice group management in the Company
    Panel. This phase creates the full UI for CRUD operations.
    
    Create InvoiceGroupResource:
    - Configure with InvoiceGroup model
    - Navigation: icon and group under "Invoices"
    - Table columns: name, description, invoice count (relationship count),
    created_at, updated_at
    - Filters: search by name, date ranges
    - Bulk delete with soft delete support
    - Row actions: view, edit, delete (with confirmation)
    
    Create form schema:
    - Text input: name (required, max 255)
    - Textarea: description (nullable)
    - Validation: unique name within company scope
    - company_id auto-set via BelongsToCompany
    
    Create resource pages:
    - ListInvoiceGroups: table with search/filters
    - CreateInvoiceGroup: creation form
    - EditInvoiceGroup: edit form
    - ViewInvoiceGroup: details with related invoices
    
    Update invoice form:
    - Add Select field for invoice_group_id (nullable, searchable)
    - Options scoped to current company
    - Position near invoice number/status
    

    Phase 3: Comprehensive Test Suite

    Create thorough test coverage for all invoice group operations following AAA pattern and testing edge cases.

    Task 1: Create InvoiceGroupFactory
    • Create factory file in Modules/Invoices/Database/Factories/
    • Extend AbstractFactory to leverage company isolation helpers
    • Define default attributes: name (using Faker), description (paragraph text), company_id (resolved via resolveCompanyId())
    • Add state method withInvoices() that creates related invoices in the afterCreating callback
    Task 2: Test List and View Operations
    • Create test file in Modules/Invoices/Tests/Feature/InvoiceGroupsTest.php
    • Extend AbstractCompanyPanelTestCase
    • Test listing groups shows only company-scoped groups with AAA comments
    • Test viewing a specific group displays correct details
    • Test viewing group shows associated invoices if any
    • Test soft-deleted groups are excluded from list by default
    Task 3: Test Create Operation
    • Test successful group creation with valid data in InvoiceGroupsTest.php
    • Test validation: required name, unique name within company, max length constraints
    • Test company_id is automatically set to current company
    • Test creating group with duplicate name in different company succeeds (company isolation)
    Task 4: Test Edit Operation
    • Test successful group update with valid data in InvoiceGroupsTest.php
    • Test updating group name to duplicate within same company fails validation
    • Test updating group that belongs to different company fails authorization (company isolation test)
    • Test editing soft-deleted group is prevented
    Task 5: Test Delete Operation
    • Test successful soft delete of empty group in InvoiceGroupsTest.php
    • Test soft delete of group with invoices succeeds and sets invoice_group_id to NULL on related invoices
    • Test deleted group no longer appears in list
    • Test deleting group from different company fails authorization
    • Test attempting to edit deleted group fails
    Task 6: Test Edge Cases
    • Test in InvoiceGroupsTest.php with group names containing special characters (quotes, apostrophes, Unicode)
    • Test with very long group descriptions (near max length)
    • Test bulk deletion of multiple groups
    • Test invoice group filter/search functionality on invoice list pages
    • Test invoice creation/update with valid and invalid invoice_group_id values
    🤖 Prompt for AI agents
    Create comprehensive test coverage for invoice groups using AAA pattern. This
    phase ensures all operations and edge cases are properly tested.
    
    Create InvoiceGroupFactory:
    - Extend AbstractFactory
    - Default attributes: name (Faker), description (paragraph), company_id
    (resolveCompanyId())
    - State method withInvoices() for creating related invoices
    
    Test list and view operations:
    - Extend AbstractCompanyPanelTestCase
    - Verify company-scoped listing with AAA comments
    - Test viewing group details and associated invoices
    - Verify soft-deleted groups excluded
    
    Test create operation:
    - Valid data creation success
    - Validation: required name, unique within company, max length
    - Auto-set company_id
    - Cross-company duplicate name allowed
    
    Test edit operation:
    - Successful updates
    - Duplicate name validation within company
    - Cross-company authorization failure
    - Prevent editing soft-deleted groups
    
    Test delete operation:
    - Soft delete empty group
    - Soft delete with invoices (nullify invoice_group_id)
    - Verify removal from list
    - Cross-company authorization
    - Prevent editing deleted groups
    
    Test edge cases:
    - Special characters in names
    - Long descriptions
    - Bulk deletion
    - Filter/search functionality
    - Valid/invalid invoice_group_id values
    

    🚀 Next Steps

    🤖 All AI agent prompts combined
    Task: 1
    
    Create the database schema and model layer for invoice groups with multi-tenant
    scoping. This phase establishes the foundational data structure.
    
    Create invoice_groups table migration:
    - Add columns: id, company_id (FK), name (string, required), description (text,
    nullable), deleted_at, timestamps
    - Foreign key company_id → companies (cascade on delete)
    - Index on company_id
    - Unique constraint on (company_id, name)
    
    Alter invoices table migration:
    - Add invoice_group_id (nullable FK)
    - Foreign key invoice_group_id → invoice_groups.id (SET NULL on delete)
    - Index on invoice_group_id
    
    Create InvoiceGroup model:
    - Use BelongsToCompany and SoftDeletes traits
    - Fillable: name, description, company_id
    - Relationships: invoices() hasMany, company() belongsTo
    
    Update Invoice model:
    - Add invoice_group_id to @property docblock
    - Add invoiceGroup() belongsTo relationship
    - Add invoice_group_id to fillable
    ===============================================================================
    
    Task: 2
    
    Build the Filament admin interface for invoice group management in the Company
    Panel. This phase creates the full UI for CRUD operations.
    
    Create InvoiceGroupResource:
    - Configure with InvoiceGroup model
    - Navigation: icon and group under "Invoices"
    - Table columns: name, description, invoice count (relationship count),
    created_at, updated_at
    - Filters: search by name, date ranges
    - Bulk delete with soft delete support
    - Row actions: view, edit, delete (with confirmation)
    
    Create form schema:
    - Text input: name (required, max 255)
    - Textarea: description (nullable)
    - Validation: unique name within company scope
    - company_id auto-set via BelongsToCompany
    
    Create resource pages:
    - ListInvoiceGroups: table with search/filters
    - CreateInvoiceGroup: creation form
    - EditInvoiceGroup: edit form
    - ViewInvoiceGroup: details with related invoices
    
    Update invoice form:
    - Add Select field for invoice_group_id (nullable, searchable)
    - Options scoped to current company
    - Position near invoice number/status
    ===============================================================================
    
    Task: 3
    
    Create comprehensive test coverage for invoice groups using AAA pattern. This
    phase ensures all operations and edge cases are properly tested.
    
    Create InvoiceGroupFactory:
    - Extend AbstractFactory
    - Default attributes: name (Faker), description (paragraph), company_id
    (resolveCompanyId())
    - State method withInvoices() for creating related invoices
    
    Test list and view operations:
    - Extend AbstractCompanyPanelTestCase
    - Verify company-scoped listing with AAA comments
    - Test viewing group details and associated invoices
    - Verify soft-deleted groups excluded
    
    Test create operation:
    - Valid data creation success
    - Validation: required name, unique within company, max length
    - Auto-set company_id
    - Cross-company duplicate name allowed
    
    Test edit operation:
    - Successful updates
    - Duplicate name validation within company
    - Cross-company authorization failure
    - Prevent editing soft-deleted groups
    
    Test delete operation:
    - Soft delete empty group
    - Soft delete with invoices (nullify invoice_group_id)
    - Verify removal from list
    - Cross-company authorization
    - Prevent editing deleted groups
    
    Test edge cases:
    - Special characters in names
    - Long descriptions
    - Bulk deletion
    - Filter/search functionality
    - Valid/invalid invoice_group_id values
    
    💡 Iterate on the plan with: @coderabbitai <feedback>
    Example Feedback
    - @coderabbitai You can skip phase 3. Add a simple unit test case for phase 2.
    - @coderabbitai For assumption 1 go ahead with option 3 and replan.
    

    💬 Have feedback or questions? Drop into our discord!

  5. changed the title [-]As an Admin, in the company Panel I want to be able to view, edit, delete Invoice Groups[/-] [+][Settings]: Company panel — manage Invoice Groups[/+] on Jun 13, 2026
  6. nielsdrost7 commented on Jul 24, 2026

    @nielsdrost7
    CollaboratorAuthor

    SMART Plan — Company panel — manage Invoice Groups (Numbering)

    Specific

    Add a NumberingResource to the Company panel (/{tenant}/invoice-groups). Provides full CRUD for Numbering (Invoice Group) series scoped to the current tenant company. Only accessible to customer_admin role.

    Measurable

    • CRUD table view listing company's invoice groups/numbering series.
    • Create action opens form with name, prefix, and starting number fields.
    • Edit action updates existing series.
    • Delete action removes series (soft-delete recommended to preserve invoice links).
    • A Filament success notification confirms each action.

    Achievable

    1. Verify Modules/Core/Models/Numbering.php has BelongsToCompany trait.
    2. Create Modules/Core/Filament/Company/Resources/NumberingResource.php.
    3. Register the resource in CompanyPanelProvider.
    4. Add tests: create, update, delete actions.

    Relevant

    Multi-entity companies often need separate invoice numbering series per division/brand. A CRUD screen lets them manage without admin panel access.

    Time-bound

    Sprint 6.

  7. nielsdrost7 commented on Jul 24, 2026

    @nielsdrost7
    CollaboratorAuthor

    Already satisfied. Modules/Core/Filament/Company/Resources/Numberings/NumberingResource.php already provides view + edit for a company's own numbering schemes (canViewAny/canView/canEdit gated on Permission::MANAGE_COMPANY_SETTINGS), scoped correctly (confirmed via NumberingTest::it_filters_numberings_by_current_company_id, NumberingPanelAccessTest::it_allows_company_user_to_edit_their_numbering_format — both passing).

    canCreate/canDelete are deliberately hardcoded false, with an explicit docblock: "Company users cannot: Create new numbering schemes (only admins can) ... " — this looks like an intentional design decision (numbering schemes affect invoice/quote numbering integrity in ways that probably warrant admin oversight), not an oversight. Closing as satisfied by existing behavior; reopen if the intent was specifically to open up create/delete to customer_admin.

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

    refinedRefined with SMART plan / test suggestions

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions