انتقل إلى المحتوى الرئيسي

كيفية إنشاء وحدات Odoo مخصصة: دليل المطور لـ OWL وORM والميراث

دليل المطور لبناء وحدات Odoo المخصصة. يغطي بنية الوحدة، وإطار عمل OWL، وراثة ORM، وطرق العرض، وقواعد الأمان، والاختبار، وإرشادات OCA.

E
ECOSIRE Research and Development Team
|19 فبراير 20267 دقائق قراءة1.3k كلمات|

عندما لا تغطي وحدات Odoo الرسمية البالغ عددها 82 وحدة وأكثر من 40.000 وحدة مجتمعية متطلبات عملك المحددة، فإن تطوير الوحدات المخصصة يسد الفجوة.

عندما لا تغطي وحدات Odoo الرسمية البالغ عددها 82 وحدة وأكثر من 40.000 وحدة مجتمعية متطلبات عملك المحددة، فإن تطوير الوحدات المخصصة يسد الفجوة. يغطي هذا الدليل أساسيات بناء وحدات Odoo في عام 2026، بما في ذلك هيكل الوحدة وإطار عمل الواجهة الأمامية OWL وأنماط ORM وآليات الوراثة وأفضل الممارسات المتوافقة مع إرشادات OCA (جمعية مجتمع Odoo).

ما هي وحدة Odoo المخصصة؟

وحدة Odoo المخصصة عبارة عن حزمة قائمة بذاتها من منطق الواجهة الخلفية لـ Python، وتعريفات عرض XML، ومكونات الواجهة الأمامية لـ JavaScript، وقواعد الأمان التي تعمل على توسيع وظائف Odoo. يمكن للوحدات إضافة ميزات جديدة تمامًا، أو تعديل السلوك الحالي، أو دمج Odoo مع الأنظمة الخارجية. كل قطعة من Odoo بحد ذاتها عبارة عن وحدة نمطية، مما يجعل البنية قابلة للتوسيع بطبيعتها.

هيكل دليل الوحدة

تتبع الوحدة المنظمة جيدًا هذا الهيكل القياسي:

my_custom_module/
├── __init__.py              # Python package init
├── __manifest__.py          # Module metadata and dependencies
├── models/
│   ├── __init__.py
│   └── my_model.py          # Business logic and data models
├── views/
│   ├── my_model_views.xml   # Form, tree, and kanban views
│   └── menu.xml             # Menu items and actions
├── security/
│   ├── ir.model.access.csv  # Access control list
│   └── security.xml         # Record rules and groups
├── data/
│   └── data.xml             # Default data records
├── static/
│   └── src/
│       ├── js/              # OWL components
│       ├── css/             # Stylesheets
│       └── xml/             # QWeb templates
├── wizard/                  # Transient models for wizards
├── reports/                 # QWeb report templates
└── tests/                   # Unit tests

ملف البيان

يحدد الملف __manifest__.py هوية الوحدة الخاصة بك:

{
    'name': 'My Custom Module',
    'version': '18.0.1.0.0',
    'category': 'Custom',
    'summary': 'Short description of what the module does',
    'description': """
        Long description with details about
        features and configuration.
    """,
    'author': 'ECOSIRE',
    'website': 'https://ecosire.com',
    'license': 'LGPL-3',
    'depends': ['base', 'sale', 'stock'],
    'data': [
        'security/ir.model.access.csv',
        'views/my_model_views.xml',
        'views/menu.xml',
        'data/data.xml',
    ],
    'assets': {
        'web.assets_backend': [
            'my_custom_module/static/src/js/**/*',
            'my_custom_module/static/src/css/**/*',
            'my_custom_module/static/src/xml/**/*',
        ],
    },
    'installable': True,
    'application': False,
    'auto_install': False,
}

اتفاقية الإصدار: {odoo_version}.{major}.{minor}.{patch} (على سبيل المثال، 18.0.1.0.0).

العمل مع ORM

يعد رسم الخرائط الارتباطية للكائنات (ORM) من Odoo هو أساس كل عمليات تطوير الواجهة الخلفية. يتم تعيين النماذج إلى جداول قاعدة البيانات، ويوفر ORM عمليات CRUD والحقول المحسوبة والقيود وإدارة سير العمل.

تحديد النموذج

from odoo import models, fields, api

class ProjectTask(models.Model):
    _name = 'my_module.task'
    _description = 'Project Task'
    _order = 'priority desc, create_date desc'

    name = fields.Char(string='Task Name', required=True)
    description = fields.Html(string='Description')
    state = fields.Selection([
        ('draft', 'Draft'),
        ('in_progress', 'In Progress'),
        ('done', 'Done'),
        ('cancelled', 'Cancelled'),
    ], default='draft', tracking=True)
    assigned_to = fields.Many2one('res.users', string='Assigned To')
    deadline = fields.Date(string='Deadline')
    priority = fields.Selection([
        ('0', 'Normal'),
        ('1', 'Important'),
        ('2', 'Urgent'),
    ], default='0')
    tag_ids = fields.Many2many('my_module.tag', string='Tags')
    progress = fields.Float(compute='_compute_progress', store=True)

