A six-rule price resolution ladder, what happens when a contract costs more than a campaign, an override approval matrix, and what a repeat order re-resolves.
Key takeaways
- A customer price is the output of a ladder of rules, not a stored field. Six rules compete for the same line and exactly one wins.
- The winning rule identifier is stored on the order line. Without a `price_rule_id` there is no answer to "why did they get that price" two months later.
- A customer contract can be more expensive than a general campaign. That is not a bug, and it is a decision that must be written down in advance: strict precedence, or lowest-wins.
- Going below the resolved price is not a technical exception but an approval request, with a band, an approver and a reason. A margin floor derived from cost cannot be quietly overridden.
- A repeat order copies items and quantities and never copies prices. Price is re-resolved at the new order date, and an unusual difference from last time is flagged.
In a wholesale business almost every customer has their own price for the same SKU, so the question is never "what is the price" but "which rule won". A system that returns a number without recording the rule that produced it cannot explain a single line, and cannot reproduce that price on the next order.
The price ladder: six rules, one winner
The price for an order line is resolved by walking six possible sources, ordered from the most specific to the most general:
- Contract price for this customer and this SKU - an agreed price with a date range, usually from a signed engagement.
- Customer-specific price list - a price table belonging to one customer only.
- Group or channel price list - distributors, chains, institutions. A customer belongs to exactly one group at any moment.
- Quantity break on the applicable list - a break is not a separate list but a rule applied to the list already selected.
- Campaign price - a dated price that does not depend on the customer.
- Base list price - the default, and what is shown when nothing else applies.
Each rule carries an identifier, a start date, an end date and an owner. The rule that won is recorded, and so are the rules that were evaluated and lost, in one resolution record. The same principle of a price as a dated row rather than a field on the product is covered in one price list for your website and your quotes.
Which price wins when a contract is worse than the campaign?
This is the question that breaks pricing systems, and it has two legitimate answers that must never be mixed.
A worked case: customer C-0412, SKU WH-4180, an order for 600 units on 2026-04-12.
| Rule | Unit price | Validity | Applies |
|---|---|---|---|
| Base list | 38.00 | Standing | Yes |
| Distributor group list | 33.50 | Standing | Yes |
| Contract for C-0412 | 31.00 | 2026-01-01 to 2026-12-31 | Yes |
| Quantity break from 500 units | 30.90 | Standing | Yes |
| Spring campaign | 29.90 | 2026-04-01 to 2026-04-30 | Yes |
Under strict precedence the contract wins at 31.00. Under a lowest-wins policy the campaign wins at 29.90. The difference on this order is 660, and it falls on the customer who signed a contract. Both policies are defensible, but only one can be in force; it lives in an organisation-level price_policy field, and it carries a version number.
The practical rule: if the contract contains an explicit undertaking that the customer never pays more than a promotional price, the policy is lowest-wins and there is nothing to debate. Where no such undertaking exists, strict precedence produces predictable behaviour, provided the salesperson can see the price the customer missed and can request an override.
Order-line fields that turn a price into an explanation
| Field | Example | What it answers |
|---|---|---|
list_price | 38.00 | What the price would be with no rule at all |
price_source | customer_contract | Which kind of rule won |
price_rule_id | CTR-0412-4180 | Which record exactly |
resolved_price | 31.00 | The ladder output, before any human intervention |
override_price | 30.00 | The price actually charged, if it was changed |
override_pct | 3.23 | The size of the deviation from the resolved price |
override_reason | competitor_match | A code from a closed list |
approved_by | U-014 | Who approved it, not who typed it |
resolved_at | 2026-04-12T09:41:00+03:00 | The moment the ladder ran |
price_policy_version | v3 | Which policy was in force |
The field most systems drop is resolved_price when an override exists. Without it only the final price survives, and there is no way to measure how much money was given away below list, which is usually the number most worth knowing.
The approval matrix: who may go below the resolved price
The margin floor is derived from landed cost and a minimum margin percentage, never from the list price. For SKU WH-4180 at a landed cost of 24.80 and a 15% minimum margin, the floor is 29.18. How that cost is calculated is set out in landed-cost calculation for an importer.
| Deviation from the resolved price | Approver | What is recorded |
|---|---|---|
| Up to 2% | The salesperson | Reason code |
| Over 2% and up to 8% | Sales manager | Reason code and a free-text note |
| Over 8% | Owner | Reason, note and a time-limited validity |
| Any price below the floor | Owner only | A separate approval, however small the deviation |
On a resolved price of 31.00 that means: down to 30.38 sits inside the salesperson's own authority, down to 28.52 needs a manager, and anything under 29.18 is blocked regardless because it breaches the floor. Note that the bands overlap, so the written rule is that the stricter of the two limits governs.
- The reason code is a closed list: competitor match, new customer opening, service-failure compensation, stock clearance, correcting an earlier quote.
- Approval attaches to a line, not to an order. A ten-line order with one deviating line needs one approval, not ten.
- Approvals expire. An override approved six months ago does not justify the same price on today's order.
What does a repeat order actually copy?
It copies what the customer asked for, not what the customer paid. That separation is the whole difference between a convenient repeat order and one that perpetuates a mistake:
- Copied: the SKU list, quantities, unit of measure, delivery address, contact, shipping notes.
- Re-resolved: every price, every quantity break, every campaign, and the availability status of each item.
- Never copied: approved price overrides, because the approval was given for the circumstances of that day.
- Blocked: a SKU that is discontinued or no longer on this customer's list. It is surfaced as a line requiring a decision, never dropped silently.
After re-resolution the order shows a comparison: previous price, new price, and the difference as a percentage, per line. Any line that moved by more than five percent is flagged. That flag catches the two common cases - a campaign that ended and a contract price that expired - before the order goes out rather than after the customer receives an invoice.
A structure like this assumes the order, the delivery note and the invoice share one identifier, as described in quote, order and invoice under one identifier.
What actually breaks
- A percentage discount on the order header. Nobody can tell which SKU absorbed it, so item-level margin becomes unmeasurable.
- A price list as a file. A spreadsheet emailed out and saved on a salesperson's laptop produces two versions of the truth inside a week.
- A price stored on the customer card. With no start and end date, nobody knows when it took effect or who decided it.
- A quantity break evaluated per line only. A customer ordering the same SKU across three lines never reaches the break, and to them it looks like a fault.
- A campaign with no end date. It stays live for two years and is discovered in a margin report.
- A verbal approval. A price agreed on the phone and never recorded becomes a precedent that cannot be withdrawn.
What to have ready before building it
- A written decision between strict precedence and lowest-wins, with a version number and an effective date.
- A closed list of customer groups, and a rule that a customer belongs to exactly one at a time.
- A minimum margin percentage per category, and a decision on which cost it is derived from.
- A list of override reason codes, short enough that people actually use it.
- A decision on what a repeat order does with a discontinued SKU: propose a replacement, or stop for review.
Sources
Frequently asked questions
Does every customer need their own price list?
Almost never. Most customers should sit on a group list, and only those with a signed contract get dedicated rows. A per-customer list produces hundreds of tables nobody maintains, and within a year the differences between them reflect who asked loudest rather than any policy.
How do you handle a customer demanding the campaign price despite a contract?
By deciding the answer at policy level rather than in the conversation. Under strict precedence the salesperson sees in the interface that the campaign is cheaper and can request an approved override. The customer then gets a consistent answer, and the business can measure how much was given away this way.
What do you do when cost rises mid-contract?
Recalculate the margin floor and flag every contract line that now sits below it. The contract does not change by itself, but that list is precisely what a conversation with the customer, or a decision to absorb the increase, needs. Without it the loss surfaces only in the quarterly report.
How long should price history be kept?
At least as long as an invoice from that period can be reopened, and in practice indefinitely, because the records are small. What is kept is not just the price but the resolution record: the rules evaluated, the rule that won, the policy version and any approved override. That is what lets a line be reconstructed rather than argued over.
Keep reading
Related service
MVP Development
Turn an idea into a validated product in weeks, not months.
About the author
Yehonatan Saadia
Freelance automation, web & MVP developer
I'm Yehonatan Saadia, a senior developer who builds business automation, custom websites, and MVPs for small and mid-sized companies across the US, Europe, and Israel. These guides come from real client work, not theory.
Work with meHave a project like this?
Tell me what you're trying to automate or build. I reply within 24 business hours with a few targeted questions, then we walk through it on a free 30-minute call, with no commitment. You come away with a scope, a timeline and a fixed price - or a straight answer that it isn't worth building.
