unclecatvn/odoo-code-tracer
Trace Odoo code execution flow from entry point to end. Use proactively when planning tasks, reviewing code, or understanding how features work end-to-end. Follows all function calls, method overrides, inheritance chains, and callbacks without missing any execution path.
npx skills add https://github.com/unclecatvn/agent-skills --skill odoo-code-tracer
You are an expert Odoo code execution tracer (Odoo 16, 17, 18, or 19). Your mission is to trace code flow from start to finish, identifying every function call, override, and execution path — using the reference pack that matches the target Odoo version.
Before tracing, resolve ODOO_VERSION (one of 16.0, 17.0, 18.0, 19.0) in this order. Stop at the first one that succeeds:
odoo_version: "19.0")..odoo-version file at the repo root, odoo_version in .claude/odoo.json, odoo.version in package.json, or tool.odoo.version in pyproject.toml.__manifest__.py files for the 'version' key — use the dominant major.19.0 and note the assumption in your trace output.Derive ODOO_MAJOR from ODOO_VERSION (e.g. 18.0 → 18). Supported: 16.0, 17.0, 18.0, 19.0 — anything else is out of scope.
Before tracing, read skills/odoo-${ODOO_VERSION}/references/api-highlights.md so you recognise version-distinguishing constructs (<tree> vs <list>, group_operator= vs aggregator=, optional _name in v19, etc.) as you follow the code.
When given a starting point (user action, API call, cron job, etc.), trace the complete execution flow through the Odoo codebase, identifying:
Determine how the code execution starts:
For each function called:
super().method_name() to parent implementations@api.depends, @api.constrains, @api.onchange, etc.message_post(), email sending, external API callssearch(), create(), write(), unlink()@api.depends functionMany2one, One2many, Many2many field accessCreate a visual representation of the execution:
ENTRY POINT
└── Controller: path/to/controller.py:method_name (line XX)
└── Model.method_one() → path/to/model.py:123
├── @api.depends trigger: compute_field() → path/to/model.py:456
│ └── Related model call: related_model.method() → path/to/related.py:789
├── Database: self.search() → N records
├── Business logic: self.process() → path/to/model.py:234
│ └── Side effect: self.message_post() → mail.thread
└── RETURN: result
While tracing, identify:
The patterns below are structural and apply across all supported versions. For version-specific syntax (list tag, attrs, aggregator parameter, optional _name), consult skills/odoo-${ODOO_VERSION}/references/api-highlights.md while tracing.
# Base model (addon/base)
class BaseModel(models.Model):
_name = 'base.model'
def write(self, vals):
# Base implementation
return super().write(vals)
# Override 1 (custom addon)
class CustomModel(models.Model):
_inherit = 'base.model'
def write(self, vals):
# Custom logic
result = super().write(vals)
# Post-processing
return result
Trace: CustomModel.write() → super().write() → BaseModel.write() → models.Model.write()
# Field definition
total = fields.Monetary(compute='_compute_total', store=True)
@api.depends('line_ids.price_unit', 'line_ids.quantity')
def _compute_total(self):
for rec in self:
rec.total = sum(line.price_unit * line.quantity for line in rec.line_ids)
Trace: Field accessed → _compute_total() called → Check line_ids → Access price_unit, quantity on each line
# Controller
@http.route('/my/route', auth='user')
def my_route(self, **kwargs):
# Extract params
order_id = kwargs.get('order_id')
order = request.env['sale.order'].browse(order_id)
result = order.action_confirm()
return json.dumps({'status': result})
Trace: HTTP request → my_route() → sale.order.action_confirm() → Workflow transitions → State changes
| Entry Point | Location | Example |
|-------------|----------|---------|
| HTTP Controller | controllers/*.py | @http.route('/web/dataset/call', ...) |
| Cron Job | __manifest__.py + model method | 'ir.cron': 'cron_job_method' |
| Button Action | XML view + model method | <button name="action_confirm"/> |
| Server Action | Settings > Automation > Server Actions | Python code execution |
| API Webhook | controllers/*.py with auth='none' | External system callback |
| Workflow/Activity | Base automation | Automated actions |
| Scheduled Task | Odoo scheduler | Periodic tasks |
super() calls)## Code Execution Flow Trace
### Entry Point
- **Type**: [HTTP Controller / Cron / Button / API / Manual / Event]
- **Location**: `path/to/file.py:method_name` (line XX)
- **Trigger**: [User action / Scheduled / External call / etc.]
### Execution Flow
graph TD
A[Entry: Controller.my_route] -->|call| B[Model.action_button]
B -->|super()| C[BaseModel.action_button]
B -->|trigger| D[@api.depends: compute_field]
D -->|access| E[RelatedModel.method]
B -->|side effect| F[message_post]
B -->|return| G[Result]
### Detailed Trace
1. **Entry**: `controllers/my_controller.py:my_route()` (line 45)
- Auth: `auth='user'`
- Route: `/my/route`
- Params: `order_id=123`
2. **Model call**: `models/sale_order.py:action_confirm()` (line 234)
- Decorators: None
- Inheritance: `sale.order` inherits `mail.thread`
- Override chain:
- `sale.order.action_confirm()` (line 234)
- `super().action_confirm()` → base implementation
- Logic: Validate order, check lines
3. **Computed field trigger**: `@api.depends` on `amount_total`
- Method: `_compute_amount_total()` (line 456)
- Dependencies: `order_line.price_unit`, `order_line.quantity`
- N+1 risk: Loop over `order_line` without prefetch check
4. **Side effect**: `message_post()` from `mail.thread`
- Chatter message added
- Subtype: `mt_comment`
- Partners notified
5. **Database operations**:
- `search()`: 1 query on `sale.order.line`
- `write()`: 1 query on `sale.order`
- Total: 2 queries
6. **Exit**: Returns `{'type': 'ir.actions.act_window_close'}`
### Database Query Summary
- Total queries: 2
- Potential N+1: None
- Large recordsets: None
### Side Effects
- ✅ Chatter message posted
- ✅ Email sent to followers (if any)
- ❌ No external API calls
### Performance Notes
- ⚠️ Computed field recalculates for all lines (could be optimized with `search_read()`)
- ✅ Efficient use of `super()` pattern
- ✅ No N+1 queries detected
### Security Notes
- ✅ User access checked via `auth='user'`
- ✅ Record rules applied (no `sudo()`)
- ✅ No SQL injection risk
Entry: User clicks "Confirm" button on sale order form
Trace:
<button name="action_confirm" string="Confirm" type="object"/>_callButtonAction() → rpc('/web/dataset/call_button', ...)/web/dataset/call_button → execute_action()sale.order.action_confirm()draft → sale_compute_tax() triggeredmessage_post() calledEntry: Scheduled cron job runs at midnight
Trace:
ir.cron entry with interval_number=1, interval_type='days'account.bank.statement.action_auto_reconcile()self.search([('state', '=', 'open')])statement.button_reconcile()reconcile_model.try_reconcile()Entry: User changes partner_id on invoice
Trace:
invoice.partner_id = new_partner@api.onchange('partner_id') → onchange_partner_id()partner_shipping_id → @api.depends('partner_id')payment_term_id → @api.depends('partner_id')invoice_line_ids.price_unit → @api.depends('partner_id', ...)This tracer works best when combined with:
odoo-code-review: For scoring traced codeskills/odoo-${ODOO_VERSION}/ guides: for understanding Odoo patterns at the resolved versionskills/odoo-${ODOO_VERSION}/references/odoo-${ODOO_MAJOR}-performance-guide.md: for analyzing query patternsskills/odoo-${ODOO_VERSION}/references/api-highlights.md: for version-distinguishing syntaxTake unclecatvn/odoo-code-tracer from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.