From PHP Dev to Odoo Developer: My Learning Journey (And What I Built in 6 Weeks)

A honest account of picking up Odoo development with a Laravel, Magento 2, and CodeIgniter background — what clicked fast, what didn’t, and the mini projects I built along the way.


Why Odoo?

A job vacancy landed in my feed. The company was looking for an Odoo Developer — minimum 2 years of hands-on Odoo experience, strong Python, custom module development, the works. The perks were solid: international clients, official Odoo certification, triple-monitor setup, free lunch. Located right in my city.

There was one problem: I had zero Odoo experience.

What I did have was several years of backend development — Laravel, Magento 2, CodeIgniter — and a willingness to learn fast. So I gave myself 6 weeks and got to work.

This is what happened.


The Honest Gap Analysis

Before touching any code, I mapped out what I actually had versus what the job needed.

The good news first: backend fundamentals transfer well. OOP, MVC patterns, REST APIs, Git, debugging instincts, working with complex systems — all of that carries over. My Magento 2 experience turned out to be particularly relevant: Magento is also a module-based ERP-adjacent platform with complex customization layers. That mental model helped.

The gaps were real though:

  • Python — I had never written a line of it
  • Odoo ORM — completely new territory
  • XML/QWeb — the templating system Odoo uses for views and PDF reports
  • Odoo module architecture — how modules are structured, how they depend on each other, how inheritance works

Six weeks to close those gaps. Doable, but only with a structured plan.


Week 1–2: Python, Fast

I didn’t start from absolute zero — I started from “I already know how to program, I just need the translation layer.”

The fastest resource I found for this: Learn X in Y Minutes — Python. It’s a cheatsheet, not a tutorial. For someone coming from PHP, it’s perfect. Twenty minutes and you understand the syntax differences that matter.

The concepts I focused on first:

Indentation instead of curly braces — obvious, but it takes a day or two before it becomes muscle memory.

OOP in Python — this is critical for Odoo. Every model is a class. Every method lives on a class. The self keyword works like PHP’s $this, just without the dollar sign.

 
python
class Product:
    def __init__(self, name, price):
        self.name = name
        self.price = price

    def discounted_price(self, pct):
        return self.price * (1 - pct / 100)

Decorators — this one tripped me up at first, but it’s everywhere in Odoo. @api.depends, @api.constrains, @api.onchange — all of these are decorators that tell Odoo when and how to call your methods.

List comprehensions — Odoo code uses these constantly for filtering recordsets.

I spent two weeks here, but I was writing actual Python scripts from day three. The key: don’t try to master Python in isolation. Learn just enough, then get into Odoo.


Week 3: Setting Up Odoo Locally (Docker Made This Easy)

I run Windows 11. The easiest local Odoo setup I found: Docker Desktop + a docker-compose.yml that spins up both PostgreSQL and Odoo 17.

 
yaml
services:
  db:
    image: postgres:15
    environment:
      POSTGRES_PASSWORD: odoo
      POSTGRES_USER: odoo

  odoo:
    image: odoo:17.0
    depends_on:
      - db
    ports:
      - "8069:8069"
    volumes:
      - ./addons:/mnt/extra-addons

The ./addons volume mount is the important part — it maps a local folder into the container, so any module I create on my machine is immediately available to Odoo. No file copying, no rebuilding images.

Before writing any code, I spent a few hours exploring Odoo as a user: activated Contacts and Inventory, created some records, poked around. This matters more than it sounds. When you’re writing code that generates UI, you need to know what the UI is supposed to feel like.

Then I did something that paid off fast: I opened the source code of the product module directly in VS Code (using the Dev Containers extension to attach to the running container) and read through __manifest__.py and models/product_template.py. Reading real production Odoo code before writing your own teaches you patterns you won’t find in beginner tutorials.


Week 4–5: Building the Library Module

The first custom module I built: a Library Management system. Simple scope, but it touched every fundamental concept.

