One Comma, Three Zeros: Parsing Supplier Prices Without Silent Magnitude Errors
A supplier file lists prices as text. One uses 1.234,50 where another uses 1,234.50. A parser that simply deletes commas, or that trusts the machine's regional setting, can multiply or divide a price by 100 or 1000 and still look normal. This article proposes a fixture and a few guards. The supplier and figures are hypothetical and are not anyone's real prices. Whether a real supplier file follows a given convention must be confirmed from that supplier's own sample, not assumed.
Principle: the format belongs to the supplier profile
Do not guess the format per cell. Record on a supplier profile: decimal separator is a comma, grouping separator is a dot or space or none. Parse with that profile only. Do not use the server's regional setting. Hold the result as a decimal type or whole minor units, not binary floating point, and keep the original text beside it.
The fixture
Profile for fictional supplier Northgate Parts: decimal comma, grouping dot, no currency symbols expected. Eight rows:
- '12,50' gives 12.50, accepted.
- '1.234,50' gives 1234.50, accepted.
- '1234,50' gives 1234.50, accepted.
- '0,99' gives 0.99, accepted.
- '1.234' gives rejected as ambiguous in practice. Under the profile the dot is grouping, so it would be 1234, yet a stray decimal dot is a common slip. Send to review.
- '1,234.50' gives rejected. The separators contradict the profile.
- '12,5,0' gives rejected. More than one decimal comma.
- '' (empty) gives rejected as missing, not as zero.
Result counts: 4 accepted, 4 rejected or held. A fixture whose counts do not add up to 8 has lost a row.
Magnitude guard
A well formed string can still be wrong by a factor of ten. When the SKU already has a stored price, compare the new value with it. If the ratio falls outside a limit you choose, for example more than a factor of 5 up or down, hold that row for review and do not apply it. In this hypothetical, a stored price of 12.50 and incoming 125,00 gives a ratio of 10, so it is held. Incoming 12,80 gives a ratio of 1.024 and passes. The limit is your own judgement and a held row is not necessarily an error, since real changes happen.
Decision branches
Branch A, the profile is unknown or unconfirmed: reject the whole file for profile confirmation. Do not infer from the first few rows.
Branch B, the file mixes formats between rows: stop the batch, since a mixed file means the profile assumption is false.
Branch C, currency symbols or text appear: strip only symbols the profile lists, and reject anything else.
Branch D, a row passes parsing but fails the magnitude guard: hold it with both values shown.
Stop conditions
Stop the import if more than a chosen share of rows are rejected or held, because this usually signals a wrong profile and not 'bad rows'. Never push held prices to a storefront or to a remote system, and do not auto repeat an uncertain remote write.
Proposed acceptance tests
These are proposals, not results.
- Run the 8 fixture rows and compare accepted and rejected counts to 4 and 4.
- Run the parser under two different regional settings and confirm identical output.
- Confirm the original text is stored next to each parsed value.
- Confirm 125,00 against a stored 12.50 is held, and 12,80 is applied.
- Confirm a file with rows 2 and 6 together stops as mixed format.
A closing habit
Show reviewers the original string, the parsed value and the reason together. A price they can read in its source form is a price they can check by eye.