Skip to content

[Settings]: Company panel — manage Payment Methods #237

Closed

Description

@nielsdrost7

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

Specific

Add a PaymentMethodResource to the Company panel (/{tenant}). The resource manages payment methods scoped to the current tenant company and provides a full CRUD interface: list, create, edit, and delete. It is accessible only to customer_admin or elevated roles.

Measurable

  • A customer_admin can view the list of payment methods for their company.
  • Edit and delete actions complete without errors and persist/remove data correctly.
  • Payment methods from other companies do not appear in the list (tenant isolation enforced).
  • A customer role cannot access the resource.

Achievable

  1. Create (or verify) PaymentMethod model with BelongsToCompany trait — Modules/Payments/Models/PaymentMethod.php
  2. Create migration for payment_methods table if it doesn't exist — Modules/Payments/Database/Migrations/
  3. Create PaymentMethodResource for the company panel — Modules/Payments/Filament/Company/Resources/PaymentMethods/PaymentMethodResource.php
  4. Create ListPaymentMethods, CreatePaymentMethod, EditPaymentMethod pages under the resource's Pages/ directory.
  5. Register the resource in CompanyPanelProvider — Modules/Core/Providers/CompanyPanelProvider.php
  6. Add role guard restricting access to customer_admin and elevated roles.

Relevant

Payment methods are the mechanism by which a company's customers pay invoices. Company admins must be able to configure, update, and remove payment options (e.g. bank transfer details, PayPal address) without involving a platform admin.

Time-bound

Sprint 6.


Arrange · Act · Assert

// Modules/Payments/Tests/Feature/Company/PaymentMethodResourceTest.php