مرجع أنواع الحقول

نوع الحقلنوع بايثونحالة الاستخدام
شارشارعنص قصير (الاسم، المرجع)
نصشارعنص عادي طويل
أتش تي أم ألشارعمحتوى نصي منسق
عدد صحيحكثافة العملياتالأعداد الصحيحة
تعويمتعويمالأعداد العشرية
منطقيةمنطقيعلامات الصواب/الخطأ
التاريخالتاريختاريخ بلا زمن
التاريخ والوقتالتاريخ والوقتالتاريخ مع الوقت
اختيارشارعاختيارات القائمة المنسدلة
Many2oneكثافة العملياترابط لسجل واحد
One2manyقائمةعكس Many2one
Many2manyقائمةالارتباط بسجلات متعددة
ثنائيبايتمرفقات الملفات

الحقول والقيود المحسوبة

@api.depends('subtask_ids.state')
def _compute_progress(self):
    for task in self:
        total = len(task.subtask_ids)
        done = len(task.subtask_ids.filtered(
            lambda t: t.state == 'done'
        ))
        task.progress = (done / total * 100) if total else 0

@api.constrains('deadline')
def _check_deadline(self):
    for task in self:
        if task.deadline and task.deadline < fields.Date.today():
            raise ValidationError(
                "Deadline cannot be in the past."
            )

آليات الوراثة

يوفر Odoo ثلاثة أنواع من الميراث، يخدم كل منها غرضًا مختلفًا:

1. وراثة الطبقة (الامتداد)

قم بتوسيع نموذج موجود عن طريق إضافة حقول أو طرق متجاوزة. هذا هو النمط الأكثر شيوعا.

class SaleOrderExtend(models.Model):
    _inherit = 'sale.order'

    custom_reference = fields.Char(string='Custom Ref')
    approved_by = fields.Many2one('res.users')

    def action_confirm(self):
        # Add custom logic before standard confirmation
        for order in self:
            if order.amount_total > 10000 and not order.approved_by:
                raise UserError("Orders over $10,000 require approval.")
        return super().action_confirm()

2. وراثة النموذج الأولي

قم بإنشاء نموذج جديد يقوم بنسخ كافة الحقول والأساليب من نموذج موجود.

class CustomPartner(models.Model):
    _name = 'my_module.partner'
    _inherit = 'res.partner'   # Copies structure
    _description = 'Custom Partner'

3. وراثة التفويض

قم بإنشاء نموذج جديد يقوم بتفويض نموذج موجود عبر رابط Many2one. تظهر حقول النموذج الأصلي في النموذج الفرعي بشفافية.

class LibraryMember(models.Model):
    _name = 'library.member'
    _inherits = {'res.partner': 'partner_id'}

    partner_id = fields.Many2one('res.partner', required=True,
                                  ondelete='cascade')
    membership_date = fields.Date()
    member_number = fields.Char()

إطار عمل OWL (الواجهة الأمامية)

يستخدم Odoo 18 OWL (مكتبة ويب Odoo) كإطار عمل للواجهة الأمامية. OWL هو إطار عمل قائم على المكونات يشبه React أو Vue ولكنه مصمم خصيصًا لتلبية احتياجات Odoo.

مكون OWL الأساسي

/** @odoo-module */

import { Component, useState } from "@odoo/owl";
import { registry } from "@web/core/registry";

class TaskDashboard extends Component {
    static template = "my_module.TaskDashboard";

    setup() {
        this.state = useState({
            tasks: [],
            filter: 'all',
        });
        this.loadTasks();
    }

    async loadTasks() {
        this.state.tasks = await this.env.services.orm.searchRead(
            "my_module.task",
            [["state", "!=", "cancelled"]],
            ["name", "state", "assigned_to", "deadline"]
        );
    }

    get filteredTasks() {
        if (this.state.filter === 'all') return this.state.tasks;
        return this.state.tasks.filter(
            t => t.state === this.state.filter
        );
    }
}

قالب QWeb (XML)

<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
    <t t-name="my_module.TaskDashboard">
        <div class="o_task_dashboard">
            <div class="task-filters">
                <button t-on-click="() => state.filter = 'all'">All</button>
                <button t-on-click="() => state.filter = 'in_progress'">
                    In Progress
                </button>
            </div>
            <div class="task-list">
                <t t-foreach="filteredTasks" t-as="task" t-key="task.id">
                    <div class="task-card">
                        <span t-esc="task.name"/>
                    </div>
                </t>
            </div>
        </div>
    </t>
