دراسة حالة · برمجيات تشغيل

برنامج المحصل

منصة لتحصيل الديون لا يستطيع فيها أحد — ولا حتى المسؤولون — تغيير الأرقام بصمت.

عشرات المحصّلين الميدانيين، ومحافظ متعددة، وآلاف حسابات العملاء. كل إجراء محفوظ للأبد، وتطبيق جوال يعمل بلا إنترنت، وكود يملكه العميل.

RustFlutterبنية الأحداثيعمل بلا إنترنت
نظرة عامة على لوحة الإدارة: الإجماليات والدين عبر الزمن
الشاشة الرئيسية: الرصيد الكلي والرسم البياني والأرصدة المستحقة
اختبارات آلية
230منها 206 سيناريوهات كاملة: عدة أشخاص يسجّلون أحداثاً في الوقت نفسه، إضافة إلى اختبارات الضغط.
من البداية إلى التشغيل
3 أشهرمن أول لقاء معنا حتى صار النظام يدير أعمالهم.
في الميدان
بلا حاجة إلى إشارةيسجّل المحصّلون الدفعات في أي مكان، ويزامن التطبيق حين يعود الاتصال.
منذ الإطلاق
لا أخطاء جسيمة مُبلَّغ عنهابحسب العميل. لم يطلبوا سوى تحسينات في الواجهة، وأجريناها.

المشكلة

مال يتحرك بسرعة، عبر أيدٍ كثيرة، بلا ذاكرة.

المحصّلون يتعاملون مع النقد، وبعضهم يدير الحسابات، وبعضهم يفعل الأمرين معاً. المحافظ مشتركة والموافقات شفهية. وحين يبدو شيء غير صحيح، لا أحد يستطيع الإجابة: من غيّر هذا، ومتى، ومن أي قيمة؟

ما طلبوه منا
سجل دائم كل معاملة محفوظة للأبد، ولا يمكن تعديل شيء بعد وقوعه.
صلاحيات دقيقة يسجّل الفريق لعملائه فقط، ولا يرى حتى محفظة فريق آخر.
يعمل بلا إشارة تطبيق ميداني يزامن حين يعود الاتصال.
ملكية كاملة الكود المصدري كاملاً، بلا اشتراكات ولا اعتماد على مزوّد.
قبل أن نكتب سطراً واحداً
“جاؤونا يطلبون سجلات في كل مكان. قلنا لهم: اجعل هيكل البيانات نفسه هو السجل.”
ما وجدناه · أرصدة فقط (مثال)
العميلالرصيد
#48211,450,000 1,200,000
#3307860,000

غيّر رقماً فيختفي القديم. من ومتى ولماذا: مجهول.

ما بنيناه · كل إجراء محفوظ (مثال)
  1. الثلاثاء 14:32 · المحصّل أ+250,000#4821
  2. الثلاثاء 16:05 · المالية−500,000#4821
  3. الأربعاء 09:10 · المحصّل ج+120,000#3307

الأرصدة تُحسب من هذه القائمة. لا شيء يُكتب فوق شيء، أبداً.

كيف يعمل

التاريخ هو مصدر الحقيقة.

أحداث تدخل، ورصيد يخرج
+250,000 · المحصّل أ−500,000 · المالية+1,450,000 · رصيد افتتاحي
رصيد #4821 1,200,000 محسوب · مخزّن مؤقتاً

رصيد مخزّن يبدو خاطئاً؟ أعد تشغيل الأحداث وأعد بناءه. التصحيحات أحداث جديدة، لا تعديلات.

القواعد تُكتب مرة وتعمل في كل مكان
نواة Rust مشتركةكل قواعد العمل في ملف مصدر واحد
الخادمAxum · Postgres · الكلمة الأخيرة
تطبيق الجوالFlutter · SQLite · يعمل بلا إنترنت

قاعدتا كود تتباعدان؛ قاعدة واحدة لا تستطيع. شكّك العميل في Rust أولاً، فأثبتناه: 230 اختباراً، معظمها سيناريوهات كاملة يتصرف فيها عدة أشخاص معاً، إلى جانب اختبارات الضغط.

في المكتب

لوحة التحكم

