Can a company start using a new ERP simply by bringing in its old spreadsheet? First I want to know what a row means and which record should survive when the same identifier appears again.
The public experience uses a predefined synthetic table rather than client files. Here I explain the review decision in more depth and connect a new independent module that separates review from proposing an applied result.
Reading a file does not establish its business meaning
A parser can read text even when an item column has been mapped to quantity. Converting an identifier to a number can remove its leading zeroes. Filling an empty quantity with zero can make an unchecked entry look like a known zero-unit order. I wanted a visible place to establish meaning after the technical act of reading a file.
The quantity-column selection in the current experience is a small demonstration of that distinction. It reviews four fixed rows for mapping, repeated identifiers and missing quantities. It does not upload a real file or implement a complete spreadsheet parser. Stating that limit prevents the simplicity of a public demonstration from becoming a promise about the difficulty of migrating an actual company’s records.
Define what identifies a row before resolving duplicates
The public fixture contains two occurrences of S01. The current experience marks the later occurrence as a duplicate and displays two ready candidates. That does not establish an operational duplicate policy. Two rows could be separate items on one order, the same local number at different branches, or the same record imported again. Those meanings must be resolved before deciding to merge or remove anything.
The new module avoids choosing a winner. It marks every occurrence of a repeated identifier for review. Identifiers remain strings, so 001 does not become 1. If a company needs a combination of branch, document and line number, define that identity before supplying identifiers to this function. Arithmetic convenience should not decide whether two business events are the same event.
Explore the implementation
Current fixed-table review function
export function exampleImport(mapping='quantity'){
const rows=[{id:'S01',item:'A',quantity:'6'},{id:'S01',item:'A',quantity:'6'},{id:'S02',item:'B',quantity:''},{id:'S03',item:'B',quantity:'4'}];
const seen=new Set();return rows.map(row=>{let status='ready';if(mapping!=='quantity')status='mapping';else if(seen.has(row.id))status='duplicate';else if(!/^[1-9]\d?$/.test(row.quantity))status='missing';seen.add(row.id);return {...row,status};});
}Ready candidates do not authorize partial application
In the public experience, a candidate is a row available for review, not a completed server write. The independent module likewise shows normalizable candidates, but withholds a complete application proposal if any incoming row has a duplicate, missing value or mapping problem. Within this small example, that avoids an accidental mixture of successful rows and later retries.
This all-or-nothing choice is not a prescription for every migration. Large datasets may need explicit review batches with their own identifiers and processing history. What matters is making partial application a deliberate policy. Define the unit that succeeds together, where failed rows remain and how a retry recognises records already imported. Do not let an exception silently determine those rules.
Recheck when the target changed after review
Another person may create an existing identifier while a review screen is open. The new module compares the expected revision with the current revision and rejects a mismatch. It also reruns input review using the supplied current identifiers instead of treating an earlier ready indicator as authority. Changing a reviewed quantity to a blank value therefore blocks the new proposal.
The result is a new in-memory array, not a database lock or an atomic write. An operational server must place the revision check and write within the same transaction or conditional-write boundary. A pure function comparing two revision numbers does not by itself solve concurrent editing. I keep that distinction explicit when describing what was implemented.
Preserve the original instead of correcting it in place
The module leaves original rows unchanged and produces candidates containing only an identifier, item and integer quantity. It accepts quantity strings from one to ninety-nine and does not guess the meaning of fractions, exponential notation or surrounding whitespace. Those are the shared example’s rules. A company needing fractional units or a different code system must define an appropriate extension.
For a real migration, preserve the original file in a protected place and record which version of the mapping rules produced each review result. That evidence differs from the normalized records eventually stored. Publishing an original client file is not necessary to explain the decision deeply. I use new synthetic fixtures and failure cases to expose the method without disclosing company information.
Start another company’s migration with its unanswered questions
Establish the meaning of blanks and zeroes, identifier scope, quantity units and overlaps with current records. Then separate reading, reviewing, proposing and actually saving. Check that failure at one stage cannot appear as success at the next. These boundaries turn an otherwise vague migration promise into decisions that a company can inspect.
The shared module includes bilingual demos for normal input, missing values, duplicates, wrong columns, existing identifiers and a revision conflict after review. Extracted-package tests and demo event adapters passed in code. That is not evidence of real client-file conversion, migration performance, operational rollout or rendered-browser testing. The documented boundaries show where readers must add their own rules.