Skip to content

[Core]: Import options (catalog, pricelists) #139

Description

@nielsdrost7

SMART Plan — [Products] Import product catalogs and pricelists

Part of the import/export epic: #85. See also: #80 (Excel support), #140 (products CSV import), #141 (clients import).

Specific

Add a "Import" action to the Products list page that accepts CSV (and optionally Excel) files containing catalog / pricelist data. The supported columns match the product schema: code, product_name, description, price, cost_price, category_name, unit_name. Common third-party catalog formats (e.g. GS1, custom pricelists) can be uploaded using the provided CSV template.

v2 implementation note: In v2, this is built using Filament's ImportAction + a custom Importer class — not a controller/route. There is no manual HTTP endpoint.

Measurable

  • An "Import" button appears on the Products list header.
  • A sample CSV template is downloadable from the import modal.
  • Uploading a valid CSV inserts the correct number of Product records.
  • Rows with missing required fields (code, product_name, price) are skipped with per-row error messages.
  • Category and unit names are resolved to existing records or created if absent.

Achievable

  1. Create Modules/Products/Filament/Company/Imports/ProductImporter.php (Filament Importer contract).
  2. Add ImportAction::make()->importer(ProductImporter::class) to ListProducts.
  3. resolveRecord() upserts on code within the current company scope.
  4. Validation rules: code required, product_name required, price required|numeric.
  5. Add PHPUnit tests.

Relevant

Companies migrating from external ERP/POS systems or managing their product catalog in spreadsheets need a reliable bulk-import path. Manual entry for large catalogues is error-prone.

Time-bound

Sprint 5.


Arrange · Act · Assert

// Modules/Products/Tests/Feature/ProductsTest.php

#[Test]
public function it_imports_products_from_a_catalog_csv(): void
{
    /* Arrange */
    $csv  = "code,product_name,description,price,cost_price,category_name,unit_name\n";
    $csv .= "P001,Widget A,A widget,9.99,5.00,Widgets,Each\n";
    $csv .= "P002,Widget B,B widget,14.99,7.00,Widgets,Each\n";

    $file = UploadedFile::fake()->createWithContent('catalog.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', 2);
}

Metadata

Metadata

Assignees

Labels

refinedRefined with SMART plan / test suggestions

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions