الكتابات/tutorial/2026/08
Tutorial26 أغسطس 2026·23 دقيقة

محرك فترة التجربة بـ TypeScript — المادة 53 من نظام العمل

ابنِ فرع فترة التجربة في نظام الموارد البشرية السعودي بلغة TypeScript، تنفيذًا للمادتين 53 و54 من نظام العمل — شرط الكتابة الذي يحدّد ما إذا كان الإنهاء بلا مقابل أو بأجر شهرين، وسقف المائة والثمانين يومًا في جميع الأحوال، والمدة التي تتوقّف عند إجازتي العيدين والإجازة المرضية، وفحص المادة 54 عند إعادة التوظيف الذي لا يستطيع جدول العقود الإجابة عنه.

كل نظام موارد بشرية يخزّن فترة تجربة. وقليل منها يستطيع الإجابة عن السؤال الوحيد المهم فيها: في اليوم الذي تُنهي فيه عقد هذا العامل، هل ما زالت فترة التجربة سارية؟

الخطأ هنا ليس فرقًا في التقريب. داخل فترة تجربة صحيحة، لكل طرف أن ينهي العقد دون إشعار ودون تعويض ودون مكافأة نهاية خدمة. وبعدها بيوم واحد، يُسعَّر الإنهاء نفسه وفق المادة 77، التي تحمل حدًّا أدنى قدره أجر شهرين مهما قصرت مدة الخدمة. الفرق بين الفرعين، لعامل أجره 12,000 ريال، هو 24,000 ريال — يقرّره بالكامل حسابُ تاريخٍ تخطئ فيه معظم الأنظمة.

وتخطئ فيه لأن مدة التجربة ليست تقويمًا. المادة 53 توقف احتسابها في إجازتي عيد الفطر وعيد الأضحى وفي الإجازة المرضية. فتاريخ المباشرة زائد تسعين يومًا ليس هو تاريخ انتهاء التجربة، وهو ما يحسبه تقريبًا كل تطبيق.

المتطلبات المسبقة

قبل أن تبدأ، تأكّد من توفّر:

  • Node.js 20 أو أحدث و TypeScript 5.4 أو أحدث
  • إلمام بالأنواع المميّزة (discriminated unions) وبعبارات switch الشاملة
  • تصوّر عام لفروع الإنهاء في نظام العمل السعودي — يغطّي محرك تسوية إنهاء الخدمة المواد من 75 إلى 81، وهو الفرع الذي يسلّم إليه هذا المحرك
  • لا حاجة إلى مكتبة تواريخ. نستخدم Date المجرّدة بتوقيت UTC ونعدّ الأيام بأنفسنا، لأن القاعدة النظامية عدد أيام لا مدة زمنية

ما الذي ستبنيه

دالّة صِرفة تأخذ عقد عمل وشرط التجربة فيه وتاريخ العامل مع صاحب العمل نفسه، وتُعيد قرارًا:

  • هل شرط التجربة صحيح أصلًا
  • مدة التجربة الفعلية بعد تطبيق السقوف النظامية
  • تاريخ انتهاء التجربة الحقيقي، مع إيقاف الاحتساب في الإجازات التي يستثنيها النظام
  • لتاريخ إنهاء محدّد: أي فرع يُسعّر الخروج — المادة 53 أم المادة 77
  • قائمة أسباب مقروءة لكل إجابة من هذه الإجابات

لا تواريخ مضمّنة في الشيفرة. وهذا يتبيّن أنه أهم قرار تصميمي في المحرك كله، والخطوة الرابعة تشرح السبب.

الخطوة 1: الشرط هو البوابة، وعنده تضيع النقود عادةً

تشترط المادة 53 النصّ على فترة التجربة صراحةً في عقد العمل، وتحديد مدتها بوضوح. وهذا ليس إجراءً ورقيًّا. فإن كان الشرط غائبًا أو غامضًا أو غير موثّق، عُدّ العقد نهائيًّا من يومه الأول — ويتحوّل الإنهاء الذي ظنّته الإدارة خروجًا مجانيًّا في التجربة إلى فصل وفق المادة 77 ومعه حدّ الشهرين.

فأول مهمة للمحرك ليست حسابية. إنها بوابة صحّة.

