...

Wholesale Pricing Odoo 19: A Technical Config Guide

Wholesale pricing Odoo 19 setup showing a pricelist with tiered volume rules assigned to a wholesale customer

If you sell to distributors, resellers, and retail buyers from the same catalog, you already know the core problem: one SKU, many prices. Getting wholesale pricing Odoo 19 right is less about discounts and more about architecture. Configured well, the pricelist engine decides the correct price the moment a customer lands on a quotation. Configured poorly, your sales team ends up patching numbers by hand and quietly leaking margin on every order.

The wrinkle that trips up most implementations is conceptual. Business owners and ERP buyers arrive expecting a “customer group” object they can create, name, and price against. Odoo does not work that way. Understanding how the platform actually models segmentation is the first step to building a wholesale structure that scales without custom code, and it changes how you plan the whole configuration.

Why "Customer Groups" Don't Exist Natively in Odoo 19

Coming from Magento, WooCommerce, or a legacy ERP, you might look for a Customer Groups menu with a pricing tier attached to each group. Odoo has no such object. Instead, it treats pricing as a relationship between three things: a partner (the customer), a pricelist, and a set of price rules inside that pricelist. Segmentation is expressed through how you assign pricelists, not through a dedicated group entity.

This is not a limitation once you understand it. It is actually more flexible, because a single customer can be moved between commercial tiers by changing one field rather than migrating them across rigid group boundaries.

How Odoo Actually Models Customer Segmentation

Every contact in Odoo carries a Sales Pricelist field, found under the Sales and Purchases tab of the contact form. Whatever pricelist sits in that field becomes the default for all quotations, sales orders, and invoices for that customer. So a “wholesale group” in practice is simply every partner whose Sales Pricelist field points at your Wholesale pricelist. There is no group record. There is a shared pointer.

One detail worth flagging for anyone auditing data: when a customer is created, the default pricelist is applied automatically, and the field cannot be left permanently blank. Even if you clear it, the default reappears when the form reloads. That behavior matters when you plan bulk assignment, because you are always replacing a value, never filling an empty one.

Partner Categories vs. the Sales Pricelist Field

Odoo does offer contact tags, technically called partner categories, and teams often reach for them to label customers as “Wholesale” or “Distributor.” Tags are excellent for filtering, reporting, and organizing list views. What they do not do out of the box is drive pricing. A tag named Wholesale changes nothing about the price a customer sees unless you also connect that tag to a pricelist through automation.

The clean pattern is to use both together. Tags describe the segment for humans and reports, while the Sales Pricelist field does the actual pricing work. Later in this guide you will see how to bridge the two so a tag can populate the pricelist automatically.

The Pricelist Engine: Three Computation Types

Before building anything, you need to understand the three ways a price rule can compute a value. Every rule in Odoo 19 uses exactly one of these methods, and choosing correctly is the foundation for a clean wholesale structure.

Fixed Price, Discount, and Formula Rules

A fixed price rule sets an absolute number. Product A costs 80 dollars on this pricelist, full stop. This is ideal for negotiated contract rates where a wholesale buyer has agreed to a specific sheet.

A discount computation type applies a percentage off the sales price. A flat 25 percent across the wholesale catalog is the classic example. If you set the price type as Discount rather than Formula, that discount stays visible to the customer on the quotation, which some B2B buyers expect and others do not.

A formula rule is the most powerful and the least documented. It computes from a base (public price or cost) and lets you layer a percentage, a fixed surcharge, a rounding multiple, and crucially a minimum margin. Cost-plus pricing for distributors lives here: base on cost, add your markup, protect the floor.

Enabling Advanced Price Rules

Out of the box, Odoo 19 only exposes basic percentage pricelists. To unlock the Fixed Price and Formula computation types, go to Settings, then Sales, then the Pricing section, and enable advanced price rules. This reveals the Computation field on every pricelist rule with all three options available. Until you toggle this, wholesale formula pricing simply is not visible in the interface, which is a common source of “the option is missing” support tickets.

