Odoo Python ڈویلپمنٹ: ابتدائی اور پیشہ ور افراد کے لیے مکمل گائیڈ
2025 Python ڈویلپر سروے کے مطابق، 47% سے زیادہ انٹرپرائز Python ڈویلپرز کم از کم ایک ERP فریم ورک کے ساتھ کام کرتے ہیں، اور Odoo اوپن سورس ERPs کے درمیان 68% مارکیٹ شیئر کے ساتھ اس حصے پر غلبہ رکھتا ہے۔ چاہے آپ اپنا پہلا ماڈیول بنا رہے ہوں یا ملٹی کمپنی کی تعیناتی کی تعمیر کر رہے ہوں، Odoo کی Python تہہ کو سمجھنا وہ واحد سب سے زیادہ مؤثر مہارت ہے جسے آپ تیار کر سکتے ہیں۔
یہ گائیڈ سطحی سطح کے جائزہ سے بہت آگے ہے۔ ہم Odoo's Python بیک اینڈ کی ہر پرت سے گزریں گے — ORM کے میٹا کلاس میجک سے لے کر ایڈوانس کنٹرولر پیٹرنز تک، پروڈکشن کے مسائل کو ڈیبگ کرنے سے لے کر تحریری ٹیسٹ تک جو حقیقت میں رجعت کو پکڑتے ہیں۔
اہم نکات
- Odoo's ORM ایک مکمل آبجیکٹ-ریلیشنل میپر ہے جو میٹاکلاسز پر بنایا گیا ہے جو ایس کیو ایل اسکیموں کو خود بخود تیار کرتا ہے، کیشنگ کا انتظام کرتا ہے، اور ملٹی کمپنی سیکیورٹی کو سنبھالتا ہے — اسے گہرائی سے سمجھنا Odoo کے ساتھ کام کرنے اور اس سے لڑنے میں فرق ہے۔
- پانچ فیلڈ زمرہ جات ہر ڈیٹا ماڈلنگ کی ضرورت کا احاطہ کرتے ہیں: بنیادی (Char، Integer، Float، Boolean، Text، Html، Date، Datetime، Binary، Selection)، Relational (Many2one، One2many، Many2many)، حسابی، متعلقہ، اور مانیٹری فیلڈز۔
- تین وراثت کے طریقہ کار آپ کو Odoo کے کسی بھی حصے کو بغیر کانٹے کے بڑھانے دیتے ہیں: کلاس وراثت (_inherit)، پروٹو ٹائپ وراثت (_inherit + _name)، اور delegation heritance (_inherits)۔
- کنٹرولرز JSON-RPC اور HTTP اختتامی نقطہ پیٹرن، تصدیق کے طریقوں، اور CORS کنفیگریشن کے ساتھ ڈیکوریٹرز (@http.route) کا استعمال کرتے ہوئے HTTP کو ہینڈل کرتے ہیں۔
- ٹرانزیکشن کیس اور ایچ ٹی پی کیس کے ساتھ خودکار جانچ پروڈکشن تک پہنچنے سے پہلے ہی ریگریشن پکڑتی ہے — کاروباری منطق پر 80%+ کوریج کا مقصد۔
1. اپنے ترقیاتی ماحول کو ترتیب دینا
کوڈ لکھنے سے پہلے، آپ کو ایک مناسب ترقیاتی ماحول کی ضرورت ہے۔ یہاں 2026 کے لیے تجویز کردہ سیٹ اپ ہے:
# Install system dependencies (Ubuntu/Debian)
# sudo apt install python3.12 python3.12-venv python3.12-dev
# sudo apt install postgresql-17 libpq-dev libxml2-dev libxslt1-dev
# Clone Odoo source (enterprise requires license)
# git clone https://github.com/odoo/odoo.git --branch 18.0 --depth 1
# git clone https://github.com/odoo/enterprise.git --branch 18.0 --depth 1
# Create virtual environment
# python3.12 -m venv odoo-venv
# source odoo-venv/bin/activate
# pip install -r odoo/requirements.txt
# Create database and start Odoo
# createdb mydb
# python odoo/odoo-bin -d mydb --addons-path=odoo/addons,enterprise,custom_addons
اپنے ماڈیولز کے لیے ایک custom_addons ڈائریکٹری بنائیں۔ ہر ماڈیول اپنی ذیلی ڈائرکٹری میں ایک مخصوص ڈھانچے کے ساتھ رہتا ہے جس کی اوڈو توقع کرتا ہے۔
2. ماڈیول آرکیٹیکچر ڈیپ ڈائیو
ہر اوڈو ماڈیول کنونشن سے زیادہ کنفیگریشن ڈھانچے کی پیروی کرتا ہے۔ ہر جزو کے کردار کو سمجھنا ضروری ہے:
my_module/
├── __init__.py # Python package initializer
├── __manifest__.py # Module metadata and dependencies
├── models/
│ ├── __init__.py
│ ├── sale_order.py # Business logic models
│ └── res_partner.py # Partner extensions
├── views/
│ ├── sale_order_views.xml # Form, tree, search views
│ └── menu.xml # Menu items and actions
├── controllers/
│ ├── __init__.py
│ └── main.py # HTTP controllers
├── security/
│ ├── ir.model.access.csv # ACL rules
│ └── security.xml # Record rules and groups
├── data/
│ ├── data.xml # Default data
│ └── demo.xml # Demo data (dev only)
├── wizards/
│ ├── __init__.py
│ └── mass_update.py # TransientModel wizards
├── report/
│ ├── report_invoice.xml # QWeb report templates
│ └── report.py # Report parsers
├── static/
│ ├── description/
│ │ └── icon.png # Module icon (128x128)
│ └── src/
│ ├── js/ # OWL components
│ └── xml/ # OWL templates
└── tests/
├── __init__.py
└── test_sale_order.py # Automated tests
مینی فیسٹ فائل
__manifest__.py آپ کے ماڈیول کا دل ہے۔ ہر میدان اہمیت رکھتا ہے:
# __manifest__.py
{
'name': 'Sales Commission Engine',
'version': '18.0.1.0.0', # Odoo version.major.minor.patch
'category': 'Sales/Commission',
'summary': 'Automated commission calculations for sales teams',
'description': """
Long description with RST formatting.
Supports multi-tier commission plans.
""",
'author': 'ECOSIRE Private Limited',
'website': 'https://ecosire.com',
'license': 'LGPL-3',
'depends': ['sale', 'account', 'hr'], # Required modules
'data': [
'security/ir.model.access.csv',
'views/commission_plan_views.xml',
'views/menu.xml',
'data/commission_data.xml',
],
'demo': ['data/demo.xml'],
'installable': True,
'application': True, # Shows in Apps menu
'auto_install': False,
'external_dependencies': {
'python': ['pandas'], # pip packages
},
}
ورژن نمبرنگ کنونشن: {odoo_version}.{major}.{minor}.{patch} — پہلا نمبر ہمیشہ Odoo سیریز سے میل کھاتا ہے۔
3. ORM API: مکمل حوالہ
Odoo کا ORM اس سے کہیں زیادہ طاقتور ہے جتنا زیادہ تر ڈویلپرز کو احساس ہے۔ یہ سکیما جنریشن، کیشنگ، ایکسیس کنٹرول، اور ملٹی کمپنی فلٹرنگ کو خود بخود ہینڈل کرتا ہے۔
ماڈل کی اقسام
from odoo import models, fields, api
# Persistent model — stored in database
class CommissionPlan(models.Model):
_name = 'commission.plan'
_description = 'Commission Plan'
_order = 'sequence, name'
_rec_name = 'name'
name = fields.Char(string='Plan Name', required=True, index=True)
active = fields.Boolean(default=True)
sequence = fields.Integer(default=10)
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
# Transient model — auto-cleaned after ~1 hour
class CommissionWizard(models.TransientModel):
_name = 'commission.calculate.wizard'
_description = 'Calculate Commissions'
date_from = fields.Date(required=True)
date_to = fields.Date(required=True)
# Abstract model — no database table, only for inheritance
class CommissionMixin(models.AbstractModel):
_name = 'commission.mixin'
_description = 'Commission Mixin'
commission_rate = fields.Float(string='Commission %')
فیلڈ کی اقسام ڈیپ ڈائیو
class SaleCommission(models.Model):
_name = 'sale.commission'
_description = 'Sale Commission Record'
# Basic fields
name = fields.Char(string='Reference', required=True, copy=False,
default=lambda self: self.env['ir.sequence'].next_by_code('sale.commission'))
amount = fields.Float(string='Amount', digits=(16, 2))
percentage = fields.Float(string='Rate %', digits=(5, 2))
is_paid = fields.Boolean(string='Paid', default=False)
notes = fields.Text(string='Internal Notes')
description = fields.Html(string='Description', sanitize=True)
date = fields.Date(string='Commission Date', default=fields.Date.today)
create_datetime = fields.Datetime(string='Created', default=fields.Datetime.now)
document = fields.Binary(string='Attachment')
document_name = fields.Char(string='File Name')
# Selection field
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
], string='Status', default='draft', tracking=True)
# Relational fields
salesperson_id = fields.Many2one('res.users', string='Salesperson',
required=True, ondelete='restrict')
order_id = fields.Many2one('sale.order', string='Sale Order')
line_ids = fields.One2many('sale.commission.line', 'commission_id', string='Lines')
tag_ids = fields.Many2many('sale.commission.tag', string='Tags')
# Computed field with store and search
total_amount = fields.Monetary(string='Total', compute='_compute_total',
store=True, currency_field='currency_id')
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
@api.depends('line_ids.amount')
def _compute_total(self):
for record in self:
record.total_amount = sum(record.line_ids.mapped('amount'))
# Related field (shortcut for computed)
salesperson_email = fields.Char(related='salesperson_id.email', store=True)
CRUD آپریشنز
# CREATE — returns new recordset
commission = self.env['sale.commission'].create({
'salesperson_id': self.env.user.id,
'order_id': order.id,
'state': 'draft',
'line_ids': [
(0, 0, {'product_id': product.id, 'amount': 150.00}), # Create
],
})
# Batch create (much faster than loop)
vals_list = [{'name': f'COM-{i}', 'amount': i * 10} for i in range(100)]
records = self.env['sale.commission'].create(vals_list)
# READ — browse by IDs
commission = self.env['sale.commission'].browse(42)
commissions = self.env['sale.commission'].browse([42, 43, 44])
# SEARCH — returns recordset matching domain
pending = self.env['sale.commission'].search([
('state', '=', 'confirmed'),
('date', '>=', '2026-01-01'),
('salesperson_id.department_id.name', '=', 'Sales'),
], order='date desc', limit=50)
# search_count — just the count
count = self.env['sale.commission'].search_count([('state', '=', 'draft')])
# search_read — search + read in one query (optimal for API calls)
data = self.env['sale.commission'].search_read(
[('state', '=', 'confirmed')],
fields=['name', 'amount', 'salesperson_id'],
limit=20, offset=0, order='amount desc'
)
# UPDATE
commission.write({'state': 'paid', 'notes': 'Paid on March 2026'})
# Batch update (all records get same values)
pending.write({'state': 'confirmed'})
# DELETE
old_drafts.unlink()
# SPECIAL: (0, 0, vals) create, (1, id, vals) update, (2, id, 0) delete
# (3, id, 0) unlink M2M, (4, id, 0) link M2M, (5, 0, 0) clear M2M
# (6, 0, [ids]) replace M2M
ڈومین اظہار
ڈومینز اوڈو کی استفسار کی زبان ہیں۔ ان میں مہارت حاصل کریں:
# Basic operators: =, !=, >, >=, <, <=, like, ilike, in, not in
# Special operators: =like (SQL LIKE), =ilike (case-insensitive LIKE)
# child_of, parent_of — for hierarchical models
domain = [
'|', # OR (next two leaves)
('state', '=', 'confirmed'),
'&', # AND (next two leaves)
('state', '=', 'draft'),
('amount', '>', 1000),
('salesperson_id', '!=', False), # Implicit AND with above
('date', '>=', '2026-01-01'),
('tag_ids.name', 'in', ['priority', 'vip']), # Dot notation for relations
]
# Negation
domain = [('state', 'not in', ['cancelled', 'paid'])]
# NULL checks
domain = [('notes', '!=', False)] # Has notes
domain = [('notes', '=', False)] # No notes
4. وراثت کے نمونے۔
Odoo وراثت کے تین طریقہ کار فراہم کرتا ہے جو آپ کو کسی بھی ماڈیول کے ماخذ کوڈ میں ترمیم کیے بغیر توسیع دینے دیتا ہے۔ کلاس وراثت موجودہ ماڈلز میں فیلڈز اور طریقے شامل کرتی ہے۔ پروٹوٹائپ وراثت علیحدہ ڈیٹا بیس ٹیبل کے ساتھ موجودہ ماڈلز کی بنیاد پر نئے ماڈل بناتی ہے۔ ڈیلیگیشن وراثت ماڈلز کو کمپوزیشن کے ذریعے جوڑتی ہے، دونوں ٹیبلز میں خود بخود ریکارڈ بناتی ہے۔
طبقاتی وراثت (سب سے عام)
class SaleOrderInherit(models.Model):
_inherit = 'sale.order' # Extend existing model
# Add new fields
commission_plan_id = fields.Many2one('commission.plan', string='Commission Plan')
commission_amount = fields.Monetary(compute='_compute_commission')
@api.depends('amount_total', 'commission_plan_id.rate')
def _compute_commission(self):
for order in self:
rate = order.commission_plan_id.rate or 0
order.commission_amount = order.amount_total * rate / 100
# Override existing method
def action_confirm(self):
res = super().action_confirm() # Always call super()
for order in self:
if order.commission_plan_id:
order._create_commission_record()
return res
def _create_commission_record(self):
self.ensure_one()
self.env['sale.commission'].create({
'order_id': self.id,
'salesperson_id': self.user_id.id,
'amount': self.commission_amount,
})
پروٹو ٹائپ وراثت
class ProjectCommission(models.Model):
_name = 'project.commission'
_inherit = 'sale.commission' # Copy structure to new model
_description = 'Project-based Commission'
# Has all fields from sale.commission PLUS its own table
project_id = fields.Many2one('project.project', required=True)
milestone_id = fields.Many2one('project.milestone')
وفد کی وراثت
class CommissionEmployee(models.Model):
_name = 'commission.employee'
_inherits = {'hr.employee': 'employee_id'} # Delegation
_description = 'Commission-eligible Employee'
employee_id = fields.Many2one('hr.employee', required=True, ondelete='cascade')
commission_plan_id = fields.Many2one('commission.plan')
lifetime_earnings = fields.Monetary()
# Can access employee fields directly: record.name, record.department_id
5. کاروباری منطق کے پیٹرنز
پابندیاں
from odoo.exceptions import ValidationError
class CommissionPlan(models.Model):
_name = 'commission.plan'
rate = fields.Float(required=True)
name = fields.Char(required=True)
# Python constraint
@api.constrains('rate')
def _check_rate(self):
for plan in self:
if not 0 <= plan.rate <= 100:
raise ValidationError(
f"Commission rate must be between 0 and 100. Got: {plan.rate}"
)
# SQL constraint (faster, database-level)
_sql_constraints = [
('name_unique', 'UNIQUE(name, company_id)',
'Commission plan name must be unique per company.'),
('rate_positive', 'CHECK(rate >= 0)',
'Commission rate must be positive.'),
]
تبدیلی کے طریقے
@api.onchange('commission_plan_id')
def _onchange_plan(self):
"""Triggered in UI when field changes. Sets defaults and warnings."""
if self.commission_plan_id:
self.percentage = self.commission_plan_id.rate
if self.commission_plan_id.rate > 30:
return {
'warning': {
'title': 'High Commission Rate',
'message': 'This plan has a rate above 30%. Please confirm with management.',
}
}
اسٹیٹ مشین کے ساتھ ورک فلو
class SaleCommission(models.Model):
_name = 'sale.commission'
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('approved', 'Approved'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
], default='draft', tracking=True)
def action_confirm(self):
for rec in self.filtered(lambda r: r.state == 'draft'):
rec.state = 'confirmed'
rec.message_post(body="Commission confirmed.")
def action_approve(self):
self.filtered(lambda r: r.state == 'confirmed').write({
'state': 'approved',
'approved_by': self.env.user.id,
'approved_date': fields.Datetime.now(),
})
def action_pay(self):
for rec in self.filtered(lambda r: r.state == 'approved'):
rec._process_payment()
rec.state = 'paid'
def action_cancel(self):
allowed = self.filtered(lambda r: r.state in ('draft', 'confirmed'))
allowed.write({'state': 'cancelled'})
6. کنٹرولرز اور HTTP اینڈ پوائنٹس
from odoo import http
from odoo.http import request, Response
import json
class CommissionController(http.Controller):
# JSON-RPC endpoint (used by Odoo's internal JS framework)
@http.route('/commission/calculate', type='json', auth='user', methods=['POST'])
def calculate_commission(self, order_id, **kwargs):
order = request.env['sale.order'].browse(order_id)
if not order.exists():
return {'error': 'Order not found'}
commission = order.commission_amount
return {'order_id': order_id, 'commission': commission}
# HTTP endpoint (REST-style, for external integrations)
@http.route('/api/v1/commissions', type='http', auth='api_key',
methods=['GET'], csrf=False)
def list_commissions(self, **kwargs):
domain = [('state', '!=', 'cancelled')]
if kwargs.get('salesperson_id'):
domain.append(('salesperson_id', '=', int(kwargs['salesperson_id'])))
commissions = request.env['sale.commission'].search_read(
domain,
fields=['name', 'amount', 'state', 'date'],
limit=int(kwargs.get('limit', 50)),
offset=int(kwargs.get('offset', 0)),
)
return Response(
json.dumps({'data': commissions, 'count': len(commissions)}),
content_type='application/json',
status=200,
)
# File download endpoint
@http.route('/commission/report/<int:commission_id>', type='http', auth='user')
def download_report(self, commission_id, **kwargs):
commission = request.env['sale.commission'].browse(commission_id)
pdf = request.env['ir.actions.report']._render_qweb_pdf(
'my_module.commission_report', commission.ids
)
return request.make_response(
pdf[0],
headers=[
('Content-Type', 'application/pdf'),
('Content-Disposition', f'attachment; filename=commission_{commission_id}.pdf'),
]
)
توثیق کے طریقے
| موڈ | تفصیل | کیس استعمال کریں |
|---|---|---|
auth='user' | لاگ ان سیشن کی ضرورت ہے | اندرونی صفحات، صارف کے اعمال |
auth='public' | سیشن اختیاری، گمنام ہوسکتا ہے | ویب سائٹ کے صفحات |
auth='none' | کوئی سیشن، کوئی env | صحت کی جانچ پڑتال، جامد اختتامی پوائنٹس |
auth='api_key' | ہیڈر میں API کلید | بیرونی REST انضمام |
7. سیکیورٹی: ACL اور ریکارڈ کے قواعد
رسائی کنٹرول فہرستیں (ir.model.access.csv)
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_commission_user,commission.user,model_sale_commission,sales_team.group_sale_salesman,1,0,0,0
access_commission_manager,commission.manager,model_sale_commission,sales_team.group_sale_manager,1,1,1,0
access_commission_admin,commission.admin,model_sale_commission,base.group_system,1,1,1,1
ریکارڈ کے قواعد (security.xml)
<record id="commission_own_rule" model="ir.rule">
<field name="name">Salesperson sees own commissions</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="groups" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
<field name="domain_force">[('salesperson_id', '=', user.id)]</field>
</record>
<record id="commission_company_rule" model="ir.rule">
<field name="name">Company-level commission isolation</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="global" eval="True"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
8. ڈیبگنگ تکنیک
اوڈو شیل کا استعمال
# Start interactive shell with full ORM access
python odoo-bin shell -d mydb --addons-path=addons,custom_addons
# In shell:
>>> orders = env['sale.order'].search([('state', '=', 'sale')], limit=5)
>>> for o in orders:
... print(f"{o.name}: {o.amount_total} - {o.partner_id.name}")
>>> env.cr.commit() # Commit changes made in shell
لاگنگ
import logging
_logger = logging.getLogger(__name__)
class SaleCommission(models.Model):
_name = 'sale.commission'
def action_confirm(self):
_logger.info("Confirming commission %s for user %s", self.name, self.env.user.name)
try:
self._validate_commission()
except Exception as e:
_logger.exception("Failed to confirm commission %s: %s", self.name, e)
raise
پرفارمنس پروفائلنگ
# Enable SQL logging in odoo.conf:
# log_level = debug_sql
# Or use the profiler decorator
from odoo.tools.profiler import profile
@profile('/tmp/commission_profile')
def action_bulk_calculate(self):
# This will generate a profile dump you can analyze
for order in self.env['sale.order'].search([]):
order._calculate_commission()
عام کارکردگی کے نقصانات:
- لوپس میں متعلقہ فیلڈز تک رسائی (N+1 سوالات) — اس کے بجائے
read()یاmapped()استعمال کریں - لوپس کے اندر
search()کو کال کرنا — اپنی تلاشوں کو بیچ دیں۔ - کثرت سے فلٹر شدہ کمپیوٹ شدہ فیلڈز پر
store=Trueاستعمال نہیں کرنا sudo()کو بھولنے سے ہر آپریشن پر اضافی رسائی کے حق کی جانچ پڑتال ہوتی ہے۔
9. خودکار جانچ
from odoo.tests.common import TransactionCase, HttpCase, tagged
from odoo.exceptions import ValidationError
@tagged('post_install', '-at_install')
class TestCommission(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env['res.partner'].create({'name': 'Test Customer'})
cls.user = cls.env['res.users'].create({
'name': 'Test Salesperson',
'login': 'test_sales',
'groups_id': [(4, cls.env.ref('sales_team.group_sale_salesman').id)],
})
cls.plan = cls.env['commission.plan'].create({
'name': 'Standard 10%',
'rate': 10.0,
})
def test_commission_calculation(self):
"""Commission amount should be 10% of order total."""
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'user_id': self.user.id,
'commission_plan_id': self.plan.id,
})
# Add order line...
self.assertAlmostEqual(order.commission_amount, 100.0, places=2)
def test_rate_constraint(self):
"""Rates above 100 should raise ValidationError."""
with self.assertRaises(ValidationError):
self.env['commission.plan'].create({
'name': 'Invalid Plan',
'rate': 150.0,
})
def test_state_machine(self):
"""Commission should follow draft -> confirmed -> paid flow."""
commission = self.env['sale.commission'].create({
'salesperson_id': self.user.id,
'amount': 500,
})
self.assertEqual(commission.state, 'draft')
commission.action_confirm()
self.assertEqual(commission.state, 'confirmed')
# HTTP/Tour tests
@tagged('post_install', '-at_install')
class TestCommissionTour(HttpCase):
def test_commission_dashboard(self):
"""Test that the commission dashboard loads correctly."""
self.start_tour('/web#action=commission.action_dashboard',
'commission_dashboard_tour', login='admin')
اس کے ساتھ ٹیسٹ چلائیں:
# Run all tests for a module
python odoo-bin -d testdb -i my_module --test-enable --stop-after-init
# Run specific test class
python odoo-bin -d testdb --test-tags /my_module:TestCommission --stop-after-init
# Run with coverage
coverage run odoo-bin -d testdb -i my_module --test-enable --stop-after-init
coverage report --include="custom_addons/my_module/*"
10. اعلی درجے کے پیٹرنز
طے شدہ کارروائیاں (کرون جابز)
<record id="ir_cron_monthly_commission" model="ir.cron">
<field name="name">Calculate Monthly Commissions</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="state">code</field>
<field name="code">model._cron_calculate_monthly()</field>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="numbercall">-1</field>
</record>
ملٹی کمپنی پیٹرنز
class CommissionPlan(models.Model):
_name = 'commission.plan'
company_id = fields.Many2one('res.company', required=True,
default=lambda self: self.env.company)
# Use sudo() carefully — only when crossing company boundaries is intended
def get_global_plans(self):
return self.sudo().search([('global_plan', '=', True)])
دوبارہ قابل استعمال منطق کے لیے مکس پیٹرن
class CommissionMixin(models.AbstractModel):
_name = 'commission.mixin'
_description = 'Adds commission tracking to any model'
commission_ids = fields.One2many('sale.commission', 'res_id')
commission_total = fields.Monetary(compute='_compute_commission_total', store=True)
@api.depends('commission_ids.amount')
def _compute_commission_total(self):
for record in self:
record.commission_total = sum(record.commission_ids.mapped('amount'))
# Apply to any model via class inheritance
class SaleOrder(models.Model):
_inherit = ['sale.order', 'commission.mixin']
حقیقی دنیا کی مثال: مکمل کمیشن ماڈیول
ایک مکمل، پروڈکشن کے لیے تیار کمیشن ماڈیول کی مثال کے لیے، ECOSIRE کے اوپن سورس Odoo ماڈیولز پر جائیں جہاں ہم اس گائیڈ میں شامل تمام بہترین طریقوں کی پیروی کرتے ہوئے کنیکٹر، یوٹیلیٹیز، اور کاروباری ماڈیول شائع کرتے ہیں۔
ہماری Odoo ڈیولپمنٹ ٹیم نے ان عین نمونوں کا استعمال کرتے ہوئے 60 سے زیادہ Odoo ماڈیولز بنائے ہیں۔ اگر آپ کو اپنی مرضی کے مطابق Odoo Python ڈیولپمنٹ کی ضرورت ہے — ایک فیلڈ کے اضافے سے مکمل ERP نفاذ تک — ہماری ٹیم سے رابطہ کریں مفت مشاورت کے لیے۔
اکثر پوچھے گئے سوالات
Odoo 18 کو Python کے کس ورژن کی ضرورت ہے؟
Odoo 18 کو Python 3.10 یا اس سے زیادہ کی ضرورت ہے، Python 3.12 تجویز کردہ ورژن کے ساتھ۔ Odoo 17 Python 3.10+ کو سپورٹ کرتا ہے۔ اپنے Odoo ورژن کے لیے درکار عین انحصار کے لیے ہمیشہ آفیشل requirements.txt کو چیک کریں۔
میں Odoo ORM سوالات کو کیسے ڈیبگ کروں؟
اپنی odoo.conf فائل میں log_level کو debug_sql پر سیٹ کرکے SQL لاگنگ کو فعال کریں، یا کمانڈ لائن پر --log-sql پاس کریں۔ آپ کوڈ میں سوالات کو ٹریس کرنے کے لیے self.env.cr.sql_log بھی استعمال کر سکتے ہیں۔ کارکردگی کی پروفائلنگ کے لیے، odoo.tools.profiler سے بلٹ ان @profile ڈیکوریٹر استعمال کریں۔
کیا مجھے ماڈلز کی توسیع کے لیے _inherit یا _inherits استعمال کرنا چاہیے؟
95% وقت میں _inherit (کلاس وراثت) کا استعمال کریں — یہ موجودہ ماڈل کے ٹیبل میں فیلڈز اور طریقے شامل کرتا ہے۔ _inherits (delegation) کا استعمال صرف اس صورت میں کریں جب آپ کو ایک علیحدہ ماڈل کی ضرورت ہو جو شفاف طریقے سے دوسرے ماڈل کے فیلڈز تک رسائی حاصل کرے، جیسے کہ ایک خصوصی ملازم کی قسم بنانا جو تمام hr.employee فیلڈز کو وراثت میں لے۔
میں ملٹی کمپنی ڈیٹا آئسولیشن کو کیسے ہینڈل کروں؟
self.env.company کو واپس کرنے والے ڈیفالٹ لیمبڈا کے ساتھ اپنے ماڈل میں ایک company_id Many2one فیلڈ شامل کریں۔ پھر ڈومین [('company_id', 'in', company_ids)] کے ساتھ ایک عالمی ir.rule بنائیں۔ یہ یقینی بناتا ہے کہ صارفین صرف ان کمپنیوں کے ریکارڈ ہی دیکھتے ہیں جن تک انہیں رسائی حاصل ہے، جو معیاری Odoo ملٹی کمپنی پیٹرن ہے۔
@api.depends اور @api.onchange میں کیا فرق ہے؟
@api.depends متحرک ہوتا ہے جب مخصوص فیلڈز ڈیٹا بیس میں تبدیل ہوتے ہیں اور اسے ذخیرہ شدہ کمپیوٹیڈ فیلڈز کے لیے استعمال کیا جاتا ہے — یہ UI اور پروگرامی تبدیلیوں دونوں کے لیے کام کرتا ہے۔ @api.onchange صرف UI میں متحرک ہوتا ہے جب صارف کسی فیلڈ میں ترمیم کرتا ہے اور اسے ڈیفالٹس سیٹ کرنے یا وارننگ دکھانے کے لیے استعمال کیا جاتا ہے۔ ڈیٹا کی سالمیت کے لیے @api.depends اور UX میں بہتری کے لیے @api.onchange استعمال کریں۔
میں اپنے Odoo ماڈیول کی مؤثر طریقے سے جانچ کیسے کروں؟
یونٹ ٹیسٹس کے لیے TransactionCase استعمال کریں جو کاروباری منطق کی جانچ کرتے ہیں — ہر ٹیسٹ ایک ٹرانزیکشن میں چلتا ہے جسے واپس کر دیا جاتا ہے۔ کنٹرولرز اور ویب ٹورز کی جانچ کے لیے HttpCase استعمال کریں۔ ماڈیول کی تنصیب کے بعد چلانے کے لیے @tagged('post_install', '-at_install') کے ساتھ ٹیگ ٹیسٹ۔ کاروباری منطق کے طریقوں پر کم از کم 80% کوڈ کوریج کا مقصد۔
اگلے اقدامات
پروڈکشن گریڈ Odoo ماڈیولز بنانے کے لیے تکنیکی مہارت اور ڈومین کی مہارت دونوں کی ضرورت ہوتی ہے۔ سادہ کلاس وراثت کے ساتھ شروع کریں، پھر مکمل حسب ضرورت ماڈیولز میں ترقی کریں۔ مزید پڑھنے کے لیے:
- اوڈو کسٹم ماڈیول ڈویلپمنٹ گائیڈ — ماڈیول ڈویلپمنٹ کا اعلیٰ سطحی جائزہ
- Odoo REST API ٹیوٹوریل — Odoo کے ساتھ بیرونی انضمام
- بہترین ERP سافٹ ویئر 2026 — مارکیٹ میں اوڈو کی پوزیشن کو سمجھنا
ماہر Odoo ڈویلپرز کی ضرورت ہے؟ ECOSIRE کی Odoo ڈیولپمنٹ ٹیم نے 40 ممالک میں کاروبار کے لیے 200+ حسب ضرورت ماڈیولز فراہم کیے ہیں۔ کمیشن انجنوں سے لے کر مارکیٹ پلیس کے مکمل انضمام تک، ہماری ٹیم اس گائیڈ میں ہر پیٹرن کی پیروی کرتے ہوئے پروڈکشن کے لیے تیار کوڈ لکھتی ہے۔ مفت مشاورت حاصل کریں۔
تحریر
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
متعلقہ مضامین
blog.posts.ai-powered-customer-segmentation-guide.title
blog.posts.ai-powered-customer-segmentation-guide.description
blog.posts.ai-supply-chain-optimization-2026.title
blog.posts.ai-supply-chain-optimization-2026.description
blog.posts.b2b-ecommerce-strategy-wholesale-guide.title
blog.posts.b2b-ecommerce-strategy-wholesale-guide.description