Skip to content
Mozn Jamousمزن جاموس
Back to workعودة إلى الأعمال

Odoo 19 · Python · Open sourceOdoo 19 · Python · مفتوح المصدر

Smart Expense Manager: an approval that takes one click.Smart Expense Manager: موافقة بنقرة واحدة.

A custom Odoo 19 module that replaces the paper expense loop. Approval emails carry a single-use token and route through the hr.employee hierarchy, so nobody has to log in. Finance sign-off generates the journal entry, and a cron job emails a monthly PDF report.وحدة Odoo 19 مخصّصة تحلّ محلّ حلقة النفقات الورقية. رسائل الموافقة تحمل رمزاً يُستخدَم مرة واحدة وتُوجَّه عبر تسلسل hr.employee، فلا يحتاج أحد إلى تسجيل الدخول. واعتماد المالية يولّد القيد المحاسبي، ومهمّة cron ترسل تقرير PDF شهرياً.

Yearالسنة
2026 to now2026 حتى الآن
Roleالدور
Designed and wrote it on my ownصمّمتها وكتبتها وحدي
Stackالتقنيات
PythonOdoo 19 ORMPostgreSQLQWebOWLXMLBootstrap
Days to secondsمن أيام إلى ثوانٍ
Approval time, on a one-click flow with no loginزمن الموافقة، على تدفّق بنقرة واحدة بلا تسجيل دخول
No loginبلا تسجيل دخول
Managers act from email; the system gets usedالمديرون يتصرّفون من البريد؛ فيُستخدَم النظام
Audit-gradeبمستوى التدقيق
Access enforced in record rules rather than hidden menusالوصول مفروض في قواعد السجلات بدل قوائم مخفية
Open sourceمفتوح المصدر
Live on GitHub under LGPL-3.0حيّ على GitHub تحت LGPL-3.0

§ Overviewنظرة عامة

What it does, and what I owned.ما الذي يفعله، وما الذي تولّيته.

An employee submits a receipt from their phone. A manager approves with one click from email. Finance posts with one click from email. The journal entry is generated automatically, and a cron job mails a monthly department digest. Nobody logs in anywhere along the way.يقدّم الموظّف إيصالاً من جوّاله. ويوافق المدير بنقرة واحدة من البريد. وتُرحّل المالية بنقرة واحدة من البريد. ويُولَّد القيد المحاسبي تلقائياً، وترسل مهمّة cron ملخّصاً شهرياً للقسم. ولا يسجّل أحد دخوله في أي موضع من الطريق.

Roleالدور
Designed it and wrote all of itصمّمتها وكتبتها كلها
Timelineالإطار الزمني
2026 to now2026 حتى الآن
Module typeنوع الوحدة
Odoo 19 customOdoo 19 مخصّصة
Usersالمستخدمون
Employees, managers, financeموظفون، ومديرون، ومالية
Auth patternنمط المصادقة
Token-secured URLرابطٌ مؤمّن برمز
Licenseالترخيص
LGPL-3.0, open sourceLGPL-3.0، مفتوح المصدر

§ Problemالمشكلة

Paper-based expense workflows in small businesses are quietly broken.تدفّقات النفقات الورقية في الشركات الصغيرة معطّلةٌ بصمت.

In a typical small business in Damascus, an employee buys something for work, keeps the receipt in a desk drawer for two weeks, hands it to a manager who signs a paper form, then walks it to finance who types it into a ledger. At every stage somebody is the bottleneck. Receipts get lost. Numbers get re-typed. And the whole loop is invisible to the company until month-end.في شركة صغيرة نموذجية بدمشق، يشتري موظّف شيئاً للعمل، ويحتفظ بالإيصال في درج مكتب أسبوعين، ثم يسلّمه لمدير يوقّع نموذجاً ورقياً، ثم يمشي به إلى المالية التي تكتبه في دفتر. وفي كل مرحلة يكون أحدهم عنق الزجاجة. تضيع الإيصالات. وتُعاد كتابة الأرقام. وتبقى الحلقة كلها غير مرئية للشركة حتى نهاية الشهر.

Existing Odoo expense modules require everyone in the chain to log into the system. In practice, managers don't log in. Approvals stall. The system goes unused. I needed a flow that works for people who never open Odoo.تتطلّب وحدات نفقات Odoo الموجودة أن يسجّل كلّ من في السلسلة دخوله إلى النظام. وعملياً، المديرون لا يسجّلون الدخول. تتعثّر الموافقات. ولا يُستخدَم النظام. احتجتُ تدفّقاً يعمل لأناسٍ لا يفتحون Odoo أبداً.

§ Users & personasالمستخدمون والـ Personas

Who has to actually use it.من عليه أن يستخدمه فعلاً.