#[Test]
public function customer_admin_can_list_payment_methods_in_company_panel(): void
{
    /* Arrange */
    $method        = PaymentMethod::factory()->for($this->company)->create();
    $otherMethod   = PaymentMethod::factory()->create(); // different company

    /* Act */
    $response = $this->testLivewire(ListPaymentMethods::class);

    /* Assert */
    $response->assertSuccessful()
             ->assertCanSeeTableRecords([$method])
             ->assertCanNotSeeTableRecords([$otherMethod]);
}

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

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

    /* Assert */
    $this->assertDatabaseMissing('payment_methods', ['id' => $method->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

    Convert Payment Methods from an enum-based system to a company-scoped database model with full CRUD capabilities through the Filament admin panel, including comprehensive AAA-structured tests and edge case coverage.

    Observations

    The application is a multi-tenant Laravel system using Filament for admin panels. Payment Methods currently exist as an enum (PaymentMethod.php) with 5 cases (bank_transfer, cash, credit_card, paypal, stripe) that are directly cast on the Payment model's payment_method string column. The codebase follows a modular architecture where company-scoped resources use the BelongsToCompany trait, Filament resources delegate to separate form/table configurators, and tests extend AbstractCompanyPanelTestCase. The Payments module includes models, migrations, services, Filament resources, and comprehensive business logic for payment operations.

    Assumptions
    Assumption 1: Should Payment Methods maintain backward compatibility with the enum structure?

    Options Considered:

    • Keep enum as a "type" field on PaymentMethod model for classification
    • Remove enum entirely and use free-form string names with validation
    • Create PaymentMethod model but keep enum for seeding and type validation

    Chosen Option: 3

    Rationale: This provides the cleanest migration path—the enum becomes a reference for seeding default payment methods per company, while the model enables CRUD. Existing Payment records migrate to foreign key relationships, and the enum can later be deprecated once all references are updated.

    💡 User Tips

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

    Implementation Steps

    Phase 1: Database Schema and Model Creation

    Create the PaymentMethod model as a company-scoped entity and migrate existing enum-based payment data to the new structure.

    Task 1: Create PaymentMethod Model and Initial Migration

    Create a company-scoped PaymentMethod model that will replace the enum-based system.

    • Create Modules/Payments/Models/PaymentMethod.php model with BelongsToCompany trait
    • Add fillable fields: name, description, is_active, sort_order
    • Define belongsTo relationship to Company
    • Create migration create_payment_methods_table.php in Modules/Payments/Database/Migrations/
    • Table structure: id, company_id (FK), name (string), description (nullable text), is_active (boolean, default true), sort_order (integer, nullable), timestamps
    • Add foreign key constraint on company_id with cascadeOnDelete
    Task 2: Seed Default Payment Methods from Enum

    Create a data migration to populate payment_methods table with default values from the existing enum for all companies.

    • Create migration seed_payment_methods_from_enum.php in Modules/Payments/Database/Migrations/
    • For each existing Company, create 5 PaymentMethod records using the enum cases (BANK_TRANSFER, CASH, CREDIT_CARD, PAYPAL, STRIPE)
    • Use enum's label() method to set name field
    • Set is_active to true for all default methods
    • Store enum value in a metadata column or use normalized names that match enum values
    Task 3: Add Foreign Key Relationship to Payments Table

    Migrate the Payment model from using an enum-cast string to a foreign key relationship with PaymentMethod.

    • Create migration add_payment_method_id_to_payments_table.php in Modules/Payments/Database/Migrations/
    • Add payment_method_id column (unsignedBigInteger, nullable temporarily for migration)
    • Add foreign key constraint to payment_methods table with restrictOnDelete
    • Create data migration to populate payment_method_id by matching string payment_method values to corresponding PaymentMethod records
    • After data migration, make payment_method_id non-nullable
    • Do NOT drop payment_method string column yet (keep for rollback safety)
    Task 4: Update Payment Model

    Modify the Payment model to use the new relationship while maintaining backward compatibility temporarily.

    • Update Modules/Payments/Models/Payment.php to add belongsTo relationship for paymentMethod()
    • Update $fillable to include payment_method_id
    • Keep payment_method enum cast temporarily for backward compatibility
    • Add accessor/mutator to sync payment_method and payment_method_id during transition period
    🤖 Prompt for AI agents
    This phase establishes the PaymentMethod model and database schema, migrating
    from enum-based payment methods to a company-scoped relational model. The goal
    is to create the foundation for CRUD operations while maintaining backward
    compatibility.
    
    Create the PaymentMethod model with BelongsToCompany trait, fillable fields
    (name, description, is_active, sort_order), and a belongsTo relationship to
    Company.
    
    Create migration for payment_methods table with columns: id, company_id (FK with
    cascadeOnDelete), name (string), description (nullable text), is_active (boolean
    default true), sort_order (integer nullable), and timestamps.
    
    Create a data migration to seed default payment methods from the existing enum
    (BANK_TRANSFER, CASH, CREDIT_CARD, PAYPAL, STRIPE) for all existing companies,
    using the enum's label() method for names.
    
    Create migration to add payment_method_id column to payments table
    (unsignedBigInteger, nullable initially), add foreign key constraint to
    payment_methods with restrictOnDelete, and populate payment_method_id by
    matching enum string values. Make payment_method_id non-nullable after data
    migration but keep the old payment_method string column for rollback safety.
    
    Update Payment model to add paymentMethod() belongsTo relationship, include
    payment_method_id in $fillable, keep enum cast temporarily, and add
    accessor/mutator to sync both fields during transition.
    

    Phase 2: Filament CRUD Interface for Payment Methods

    Build the Filament admin interface for managing Payment Methods in the company panel following established patterns.

    Task 1: Create PaymentMethod Filament Resource

    Create the Filament resource structure for Payment Methods in the company panel.

    • Create Modules/Payments/Filament/Company/Resources/PaymentMethods/PaymentMethodResource.php extending BaseResource
    • Set model binding to PaymentMethod::class
    • Set isScopedToTenant = true for company scoping
    • Define getPages() to return index page route
    • Add navigation icon and group to organize under Payments section
    Task 2: Create PaymentMethod Form Configurator

    Build the form schema for creating and editing Payment Methods.

    • Create Modules/Payments/Filament/Company/Resources/PaymentMethods/Schemas/PaymentMethodForm.php configurator class
    • Implement configure() method returning form schema with fields:
      • TextInput for name (required, max 255)
      • Textarea for description (nullable)
      • Toggle for is_active (default true)
      • Number input for sort_order (nullable)
    • Add validation rules matching model requirements
    • Follow existing form configurator patterns from ContactForm, ProductForm
    Task 3: Create PaymentMethod Table Configurator

    Build the table configuration for listing and managing Payment Methods.

    • Create Modules/Payments/Filament/Company/Resources/PaymentMethods/Schemas/PaymentMethodsTable.php configurator class
    • Implement configure() method with columns: name, description (truncated), is_active (boolean badge), sort_order
    • Add filters for is_active status
    • Add bulk actions for activate/deactivate
    • Add individual row actions: Edit, Delete (with confirmation)
    • Include soft delete prevention if PaymentMethod has associated Payment records (check hasMany relationship count)
    Task 4: Create PaymentMethod List Page

    Create the list page component for the Payment Methods resource.

    • Create Modules/Payments/Filament/Company/Resources/PaymentMethods/Pages/ListPaymentMethods.php extending ListRecords
    • Configure to use PaymentMethodsTable
    • Add header actions for creating new payment methods
    • Implement company scoping through getEloquentQuery() if not handled by BaseResource
    Task 5: Add Deletion Protection Logic

    Implement business logic to prevent deletion of Payment Methods that are in use by existing Payments.

    • Update Modules/Payments/Models/PaymentMethod.php to add hasMany relationship to Payment
    • Create PaymentMethodObserver in Modules/Payments/Observers/ directory
    • Implement deleting event to check if paymentMethod->payments()->exists()
    • If exists, throw ValidationException with user-friendly message
    • Register observer in PaymentsServiceProvider
    🤖 Prompt for AI agents
    This phase builds the complete Filament admin interface for Payment Methods CRUD
    operations. The goal is to create a fully functional admin panel following
    established codebase patterns with proper company scoping and deletion
    protection.
    
    Create PaymentMethodResource extending BaseResource with model binding to
    PaymentMethod::class, isScopedToTenant = true, getPages() returning index route,
    and navigation configured under Payments section.
    
    Create PaymentMethodForm configurator with configure() method returning form
    schema containing TextInput for name (required, max 255), Textarea for
    description (nullable), Toggle for is_active (default true), and Number input
    for sort_order (nullable). Follow existing configurator patterns.
    
    Create PaymentMethodsTable configurator with columns for name, description
    (truncated), is_active (boolean badge), and sort_order. Add filters for
    is_active status, bulk actions for activate/deactivate, and row actions for Edit
    and Delete with confirmation. Include deletion prevention logic checking hasMany
    relationship count.
    
    Create ListPaymentMethods page extending ListRecords, configured to use
    PaymentMethodsTable, with header actions for creating new payment methods and
    company scoping via getEloquentQuery() if needed.
    
    Add hasMany relationship to Payment in PaymentMethod model. Create
    PaymentMethodObserver with deleting event that checks if payments exist and
    throws ValidationException if they do. Register observer in
    PaymentsServiceProvider.
    

    Phase 3: Update Payment Dependencies

    Update all components that interact with Payment Methods to use the new relationship model.

    Task 1: Update Payment Form to Use Relationship

    Modify the Payment form to select from company's PaymentMethod records instead of enum cases.

    • Update Modules/Payments/Filament/Company/Resources/Payments/Schemas/PaymentForm.php
    • Replace PaymentMethod::cases() enum select with relationship select
    • Configure select to load options from auth()->user()->company->paymentMethods()->where('is_active', true)
    • Use getOptionLabelFromRecordUsing() to display payment method name
    • Add searchable and preload options for better UX
    Task 2: Update PaymentService

    Modify the PaymentService to handle payment_method_id instead of enum values.

    • Update Modules/Payments/Services/PaymentService.php create method
    • Ensure payment_method_id is properly handled in data processing
    • Update validation logic to check payment_method_id exists and belongs to user's company
    • Remove enum-specific processing logic if any
    Task 3: Update Payment Factory and Seeder

    Update test data generation to use PaymentMethod relationships.

    • Update Modules/Payments/Database/Factories/PaymentFactory.php
    • Replace payment_method enum value with payment_method_id callback
    • Callback should create or find PaymentMethod for the payment's company
    • Update Modules/Payments/Database/Seeders/PaymentsSeeder.php to ensure PaymentMethod records exist before creating Payments
    • Ensure seeder creates PaymentMethod records for test company first
    🤖 Prompt for AI agents
    This phase updates all existing Payment-related components to use the new
    PaymentMethod relationship model instead of the enum. The goal is to ensure
    seamless integration across forms, services, and test data generation.
    
    Update PaymentForm to replace PaymentMethod::cases() enum select with a
    relationship select that loads options from
    auth()->user()->company->paymentMethods()->where('is_active', true). Use
    getOptionLabelFromRecordUsing() to display names and add searchable/preload
    options.
    
    Update PaymentService create method to handle payment_method_id instead of enum
    values. Ensure payment_method_id is processed correctly and add validation to
    check it exists and belongs to the user's company. Remove any enum-specific
    processing logic.
    
    Update PaymentFactory to replace payment_method enum value with
    payment_method_id callback that creates or finds a PaymentMethod for the
    payment's company. Update PaymentsSeeder to create PaymentMethod records for the
    test company before creating Payment records.
    

    Phase 4: Payment Method CRUD Tests with AAA Structure

    Create comprehensive test coverage for Payment Method CRUD operations with explicit Arrange-Act-Assert structure.

    Task 1: Create Test File Structure and Helpers

    Set up the test file with proper organization and reusable helper methods.

    • Create Modules/Payments/Tests/Feature/PaymentMethodsTest.php extending AbstractCompanyPanelTestCase
    • Add test group attribute #[Group('payment-methods')]
    • Create setUp() method to initialize common test fixtures
    • Add helper methods:
      • createPaymentMethod(array $attributes = []): PaymentMethod
      • createPaymentWithMethod(PaymentMethod $method): Payment
      • createInactivePaymentMethod(): PaymentMethod
      • actingAsUserWithCompany(): void
    Task 2: Test Payment Method List View - Happy Path

    Verify that users can successfully view their company's payment methods list.

    Write test: test_user_can_view_payment_methods_list()

    Arrange:

    • Create authenticated user with company
    • Create 3 PaymentMethod records for user's company with distinct names

    Act:

    • Use Livewire::test() to navigate to ListPaymentMethods page
    • Render the table component

    Assert:

    • assertSuccessful() - page loads without errors
    • assertCanSeeTableRecords() - all 3 payment methods visible
    • Assert each payment method name appears in the table
    Task 3: Test Payment Method List View - Empty State

    Verify proper handling when company has no payment methods.

    Write test: test_payment_methods_list_shows_empty_state_when_no_records()

    Arrange:

    • Create authenticated user with company
    • Ensure no PaymentMethod records exist for the company

    Act:

    • Navigate to ListPaymentMethods page
    • Render the table

    Assert:

    • assertSuccessful()
    • assertSee() with empty state message
    • assertCannotSeeTableRecords()
    Task 4: Test Payment Method Creation - Valid Data

    Test successful creation of a new payment method with valid data.

    Write test: test_user_can_create_payment_method_with_valid_data()

    Arrange:

    • Create authenticated user with company
    • Prepare valid payment method data: ['name' => 'Wire Transfer', 'description' => 'Bank wire transfer', 'is_active' => true, 'sort_order' => 1]

    Act:

    • Navigate to CreatePaymentMethod page
    • fillForm() with prepared data
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods') with name and company_id
    • assertRedirect() to list page
    • assertNotified() with success message
    Task 5: Test Payment Method Creation - Required Field Validation

    Test that required fields are validated during creation.

    Write test: test_payment_method_creation_requires_name_field()

    Arrange:

    • Create authenticated user with company
    • Prepare invalid data with missing name: ['description' => 'Test', 'is_active' => true]

    Act:

    • Navigate to CreatePaymentMethod page
    • fillForm() with invalid data
    • call('create') action

    Assert:

    • assertHasFormErrors(['name'])
    • assertDatabaseMissing('payment_methods')
    • Assert error message indicates name is required
    Task 6: Test Payment Method Creation - Name Length Validation

    Test that name field respects maximum length constraints.

    Write test: test_payment_method_name_cannot_exceed_maximum_length()

    Arrange:

    • Create authenticated user with company
    • Prepare data with name exceeding 255 characters: ['name' => str_repeat('a', 256)]

    Act:

    • Navigate to CreatePaymentMethod page
    • fillForm() with oversized name
    • call('create') action

    Assert:

    • assertHasFormErrors(['name'])
    • Assert error message indicates max length violation
    • assertDatabaseMissing('payment_methods')
    Task 7: Test Payment Method Creation - Duplicate Name Handling

    Test that duplicate payment method names are handled appropriately within same company.

    Write test: test_company_can_create_payment_methods_with_duplicate_names()

    Arrange:

    • Create authenticated user with company
    • Create existing PaymentMethod with name 'Credit Card'
    • Prepare data with same name: ['name' => 'Credit Card', 'description' => 'Different description']

    Act:

    • Navigate to CreatePaymentMethod page
    • fillForm() with duplicate name
    • call('create') action

    Assert:

    • assertHasNoFormErrors() - duplicates allowed within same company
    • assertDatabaseCount('payment_methods', 2)
    • Assert both records exist with same name but different descriptions
    Task 8: Test Payment Method Creation - Special Characters in Name

    Test that special characters in payment method names are handled correctly.

    Write test: test_payment_method_name_accepts_special_characters()

    Arrange:

    • Create authenticated user with company
    • Prepare data with special characters: ['name' => 'Credit/Debit & Cash - €/$']

    Act:

    • Navigate to CreatePaymentMethod page
    • fillForm() with special character name
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods', ['name' => 'Credit/Debit & Cash - €/$'])
    • Assert special characters stored correctly without escaping issues
    Task 9: Test Payment Method Edit - Happy Path

    Test successful editing of an existing payment method.

    Write test: test_user_can_edit_existing_payment_method()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod: ['name' => 'Original Name', 'is_active' => true]
    • Prepare updated data: ['name' => 'Updated Name', 'description' => 'New description', 'is_active' => false]

    Act:

    • Navigate to EditPaymentMethod page with payment method ID
    • fillForm() with updated data
    • call('save') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods', ['name' => 'Updated Name', 'is_active' => false])
    • assertDatabaseMissing('payment_methods', ['name' => 'Original Name'])
    • assertNotified() with success message
    Task 10: Test Payment Method Edit - Validation on Update

    Test that validation rules apply when editing payment methods.

    Write test: test_editing_payment_method_validates_required_fields()

    Arrange:

    • Create authenticated user with company
    • Create existing PaymentMethod with valid data
    • Prepare update data with empty name: ['name' => '', 'description' => 'Test']

    Act:

    • Navigate to EditPaymentMethod page
    • fillForm() with invalid data
    • call('save') action

    Assert:

    • assertHasFormErrors(['name'])
    • assertDatabaseMissing('payment_methods', ['name' => ''])
    • Assert original payment method data unchanged
    Task 11: Test Payment Method Edit - Preserving Relationships

    Test that editing a payment method preserves its relationships with existing payments.

    Write test: test_editing_payment_method_preserves_existing_payment_relationships()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod with name 'Original'
    • Create 3 Payment records using this payment method
    • Prepare update data: ['name' => 'Updated Name']

    Act:

    • Edit the payment method name
    • call('save') action

    Assert:

    • assertHasNoFormErrors()
    • Assert all 3 payments still reference same payment_method_id
    • Assert payments->paymentMethod->name returns 'Updated Name'
    • Assert relationship integrity maintained
    Task 12: Test Payment Method Deletion - Unused Method

    Test successful deletion of a payment method with no associated payments.

    Write test: test_user_can_delete_unused_payment_method()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod with no associated payments
    • Verify payments()->count() === 0

    Act:

    • Navigate to ListPaymentMethods page
    • call('delete') action on the payment method row
    • Confirm deletion in modal

    Assert:

    • assertNotified() with success message
    • assertDatabaseMissing('payment_methods', ['id' => payment method ID])
    • Assert record deleted from database
    Task 13: Test Payment Method Deletion - Method In Use

    Test that payment methods with associated payments cannot be deleted.

    Write test: test_user_cannot_delete_payment_method_in_use()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod
    • Create Payment using createPaymentWithMethod() helper
    • Verify payments()->count() === 1

    Act:

    • Navigate to ListPaymentMethods page
    • Attempt to call('delete') action on the payment method
    • Attempt to confirm deletion

    Assert:

    • assertNotified() with error message indicating method is in use
    • assertDatabaseHas('payment_methods', ['id' => payment method ID])
    • Assert payment method still exists with associated payment
    • Assert ValidationException thrown by observer
    Task 14: Test Payment Method Deletion - Multiple Payments

    Test deletion prevention when payment method has multiple associated payments.

    Write test: test_cannot_delete_payment_method_with_multiple_payments()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod
    • Create 5 Payment records all using this payment method
    • Verify payments()->count() === 5

    Act:

    • Attempt to delete the payment method via table action

    Assert:

    • assertNotified() with error message
    • assertDatabaseHas('payment_methods', ['id' => payment method ID])
    • Assert all 5 payments still reference the payment method
    • Assert deletion prevented by observer
    Task 15: Test Payment Method Deletion - After Payments Deleted

    Test that payment method becomes deletable after all associated payments are removed.

    Write test: test_can_delete_payment_method_after_payments_removed()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod
    • Create 2 Payment records using this method
    • Delete both payment records

    Act:

    • Attempt to delete the payment method
    • Confirm deletion

    Assert:

    • assertHasNoFormErrors()
    • assertNotified() with success
    • assertDatabaseMissing('payment_methods', ['id' => payment method ID])
    • Assert successful deletion after payments removed
    Task 16: Test Company Scoping - List Isolation

    Verify that payment methods list only shows company's own records.

    Write test: test_payment_methods_list_scoped_to_users_company()

    Arrange:

    • Create Company A with user and 3 PaymentMethods
    • Create Company B with 2 PaymentMethods
    • Authenticate as Company A user

    Act:

    • Navigate to ListPaymentMethods page
    • Render table

    Assert:

    • assertCanSeeTableRecords() with Company A's 3 payment methods
    • assertCannotSeeTableRecords() with Company B's payment methods
    • Assert company scoping via BelongsToCompany trait working correctly
    Task 17: Test Company Scoping - Edit Isolation

    Verify that users cannot edit payment methods from other companies.

    Write test: test_user_cannot_edit_other_company_payment_methods()

    Arrange:

    • Create Company A with authenticated user
    • Create Company B with PaymentMethod
    • Get ID of Company B's payment method

    Act:

    • Attempt to navigate to EditPaymentMethod page with Company B's payment method ID
    • Attempt to load form

    Assert:

    • assertForbidden() or assertNotFound()
    • Assert cannot see form
    • Assert Company B's payment method unchanged
    Task 18: Test Company Scoping - Delete Isolation

    Verify that users cannot delete payment methods from other companies.

    Write test: test_user_cannot_delete_other_company_payment_methods()

    Arrange:

    • Create Company A with authenticated user
    • Create Company B with PaymentMethod
    • Get ID of Company B's payment method

    Act:

    • Attempt to call delete action with Company B's payment method ID from ListPaymentMethods

    Assert:

    • assertForbidden() or assertNotFound()
    • assertDatabaseHas('payment_methods', ['id' => Company B payment method ID])
    • Assert Company B's payment method still exists and unchanged
    Task 19: Test Company Scoping - Cross-Company Creation

    Verify that created payment methods are automatically scoped to user's company.

    Write test: test_created_payment_methods_automatically_scoped_to_users_company()

    Arrange:

    • Create authenticated user with Company A
    • Prepare payment method data without company_id
    • Capture Company A's ID

    Act:

    • Create payment method via form
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods', ['name' => payment method name, 'company_id' => Company A ID])
    • Assert company_id automatically set by BelongsToCompany trait
    Task 20: Test Active/Inactive Status Toggle

    Test toggling payment method activation status.

    Write test: test_payment_method_can_be_activated_and_deactivated()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod with is_active = true

    Act:

    • Edit payment method and set is_active = false
    • call('save') action
    • Edit again and set is_active = true

    Assert:

    • After first edit: assertDatabaseHas('payment_methods', ['is_active' => false])
    • After second edit: assertDatabaseHas('payment_methods', ['is_active' => true])
    • Assert toggle works in both directions
    Task 21: Test Bulk Activation Action

    Test bulk action to activate multiple payment methods at once.

    Write test: test_can_bulk_activate_multiple_payment_methods()

    Arrange:

    • Create authenticated user with company
    • Create 3 inactive PaymentMethods (is_active = false)

    Act:

    • Navigate to ListPaymentMethods page
    • Select all 3 payment methods in table
    • Execute bulk 'activate' action

    Assert:

    • assertNotified() with success
    • Assert all 3 payment methods now have is_active = true
    • assertDatabaseCount('payment_methods', 3) with is_active = true filter
    Task 22: Test Bulk Deactivation Action

    Test bulk action to deactivate multiple payment methods at once.

    Write test: test_can_bulk_deactivate_multiple_payment_methods()

    Arrange:

    • Create authenticated user with company
    • Create 4 active PaymentMethods (is_active = true)

    Act:

    • Navigate to ListPaymentMethods page
    • Select all 4 payment methods
    • Execute bulk 'deactivate' action

    Assert:

    • assertNotified() with success
    • Assert all 4 payment methods now have is_active = false
    • Assert inactive payment methods still visible in admin list
    Task 23: Test Sort Order Functionality

    Test that payment methods respect custom sort_order values.

    Write test: test_payment_methods_display_in_sort_order()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethods: A (sort_order: 3), B (sort_order: 1), C (sort_order: 2)

    Act:

    • Navigate to ListPaymentMethods page
    • Render table

    Assert:

    • assertCanSeeTableRecords() in order: B, C, A
    • Assert table sorting respects sort_order column
    • Assert null sort_order values handled appropriately (appear last or first)
    Task 24: Test Filter by Active Status

    Test table filtering by is_active status.

    Write test: test_can_filter_payment_methods_by_active_status()

    Arrange:

    • Create authenticated user with company
    • Create 2 active and 2 inactive PaymentMethods

    Act:

    • Navigate to ListPaymentMethods page
    • Apply filter: is_active = true
    • Apply filter: is_active = false
    • Clear filters

    Assert:

    • With active filter: assertCanSeeTableRecords() only active methods
    • With inactive filter: assertCanSeeTableRecords() only inactive methods
    • Without filter: assertCanSeeTableRecords() all 4 methods
    🤖 Prompt for AI agents
    This phase creates comprehensive test coverage for Payment Method CRUD
    operations using explicit AAA (Arrange-Act-Assert) structure. The goal is to
    ensure all functionality, edge cases, multi-tenancy isolation, and validation
    work correctly.
    
    Create PaymentMethodsTest extending AbstractCompanyPanelTestCase with
    #[Group('payment-methods')] attribute. Add setUp() and helper methods:
    createPaymentMethod(), createPaymentWithMethod(), createInactivePaymentMethod(),
    actingAsUserWithCompany().
    
    Write happy path tests for list view, creation with valid data, editing,
    deletion of unused methods, company scoping, and status toggling. Each test
    explicitly arranges test data, acts on the system, and asserts expected
    outcomes.
    
    Write validation tests for required fields, name length constraints, special
    characters, duplicate names (allowed), and empty name validation on edit.
    
    Write deletion tests covering unused methods (success), methods in use
    (prevented), multiple payments (prevented), and deletion after payments removed
    (success).
    
    Write company scoping tests for list isolation, edit isolation, delete
    isolation, and automatic company assignment on creation.
    
    Write bulk action tests for activating and deactivating multiple payment methods
    simultaneously.
    
    Write feature tests for sort order display, filtering by active status, and
    empty state handling.
    
    Write relationship preservation test ensuring edits don't break payment
    relationships.
    

    Phase 5: Payment Integration Tests with AAA Structure

    Test that Payment model correctly integrates with the new PaymentMethod relationship.

    Task 1: Test Payment Creation with Valid Payment Method

    Test creating payments using valid payment method foreign keys.

    Write test: test_can_create_payment_with_valid_payment_method_id()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod for the company
    • Prepare payment data with payment_method_id

    Act:

    • Create payment via PaymentService or Filament form
    • Save to database

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payments', ['payment_method_id' => payment method ID])
    • Assert payment->paymentMethod relationship loads correctly
    • Assert payment->paymentMethod->name returns expected value
    Task 2: Test Payment Creation with Invalid Payment Method

    Test that creating payments with non-existent payment method IDs fails validation.

    Write test: test_cannot_create_payment_with_invalid_payment_method_id()

    Arrange:

    • Create authenticated user with company
    • Prepare payment data with payment_method_id = 9999 (non-existent)

    Act:

    • Attempt to create payment via form
    • call('create') action

    Assert:

    • assertHasFormErrors(['payment_method_id'])
    • assertDatabaseMissing('payments', ['payment_method_id' => 9999])
    • Assert validation error indicates invalid payment method
    Task 3: Test Payment Creation with Other Company's Payment Method

    Test that payments cannot use payment methods from different companies.

    Write test: test_cannot_create_payment_with_other_company_payment_method()

    Arrange:

    • Create Company A with authenticated user
    • Create Company B with PaymentMethod
    • Prepare payment data using Company B's payment_method_id

    Act:

    • Attempt to create payment as Company A user
    • call('create') action

    Assert:

    • assertHasFormErrors(['payment_method_id'])
    • assertDatabaseMissing('payments', ['payment_method_id' => Company B payment method ID])
    • Assert cross-company payment method usage prevented
    Task 4: Test Payment Form Shows Only Active Methods

    Test that payment creation form dropdown only displays active payment methods.

    Write test: test_payment_form_only_shows_active_payment_methods()

    Arrange:

    • Create authenticated user with company
    • Create 2 active PaymentMethods (is_active = true)
    • Create 2 inactive PaymentMethods (is_active = false)

    Act:

    • Navigate to Payment creation form
    • Inspect payment_method_id select field options

    Assert:

    • Assert select field contains only 2 options (active methods)
    • assertCannotSee() inactive payment method names in dropdown
    • Assert inactive methods excluded from form options
    Task 5: Test Payment Form Shows Company-Scoped Methods

    Test that payment form only shows payment methods from user's company.

    Write test: test_payment_form_only_shows_users_company_payment_methods()

    Arrange:

    • Create Company A with authenticated user and 3 PaymentMethods
    • Create Company B with 2 PaymentMethods

    Act:

    • Navigate to Payment creation form as Company A user
    • Inspect payment_method_id select field options

    Assert:

    • Assert select field contains exactly 3 options (Company A methods)
    • assertCannotSee() any Company B payment method names
    • Assert company scoping applied to form options
    Task 6: Test Payment Factory with PaymentMethod

    Test that payment factory creates valid payment method relationships.

    Write test: test_payment_factory_creates_valid_payment_methods()

    Arrange:

    • Create Company for factory context
    • No explicit payment method creation

    Act:

    • Payment::factory()->create()
    • Load created payment with paymentMethod relationship

    Assert:

    • Assert payment has valid payment_method_id
    • Assert payment->paymentMethod loads without error
    • Assert paymentMethod->company_id matches payment->company_id
    • Assert factory creates or finds appropriate payment method
    Task 7: Test Payment Relationship Query Performance

    Test that payment method relationships use efficient queries without N+1 issues.

    Write test: test_payment_relationship_queries_are_efficient()

    Arrange:

    • Create authenticated user with company
    • Create 5 PaymentMethods
    • Create 50 Payments spread across the payment methods

    Act:

    • Query Payment::with('paymentMethod')->get()
    • Enable query logging

    Assert:

    • Assert query count does not increase with number of payments (no N+1)
    • Assert eager loading works correctly
    • Assert relationship data accessible without additional queries
    Task 8: Test Payment Method Updates Reflect in Payments

    Test that updating a payment method's name reflects in associated payments.

    Write test: test_payment_method_name_updates_visible_to_associated_payments()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod with name 'Original Name'
    • Create 3 Payments using this payment method

    Act:

    • Update payment method name to 'Updated Name'
    • Reload payments and access paymentMethod relationship

    Assert:

    • Assert all 3 payments->paymentMethod->name returns 'Updated Name'
    • Assert relationship reflects current payment method data
    • Assert no stale data cached
    Task 9: Test Payment Deletion Does Not Affect Payment Method

    Test that deleting payments does not cascade delete the payment method.

    Write test: test_deleting_payment_does_not_delete_payment_method()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod
    • Create Payment using this payment method

    Act:

    • Delete the payment record
    • Verify deletion

    Assert:

    • assertDatabaseMissing('payments', ['id' => payment ID])
    • assertDatabaseHas('payment_methods', ['id' => payment method ID])
    • Assert payment method remains after payment deletion
    • Assert restrictOnDelete constraint working
    Task 10: Test Payment Seeder with PaymentMethods

    Test that payment seeder creates valid data with payment method relationships.

    Write test: test_payment_seeder_creates_valid_payment_methods()

    Arrange:

    • Truncate payments and payment_methods tables
    • Prepare test company

    Act:

    • Run PaymentsSeeder
    • Query all created payments with paymentMethod relationship

    Assert:

    • Assert seeder creates expected number of payments
    • Assert all payments have valid payment_method_id
    • Assert all payment->paymentMethod relationships load successfully
    • Assert seeder creates payment methods before payments
    🤖 Prompt for AI agents
    This phase tests the integration between Payment and PaymentMethod models,
    ensuring relationships, validation, and data integrity work correctly. The goal
    is to verify seamless integration across all payment operations.
    
    Write test for creating payments with valid payment_method_id, asserting
    successful creation and relationship loading.
    
    Write validation test for invalid payment_method_id (non-existent), asserting
    form errors and database integrity.
    
    Write cross-company test preventing payments from using other companies' payment
    methods, asserting validation failure.
    
    Write form tests verifying only active payment methods appear in dropdown and
    only company-scoped methods are available.
    
    Write factory test ensuring Payment::factory()->create() generates valid payment
    method relationships with correct company scoping.
    
    Write performance test checking for N+1 query issues when loading payments with
    paymentMethod relationship using query logging.
    
    Write update propagation test verifying payment method name changes reflect in
    associated payments without caching issues.
    
    Write cascade test ensuring payment deletion doesn't delete payment method due
    to restrictOnDelete constraint.
    
    Write seeder test verifying PaymentsSeeder creates valid payment methods before
    payments and all relationships load correctly.
    

    Phase 6: Edge Cases and Error Handling Tests

    Test edge cases, boundary conditions, and error scenarios with explicit AAA structure.

    Task 1: Test Concurrent Payment Method Creation

    Test handling of concurrent payment method creation attempts.

    Write test: test_concurrent_payment_method_creation_handles_race_conditions()

    Arrange:

    • Create authenticated user with company
    • Prepare identical payment method data for both requests

    Act:

    • Simulate 2 concurrent creation requests for payment methods with same name
    • Use parallel execution or transaction testing

    Assert:

    • Assert both requests succeed or one fails gracefully
    • Assert database integrity maintained
    • Assert no duplicate unique constraints violated (if implemented)
    Task 2: Test Payment Method with Extremely Long Description

    Test boundary conditions for description field.

    Write test: test_payment_method_description_handles_long_text()

    Arrange:

    • Create authenticated user with company
    • Prepare data with very long description (5000+ characters)

    Act:

    • Create payment method with long description
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods') with full description stored
    • Assert description field has no unexpected truncation
    Task 3: Test Payment Method with Null Optional Fields

    Test that nullable fields accept null values correctly.

    Write test: test_payment_method_accepts_null_for_optional_fields()

    Arrange:

    • Create authenticated user with company
    • Prepare data with only required name: ['name' => 'Test', 'description' => null, 'sort_order' => null]

    Act:

    • Create payment method
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods', ['name' => 'Test', 'description' => null, 'sort_order' => null])
    • Assert null values stored correctly
    Task 4: Test Payment Method Sort Order Boundaries

    Test sort order with negative, zero, and very large values.

    Write test: test_payment_method_sort_order_accepts_various_integer_values()

    Arrange:

    • Create authenticated user with company

    Act:

    • Create payment method with sort_order = -10
    • Create payment method with sort_order = 0
    • Create payment method with sort_order = 999999

    Assert:

    • assertHasNoFormErrors() for all cases
    • Assert all sort_order values stored correctly
    • Assert sorting works with negative and large values
    Task 5: Test Payment Method Name with Only Whitespace

    Test validation of names containing only whitespace.

    Write test: test_payment_method_name_cannot_be_only_whitespace()

    Arrange:

    • Create authenticated user with company
    • Prepare data with whitespace-only name: ['name' => ' ']

    Act:

    • Attempt to create payment method
    • call('create') action

    Assert:

    • assertHasFormErrors(['name'])
    • assertDatabaseMissing('payment_methods', ['name' => ' '])
    • Assert validation requires non-whitespace content
    Task 6: Test Payment Method Deactivation Impact on Existing Payments

    Test that deactivating a payment method does not affect existing payments using it.

    Write test: test_deactivating_payment_method_does_not_affect_existing_payments()

    Arrange:

    • Create authenticated user with company
    • Create active PaymentMethod
    • Create 3 Payments using this method

    Act:

    • Deactivate the payment method (is_active = false)
    • Load the 3 payments with paymentMethod relationship

    Assert:

    • Assert all 3 payments still reference the payment method
    • Assert payment->paymentMethod loads successfully despite is_active = false
    • Assert existing payments unaffected by deactivation
    Task 7: Test Payment Method with Unicode Characters

    Test that payment method names support international characters.

    Write test: test_payment_method_name_supports_unicode_characters()

    Arrange:

    • Create authenticated user with company
    • Prepare data with unicode name: ['name' => '电汇转账 - 日本 - €']

    Act:

    • Create payment method
    • call('create') action

    Assert:

    • assertHasNoFormErrors()
    • assertDatabaseHas('payment_methods', ['name' => '电汇转账 - 日本 - €'])
    • Assert unicode characters stored and retrieved correctly
    Task 8: Test Payment Method Deletion with Large Number of Payments

    Test deletion prevention with high volume of associated payments.

    Write test: test_cannot_delete_payment_method_with_many_payments()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod
    • Create 1000 Payment records using this method

    Act:

    • Attempt to delete payment method
    • Confirm deletion attempt

    Assert:

    • assertNotified() with error
    • assertDatabaseHas('payment_methods', ['id' => payment method ID])
    • Assert observer handles large relationship counts efficiently
    Task 9: Test Payment Creation Form After All Methods Deactivated

    Test payment form behavior when no active payment methods exist.

    Write test: test_payment_form_shows_message_when_no_active_payment_methods()

    Arrange:

    • Create authenticated user with company
    • Create 3 PaymentMethods, all with is_active = false

    Act:

    • Navigate to Payment creation form
    • Inspect payment_method_id field

    Assert:

    • assertSee() message indicating no active payment methods
    • Assert form either shows empty select or helpful message
    • Assert user cannot submit payment without active methods
    Task 10: Test Payment Method Observer Exception Handling

    Test that observer exceptions are caught and handled gracefully.

    Write test: test_payment_method_observer_handles_exceptions_gracefully()

    Arrange:

    • Create authenticated user with company
    • Create PaymentMethod with associated Payment
    • Mock database to throw exception during deletion check

    Act:

    • Attempt to delete payment method

    Assert:

    • assertNotified() with appropriate error message
    • Assert exception caught and user-friendly message displayed
    • assertDatabaseHas('payment_methods', ['id' => payment method ID])
    🤖 Prompt for AI agents
    This phase tests edge cases, boundary conditions, and error handling scenarios.
    The goal is to ensure the application handles unusual inputs, extreme values,
    and error conditions gracefully.
    
    Write concurrent creation test simulating race conditions with identical payment
    method data, asserting database integrity.
    
    Write boundary tests for extremely long descriptions (5000+ characters), null
    optional fields, and sort_order with negative/zero/large values.
    
    Write validation test for whitespace-only names, asserting form errors and
    database integrity.
    
    Write deactivation impact test verifying existing payments remain functional
    when payment method is deactivated.
    
    Write unicode support test with international characters in payment method
    names, asserting correct storage and retrieval.
    
    Write high-volume deletion test with 1000 associated payments, asserting
    observer efficiently prevents deletion.
    
    Write form behavior test when all payment methods are inactive, asserting
    helpful messaging or empty state handling.
    
    Write observer exception handling test mocking database errors during deletion
    checks, asserting graceful error handling and user-friendly messaging.
    

    🚀 Next Steps

    🤖 All AI agent prompts combined
    Task: 1
    
    This phase establishes the PaymentMethod model and database schema, migrating
    from enum-based payment methods to a company-scoped relational model. The goal
    is to create the foundation for CRUD operations while maintaining backward
    compatibility.
    
    Create the PaymentMethod model with BelongsToCompany trait, fillable fields
    (name, description, is_active, sort_order), and a belongsTo relationship to
    Company.
    
    Create migration for payment_methods table with columns: id, company_id (FK with
    cascadeOnDelete), name (string), description (nullable text), is_active (boolean
    default true), sort_order (integer nullable), and timestamps.
    
    Create a data migration to seed default payment methods from the existing enum
    (BANK_TRANSFER, CASH, CREDIT_CARD, PAYPAL, STRIPE) for all existing companies,
    using the enum's label() method for names.
    
    Create migration to add payment_method_id column to payments table
    (unsignedBigInteger, nullable initially), add foreign key constraint to
    payment_methods with restrictOnDelete, and populate payment_method_id by
    matching enum string values. Make payment_method_id non-nullable after data
    migration but keep the old payment_method string column for rollback safety.
    
    Update Payment model to add paymentMethod() belongsTo relationship, include
    payment_method_id in $fillable, keep enum cast temporarily, and add
    accessor/mutator to sync both fields during transition.
    ===============================================================================
    
    Task: 2
    
    This phase builds the complete Filament admin interface for Payment Methods CRUD
    operations. The goal is to create a fully functional admin panel following
    established codebase patterns with proper company scoping and deletion
    protection.
    
    Create PaymentMethodResource extending BaseResource with model binding to
    PaymentMethod::class, isScopedToTenant = true, getPages() returning index route,
    and navigation configured under Payments section.
    
    Create PaymentMethodForm configurator with configure() method returning form
    schema containing TextInput for name (required, max 255), Textarea for
    description (nullable), Toggle for is_active (default true), and Number input
    for sort_order (nullable). Follow existing configurator patterns.
    
    Create PaymentMethodsTable configurator with columns for name, description
    (truncated), is_active (boolean badge), and sort_order. Add filters for
    is_active status, bulk actions for activate/deactivate, and row actions for Edit
    and Delete with confirmation. Include deletion prevention logic checking hasMany
    relationship count.
    
    Create ListPaymentMethods page extending ListRecords, configured to use
    PaymentMethodsTable, with header actions for creating new payment methods and
    company scoping via getEloquentQuery() if needed.
    
    Add hasMany relationship to Payment in PaymentMethod model. Create
    PaymentMethodObserver with deleting event that checks if payments exist and
    throws ValidationException if they do. Register observer in
    PaymentsServiceProvider.
    ===============================================================================
    
    Task: 3
    
    This phase updates all existing Payment-related components to use the new
    PaymentMethod relationship model instead of the enum. The goal is to ensure
    seamless integration across forms, services, and test data generation.
    
    Update PaymentForm to replace PaymentMethod::cases() enum select with a
    relationship select that loads options from
    auth()->user()->company->paymentMethods()->where('is_active', true). Use
    getOptionLabelFromRecordUsing() to display names and add searchable/preload
    options.
    
    Update PaymentService create method to handle payment_method_id instead of enum
    values. Ensure payment_method_id is processed correctly and add validation to
    check it exists and belongs to the user's company. Remove any enum-specific
    processing logic.
    
    Update PaymentFactory to replace payment_method enum value with
    payment_method_id callback that creates or finds a PaymentMethod for the
    payment's company. Update PaymentsSeeder to create PaymentMethod records for the
    test company before creating Payment records.
    ===============================================================================
    
    Task: 4
    
    This phase creates comprehensive test coverage for Payment Method CRUD
    operations using explicit AAA (Arrange-Act-Assert) structure. The goal is to
    ensure all functionality, edge cases, multi-tenancy isolation, and validation
    work correctly.
    
    Create PaymentMethodsTest extending AbstractCompanyPanelTestCase with
    #[Group('payment-methods')] attribute. Add setUp() and helper methods:
    createPaymentMethod(), createPaymentWithMethod(), createInactivePaymentMethod(),
    actingAsUserWithCompany().
    
    Write happy path tests for list view, creation with valid data, editing,
    deletion of unused methods, company scoping, and status toggling. Each test
    explicitly arranges test data, acts on the system, and asserts expected
    outcomes.
    
    Write validation tests for required fields, name length constraints, special
    characters, duplicate names (allowed), and empty name validation on edit.
    
    Write deletion tests covering unused methods (success), methods in use
    (prevented), multiple payments (prevented), and deletion after payments removed
    (success).
    
    Write company scoping tests for list isolation, edit isolation, delete
    isolation, and automatic company assignment on creation.
    
    Write bulk action tests for activating and deactivating multiple payment methods
    simultaneously.
    
    Write feature tests for sort order display, filtering by active status, and
    empty state handling.
    
    Write relationship preservation test ensuring edits don't break payment
    relationships.
    ===============================================================================
    
    Task: 5
    
    This phase tests the integration between Payment and PaymentMethod models,
    ensuring relationships, validation, and data integrity work correctly. The goal
    is to verify seamless integration across all payment operations.
    
    Write test for creating payments with valid payment_method_id, asserting
    successful creation and relationship loading.
    
    Write validation test for invalid payment_method_id (non-existent), asserting
    form errors and database integrity.
    
    Write cross-company test preventing payments from using other companies' payment
    methods, asserting validation failure.
    
    Write form tests verifying only active payment methods appear in dropdown and
    only company-scoped methods are available.
    
    Write factory test ensuring Payment::factory()->create() generates valid payment
    method relationships with correct company scoping.
    
    Write performance test checking for N+1 query issues when loading payments with
    paymentMethod relationship using query logging.
    
    Write update propagation test verifying payment method name changes reflect in
    associated payments without caching issues.
    
    Write cascade test ensuring payment deletion doesn't delete payment method due
    to restrictOnDelete constraint.
    
    Write seeder test verifying PaymentsSeeder creates valid payment methods before
    payments and all relationships load correctly.
    ===============================================================================
    
    Task: 6
    
    This phase tests edge cases, boundary conditions, and error handling scenarios.
    The goal is to ensure the application handles unusual inputs, extreme values,
    and error conditions gracefully.
    
    Write concurrent creation test simulating race conditions with identical payment
    method data, asserting database integrity.
    
    Write boundary tests for extremely long descriptions (5000+ characters), null
    optional fields, and sort_order with negative/zero/large values.
    
    Write validation test for whitespace-only names, asserting form errors and
    database integrity.
    
    Write deactivation impact test verifying existing payments remain functional
    when payment method is deactivated.
    
    Write unicode support test with international characters in payment method
    names, asserting correct storage and retrieval.
    
    Write high-volume deletion test with 1000 associated payments, asserting
    observer efficiently prevents deletion.
    
    Write form behavior test when all payment methods are inactive, asserting
    helpful messaging or empty state handling.
    
    Write observer exception handling test mocking database errors during deletion
    checks, asserting graceful error handling and user-friendly messaging.
    
    💡 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. nielsdrost7 commented on Jan 3, 2026

    @nielsdrost7
    CollaboratorAuthor

    @coderabbitai make test Scenario's, including the subissues: Arrange, Act, Assert. And also make sure you test edge cases

  6. coderabbitai commented on Jan 3, 2026

    @coderabbitai
    Contributor

    I've updated the plan above based on your feedback. Ready to proceed when you are!

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

    @nielsdrost7
    CollaboratorAuthor

    SMART Plan — Company panel — manage Payment Methods

    Specific

    Add a PaymentMethodResource to the Company panel (/{tenant}/payment-methods). Provides full CRUD for Payment Methods scoped to the current tenant company. Only accessible to customer_admin role.

    Measurable

    • CRUD table view listing company's payment methods.
    • Create action opens form with name, description, and type fields.
    • Edit action updates existing method.
    • Delete action removes method.
    • A Filament success notification confirms each action.

    Achievable

    1. Verify Modules/Payments/Models/PaymentMethod.php (or similar) has BelongsToCompany trait.
    2. Create Modules/Payments/Filament/Company/Resources/PaymentMethodResource.php.
    3. Register the resource in CompanyPanelProvider.
    4. Add tests: create, update, delete actions.

    Relevant

    Companies often add/retire payment channels (PayPal, Stripe, bank transfer) based on business needs. A CRUD screen lets them self-manage without admin involvement.

    Time-bound

    Sprint 6.

  9. added a commit that references this issue on Jul 24, 2026
  10. nielsdrost7 commented on Jul 24, 2026

    @nielsdrost7
    CollaboratorAuthor

    Implemented on feature/235-243-company-settings-cluster, combined with #241 since they're the same feature — see that issue's comment for details. PaymentMethod stays the existing fixed enum (Modules/Payments/Enums/PaymentMethod.php, used only as Payment::payment_method's cast) rather than becoming a new CRUD-able entity: there's nothing else per-company to create/edit given a fixed, hardcoded set of gateway types (bank transfer, cash, credit card, PayPal, Stripe). The company-configurable surface is which of those are enabled — that's what this delivers.

  11. added a commit that references this issue on Aug 15, 2026
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