السحبات (العملية الأساسية) + التعديلات + القيود المحاسبية + القبض الجزئي + الموافقة الاختيارية
كل عملية مالية بتأثر على الراتب لازم تتسجل بشكل صحيح — مش مجرد خصم من الراتب، لأن فيه فلوس بتتحرك فعلياً.
| النوع | الشرح | فلوس بتتحرك؟ | من فين؟ | التأثير على الراتب |
|---|---|---|---|---|
| سحب تحت حساب | الموظف ياخد فلوس كاش من الخزنة أو تحويل قبل آخر الشهر | نعم — فوراً | الخزنة أو البنك | يتخصم من الراتب آخر الشهر |
| سلفة مقدمة | مبلغ كبير يتسدد على أقساط | نعم — فوراً | الخزنة أو البنك | قسط شهري يتخصم تلقائي |
| خصم إتلاف / جزاء | خصم بسبب إتلاف ممتلكات أو مخالفة | لا — خصم فقط | — | يتخصم من الراتب |
| حافز / مكافأة | مبلغ إضافي لمرة واحدة | حسب — ممكن نقدي أو مع الراتب | مع الراتب أو من الخزنة | يتضاف على الراتب |
| خصم تأخير / غياب | خصم تلقائي من الحضور | لا — تلقائي | — | يتخصم تلقائي |
| بدلات ثابتة | سكن، مواصلات، طعام (كل شهر) | لا — مع الراتب | — | يتضاف تلقائي |
| استقطاعات ثابتة | تأمينات، ضريبة (كل شهر) | لا — مع الراتب | — | يتخصم تلقائي |
جدول جديد لتسجيل أي خصم أو إضافة لمرة واحدة على مسير محدد — بدل ما تعدل هيكل الراتب.
| هيكل الراتب (Salary Structure) | تعديلات المسير (Payroll Adjustments) | |
|---|---|---|
| التكرار | كل شهر (دائم) | مرة واحدة على مسير محدد |
| الأمثلة | بدل سكن، تأمينات | سحب تحت حساب، خصم إتلاف، حافز |
| التعديل | من شاشة هيكل الراتب | من شاشة المسير مباشرة |
| الربط | مرتبط بالموظف | مرتبط بالموظف والمسير |
payroll_adjustments:| الحقل | النوع | الشرح |
|---|---|---|
| id | bigint PK | |
| payroll_id | FK → payrolls | المسير |
| employee_id | FK → employees | الموظف |
| type | enum(earning, deduction) | إضافة أو خصم |
| category | enum(advance, penalty, damage, bonus, incentive, other) | تصنيف العملية |
| amount | decimal(12,3) | المبلغ (موجب دائماً) |
| description | text | الوصف / السبب |
| reference_number | varchar nullable | رقم مرجعي (إيصال، أمر صرف) |
| payment_method | enum(cash, bank, from_salary) nullable | كيف اتصرف المبلغ |
| cash_account_id | FK → accounts nullable | حساب الخزنة/البنك اللي اتصرف منه |
| journal_entry_id | FK → journal_entries nullable | القيد المحاسبي (لو فيه فلوس اتحركت) |
| is_paid_out | boolean default false | هل الفلوس اتصرفت فعلاً (للسحبات) |
| paid_at | timestamp nullable | تاريخ الصرف الفعلي |
| status | enum(pending, approved, rejected) default 'approved' | حالة الموافقة — لو approval_required = false يتعمل approved مباشرة |
| approved_by | FK → users nullable | مين وافق |
| company_id | FK | |
| created_by / updated_by | FK | |
| timestamps |
| # | التاريخ | النوع | التصنيف | المبلغ | من فين | اتصرف؟ |
|---|---|---|---|---|---|---|
| 1 | 5 أبريل | خصم | سحب تحت حساب | 1,000 | الخزنة | نعم ✅ |
| 2 | 12 أبريل | خصم | سحب تحت حساب | 500 | تحويل بنكي | نعم ✅ |
| 3 | 20 أبريل | خصم | خصم إتلاف | 300 | من الراتب | لا (يتخصم آخر الشهر) |
approval_required = true → المدير يوافق أولاًapproval_required = false → يتصرف مباشرة (مرحلة واحدة — مناسب للمؤسسات الصغيرة)
auto_journal = true): القيد يتعمل مباشرة مع تسجيل السحب — عملية واحدة من مكان واحد ✅auto_journal = false): HR يسجل السحب → المحاسب يراجع ويعمل القيد منفصلhr_settings:| المفتاح | النوع | القيمة الافتراضية | الشرح |
|---|---|---|---|
payroll_adjustment_approval_required |
boolean | false |
هل تعديلات المسير (سحبات، خصومات...) تحتاج موافقة مدير قبل الصرف؟ |
payroll_adjustment_approval_roles |
json array | ["hr_manager"] |
الأدوار اللي تقدر توافق (لو الموافقة مفعّلة) |
payroll_adjustment_auto_journal |
boolean | true |
هل القيد المحاسبي يتعمل تلقائي مع تسجيل السحب؟ أو يحتاج المحاسب يعمله يدوي؟ |
| الإعداد | الفلو | مناسب لـ |
|---|---|---|
approval_required = false |
HR يضيف التعديل → يتصرف مباشرة (بدون موافقة) | مؤسسات صغيرة، مالك واحد، فريق صغير |
approval_required = true |
HR يضيف التعديل → المدير يوافق → المحاسب يصرف | شركات كبيرة، فصل صلاحيات، رقابة مالية |
| الإعداد | الفلو | مناسب لـ |
|---|---|---|
auto_journal = true |
HR يضيف السحب → القيد المحاسبي يتعمل أوتوماتيك فوراً — عملية واحدة من مكان واحد | مؤسسات صغيرة، بدون محاسب منفصل |
auto_journal = false |
HR يضيف السحب → المحاسب يراجع ويعمل القيد يدوي من شاشة القيود | شركات كبيرة، فصل بين HR والمحاسبة |
الأصول المتداولة (1000)
├── الخزينة (1100)
├── البنك (1110)
│
├── مسحوبات موظفين — تحت حساب (1300)
│ ├── أحمد محمد (1300-EMP001)
│ ├── خالد علي (1300-EMP002)
│ └── سارة حسن (1300-EMP003)
│
├── قروض موظفين (1310)
│ ├── أحمد محمد (1310-EMP001)
│ ├── خالد علي (1310-EMP002)
│ └── سارة حسن (1310-EMP003)
│
└── ذمم موظفين — جزاءات وإتلاف (1320)
├── أحمد محمد (1320-EMP001)
├── خالد علي (1320-EMP002)
└── سارة حسن (1320-EMP003)
المصروفات (4000)
├── مصروف رواتب وأجور (4100)
│ ├── أحمد محمد (4100-EMP001)
│ ├── خالد علي (4100-EMP002)
│ └── سارة حسن (4100-EMP003)
│
├── مصروف بدلات (4110)
│ ├── أحمد محمد (4110-EMP001)
│ ├── خالد علي (4110-EMP002)
│ └── سارة حسن (4110-EMP003)
│
├── مصروف حوافز ومكافآت (4200)
│ ├── أحمد محمد (4200-EMP001)
│ ├── خالد علي (4200-EMP002)
│ └── سارة حسن (4200-EMP003)
│
└── مصروف تأمينات (حصة الشركة) (4300)
├── أحمد محمد (4300-EMP001)
├── خالد علي (4300-EMP002)
└── سارة حسن (4300-EMP003)
الالتزامات (2000)
├── رواتب مستحقة (2100)
│ ├── أحمد محمد (2100-EMP001)
│ ├── خالد علي (2100-EMP002)
│ └── سارة حسن (2100-EMP003)
│
├── تأمينات مستحقة (2200)
│ ├── أحمد محمد (2200-EMP001)
│ └── ...
│
└── ضرائب مستحقة (2300)
├── أحمد محمد (2300-EMP001)
└── ...
{parent_code}-{employee_number} — مثال: 1300-EMP001{اسم الحساب الرئيسي} — {اسم الموظف} — مثال: مسحوبات — أحمد محمد
| الفائدة | الشرح |
|---|---|
| رصيد مباشر | تفتح حساب أحمد في المسحوبات → تعرف عليه كام فوراً |
| تسوية نهاية خدمة | لما الموظف يمشي → الرصيد المعلق واضح في حسابه |
| تقارير | المدير المالي يشوف شجرة السلف ويعرف كل موظف رصيده |
| مراجعة | المراجع يطابق الحساب الفرعي مع كشف الموظف |
| ميزان المراجعة | الحساب الرئيسي = مجموع الفرعيات تلقائي |
عند الصرف — يتعمل قيد فوراً (مثال: أحمد سحب 1,000 كاش):
| الحساب | مدين | دائن |
|---|---|---|
| مسحوبات — أحمد محمد (1300-EMP001) | 1,000 | |
| الخزينة (1100) | 1,000 |
payroll_adjustment_auto_journal:
true — الافتراضي): القيد يتعمل أوتوماتيك لحظة تسجيل السحب — عملية واحدة من مكان واحد.false): HR يسجل السحب، والمحاسب يعمل القيد منفصل.عند حساب الراتب (آخر الشهر):
السحب يتخصم من الراتب — قيد المسير يقفل حساب الموظف الفرعي:
| الحساب | مدين | دائن |
|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | |
| مسحوبات — أحمد (1300-EMP001) | 1,000 | |
| رواتب مستحقة — أحمد (2100-EMP001) | 7,000 |
← كده حساب مسحوبات أحمد رصيده صفر — السحب اتقفل مع الراتب.
| الحساب | مدين | دائن |
|---|---|---|
| مسحوبات — أحمد محمد (1300-EMP001) | 500 | |
| البنك (1110) | 500 |
عند صرف السلفة:
| الحساب | مدين | دائن |
|---|---|---|
| قروض — أحمد محمد (1310-EMP001) | 6,000 | |
| الخزينة (1100) | 6,000 |
كل شهر (قسط 1,000 يتخصم من الراتب):
| الحساب | مدين | دائن |
|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | |
| قروض — أحمد (1310-EMP001) | 1,000 | |
| رواتب مستحقة — أحمد (2100-EMP001) | 7,000 |
← كده كل شهر رصيد القرض بينقص 1,000 تلقائي لحد ما يخلص.
مفيش قيد فوري — بيتخصم من الراتب آخر الشهر. مثال: أحمد عليه جزاء 300:
| الحساب | مدين | دائن |
|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | |
| ذمم جزاءات — أحمد (1320-EMP001) | 300 | |
| رواتب مستحقة — أحمد (2100-EMP001) | 7,700 |
بيتضاف على الراتب — مثال: أحمد حافز 2,000 مع الراتب:
| الحساب | مدين | دائن |
|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | |
| مصروف حوافز — أحمد (4200-EMP001) | 2,000 | |
| رواتب مستحقة — أحمد (2100-EMP001) | 10,000 |
مثال: أحمد حافز 2,000 كاش من الخزنة:
| الحساب | مدين | دائن |
|---|---|---|
| مصروف حوافز — أحمد (4200-EMP001) | 2,000 | |
| الخزينة (1100) | 2,000 |
لما المسير يتحسب ويترحّل، القيد الكامل بكل التفاصيل:
| الحساب | مدين | دائن | الشرح |
|---|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | الراتب الأساسي | |
| مصروف بدلات — أحمد (4110-EMP001) | 2,500 | سكن 2000 + مواصلات 500 | |
| مصروف حوافز — أحمد (4200-EMP001) | 2,000 | حافز مبيعات | |
| مسحوبات — أحمد (1300-EMP001) | 1,500 | سحب 1000 + سحب 500 (اتصرفوا قبل كده) | |
| قروض — أحمد (1310-EMP001) | 500 | قسط السلفة الشهري | |
| ذمم جزاءات — أحمد (1320-EMP001) | 300 | خصم إتلاف | |
| تأمينات مستحقة — أحمد (2200-EMP001) | 780 | GOSI 9.75% | |
| تأمين طبي مستحق | 200 | تأمين طبي | |
| خصم غياب | 533.33 | غياب يومين | |
| رواتب مستحقة — أحمد (2100-EMP001) | 7,186.67 | ← المبلغ الفعلي المستحق (الصافي - المسحوبات) | |
| إجمالي مدين: 12,500 | إجمالي دائن: 12,500 ✅ | ||
لكل موظف نشط:
// ===== المكاسب =====
earnings = basic_salary // 8,000
// بدلات ثابتة (من هيكل الراتب)
+ housing_allowance (25% × 8000) // +2,000
+ transport_allowance (ثابت) // +500
// ساعات إضافية (من الحضور)
+ overtime (10h × 33.33 × 1.5) // +500
// تعديلات إيجابية (من payroll_adjustments)
+ حافز مبيعات // +2,000
---
total_earnings = 13,000
// ===== الخصومات =====
deductions = 0
// خصومات ثابتة (من هيكل الراتب)
+ GOSI (9.75% × 8000) // +780
+ medical_insurance (ثابت) // +200
// خصم الغياب (من الحضور)
+ absent_deduction (2 × 266.67) // +533.33
// تعديلات سلبية (من payroll_adjustments)
+ سحب تحت حساب 1 // +1,000
+ سحب تحت حساب 2 // +500
+ خصم إتلاف // +300
// أقساط القروض (من loans — تلقائي)
+ قسط سلفة // +500
---
total_deductions = 3,813.33
// ===== الصافي =====
net_salary = total_earnings - total_deductions
= 13,000 - 3,813.33
= 9,186.67
// ===== المستحق الفعلي (بعد خصم اللي اتصرف) =====
already_paid_out = 1,000 + 500 // السحبات اللي اتصرفت فعلاً
remaining_to_pay = net_salary - already_paid_out
= 9,186.67 - 1,500
= 7,686.67 ← ده اللي يتصرف آخر الشهر
| المكاسب | |
|---|---|
| الراتب الأساسي | 8,000.00 |
| بدل سكن (25%) | +2,000.00 |
| بدل مواصلات | +500.00 |
| ساعات إضافية (10h) | +500.00 |
| حافز مبيعات ⭐ | +2,000.00 |
| إجمالي المكاسب | 13,000.00 |
| الخصومات | |
| تأمينات اجتماعية (9.75%) | -780.00 |
| تأمين طبي | -200.00 |
| خصم غياب (2 يوم) | -533.33 |
| سحب تحت حساب (5 أبريل) ⭐ | -1,000.00 |
| سحب تحت حساب (12 أبريل) ⭐ | -500.00 |
| خصم إتلاف ⭐ | -300.00 |
| قسط سلفة | -500.00 |
| إجمالي الخصومات | -3,813.33 |
| صافي الراتب | 9,186.67 |
| الصرف | |
| اتصرف مقدماً (سحبات) | -1,500.00 |
| المستحق الفعلي (يتصرف آخر الشهر) | 7,686.67 |
بدل ما تصرف كل الموظفين مرة واحدة — تقدر تصرف موظف واحد أو مجموعة.
payroll_items:| الحقل | النوع | الشرح |
|---|---|---|
payment_status | enum(unpaid, paid, held) | حالة الصرف لهذا الموظف |
paid_at | timestamp nullable | تاريخ الصرف |
hold_reason | text nullable | سبب الإيقاف (لو held) |
already_paid_out | decimal(12,3) default 0 | المبلغ اللي اتصرف مقدماً (سحبات) |
remaining_to_pay | decimal(12,3) | المستحق الفعلي = net_salary - already_paid_out |
| الحالة | المعنى |
|---|---|
| draft | مسودة — لم يُحسب بعد |
| calculated | محسوب — جاهز للمراجعة |
| approved | معتمد — جاهز للترحيل |
| posted | مرحّل — القيد المحاسبي اتعمل |
| partially_paid | جديد — بعض الموظفين اتصرفوا |
| paid | كل الموظفين اتصرفوا |
| Method | Endpoint | الوظيفة |
|---|---|---|
| POST | /payroll/{id}/pay-item | صرف موظف واحد — body: { payroll_item_id } |
| POST | /payroll/{id}/hold-item | إيقاف صرف موظف — body: { payroll_item_id, reason } |
| POST | /payroll/{id}/pay | صرف كل المتبقي (unpaid) دفعة واحدة |
| Method | Endpoint | الوظيفة |
|---|---|---|
| GET | /payroll/{id}/adjustments | قائمة التعديلات لمسير محدد |
| POST | /payroll/{id}/adjustments | إضافة تعديل (خصم/إضافة) — body: { employee_id, type, category, amount, description, payment_method, cash_account_id } |
| PUT | /payroll/{id}/adjustments/{adj_id} | تعديل |
| DELETE | /payroll/{id}/adjustments/{adj_id} | حذف (قبل الحساب فقط) |
| POST | /payroll/{id}/adjustments/{adj_id}/pay-out | صرف المبلغ فعلياً (يعمل قيد محاسبي) |
عند POST /payroll/{id}/calculate:
payroll_adjustments لهذا المسيرalready_paid_out (مجموع التعديلات اللي is_paid_out = true)remaining_to_pay = net_salary - already_paid_out| المكان | branch_id موجود؟ | التفاصيل |
|---|---|---|
| الموظف (employees) | نعم ✅ | branch_id موجود ومطلوب (required) |
| الأقسام (departments) | نعم ✅ | branch_id على كل قسم |
| المسير (payrolls) | لا ❌ | مفيش branch_id — بيجيب كل الموظفين |
| الحضور (attendance) | نعم ✅ | مرتبط بالموظف اللي عنده فرع |
branch_id على جدول payrolls| الحقل | النوع | الشرح |
|---|---|---|
branch_id | FK → branches, nullable | null = كل الفروع (مسير شامل)قيمة محددة = فرع واحد فقط |
PayrollService::calculate()
// الحالي (خطأ):
$employees = Employee::where('company_id', $companyId)
->active()->get();
// المطلوب:
$query = Employee::where('company_id', $companyId)->active();
if ($payroll->branch_id) {
$query->where('branch_id', $payroll->branch_id);
}
$employees = $query->get();
| الشاشة | الفلتر |
|---|---|
| قائمة المسيرات | filter by branch_id — المستخدم يشوف بس مسيرات فرعه (أو الكل لو super admin) |
| إنشاء مسير | اختيار الفرع (أو "كل الفروع") عند الإنشاء |
| التقارير | تقرير الرواتب يدعم فلتر بالفرع |
حالياً: unique(company_id, month, year) — يعني مسير واحد بس في الشهر.
المطلوب: unique(company_id, branch_id, month, year) — مسير لكل فرع في الشهر.
branch_id = null (مسير شامل) يظل unique بـ (company_id, NULL, month, year).يعمل مسير بدون اختيار فرع (branch_id = null) → كل الموظفين يتحسبوا مع بعض. نفس السلوك الحالي.
branch_id = 1branch_id = 2branch_id = 3مدير فرع القاهرة يدخل يشوف بس مسيرات فرعه — مش بيشوف الإسكندرية أو أسوان.
Super admin يشوف كل المسيرات من كل الفروع.
لما المسير يترحّل — القيد المحاسبي يكون مرتبط بنفس الفرع:
| الحساب | مدين | دائن | الفرع |
|---|---|---|---|
| مصروف رواتب — أحمد (4100-EMP001) | 8,000 | القاهرة | |
| مصروف رواتب — خالد (4100-EMP002) | 6,000 | القاهرة | |
| رواتب مستحقة — أحمد (2100-EMP001) | 8,000 | القاهرة | |
| رواتب مستحقة — خالد (2100-EMP002) | 6,000 | القاهرة |
كده التقارير المالية تقدر تفصل مصروفات الرواتب بالفرع.
| # | المهمة | الأولوية |
|---|---|---|
| 1 | جدول payroll_adjustments + migration | عاجل |
| 2 | CRUD APIs للتعديلات + pay-out endpoint | عاجل |
| 3 | تعديل PayrollService::calculate() ليشمل التعديلات + already_paid_out | عاجل |
| 4 | حقول payment_status + paid_at + hold_reason + remaining_to_pay على payroll_items | عالي |
| 5 | endpoints: pay-item + hold-item + حالة partially_paid | عالي |
| 6 | PayrollItemResource يشمل details + adjustments | عالي |
| 7 | ⭐ قيد محاسبي تلقائي عند صرف السحب (pay-out) — دي العملية الأساسية. يدعم الوضعين: تلقائي (قيد فوري مع السحب) أو يدوي (المحاسب يعمله) | عاجل |
| 8 | إعدادات: payroll_adjustment_approval_required + payroll_adjustment_auto_journal + payroll_adjustment_approval_roles | عالي |
| 9 | حسابات فرعية لكل موظف: إنشاء تلقائي لحساب فرعي تحت (1300 مسحوبات، 1310 قروض، 1320 جزاءات، 4100 رواتب، 4110 بدلات، 4200 حوافز، 2100 مستحقات) عند أول عملية — كود: {parent}-{emp_number} | عاجل |
| 10 | قيد المسير يستخدم الحسابات الفرعية لكل موظف (مش حساب عام واحد) | عاجل |
| 11 | دعم الفروع في المسير: إضافة branch_id (nullable FK) على payrolls + تعديل calculate() ليفلتر بالفرع + تعديل unique constraint لـ (company_id, branch_id, month, year) + فلتر المسيرات بفرع المستخدم + القيد المحاسبي يكون مرتبط بالفرع | عاجل |
| # | المهمة | الأولوية |
|---|---|---|
| 1 | شاشة تعديلات المسير (إضافة/حذف خصومات وإضافات) | عاجل |
| 2 | تفاصيل كشف الراتب (مكاسب + خصومات بالاسم) | عالي |
| 3 | القبض الجزئي (صرف/إيقاف لكل موظف) | عالي |
| 4 | طباعة كشف الراتب | متوسط |
| 5 | فلتر الفرع في شاشة المسير (إنشاء مسير لفرع محدد + فلتر القائمة بالفرع) | عالي |