← All case files

Case 007 · Online furniture shop · Price import

A 1,299 lei sofa listed at 1.30 lei.

The price import reads 1.299,00 lei as 1.299 lei, rounded to 1.30.

A dark phone showing a product card “Canapea 3 locuri” with 1.299,00 lei struck through and 1,30 lei in red.
Before
1.299,00 lei imported as 1.30 lei.
After
Strict Romanian-format parsing; import tests on Romanian prices.

The bug

An online furniture shop imports prices from its supplier’s file. The file uses Romanian number format, a dot for thousands and a comma for decimals, so a sofa is listed as 1.299,00, meaning 1,299 lei. The import script turns that text into a number with JavaScript’s parseFloat, which reads from the left and stops at the first character it can’t use. It takes the dot as a decimal point, stops at the comma and returns 1.299, a little over one leu. Rounded to two decimals, the sofa costs 1.30 lei.

How it shows up

The import finishes without an error, and every product at 1,000 lei or more now sells for a thousandth of its price. Cheaper products break too, less visibly: 999,99 comes out as 999, because parseFloat stops at the comma and drops the bani. Number("1.299,00") would have returned NaN and failed loudly; parseFloat fails quietly. The first people to notice are customers who find a sofa at 1.30 lei and order it.

How a test finds it

Feed the import the prices that trip a lenient parser and assert the exact values that come out. A value a thousand times too small, or one that lost its bani, fails. Anyone can check this in a browser console: parseFloat("1.299,00") returns 1.299.

The fix

Parse prices with explicit rules for the supplier’s format: remove the thousands dots, turn the decimal comma into a point, then convert. Reject any value that doesn’t match the pattern, so a bad row stops the import. Hold any import that moves a price by more than a set percentage until someone approves it.

The test that catches it

Import tests with the fixtures 1.299,00, 999,99 and 12.345,50, asserting 1299.00, 999.99 and 12345.50 exactly, plus a test that a price change above the threshold waits for approval. They run on every change to the import script.

price-import.spec.tsIllustrative
import { test, expect } from "@playwright/test";
import { importPrices, parsePrice } from "../src/import";

const fixtures: [text: string, lei: number][] = [
  ["1.299,00", 1299.0],
  ["999,99", 999.99],
  ["12.345,50", 12345.5],
];

for (const [text, lei] of fixtures) {
  test(`reads ${text} as ${lei} lei`, () => {
    expect(parsePrice(text)).toBe(lei);
  });
}

test("a value that doesn't match the format stops the import", () => {
  expect(() => parsePrice("1.299")).toThrow();
});

test("a large price change waits for approval", async () => {
  const result = await importPrices([{ sku: "SOFA-3", price: "1,30" }], {
    current: { "SOFA-3": 1299 },
  });
  expect(result.held).toEqual(["SOFA-3"]);
});

What it puts at risk

The most expensive stock, sold for almost nothing until someone notices. Then come the cancellations, the refunds and the customers who feel cheated.

Tell us what you’re shipping.

An app, a rewrite, an AI agent, an automation, or a release that needs testing. A free 30-minute call, then a price in writing. If you don’t need us yet, we’ll say so.

Book a free call