💰 Pricing Setup
Learn how to configure prices for your products correctly — including setting retail and wholesale tiers, handling multiple currencies, using quantity-break discounts, and understanding why a product might appear to have a price but still fail to add to cart.
- At least one product must exist in your catalog (see Catalog Management)
- You must have a B2B account with Seller or Admin role
- Know your selling currency — this is set on your Company profile and used as the default for all prices
Part 1 — How Pricing Works
Each product can have multiple Price records. The platform picks the best matching price at checkout using a 5-step resolution process. Understanding this prevents the most common setup mistakes.
| Field | What it controls | Common mistake |
|---|---|---|
currency |
Which currency this price is expressed in (e.g. UGX, USD, KES) | Setting currency to USD but your company's default is UGX — the price is found but the label shows the wrong currency |
customer_tier |
retail (B2C buyers), wholesale (B2B buyers), or vip | Only setting a wholesale price — B2C buyers can't add to cart |
min_quantity |
Minimum order quantity for this price to apply | Setting min_quantity=10 on the only price — adding 1 unit fails because no price applies |
valid_from / valid_until |
Date window when this price is active (leave blank = always active) | Setting valid_until in the past — the price appears in admin but is invisible to buyers |
is_active |
Master on/off switch for the price record | Accidentally deactivating the only price for a product |
How the platform finds a price at checkout
When a buyer adds a product to cart, the platform tries these steps in order and stops at the first match:
Exact match — buyer's currency + buyer's tier
e.g. a UGX retail price for a retail buyer. Best case — always aim for this.
Same currency, retail tier (tier downgrade)
A B2B wholesale buyer gets the retail price when no wholesale price exists in their currency.
Any currency + buyer's tier
A US buyer (USD context) can still purchase a UGX-priced product. The price is shown in UGX with an automatic conversion displayed for reference.
Any currency + retail (last sensible fallback)
Wholesale buyer, wrong currency — still finds a retail price to fall back on.
Absolute last resort — any active price at all
VIP-only prices, exotic currencies — if anything active exists, it is used.
If no valid active price is found for the requested quantity, the platform raises a "has no price configured" error at add-to-cart. See Part 5 — Diagnosing Price Problems for how to fix this.
Part 2 — Adding Your First Price
Every product needs at least one active retail price before buyers can add it to cart.
Open the product and go to the Pricing tab
Navigate to Catalog → Products, click your product, then select the Pricing tab.
Click + Add Price
Fill in the form with these values for a standard single retail price:
| Field | Value | Notes |
|---|---|---|
| Currency | UGX (or your local currency) | Must match your company's default currency for step-1 resolution |
| Customer Tier | retail | Required so B2C buyers can purchase. Add a separate wholesale row later for B2B pricing. |
| Unit Price | e.g. 45000 | The price per single unit |
| Min Quantity | 1 | ⚠️ Must be 1 for a standard price. Higher values mean the price only activates for bulk orders. |
| Valid From | (leave blank) | Blank = active immediately |
| Valid Until | (leave blank) | Blank = never expires |
| Active | ✓ ticked | Must be ticked or the price is invisible to buyers |
Click Save Price.
Verify the price is live
Open your product in the shop (Shop → Browse Catalog) and confirm the price is displayed. Try adding 1 unit to cart to confirm end-to-end.
Part 3 — Adding Wholesale (B2B) Pricing
B2B buyers are automatically assigned the wholesale tier. Add a separate price row to give them a discounted rate. If no wholesale price exists, the platform falls back to the retail price — B2B buyers can always purchase.
Add a second price row with tier = wholesale
Click + Add Price again on the same product and set:
| Field | Value |
|---|---|
| Currency | Same as your retail price (e.g. UGX) |
| Customer Tier | wholesale |
| Unit Price | Your discounted B2B rate, e.g. 38000 |
| Min Quantity | 1 (or higher if the discount only applies for bulk) |
| Active | ✓ ticked |
- B2C buyer adds 1 unit → pays UGX 45,000 (retail)
- B2B buyer adds 1 unit → pays UGX 38,000 (wholesale)
Part 4 — Quantity Break Discounts
Use multiple price rows with different min_quantity values to
automatically apply bulk discounts. The platform always selects the row with the
highest min_quantity that still fits the buyer's order quantity.
| Row | Tier | Min Qty | Unit Price | When applied |
|---|---|---|---|---|
| 1 | retail | 1 | UGX 45,000 | Orders of 1–9 units |
| 2 | retail | 10 | UGX 40,000 | Orders of 10–49 units |
| 3 | retail | 50 | UGX 35,000 | Orders of 50+ units |
⚠️ Always include a row with min_quantity=1. If your smallest row has min_quantity=10, a buyer adding 5 units will get a "no price configured" error even though prices clearly exist in the admin.
Part 5 — Diagnosing "No Price Configured" Errors
You see a price listed on the product page but the buyer gets an error when adding to cart. This happens when the price record exists but fails all five resolution steps. Here are the common causes and fixes:
| Symptom | Root cause | Fix |
|---|---|---|
| Error on add-to-cart for qty=1, price visible in admin | min_quantity is set to something greater than 1 on all price rows |
Add a new price row with min_quantity = 1 |
| Price visible in admin but not in shop | is_active = False on the price record |
Open the price record, tick Active, save |
| Price worked last week, fails now | valid_until date has passed |
Update valid_until to a future date, or clear it (no expiry) |
| Price set up but not activating yet | valid_from is in the future |
Clear valid_from or backdate it to today |
| B2C buyer gets error, B2B buyer is fine | Only a wholesale tier price exists — no retail row | Add a separate price row with customer_tier = retail |
| Price shows correctly in shop but Order Summary shows wrong currency | Product price currency does not match company default currency | Check your Company profile currency matches the currency code on your price records |
Platform administrators can scan all products for price configuration issues from the Render shell:
python manage.py check_product_prices
To check a single product:
python manage.py check_product_prices --product-id 42
To automatically reactivate prices that are inactive but otherwise valid:
python manage.py check_product_prices --fix-inactiveThe tool reports BLOCKING problems (will fail add-to-cart), warnings (prices expiring soon), and an OK summary for healthy products.
Part 6 — Multi-Currency & International Buyers
As a seller you set prices in your local currency (e.g. UGX). International buyers automatically see a converted price for reference alongside your original price. You do not need to create separate price records for each currency.
- Exchange rates are refreshed daily from Open Exchange Rates.
- On the shop listing: UGX 45,000 with ≈ USD 12.20 shown below in smaller text.
- On the product detail page: an "Approx." line below the headline price with the buyer's currency and a conversion badge (e.g.
UGX → USD). - In the cart Order Summary: an indicative total in the buyer's currency with the note "actual charge in UGX".
- The actual transaction is always processed in the seller's currency. The conversion is informational only.
- If no FX rate is available for a pair (e.g. an obscure currency), the converted line is simply hidden — no error is shown.
Go to Settings → Company Profile → Default Currency and set it to the currency your prices are denominated in (e.g. UGX for Uganda, KES for Kenya, USD for USD-denominated businesses). This currency is used as the cart total label and the FX conversion base. Mismatched settings cause the Order Summary to show the wrong currency code on the correct number.
Part 7 — Promotional & Time-Limited Prices
Use valid_from and valid_until to schedule promotional pricing
without touching your standard price row.
Keep your standard price row intact
Never edit the dates on your permanent price row. Instead, create a second price row with the promotional rate.
Add a promotional price row
| Field | Value |
|---|---|
| Currency | Same as standard price |
| Customer Tier | retail (or wholesale) |
| Unit Price | Your promotional rate, e.g. 35,000 |
| Min Quantity | 1 |
| Valid From | Promotion start date/time |
| Valid Until | Promotion end date/time |
| Active | ✓ ticked |
How the platform handles the overlap
During the promotional window, both rows are valid. The
platform selects the row with the highest min_quantity that
still applies — so if both have min_quantity=1, the lower-priced row wins
(it's ordered by -min_quantity, then the query returns whichever
comes first). To guarantee the promo wins, set it to a slightly higher
min_quantity (e.g. 1 for promo vs. 0 is not possible, so use an explicit
valid_until instead — the standard row should have no dates).
is_active toggle rather than editing dates.