The bulk product import module shipped in Q2 2026 quickly became one of the most-used features in Netorigo Admin. A concrete example: a tenant managing 120,000 SKUs of washing-machine spare parts uploaded 5,000 new SKUs from a CSV in about 7 minutes. Below is how the flow works and where the limits are.
Column mapping wizard
The upload is two steps: file upload (XLSX or CSV, max 50 MB), then a column mapping screen. The wizard auto-detects from the first 100 rows and proposes a mapping: headers like Cikkszam, SKU, Item Code, Article ID all default to the sku field; Nev, Name, Megnevezes all default to name. The operator can override any suggestion and add non-required fields (description, barcode, weight_grams, etc.).
A mapping can be saved as a template (import_template table, tenant-scoped), so next month's upload is one click to reload. 47 fields are supported today, mostly string + number + boolean, plus a category_path helper that creates a category tree on-the-fly using > as separator (Electronics > Washing Machines > Pumps).
Dry-run preview
Before a single row is written, the system parses a 10-row sample and shows a validation diff: green = new, yellow = update to existing SKU, red = error (missing required field, invalid format, etc.). The operator decides whether to skip error rows or cancel the whole batch.
The dry-run is persisted to a temp_import_session table with a 24-hour TTL. So an operator can preview at noon, eat lunch, and hit Confirm at 13:00 without re-uploading the CSV.
Idempotency by SKU
The sku field is the dedup key. If a 5,000-row CSV contains an SKU that already exists, that's not an error: the existing product is updated with the CSV values (UPDATE), and the audit log records one bulk_import_update event with a diff of the changes. So the same CSV can be re-run any number of times without creating duplicates.
Per-row errors are downloadable as CSV: SKU, row number, error code, error message. Most common errors are category_path syntax problems (e.g. doubled > separator) and barcode EAN-13 check-digit failures.
Why we removed inline image upload
An early prototype supported an image_url column where the worker downloaded and S3-uploaded the image with size variants. We pulled it because (a) a 5,000-row import meant 5,000 HTTP requests to unknown domains, a security-audit risk, and (b) average images slowed the import 8x. Instead: the media library is a separate surface, bulk image upload by SKU-pattern works, and the backfill is a separate BullMQ job.
The bottleneck: full-text reindex
5,000 SKU INSERT/UPDATEs through Prisma take about 90 seconds on a db.medium-2 instance (8 vCPU, 32 GB RAM). The remaining ~6 minutes are the full-text search reindex (Postgres tsvector + GIN index), which a BullMQ worker handles asynchronously so the import response doesn't block the UI. The operator sees correct rows in the admin catalog list 90 seconds after upload, but the public storefront search shows them 5-7 minutes later.
For larger tenants (1M+ products) this doesn't scale linearly: on db.large-4 instances (16 vCPU) we measured a 4-minute full import for 5,000 SKUs. So the benchmark ("5,000 SKUs in 7 minutes") applies to 100-200k-catalog tenants.
What it doesn't do
The current version does not support: inline product variants (size + color matrix), bundle assembly, category-tree restructuring (it only goes one way: new categories are created), time-based price scheduling (that's the Promotions module). All of these are on the next-two-sprints list.