/** ISO calendar date, 'YYYY-MM-DD'. Dates here are days, never instants. */
type IsoDate = string;
 
/**
 * One agreed stretch of probation. Article 53 permits the period to be extended,
 * so a clause is a list of tranches, not a single number.
 */
type ProbationTranche = {
  days: number;
  /** Article 53 requires extensions to rest on a written agreement. */
  agreedInWriting: boolean;
};
 
type ProbationClause = {
  /** Article 53: stated expressly in the contract, or the contract is final. */
  statedInContract: boolean;
  /** Duration must be clearly defined — an open-ended clause is not a clause. */
  durationClearlyDefined: boolean;
  tranches: ProbationTranche[];
};

لاحظ ما ليس موجودًا في هذا النوع: حقل منطقي باسم isOnProbation. هذا الحقل هو العلّة. يُضبط عند التعيين، ولا يُعاد حسابه أبدًا، ويفارق العقد في اللحظة التي يقع فيها أي حدث ذي أثر.

الخطوة 2: سقفان، واحد منهما فقط مطلق

تضع المادة 53 فترة تجربة أساسها تسعون يومًا، قابلة للتمديد باتفاق مكتوب بين الطرفين، على ألّا يزيد المجموع في جميع الأحوال على مائة وثمانين يومًا.

اقرأ نتائج البحث في هذه المسألة تجد مكاتب محاماة تجزم بسقف مطلق قدره 180 يومًا وأخرى تجزم بتسعين. وكلاهما يصف القاعدة نفسها من جهة مختلفة: التسعون هي ما قد تبلغه فترة واحدة بذاتها، والمائة والثمانون هي السقف بعد احتساب التمديدات المكتوبة. السقف هو الثابت. فرمّزه بوصفه كذلك.

/** Article 53 — a single agreed stretch, before any written extension. */
const SINGLE_TRANCHE_CAP_DAYS = 90;
 
/** Article 53 — the absolute total, in all circumstances. This one never bends. */
const ABSOLUTE_CAP_DAYS = 180;

القراءة المتحفّظة، وهي التي تُشحن: الأيام الزائدة باطلة، لكنها لا تُفسد الشرط كله. فعقد يكتب 200 يوم تجربة لديه فترة تجربة صحيحة مدتها 180 يومًا و20 يومًا من العمل المعتاد، لا شرط باطل. البتر آمن، أما إبطال الشرط نيابةً عن صاحب العمل فليس كذلك.

type ClauseAssessment = {
  valid: boolean;
  effectiveDays: number;
  reasons: string[];
};
 
function assessClause(clause: ProbationClause): ClauseAssessment {
  const reasons: string[] = [];
 
  if (!clause.statedInContract) {
    return {
      valid: false,
      effectiveDays: 0,
      reasons: ['probation not stated expressly in the contract (Art. 53) — contract is final'],
    };
  }
 
  if (!clause.durationClearlyDefined) {
    return {
      valid: false,
      effectiveDays: 0,
      reasons: ['probation duration not clearly defined (Art. 53) — contract is final'],
    };
  }
 
  if (clause.tranches.length === 0) {
    return {
      valid: false,
      effectiveDays: 0,
      reasons: ['probation clause carries no duration'],
    };
  }
 
  // The first tranche stands on the contract. Every later one needs writing.
  let counted = 0;
  clause.tranches.forEach((tranche, index) => {
    if (index > 0 && !tranche.agreedInWriting) {
      reasons.push(`extension ${index} not agreed in writing (Art. 53) — not counted`);
      return;
    }
    if (index === 0 && tranche.days > SINGLE_TRANCHE_CAP_DAYS) {
      reasons.push(
        `initial probation of ${tranche.days} days exceeds the ${SINGLE_TRANCHE_CAP_DAYS}-day base ` +
          'and is only sustainable as a written extension',
      );
    }
    counted += tranche.days;
  });
 
  const effectiveDays = Math.min(counted, ABSOLUTE_CAP_DAYS);
  if (counted > ABSOLUTE_CAP_DAYS) {
    reasons.push(
      `total probation of ${counted} days truncated to the Art. 53 ceiling of ${ABSOLUTE_CAP_DAYS}`,
    );
  }
 
  return { valid: effectiveDays > 0, effectiveDays, reasons };
}

