Companies migrating from another system or managing products in spreadsheets need a reliable bulk-import path. Manual row-by-row entry for large catalogues is error-prone and time-consuming.
Sprint 5.
// Modules/Products/Tests/Feature/ProductsTest.php
#[Test]
public function it_imports_three_valid_product_rows_from_csv(): void
{
/* Arrange */
$csv = "code,product_name,description,price,cost_price,category_name,unit_name\n"
. "P001,Widget A,A widget,9.99,5.00,Widgets,Each\n"
. "P002,Widget B,B widget,14.99,7.00,Widgets,Each\n"
. "P003,Gadget X,A gadget,29.99,15.00,Gadgets,Box\n";
$file = UploadedFile::fake()->createWithContent('products.csv', $csv);
/* Act */
$component = Livewire::actingAs($this->user)
->test(ListProducts::class, ['tenant' => Str::lower($this->company->search_code)])
->callAction('import', ['file' => $file]);
/* Assert */
$component->assertSuccessful();
$this->assertDatabaseCount('products', 3);
}
#[Test]
public function it_skips_a_row_missing_product_name(): void
{
/* Arrange */
$csv = "code,product_name,description,price,cost_price,category_name,unit_name\n"
. "P001,,A widget,9.99,5.00,Widgets,Each\n"
. "P002,Widget B,B widget,14.99,7.00,Widgets,Each\n";
$file = UploadedFile::fake()->createWithContent('products.csv', $csv);
/* Act */
$component = Livewire::actingAs($this->user)
->test(ListProducts::class, ['tenant' => Str::lower($this->company->search_code)])
->callAction('import', ['file' => $file]);
/* Assert */
$component->assertSuccessful();
$this->assertDatabaseCount('products', 1);
}
SMART Plan — [Products] As a user_admin, I want to import products from external sources
Specific
Add a Filament
ImportActionto theListProductspage that accepts a CSV file with columnscode,product_name,description,price,cost_price,category_name,unit_name. Rows missing required fields (code,product_name,price) are skipped and reported back to the user as validation failures.Measurable
ListProductsheader.Productrecords scoped to the current company.product_nameis skipped; no partial record is created.category_nameandunit_nameare resolved to existingProductCategory/ProductUnitrecords (or created if absent).Achievable
Modules/Products/Filament/Company/Imports/ProductImporter.phpimplementing Filament'sImportercontract.ImportAction::make()->importer(ProductImporter::class)toModules/Products/Filament/Company/Resources/Products/Pages/ListProducts.php.resolveRecord()to upsert oncodewithin the current company scope.getValidationRules():coderequired,product_namerequired,pricerequired|numeric.Modules/Products/Tests/Feature/ProductsTest.php.Relevant
Companies migrating from another system or managing products in spreadsheets need a reliable bulk-import path. Manual row-by-row entry for large catalogues is error-prone and time-consuming.
Time-bound
Sprint 5.
Arrange · Act · Assert