ARONLIGHT — PRICING MECHANISM

How Aronlight's price lists are actually structured

Confirmed directly by Nuno, screen-share walkthrough, 2026-07-13 (docs/calls/2026-07-13-nuno-pricing.md). This is the data structure — the algorithm below is how the agent should read it.

↔ this entire stack almost always exists TWICE — once under Aronlight PT, once under Aronlight España — same names, same prices (LIQUIDO-1). Verified: 40 of 43 unique pricelists confirmed as an exact pair. 3 known exceptions: 2 ES-only custom deals, 1 PT-only list.
Entry point — this is where "Lista de Preços" actually lives
Client record (default) or the order itself (override)
Odoo fields property_product_pricelist (client) / pricelist_id (order). This field is not a layer in the stack below — it's the pointer that picks ONE specific box from the row directly below, for this one quote.
↓ picks exactly one of these three ↓
Assignable · Pure discount
e.g. "52%"
One rule: flat % off a named base list below. Simple enough to manage in the Odoo dashboard directly — no Excel needed.
Assignable · Mixed
e.g. "52% mais líquidos"
Per-product net (líquido) price where defined, + fallback % off the base list if no net price is set for that product. (LIQUIDO-2)
Assignable · Volume tier
e.g. "España 60% + Netos"
Same as Mixed, but multiple net prices per product, gated by minimum quantity. Odoo picks the highest tier ≤ the requested quantity — it does NOT check tiers decrease monotonically. (LIQUIDO-3)
Customer-specific price lists (e.g. "Lunisk Cop": 6 products with special net prices + standard 50% fallback) are also assignable here — same pattern, just scoped to one client instead of a market. "Visible" and "non-visible" (LIQUIDO-6) are two assignable variants of the same discount, not a separate layer — see below.
↓ each of these references, as its base ↓
Base list — Portugal
Not directly assignable
Structurally unique — no separate list at all. Reads sales_price straight off the product record, 0% discount. Can't even be exported the same way as the other three.
Base list — Spain
Not directly assignable
Explicit fixed-price line per product — 2,959 lines, independently maintained.
Base list — France
Not directly assignable
Explicit fixed-price line per product, same pattern as Spain.
Base list — Others (Italy +)
Not directly assignable
Explicit fixed-price line per product, same pattern as Spain.
No client or order is ever assigned directly to one of these 4 — they only exist to be referenced by the assignable lists above. ES / FR / Others each carry a catch-all rule: price = €0 if the product isn't listed here. Deliberate — blocks Odoo's native behavior of silently falling back to the Portuguese price on a non-PT quote.

Visible vs. non-visible — same math, different display

Visible — "Lista PT 48%"
List price: €59.17
Discount: 48%
Final: €30.77
=
Non-visible — "…(sem desconto visível)"
List price: hidden
Discount: hidden
Final: €30.77
Mechanism: the non-visible variant is a formula rule referencing the visible list as its base, with 0% additional adjustment — designed to compute the identical price, just displayed without the discount column. Confirmed real: back office manually points a specific sale order at either variant (Carla's Dinolux order used the non-visible one, while the client's stored default is the visible one) — a genuine order-level override, independent of the client record. Still unknown: the criteria for when back office makes this switch. (LIQUIDO-6)

How the agent should read this structure

A quote line goes in as (client, SKU, quantity). This is the path it takes to come out as a final price.

✅ confirmed ready to build now 🔧 needs build rule known, no data model yet ❓ open genuinely unresolved, needs a person
0 Routing — which Odoo company owns this order? ✅ SPAIN-1
  • Proposal only, no sale yet → Odoo Spain
  • Actual sale (dropship) → Odoo Portugal (Aronlight España as customer, real client only in delivery address)
  • Not Spain-related → default company (Portugal)