الخطوة 3: المادة 54 — الفحص الذي لا يستطيع جدول العقود إجراءه

تمنع المادة 54 وضع العامل تحت التجربة أكثر من مرة واحدة لدى صاحب عمل واحد. وللقاعدة استثناءان اثنان لا ثالث لهما، وكلاهما يقوم على وقائع تقع خارج سجلّ العقد الحالي:

  1. أن يكون العامل في مهنة أو عمل آخر، أو
  2. أن تكون قد مضت ستة أشهر على الأقل على انتهاء علاقة العمل السابقة مع ذلك صاحب العمل.

هذه هي القاعدة التي تُعطب حالات إعادة التوظيف بصمت. متعاقد عائد، أو عامل موسمي، أو موظف انتقل بين كيانين تحت سجل تجاري واحد — كلٌّ منهم يمرّ بالتعيين من جديد، ويحصل على عقد جديد بشرط تجربة جديد، ولا أحد يتحقّق. فتكون التجربة الثانية باطلة، ويُسعَّر الخروج الذي كان يُفترض أنه مجاني وفق المادة 77.

type PriorEngagement = {
  profession: string;
  endedOn: IsoDate;
  hadProbation: boolean;
};
 
const SIX_MONTHS_IN_DAYS = 180;
 
function daysBetween(from: IsoDate, to: IsoDate): number {
  const MS_PER_DAY = 86_400_000;
  return Math.round((Date.parse(`${to}T00:00:00Z`) - Date.parse(`${from}T00:00:00Z`)) / MS_PER_DAY);
}
 
/**
 * Article 54 — may this worker lawfully be placed under probation again?
 * Only the prior engagements that actually carried probation are relevant.
 */
function repeatProbationAllowed(
  newProfession: string,
  startDate: IsoDate,
  history: PriorEngagement[],
): { allowed: boolean; reason: string } {
  const priorProbations = history.filter((h) => h.hadProbation);
  if (priorProbations.length === 0) {
    return { allowed: true, reason: 'no prior probation with this employer' };
  }
 
  if (priorProbations.every((h) => h.profession !== newProfession)) {
    return { allowed: true, reason: 'different profession from every prior engagement (Art. 54)' };
  }
 
  const mostRecentEnd = priorProbations
    .map((h) => h.endedOn)
    .sort()
    .at(-1) as IsoDate;
 
  const gap = daysBetween(mostRecentEnd, startDate);
  if (gap >= SIX_MONTHS_IN_DAYS) {
    return { allowed: true, reason: `${gap} days since the previous engagement ended (Art. 54)` };
  }
 
  return {
    allowed: false,
    reason:
      `same profession and only ${gap} days since the previous engagement — ` +
      'a second probation is not permitted (Art. 54)',
  };
}

تفصيلان يستحقّان الوقوف. المقارنة تجري مع أحدث ارتباط سابق لا مع أوّلها — وترتيب سلاسل التواريخ بصيغة ISO آمن هنا لأنها ISO تحديدًا. وحقل hadProbation مهم: ارتباط سابق لم يحمل تجربة قط لا يستهلك المرّة الواحدة المسموح بها.

الخطوة 4: الاحتساب ليس تقويمًا

هنا القاعدة التي يُغفلها تقريبًا كل تطبيق. تستثني المادة 53 من حساب فترة التجربة إجازتي عيد الفطر وعيد الأضحى وأي إجازة مرضية. فالاحتساب لا يمضي خلالها — بل يتوقّف ويستأنف عند عودة العامل. عشرة أيام من إجازة مرضية موثّقة داخل تجربة مدتها تسعون يومًا تدفع تاريخ الانتهاء عشرة أيام إلى الأمام.

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

type PauseKind = 'eid-al-fitr' | 'eid-al-adha' | 'sick-leave';
 
/** A stretch of days that does not count toward probation. Both ends inclusive. */
type PausePeriod = {
  kind: PauseKind;
  from: IsoDate;
  to: IsoDate;
};
 