Building a Wholesale Pricing Structure

With the engine understood, the wholesale structure itself is a matter of layering rules in the right order. Two patterns cover the vast majority of real cases.

Tiered Volume Breaks for Bulk Buyers

Wholesale is fundamentally about quantity, so tiered volume pricing is the workhorse. Inside a single pricelist, you add several rules for the same product or category, each with a different minimum quantity. Odoo evaluates quantity rules from highest to lowest and applies the first rule where the ordered quantity qualifies. Order 500 units and the 500-plus rule fires. Order 50 and it falls through to the 50-plus tier.

The practical guidance here is to always include a base rule with no minimum quantity so that small orders still resolve to a defined price. A gap in your tiers means an order can fall through to the public price unexpectedly, which is exactly the kind of silent margin leak that erodes trust with wholesale accounts.

Cost-Plus Formulas and Minimum Margin Guards

Distributor pricing usually runs on margin rather than list discount. Using a formula rule based on cost, you can express something like cost multiplied by 1.3 for a 30 percent markup. This keeps the wholesale price anchored to your actual cost, so when supplier costs shift, your floor moves with them.

The trap to avoid is pricelist stacking that compounds below cost. Chain a distributor markup with a promotional discount and the math can quietly push you under cost while the system happily confirms every order. Odoo does not warn you unless you explicitly set a minimum margin on the formula rule. On any cost-derived wholesale rule, set the minimum margin field. It is the single most important safeguard in a serious wholesale configuration.

For businesses that also run a storefront, these same pricing rules flow into the checkout, and it is worth reviewing how POS and eCommerce channels consume pricelists. My walkthrough on Retail and POS setup in Odoo 19 covers how store-level pricing and discounts behave once these wholesale rules are live, which is useful if your wholesale and retail operations share a catalog.

Assigning Pricelists to Customer Groups at Scale

Building the pricelist is half the job. The other half is getting it onto the right customers efficiently, especially when you have hundreds of wholesale accounts.

Tag-Based Segmentation and Mass Editing

The manual route is to open each contact and set the Sales Pricelist field, which is fine for a handful of accounts and painful for a hundred. The scalable pattern starts with tags. Apply a Wholesale partner category to the relevant contacts, then open the Contacts list, filter by that tag, select all, and use mass editing to write the wholesale pricelist to every selected record in one action. This gives you a repeatable, auditable way to move a whole segment onto a pricelist.

Automating Assignment with a Server Action

For teams that onboard wholesale customers continuously, even mass editing becomes repetitive. A small server action can enforce the rule that any contact tagged Wholesale receives the wholesale pricelist automatically. This is the bridge between the human-facing tag and the pricing-facing field.

python
# Server Action on res.partner # Set "Model" to Contact, trigger on create/write or run on demand.
wholesale_tag = env.ref('__custom__.partner_tag_wholesale', raise_if_not_found=False)
wholesale_pricelist = env['product.pricelist'].search( [('name', '=', 'Wholesale')], limit=1) if wholesale_tag and wholesale_pricelist: for partner in records: if wholesale_tag in partner.category_id \
                and partner.property_product_pricelist != wholesale_pricelist:
            partner.property_product_pricelist = wholesale_pricelist

Note that the pricelist on a partner is stored in the property_product_pricelist field, which is a company-dependent property field, not an ordinary many2one. Writing to it directly, as above, is correct. Wiring this into an automation rule on the tag field turns segmentation into a set-and-forget process, which is exactly what a growing wholesale operation needs.

Pricelist Priority and the partner_id Onchange Trap

Odoo resolves which pricelist to use in a strict priority order. A pricelist chosen manually on the sales order wins first. If none is set there, the customer’s default pricelist from their contact record applies. If that is somehow absent, the company default takes over. Understanding this order prevents a lot of confused debugging.

