# Fix Template Error Summary - website_sale_aplicoop
**Date**: 2026-02-16
**Final Status**: ✅ PERMANENTLY RESOLVED
**Solution Commit**: 5721687
**Version**: 18.0.1.1.1
---
## Problem
The `eskaera_shop_products` QWeb template was throwing a `TypeError: 'NoneType' object is not callable` error when loading the store page.
### Root Cause - QWeb Parsing Limitations
QWeb has strict limitations on what expressions it can parse:
1. **Complex nested conditionals in t-set fail**
```xml
❌
```
2. **Direct 'or' in attributes unreliable**
```xml
❌
```
3. **Deep object chains with conditionals fail**
```xml
❌ t-set="uom" t-value="product.uom_id.category_id.name if product.uom_id.category_id else ''"
```
---
## Solution
### Architecture: Move Logic to Controller
**Final insight**: Don't fight QWeb's limitations. Move ALL complex logic to the Python controller where it belongs.
#### The Pattern
```
CONTROLLER (Python)
↓ (process data, handle None)
product_display_info = {
product.id: {
'display_price': 10.99, # Always a float, never None
'safe_uom_category': 'Weight' # Always a string, never None
}
}
↓ (pass clean data to template)
TEMPLATE (QWeb)
↓ (simple dict.get() calls, no logic)
```
#### Implementation
**In Controller** - Added `_prepare_product_display_info()` method:
```python
def _prepare_product_display_info(self, product, product_price_info):
"""Pre-process all display values for QWeb safety.
All logic happens HERE in Python, not in template.
Returns dict with safe values ready for display.
"""
# Get price - handle None safely
price_data = product_price_info.get(product.id, {})
price = price_data.get("price", product.list_price) if price_data else product.list_price
price_safe = float(price) if price else 0.0
# Get UoM category - handle None/nested attributes safely
uom_category_name = ""
if product.uom_id:
if product.uom_id.category_id:
uom_category_name = product.uom_id.category_id.name or ""
return {
"display_price": price_safe, # Never None
"safe_uom_category": uom_category_name, # Never None
}
```
**In Template** - Simple dict.get() calls:
```xml
```
---
## What Changed
### Files Modified
1. **website_sale_aplicoop/controllers/website_sale.py**
- Added `_prepare_product_display_info()` method (lines 390-417)
- Generate `product_display_info` dict in `eskaera_shop()` (lines 1062-1065)
- Generate `product_display_info` dict in `load_eskaera_page()` (lines 1260-1263)
- Pass to template renders
2. **website_sale_aplicoop/views/website_templates.xml**
- Removed complex conditional expressions from template
- Replaced with simple `dict.get()` calls
- No business logic remains in template
### Iteration History
| Commit | Approach | Result |
|--------|----------|--------|
| df57233 | Add `or` operators in attributes | ❌ Error persisted |
| 0a0cf5a | Complex nested conditionals in t-set | ❌ Error persisted |
| 8e5a4a3 | Three-step pattern with `or` chains | ⚠️ Error persisted |
| 5721687 | Move logic to controller | ✅ SOLVED |
### Why This Works
1. **Two-step computation**: Separates extraction from fallback logic
2. **Python short-circuit evaluation**: `or` operator properly handles None values
3. **Avoids complex conditionals**: Simple `or` chains instead of nested `if-else`
4. **QWeb-compatible**: The `or` operator works reliably when value is pre-extracted
5. **Readable**: Clear intent - extract value, then fall back
---
## Changes Made
### File: `website_sale_aplicoop/views/website_templates.xml`
**Location 1**: Price computation (lines 1165-1177)
**Before**:
```xml
```
**After**:
```xml
```
**Location 2**: Form element (lines 1215-1228)
**Before**:
```xml