function eachDay(from: IsoDate, to: IsoDate): IsoDate[] {
  const out: IsoDate[] = [];
  const cursor = new Date(`${from}T00:00:00Z`);
  const end = new Date(`${to}T00:00:00Z`);
  while (cursor <= end) {
    out.push(cursor.toISOString().slice(0, 10));
    cursor.setUTCDate(cursor.getUTCDate() + 1);
  }
  return out;
}
 
/**
 * Walk forward from the start date, counting only days the statute counts,
 * until `effectiveDays` of probation have actually elapsed.
 *
 * Deliberately a day walk rather than date arithmetic: pauses overlap
 * (sick leave across Eid is one excluded day, not two) and the only honest
 * way to handle overlap is a set of excluded days.
 */
function probationEndDate(
  startDate: IsoDate,
  effectiveDays: number,
  pauses: PausePeriod[],
): IsoDate {
  const excluded = new Set<IsoDate>();
  for (const pause of pauses) {
    for (const day of eachDay(pause.from, pause.to)) excluded.add(day);
  }
 
  let counted = 0;
  const cursor = new Date(`${startDate}T00:00:00Z`);
  // Hard stop well past any lawful probation, so a pathological pause set
  // cannot spin forever.
  const MAX_ELAPSED_DAYS = ABSOLUTE_CAP_DAYS * 4;
 
  for (let elapsed = 0; elapsed < MAX_ELAPSED_DAYS; elapsed++) {
    const day = cursor.toISOString().slice(0, 10);
    if (!excluded.has(day)) counted += 1;
    if (counted === effectiveDays) return day;
    cursor.setUTCDate(cursor.getUTCDate() + 1);
  }
 
  throw new Error('probation end date not reached — check the pause periods for an open range');
}

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

الخطوة 5: قرار واحد، مُسعَّر من الجهتين

الآن جمّع القطع. يأخذ المحرك تاريخ إنهاء ويقول أي فرع من النظام يُسعّره.

type Contract = {
  workerId: string;
  profession: string;
  startDate: IsoDate;
  clause: ProbationClause;
};
 
type ExitBranch =
  | {
      branch: 'probation';
      /** Article 53 — the termination carries none of these. */
      noticeDays: 0;
      compensationOwed: false;
      endOfServiceAward: false;
      probationEndsOn: IsoDate;
      reasons: string[];
    }
  | {
      branch: 'article-77';
      /** Priced by the termination settlement engine, with its two-month floor. */
      probationEndsOn: IsoDate | null;
      reasons: string[];
    };
 
function classifyExit(
  contract: Contract,
  terminationDate: IsoDate,
  pauses: PausePeriod[],
  history: PriorEngagement[],
): ExitBranch {
  const reasons: string[] = [];
 
  const repeat = repeatProbationAllowed(contract.profession, contract.startDate, history);
  reasons.push(repeat.reason);
  if (!repeat.allowed) {
    return { branch: 'article-77', probationEndsOn: null, reasons };
  }
 
  const assessment = assessClause(contract.clause);
  reasons.push(...assessment.reasons);
  if (!assessment.valid) {
    return { branch: 'article-77', probationEndsOn: null, reasons };
  }
 
  const relevant = pauses.filter((p) => p.from >= contract.startDate);
  const endsOn = probationEndDate(contract.startDate, assessment.effectiveDays, relevant);
  reasons.push(
    `probation of ${assessment.effectiveDays} counted days runs to ${endsOn} ` +
      `(${pauses.length} excluded period(s) applied)`,
  );
 
  if (terminationDate <= endsOn) {
    return {
      branch: 'probation',
      noticeDays: 0,
      compensationOwed: false,
      endOfServiceAward: false,
      probationEndsOn: endsOn,
      reasons,
    };
  }
 
  reasons.push(`termination on ${terminationDate} falls after probation ended on ${endsOn}`);
  return { branch: 'article-77', probationEndsOn: endsOn, reasons };
}

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

وثمّة أمر لا تفعله هذه الدالّة عمدًا: لا تُنقص مدة خدمة العامل. فترة التجربة تُحتسب ضمن مدة الخدمة لأغراض مكافأة نهاية الخدمة إن استمر العامل بعدها. الذي تنفيه المادة 53 هو المكافأة عند إنهاء يقع أثناء التجربة — لا الأيام نفسها. والنظام الذي يطرح التجربة من مدة الخدمة يبخس كل مكافأة يحسبها.