The technical trap every wholesale implementation should know about lives in the partner_id onchange. Picture a rep who manually selects a special VIP pricelist on a quotation, then changes the customer on that same order. Odoo silently resets the pricelist to the new customer’s default, overwriting the manual selection. The rep does not notice, and the buyer receives a quote at the wrong price. If your workflow depends on manual overrides, the fix is a small customization: add a lock flag on the sales order and override the onchange to skip the reset when the lock is active. It is a one-field, few-line change that protects negotiated pricing from being clobbered.

Wholesale Pricing in POS and eCommerce Contexts

Wholesale pricing is one of those Odoo areas where a clean architecture saves real money and a messy one bleeds it slowly. If you are weighing tiered volume breaks against cost-plus formulas, planning tag-driven assignment for hundreds of accounts, or trying to close the gap between negotiated rates and what actually gets invoiced, a focused working session will get you to a robust configuration faster than trial and error. Book a Consultation and we can map your wholesale pricing structure to your exact customer segments and channels.

Book a Consultation

Wholesale pricing is one of those Odoo areas where a clean architecture saves real money and a messy one bleeds it slowly. If you are weighing tiered volume breaks against cost-plus formulas, planning tag-driven assignment for hundreds of accounts, or trying to close the gap between negotiated rates and what actually gets invoiced, a focused working session will get you to a robust configuration faster than trial and error. Book a Consultation and we can map your wholesale pricing structure to your exact customer segments and channels.

Conclusion

The key mental shift for wholesale pricing in Odoo 19 is letting go of the customer group object you expect and embracing how Odoo really models segmentation: partners pointing at pricelists, with rules doing the pricing work. Once that clicks, the rest is deliberate configuration. Choose the right computation type for each scenario, layer tiered volume breaks with a base rule that catches everything, guard cost-plus formulas with a minimum margin, and automate pricelist assignment so segmentation stays consistent as you grow. Add the partner_id onchange safeguard and channel-aware POS discipline, and you have a wholesale pricing system that behaves predictably for distributors, resellers, and retail buyers alike, without a spreadsheet in sight.

Frequently Asked Questions

Does Odoo 19 have a built-in customer group feature for wholesale pricing?

No. Odoo 19 has no dedicated customer group object. Segmentation is achieved by assigning a pricelist to each customer through the Sales Pricelist field on their contact, optionally organized with partner category tags for filtering and reporting. The tag describes the segment while the pricelist field does the actual pricing.

How do I apply the same wholesale pricelist to hundreds of customers quickly?

Tag the relevant contacts with a Wholesale partner category, filter the Contacts list by that tag, select all, and use mass editing to set the Sales Pricelist field in one action. For continuous onboarding, a server action or automation rule can assign the pricelist automatically whenever the Wholesale tag is applied.

What is the difference between the Discount and Formula computation types?

A Discount rule applies a percentage off the sales price and remains visible to the customer on the quotation. A Formula rule computes from cost or list price, supports markups, rounding, and a minimum margin, and by default does not display the discount to the customer. Formula is the right choice for cost-plus distributor pricing.

Why did my manually selected pricelist reset on the sales order?

Changing the customer on an existing quotation triggers the partner_id onchange, which resets the pricelist to the new customer’s default and overwrites any manual selection. If your process relies on manual overrides, add a lock field on the sales order and override the onchange to skip the reset when the lock is enabled.

How do I stop wholesale formula pricing from selling below cost?

Set the minimum margin field on any cost-derived formula rule. Odoo does not warn you when stacked pricelists or compounded discounts push a price under cost, so the minimum margin is your explicit floor. It is the most important safeguard on any cost-plus wholesale configuration.

Reach Out for Support

Facing a problem? Contact us and receive expert help and fast solutions.

Seraphinite AcceleratorOptimized by Seraphinite Accelerator
Turns on site high speed to be attractive for people and search engines.