</templates>

تكوين الأمان

يحتاج كل نموذج إلى قواعد وصول واضحة. وبدونها، لا يمكن لأي مستخدم الوصول إلى بيانات النموذج.

قائمة التحكم في الوصول (ir.model.access.csv)

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_task_user,task.user,model_my_module_task,base.group_user,1,1,1,0
access_task_manager,task.manager,model_my_module_task,my_module.group_manager,1,1,1,1

قواعد السجل (security.xml)

<record id="task_own_rule" model="ir.rule">
    <field name="name">Own Tasks Only</field>
    <field name="model_id" ref="model_my_module_task"/>
    <field name="domain_force">
        [('assigned_to', '=', user.id)]
    </field>
    <field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>

اختبار الوحدة الخاصة بك

يدعم Odoo اختبارات وحدة Python باستخدام إطار عمل unittest مع فئات اختبار خاصة بـ Odoo:

from odoo.tests.common import TransactionCase

class TestTask(TransactionCase):
    def setUp(self):
        super().setUp()
        self.task = self.env['my_module.task'].create({
            'name': 'Test Task',
            'state': 'draft',
        })

    def test_task_creation(self):
        self.assertEqual(self.task.state, 'draft')
        self.assertEqual(self.task.progress, 0)

    def test_deadline_constraint(self):
        with self.assertRaises(ValidationError):
            self.task.write({
                'deadline': fields.Date.subtract(
                    fields.Date.today(), days=1
                ),
            })

إجراء الاختبارات باستخدام: odoo-bin -d test_db --test-enable -i my_custom_module --stop-after-init

الأسئلة المتداولة

س: كم من الوقت يستغرق إنشاء وحدة Odoo مخصصة؟ تستغرق الوحدات البسيطة (الحقول الجديدة وطرق العرض الأساسية) من يوم إلى ثلاثة أيام. تستغرق الوحدات المتوسطة (النماذج الجديدة وسير العمل والتقارير) من 1 إلى 3 أسابيع. تستغرق الوحدات المعقدة (أنظمة متعددة النماذج، وعمليات التكامل الخارجية، ومكونات OWL المخصصة) من 4 إلى 12 أسبوعًا. بالنسبة للشركات التي تحتاج إلى وحدات مخصصة ولكنها تفتقر إلى مطوري Odoo الداخليين، قم بتعيين مطور Odoo ذو خبرة من فريقنا.

س: هل يجب علي تعديل كود Odoo الأساسي أو إنشاء وحدة منفصلة؟ قم دائمًا بإنشاء وحدة منفصلة. يؤدي تعديل التعليمات البرمجية الأساسية إلى قطع إمكانية الترقية وإنشاء تعارضات دمج أثناء تحديثات الإصدار. استخدم الوراثة لتوسيع النماذج وطرق العرض الموجودة من الوحدة المخصصة الخاصة بك.

س: ما هي إرشادات OCA؟ تنشر جمعية مجتمع Odoo (OCA) معايير الترميز لجودة الوحدة، بما في ذلك اصطلاحات التسمية، ومتطلبات التوثيق، وتوقعات تغطية الاختبار، وقواعد نمط التعليمات البرمجية. يضمن اتباع إرشادات OCA أن الوحدة الخاصة بك قابلة للصيانة ومتوافقة مع النظام البيئي للمجتمع الأوسع.

الحصول على مساعدة احترافية

يتطلب إنشاء وحدات Odoo المخصصة خبرة في Python، وJavaScript، وPostgreSQL، واتفاقيات إطار عمل Odoo. سواء كنت بحاجة إلى امتداد حقل بسيط أو نظام معقد متعدد الوحدات، فإن خدمات تخصيص Odoo من ECOSIRE تقدم وحدات جاهزة للإنتاج تم تصميمها وفقًا لمعايير OCA.

اتصل بنا لمناقشة متطلبات الوحدة المخصصة الخاصة بك والحصول على تقدير التطوير.

مشاركة:
E

بقلم

ECOSIRE Research and Development Team

الكتابة التقنية

يغطي فريق الكتابة التقنية في ECOSIRE موضوعات Odoo ERP والتجارة الإلكترونية عبر Shopify ووكلاء الذكاء الاصطناعي وتحليلات Power BI والأتمتة عبر GoHighLevel وأفضل ممارسات برمجيات المؤسسات. تساعد أدلتنا الشركات على اتخاذ قرارات تقنية مدروسة.

ECOSIRE

قم بتحويل أعمالك باستخدام Odoo ERP

تنفيذ وتخصيص ودعم خبير Odoo لتبسيط عملياتك.