- Professional buyers rarely begin with an empty search box.
- Asking the buyer to search for every product again throws away that history.
- We built a CSV-to-shopping-list workflow around Medusa to bridge those two worlds.
- The import accelerates preparation.
The client problem
Professional buyers rarely begin with an empty search box. They already have recurring order sheets, supplier catalogues and purchasing templates built over years of work. Those files often contain thousands of SKUs, quantities and local columns that matter to the organisation.
Asking the buyer to search for every product again throws away that history. Importing the spreadsheet directly into a cart is equally dangerous: a SKU can belong to the wrong supplier, use different capitalisation, match several products or no longer exist.
We built a CSV-to-shopping-list workflow around Medusa to bridge those two worlds. An organisation master selects the supplier and SKU column, uploads the file, and receives a persisted list whose rows are matched against that supplier's current catalogue in bounded batches. Original columns remain available for review and export.
The import accelerates preparation. It does not skip product identity or human review.
The client problem is migration of purchasing intent
A property organisation may maintain standard replenishment sheets for plumbing, appliances or turnover supplies. The spreadsheet carries more than product codes: quantities, descriptions, internal notes, pack information and the order in which the team expects to review items.
The marketplace needs to recognise the products without destroying the file's useful context. It also needs to answer several business questions:
- Which supplier catalogue should these SKUs be checked against?
- Which column contains the product identity?
- Which rows matched a live product?
- Which rows need attention?
- Can the organisation revisit or export the result?
- Can a large file be processed without blocking the browser?
That requires an ingestion workflow, not a generic file upload component.
Make supplier selection part of identity
A SKU is not globally unique across a marketplace. Two suppliers can use the same short code for unrelated products.
The upload wizard therefore asks the organisation to select one seller before matching begins. The backend stores that seller on the shopping list and queries only product relationships belonging to it.
This is the first and most important narrowing rule. The file does not decide the tenant merely because one cell happens to resemble a supplier identifier. An authenticated organisation master chooses the supplier through the product interface, and the workflow verifies uniqueness rules for the resulting organisation-seller list.
For a truly mixed-supplier spreadsheet, the safer product design is to split rows by an explicit supplier column into separate review lists. The implemented workflow deliberately treats one import as one supplier context rather than guessing across the entire marketplace.
Upload through an owned session
Large files should not travel through an ordinary JSON request. The flow first creates a purpose-bound upload session and sends the file to storage. The ingestion request then references that session.
Before reading anything, the backend verifies that the session belongs to the authenticated customer, has the shopping-list purpose and reached the uploaded state. Another customer cannot submit a guessed session ID, and a file uploaded for a different feature cannot be repurposed silently.
Only organisation masters can create these catalogue lists. That prevents every shopper from initiating bulk catalogue operations that affect the shared organisation surface.
The session becomes the operational handle for validation, progress, completion and failure.
Parse CSV as a real format
Supplier files contain awkward but legitimate CSV structures. A description may include commas. A quoted note may span lines. Excel-generated exports often begin with a UTF-8 byte-order mark and use Windows line endings.
The parser handles quoted fields, doubled quotation marks, embedded newlines, LF or CRLF endings and the optional byte-order mark. It removes blank trailing lines, reads the first logical row as headers and pads or trims later rows to that stable width.
The file must contain both a header and data. Empty files fail with a readable validation message instead of producing a meaningless list.
This implementation supports CSV. Native XLSX workbooks would need a separate parser and explicit sheet-selection rules; installing a spreadsheet library elsewhere in a frontend is not evidence that this workflow accepts .xlsx files.
Let the buyer choose the SKU column
Supplier sheets do not agree on a header. The product code may be called SKU, Item, Vendor Part, Material Number or something organisation-specific.
After parsing, the wizard presents the detected headers. The buyer chooses which column contains supplier product identity. The backend checks that the named header exists before starting database-heavy work.
Rows with a blank selected SKU are skipped. Every retained row keeps its original zero-based position, the cleaned SKU and a map of all cells by header.
This avoids a brittle global mapping convention while preserving the source file's structure. The marketplace asks the user one question it cannot infer safely, then performs the repetitive matching work automatically.
Preserve the original rows for traceability
It would be tempting to discard the CSV after resolving product IDs. We kept each source row as a durable shopping-list row.
The row stores its position, original supplier SKU, match flag and metadata containing the source cells and header order. That supports review and reconstruction even if the linked Medusa product changes later.
Original context is valuable when matching fails. An unmatched A-102 is not very informative alone; its description, unit and notes may allow an operator to identify the intended product or correct the source file.
The imported list therefore contains both worlds: marketplace product links for resolved items and source rows for the purchasing evidence that arrived.
Normalise case without pretending ambiguity disappeared
Supplier files and catalogues frequently disagree about capitalisation or surrounding whitespace. Matching normalises the CSV SKU and stored variant SKU to lowercase after trimming.
The product-import pipeline can append a seller-specific suffix to variant SKUs. To keep raw supplier sheets usable, the matcher searches both the normalised input and its seller-suffixed form. A buyer can upload the original vendor code without knowing the marketplace's internal uniqueness convention.
One SKU can still point to more than one product. The implementation links all product hits and marks the source row matched; it does not currently persist a dedicated “ambiguous” state. That is an important review implication: matched means at least one seller product resolved, not necessarily that the row identified exactly one candidate.
A richer next step can expose candidate count and require a choice before activation when several products share the code.
Match in bounded database batches
A 20,000-row file should not require loading the supplier's entire catalogue and every row into application memory.
The workflow reads shopping-list rows in batches of 1,000. For each batch, it builds the relevant normalised search terms and asks the database for variants linked to the selected seller whose SKUs match. It maps those results back to source rows, creates deduplicated product links and updates match flags.
Progress is written after each batch with the current phase, processed count and total. The upload request can return 202 Accepted while the browser polls the session status.
This architecture makes file size an explicit workload. Validation errors remain synchronous and understandable; heavy matching continues asynchronously with visible progress.
Separate validation failure from ingestion failure
Some problems should stop immediately:
- the upload session is missing or belongs to another customer;
- the file purpose or status is wrong;
- the CSV is empty or malformed;
- the selected SKU header does not exist;
- no non-empty SKU rows remain.
Those conditions mark the session failed and return a useful client error.
Failures after asynchronous work begins are recorded against the session with a failed phase and message. The frontend does not have to infer failure from a spinner that never ends.
This distinction gives support a much better starting point. “Header not found” requires a different response from “matching workflow failed after 8,000 rows”.
Keep aggregate results with the list
After matching, the list records total, matched and unmatched row counts. These aggregates give the user a quick quality signal before activation.
A list with 19,980 matches and 20 unmatched rows may be useful after a short review. A list with only 200 matches probably has the wrong supplier or SKU column. The counts turn ingestion into a decision rather than a silent background transformation.
The product links are deduplicated even when several source rows reference the same product. The original rows remain separate, preserving source quantities and context, while catalogue navigation can operate on one link per resolved product.
Activation can then be a deliberate user action after the imported result has been inspected.
Make compensation part of the workflow
The matching step can create product links and update row flags across many batches. If a later workflow step fails, leaving half the links active would create a misleading list.
The Medusa workflow step records the links it created and the rows it changed. Its compensation path can dismiss those links, restore previous match state and reset aggregates.
This does not make an external file import one magical database transaction. It gives the workflow a defined way to undo its own completed side effects when orchestration reverses the operation.
That distinction matters at scale. Recoverability comes from explicit state and compensation, not from hoping a 20,000-row process never stops halfway.
What the organisation experiences
The completed flow is straightforward:
- An organisation master chooses the supplier.
- They upload a CSV and select the SKU column.
- Structural errors appear before processing begins.
- The backend persists source rows and matches them in bounded batches.
- The interface reports progress rather than holding one long request open.
- The resulting list shows matched and unmatched totals.
- Original columns remain available for review and export.
- The organisation activates the useful list only after inspecting the result.
The spreadsheet stops being a dead file and becomes a governed catalogue view inside the marketplace.
Why Medusa is the right foundation
Medusa provides the seller-product relationships, variants and workflow compensation used to resolve the file. The custom shopping-list module adds the organisation ownership, durable source rows, progress and activation lifecycle the client needed.
We chose that boundary because the business problem was not “upload a spreadsheet”. It was “bring existing purchasing habits into the marketplace without corrupting product identity or losing source context”.
An extensible commerce engine lets the imported list point to the same live products buyers later search, compare and purchase. There is no parallel catalogue hiding behind the upload.
The commercial payoff is continuity. The organisation does not have to abandon a familiar purchasing artefact on day one, and the marketplace does not have to accept that artefact as unquestioned truth. The workflow translates it into product relationships that can participate in the rest of the platform while retaining the original evidence needed to resolve exceptions. Adoption and data governance move forward together instead of being traded against each other.
If your buyers maintain recurring supplier sheets outside the storefront, WeAreSouk can design a Medusa ingestion workflow that turns those files into reviewable, seller-scoped shopping lists without forcing manual product search.