الخطوة 6: اختبار التنفيذ

الحالات المهمة هي الحدود وعيوب الشرط، لا المسار السعيد.

import test from 'node:test';
import assert from 'node:assert/strict';
 
const baseClause: ProbationClause = {
  statedInContract: true,
  durationClearlyDefined: true,
  tranches: [{ days: 90, agreedInWriting: false }],
};
 
const contract: Contract = {
  workerId: 'W-1',
  profession: 'accountant',
  startDate: '2026-01-01',
  clause: baseClause,
};
 
test('an unwritten probation clause routes the exit to Article 77', () => {
  const c = { ...contract, clause: { ...baseClause, statedInContract: false } };
  const exit = classifyExit(c, '2026-02-01', [], []);
  assert.equal(exit.branch, 'article-77');
});
 
test('the last probation day is still inside probation', () => {
  // 90 counted days from 2026-01-01 inclusive lands on 2026-03-31.
  const inside = classifyExit(contract, '2026-03-31', [], []);
  assert.equal(inside.branch, 'probation');
  const outside = classifyExit(contract, '2026-04-01', [], []);
  assert.equal(outside.branch, 'article-77');
});
 
test('sick leave pauses the clock and pushes the end date out', () => {
  const pauses: PausePeriod[] = [
    { kind: 'sick-leave', from: '2026-02-10', to: '2026-02-19' }, // 10 days
  ];
  const exit = classifyExit(contract, '2026-04-01', pauses, []);
  assert.equal(exit.branch, 'probation');
  assert.equal(exit.probationEndsOn, '2026-04-10');
});
 
test('overlapping pauses are counted once', () => {
  const pauses: PausePeriod[] = [
    { kind: 'eid-al-fitr', from: '2026-03-19', to: '2026-03-22' },
    { kind: 'sick-leave', from: '2026-03-20', to: '2026-03-23' },
  ];
  // Union is 2026-03-19..2026-03-23 — five days, not eight.
  const exit = classifyExit(contract, '2026-04-05', pauses, []);
  assert.equal(exit.probationEndsOn, '2026-04-05');
});
 
test('the 180-day ceiling truncates rather than voiding the clause', () => {
  const c: Contract = {
    ...contract,
    clause: {
      statedInContract: true,
      durationClearlyDefined: true,
      tranches: [
        { days: 90, agreedInWriting: false },
        { days: 150, agreedInWriting: true },
      ],
    },
  };
  const exit = classifyExit(c, '2026-06-29', [], []);
  assert.equal(exit.branch, 'probation'); // day 180
  assert.equal(classifyExit(c, '2026-06-30', [], []).branch, 'article-77');
});
 
test('a rehire into the same profession inside six months cannot be re-probated', () => {
  const history: PriorEngagement[] = [
    { profession: 'accountant', endedOn: '2025-10-01', hadProbation: true },
  ];
  assert.equal(classifyExit(contract, '2026-02-01', [], history).branch, 'article-77');
});
 
test('a rehire into a different profession may be re-probated', () => {
  const history: PriorEngagement[] = [
    { profession: 'driver', endedOn: '2025-12-01', hadProbation: true },
  ];
  assert.equal(classifyExit(contract, '2026-02-01', [], history).branch, 'probation');
});

نفّذ بالأمر node --test --experimental-strip-types. واختبار التداخل هو الذي يمسك الانحدار الذي ستُدخله تحسينات لاحقة، حين يستبدل أحدهم المشي اليومي بجمع أطوال فترات التوقّف لأنه أسرع.

حل المشكلات

تاريخ انتهاء التجربة يزيد أو ينقص يومًا. يكاد يكون دائمًا خطأ شمول أو استبعاد لتاريخ البداية. اليوم الأول من التجربة هو تاريخ المباشرة نفسه، فتسعون يومًا محتسبة من الأول من يناير تنتهي في 31 مارس لا في الأول من أبريل. والحلقة أعلاه تُعيد اليوم الذي يبلغ عنده العدّ قيمة effectiveDays، وهو الانتهاء الشامل.

