Skip to content

feat(products): filter products by category and unit#592

Open
nielsdrost7 wants to merge 1 commit into
InvoicePlane:developfrom
underdogg-forks:feature/390-product-category-filter
Open

feat(products): filter products by category and unit#592
nielsdrost7 wants to merge 1 commit into
InvoicePlane:developfrom
underdogg-forks:feature/390-product-category-filter

Conversation

@nielsdrost7

@nielsdrost7 nielsdrost7 commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds two dropdown filters to the company-panel products table (Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php):

  • CategorySelectFilter on category_id, options from ProductCategory (ordered by name)
  • UnitSelectFilter on unit_id, options from ProductUnit (ordered by name)

Both option lists are tenant-scoped automatically by the BelongsToCompany global scope, so a company only ever sees its own categories/units. Same pattern as the numbering filter on the invoices table (PR #581).

Closes

Closes #390 — products can be filtered by category (and by unit, same pattern, added while in there)

Test plan

Modules/Products/Tests/Feature/ProductCategoryFilterTest.php (3 tests, 15 assertions — all passing locally against MariaDB):

  • Filtering by category shows only products in that category
  • Filtering by unit shows only products with that unit
  • Filter options only contain the current tenant's categories (cross-company category invisible)
  • Full Products module suite: 36 passed, 3 skipped (pre-existing skips)

Note: the local test run needed the permission-seeding test fixes from origin/develop (fix(tests): seed roles/permissions in panel test base classes) which are not on upstream/develop yet — without them every company-panel Livewire test 403s. The feature itself has no dependency on them.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added product list filters for category and unit, making it easier to narrow down products.
    • Filter options now respect the current tenant, so only relevant categories are shown.
  • Bug Fixes

    • Improved product list filtering behavior to ensure results update correctly when a category or unit is selected.

Closes InvoicePlane#390

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds two SelectFilter instances (category_id, unit_id) to the Products table, populated from tenant-scoped ProductCategory and ProductUnit queries, replacing the previously empty filters configuration. Adds a new feature test class validating filtering behavior and tenant scoping of filter options.

Changes

Product table filters

Layer / File(s) Summary
Category and unit filter implementation
Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php
Imports ProductCategory and ProductUnit models and replaces the empty filters array with SelectFilter definitions for category_id and unit_id, each building options from an ordered, plucked id-to-name query.
Feature tests for filter behavior and tenant scoping
Modules/Products/Tests/Feature/ProductCategoryFilterTest.php
Adds tests verifying products can be filtered by category and by unit, and that category filter options only include categories belonging to the current tenant.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant ListProducts
  participant ProductsTable
  participant ProductCategory
  participant ProductUnit

  User->>ListProducts: open products table
  ListProducts->>ProductsTable: render filters
  ProductsTable->>ProductCategory: query ordered names, pluck id => name
  ProductsTable->>ProductUnit: query ordered names, pluck id => name
  User->>ListProducts: select category_id/unit_id filter
  ListProducts->>ProductsTable: apply filter to query
  ProductsTable-->>ListProducts: filtered product rows
Loading

Related issues: #390 — adds filters to the products view for filtering by category/family.

Suggested labels: enhancement, products, filament

Suggested reviewers: none identified

🐰 A carrot filter, snug and neat,
Sorts my products, oh so sweet,
By family or by unit small,
Tenants keep their own — that's all!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding product filters by category and unit.
Linked Issues check ✅ Passed The PR adds product filters, satisfying the issue's request to filter products in the view.
Out of Scope Changes check ✅ Passed No unrelated changes are evident beyond the requested filters and supporting tests.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
📋 Issue Planner

Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).

View plan for ticket: #390

✨ 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
Modules/Products/Tests/Feature/ProductCategoryFilterTest.php (2)

90-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use imported Company class instead of fully-qualified inline reference.

\Modules\Core\Models\Company::factory() could use a top-level use import consistent with the rest of the file's style.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Modules/Products/Tests/Feature/ProductCategoryFilterTest.php` at line 90,
Replace the fully qualified Company reference in the product category filter
test with the imported Company model to match the file’s existing style. Update
the setup in ProductCategoryFilterTest to use the top-level Company import where
the factory is called, keeping the test code consistent with the other model
references in this class.

22-49: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Combine /* Act */ and /* Assert */ into separate blocks per test-structure guideline.

Both tests merge assertions and actions under a single /* Act + Assert */ comment. As per coding guidelines, "Structure tests with /* Arrange /, / Act /, and / Assert */ blocks; define all variables in the Act section before asserting."

✏️ Suggested restructuring (test 1 shown, apply similarly to test 2)
-        /* Act + Assert */
-        Livewire::actingAs($this->user)
+        /* Act */
+        $component = Livewire::actingAs($this->user)
             ->test(ListProducts::class, ['tenant' => Str::lower($this->company->search_code)])
-            ->assertSuccessful()
-            ->assertCanSeeTableRecords([$hardwareProduct, $softwareProduct])
-            ->filterTable('category_id', $hardware->id)
-            ->assertCanSeeTableRecords([$hardwareProduct])
-            ->assertCanNotSeeTableRecords([$softwareProduct]);
+            ->filterTable('category_id', $hardware->id);
+
+        /* Assert */
+        $component->assertSuccessful()
+            ->assertCanSeeTableRecords([$hardwareProduct])
+            ->assertCanNotSeeTableRecords([$softwareProduct]);