The flow crosses three roles, and the person who decides whether the system lives or dies is the manager who never logs in. Each role became a persona with a short story and the friction that kept the old paper loop alive.يعبر التدفّق ثلاثة أدوار، ومن يقرّر حياة النظام أو موته هو المدير الذي لا يسجّل الدخول أبداً. وصار كل دور persona بقصة قصيرة والاحتكاك الذي أبقى الحلقة الورقية القديمة حيّة.

Omar, an employeeعمر، موظّف

Submits the expenseيقدّم النفقة

“I want to photograph the receipt and get reimbursed, not walk a paper form around the building.أريد أن أصوّر الإيصال وأُعوَّض، لا أن أتنقّل بنموذج ورقي في المبنى.”

Goalsالأهداف

Submit from his phone in seconds; know where the request stands.التقديم من جوّاله في ثوانٍ؛ ومعرفة أين وصل الطلب.

Frustrationsالإحباطات

Lost receipts, and reimbursements that take weeks because a form sat in a drawer.إيصالات ضائعة، وتعويضات تستغرق أسابيع لأن نموذجاً بقي في درج.

Nour, a line managerنور، مديرة مباشِرة

Approves, and makes or breaks the systemتوافق، وعليها يقوم النظام أو يسقط

“I want to approve in one tap from my inbox. I am not logging into another system just to click yes.أريد أن أوافق بنقرة واحدة من بريدي. ولن أسجّل الدخول إلى نظام آخر فقط لأنقر «نعم».”

Goalsالأهداف

Clear the approval the moment she sees it, wherever she is.إنهاء الموافقة لحظة رؤيتها، أينما كانت.

Frustrationsالإحباطات

Being asked to log into Odoo for a five-second decision, which is why approvals stall for days.أن يُطلب منها تسجيل الدخول إلى Odoo لقرار من خمس ثوانٍ، ولهذا تتعثّر الموافقات أياماً.

Farah, in financeفرح، في المالية

Posts to the ledgerتُرحّل إلى دفتر الأستاذ

“I want a clean ledger with real approved liabilities in it, and nothing I have to reverse later.أريد دفتر أستاذ نظيفاً فيه التزامات حقيقية معتمدة، ولا شيء أُضطرّ لعكسه لاحقاً.”

Goalsالأهداف

Post with one click, and trust that every entry was already approved.الترحيل بنقرة واحدة، والثقة بأن كل قيد كان معتمداً أصلاً.

Frustrationsالإحباطات

A ledger filling with unapproved or duplicate entries she has to clean up.دفتر أستاذٍ يمتلئ بقيودٍ غير معتمدة أو مكرّرة عليها تنظيفها.

§ Design strategyاستراتيجية التصميم

Optimize for the approval that actually happens.حسّن من أجل الموافقة التي تحدث فعلاً.

A module that is technically complete and never used has failed. So the whole strategy was to remove every reason a manager might not approve, starting with the login.الوحدة المكتملة تقنياً التي لا تُستخدَم أبداً فاشلة. فكانت الاستراتيجية كلها إزالة كل سبب قد يجعل المدير لا يوافق، وأولها تسجيل الدخول.

Goalالهدف
Approvals that actually get done, fastموافقاتٌ تُنجَز فعلاً، وبسرعة
Hypothesisالفرضية
Putting the action in email removes the stall that kills adoptionوضع الإجراء في البريد يزيل التعثّر الذي يقتل التبنّي
Priorityالأولوية
Less friction ahead of more featuresاحتكاك أقل قبل ميزات أكثر
Tradeoffالمفاضلة
Carrying token security to get a login-free flowحمل عبء تأمين الرمز لنيل تدفّق بلا تسجيل دخول

§ Approachالمقاربة

Email is the UI.البريد هو الواجهة.

The module attaches a unique token to an expense request when it goes out to a manager. The approval email carries two links, Approve and Reject, each a one-time URL bound to that token, the request ID and the manager's hr.employee record. Clicking one moves the request to its next state, with no Odoo login anywhere in it.ترفق الوحدة رمزاً فريداً بطلب النفقة عند إرساله إلى المدير. ويحمل بريد الموافقة رابطين، موافقة ورفض، كل واحد عنوان يُستخدَم مرة واحدة مرتبط بذلك الرمز ومعرّف الطلب وسجلّ hr.employee للمدير. والنقر على أحدهما ينقل الطلب إلى حالته التالية، دون أي تسجيل دخول إلى Odoo فيه.

▦ Architectureالبنية · Smart Expense Manager: the approval flowSmart Expense Manager: مسار الموافقة

SVG

EMPLOYEESubmitmobile receipthr.employeeMANAGERApprove1-click · emailtoken-securedFINANCEPost1-click · emailpostJOURNALaccount.moveauto-generated⏱ cron job · QWeb PDF monthly digest emailed to managers
Token-secured email approvals · auto-journal on finance sign-off.موافقات عبر البريد محميّة برمز · قيد محاسبي تلقائي عند اعتماد المالية.

Approval email

Visual placeholder · add exportصورة مؤقتة · أضف التصدير

The one-click approve or reject email. Screenshot still to add.بريد الموافقة أو الرفض بنقرة واحدة. اللقطة لم تُضَف بعد.

§ Technical architectureالبنية التقنية

Three trade-offs that shaped the build.ثلاث مفاضلاتٍ شكّلت البناء.

ADR-001

Token-secured URLs over OAuth approval flows.روابط مؤمّنة برمز بدل تدفّقات موافقة OAuth.

Contextالسياق

The standard Odoo pattern emails a notification with a link into the Odoo UI. The manager logs in, finds the record, clicks approve. What actually happens is that approvals stall for days.النمط القياسي في Odoo يرسل إشعاراً برابط إلى واجهة Odoo. يسجّل المدير الدخول، ويجد السجلّ، وينقر موافقة. وما يحدث فعلاً أن الموافقات تتعثّر أياماً.

Decisionالقرار

I embed a cryptographically random token in the email URL itself. The token is single-use, bound to the request and the manager's employee record, and expires once acted on.أُضمّن رمزاً عشوائياً تشفيرياً في رابط البريد نفسه. الرمز يُستخدَم مرّةً واحدة، ومرتبطٌ بالطلب وبسجلّ موظّف المدير، وينتهي بمجرّد التصرّف به.

Consequencesالنتائج

Approvals drop from days to seconds, and every state transition records the token that was used. The cost is that mail has to go over TLS-encrypted SMTP, because the token itself grants the right to act.تنزل الموافقات من أيام إلى ثوانٍ، وكل انتقال حالة يسجّل الرمز الذي استُخدم. والكلفة أن البريد يجب أن يمرّ عبر SMTP مشفّر بـTLS، لأن الرمز نفسه يمنح حقّ التصرّف.
ADR-002

Journal entries on finance approval, not on submission.القيود المحاسبية عند موافقة المالية، لا عند التقديم.

Contextالسياق

An eager design creates the journal entry the moment an employee submits. That is a simpler state machine, and it fills the ledger with unapproved entries somebody then has to reverse.التصميم المتعجّل يُنشئ القيد المحاسبي لحظة تقديم الموظّف. وهذه آلة حالات أبسط، وهي تملأ دفتر الأستاذ بقيود غير معتمدة على أحدهم أن يعكسها بعدها.

Decisionالقرار

The account.move is only generated on the finance-approval transition. Until then, the request is just an HR record.لا يُولَّد account.move إلا عند انتقال موافقة المالية. حتى ذلك الحين، يبقى الطلب مجرّد سجلّ موارد بشرية.

Consequencesالنتائج

The ledger stays clean, and an accountant only ever sees entries that are real liabilities. The cost is one more state, plus the cron digest having to know to skip pending requests.يبقى دفتر الأستاذ نظيفاً، ولا يرى المحاسب إلا قيوداً تمثّل التزامات حقيقية. والكلفة حالة إضافية، مع أن على ملخّص cron أن يعرف تخطّي الطلبات المعلّقة.
ADR-003

RBAC at the record-rule layer, not the menu layer.تحكّمٌ بالوصول في طبقة قواعد السجلات، لا طبقة القوائم.

Contextالسياق

The easiest Odoo access control hides menus per group. A user can still construct a URL and reach records they were never meant to see.أسهل تحكّم بالوصول في Odoo يخفي القوائم حسب المجموعة. ويستطيع المستخدم مع ذلك أن يركّب رابطاً ويصل إلى سجلات لم يكن مقصوداً أن يراها.

Decisionالقرار

Permissions live in record rules at the ORM layer. An employee sees their own requests, a manager sees the requests routed to them, and finance sees the approved queue. Hiding menus is cosmetic; the rules are the contract.تعيش الصلاحيات في قواعد السجلات في طبقة ORM. الموظّف يرى طلباته، والمدير يرى الطلبات المُوجَّهة إليه، والمالية ترى الطابور المعتمد. وإخفاء القوائم تجميلي؛ القواعد هي العقد.

Consequencesالنتائج

Access enforcement you can audit. An employee poking at URLs hits an ORM exception instead of finding a leak. The cost is a small testing matrix for cross-role visibility.فرضُ وصول يمكنك تدقيقه. الموظّف الذي يعبث بالروابط يصطدم باستثناء ORM بدل أن يجد تسريباً. والكلفة مصفوفة اختبار صغيرة للرؤية عبر الأدوار.

§ Bilingual by defaultثنائي اللغة افتراضياً

Arabic and English, both complete.العربية والإنجليزية، كلتاهما كاملة.

