كل الإعدادات والسيناريوهات والاستثناءات — للباك إند والفرونت إند
استثناء للسيدات المرضعات — سماح إضافي في الحضور.
على الموظف: حقل is_nursing: boolean + nursing_start_date: date
موظفة مرضعة، وردية 8:00، سماح عادي 60 + رضاعة 60 = 120 دقيقة:
على الموظف: حقل is_special_needs: boolean + special_needs_type: varchar
بصمات إضافية أثناء الدوام للتأكد إن الموظف موجود في مكان العمل.
وردية 8:00 - 16:00، إثبات تواجد كل ساعتين:
لو فوّت بصمة إثبات → يتسجل تنبيه (مش تأخير — بس notification للمدير)
status: 6 (punch type: presence_verification)كل وردية ليها إعداداتها الخاصة — تطغى على الإعدادات العامة.
كل الإعدادات بقت على مستوى الوردية — مش الشركة. كل وردية ليها قواعدها الخاصة.
| الحقل | الحالة | الشرح |
|---|---|---|
| الحقول الموجودة | ||
| name_ar / name_en | موجود | اسم الوردية |
| start_time / end_time | موجود | وقت البداية والنهاية |
| working_hours | موجود | ساعات العمل (محسوبة) |
| break_duration_minutes | موجود | مدة الاستراحة |
| grace_period_minutes | موجود | فترة السماح (حالياً 15 — المفروض 60) |
| is_night_shift | موجود | وردية ليلية |
| حقول جديدة — إعدادات الحضور (على مستوى الوردية) | ||
| late_threshold_minutes | جديد | سماح التأخير بالدقائق (مثال: 60) |
| late_counts_full | جديد | bool — لو true يحسب كل الوقت تأخير |
| max_late_before_absent | جديد | الحد الأقصى للتأخير قبل الغياب (مثال: 180 دقيقة) |
| حقول جديدة — إعدادات الانصراف | ||
| early_leave_threshold_minutes | جديد | سماح الانصراف المبكر بالدقائق (مثال: 30) |
| early_leave_counts_full | جديد | bool — لو true يحسب كل الوقت انصراف مبكر |
| حقول جديدة — المرونة والورديات المتعددة | ||
| flexibility_minutes | جديد | مرونة الوقت (0 = بدون مرونة، 120 = ساعتين) |
| hours_per_day | جديد | كام ساعة تحسب يوم واحد (افتراضي 6، ممكن 8 أو أي رقم) |
| equivalent_days | جديد — محسوب تلقائي | = working_hours ÷ hours_per_day (مثال: 24h ÷ 6h = 4 أيام) |
| deduction_base_hours | جديد — = hours_per_day | الساعات اللي يُخصم منها التأخير (= يوم واحد) |
| حقول جديدة — استثناءات | ||
| nursing_extra_minutes | جديد | سماح إضافي للرضاعة (0 = بدون، 60 = ساعة) |
| special_needs_extra_minutes | جديد | سماح إضافي لذوي الهمم (0 = بدون) |
| حقول جديدة — إثبات التواجد | ||
| presence_check_enabled | جديد | bool — هل مطلوب بصمة تواجد أثناء الدوام |
| presence_check_interval | جديد | كل كام دقيقة (مثال: 120) |
وردية 8:00 - 16:00، مرونة 120 دقيقة (ساعتين):
| مدة الوردية | hours_per_day | equivalent_days | الخصم من |
|---|---|---|---|
| 6 ساعات | 6 | 6÷6 = 1 يوم | أول 6h |
| 12 ساعة | 6 | 12÷6 = 2 يوم | أول 6h |
| 18 ساعة | 6 | 18÷6 = 3 أيام | أول 6h |
| 24 ساعة | 6 | 24÷6 = 4 أيام | أول 6h |
| 36 ساعة | 6 | 36÷6 = 6 أيام | أول 6h |
| مدة الوردية | hours_per_day | equivalent_days | الخصم من |
|---|---|---|---|
| 8 ساعات | 8 | 8÷8 = 1 يوم | أول 8h |
| 12 ساعة | 8 | 12÷8 = 1.5 يوم | أول 8h |
| 16 ساعة | 8 | 16÷8 = 2 يوم | أول 8h |
| 24 ساعة | 8 | 24÷8 = 3 أيام | أول 8h |
| 36 ساعة | 8 | 36÷8 = 4.5 أيام | أول 8h |
وردية من 8:00 صباح السبت لـ 8:00 صباح الأحد (24 ساعة = 4 أيام):
لو الموظف تأخر أكتر من حد معين، يتحول من "متأخر" لـ "غائب".
| الوردية | التأخير | النتيجة |
|---|---|---|
| 6 ساعات (يوم واحد) | > 180 دقيقة | غائب يوم كامل |
| 12 ساعة (يومين) | 61 - 180 دقيقة | متأخر — يخصم من أول 6h فقط. اليوم الثاني عادي. |
| 181 - 360 دقيقة (3-6 ساعات) | اليوم الأول غائب كامل — اليوم الثاني يحسب عادي | |
| > 360 دقيقة (> 6 ساعات) | اليوم الأول غائب + اليوم الثاني متأخر | |
| 24 ساعة (4 أيام) | 181 - 360 دقيقة | اليوم الأول غائب — باقي 3 أيام عادي |
| > 360 دقيقة | اليوم الأول غائب + التأخير يتوزع على الأيام التالية |
مثال 1 — وردية 6 ساعات (عادية):
مثال 2 — وردية 12 ساعة (يومين):
مثال 3 — وردية 24 ساعة (4 أيام):
وردية تنتهي 4:00 مساءً، سماح 30 دقيقة:
مع المرونة: لو الوردية عندها flexibility — وقت الانصراف = check_in + working_hours
function calculateCheckIn(employee, shift, checkInTime):
// 1. حساب السماح الإجمالي (كل الإعدادات من الوردية)
grace = shift.late_threshold_minutes // 60 (من الوردية مباشرة)
if (shift.flexibility_minutes > 0):
grace = shift.flexibility_minutes // المرونة تحل محل السماح
if (employee.is_nursing):
grace += shift.nursing_extra_minutes // +60 (من الوردية)
if (employee.is_special_needs):
grace += shift.special_needs_extra_minutes // +60 (من الوردية)
// 2. حساب التأخير
diff = checkInTime - shift.start_time // بالدقائق
max_late = shift.max_late_before_absent // 180 (من الوردية)
if (diff <= grace):
// ✅ حاضر — في حدود السماح
status = PRESENT
late_minutes = 0
absent_days = 0
else if (diff <= max_late):
// ⚠️ متأخر — تجاوز السماح بس لسه في حدود التأخير
status = LATE
if (shift.late_counts_full):
late_minutes = diff // كل الوقت
else:
late_minutes = diff - grace
absent_days = 0
else:
// ❌ غائب — تجاوز حد التأخير الأقصى
status = ABSENT
late_minutes = 0 // مش متأخر — غائب
// للورديات متعددة الأيام:
if (shift.equivalent_days > 1):
absent_days = 1 // اليوم الأول بس غائب
remaining_days = shift.equivalent_days - 1 // باقي الأيام حاضر
// لو التأخير > 6 ساعات (deduction_base_hours):
if (diff > shift.deduction_base_hours * 60):
// التأخير يتوزع على اليوم التالي
extra_late = diff - (shift.deduction_base_hours * 60)
// اليوم الثاني = متأخر بالزيادة
else:
absent_days = 1 // وردية عادية — يوم واحد غائب
// 3. حساب الانصراف المتوقع (لو مرونة)
if (shift.flexibility_minutes > 0 && diff > 0 && diff <= shift.flexibility_minutes):
expected_checkout = checkInTime + shift.working_hours
else:
expected_checkout = shift.end_time
return { status, late_minutes, absent_days, expected_checkout }
function calculateCheckOut(attendance, shift, checkOutTime):
// 1. حساب سماح الانصراف (من الوردية)
early_grace = shift.early_leave_threshold_minutes // 30
if (employee.is_special_needs):
early_grace += shift.special_needs_extra_minutes // +60
// 2. حساب الانصراف المتوقع
expected_end = attendance.expected_checkout ?? shift.end_time
// 3. حساب الانصراف المبكر
early_diff = expected_end - checkOutTime // بالدقائق
if (early_diff <= early_grace):
early_leave_minutes = 0
else:
if (shift.early_leave_counts_full):
early_leave_minutes = early_diff
else:
early_leave_minutes = early_diff - early_grace
// 4. حساب ساعات العمل
worked_hours = checkOutTime - attendance.check_in
overtime = max(0, worked_hours - shift.working_hours)
// 5. حساب أيام الحضور
hours_per_day = shift.hours_per_day ?? 6
equivalent_days = ceil(shift.working_hours / hours_per_day) // محسوب تلقائي
return { worked_hours, overtime, early_leave_minutes, equivalent_days }
| المفتاح | النوع | القيمة |
|---|---|---|
hrm.late_threshold_minutes | int | 60 |
hrm.late_counts_full | bool | true |
hrm.early_leave_threshold_minutes | int | 30 |
hrm.early_leave_counts_full | bool | true |
hrm.nursing_enabled | bool | true |
hrm.nursing_extra_minutes | int | 60 |
hrm.nursing_max_child_age_months | int | 24 |
hrm.special_needs_enabled | bool | true |
hrm.special_needs_extra_minutes | int | 60 |
hrm.special_needs_early_leave_extra | int | 30 |
hrm.presence_verification_enabled | bool | false |
hrm.presence_verification_interval | int | 120 |
hrm.presence_verification_tolerance | int | 15 |
hrm.max_late_before_absent_minutes | int | 180 |
hrm.absent_deducts_full_day | bool | true |
employees:| الحقل | النوع |
|---|---|
is_nursing | boolean default false |
nursing_start_date | date nullable |
is_special_needs | boolean default false |
special_needs_type | varchar(100) nullable |
shifts (كل الإعدادات على مستوى الوردية):| الحقل | النوع | الشرح |
|---|---|---|
| إعدادات الحضور | ||
late_threshold_minutes | int default 60 | سماح التأخير |
late_counts_full | bool default true | احتساب كامل |
max_late_before_absent | int default 180 | حد الغياب |
| إعدادات الانصراف | ||
early_leave_threshold_minutes | int default 30 | سماح الانصراف المبكر |
early_leave_counts_full | bool default true | احتساب كامل |
| المرونة والأيام | ||
flexibility_minutes | int default 0 | مرونة الوقت |
hours_per_day | decimal(4,1) default 6 | كام ساعة = يوم واحد |
equivalent_days | decimal(4,1) — محسوب | = working_hours ÷ hours_per_day |
deduction_base_hours | decimal(4,1) — = hours_per_day | ساعات الخصم = يوم واحد |
| استثناءات | ||
nursing_extra_minutes | int default 60 | سماح الرضاعة |
special_needs_extra_minutes | int default 60 | سماح ذوي الهمم |
| إثبات التواجد | ||
presence_check_enabled | bool default false | تفعيل بصمة التواجد |
presence_check_interval | int default 120 | كل كام دقيقة |
attendances:| الحقل | النوع |
|---|---|
expected_checkout | time nullable (لو مرونة: check_in + working_hours) |
equivalent_days | int default 1 (من الوردية) |
AttendanceResource:إضافة في الـ response: device_name, punch_type, expected_checkout, equivalent_days, early_leave_minutes
AttendanceService::checkIn() و checkOut()حسب الـ Algorithm في القسم 7 فوق.