POS Applet — SA-Governed Retail Sales¶
Status: Draft
Date: 2026-06-12
ADR: 0005 - V3 POS Applet Design
Overview¶
The POS Applet extends the V3 association-model pattern to Odoo's Point of Sale
module. Unlike B2B sales (sale.order), POS collapses the entire sales process
into a single checkout step. The POS Applet governs these transactions through
functionality-scoped Serviced Accounts.
B2B vs POS Comparison¶
| Step | B2B (sale.order) |
POS (pos.order) |
|---|---|---|
| Customer | Selected upfront | Identified at checkout (phone/card) |
| Products | Added to quote | Added to cart |
| Pricing | Pricelist on SO | Pricelist on POS config, SA-scoped |
| Inventory | Separate delivery order | Auto-decremented from POS location |
| Invoice | Explicit create step | Auto-generated on validation |
| Payment | Separate reconciliation | One-step at checkout |
| Receipt | Optional | Auto-printed/emailed |
| SA context | From customer | From cashier's membership |
Core Principle¶
SA = Functionality Boundary
In POS, the Serviced Account represents what type of products or services are being sold — not who the customer is or where the store is located.
Retail Shop SA → physical goods (fans, batteries, solar systems)
Swap Station SA → battery swap services
Supermarket SA → combined retail (multiple product families)
Architecture¶
Association Models¶
ov.sa_pos_config (NEW)
├── account_id → SA # Functionality (Retail, Swap, etc.)
├── config_id → pos.config # POS terminal/store config
└── association_kind → binding/access
ov.sa_pricelist (NEW)
├── account_id → SA # Functionality that uses this pricelist
├── pricelist_id → product.pricelist
└── association_kind → binding/access
ov.sa_pos_order (EXISTS, 0 records)
├── account_id → SA # Cashier's active functionality SA
├── pos_order_id → pos.order # The POS transaction (NOT sale.order)
└── association_kind → binding
ov.actor_sa_pos_order (EXISTS, 0 records)
├── assignment_id → ov.sa_pos_order # Follows V3 subset rule
├── actor_id → res.partner # The cashier
└── state / is_primary
Key Difference from B2B¶
B2B: SA context comes from the CUSTOMER
ov.sa_sale_order → account_id = customer's governing SA
POS: SA context comes from the CASHIER'S MEMBERSHIP
ov.sa_pos_order → account_id = cashier's active functionality SA
This means:
- A cashier at a combined shop+swap location selects their role at shift start
- Retail sales go to Retail SA; swap transactions go to Swap SA
- The same customer can shop at both without being pre-assigned
- New customers are auto-admitted to the SA at checkout
Transaction Flow¶
Shift Start¶
Cashier logs into POS
→ System determines available SAs (via cashier's ov.membership)
→ Cashier selects functionality SA for the shift
(auto-selected if only one membership)
→ UI filters:
• Pricelists → ov.sa_pricelist for selected SA
• Customers → not filtered (search all)
Checkout¶
1. Customer identified (phone / service card)
2. Auto-admission: if new customer, system creates ov.sa_customer
3. Cart: products added, serials scanned
4. Payment: M-Pesa / cash / card (standard POS)
5. Validate (one-click):
• pos.order created
• ov.sa_pos_order created (account_id = cashier's SA)
• ov.actor_sa_pos_order created (actor_id = cashier)
• Invoice auto-created
• Stock decremented
• Receipt printed
Customer Auto-Admission¶
Customers are always served. At checkout:
- Lookup by phone or card
- Check
ov.sa_customerfor this partner + this SA - If no record exists → auto-create with
association_kind = 'binding' - The act of purchase constitutes admission to the SA's functionality scope
Implementation Status¶
| Model | Status | Action Needed |
|---|---|---|
ov.sa_pos_config |
❌ Not found | Create (V3 canonical structure) |
ov.sa_pricelist |
❌ Not found | Create (general-purpose, not POS-specific) |
ov.sa_pos_order |
✅ Exists | Add ir.rule on pos.order to activate |
ov.actor_sa_pos_order |
✅ Exists | No action needed |
Open Questions¶
- Cashier UI: Does the POS front-end need a custom SA selector, or can the cashier switch Odoo company/user context?
- Pricelist filtering: How does
ov.sa_pricelistfeed into the POS pricelist dropdown — viair.ruleor server-side compute? - Customer search scope: Should the POS partner search be restricted to
customers visible through
ov.sa_customer, or search all partners?
References¶
- ADR 0003: V3 Association-Model Migration Pattern
- ADR 0005: V3 POS Applet Design
- Odoo Extension Architecture
ov.sa_sale_order— Reference model (689 records, actively used)