ملاحظات نموذج البيانات والقواعد المحاسبية — نظام إدارة المغسلة¶
معلومات الوثيقة¶
| الحقل | القيمة |
|---|---|
| المشروع | نظام إدارة المغسلة |
| الإصدار | 1.0 |
| اللغة | العربية |
| التقنيات | ASP.NET Core + Angular + PostgreSQL (مركزي) / SQLite (فروع غير متصلة) |
| نوع الوثيقة | مذكرات تقنية لنموذج البيانات |
جدول المحتويات¶
- نظرة عامة على الكيانات
- الكيانات الأساسية والعلاقات
- دليل الحسابات والأستاذ العام
- محرك القواعد المحاسبية
- التدفقات المحاسبية حسب نوع المعاملة
- استراتيجية UUID والمزامنة غير المتصلة
- نمط عقد API التقارير
- نماذج تسوية شركات السجاد
- نماذج دفع الخياطين
- تكوين استراتيجيات التسعير
- الحذف الناعم وسجل التدقيق
- مراجع المكتبات التقنية
1. نظرة عامة على الكيانات¶
ينقسم النظام إلى أربع طبقات من الكيانات:
| الطبقة | الغرض | أمثلة |
|---|---|---|
| البيانات الرئيسية | تكوين، نادراً ما يتغير | المستخدمون، الفروع، دليل الحسابات، العملات، قوائم الأسعار، أنواع القطع، أنواع الخدمات، التصنيفات، أنواع السجاد |
| البيانات التشغيلية | معاملات الأعمال اليومية | الفواتير، أسطر الفواتير، إيصالات السجاد، بنود إيصالات السجاد، فواتير السجاد النهائية، طلبات الخياطة، مهام الخياطة، حركات المخزون |
| البيانات المالية | سجلات محاسبية (لا تعدل مباشرة أبداً) | قيود اليومية، أسطر قيود اليومية (الأستاذ العام)، المدفوعات |
| البيانات المساعدة | تتبع وتسجيل | أرصدة الزبائن الدائنة، بطاقات الباركود، جلسات الورديات، سجل التدقيق، حزم المزامنة |
2. الكيانات الأساسية والعلاقات¶
2.1 كيانات البيانات الرئيسية¶
branches (id uuid PK, code, name, mode [online|offline], default_currency_id, status)
users (id uuid PK, username, password_hash, role, branch_id FK, status)
roles (id uuid PK, name)
role_permissions (role_id FK, module, action, is_allowed)
customers (id uuid PK, name, phone, classification_id FK, branch_id FK, credit_limit, status, is_transient)
customer_classifications (id uuid PK, name, default_price_list_id FK, default_discount_pct, allow_deferred_payment, is_default)
garment_types (id uuid PK, code, name_en, name_ar, category, status)
service_types (id uuid PK, code, name_en, name_ar, status)
garment_service_links (garment_type_id FK, service_type_id FK) — أي الخدمات تنطبق على أي القطع
price_lists (id uuid PK, name, currency_id FK, status)
price_list_entries (id uuid PK, price_list_id FK, garment_type_id FK, service_type_id FK, base_price numeric(18,4), effective_from, effective_to)
currencies (id uuid PK, code, name, symbol, decimal_places)
currency_rates (id uuid PK, from_currency_id FK, to_currency_id FK, rate numeric(18,6), effective_date)
fiscal_years (id uuid PK, name, start_date, end_date, is_closed)
tax_settings (id uuid PK, is_enabled bool, tax_name, tax_rate numeric(8,4), is_inclusive bool, tax_account_code)
carpet_types (id uuid PK, code, name_en, name_ar)
carpet_companies (id uuid PK, name, contact, phone, address, settlement_model [accrual|cash|prepayment], status)
carpet_company_prices (id uuid PK, company_id FK, carpet_type_id FK, price_per_sqm numeric(18,4), price_per_piece numeric(18,4), effective_date)
tailors (id uuid PK, name, phone, payout_model [periodic|peritem|salary_commission], base_salary numeric(18,4), status)
inventory_items (id uuid PK, code, name_en, name_ar, category, uom, selling_price numeric(18,4), cost_price numeric(18,4), current_quantity numeric(18,4), reorder_level numeric(18,4), branch_id FK, status)
2.2 الكيانات التشغيلية¶
invoices (id uuid PK, invoice_number, customer_id FK nullable, branch_id FK, date, due_date, operational_status [draft|confirmed|in_progress|ready|delivered], financial_status [unpaid|partially_paid|paid|deferred], subtotal numeric(18,4), discount_amount numeric(18,4), tax_amount numeric(18,4), grand_total numeric(18,4), paid_amount numeric(18,4), remaining_amount numeric(18,4), notes, created_by_user_id FK, currency_id FK)
invoice_items (id uuid PK, invoice_id FK, garment_type_id FK, service_type_id FK, quantity int, unit_price numeric(18,4), line_total numeric(18,4), discount_amount numeric(18,4), is_tailoring bool, tailor_id FK nullable, tailor_cost numeric(18,4), line_status [active|damaged|lost], compensation_amount numeric(18,4))
barcode_tags (id uuid PK, tag_code, invoice_id FK, invoice_item_id FK, garment_type_id FK, printed_at, scanned_at_delivery)
carpet_receipts (id uuid PK, receipt_number, customer_id FK, branch_id FK, date, advance_amount numeric(18,4), advance_method, notes, status [received|assigned_to_company|returned|ready|delivered], external_company_id FK nullable)
carpet_receipt_items (id uuid PK, receipt_id FK, carpet_type_id FK, quantity int, estimated_size, notes_damages)
carpet_final_invoices (id uuid PK, invoice_number, linked_receipt_id FK UNIQUE, customer_id FK, branch_id FK, date, operational_status, financial_status, subtotal numeric(18,4), discount_amount numeric(18,4), tax_amount numeric(18,4), grand_total numeric(18,4), advance_deduction numeric(18,4), paid_amount numeric(18,4), remaining_amount numeric(18,4), notes)
carpet_final_invoice_items (id uuid PK, final_invoice_id FK, carpet_type_id FK, pricing_strategy [per_sqm|per_piece|cost_plus], actual_area_sqm numeric(18,4) nullable, quantity int, unit_rate numeric(18,4), company_cost numeric(18,4), markup_pct numeric(8,4), line_total numeric(18,4))
tailoring_orders (id uuid PK, order_number, customer_id FK, linked_invoice_id FK nullable, branch_id FK, date, operational_status, financial_status)
tailoring_tasks (id uuid PK, order_id FK, service_description, garment_type_id FK, quantity int, unit_price numeric(18,4), assigned_tailor_id FK, tailor_cost numeric(18,4), task_status [pending|in_progress|completed])
inventory_transactions (id uuid PK, item_id FK, branch_id FK, transaction_type [sale|sale_cancellation|stock_receipt|adjustment], quantity_change numeric(18,4), reference_type, reference_id, date)
2.3 الكيانات المالية¶
chart_of_accounts (id uuid PK, account_code, account_name_en, account_name_ar, account_type [asset|liability|equity|income|expense], parent_id FK nullable, is_active, is_contra)
journal_entries (id uuid PK, entry_number, date, reference_type, reference_id, narration, branch_id FK, fiscal_year_id FK, is_manual bool, created_by_user_id FK, status [draft|posted|reversed])
journal_entry_lines (id uuid PK, journal_entry_id FK, account_code FK, debit numeric(18,4), credit numeric(18,4), branch_id FK, customer_id FK nullable, supplier_id FK nullable, tailor_id FK nullable, currency_id FK, narration)
payments (id uuid PK, payment_number, invoice_id FK nullable, carpet_final_invoice_id FK nullable, customer_id FK, branch_id FK, date, amount numeric(18,4), method [cash|card|bank_transfer|customer_credit|other], reference_no, notes, shift_id FK, status)
supplier_payments (id uuid PK, payment_number, supplier_type [carpet_company|tailor], supplier_id, branch_id FK, date, amount numeric(18,4), method, reference_no, notes)
customer_credits (id uuid PK, customer_id FK, branch_id FK, amount numeric(18,4), source_type, source_id, date, notes, remaining_amount numeric(18,4))
2.4 الكيانات المساعدة¶
shift_sessions (id uuid PK, user_id FK, branch_id FK, open_time, close_time, opening_float numeric(18,4), expected_cash numeric(18,4), actual_cash numeric(18,4), discrepancy numeric(18,4), notes)
audit_logs (id uuid PK, entity_type, entity_id, action, user_id FK, timestamp, old_value jsonb, new_value jsonb, branch_id FK)
sync_batches (id uuid PK, source_branch_id FK, target_branch_id FK nullable, export_date, import_date, total_rows, status [pending|imported|validated|rejected], file_path, validation_errors text)
system_settings (key text PK, value text)
3. دليل الحسابات والأستاذ العام¶
3.1 هيكل دليل الحسابات¶
دليل الحسابات (CoA) هو هيكل شجري هرمي. كل حساب ينتمي لواحد من خمسة أنواع رئيسية:
| النوع | مدى الأكواد | أمثلة |
|---|---|---|
| الأصول (1XXX) | 1000-1999 | نقدية (1000)، بنك (1010)، ذمم مدينة (1200)، مخزون (1300)، مصروفات مدفوعة مقدماً (1400) |
| الخصوم (2XXX) | 2000-2999 | ذمم دائنة (2000)، مستحقات خياطين (2100)، ضريبة مستحقة (2200)، عربون زبائن / إيراد غير مكتسب (2300) |
| حقوق الملكية (3XXX) | 3000-3999 | رأس مال المالك (3000)، أرباح محتجزة (3100) |
| الإيرادات (4XXX) | 4000-4999 | إيراد غسيل (4000)، إيراد سجاد (4010)، إيراد خياطة (4020)، إيراد مبيعات منتجات (4030) |
| المصروفات (5XXX) | 5000-5999 | مستلزمات غسيل (5000)، مصروف غسيل سجاد (5010)، تكلفة خياطة (5020)، تكلفة بضاعة مباعة (5030)، رواتب (5040)، مصروف تالف/مفقود (5050)، مصروف استرداد (5060) |
قاعدة: الحسابات الفرعية ترث نوع الحساب الأب. الكود الهرمي يعكس الشجرة (مثلاً: 1200 = ذمم مدينة، 1201 = ذمم مدينة - أفراد، 1202 = ذمم مدينة - شركات).
3.2 جدول الأستاذ العام¶
جدول journal_entry_lines هو الأستاذ العام نفسه. كل صف هو جانب واحد من قيد مزدوج.
القيود الأساسية:
- لكل قيد يومية: SUM(debit) == SUM(credit). يفرض على مستوى التطبيق قبل الترحيل.
- بمجرد ترحيل قيد اليومية، لا يمكن تعديله. التصحيحات تتم بإنشاء قيد عكسي.
- كل سطر يحمل branch_id لتقارير مستوى الفرع.
- كل سطر يحمل اختيارياً customer_id أو supplier_id أو tailor_id لتسويات الدفاتر المساعدة.
3.3 حساب الأرصدة¶
أرصدة الزبائن والموردين والخياطين لا تخزن أبداً في عمود balance على تلك الكيانات. يتم حسابها دائماً:
-- رصيد الزبون (الذمم المدينة)
SELECT COALESCE(SUM(debit), 0) - COALESCE(SUM(credit), 0) AS balance
FROM journal_entry_lines
WHERE account_code LIKE '120%' -- جميع حسابات الذمم المدينة الفرعية
AND customer_id = 'CUST-UUID';
لتحسين الأداء، يمكن الاحتفاظ بجدول مادي customer_balances، لكنه يعاد بناؤه من الأستاذ العام عند الطلب ولا يعامل أبداً كمصدر الحقيقة.
4. محرك القواعد المحاسبية¶
4.1 المفهوم¶
بدلاً من ترميز المنطق المحاسبي لكل نوع معاملة، يستخدم النظام جدول القواعد المحاسبية accounting_rules. كل قاعدة تعرف:
| الحقل | الوصف |
|---|---|
| معرف القاعدة | UUID |
| كود القاعدة | معرف قصير (مثلاً: "PAID_LAUNDRY_INVOICE") |
| الحدث | الحدث التجاري الذي يشغل هذه القاعدة |
| كود حساب المدين | |
| كود حساب الدائن | |
| الشرط | مرشح JSON اختياري (مثلاً: لطريقة دفع محددة فقط) |
| الأولوية | ترتيب التنفيذ إذا تطابقت عدة قواعد |
4.2 آلية العمل¶
عند وقوع حدث تجاري (مثلاً: InvoiceConfirmed، PaymentReceived، CarpetReturned)، يقوم النظام:
- بالبحث عن جميع القواعد المطابقة للحدث.
- لكل قاعدة، يقيم الشرط.
- يولد أسطر قيد اليومية (مدين X، دائن Y) للحسابات المطابقة.
- يغلف كل شيء في معاملة قاعدة بيانات واحدة مع تغييرات البيانات التشغيلية.
4.3 لماذا محرك قواعد؟¶
- يمكن لصاحب المغسلة إضافة نموذج تسوية جديد لشركات السجاد بإضافة صف في
accounting_rules، بدون تغيير الكود. - يمكن للمحاسب تعديل توجيهات الأستاذ العام دون إعادة نشر التطبيق.
- التوسع لتعدد العملات: إضافة قواعد لقيود تحويل العملات.
5. التدفقات المحاسبية حسب نوع المعاملة¶
5.1 فاتورة غسيل مدفوعة (نقداً عند الاستلام)¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | نقدية / بنك (1000) | الإجمالي الكلي | |
| 2 | إيراد غسيل (4000) | الإجمالي الكلي |
إذا كانت الضريبة مفعلة: | 3 | نقدية (1000) | قيمة الضريبة | | | 4 | ضريبة مستحقة (2200) | | قيمة الضريبة |
5.2 فاتورة غسيل آجلة¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | ذمم مدينة - الزبون (120X) | الإجمالي الكلي | |
| 2 | إيراد غسيل (4000) | الإجمالي الكلي |
عند استلام الدفع لاحقاً: | 3 | نقدية (1000) | المبلغ المدفوع | | | 4 | ذمم مدينة - الزبون (120X) | | المبلغ المدفوع |
5.3 الدفع الجزئي¶
المدفوعات الجزئية: مدين نقدية، دائن ذمم مدينة بقيمة الجزء المدفوع فقط.
5.4 فاتورة تحتوي على أصناف مخزون¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | نقدية / ذمم مدينة (1000/1200) | الإجمالي الكلي | |
| 2 | إيراد غسيل (4000) | حصة الخدمات | |
| 3 | إيراد مبيعات منتجات (4030) | حصة المنتجات | |
| 4 | تكلفة بضاعة مباعة (5030) | تكلفة المنتج | |
| 5 | أصل المخزون (1300) | تكلفة المنتج |
5.5 العربون / الدفع المقدم (سجاد)¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | نقدية (1000) | مبلغ العربون | |
| 2 | عربون زبائن - إيراد غير مكتسب (2300) | مبلغ العربون |
عند إنشاء فاتورة السجاد النهائية: | 3 | عربون زبائن (2300) | مبلغ العربون | | | 4 | إيراد سجاد (4010) | | الإجمالي النهائي | | 5 | إذا العربون > النهائي: عربون زبائن (2300) | الفائض | | | 6 | إذا العربون > النهائي: ذمم دائنة رصيد زبون (23XX) | | الفائض | | 7 | إذا النهائي > العربون: ذمم مدينة - الزبون (120X) | النقص | | | 8 | إذا النهائي > العربون: إيراد سجاد (4010) | | النقص |
5.6 شركة السجاد — نموذج المستحقات (Accrual)¶
عند ارتجاع السجاد من الشركة: | 1 | مصروف غسيل سجاد (5010) | تكلفة الشركة | | | 2 | ذمم دائنة - شركة س (200X) | | تكلفة الشركة |
عند الدفع للشركة: | 3 | ذمم دائنة - شركة س (200X) | المبلغ المدفوع | | | 4 | نقدية (1000) | | المبلغ المدفوع |
5.7 شركة السجاد — النموذج النقدي (Cash)¶
عند الدفع للشركة: | 1 | مصروف غسيل سجاد (5010) | المبلغ المدفوع | | | 2 | نقدية (1000) | | المبلغ المدفوع |
5.8 شركة السجاد — نموذج المقدم (Prepayment)¶
عند دفع العربون للشركة: | 1 | مصروف مدفوع مقدماً - شركة س (140X) | العربون | | | 2 | نقدية (1000) | | العربون |
عند ارتجاع السجاد: | 3 | مصروف غسيل سجاد (5010) | تكلفة الشركة | | | 4 | مصروف مدفوع مقدماً - شركة س (140X) | | تكلفة الشركة |
5.9 الخياطة — نموذج دوري / بالقطعة¶
عند اكتمال مهمة الخياطة: | 1 | تكلفة خياطة (5020) | تكلفة الخياط | | | 2 | مستحقات خياط - الخياط س (210X) | | تكلفة الخياط |
عند الدفع للخياط: | 3 | مستحقات خياط - الخياط س (210X) | المبلغ المدفوع | | | 4 | نقدية (1000) | | المبلغ المدفوع |
5.10 الخياطة — نموذج راتب + عمولة¶
الراتب الشهري (يولد تلقائياً عبر مهمة Quartz): | 1 | مصروف رواتب (5040) | الراتب الأساسي | | | 2 | مستحقات خياط - الخياط س (210X) | | الراتب الأساسي |
العمولة على القطع المنجزة: | 3 | مصروف مكافأة خياطة (5045) | قيمة العمولة | | | 4 | مستحقات خياط - الخياط س (210X) | | قيمة العمولة |
5.11 تعويض التالف / المفقود¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | مصروف تالف/مفقود (5050) | قيمة التعويض | |
| 2 | ذمم دائنة رصيد زبون (23XX) | قيمة التعويض |
أو إذا تم الاسترداد نقداً: | 1 | مصروف تالف/مفقود (5050) | قيمة التعويض | | | 2 | نقدية (1000) | | قيمة التعويض |
5.12 الاسترداد (فاتورة مدفوعة)¶
| # | الحساب | مدين | دائن |
|---|---|---|---|
| 1 | مصروف استرداد / إيراد مقابل (5060) | مبلغ الاسترداد | |
| 2 | نقدية / رصيد الزبون (1000 / 23XX) | مبلغ الاسترداد |
5.13 إلغاء فاتورة (غير مدفوعة)¶
يتم عكس القيد الأصلي: | # | الحساب | مدين | دائن | |---|--------|------|------| | 1 | إيراد غسيل (4000) | المبلغ الأصلي | | | 2 | ذمم مدينة (120X) | | المبلغ الأصلي |
6. استراتيجية UUID والمزامنة غير المتصلة¶
6.1 لماذا UUID¶
جميع المفاتيح الرئيسية هي UUID مولدة من جانب الخادم (C# Guid.NewGuid()). هذا يمنع تضارب المعرفات عند دمج بيانات الفروع غير المتصلة في الخادم المركزي.
6.2 قاعدة توليد UUID¶
- تولد UUID في طبقة التطبيق (ASP.NET Core)، وليس في قاعدة البيانات.
- الواجهة لا تولد UUID أبداً — ترسل طلبات POST بدون معرفات، والخلفية تولد UUID.
- هذا يضمن نقطة وحيدة لإنشاء UUID.
6.3 نموذج بيانات الفرع غير المتصل¶
تستخدم الفروع غير المتصلة SQLite بهيكل جداول مطابق. SQLite يدعم:
- TEXT لـ UUID (يخزن كنص بطول 36 حرفاً).
- NUMERIC للقيم العشرية.
- مشغلات SQLite أو منطق التطبيق يفرضان نفس قاعدة SUM(debit) == SUM(credit).
6.4 هيكل حزمة المزامنة¶
كل صف في قاعدة البيانات غير المتصلة له sync_status = 'local' | 'pending' | 'synced'.
حزمة التصدير (بتنسيق JSON):
{
"batch_id": "uuid",
"source_branch_id": "uuid",
"source_branch_code": "CAIRO-01",
"export_date": "2026-05-09T18:00:00Z",
"tables": {
"journal_entry_lines": [
{ "id": "uuid", "journal_entry_id": "uuid", ... }
],
"invoices": [ ... ],
"payments": [ ... ],
"customers": [ ... ],
...
}
}
6.5 منطق الاستيراد والدمج¶
1. تحميل ملف JSON المصدر.
2. التحقق من تطابق branch_id مع المصدر المتوقع.
3. التحقق من SUM(debit) == SUM(credit) داخل الحزمة.
4. التحقق من وجود جميع مراجع account_code في دليل الحسابات المركزي.
5. لكل جدول، تكرار الصفوف:
- إذا كان row.id غير موجود مركزياً ← INSERT.
- إذا كان row.id موجوداً مركزياً (إعادة استيراد) ← UPSERT (تحديث إذا المصدر أحدث).
6. تسجيل sync_batch بحالة 'imported' أو 'validated'.
7. تحديث صفوف المصدر (إذا كانت متاحة) إلى sync_status = 'synced'.
6.6 حل التعارضات¶
- تضارب رقم هاتف الزبون: إذا كان زبون بنفس رقم الهاتف موجوداً مركزياً، تعلم عملية الاستيراد بذلك. يختار المدير:
- ربط (ربط الزبون غير المتصل بالزبون المركزي الموجود).
- إبقاء منفصل (الاستيراد ينشئ سجل زبون جديد).
- تضارب رقم الفاتورة: أرقام الفواتير مسبوقة بكود الفرع، لذا لا يفترض حدوث ذلك. إذا حدث، يرفض الاستيراد.
- المزامنة المكررة: بما أن جميع المفاتيح UUID، إعادة استيراد نفس الملف آمنة (upsert حسب id).
7. نمط عقد API التقارير¶
7.1 تنسيق الاستجابة القياسي¶
كل نقطة API تقرير ترجع هذا الغلاف:
{
"meta": {
"reportName": "نص",
"generatedAt": "ISO8601",
"branchId": "uuid | 'all'",
"currency": "EGP",
"filters": {
"dateFrom": "YYYY-MM-DD",
"dateTo": "YYYY-MM-DD"
}
},
"summary": {
"totalRevenue": 12500.00,
"totalInvoices": 45,
...
},
"details": [
{ /* صف محسوب مسبقاً */ }
],
"pagination": {
"page": 1,
"pageSize": 50,
"totalCount": 450
}
}
7.2 المبادئ الأساسية¶
| المبدأ | القاعدة |
|---|---|
| جميع الحسابات في SQL | الأرصدة الجارية، SUM، AVG، التجميع، التصفية — كلها تنفذ في قاعدة البيانات. |
| الواجهة لا تحسب المال أبداً | حقول balance وtotal وremaining محسوبة مسبقاً. الواجهة فقط تنسق وتعرض. |
| ترقيم الصفحات للبيانات الكبيرة | الخلفية ترقم الصفحات. الواجهة تستخدم التحميل الكسول على جداول PrimeNG. |
| نقطة ملخص منفصلة | لوحات التحكم تستدعي /report/daily-sales/summary (تجميعات فقط). التفاصيل تستدعي /report/daily-sales/details (صفوف كاملة). |
| التفويض على مستوى API | الخلفية ترجع فقط البيانات المسموح للمستخدم برؤيتها (مرشح الفرع، مرشح الدور). |
| حدود النطاق الزمني | التقارير افتراضياً 30 يوماً. حد أقصى سنة واحدة بدون تجاوز المدير. |
7.3 نقاط API التقارير (مرجع)¶
GET /api/reports/daily-sales ?branchId=&dateFrom=&dateTo=
GET /api/reports/shift-closing ?shiftId=
GET /api/reports/customer-statement ?customerId=&dateFrom=&dateTo=
GET /api/reports/general-ledger ?accountCode=&dateFrom=&dateTo=&page=&pageSize=
GET /api/reports/trial-balance ?dateFrom=&dateTo=
GET /api/reports/income-statement ?branchId=&dateFrom=&dateTo=
GET /api/reports/balance-sheet ?date=
GET /api/reports/tailor-payout ?tailorId=&dateFrom=&dateTo=
GET /api/reports/company-payables ?companyId=&dateFrom=&dateTo=
GET /api/reports/inventory-levels ?branchId=&category=
GET /api/reports/best-selling ?dateFrom=&dateTo=&limit=
GET /api/reports/corporate-summary ?customerId=&year=&month=
GET /api/reports/damage-report ?dateFrom=&dateTo=
GET /api/reports/discounts-overrides ?dateFrom=&dateTo=&userId=
GET /api/reports/branch-comparison ?dateFrom=&dateTo=
GET /api/reports/audit-log ?entityType=&entityId=&userId=&dateFrom=&dateTo=
8. نماذج تسوية شركات السجاد (قابلة للتكوين لكل شركة)¶
| النموذج | عند ارتجاع السجاد من الشركة | عند الدفع للشركة |
|---|---|---|
| المستحقات (Accrual) | مدين مصروف، دائن ذمم دائنة | مدين ذمم دائنة، دائن نقدية |
| النقدي (Cash) | لا شيء | مدين مصروف، دائن نقدية |
| المقدم (Prepayment) | مدين مصروف، دائن مصروف مدفوع مقدماً | مدين مصروف مدفوع مقدماً (العربون)، دائن نقدية؛ الرصيد يحسب تلقائياً |
كل carpet_company له حقل settlement_model يمكن تجاوزه لكل معاملة إذا لزم الأمر.
9. نماذج دفع الخياطين (قابلة للتكوين لكل خياط)¶
| النموذج | عند اكتمال المهمة | عند الدفع |
|---|---|---|
| الدوري (Periodic) | مدين تكلفة خياطة، دائن مستحقات | مدين مستحقات، دائن نقدية |
| بالقطعة (Per-Item) | مدين تكلفة خياطة، دائن مستحقات (فوري) | مدين مستحقات، دائن نقدية (أي وقت) |
| راتب + عمولة | الراتب: مهمة Quartz الشهرية: مدين رواتب، دائن مستحقات. العمولة: مثل الدوري لبنود المكافأة. | مثل النماذج الأخرى |
كل tailor له حقل payout_model. محرك المحاسبة يقرؤه ويطبق القاعدة المطابقة.
10. تكوين استراتيجيات التسعير¶
10.1 استراتيجيات تسعير السجاد¶
| الاستراتيجية | المعاملات | حساب السعر |
|---|---|---|
| للمتر المربع | actual_area_sqm, selling_rate_per_sqm |
actual_area_sqm × selling_rate_per_sqm |
| للقطعة (سعر ثابت) | quantity, fixed_price_per_piece |
quantity × fixed_price_per_piece |
| هامش ربح على التكلفة | company_cost, markup_pct |
company_cost × (1 + markup_pct / 100) |
كل سجل carpet_type يحدد الاستراتيجيات المسموحة (allowed_strategies حقل JSON: ["per_sqm", "per_piece", "cost_plus"]).
10.2 معمارية قائمة الأسعار المرنة¶
تصنيف الزبون (CustomerClassification)
│
▼
قائمة الأسعار (PriceList) — مختارة لكل تصنيف
│
▼
إدخال قائمة الأسعار (PriceListEntry) — نوع_القطعة + نوع_الخدمة ← السعر_الأساسي
│
▼ (تجاوز اختياري مع سبب)
سطر الفاتورة (InvoiceItem) — سعر مجمد عند تأكيد الفاتورة
يمكن تفعيل عدة قوائم أسعار في وقت واحد. تغيير قائمة الأسعار لا يؤثر على الفواتير التاريخية.
11. الحذف الناعم وسجل التدقيق¶
11.1 لا حذف فعلي للبيانات المالية¶
- الفواتير والمدفوعات وقيود اليومية والإيصالات لا تحذف فعلياً أبداً.
- "الإلغاء" ينشئ تغيير حالة + قيد يومية عكسي.
- فقط فواتير المسودة (غير المؤكدة، لا تأثير مالي) يمكن إزالتها.
11.2 جدول سجل التدقيق¶
CREATE TABLE audit_logs (
id UUID PRIMARY KEY,
entity_type VARCHAR(50) NOT NULL, -- 'Invoice', 'Payment', 'Customer', إلخ
entity_id UUID NOT NULL,
action VARCHAR(20) NOT NULL, -- 'CREATE', 'UPDATE', 'DELETE', 'STATUS_CHANGE'
user_id UUID NOT NULL,
timestamp TIMESTAMPTZ NOT NULL DEFAULT NOW(),
old_value JSONB, -- NULL لـ CREATE
new_value JSONB, -- NULL لـ DELETE
branch_id UUID NOT NULL
);
11.3 ما يشغل إدخال تدقيق¶
- أي استدعاء API يعدل البيانات الرئيسية أو التشغيلية أو المالية.
- تغييرات الحالة (مثلاً: فاتورة من "جاهز" إلى "مسلّم").
- تجاوزات الأسعار والخصومات اليدوية (السعر القديم، السعر الجديد، السبب).
- تغييرات الصلاحيات.
12. مراجع المكتبات التقنية¶
12.1 الخلفية (ASP.NET Core)¶
| المكتبة | حزمة NuGet | الغرض |
|---|---|---|
| Entity Framework Core | Microsoft.EntityFrameworkCore |
ORM |
| Npgsql EF Core Provider | Npgsql.EntityFrameworkCore.PostgreSQL |
موفر PostgreSQL |
| Dapper | Dapper |
SQL عالي الأداء للتقارير واستعلامات الأستاذ |
| MediatR | MediatR |
نمط CQRS (أوامر للكتابة، استعلامات للقراءة) |
| FluentValidation | FluentValidation.AspNetCore |
التحقق من المدخلات |
| AutoMapper | AutoMapper |
تحويل DTO |
| Serilog | Serilog.AspNetCore |
تسجيل منظم |
| Serilog Npgsql Sink | Serilog.Sinks.PostgreSQL |
تسجيل إلى قاعدة البيانات |
| Quartz.NET | Quartz.Extensions.Hosting |
مهام خلفية (توليد المستحقات، مزامنة ليلية) |
| BCrypt.Net | BCrypt.Net-Next |
تشفير كلمات المرور |
| Microsoft Identity / JWT | Microsoft.AspNetCore.Authentication.JwtBearer |
المصادقة |
12.2 الواجهة (Angular)¶
| المكتبة | حزمة npm | الغرض |
|---|---|---|
| PrimeNG | primeng |
مكونات واجهة المستخدم (معتمدة من العميل) |
| PrimeFlex | primeflex |
أدوات CSS |
| PrimeIcons | primeicons |
الأيقونات |
| ngx-translate | @ngx-translate/core |
ترجمة AR/EN |
| SheetJS | xlsx |
تصدير Excel |
| html2canvas | html2canvas |
تحويل HTML إلى لوحة رسم |
| jspdf | jspdf |
إنشاء PDF |
| jsbarcode | jsbarcode |
توليد باركود |
| RxJS | rxjs |
إدارة الحالة التفاعلية |
12.3 قاعدة البيانات¶
| التقنية | الدور |
|---|---|
| PostgreSQL 16+ | قاعدة البيانات المركزية |
| SQLite 3 | قاعدة بيانات الفروع غير المتصلة |
| Npgsql | موفر .NET لـ PostgreSQL |
| Microsoft.Data.Sqlite | موفر .NET لـ SQLite |
ملحق أ: مخططات انتقال الحالة¶
أ.1 الحالة التشغيلية للفاتورة¶
stateDiagram-v2
[*] --> مسودة
مسودة --> مؤكدة : تأكيد
مؤكدة --> قيد_التنفيذ : بدء المعالجة
قيد_التنفيذ --> جاهزة : كل القطع جاهزة
جاهزة --> مسلمة : استلام الزبون
مسودة --> [*] : إزالة
مؤكدة --> [*] : إلغاء (إذا غير مدفوعة)
جاهزة --> تالف_مفقود : تعليم تالف/مفقود
أ.2 الحالة المالية للفاتورة¶
stateDiagram-v2
[*] --> غير_مدفوع
غير_مدفوع --> مدفوع_جزئيا : دفع جزئي
مدفوع_جزئيا --> مدفوع : دفع المتبقي
غير_مدفوع --> آجل : شروط دفع آجل
آجل --> مدفوع_جزئيا : دفع جزئي
آجل --> مدفوع : دفع كامل
مدفوع --> استرداد : معالجة استرداد
أ.3 إيصال السجاد → الفاتورة النهائية¶
stateDiagram-v2
[*] --> مستلم : استلام السجاد (إنشاء الإيصال)
مستلم --> مسند : إسناد إلى الشركة
مسند --> مرتجع : ارتجاع السجاد (قياس المساحة)
مرتجع --> فاتورة_نهائية : إنشاء الفاتورة النهائية (مرتبطة 1-لـ-1)
فاتورة_نهائية --> مخصوم : خصم العربون تلقائياً
مخصوم --> دفع_المتبقي : العربون < النهائي → دفع المتبقي
مخصوم --> رصيد_دائن : العربون > النهائي → رصيد دائن
دفع_المتبقي --> [*]
رصيد_دائن --> [*]
ملحق ب: مرجع سريع — أكواد الحسابات المحاسبية¶
| الكود | اسم الحساب | النوع |
|---|---|---|
| 1000 | نقدية | أصل |
| 1010 | بنك | أصل |
| 1200 | ذمم مدينة | أصل |
| 1300 | مخزون | أصل |
| 1400 | مصروفات مدفوعة مقدماً | أصل |
| 2000 | ذمم دائنة | خصم |
| 2100 | مستحقات خياطين | خصم |
| 2200 | ضريبة مستحقة | خصم |
| 2300 | عربون زبائن / إيراد غير مكتسب | خصم |
| 3000 | رأس مال المالك | حقوق ملكية |
| 3100 | أرباح محتجزة | حقوق ملكية |
| 4000 | إيراد غسيل | إيراد |
| 4010 | إيراد سجاد | إيراد |
| 4020 | إيراد خياطة | إيراد |
| 4030 | إيراد مبيعات منتجات | إيراد |
| 5000 | مصروف مستلزمات غسيل | مصروف |
| 5010 | مصروف غسيل سجاد | مصروف |
| 5020 | تكلفة خياطة | مصروف |
| 5030 | تكلفة بضاعة مباعة | مصروف |
| 5040 | مصروف رواتب | مصروف |
| 5045 | مصروف مكافأة خياطة | مصروف |
| 5050 | مصروف تالف/مفقود | مصروف |
| 5060 | مصروف استرداد / إيراد مقابل | مصروف |
سجل المراجعة¶
| التاريخ | الإصدار | المعد | التغييرات |
|---|---|---|---|
| 2026-05-10 | 1.0 | محلل النظم | الإصدار الأولي لمذكرات نموذج البيانات |