ملخص المقال: دليل معماري لعزل الشركات في أودو: كيف يعمل حقل الشركة وقواعد السجلات، ولماذا تختلف الحقول المستقلة عن المشتركة، وكيف تُدار العملات وأسعار الصرف، والتعامل بين الشركتين، والمستودعات وقواعد التوريد عند تشغيل مجموعة في نسخة واحدة.
طريقة القراءة: ابدأ بفهم المشكلة، ثم راجع القرار أو الخطوات العملية في الأقسام التالية، واستفد من المخطط لتصور الأجزاء المرتبطة ببعضها.
ماذا ستخرج به من هذا المقال؟
السؤال الأول عند تشغيل مجموعة على أودو ليس «كيف أضيف شركة جديدة»، بل «ما الذي يجب أن ينفصل وما الذي يجب أن يبقى واحدًا». سنمشي في الطبقات بالترتيب: نموذج العزل نفسه (حقل الشركة، الشركات المسموح بها، قواعد السجلات، فحص الاتساق)، ثم الحقول المستقلة والمشتركة وأين تُخزَّن قيمتها فعليًا، ثم العملة وأسعار الصرف حين توجد أكثر من عملة، ثم المعاملة بين شريكتين وما لا يفعله أودو تلقائيًا، وأخيرًا المستودعات والمواقع وقواعد التوريد وقنوات البيع. في كل طبقة قرار مقابله مقايضته، وتنتهي بقائمة تحقق تكشف التداخل قبل أن يتحول إلى رصيد لا يُفسّر.
نموذج العزل في أودو: من حقل الشركة إلى قواعد السجلات
العزل في أودو ليس فلترًا في الواجهة، بل ثلاث آليات تعمل معًا: حقل company_id في الجداول التي تدعم تعدد الشركات؛ وres.users.company_ids وهو قائمة الشركات المسموح بها للمستخدم ومنها تُشتق الفلاتر؛ وجدول ir.rule الذي يقيّد search وread على مستوى ORM، فلا يتجاوزه أي كود إلا بـ sudo. ويكملها السياق allowed_company_ids الذي تبدّله الواجهة أو الكود في كل طلب.
النتيجة الأولى: كل تقرير وكل مهمة مؤتمتة وكل سكربت تكامل يجب أن يمرّر هذا السياق صراحةً، فمهمة خلفية تُنفَّذ في سياق الشركة الافتراضية قد تعدّل بيانات شركة أخرى دون خطأ ظاهر. والنتيجة الثانية أن التصميم الصحيح يبدأ من مستوى السجلات: التقرير الذي يفلتر بحقل الشركة في الشاشة يعمل لكنه أبطأ، ولا تستطيع فتح سجلاته من كود آخر أو تطبيق جوال لأن قواعد السجلات منعت القراءة أصلًا.
فحص اتساق الشركة داخل النماذج
لأن حقل الشركة قد يتغيّر في السجلات المرتبطة (فواتير، أوامر بيع، حركات مخزون)، يوفّر أودو في معظم إصداراته فحصًا على مستوى ORM اسمه _check_company() يقارن شركة كل سجل بشركات الجلسة ويمنع العملية عند التعارض. ويقابله في الواجهة خيار الحقل check_company=True الذي يعرض تحذيرًا ولا يسمح بالحفظ. افهم الفرق: الأول ضابط وصول حقيقي، والثاني حارس لتجربة المستخدم، وأي تكامل يتجاوزهما بـ sudo() يحتاج تحققًا خاصًا به.
ترتيب تشخيص «بيانات شركة ظهرت في شاشة أخرى»
راجع بهذا الترتيب: res.users.company_ids للمستخدم، ثم allowed_company_ids في الجلسة، ثم قواعد ir.rule على الجدول المعني، وأخيرًا check_company في النموذج. أغلب حالات الخلط سببها المستوى الأول أو الثاني لا القاعدة الثالثة.
الحقول المستقلة والحقول المشتركة: أين تُخزَّن القيمة فعليًا
الحقل المعلَّم بـ company_dependent=True لا يُخزَّن في عمود واحد؛ تُحفظ نسخته في جدول ir.property مرتبطة بالشركة والحقل والسجل، وقيمتها نص JSON في الإصدارات الحديثة. تخرج من هذا التصميم ثلاث نتائج مباشرة: قيمة افتراضية على مستوى قاعدة البيانات قد تتسرّب إلى شركة لم تُخصَّص لها قيمة؛ وحقول مثل التكلفة المعيارية ورموز الضرائب ومسارات التوريد يمكن أن تختلف بشكل مشروع؛ وأي استعلام مباشر على جدول المنتجات يتجاوز ir.property يُرجع قيمة فارغة أو خاطئة. أما الحقل غير المعلَّم فيبقى واحدًا لكل السجلات، فعدّله من أي شركة يغيّره للجميع.
- يستحق الاستقلال: التكلفة المعيارية، رموز الضرائب، قواعد التوريد، وسياسات الهامش؛ كل سياسة مختلفة تحتاج سجلًا منفصلًا.
- يجب أن يبقى مشتركًا: الاسم، الرمز الداخلي، الوصف، ووحدة القياس؛ تكرارها عبء صيانة ومصدر اختلاف في التقارير المدمجة.
- طبقة وسيطة: اربط قوائم الأسعار وشرائح الخصم بالشركة عبر
company_idعلى القائمة نفسها، لا بتكرار المنتج. - التتبّع التحليلي ليس عزلًا: الحسابات التحليلية تجيب عن «لماذا هذه التكلفة؟»، والعزل يبقى من وظيفة حقل الشركة.
العملة وأسعار الصرف: متى تدخل العملة الثانية إلى الدفاتر
لكل شركة عملتها في res.company، ولكل دفتر حسابات عملة. حين تبيع شركة عملتها اليورو لعميل في كندا، لا يغيّر أودو عملة الدفتر بل يُبقي عملة الشركة ويضيف عملة أجنبية وسعرًا ومبلغًا محوَّلًا. فتحمل الفاتورة ثلاثة أرقام: المبلغ بعملة العميل، وسعر الصرف بتاريخ الفاتورة، والمبلغ بعملة الشركة الذي تدخل به القيود. المبدأ الذي يحل نصف مشاكل التقارير: افصل بين عملة المعاملة الوظيفية وعملة الدفتر المحاسبية، ولا تدع أحدهما ينتحل الأخرى.
كيف يُختار سعر الصرف
أسعار الصرف صارت في الإصدارات الحديثة صفوفًا مؤرخة في جدول مستقل، لا حقلًا واحدًا على العملة. يُختار السعر بثلاثة شروط: العملة، وتاريخ المستند، وأحدث صف ليس بعد هذا التاريخ. وتكون النتيجة أن تعديل سعر اليوم لا يغيّر فاتورة الأمس لأنها مرتبطة بتاريخها. لكن ليس كل شيء تاريخيًا؛ تقييم المخزون وتسويات المستودع تعتمد سعر اليوم، فقد يختلف تقرير تقييم المخزون عن مجموع فواتير الفترة نفسها إذا تحركت الأسعار في منتصفها.
- التقريب مستقل لكل عملة: فرق التقريب الصغير على السطر يظهر كفرق في الإجمالي، فلا تفتح نشرة حساب بسببه.
- تقرير فرق التحويل: قارن شهريًا بين إجمالي الفواتير بعملة العميل والقيمة الدفترية بعملة الشركة؛ أسرع كاشف لخلط العملات.
- اجعل السعر عمودًا إلزاميًا: بعض أدوات التقارير تعرض رقمًا محوَّلًا دون إظهار السعر المستخدم، فيصبح الرقم غير قابل للتدقيق.
- انتبه للقيم الافتراضية: في setups متعددة العملات تبقى عملة الشركة افتراضيًا لكل ما لم يُحدَّد صراحة؛ راجع الإعدادات في كل شركة قبل فتح جلسة بيع عليها.
تحذير: الفوترة بعملة شركة أخرى
- لا تُدخل فاتورة بعملة شركة أخرى لمجرد أن العميل يدفع بتلك العملة؛ فرق إعادة التوازن يُقيَّد كتقييم أو تسوية ربح وخسارة ويشوّش المقارنة بين الشركتين.
- تأكد أن تاريخ الفاتورة وتاريخ الاستحقاق يختاران السعر نفسه؛ اختلاف يوم واحد يغيّر المبلغ الدفتري ويظهر سطرًا غير متوقع.
- تغيير دقة العملة لاحقًا لا يعيد حساب الفواتير القديمة؛ صحّح البيانات ولا تترك الانحراف داخل التقارير.
التعامل بين شركات المجموعة: ما يفعله أودو وما لا يفعله
الشركة في أودو كيان له partner_id خاص به، أي أنها مسجَّلة كطرف في دليل الشركات. عند البيع من شركة (أ) إلى شركة (ب) يمر المسار بثلاث خطوات: أمر بيع في (أ) موجّه إلى شريك (ب)، ثم فاتورة من (ب)، ثم فاتورة شراء في (أ). أي أن المعاملة تظهر مرتين في دفتارين، ولا يلغيها أودو ولا يطابقها تلقائيًا؛ فالمطابقة والتسوية مسؤوليتك أو وحدة إضافية، لا النواة.
- شركة الفاتورة ليست دائمًا شركة الجلسة: يشتق أودو شركة الحساب من الشركة التي ينتمي إليها الشريك التجاري للعميل، فإذا كان العميل شريكًا لشركة أخرى قد تُنشأ الفاتورة في غير شركة الجلسة.
- الضريبة تعبر الحد: رمز الضريبة المسحوب من أمر البيع قد يكون مرتبطًا بشركة مختلفة، فتخرج فاتورة بلا ضريبة أو بضريبة دولة أخرى.
- الوحدة ومعدل التحويل: إن كان التسليم بوحدة بين الشركتين والطلب بوحدة أخرى، ظهر الفرق على سطر الفاتورة قبل أن يصل إلى الحساب.
- فصل الحساب البنكي: عمليًا يُفضَّل حساب بنكي لكل شركة أو طرف وسيط معروف، حتى يظهر الفرق كدفعة قائمة بدل أن يضيع في رصيد مغلق.
سيناريو: مجموعة فيها كيان محاسبي وكيان تجاري في بلدين
الحل الأخف: كيان واحد بحسابين تحليليين وتقارير على الأبعاد التحليلية. الحل الأنقل: شركتان بدفاتر وفواتير عملتين وأسعار صرف مستقلة. القاعدة: انتقل إلى فكرتين منفصلتين فقط عند وجود التزام قانوني أو ضريبي أو مخزني يفصل الكيانين، وإلا فأنت تدفع تكلفة عزل تشتريها مقابل تعقيد بلا مقابل.
المستودعات والمواقع وقواعد التوريد: إلى أين يذهب كل طلب
المستودع stock.warehouse يملك حقل شركة، والمواقع stock.location ترث الشركة من المستودع أعلى الشجرة ما لم تُحدَّد لها غير ذلك، ويُوصف كل موقع بنوع الاستخدام (مخزّن، داخلي، عميل، مورّد، عبور، إنتاج). ويمكن أن يُشارك موقع واحد أكثر من شركة، وهنا يبدأ الالتباس: مستودع مشترك يعني مخزونًا واحدًا يُصرف لمبيعات شركتين، فتختفي نسبة توفّر تخص كل كيان.
قرارات لكل مستودع
- من يملك المستودع: الشركة صاحبة المستودع تتولى المخزون والتقييم والحركات، والشركة الأخرى طرف عميل لا يملك مخزونًا.
- هل المسار يتبع مستودع الشريك: في قواعد التوريد خيار استخدام مستودع شركة الشريك، وهو أساس التشغيل الداخلي حين تذهب الطلبية إلى المستودع مباشرة بلا مستودع وسيط.
- تسلسل المستندات: اجعل تسلسل الأرقام مرتبطًا بالشركة وإلا تداخلت أرقام الشركتين في سلسلة واحدة واضاع التتبع.
- القنوات تفرض شركة: نقطة البيع مسجَّلة على شركة واحدة، والمتجر الإلكتروني في التكوين القياسي مرتبط بشركة أيضًا؛ عدّ القنوات بعدد الشركات ولا تفترض أن القناة تتبع الجلسة.
- قائمة الأسعار تختار نفسها: اربطها بالشركة حتى لا يعرض سعر مجموعة على مبيعات مجموعة أخرى.
مصفوفة القرار وأدوات الفحص قبل الانطلاق
قبل إضافة الشركة الثانية، أجب عن سؤال واحد لكل صف: إن نفذتها ككيان مستقل، فما الذي يتغير؟ الجدول يلخّص التكلفة والمقايضة.
| البند | السؤال الحاسم | فكرتان منفصلتان | فكرة واحدة |
|---|---|---|---|
| المخزون | هل لكل كيان مخزون وحساب تقييم مستقل؟ | مستودع لكل شركة | مستودع واحد وتتبّع تحليلي |
| الفوترة والضريبة | هل الكيانان في نظامين ضريبيين؟ | دفاتر ورموز ضرائب منفصلة | دفتر واحد بأبعاد تحليلية |
| العملات | هل تختلف عملة كل كيان؟ | عملة وأسعار صرف مستقلة | عملة واحدة وتقارير متعددة |
| الصلاحيات | من يرى أي دفاتر؟ | مجموعات مستخدمين لكل شركة | أدوار وظيفية واحدة |
بعد التطبيق لا تنتظر الشكوى: شغّل تقرير تداخل دوريًا يقارن الفواتير المتبادلة ويتحقق أن لكل سجل مبيعات فاتورة مقابلة في الشركة الأخرى.
-- تداخل الفواتير بين شركتين: مثال تشخيصي
SELECT
a.company_id AS issuing_company,
b.company_id AS partner_company,
a.amount_total AS invoice_amount,
a.amount_residual AS still_open,
a.date AS posted_on
FROM account_move a
JOIN res_partner pa ON pa.id = a.partner_id
JOIN res_partner pb ON pb.id = pa.commercial_partner_id
JOIN res_company b ON b.partner_id = pb.id
WHERE a.state = 'posted'
AND b.id <> a.company_id
AND a.company_id IN (1, 2) -- مثال: مجموعة من شركتين
ORDER BY a.date;
# فحص اتساق الشركة قبل أي كتابة عبر التكامل
for partner in self.env['res.partner'].browse(partner_ids):
target = partner.commercial_partner_id.company_id
if target and target.id not in self.env.companies.ids:
raise ValidationError(
_('ستُسجَّل العملية في شركة خارج نطاق الجلسة: %s') % target.name
)
move = self.env['account.move'].with_company(target or self.env.company).create({
'partner_id': partner.id,
'invoice_date': date.today(),
'invoice_line_ids': [(0, 0, {'name': label, 'quantity': 1.0, 'price_unit': price})],
})
لاحظ أن المقتطف الثاني يتجاوز حارس الواجهة ويكتب على مستوى ORM، فهنا يصبح التحقق من شركة الفاتورة مسؤوليتك أنت لا النموذج. هذه هي النقطة التي تفشل عندها معظم التكاملات.
أخطاء شائعة
أخطاء تكلف وقتًا حقيقيًا
- إنشاء مهمة مجدولة أو تقرير دون تمرير
allowed_company_ids، فتُنفَّذ في سياق الشركة الافتراضية. - افتراض أن أودو يطابق فاتورة شركة مع فاتورة الأخرى تلقائيًا؛ لا تسوية تلقائية في النواة.
- ترك فاتورة بعملة شركة أخرى أو رمز ضريبة مرتبط بشركة مختلفة، فتخرج فاتورة بلا ضريبة أو محوّلة بلا سبب.
- ترك تسلسل أرقام المستودع مشتركًا، فلا تعرف أي شركة ينتمي إليه الرقم.
- الاكتفاء بقواعد السجلات دون مجموعات صلاحيات وظيفية؛ الأدوار تجعل التنقل أسرع وأقل خطورة.
قائمة تحقق
- حدّد الحقول المستقلة التي تفرّقها فعلًا، وراجع قيمة كل حقل في
ir.propertyقبل فتح الجلسة. - اجعل كل سكربت ومهمة مؤتمتة يمرّر
allowed_company_idsصريحًا، واختبره على مستخدم بصلاحية شركة واحدة. - اضبط عملة كل شركة ودقتها، وأدخل أسعار صرف مؤرخة تغطي الفترة التي ستدخل فيها فواتير لا اليوم فقط.
- راجع شركة كل فاتورة: مشتقة من الجلسة أم من شركة العميل، وهل ذلك مقصود في كل حالة.
- اختبر مسار التوريد لكل قناة: أي مستودع يخدمها، وهل تسلسل الأرقام معزول.
- شغّل تقرير تداخل الفواتير شهريًا واحتفظ بنتيجته، فالفرق المتكرر مؤشر خطأ إعداد لا فرق صرف.
الأسئلة الشائعة
هل يمكن إدارة عدة شركات في نسخة واحدة دون تداخل؟
نعم، بشرط ضبط الصلاحيات على مستوى السجلات وعدم تجاوزها بـ sudo في التكاملات. الفصل على مستوى الجدول يضمن أن كل مستخدم يرى ما يخصه، لكن أي مهمة خلفية تنسى سياقها ستكتب عبر الفصل؛ لذلك الفصل في أودو ليس إعدادًا مرة واحدة بل اتفاق يجب أن يبقى في مراجعة الكود.
ما الفرق بين عزل البيانات والحساب التحليلي؟
الحساب التحليلي يقسّم بيانات كيان واحد ولا يمنع ظهور سجل من كيان في تقرير كيان آخر، أما حقل الشركة فهو ما يفرض الفصل عبر قواعد السجلات. استعمل التحليلي للربحية والتكلفة، والشركة للعزل القانوني والرقابي.
أين أبدأ عند وجود قوائم أسعار ومواقع مشتركة؟
أنشئ سجل الشركة، ثم أضف قيم الحقول المستقلة، ثم امنحها مستودعًا وتسلسل أرقام خاصًا، واربط قوائم أسعارها. وإن بقي الموقع مشتركًا فقد اخترت مخزونًا مشتركًا عن قصد: وثّقه بجملة واحدة، لأنه يغيّر كل تقارير التوفّر والالتزام.
الخلاصة
ما يجب أن يبقى معك
- العزل ثلاث آليات: حقل الشركة، والشركات المسموح بها، وقواعد السجلات؛ والاختفاء في الكود الخلفي غالبًا سببُه السياق لا القاعدة.
- الحقل المستقل يُخزَّن في جدول الخصائص لا في جدول السجل، فأي قراءة مباشرة على الجدول تُضلّل.
- افصل عملة المعاملة عن عملة الدفتر، واجعل سعر الصرف عمودًا ظاهرًا في كل تقرير.
- المعاملة بين الشركتين تُسجَّل مرتين ولا تُطابق تلقائيًا؛ السداد والتسوية مسؤوليتك.
- المستودع المشترك قرار مالي لا تفصيل تقني، لأنه يغيّر التقارير ويخفي التوفّر.
الخطوة التالية
بعد ضبط الفصل والعملات تظهر الطبقة الأصعب: الأتمتة. في الجزء التالي من هذه السلسلة نصمم التدفقات التي تعبر حدود الشركات: طلب بيع داخلي يولّد فاتورة في الشركتين، واعتماد متعدد المستويات، وخطة تحصيل موحّدة، مع بقاء كل عملية داخل شركة واحدة قابلة للتتبع.
كيف تستخدم هذا المقال في عمل حقيقي؟
خذ المقال كقائمة تشخيص لا كوصفة جاهزة. ابدأ بجرد ما لديك: كم شركة، وما الحقول التي تختلف فعلًا اليوم، وهل يوجد مستودع أو قائمة أسعار مشتركة بلا قرار موثق. ثم اختر بنية ووثّقها بجملة واحدة: لماذا فصلت هنا ولماذا لم تفصل هناك. اختبر على بيانات حقيقية لا تجريبية، وقِس أثر القرار على التقارير المالية قبل تعميمه. وإذا وقع قرارك في منتصف نطاق التعقيد، نفّذه على مجموعة صغيرة أولًا حتى يثبت في التقارير قبل التعميم.
نقطة تحقق
مخطط يربط عناصر الموضوع
flowchart LR
A["واجهة المستخدم"]
B["وحدة Odoo"]
C["قواعد الصلاحيات وORM"]
D["PostgreSQL"]
E["تقارير ومخرجات"]
A --> B
B --> C
C --> D
D --> E
التسلسل: واجهة المستخدم ← وحدة Odoo ← قواعد الصلاحيات وORM ← PostgreSQL ← تقارير ومخرجات.
سيناريو تطبيقي ومراجعة
افترض أنك مسؤول عن تطبيق ما ورد في هذا المقال داخل فريق عمل. ابدأ بتحديد الهدف الذي يقيس النجاح في موضوع أودو متعدد الشركات: العملات والمواقع والتداخلات، ثم اكتب المدخلات المطلوبة والقيود والصلاحيات، وجرب حالة طبيعية وأخرى استثنائية قبل الاعتماد.
- حدد الافتراضات التي تحتاج إلى تأكيد في بيئتك.
- حوّل القرارات الأساسية الواردة أعلاه إلى خطوات أو اختبارات قابلة للتنفيذ.
- وثّق طريقة التراجع أو التصحيح إذا فشل أحد الاختبارات.
أسئلة للمراجعة
كيف أبدأ بتطبيق هذا الموضوع؟
اختر حالة استخدام واحدة، وطبّق أصغر جزء يحقق قيمة قابلة للقياس؛ ثم قارن النتيجة بالمتطلبات.
كيف أتحقق من جودة الحل؟
اختبر المسار المعتاد ومسار الخطأ، وراجع الأمان وسلامة البيانات والأداء وفق طبيعة الموضوع، ثم أعد الاختبار بعد أي تعديل.
الرد على التعليق