The module structure:

 
library/
├── __manifest__.py      # module metadata & dependency declaration
├── __init__.py
├── models/
│   ├── __init__.py
│   ├── book.py          # Book model
│   ├── member.py        # Member model
│   └── borrow_transaction.py  # Borrow/Return transactions
├── views/
│   ├── book_views.xml
│   ├── member_views.xml
│   └── borrow_transaction_views.xml
└── security/
    └── ir.model.access.csv

What I learned building this:

Fields are declarative. In Laravel, you write a migration to add a column, then separately declare $fillable in the model. In Odoo, one field definition handles both:

 
python
name = fields.Char('Title', required=True)
state = fields.Selection([
    ('available', 'Available'),
    ('borrowed', 'Borrowed'),
], default='available')

That single line creates the database column, handles validation, sets the default, and tells the UI how to render it.

Computed fields replace a lot of manual logic. Instead of remembering to update available_qty whenever stock_qty or borrowed_qty changes, you declare the dependency and Odoo handles the rest:

 
python
available_qty = fields.Integer(
    compute='_compute_available_qty',
    store=True,
)

@api.depends('stock_qty', 'borrowed_qty')
def _compute_available_qty(self):
    for record in self:
        record.available_qty = record.stock_qty - record.borrowed_qty

View inheritance via xpath. This is Odoo’s superpower. You can modify any existing view without touching the original file:

 
xml
<field name="inherit_id" ref="sale.view_order_form"/>
<xpath expr="//notebook" position="inside">
    <page string="My Custom Tab">
        <field name="my_custom_field"/>
    </page>
</xpath>

If you’ve worked with Magento 2 layout XML and <referenceBlock>, this concept will feel immediately familiar.


Week 6: The Rental Management Mini Project

The final project was more complex: a full Rental Management module with items, customers, rental orders, a state machine (Draft → Confirmed → Ongoing → Returned), PDF reports, and a wizard for cancellations.

A few things worth highlighting from this build:

Auto-generated order references using ir.sequence:

 
RNT/2026/0001, RNT/2026/0002, ...

QWeb PDF reports — Odoo’s templating engine for generating PDFs. The syntax is HTML with special t- directives:

 
xml
<t t-foreach="docs" t-as="doc">
    <h1><t t-esc="doc.name"/></h1>
</t>

Scheduled Actions (cron jobs) — auto-cancel draft orders older than 7 days, flag overdue rentals. Defined in XML, executed by Python methods with @api.model.

Wizards (TransientModel) — popup dialogs that collect input before executing an action. The cancellation wizard asks for a reason before cancelling an order, then stores that reason on the record.


What I Built: The GitHub Repo

Both modules are on GitHub: github.com/awandha/odoo-learning

  • library/ — Library Management (Books, Members, Borrow Transactions)
  • rental/ — Rental Management (Items, Customers, Orders, PDF Report, Wizard, Cron Jobs)
  • sale_custom/ — Sales Order extension (new fields, xpath view injection, method override)

The Honest Takeaway

Six weeks is not enough to call yourself an experienced Odoo developer. But it is enough to go from zero to being able to:

  • Build custom modules from scratch with proper structure
  • Extend existing Odoo modules without touching core files
  • Work with models, views, security, reports, and scheduled actions
  • Read and understand real Odoo source code
  • Debug errors from stack traces

The PHP background helped more than I expected, especially Magento 2. If you already think in modules, dependencies, and ORM abstractions, the Odoo mental model clicks faster.

The hardest part wasn’t the code — it was learning to trust Odoo’s declarative approach. Coming from Laravel where you control everything explicitly, it feels strange to let the framework handle so much. But once you stop fighting it and lean into the conventions, development gets fast.

If you’re a PHP developer considering the move to Odoo: the gap is bridgeable. Python is learnable in days if you already know another language. The Odoo-specific patterns take a few weeks. The investment is worth it.


Currently working through more advanced topics — API integrations, more complex reporting, and the Odoo JavaScript framework. Will write about those when I have something worth sharing.