How the price row is chosen
When you add a product to an order or invoice, or print its label, the system runs the same resolution in every module. Nothing is asked from the user — the barcode, the warehouse context and the schedules decide automatically.
The resolution steps
1. START → product's own price + own discount
2. FILTER → keep only multi-code rows that pass ALL of:
• barcode test (what was scanned — see table below)
• schedule test (row is inside its date/time/weekday window)
• warehouse test (row warehouse empty OR qualifies — see below)
3. MERGE → walk the surviving rows top to bottom;
each row's price becomes the current price,
each row's discount becomes the current discount
4. RESULT → current price + current discount land on the line/label,
and the WINNING ROW's checkbox flags steer how the discount
competes next (see Discount priorities)
The barcode test
| How the product was added | Rows that pass the barcode test |
|---|---|
| Scanned — a multi-row's code | That exact row, plus fully-generic rows (no code, no box code). The scanned row wins any field it sets. |
| Scanned — a multi-row's box code | Same as above; quantity is also multiplied by the box quantity. |
| Scanned — the product's main barcode | Only fully-generic rows. Barcode-specific prices do not apply. |
| Picked by name / reference | Only fully-generic rows — a code-specific row needs its scan. Warehouse and schedule still filter them. |
This is what guarantees the business rule "a specific barcode's price only applies when that barcode is scanned".
The warehouse test
- Orders & invoices: a row with a warehouse participates when its own location has Is main warehouse checked — the line's stock location is never consulted. A shop-bound row prices only on that shop's POS register; a main-warehouse row prices in the editors regardless of where the stock physically sits.
- Labels: the context is the warehouse chosen on the label sheet (see Location pricing on labels).
- POS: a row with a warehouse participates only when its location matches the register's location.
- Rows with an empty warehouse pass the test everywhere.
The schedule test
A scheduled row (date range, time window and/or weekday list) only participates while its schedule is active — outside the window the row contributes nothing at all, not even its flags. See Schedules (happy-hour pricing).
The winning row
After the merge, the winning row is the last participating row that supplied a value — the discount if any row carried one, otherwise the price. The winning row decides everything else on the line:
- its price is the line's unit price (a lower row price still overrides the product's list price — an override, not a maximum),
- its discount competes in the priority ladder (a row bound to a warehouse and/or its own barcode replaces the product's own discount, even when smaller; a fully-generic row's discount competes with it and the bigger one wins),
- its three checkbox flags (Lock discount, Ignore quantity discounts, Ignore BXGX) apply — flags from non-winning rows are ignored.
The full discount competition is described in Discount priorities.
Worked examples
The sample product: base price 2.282 € net, main barcode
8000005045169.
| Row | Code | Price | Discount | Warehouse |
|---|---|---|---|---|
| 1 | 80000050451691 |
— | 5 % | V (location 9, main warehouse) |
| 2 | 80000050451693 |
2.50 € | 5 % | all |
Scan 80000050451691 (row 1's code)
- Barcode test: row 1 matches; row 2 has a different code → excluded.
- Result: row 1's 5 % discount on the product's base price.
- The line gets price 2.282 € and 5 % discount — row 1 carries no price, so the product's own price stays.
Scan 80000050451693 (row 2's code)
- Row 2 wins: price 2.50 € (net, as stored) and 5 % discount.
Scan 8000005045169 (the main barcode)
- Only fully-generic rows pass — neither row qualifies (both carry codes).
- Result: the product's own pricing, 2.282 € with no discount.
Add by name (no scan)
- Only fully-generic rows pass the barcode test — neither row qualifies (both carry codes).
- Result: the product's own pricing, 2.282 € with no discount.
Compare with a generic row 3 (no code, no warehouse, price 2.10 €): a name-add would now end at 2.10 € — the last generic row with a price wins. That is intended behaviour — row order is part of your pricing setup. If you want a row to win more often, place it lower in the list.
Where each context comes from
| Module | Warehouse context | Barcode context |
|---|---|---|
| Order / invoice line | The row's own Is main warehouse flag decides participation (the line's stock location is never a pricing input) | The scanned code, when the line came from a scan |
| Product change on a line | Same as above | None (a picker selection — generic rows only) |
| Label sheet | Warehouse selected on the sheet | The scanned code, when the label came from a scan |
| POS register | The register's configured location | Always the scanned code |
Frequently asked
Why didn't my warehouse-specific price apply? Either the row's schedule is currently inactive, or the selling surface is not the one the row is bound to: in orders and invoices a warehouse-bound row needs its location marked Is main warehouse; on the POS it needs to match the register's location. Check the row's warehouse and schedule, and the flag on the location itself.
Can several rows combine? Yes — that is the per-field merge. A generic discount row plus a barcode-specific price row combine: the scanned row sets the price, the generic row supplies the discount if the scanned row has none.
Does changing a row later change existing lines? Yes — lines are re-resolved from the current rows every time the order or invoice is opened or edited (quantity changes, customer changes, reloads). A schedule that has ended therefore stops applying on the next re-evaluation, and the winning row's ID is stamped on the line for reference. Two things are never touched: a manually typed line discount keeps its percentage, and a manually typed price is never overwritten. Cancelled documents are frozen.