$ 114.84
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Point of Sale (point_of_sale)
• Discuss (mail) • Inventory (stock) • Invoicing (account) |
| Lines of code | 36 |
| Technical Name |
l10n_sa_edi_pos_fix |
| License | OPL-1 |
| Website | https://icloud-solutions.net |
| Availability |
Odoo Online
Odoo.sh
On Premise
|
| Odoo Apps Dependencies |
•
Point of Sale (point_of_sale)
• Discuss (mail) • Inventory (stock) • Invoicing (account) |
| Lines of code | 36 |
| Technical Name |
l10n_sa_edi_pos_fix |
| License | OPL-1 |
| Website | https://icloud-solutions.net |
Saudi Arabia — EDI POS Concurrency Fix
إصلاح تعارض العمليات المتزامنة — فواتير نقاط البيع ZATCA
Eliminates SerializationFailure
crashes when multiple POS cashiers submit orders simultaneously under ZATCA Phase 2.
يعالج أخطاء التعارض في قاعدة البيانات عند إرسال عدة طلبيات من كاشيرات مختلفة في نفس الوقت.
Published by iCloud Solutions — Odoo partners for KSA & MENA
نشر: iCloud Solutions — شركاء أودو للمملكة والشرق الأوسط وشمال أفريقيا
iCloud Solutions
The Problem
What happens in a busy retail store
When two or more POS cashiers complete a sale at the same second, Odoo triggers a
synchronous ZATCA web-service call for each order inside a single HTTP request.
Every request races to lock the same ir_sequence database row
(the ZATCA chain sequence) using PostgreSQL's
SELECT … FOR UPDATE NOWAIT.
NOWAIT means: if another session already holds the lock, fail immediately. Only one cashier wins. The others exhaust Odoo's 5-retry limit and receive this exception:
SELECT number_next FROM ir_sequence
WHERE id=157 FOR UPDATE NOWAIT
ERROR: could not serialize access due to concurrent update
psycopg2.errors.SerializationFailure:
could not serialize access due to concurrent update
odoo.service.model: SERIALIZATION_FAILURE,
maximum number of tries reached!
Exact call stack
↓ hm_pos_refund_anysessions
↓ _process_order
↓ _process_saved_order
↓ _generate_pos_order_invoice
↓ account_move_send
↓ _call_web_service_before_pdf
↓ l10n_sa_edi
↓ action_process_edi_web_services
↓ _l10n_sa_post_zatca_edi
↓ _l10n_sa_edi_get_next_chain_index
↓ ir_sequence.next_by_id()
SELECT … FOR UPDATE NOWAIT
💥 SerializationFailure
- POS returns HTTP 200 with an error body — no popup warning
- The order appears paid on screen but is NOT saved to the database
- Receipt is printed but the invoice is missing in Odoo
- ZATCA chain sequence can be left in an inconsistent state
المشكلة
ماذا يحدث في المتجر المشغول؟
عندما يُنهي كاشيران أو أكثر عملية بيع في نفس الثانية، يُطلق أودو استدعاءً متزامناً
لخدمة ZATCA الإلكترونية لكل طلبية داخل طلب HTTP واحد. كل طلب يتسابق للحصول على
قفل الصف نفسه في قاعدة البيانات (ir_sequence) وهو تسلسل سلسلة ZATCA
باستخدام:
SELECT … FOR UPDATE NOWAIT
NOWAIT تعني: إذا كانت جلسة أخرى تحتفظ بالقفل، يفشل الطلب فوراً. كاشير واحد فقط ينجح. الباقون يستنفدون المحاولات الخمس ويتلقون خطأ:
- نقطة البيع تُعيد HTTP 200 مع جسم خطأ — لا يظهر أي تحذير
- الطلبية تبدو مكتملة على الشاشة لكنها لم تُحفظ في قاعدة البيانات
- الإيصال يُطبع لكن الفاتورة مفقودة في أودو
- قد تبقى سلسلة تسلسل ZATCA في حالة غير متسقة
لماذا يحدث هذا بالتحديد؟
معيار ZATCA المرحلة الثانية يتطلب تسلسلاً متواصلاً بدون فجوات
في مؤشر سلسلة الفواتير. لذلك استخدم أودو تسلسل no_gap مع
FOR UPDATE NOWAIT. هذا الخيار صحيح من الناحية المنطقية لمنع الفجوات،
لكنه يفشل تحت الضغط العالي في البيئات التجارية المزدحمة.
في البيئات ذات الحركة المنخفضة، تُكمل كل طلبية قبل وصول التالية. في المتاجر الكبرى ذات الكاشيرات المتعددة، يصبح التزامن حتمياً وتُصبح المشكلة متكررة ومؤثرة بشكل مباشر على المبيعات.
التسلسل الكامل للخطأ
create_from_ui → _process_order
→ _generate_pos_order_invoice
→ _call_web_service_before_pdf
→ l10n_sa_edi → next_by_id()
→ FOR UPDATE NOWAIT 💥
Fix 1 — Defer ZATCA for POS Invoices to the Cron
models/account_move_send.py — Primary fix. Eliminates the race condition entirely.
How it works
We override _call_web_service_before_invoice_pdf_render in
account.move.send. Before calling super(), we inspect every
invoice in invoices_data. For any invoice linked to a POS order
(invoice.pos_order_ids), we temporarily set
l10n_sa_edi_zatca = False.
The l10n_sa_edi wizard checks this flag to build its
to_process recordset. With the flag off, those POS invoices are skipped —
no call to ir_sequence, no lock, no crash.
The EDI document remains in to_send state.
Odoo's built-in cron "EDI: Perform web services operations" submits it
serially in its next run (typically within minutes).
ZATCA Phase 2 allows up to 24 hours for reporting simplified
invoices (B2C). Deferring POS ZATCA submission to a cron that runs every few minutes
is fully within the compliance window.
Standard B2B invoices are not affected — they continue to use
synchronous clearance as before.
Before vs After
→ ZATCA called immediately
→ ir_sequence FOR UPDATE NOWAIT
→ 💥 SerializationFailure
→ Order lost
→ l10n_sa_edi_zatca = False (POS only)
→ PDF generated instantly
→ ✓ Order saved successfully
→ Cron submits ZATCA <1 min later
الإصلاح الأول — تأجيل إرسال ZATCA لفواتير نقطة البيع إلى المهمة المجدولة
كيف يعمل الإصلاح؟
نُعيد تعريف (override) الدالة _call_web_service_before_invoice_pdf_render
في account.move.send. قبل استدعاء super()، نفحص كل
فاتورة في invoices_data. لأي فاتورة مرتبطة بطلبية نقطة بيع
(invoice.pos_order_ids)، نضع l10n_sa_edi_zatca = False
مؤقتاً.
المعالج في l10n_sa_edi يفحص هذا المفتاح
لبناء قائمة الفواتير المراد معالجتها. بجعله False، تُستثنى فواتير نقاط
البيع — لا استدعاء لـ ir_sequence، لا قفل، لا تعارض.
يبقى مستند EDI في حالة to_send. المهمة
المجدولة المدمجة في أودو "EDI: Perform web services operations" ترسله
بشكل تسلسلي في تشغيلها القادم (عادةً خلال دقائق).
تسمح المرحلة الثانية من ZATCA بمدة تصل إلى 24 ساعة للإبلاغ عن
الفواتير المبسطة (B2C — نقاط البيع للأفراد). إرسال الفاتورة عبر مهمة مجدولة
تعمل كل دقيقة يبقى ضمن نافذة الامتثال بشكل كامل.
فواتير B2B (الشركات) لا تتأثر — تستمر في استخدام المسار
المتزامن كما كانت.
قبل الإصلاح وبعده
→ استدعاء ZATCA فوري
→ ir_sequence FOR UPDATE NOWAIT
→ 💥 SerializationFailure
→ الطلبية ضائعة
→ l10n_sa_edi_zatca = False
→ PDF يُولَّد فورياً
→ ✓ الطلبية محفوظة بنجاح
→ المهمة المجدولة ترسل ZATCA <دقيقة
Fix 2 — PostgreSQL Advisory Lock on Chain Index
models/account_journal.py — Safety net for all remaining synchronous ZATCA paths.
Why a second fix?
Fix 1 covers POS invoices. However, the original race condition in
_l10n_sa_edi_get_next_chain_index can still theoretically affect:
- Manual "Send & Print" on a POS-generated invoice
- Simultaneous posting of two standard (B2B) invoices from the same journal
- Any future code path that calls the chain index method synchronously
How pg_advisory_xact_lock works
Before delegating to super(), we acquire a PostgreSQL
advisory transaction lock keyed on the journal's database ID:
- Blocks (waits patiently) instead of failing with NOWAIT
- Released automatically at end of the current transaction
- Scoped to a single journal — cashiers on different journals don't block each other
- The underlying
FOR UPDATE NOWAITinir_sequencealways succeeds because the advisory lock guarantees no two sessions enternext_by_id()simultaneously for the same journal
Lock behaviour comparison
| Behaviour | Original NOWAIT | Advisory Lock |
|---|---|---|
| Already locked by another session | Fail immediately | Wait & succeed |
| Sequence gaps | None | None |
| Chain integrity | Maintained | Maintained |
| Table-level interference | Row-level | None (advisory) |
| Auto-released | On row commit | On transaction end |
الإصلاح الثاني — قفل استشاري PostgreSQL على مؤشر السلسلة
لماذا نحتاج إصلاحاً ثانياً؟
الإصلاح الأول يُغطي فواتير نقاط البيع. غير أن حالة التعارض الأصلية في
_l10n_sa_edi_get_next_chain_index قد تؤثر أيضاً في:
- الإرسال اليدوي عبر "إرسال وطباعة" على فاتورة مولودة من نقطة البيع
- ترحيل فاتورتَي B2B في نفس الوقت من نفس الدفتر
- أي مسار كود مستقبلي يستدعي دالة مؤشر السلسلة بشكل متزامن
كيف يعمل القفل الاستشاري؟
قبل تفويض التنفيذ إلى super()، نحصل على
قفل معاملة استشاري في PostgreSQL مفتاحه معرّف الدفتر في قاعدة البيانات:
- ينتظر بصبر بدلاً من الفشل فوراً مع NOWAIT
- يُحرَّر تلقائياً في نهاية المعاملة الحالية
- محدود النطاق بدفتر واحد — الكاشيرات على دفاتر مختلفة لا تتعارض
- الـ
FOR UPDATE NOWAITفيir_sequenceينجح دائماً لأن القفل الاستشاري يضمن عدم دخول جلستينnext_by_id()في نفس الوقت
مقارنة سلوك القفل
| السلوك | NOWAIT الأصلي | القفل الاستشاري |
|---|---|---|
| جلسة أخرى تحتفظ بالقفل | يفشل فوراً | ينتظر وينجح |
| فجوات في التسلسل | لا يوجد | لا يوجد |
| سلامة السلسلة | محافظ عليها | محافظ عليها |
| التأثير على الجداول | قفل صف | لا تأثير (استشاري) |
How the Two Fixes Work Together كيف يعملان معاً
الكاشير
account_move_send
sets edi_zatca=False
for POS invoices
serial execution
no race condition
advisory_xact_lock
blocks, not fails
for B2B / manual
تم الإرسال
Technical Specification
| Odoo Version | 17.0 |
|---|---|
| Technical Name | l10n_sa_edi_pos_fix |
| Dependencies | l10n_sa_edi, point_of_sale |
| Models overridden | account.move.sendaccount.journal |
| Database changes | None — zero migration |
| ZATCA compliance | Phase 2 — B2C 24h reporting window |
| License | OPL-1 (Odoo Proprietary License) |
| Price | €100.00 EUR (excl. VAT where applicable) |
| Publisher | iCloud Solutions |
| Website | icloud-solutions.net |
| Support | contact@icloud-solutions.net |
| Version | 17.0.1.0.0 |
المواصفات التقنية
| إصدار أودو | 17.0 |
|---|---|
| الاسم التقني | l10n_sa_edi_pos_fix |
| التبعيات | l10n_sa_edi, point_of_sale |
| النماذج المعدّلة | account.move.sendaccount.journal |
| تغييرات قاعدة البيانات | لا شيء — لا ترحيل مطلوب |
| امتثال ZATCA | المرحلة الثانية — نافذة 24 ساعة B2C |
| الرخصة | OPL-1 (رخصة أودو الاحتكارية) |
| السعر | 100 يورو EUR (قبل الضريبة حيث ينطبق) |
| الناشر | iCloud Solutions |
| الموقع | icloud-solutions.net |
| الدعم | contact@icloud-solutions.net |
| الإصدار | 17.0.1.0.0 |
Installation
cp -r l10n_sa_edi_pos_fix /mnt/extra-addons/
# Restart Odoo
docker restart odoo
# Install via shell or Apps menu
odoo -d jayla17 -i l10n_sa_edi_pos_fix
--stop-after-init
التثبيت
cp -r l10n_sa_edi_pos_fix /mnt/extra-addons/
# أعد تشغيل أودو
docker restart odoo
# ثبّت من الصدفة أو قائمة التطبيقات
odoo -d jayla17 -i l10n_sa_edi_pos_fix
--stop-after-init
iCloud Solutions آي كلاود سوليوشنز — حلول أودو والتكامل والامتثال
iCloud Solutions delivers Odoo implementation, localization, API integrations, and ongoing support for businesses in Saudi Arabia, the GCC, and the wider MENA region. We specialize in accounting compliance (including ZATCA e-invoicing), retail & POS, and enterprise rollouts.
- Odoo Enterprise & Community — development & maintenance
- KSA localization, ZATCA, and third-party integrations
- Training, support, and custom modules (OPL-1)
iCloud Solutions تقدّم تنفيذ أودو، والتوطين، وتكامل واجهات البرمجة، والدعم المستمر للشركات في المملكة العربية السعودية ودول مجلس التعاون ومنطقة الشرق الأوسط وشمال أفريقيا. نتخصص في الامتثال المحاسبي بما في ذلك الفوترة الإلكترونية ZATCA، وتجارة التجزئة ونقاط البيع، ومشاريع المؤسسات.
- أودو Enterprise و Community — تطوير وصيانة
- التوطين السعودي وZATCA والتكامل مع الأنظمة الخارجية
- تدريب ودعم ووحدات مخصصة (OPL-1)
l10n_sa_edi_pos_fix
Saudi Arabia — EDI POS Concurrency Fix | v17.0.1.0.0
€100.00 EUR · License: OPL-1
iCloud Solutions — Odoo integration & localization for KSA & MENA
This module is licensed under the Odoo Proprietary License v1.0 (OPL-1). Purchase grants use according to the license terms. Read OPL-1
هذه الوحدة مرخّصة بموجب رخصة أودو الاحتكارية OPL-1. الشراء يمنح الاستخدام وفق شروط الرخصة.
© 2026 iCloud Solutions — icloud-solutions.net
Odoo Proprietary License v1.0 This software and associated files (the "Software") may only be used (executed, modified, executed after modifications) if you have purchased a valid license from the authors, typically via Odoo Apps, or if you have received a written agreement from the authors of the Software (see the COPYRIGHT file). You may develop Odoo modules that use the Software as a library (typically by depending on it, importing it and using its resources), but without copying any source code or material from the Software. You may distribute those modules under the license of your choice, provided that this license is compatible with the terms of the Odoo Proprietary License (For example: LGPL, MIT, or proprietary licenses similar to this one). It is forbidden to publish, distribute, sublicense, or sell copies of the Software or modified copies of the Software. The above copyright notice and this permission notice must be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Please log in to comment on this module