التجربة تبدو أطول من 180 يومًا. هذا صحيح ومتوقّع متى طُبّقت فترات التوقّف. فالسقف في المادة 53 على الأيام المحتسبة لا على أيام التقويم المنقضية. والعامل الذي أخذ ثلاثة أسابيع إجازة مرضية داخل تجربة مدتها 180 يومًا يكون نظاميًّا تحت التجربة 201 يومًا تقويميًّا.

تواريخ العيد تنزاح من سنة إلى أخرى. ومن المفترض أن تفعل. فإن كانت نوافذ العيد لديك مأخوذة من جدول مضمَّن وُلِّد مرة واحدة، فهي خاطئة لكل سنة بعد السنة التي وُلِّد فيها. حمّلها من المصدر نفسه الذي تستخدمه رواتبك للعطل الرسمية واحفظ إصداراتها، حتى يعيد حسابُ تاريخ التجربة في نزاعٍ ما القيمةَ نفسها التي استعملها النظام حينها.

مكافأة نهاية خدمة انخفضت عند إضافة التجربة. ثمّة ما يطرح فترة التجربة من مدة الخدمة. التجربة تُحتسب ضمن الخدمة؛ المستثنى هو المكافأة عند إنهاء يقع داخلها فقط.

المناطق الزمنية تزيح حدًّا بيوم. كل كائن Date هنا يُبنى بـ T00:00:00Z صريحة ويُقرأ بـ toISOString().slice(0, 10). أدخِل في هذا الملف new Date(y, m, d) بالتوقيت المحلي وستفشل اختبارات الحدود في نصف مناطق العالم الزمنية.

الخطوات التالية

  • سلّم فرع المادة 77 إلى محرك تسوية إنهاء الخدمة — فهو يسعّر الإشعار والتعويض وحدّ الشهرين الذي يوجّه إليه هذا المحرك
  • طابِق الإجازات التي توقف الاحتساب مع محرك رصيد الإجازات السنوية، فالتجربة لا تُراكم الإجازة كما تفعل الخدمة المعتادة
  • سعِّر الإجازة المرضية التي توقف الاحتساب عبر محرك أجر الإجازة المرضية — فالغياب نفسه الذي يمدّ التجربة هو أيضًا شريحة إجازة مدفوعة وفق المادة 117
  • تحقّق من أثر الخروج في التجربة على نسب التوطين عبر محرك نطاقات — فالعمالة التي تغادر داخل التجربة حرّكت مع ذلك المتوسط المتحرّك لنطاق منشأتك
  • دع العامل أو مسؤول الموارد البشرية يتحقّق من الأرقام الناتجة مجانًا عبر الحاسبة العمالية و حاسبة نهاية الخدمة

الخاتمة

المادة 53 أربع جمل، وكل واحدة منها فرع: الشرط يجب أن يكون مكتوبًا، والمجموع لا يزيد على مائة وثمانين يومًا، والعيدان والإجازة المرضية لا يُحتسبان، والإنهاء داخل المدة لا يحمل شيئًا. وتضيف المادة 54 فرعًا خامسًا لا يستطيع سجلّ عقد بمفرده الإجابة عنه. أغفِل أيًّا منها ولن يرمي النظام خطأً — بل سيُعيد بهدوء «ما زال تحت التجربة» ويُنهي أحدهم عقدًا بناءً عليه.

المحرك بضع مئات من الأسطر، وأثمن ما يخرج منه ليس التاريخ. إنه قائمة الأسباب: سجلّ بالقاعدة التي قرّرت الفرع، قابل لإعادة الإنتاج بعد أشهر حين يُناقَش القرار أمام محكمة عمّالية.

إن كان نظامك يخزّن فترة التجربة حقلًا منطقيًّا يُضبط عند التعيين، أو كنت على وشك إنهاء عقد أحدهم في الشهر الخامس بناءً على تاريخ لم يُعِد أحد حسابه منذ خطاب العرض، أرسل لنا بيانات عقودك وإجازاتك — سنمرّر حالاتك الفعلية على محرك كهذا ونخبرك أيّ الخروجات تقع فعليًّا على جانب المادة 77 من الخط.