Every field, label, status, email template, and PDF report ships with full Arabic and English translations. The approval emails detect the recipient's preferred language from their res.users.lang setting, and QWeb-generated PDF reports pick up the same locale automatically.كلّ حقلٍ وتسمية وحالة وقالب بريدٍ وتقرير PDF يُطلق بترجمةٍ كاملة بالعربية والإنجليزية. وتكتشف رسائل الموافقة لغة المستلِم المفضّلة من إعداد res.users.lang الخاص به، وتلتقط تقارير PDF المُولّدة بـQWeb المنطقة نفسها تلقائياً.

In a Damascus SME, that is the difference between a module that gets installed and one that gets used.في شركة صغيرة بدمشق، هذا هو الفرق بين وحدة تُثبَّت وأخرى تُستخدَم.

§ Outcomesالنتائج

A pattern worth open-sourcing.نمطٌ يستحقّ أن يُفتَح مصدره.

Smart Expense is live and open source under LGPL-3.0. The token-secured approval flow turns a multi-day paper loop into a one-click action a manager finishes from their inbox, with access enforced at the ORM layer and a clean ledger downstream.Smart Expense حيّ ومفتوح المصدر تحت LGPL-3.0. ويحوّل تدفّق الموافقة المؤمّن بالرمز حلقةً ورقية تستغرق أياماً إلى إجراء بنقرة واحدة يُنجزه المدير من بريده، بوصول مفروض في طبقة ORM ودفتر أستاذ نظيف تالياً.

  1. Reusable. The token-secured approval pattern generalizes far beyond expense reports.قابل لإعادة الاستخدام. نمط الموافقة المؤمّن بالرمز يتعمّم إلى ما هو أبعد بكثير من تقارير النفقات.
  2. Auditable. An Odoo 19 module signed with my name lives longer than a CV bullet.قابل للتدقيق. وحدة Odoo 19 موقّعةٌ باسمي تعيش أطول من سطرٍ في سيرةٍ ذاتية.
  3. Low IP risk. The module is mechanical rather than strategic, so sharing it costs nothing and helps the Odoo community.مخاطر ملكية فكرية منخفضة. الوحدة آليّة أكثر منها استراتيجية، فمشاركتها لا تكلّف شيئاً وتفيد مجتمع Odoo.

§ Validationالتحقّق

The real test is adoption.الاختبار الحقيقي هو التبنّي.

For an internal tool the success metric is not a satisfaction score. It is whether managers actually approve. I would validate with a short usability study on the one-click email path, then read the token logs, which already record every transition, to see where the flow stalls, and prioritize from there.بالنسبة لأداة داخلية، مقياس النجاح ليس درجة رضا. بل ما إذا كان المديرون يوافقون فعلاً. سأتحقّق بدراسة قابلية استخدام قصيرة على مسار البريد بنقرة واحدة، ثم أقرأ سجلّات الرمز، التي تسجّل كل انتقال أصلاً، لأرى أين يتعثّر التدفّق، وأرتّب الأولويات من هناك.

  • P0The one-click approve and reject links have to work reliably in the email clients managers actually use. If it breaks once, nobody trusts the link again.على رابطَي الموافقة والرفض بنقرة واحدة أن يعملا بثبات في عملاء البريد التي يستخدمها المديرون فعلاً. وإن تعطّل مرة، لن يثق أحد بالرابط بعدها.
  • P1The Arabic and English approval emails must render identically well, since recipients act straight from the inbox.بريدا الموافقة بالعربية والإنجليزية يجب أن يُعرَضا بجودةٍ متطابقة، لأن المستلِمين يتصرّفون مباشرةً من البريد.
  • P2A lightweight audit dashboard over the token logs, proving adoption from data that already exists.لوحة تدقيق خفيفة على سجلّات الرمز، تُثبت التبنّي من بيانات موجودة أصلاً.

§ Reflectionتأمّل

What I'd carry forward.ما سأحمله معي.

The win here was a product insight, not a technical one: meeting people where they already are (their inbox) beats any amount of in-app polish they'll never see. I'll keep reaching for the lowest-friction surface before building a new screen.كان الفوز هنا استنتاجاً عن المنتج، لا تقنياً: لقاء الناس حيث هم أصلاً (بريدهم) يتفوّق على أي قدرٍ من الصقل داخل التطبيق لن يروه أبداً. وسأظلّ أمدّ يدي إلى أقلّ السطوح احتكاكاً قبل بناء شاشةٍ جديدة.

Next I would add usage analytics and a lightweight audit dashboard. The token model already logs every transition, so the data that proves adoption is already there waiting to be shown.تالياً سأضيف تحليلات استخدام ولوحة تدقيق خفيفة. نموذج الرمز يسجّل كل انتقال أصلاً، فالبيانات اللازمة لإثبات التبنّي موجودة وجاهزة للعرض.