Confirmed directly by Adriana Fernandes, 2026-07-13 ("exactamente").
1 Resolve "Lista de Preços" — the single most load-bearing field ✅ LIQUIDO-1 / LIQUIDO-6
Nothing in Step 2/3 has anything to search until this is resolved. Every price computed downstream inherits whatever mistake happens here — this is not a small error, it's a total miss.
  • On the client record — Odoo field property_product_pricelist. This is the default. It's also literally what back office reads first when investigating a quote (Carla, on a real case: "vejo: Lista PT 48%...").
  • On the order/proposal itself — Odoo field pricelist_id. Read this FIRST. Confirmed real (Carla's Dinolux order): this can differ from the client's default when back office manually switches it while building a specific quote. Only fall back to the client's default if the order doesn't have its own value yet.
Three ways this goes wrong, each needing different handling:
  • Blank (new client, never assigned) — don't guess or default silently. Flag for manual assignment.
  • Wrong (data entry error, deal changed, nobody updated the record) — the agent has no way to catch this on its own; it will compute a confident, wrong price and never know it's wrong.
  • Overridden (a one-off order-level switch — see "Visible vs. non-visible" above) — real and confirmed. Read the order's own field before falling back to the client default.
2 Look for a líquido (fixed) price ✅ LIQUIDO-2 / LIQUIDO-3
Search the resolved pricelist for a product-specific fixed-price rule.
  • 2+ quantity tiers on this product? → pick the highest tier ≤ requested quantity
  • Found a líquido price? → use it, skip step 3 entirely
Verified directly in Odoo: one real pricelist has 66 of 232 products with confirmed multi-tier quantity breaks, identical on both PT and ES company copies.
3 Fallback — standard % discount ✅ LIQUIDO-2
Only reached if step 2 found nothing. Uses the pricelist's single global rule. Verified: one pricelist has exactly 231 fixed-price rules + 1 global percentage rule — this is Odoo's own rule-matching order, not custom logic we'd have to build.
4 Second discount, market-specific 🔧 DISC-1
  • Spain → ad hoc, salesperson-entered. Never computed — just preserved.
  • Italy → fixed, per client. No storage exists yet (aronlight#398).
  • Portugal / France → unknown. No evidence either way.
5 Known gaps — flag, don't silently trust ❓ LIQUIDO-4 / LIQUIDO-5
  • Sales-team ad hoc exceptions — trigger unknown, route to a human
  • "Caixa fechada" (box quantity) — the data exists (product.packaging, 215+ products), but it's not confirmed to affect price on the one product we checked
  • Pricelist rules may get hand-tuned over time — Carla's case shows a live example
  • Validate volume-tier monotonicity ourselves — Odoo only matches minimum quantity, it does NOT check that a higher tier is actually cheaper than a lower one or the fallback. Confirmed as a real risk by Nuno unprompted, on the 2026-07-13 call. If a tier price is higher than what a smaller order would pay, flag it — don't just apply whatever Odoo returns.
6 Display — show the discount, or hide it? 🔧 needs UI
Per-user default + always-available per-case override. The "sem desconto visível" pattern found this week suggests this may already be half-built at the Odoo level — the platform may just need to surface it, not invent it.

Rules confirmed so far

IDRuleConfidenceEvidence
SPAIN-1Proposal (Odoo Spain) vs. sale (Odoo Portugal) routingHIGHConfirmed by Adriana directly
LIQUIDO-1Every pricelist duplicated across PT + ES companies, same nameHIGHWhatsApp + Odoo data (40/43 pairs confirmed)
LIQUIDO-2Líquido price always wins over computed discountHIGHWhatsApp + Odoo data (231 fixed + 1 global rule)
LIQUIDO-3Volume tiers = líquido prices gated by min_quantityHIGHES + Italy in Odoo; PT confirmed manual-only (Excel)
LIQUIDO-6Back office manually switches pricelist on the sale orderMEDIUMConfirmed live on Carla's Dinolux case with Nuno
OD-4Tabela vs. Odoo source-of-truth splitHIGH (code)Read directly in the matching code
DISC-1Second-discount semantics are market-specificMEDIUMSpain + Italy confirmed; PT/France open
LIQUIDO-4Ad hoc price-table exceptions — not yet codifiableLOWExplicitly "não sei" from Nuno — needs sales team
LIQUIDO-5"Caixa fechada" — data location confirmed, pricing link notMEDIUM (location) / LOW (link)Odoo data + independently confirmed uncertain by Nuno too

Answered on the 2026-07-13 Nuno call

Still open

Nuno named José Monteiro directly for these 2, on the 2026-07-13 call:

  1. What are the switching criteria for when back office moves a customer between the visible and non-visible pricelist on a specific order?
  2. Volume-tier + "cache complète" Excel — same table for both? Which countries does each apply to? Is cache-complète ever applied automatically in Odoo?

Carried over from earlier — likely also need José Monteiro or the sales team, but weren't raised with Nuno by name in this call:

  1. What are the known ad hoc pricing exceptions (LIQUIDO-4)? Nuno pointed to the sales team generally for this, in the earlier WhatsApp thread — still the single biggest gap in what's buildable today.
  2. Why is "ITALIA 50+20%" assigned to zero customers? The combined pricelist exists and works — just needs assignment, unless something's blocking it.
  3. The Spain +25% markup (some ES clients see prices above PT list) — legitimate separate base pricing, or a data error nobody's caught?
  4. Portugal / France second-discount behavior — do either follow Spain's ad hoc pattern, Italy's fixed pattern, or something else entirely?