Also applies to: 53-80

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Modules/Products/Tests/Feature/ProductCategoryFilterTest.php` around lines 22
- 49, The test in ProductCategoryFilterTest::it_filters_products_by_category
currently mixes actions and assertions under a single Act + Assert section;
split the flow into distinct /* Arrange */, /* Act */, and /* Assert */ blocks.
Move the Livewire::actingAs(...)->test(...)->filterTable(...) interaction into
the Act block, then place the visibility assertions in the Assert block, and
apply the same restructuring to the other test in this file as well.

Source: Path instructions

Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php (1)

65-78: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use relationship() for the category/unit filters

ProductCategory and ProductUnit already carry the company scope, so these filters can be simplified to Filament relationships and the manual option queries/imports can go away.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php`
around lines 65 - 78, The category and unit filters in ProductsTable are
manually querying options even though ProductCategory and ProductUnit already
have the company scope and can be handled through Filament relationships. Update
the SelectFilter definitions for category_id and unit_id to use relationship()
instead of options(), and remove the now-unneeded ProductCategory/ProductUnit
option query logic and any related imports so the filters rely on the model
relationships directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Modules/Products/Tests/Feature/ProductCategoryFilterTest.php`:
- Around line 87-93: The test setup in ProductCategoryFilterTest creates
`$ownCategory` and `$foreignCategory` but the assertions still use hard-coded
category names, so the variables are unused. Update the relevant assertions in
the test methods that use these fixtures to reference the models’
`category_name` values instead of literals, using the existing `ProductCategory`
variables so the test stays aligned with the seeded data and static analysis no
longer flags them.

---

Nitpick comments:
In
`@Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php`:
- Around line 65-78: The category and unit filters in ProductsTable are manually
querying options even though ProductCategory and ProductUnit already have the
company scope and can be handled through Filament relationships. Update the
SelectFilter definitions for category_id and unit_id to use relationship()
instead of options(), and remove the now-unneeded ProductCategory/ProductUnit
option query logic and any related imports so the filters rely on the model
relationships directly.

In `@Modules/Products/Tests/Feature/ProductCategoryFilterTest.php`:
- Line 90: Replace the fully qualified Company reference in the product category
filter test with the imported Company model to match the file’s existing style.
Update the setup in ProductCategoryFilterTest to use the top-level Company
import where the factory is called, keeping the test code consistent with the
other model references in this class.
- Around line 22-49: The test in
ProductCategoryFilterTest::it_filters_products_by_category currently mixes
actions and assertions under a single Act + Assert section; split the flow into
distinct /* Arrange */, /* Act */, and /* Assert */ blocks. Move the
Livewire::actingAs(...)->test(...)->filterTable(...) interaction into the Act
block, then place the visibility assertions in the Assert block, and apply the
same restructuring to the other test in this file as well.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: da293fd8-9c84-4e24-ab29-810d68f200f4

📥 Commits

Reviewing files that changed from the base of the PR and between dc79079 and 47ca42b.

📒 Files selected for processing (2)
  • Modules/Products/Filament/Company/Resources/Products/Tables/ProductsTable.php
  • Modules/Products/Tests/Feature/ProductCategoryFilterTest.php

Comment on lines +87 to +93
$ownCategory = ProductCategory::factory()->for($this->company)->create([
'category_name' => 'Own Category',
]);
$companyB = \Modules\Core\Models\Company::factory()->create();
$foreignCategory = ProductCategory::factory()->for($companyB)->create([
'category_name' => 'Foreign Category',
]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Unused variables $ownCategory/$foreignCategory flagged by static analysis; use them instead of hard-coded strings.

Static analysis reports both variables as unused since the assertions reference literal strings rather than the created models' attributes, which also weakens the test's coupling to the actual seeded data.

🧹 Suggested fix
-        $this->assertContains('Own Category', $options);
-        $this->assertNotContains('Foreign Category', $options);
+        $this->assertContains($ownCategory->category_name, $options);
+        $this->assertNotContains($foreignCategory->category_name, $options);

Also applies to: 106-107

🧰 Tools
🪛 PHPMD (2.15.0)

[warning] 87-87: Avoid unused local variables such as '$ownCategory'. (undefined)

(UnusedLocalVariable)


[warning] 91-91: Avoid unused local variables such as '$foreignCategory'. (undefined)

(UnusedLocalVariable)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Modules/Products/Tests/Feature/ProductCategoryFilterTest.php` around lines 87
- 93, The test setup in ProductCategoryFilterTest creates `$ownCategory` and
`$foreignCategory` but the assertions still use hard-coded category names, so
the variables are unused. Update the relevant assertions in the test methods
that use these fixtures to reference the models’ `category_name` values instead
of literals, using the existing `ProductCategory` variables so the test stays
aligned with the seeded data and static analysis no longer flags them.

Source: Linters/SAST tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Products]: Filter Products by category/family

1 participant