المحافظ والفرق والعملاء في مكان واحد، وكل رقم يعود إلى الإجراءات التي أنتجته.

لوحة الإدارة: الإجماليات، والدين عبر الزمن، وسجل الأحداث
لوحة الإدارة. كل رقم في الأعلى يعود إلى سجل الأحداث تحته.
في الميدان

تطبيق المحصّل

يسجّل المحصّلون الدفعات حيث يقفون، بإشارة أو بدونها. يُحفظ العمل على الهاتف ويُزامن لحظة عودة الاتصال.

الشاشة الرئيسية: الرصيد الكلي والرسم البياني والأرصدة المستحقة قائمة جهات الاتصال مع ما أعطاه أو استلمه كل منها رصيد عميل واحد وسجل معاملاته إضافة معاملة بالمبلغ والتاريخ وتاريخ الاستحقاق
الرئيسية 1 / 4 الرصيد بنظرة واحدة.
محفظة
فريق · مثل المبيعات
مجموعة عملاء · مثل منطقة الشمال
قراءةإنشاءتعديلحذفإغلاق
مثال: هذا الفريق يقرأ وينشئ ويغلق، ولا يستطيع الحذف.
الصلاحيات

الجزء الذي لا يفكر فيه أحد حتى يحتاجه.

  1. على مستوى المحفظة — من يضيف الأعضاء، ويعيد التسمية، ويعدّل القواعد.
  2. بين الفرق — "مدير فريق المبيعات" يدير فريقه فقط، لا فريق المالية.
  3. مجموعات العملاء — أي الفرق تستطيع تصنيف العملاء أو إزالة تصنيفهم.
  4. على مستوى الإجراء — قراءة، إنشاء، تعديل، حذف، إغلاق: لكل فريق ولكل مجموعة.

كل تغيير في الصلاحيات هو نفسه حدث مسجّل.

ما وراء سجل التدقيق

خوادم تتحقق من بعضها.

عدة خوادم تقارن البصمات التشفيرية لتاريخ أحداثها. لتزوير سجل عليك إعادة كتابة كل خادم وكل جهاز في آن واحد، وحتى حينها يُطلق كل منها إنذاراً، ويوقف استقبال الأحداث الجديدة، ويُنبّه المسؤولين. وتبقى التطبيقات تعمل دون اتصال حتى تتفق الخوادم.

متاحة في بناءاتنا المخصصة.

a3f9…c2 a3f9…c2 a3f9…c2
الإطلاق

كيف انتقلنا دون أن نعرّض أعمالهم للخطر.

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

ستة أشياء لم تكن لديهم من قبل.

  • ملكية كاملةملف تنفيذي واحد يعمل على خادمه الخاص. بلا سحابة خارجية، بلا اشتراك، بلا مزوّد.
  • سجل دائمكل إجراء موثّق بشكل دائم. الأرصدة قابلة للتحقق بإعادة تشغيل التاريخ. النزاعات لها إجابة.
  • محافظ متعددةكل محفظة بفريقها وعملائها وقواعد صلاحياتها — معزولة تماماً.
  • تطبيق جواليعمل بدون إنترنت ويزامن حين يعود الاتصال.
  • صلاحيات دقيقةقائد الفريق يدير فريقه فقط، ولا يلمس شيئاً آخر.
  • خدمات إضافيةتذكيرات بمواعيد الاستحقاق، وكشوف حساب عبر SMS وواتساب، وأدوات للمكتب: بجانب النواة لا داخلها.
للمطوّرين
  • Rust — خادم (Postgres + Axum) وطبقة منطق العميل
  • Flutter — واجهة التطبيق عبر Flutter Rust Bridge
  • SQLite — قاعدة بيانات التطبيق المحلية
  • بنية الأحداث — كل إجراء مسجّل، الأرصدة محسوبة وليست مخزّنة
  • مكتبات Rust مشتركة — منطق العمل مكتوب مرة واحدة، يعمل على الخادم والتطبيق
مفتوح المصدر على GitHubالنسخة مفتوحة المصدر من هذا النظام: الشيفرة كاملة، اقرأها أو شغّلها بنفسك.github.com/dark-cli/deptmasterالرخصةPolyForm Noncommercial 1.0.0