# Attendance Module — ব্যবসা ও API গাইড

এই গাইডটি ERPFLOW ব্যাকএন্ডের **Attendance** মডিউলের প্রতিটি নিশ্চিত ফিচারকে ডকুমেন্ট করে। নিচের প্রতিটি বাক্য যুক্তিসঙ্গতভাবে যুক্ত উৎস ফাইলের ওপর ভিত্তি করে (আপেন্ডিক্স এ-এ তালিকাভুক্ত)। যদি কোনো বিবরণ যাচাইয়েও পাওয়া না যায়, তবে এই গাইডটি স্পষ্টভাবে লিখবে: **"Not specified in the reviewed source files।"**

> English version: [`ATTENDANCE_MODULE_GUIDE_EN.md`](./ATTENDANCE_MODULE_GUIDE_EN.md) · বাইলিংুয়াল PDF: [`ATTENDANCE_MODULE_GUIDE.pdf`](./ATTENDANCE_MODULE_GUIDE.pdf)

---

## ১। এই মডিউল কী?

Attendance মডিউল কর্পোরেটের **ডিজিটাল সময়-ও ছুটি ইঞ্জিন**। যা যাচাই করা গেল:

- ক্যাপচার করা **punch** (একদিনে একাধিক punch, একাধিক সোর্স থেকে)
- মাস্টার ডাটা: **Shifts**, **Attendance Types**, **Leave/Holiday Policies**
- shifts এবং policies সংযুক্ত করা স্কোপে (**Assignments**) — কর্পোরেট স্তর থেকে একজন কর্মচারী পর্যন্ত
- ঠিক একটি তারিখে কোন কর্মচারী কী ধরে (এটা **Assignment Resolution**)
- একটি ফ্রিন করা policy snapshot সহ **daily attendance records** হিসাব
- সhedules বা ম্যানুয়ালি **Close Day** হিসাব করা জব
- **Leave Balances**, অ্যাক্রুয়াল, ক্যারি-ফরোর্ড এবং ম্যানুয়াল সমন্বয় একটি পূর্ণ ledger সহ
- **Leave Request** চক্রপূর্ণ ওয়ার্কফ্লো (preview → apply → approve/reject/cancel)
- **Attendance Correction Requests** (missing/incorrect punch, wrong status)
- **Monthly Attendance Approval** দিয়ে মাস বন্ধ করা, রেকর্ড লক করা এবং `ready_for_payroll` ফ্ল্যাগ করা

** গুরুত্বপূর্ণ:** সবকিছু আপনার **বর্তমান কোম্পানি** সংগে যুক্ত। প্রতিটি কুয়েরীকে platform tenancy context দ্বারা স্কোপ করা হয়; কোম্পানি রিকোয়েস্টে `X-Company-Id` হেডার হিসেবে আসে।

---

## ২। কেরা এটা ব্যবহার করবে?

`permission:` মিডলওয়্যার গঠন আর করা পূর্তি চাবিগুলি চালু করে। নিচের রেজিস্ট্রি স্পেক থেকে (সেকশন ৪) আর রুট ব্যবহার জুড়ে ক্রস-চেক করা হয়েছে:

| পারমিশন কী | কি অনুমোদন দেয় |
|---|---|
| `attendance.menu-view` | দেখতে Attendance সেকশন; policies, attendance types, shifts, assignments পঢ়তে; punches তালিকা |
| `attendance.config-manage` | attendance types, shifts, policies ক্রিয়াত্মক (CRUD) |
| `attendance.policy-group-view` | policy groups ও eligibility attributes দেখতে |
| `attendance.policy-group-manage` | policy groups তৈরি/এডিট/স্ট্যাটাস/ডিলিট; eligibility preview; কর্মচারীদের প্রয়োগ/ব্যাকফিল |
| `attendance.assignment-manage` | assignments তৈরি/আপডেট/শেষ; bulk assign; রেকর্ড পুনরগণনা endpoint ট্রিগার |
| `attendance.assignment-preview` | assignment resolution নির্ণয়ীয় টুল |
| `attendance.punch-create` | punch in/out |
| `attendance.punch-create-others` | অন্য কর্মচারীর পক্ষে punch/তালিকা (PunchService ভেতরে যাচাই করা হয়) |
| `attendance.record-view-own` / `-team` / `-all` | রেকর্ড দৃশ্যমানতা স্তর |
| `attendance.record-export` | দৈনিক সারাংশ এক্সপোর্ট |
| `attendance.record-recalculate` | "বর্তমান policy দিয়ে পুনর্গণনা" ওভাররাইট (পুনর্গণনা সেবার ভেতরে) এবং close-day জব endpoints |
| `attendance.correction-create` | নিজের correction request জমা/বাতিল; correction কিউ দেখা |
| `attendance.correction-approve` | correction অনুমোদন/প্রত্যাখ্যান (সরাসরি fallback path) |
| `attendance.correction-override-lock` | লক্ড মাসে correction অনুমোদন (AttendanceCorrectionExecutor ভেতরে যাচাই) |
| `attendance.leave-apply` | নিজের leave আবেদন/প্রিভিউ/বাতিল |
| `attendance.leave-approve` | leave অনুমোদন/প্রত্যাখ্যান; যেকোনো কর্মচারীর leave দেখা |
| `attendance.leave-balance-view` | সকল ব্যালেন্স তালিকা; অন্য কর্মচারীর ledger ইতিহাস |
| `attendance.leave-balance-adjust` | ম্যানুয়াল ব্যালেন্স সমন্বয়; accrual / carry-forward জব চালু |
| `attendance.monthly-view` | মাসিক অনুমোদন গ্রিড, বিবরণ, ব্রেকডাউন, ব্যাচ স্ট্যাটাস |
| `attendance.monthly-approve` | মাস বানানো/অনুমোদন/বাল্ক অনুমোদন |
| `attendance.monthly-unlock` | অনুমোদিত মাস আনলক |

স্পেক অনুসারে, স্ব-সেবা (self-service) কীগুলি (`attendance.record-view-own`, `attendance.punch-create`, `attendance.correction-create`, `attendance.leave-apply`) একটি seeded `employee` ভূমিকার অন্তর্ভুক্ত — যা প্রতিদিন পাওয়া যেন।

---

## ৩। প্রতিটি endpoint প্রয়োগ করে কী কনভেনশন?

| কনভেনশন | তথ্য (verified) |
|---|---|
| বেস URL | `/api/v1/attendance` (`RouteServiceProvider`) |
| Route-name prefix | `api.attendance.` |
| গ্লোবাল মিডলওয়্যার | `auth:api`, `onboarding.access`, `onboarding.document` — মডিউলের **প্রতিটি** route আবৃত্ত |
| রুটলেভেল অথরাইজেশন | `permission:<key>` মিডলওয়্যার গোষ্ঠী (কীগুলি §২ এ) |
| টেন্যান্সি | কোম্পানি id `TenantContext` থেকে আসে; কোম্পানি অনুপস্থিতি হলে `PermissionDeniedException('company.context')`। হেডার: `X-Company-Id` (spec §0.3) |
| কর্মচারী পরিচয় | `employee_id` `employee_personal_infos.id`-এর রেফারেন্স; কিছু validation `exists:employee_personal_infos,id` ব্যবহার করে |
| অ্যাপ্রুভেল ইঞ্জিন | leave, correction এবং monthly অনুমোদন platform **ApprovalGateway**-এর মধ্য দিয়ে যায়; যখন কোনো action জন্য approval বন্ধ থাকে, `onApproved` সিঙ্ক্রোনাসভাবে চলে (একটি ত্রুটি নয়) |
| পেজিনেশন | তালিকা endpoint পেজিনেটেড রিসোর্স ফেরত দেয়; ডিফল্ট সাইজ ১৫ (যদি না অন্যথা উল্লেখ করা হয়) |

---

## ৪। ফিচার ক্যাটালগ

### ৪.১ মডিউল স্বাস্থ্য যাচাই (Health Check)

- **ব্যবসা উদ্দেশ্য:** যে মডিউলটি স্বাস্থ আছে এবং এর assignment-resolution cache বাস্তবে আইনভঙ্গ করা যায় কিনা তা যাচাই করা।
- **কী করে:** cache store এর নাম, ট্যাগ সাপোর্ট, সামগ্রীক `status` (`ok`/`degraded`) এবং একটি `detail` বার্তা ফেরত দেয়। Activity event `attendance.health_viewed` লগ করে।
- **Endpoint:** `GET /health` → `AttendanceController@health` (অতিরিক্ত permission নেই)।
- **Response:** JSON `{module: "attendance", status: "ok"|"degraded", cache:{store, supports_tags, supported, detail}}`। ট্যাগ সাপোর্ট না থাকলে `degraded` (কারণ resolved assignments ২৪ ঘণ্টা পর্যন্ত stale থাকতে পারে)।
- **ডেটা এন্টিটি:** নেই (cache.default কনফিগ থেকে পঢ়ে)।

### ৪.২ Attendance Types (মাস্টার ডাটা)

- **ব্যবসা উদ্দেশ্য:** স্ট্যাটাসের ভাষা সংজ্ঞায়িত করা — Present, Absent, Late, Half Day, Leave, Holiday, Weekend, WFH, Business Trip, Missing Check-In, Missing Check-Out — সহ কোম্পানির নিজস্ব স্ট্যাটাস।
- **কী করে:** CRUD + স্ট্যাটাস টগল। System-seeded রোয়ে `system_code` আছে; HR-তৈরি রোয়ের `system_code = NULL`। হিসাব ইঞ্জিন `system_code` দিয়েই স্ট্যাটাস সলিভ করে (নাম/id নয়)।
- **Endpoints** (read `attendance.menu-view`; write `attendance.config-manage`):

  | পদ্ধতি | Path | Controller@action |
  |---|---|---|
  | GET | `/attendance-types` | `AttendanceTypeController@index` |
  | GET | `/attendance-types/{id}` | `AttendanceTypeController@show` |
  | POST | `/attendance-types` | `AttendanceTypeController@store` (201) |
  | PUT | `/attendance-types/{id}` | `AttendanceTypeController@update` |
  | PATCH | `/attendance-types/{id}/status` | `AttendanceTypeController@updateStatus` |
  | DELETE | `/attendance-types/{id}` | `AttendanceTypeController@destroy` |

- **Validation (verified):** `name` required ≤100, company-এ ইউনিক · `code` required ≤50, regex `^[A-Z0-9_]+$`, company-এ ইউনিক · `system_code` **prohibited** · `category` required ≤50 · `is_paid`, `counts_as_working_day`, `eligible_for_payroll` required bool · `color` ≤30, `icon` ≤50 required · স্ট্যাটাস পরিবর্তন `active|inactive` মাত্র। Update বেসিক ৪টিতে uniqueness যাচাই।
- **Delete guard:** সিস্টেম স্ট্যাটাস ডিলিট করা যায় না (409); যদি records আছে তবে ডিলিট করা যায় না (409)।
- **ডেটা এন্টিটি:** `attendance_types` টেবিল।

### ৪.৩ Shifts

- **ব্যবসা উদ্দেশ্য:** কাজের সময় টেমপ্লেট — সূচনা/শেষ সময়, টাইমজোন, বিরতি, grace period, present/half-day অনুপাত, কর্মদিবস, overnight flag।
- **Endpoints:**

  | পদ্ধতি | Path | Permission | Controller@action |
  |---|---|---|---|
  | GET | `/shifts` | menu-view | `Shift\ShiftManagementController@index` |
  | GET | `/shifts/{shiftId}` | menu-view | `…@show` |
  | POST | `/shifts` | config-manage | `…@store` (২০০ "Shift Create Successfully Done") |
  | PUT | `/shifts/{shiftId}` | config-manage | `…@update` |
  | PATCH | `/shifts/{shiftId}/status` | config-manage | `…@updateStatus` |
  | DELETE | `/shifts/{shiftId}` | config-manage | `…@destroy` ("Shift archived successfully.") |

- **Validation (verified):** `name` ≤150 (create-এ required, update-এ nullable) · `start_time`/`end_time` required `H:i` · `timezone` nullable valid timezone · `break_minutes` int ≥0 · `working_hours` numeric >0 ≤24 · `grace_minutes` int ≥0 · `min_hours_present` numeric >0 ≤24 · `min_hours_half_day` numeric ≥0 ≤24 · `working_days` required array ≥1 distinct integers 1–7 (ISO-8601: ১ = সোমবার) · `is_overnight` required bool · `status` nullable `active|inactive`।
- **কোড তথ্য:** তালিকা endpoint `per_page` ডিফল্ট **১**।
- **ডেটা এন্টিটি:** `shifts` টেবিল।

### ৪.৪ Attendance Policies (Leave & Holiday)

- **ব্যবসা উদ্দেশ্য:** Leave সুবিধা ও holiday ক্যালেন্ডার নিয়ম লিখে, effective-ডেটেড, JSON `config` সহ।
- **কী করে:** দুটি ধরণ — `leave` (entitlement, accrual, carry-forward, encashment, half-day, LWP, advance notice, backdate limit, document threshold) এবং `holiday` (holiday এন্ট্রিগুলি + optional `recurring`)। Config shape ভেরিয়েটর (`HolidayPolicyConfigRule` / `LeavePolicyConfigRule`) দ্বারা ধরণ অনুযায়ী যাচাই হয়।
- **Endpoints:** read `menu-view` (`GET /policies`, `GET /policies/{id}`); write `config-manage`: `POST /policies` (201), `PUT /policies/{id}`, `PATCH /policies/{id}/status`, `DELETE /policies/{id}`।
- **Validation (verified):** create-এ `policy_type` ∈ {`leave`,`holiday`}, `name` ≤150, `code` ≤50, `effective_date` date, `config` array, optional `status` ∈ {`Active`,`Inactive`,`Archived`}। স্ট্যাটাস পরিবর্তন একই সেট।
- **ডেটা এন্টিটি:** `attendance_policies` (`policy_type`, `config` json-cast)।

### ৪.৫ Policy Groups (eligibility bundles + apply/backfill)

- **ব্যবসা উদ্দেশ্য:** একটি **eligibility rule document** সহ policies বান্ডল করে — HR বলতে পারে "কে কী পায়", তারপর সেটিকে কর্মচারীদের সাধারণ `assignments` হিসেবে প্রয়োগ করে।
- **কী করে (verified model docblock):** Groups কেবলমাত্র একটি লেখার স্তর — runtime-�এ কোনো group দিয়ে সমাধান করা হয় না; group প্রয়োগ করলে `assignments` রো তৈরি হয়। Group এডিট করেও কাউকে প্রভাবিত করে না; প্রয়োগ আলাদাভাবে করতে হয় এবং preview আগে নিতে হয়।
- **Endpoints:**

  | পদ্ধতি | Path | Permission | উদ্দেশ্য |
  |---|---|---|---|
  | GET | `/policy-groups` | policy-group-view | পৃষ্ঠাপত্র তালিকা |
  | GET | `/policy-groups/attributes` | policy-group-view | রিল বিল্ডারের ভাষা: attributes + operators |
  | GET | `/policy-groups/{id}` | policy-group-view | বিস্তারিত |
  | POST | `/policy-groups` | policy-group-manage | তৈরি (201) |
  | POST | `/policy-groups/eligibility/preview` | policy-group-manage | প্রিভিউ — কতজন active কর্মচারী (matched_count, total_active, sample) |
  | PUT | `/policy-groups/{id}` | policy-group-manage | আপডেট |
  | PATCH | `/policy-groups/{id}/status` | policy-group-manage | Active/Inactive |
  | DELETE | `/policy-groups/{id}` | policy-group-manage | মুছে ফেলা |
  | POST | `/policy-groups/{id}/apply/preview` | policy-group-manage | ব্যাকফিল প্রিভিউ (eligible / already_assigned / new / conflicts / skipped + sample) |
  | POST | `/policy-groups/{id}/apply` | policy-group-manage | চাপার ব্যাকফিল (202) |
  | GET | `/policy-groups/{id}/apply/{batchId}` | policy-group-manage | ব্যাচ স্ট্যাটাস |

- **Eligibility attributes (verified registry):** `employment_type_id`, `grade_id`, `branch_id`, `division_id`, `department_id`, `section_id`, `team_id` (কোম্পানি-স্কোপড টেবিল থেকে) এবং `gender`, `probation_status` (নরমালাইজড স্ট্রিং)। operators: `in`, `not_in`, `equals`, `not_equals`। ফাঁকা rule সমগ্র কোম্পানি মানে ("Company General")। `match` হতে পারে `all` (ডিফল্ট) অথবা `any`।
- **Validation (verified):** `name` ≤150, `code` ≤50 (দুটোই company-এ ইউনিক — সেবা-স্তর) · `description` ≤1000 nullable · `eligibility` required array (`PolicyGroupEligibilityRuleValidator` দ্বারা; কোম্পানির আছে এমন আইডি যাচাই করে) · `effective_date_strategy` ∈ {`joining_date`,`probation_end_date`} (ডিফল্ট `joining_date`; probation fallback joining_date) · `status` ∈ {`Active`,`Inactive`} · `policy_ids` array of ints যারা সক্রিয় হতে হবে। ব্যাকফিল অনুরোধে optional `date` এবং `sample_size` (0–100 apply-preview; 0–50 eligibility-preview)।
- **ডেটা এন্টিটি:** `attendance_policy_groups`, pivot `attendance_policy_group_policy`, উৎপন্ন `assignments` (সাথে `policy_group_id` stamped)।
- **অটো-অ্যাসাইনমেন্ট:** `PolicyGroupAutoAssignmentService` একজন কর্মচারীর policy assignments স্বয়ংসম্পূর্ণতা করে; `EligibilitySweepService` সাম্প্রত যুক্ত কর্মালয় সংযুক্তি ঘোষণা করে।

### ৪.৬ Assignments (shifts/policies স্কোপে যুক্ত করা)

- **ব্যবসা উদ্দেশ্য:** ঠিক কোন স্কোপে কোন shift, কোন leave policies ও holiday ক্যালেন্ডার কোনও প্রয়োজনীয় effective তারিখে প্রযোজ্য।
- **স্কোপ মডেল:** `scope_type` ∈ hierarchy `company → branch → division → department → section → team → employee`; resolution সবসময় **সবচেয়ে নির্দিষ্ট** থেকে। `assignable_type` ∈ {`shift`,`policy`}; policy-এর জন্য `assignable_subtype` ∈ {`leave`,`holiday}` এবং মিলতে হবে policy-এর ধরণের সাথে। কার্ডিনালিটি: shift ও holiday স্কোপে বিলকুল; leave মাল্টি। Sources: `manual`, `auto`, `bulk`। স্ট্যাটাস: `Active`, `Inactive`।

  | পদ্ধতি | Path | Permission | নোট |
  |---|---|---|---|
  | GET | `/assignments` | menu-view | ফিল্টার: scope_type, scope_id, assignable_type, assignable_subtype, source, history, per_page ≤100 |
  | GET | `/assignments/{id}` | menu-view | বিস্তারিত |
  | POST | `/assignments` | assignment-manage | আইডেম্পোটেন্ট (assignOrGet) — একই assignable+স্কোপ+effective তারিখ আবার না লিখে আগের রো রিটার্ন করে। কেবলমাত্র **Active** shifts/policies যুক্ত করা যায়। overlap conflict → `AssignmentOverlapException` |
  | PATCH | `/assignments/{id}` | assignment-manage | `end_date`, `status` (partial) |
  | POST | `/assignments/{id}/end` | assignment-manage | `end_date` আবশ্যক |
  | POST | `/assignments/bulk` | assignment-manage | `filter{scope_type,scope_id}` (company…team) অথবা `employee_ids`; সিঙ্ক রেসপন্স অথবা **202 queued** `{batch_id,status,total,processed,succeeded,failed}` |
  | GET | `/assignments/bulk/{batchId}` | assignment-manage | ব্যাচ অগ্রগতি |

- **Create validation:** shift-এর জন্য subtype অবশ্যই null; `company` স্কোপে `scope_id` নিষিদ্ধ, অন্যথায় required; `effective_date` required; `end_date` ≥ effective_date। Bulk-এ `filter` বা `employee_ids` অবশ্যক।
- **Queued mode থ্রেশহোল্ড:** সিঙ্ক vs queued সিদ্ধান্ত থ্রেশহোল্ডের ওপরে নির্ভর করে; সংখ্যাগত মান **Not specified in the reviewed source files**।
- **ডেটা এন্টিটি:** `assignments`; স্কোপ বিদ্যমানতা (`assertScopeExists`) যাচাই করে।
- **ইভেন্ট:** `AssignmentChanged` ক্লাস存在し (AssignmentService দ্বারা ইম্পোর্টেড)।

### ৪.৭ Assignment Resolution (নির্ণয়ীয় + ইঞ্জিন)

- **ব্যবসা উদ্দেশ্য:** "এই কর্মচারী কী ধরে?" — punch, calculation এবং leave ইঞ্জিনগুলোই একই উত্তর ব্যবহার করে।
- **Endpoint:** `GET /resolve/assignment?employee_id=&date=` (`attendance.assignment-preview`)। Validation: `employee_id` required integer; `date` required `Y-m-d`।
- **Response (verified DTO):** `{shift, leave_policies, holiday_calendar, timezone, resolved_from}` (resolved_from জয়তক স্কোপ নাম্বেরে)। timezone fallback shift → company। কোন active shift না থাকলে `unassigned` ব্যাগে validation error ("No active shift found for the given date.")।
- **Cache:** ২৪ ঘণ্টা TTL, company/employee/date কী, ট্যাগ-ভিত্তিক আইনভঙ্গযোগ্য।

### ৪.৮ Punch In/Out (append-only মাল্টি-punch)

- **ব্যবসা উদ্দেশ্য:** Web, mobile, biometric, API বা manual থেকে স্বয়ংক্রিয়ভাবে আঘাত সংগ্রহ — পরের পর্যন্ত পরিবর্তন নয়, শুধুমাত্র সুপারসিড করা যায়।
- **Endpoints:** `POST /punch` (`attendance.punch-create`) · `GET /punches?employee_id=&date=` (`attendance.menu-view`)।
- **Validation (verified `StorePunchRequest`):** `punch_type` ∈ {`in`,`out`} · `source` ∈ {`web`,`mobile`,`biometric`,`api`,`manual`} · `punch_time` nullable `Y-m-d H:i:s` · `employee_id` nullable int · `ip_address` ≤45 · `device_info` ≤255 · `remarks` ≤255।
- **Behavior (verified `PunchService`):**
  - punch কলার নিজস্ব প্রোফাইলে যায়; অন্য `employee_id` দিলে `attendance.punch-create-others` প্রয়োজন (না হলে 403)। তালিকায়ও একই আবেদন।
  - "আজ" সংগণানা কোম্পানির টাইমজোনে (ফallback `Asia/Dhaka`)।
  - প্রথমে assignment resolution; কোন shift না থাকলে **422**, `code: unassigned` ("No shift is assigned for this date.")।
  - Overnight shift: স্থানীয় সময় `start_time`-এর আগে হলে আগের `attendance_date`-এ যায়।
  - একই ধরণের দুটি ক্রমিক punch প্রতিবাদী (422, শেষ punch সময় বার্তায়)।
  - রো read-only (`UPDATED_AT = null`), দৈনিক `sequence_no` সহ; পরে `superseded_by_id`-এ ref (approved correction দ্বারা সেট করা হয়; মূল never mutated/deleted)।
  - `PunchCreated` event ফায়ার হয়।
- **Response:** 201 `PunchResource` (id, company_id, employee_id, attendance_date, punch_type, punch_time ISO-8601, source, ip_address, device_info, remarks, sequence_no, superseded_by_id, correction_request_id, created_at)।
- **ডেটা এন্টিটি:** `attendance_punches` টেবিল।

### ৪.৯ Daily Attendance Records (দেখা ও এক্সপোর্ট)

- **ব্যবসা উদ্দেশ্য:** প্রতি কর্মচারী/দিন একটি হিসাব রো — প্রথম in, শেষ out, কাজ/ওভারটাইম ঘণ্টা, দেরি/আগে আসা, punch সংখ্যা, প্রয়োগিত policy snapshot, lock ফ্ল্যাগ।
- **Endpoints:**

  | পদ্ধতি | Path | Permission | নোট |
  |---|---|---|---|
  | GET | `/attendance-records/today` | record-view-own \| -team \| -all | কলার employee profile লিংক আবশ্যক (না থাকলে 404); today পুনরায় হিসাব করে রেসপন্স |
  | GET | `/attendance-records` | same | ফিল্টার `employee_id,start_date,end_date,status`; range >366 দিন ⇒ 422; ভিজিবিলিটি রেপোজিটরিতে যুক্ত ইউজার ধরে |
  | GET | `/attendance-records/{id}` | same | 404 যদি না থাকে; `record-view-all` ছাড়া রেকর্ডটি কলার স্কোপে মিলতে হবে, না মিললে `PermissionDeniedException('attendance.record-view')` |
  | GET | `/attendance-records/{id}/punches` | same | সেই দিনের সব punch (superseded সহ) |
  | GET | `/attendance-records/export` | record-export | `format` query ডিফল্ট `xlsx`; ≤2000 সারি (কনফিগ `attendance.exports.export_queue_threshold`, ডিফল্ট 2000) Excel-এর মাধ্যমে স্ট্রিম `attendance_records_<timestamp>.<format>`; তার ওপর `QueueAttendanceExportJob` ডিসপ্যাচ → **202** `{token}` (40-অক্ষর random) |

- **Calculation core (verified `AttendanceRecordService::calculateDaily`):** লক্ড রেকর্ড সরাসরি ফেরত; না হলে non-superseded punches সময় ক্রমে জোড়া দেয়া হয় shift/timezone-এ; weekend / holiday (recurring month-day match সহ) / working-day শ্রেণীবিন্যাস; grace vs late; half-day থ্রেশহোল্ড shift থেকে; সিস্টেম স্ট্যাটাস on-demand `system_code` দিয়ে; প্রয়োগিত সেটিংগগুলি `policy_snapshot`-এ ফ্রিন করা হয় (grace_minutes, working_hours, min-hours present/half-day, working_days, timezone, resolved_at)। লক্ড দিন পুনরায় হিসাব করা যায় না।
- **ডেটা এন্টিটি:** `attendance_records` (+ relations employee/shift/attendanceType/punches)।

### ৪.১০ Record Recalculation (snapshot replay vs live override)

- **ব্যবসা উদ্দেশ্য:** ডেটা ঠিক করার পর দিন পুনরাৎ হিসাব — সাধারণত **stored snapshot** replay করে ঐতিহ্য বজায় রাখে; ঐচ্ছিকভাবে **live policy** পুনরায় সমাধান করে audit trail সহ।
- **Endpoint:** `POST /records/{recordId}/recalculate`। **রুট মিডলওয়্যার `attendance.assignment-manage`।** Body (`RecalculateAttendanceRecordRequest`): `use_current_policy` **required bool**, অন্যান্য nullable (`user_id,date,policy_snapshot,mock_resolved_policy,shift,is_locked,has_permission`)।
- **Behavior (verified):** 404 যদি রেকর্ড না থাকে বা অন্য কোম্পানিতে। লক্ড রেকর্ড ⇒ `AttendanceRecordLockedException`। `use_current_policy=true`-এর সাথে ইঞ্জিন পারমিশন **`attendance.record-recalculate`** প্রয়োজন (না হলে `RecalculationUnauthorizedException`); cache ফ্লাশ, live policy সমাধান, before/after audit log। সাধারণত stored `policy_snapshot` replay হয় (পুরোনো snapshot বিহীন রেকর্ডের জন্য live resolve + warning log)।

### ৪.১১ Close Day job (ম্যানুয়াল ট্রিগার + স্ট্যাটাস)

- **ব্যবসা উদ্দেশ্য:** নিশ্চিত করুন প্রতিটি সক্রিয় কর্মচারী একটি গেঁটে দিবসের রেকর্ড পায় — ঠিক একই কাজ যা রাতের সময়সূচী করে (9.1-BE: ঘণ্টা ০২:০০ স্থানীয় সময়ে কোম্পানির ব্যাচ)।
- **Endpoints:** `POST /jobs/close-day` এবং `GET /jobs/close-day/{batchId}` — রুট পারমিশন **`attendance.record-recalculate`**, prefix `jobs`।
- **Validation (verified `CloseDayRequest`):** `date` required `Y-m-d`, `before_or_equal:today`, `after_or_equal: today−90 days`; `employee_id` nullable `exists:employee_personal_infos,id`।
- **Behavior (verified):** Payroll মডিউলের `PayrollPeriodGuard` যদি সময়সীমা বন্ধ ঘোষণা করে → **200** "No employees to process or period is locked." নাহয়, রেকর্ড না থাকা সক্রিয় কর্মচারীকে chunk size 200-এ `ProcessCloseDayChunkJob` এ ভাগ করে **202** `{batchId}` ফেরত। স্ট্যাটাস endpoint Laravel batch ফিল্ড বা 404 ফেরত দেয়।
- **Cross-module dependency:** `Modules\Payroll\Support\PayrollPeriodGuard` (এক্সপ্লিসিট ইম্পোর্ট)।

### ৪.১২ Leave Balances (দেখা, সমন্বয়, ledger)

- **ব্যবসা উদ্দেশ্য:** কর্মচারী/policy/বছর অনুযায়ী entitled, used, carried-forward, encashed — সহ একটি পূর্ণ ledger।
- **Available-days সূত্র (verified model accessor):** `entitled_days + carried_forward_days − used_days − encashed_days`।
- **Endpoints:**

  | পদ্ধতি | Path | Authorization | নোট |
  |---|---|---|---|
  | GET | `/leave-balances/me` | auth group ছাড়া কিছু নেই | বর্তমান বছর; employee লিংক না থাকলে 403 |
  | GET | `/leave-balances/{employeeId}/history` | internal check | নিজের id সর্বদা; অন্যের জন্য `attendance.leave-balance-view` আবশ্যক। `year` ডিফল্ট বর্তমান বছর; paginated ledger |
  | GET | `/leave-balances` | leave-balance-view | ফিল্টার `employee_id` (∃ employee_personal_infos), `year` 2000–2100, `policy_id` (∃ attendance_policies) |
  | PATCH | `/leave-balances/{id}/adjust` | leave-balance-adjust | নিচে দেখুন |

- **Adjustment (verified):** body `days` required numeric ≠0, `reason` required ≤255, `allow_negative` nullable bool। `FOR UPDATE` লক; `entitled_days`-এ প্রয়োগ; negative ফলে `DomainException` (`allow_negative` ছাড়াত) এবং সেই transaction-এ `manual_adjustment` ledger রো (signed days, reason, actor)।
- **Ledger entry types (verified enum):** `accrual`, `carry_forward`, `consumption`, `reversal`, `encashment`, `manual_adjustment`।
- **ডেটা এন্টিটি:** `leave_balances`, `leave_balance_ledger`।

### ৪.১৩ Leave Accrual & Carry-Forward jobs

- **ব্যবসা উদ্দেশ্য:** মাসিকভাবে policy নিয়ম অনুযায়ী ব্যালেন্স বাড়ান এবং year-end-এ অব্যবহৃত দিন স্থানান্তর করুন — schedule (কন্সোল কমান্ড আছে) বা ম্যানুয়ালি backlog হিসাবে।
- **Endpoints (পারমিশন `attendance.leave-balance-adjust`, prefix `jobs`):**
  - `POST /jobs/accrue-leave` — body: `month` 1–12, `year` current±5, optional `policy_id` (∃ attendance_policies)। chunked accrual; **202** `{batchId}` অথবা **200** `{applied:0}` "No active leave assignments found to accrue or accrual already completed."
  - `POST /jobs/carry-forward` — body: `from_year` current±5, optional `policy_id`। **202** `{batchId}` অথবা **200** "No active leave assignments found for carry-forward."
  - `GET /jobs/{batchId}` — Laravel batch progress অথবা 404।
- **Carry-forward নিয়ম (verified service):** policy config `carry_forward_allowed` ও `max_carry_forward` (`min(available, max)`) দ্বারা নিয়ন্ত্রিত; idempotent — লক্ষ্য balance এর জন্য `carry_forward` ledger রো আছে কিনা চেক করে; স্কোপ historical date অনুযায়ী সমাধান করা হয় (অতীতের বছরের রানের জন্য সেই সংস্থাগত সংযুক্তি ব্যবহার করে)।

### ৪.১৪ Leave Requests (স্ব-সেবা জীবনচক্র)

- **ব্যবসা উদ্দেশ্য:** কর্মচারীরা assigned policies-এর বিরুদ্ধে ছুটি আবেদন করে; অনুমোদক সিদ্ধান্ত নেয়; অনুমোদন leave দিন stamp করে এবং ব্যালেন্স হ্রাস করে।
- **Endpoints (group `attendance.leave-apply | attendance.leave-approve`, prefix `/leave-requests`):**

  | পদ্ধতি | Path | কাজ |
  |---|---|---|
  | GET | `/applicable-policies?date=` | কলার জন্য প্রযোজ্য policies (assignment-driven, balance নয়, ফলে প্রথমবারও দেখা যায়)। ফিল্ড: id, name, code, half_day_allowed, lwp_allowed, available_days, entitled_days, year, balance_provisioned। employee লিংক আবশ্যক (না থাকলে 403); `date` `Y-m-d`, ডিফল্ট today |
  | POST | `/preview` | ড্রাই-রান — `total_days`, `excluded_dates[]`, `balance{available_days, after_request_days}`, `requires_document`, `document_threshold_days`, `blockers[]` (রিপোর্টেড, throw করে না), `warnings[]` |
  | POST | `/` | জমা (201)। `reason` required, `attachment` ফাইল pdf/jpg/jpeg/png ≤5120KB (`leave_attachments` public disk)। ব্লকার error-এ রূপান্তরিত; অন্য approved leave-এর সাথে overlap ⇒ 409। ApprovalGateway (`leave-approve`, correlation `leave_request:{id}`) |
  | GET | `/` | approver + `employee_id` ছাড়া indexForHr (সব); অন্যরা নিজেরাই — colleague id বিনা `attendance.leave-approve` ⇒ denial। ফিল্টার: status, employee_id, policy_id, from, to |
  | GET | `/{id}` | approver সব দেখে; অন্যরা শুধুমাত্র নিজস্ব |
  | PATCH | `/{id}/cancel` | নিজস্ব only; linked approval request বাতিল করে (রেসপন্সে উল্লেখ) |
  | POST | `/{id}/approve` | অ্যাকশনের ভেতরের চেক `attendance.leave-approve`; optional `comment`/`reason` |
  | POST | `/{id}/reject` | একই পারমিশন; non-empty `reason` ছাড়া 422 |

- **Approve effects (verified `LeaveRequestExecutor`):** row লক; pending-only (409 "Request already finalized"); অন্য approved request-এর সাথে overlap ⇒ 409; weekends/holidays বাদে working day-এ day value গণনা (resolved shift+holiday calendar); সেই দিনগুলিকে leave type-এর `attendance_records` হিসেবে `policy_snapshot {leave_request_id}` সহ stamp করে (spec §5.3: executor সেবামতে balance হ্রাস + consumption ledger রো লিখে)；Σday-values == `total_days` যাচাই (না হলে 422)；approved/decided_by/at সেট; `LeaveDecided` ইভেন্ট ফায়ার。
- **Validation inputs:** preview/apply শেয়ার করে `leave_policy_id` int, `start_date`/`end_date` `Y-m-d` (end ≥ start), `duration_type` ∈ {`full_day`,`half_day_first`,`half_day_second`}।
- **Data entities:** `leave_requests`, `leave_request_days` (প্রতি working day, `day_value` 1.00/0.50, void সহায়তা)।

### ৪.১৫ Attendance Correction Requests

- **ব্যবসা উদ্দেশ্য:** কর্মচারী একদিন প্রতিবাদ করতে পারে — missing check-in/out, incorrect time, wrong status, other — evidence সহ; HR সিদ্ধান্ত; অনুমোদন ঐতিহ্য ধ্বংস না করে দিনটি পুনর্লিখন করে।
- **Endpoints:**

  | পদ্ধতি | Path | Permission | নোট |
  |---|---|---|---|
  | POST | `/correction-requests` | correction-create | 201; duplicate pending same date ⇒ **409** |
  | POST | `/correction-requests/{id}/cancel` | correction-create | Policy-authorised cancel |
  | GET | `/correction-requests` | correction-create \| correction-approve | query `status` validated (invalid ⇒ 422), `employee_id` ডিফল্ট `me`, `from`,`to` |
  | GET | `/correction-requests/{id}` | correction-create \| correction-approve | `approval_request_uuid/status` + `current_day` snapshot (record metrics + সব punch, superseded সহ) — approver-এর আলাদা record permission লাগে না |
  | POST | `/correction-requests/{id}/approve` | correction-approve | সরাসরি fallback যখন কোনো প্ল্যাটফর্ম approval request না পেছনে (যেমন submission-এ approval বন্ধ) |
  | POST | `/correction-requests/{id}/reject` | correction-approve | Inline validation: `reason` required string ≤500 |

- **Submission validation (verified `StoreCorrectionRequestRequest`):** `attendance_date` required date; `request_type` ∈ {`missing_in`,`missing_out`,`incorrect_time`,`wrong_status`,`other`}; `requested_check_in` required (missing_in/incorrect_time, `Y-m-d H:i:s`); `requested_check_out` required (missing_out/incorrect_time); `attendance_type_override_id` required (wrong_status, ∃ attendance_types); `reason` ≤5000 required; `attachment` optional file, mimes config থেকে `employee.documents.allowed_mimes` (ডিফল্ট pdf,jpg,jpeg,png,doc,docx), size cap `employee.documents.max_file_size_kb` (ডিফল্ট 5120)।
- **Service guards (verified `CorrectionRequestService`):** নিজস্ব row only (employee_id মিলয়ন না হলে denial); correction window (spec: `attendance.correction_window_days`, ডিফল্ট 30); একই তারিখে ২য় pending নেই; requested times shift window-এর মধ্যে আন্তে হয়; `month_locked` রোয়ে রেকর্ড করা হয় এবং এটি ওভাররাইট করতে **`attendance.correction-override-lock`** দরকার (executor constant + spec)।
- **Approve effects (verified `AttendanceCorrectionExecutor`):** `source=manual` এবং `correction_request_id`-যুক্ত replacement punch যোগ করে, superseded করা originals-কে `superseded_by_id` Stamp করে (originals কখনো mutate/delete হয় না), ফলাফল sequence যাচাই (একই ধরণের দুটি ক্রমিক ⇒ 422), snapshot বজায়ে day-এর পুনরায় হিসাব; `wrong_status` ওয়️ `attendance_type_id`-কে ওভাররাইট করে audit log; reject কোনো thing বদলায় না, কেবল status/reason। `CorrectionDecided` ইভেন্ট (ইম্পোর্টেড)।
- **Data entity:** `correction_requests` (অন্তর্ভুক্ত `attendance_type_override_id`, migration 2026_08_20)।

### ৪.১৬ Monthly Attendance Approval (মাস বন্ধ → payroll-ready)

- **ব্যবসা উদ্দেশ্য:** একজন/মাস ভিতরে audited সংযোজন সংখ্যা বের করে, অসমস্যা দেখায়, অনুমোদন (optional override), ভিতরের দিনগুলো লক করে এবং `ready_for_payroll`-এ চিহ্নিত করে। unlock Finance দ্বারা ফ্রিজ না হওয়া পর্যন্ত বিপরীত কাজ করে।
- **Endpoints:**

  | পদ্ধতি | Path | Permission | নোট |
  |---|---|---|---|
  | POST | `/monthly-attendance/build` | monthly-approve | `employee_id`-সহ: সিঙ্ক (201)। ছাড়া: bulk queued (202); ফাঁকা নির্বাচন ⇒ "No employees found in the specified criteria."। approved মাস পুনর্গঠন ⇒ 409 |
  | GET | `/monthly-attendance` | monthly-view | ফিল্টার: month, year, status, ready_for_payroll, department_id, search, per_page |
  | GET | `/monthly-attendance/{id}` | monthly-view | বিস্তারিত |
  | GET | `/monthly-attendance/{id}/breakdown` | monthly-view | দৈনিক রেকর্ড (ফিল্টার status, per_page; user-scoped) |
  | GET | `/monthly-attendance/batch/{batchId}` | monthly-view | cache ভিত্তিক ব্যাট স্ট্যাটাস (TTL 24h; 404) |
  | POST | `/monthly-attendance/{id}/approve` | monthly-approve | body `override` nullable bool। already-approved ⇒ 409। unresolved without override ⇒ 409 with issue list। override activity-log-এ (`monthly_attendance.approved_with_override`) |
  | POST | `/monthly-attendance/bulk-approve` | monthly-approve | `ids` required (∃ monthly_attendance_approvals); chunks of 50 queued; **202** `{mode:"queued",batch_id,…}` |
  | POST | `/monthly-attendance/{id}/unlock` | monthly-unlock | `reason` required ≤255। frozen month ⇒ 409 "Cannot unlock a frozen month."। is_locked/ready_for_payroll রিসেট; reason সংরক্ষণ; status pending; দিনগুলো আনলক |

- **Build totals (verified):** present/absent/leave/unpaid-leave/half days, late count, overtime & working hours সমষ্ট। `unresolved_flag` হয় যদি: missing check-out দিন পাওয়া যায়, elapsed days < records (unassigned/missing data), **pending correction requests**, **pending leave requests** — স্ট্রিং `getUnresolvedIssuesList`-থেকে।
- **Approve effects (verified gateway + executor):** ApprovalGateway (`monthly-approve`, entity `monthly_attendance_approvals`, correlation `monthly_attendance:{id}`)；executor সেট `status=approved`, `approved_by/at`, `is_locked=true`, `ready_for_payroll=true` এবং সেই employee-month-এর সব `attendance_records` লক করে；event **`MonthApproved`** ফায়া�। spec §5.3 নিশ্চিত করে এই transitions executor-এরই মাধ্যমে।
- **Data entity:** `monthly_attendance_approvals` (unique per company+employee+month+year; `frozen_at/frozen_by/unfreeze_reason`)。

---

## ৫। ব্যাকগ্রাউন্ড জব ও সhedul কন্সোল কমান্ড

**Queue jobs:** `BuildBulkMonthlyAttendanceJob`, `ProcessBulkMonthlyApproveJob`, `ProcessBulkAssignmentJob`, `ProcessCloseDayChunkJob`, `ProcessLeaveAccrualChunkJob`, `ProcessCarryForwardChunkJob`, `ProcessPolicyGroupBackfillChunkJob`, `QueueAttendanceExportJob`।

**Console schedulors:** `ScheduleCloseDayCommand` (hourly; 9.1-BE — local hour == 2-এ কোম্পানির ব্যাট চালু)、`ScheduleLeaveAccrualCommand`、`ScheduleLeaveCarryForwardCommand` (ম্যানুয়াল ট্রিগারের সাথে জোড়ি দেয় — 9.2-BE)、`ScheduleEligibilitySweepCommand` (policy-group auto-assignment-এর জন্য সাম্প্রত সংযুক্তি ঘোষণা)।

---

## ৬। ডোমেইন ইভেন্ট

`PunchCreated` (PunchService-এ ডিসপ্যাচ) · `AssignmentChanged` (AssignmentService-এ ইম্পোর্টেড) · `AttendanceCalculated` · `LeaveDecided` (LeaveRequestExecutor-এ ডিসপ্যাচ) · `LeaveDayVoided` · `CorrectionDecided` (AttendanceCorrectionExecutor-এ ইম্পোর্টেড) · `MonthApproved` (MonthlyAttendanceApprovalService-এ ডিসপ্যাচ)।

---

## ৭। মডিউলের ডেটাবেস এন্টিটি

Migrations (verified filenames): `shifts`, `attendance_types`, `attendance_policies`, `assignments` (+ scope-normalization & identity-constraint, `policy_group_id` addition), `attendance_punches`, `leave_balances`, `leave_balance_ledger`, `attendance_records`, `correction_requests` (+ `attendance_type_override_id` migration), `leave_requests`, `leave_request_days`, `monthly_attendance_approvals`, `attendance_policy_groups`, `attendance_policy_group_policy`।

Seeders: `AttendanceDatabaseSeeder`, `AttendanceTypeSeeder`, `ShiftSeeder`।

---

## ৮। অন্য মডিউলের সাথে ইন্টিগ্রেশন

| মডিউল / প্ল্যাটফর্ম সেวা | প্রমাণ |
|---|---|
| **Employee** | punches/records/corrections/leave/monthly-এ `EmployeePersonalInfo` রিলেশন; `exists:employee_personal_infos,id` validation; close-day & bulk-build employee সিলেকশন রিপোজিটরি; `user->employeeProfile` স্ব-সেবা |
| **Configuration** | `AssignmentService::scopeModelMap()`-এ Branch/Division/Department/Section/Team; monthly build validation-এ `departments` existence check |
| **Payroll** | `Modules\Payroll\Support\PayrollPeriodGuard` close-day গেট; `ready_for_payroll` + `frozen_at` hand-off (§৯) |
| **Platform Tenancy** | `TenantContext` স্কোপিং; `X-Company-Id` হেডার |
| **Platform Permissions** | `permission:` middleware + `PermissionEngineContract` point-checks (punch-others, record-view-all, leave-approve, leave-balance-view, record-recalculate, override-lock) |
| **Platform Approval Gateway/Engine** | leave-approve, correction-approve, monthly-approve সাবমিশন `correlationId`-সহ; executors entity type ভিত্তিক (`leave_request`, `attendance_correction`, `monthly_attendance`) |
| **Activity Log** | health view, unresolved-month override, correction lock-override & wrong-status override |
| **Excel (Maatwebsite)** | attendance এক্সপোর্ট স্ট্রিমিং + queued export |

---

## ৯। পে-রোল হ্যান্ড-অফ (spec + build sequence থেকে, কোড-ভিত্তিক যেখানে সম্ভব)

- অনুমোদিত মাস `ready_for_payroll = true` সেট করে; Finance-এর **freeze/unfreeze Payroll মডিউলে** থাকে (`payroll.month-freeze`, `attendance.monthly-approve`-এর সাথে segregation-of-duties)। Freeze requires approved; unfreeze requires কারণ এবং paid run-এর জন্য `payroll.month-unfreeze-paid`।
- **`attendance_snapshots`** (কার্ড 8.0-BE, সম্পন্ন): immutable, timestamp-less, `unique(payroll_run_id, employee_id)`; frozen monthly approval থেকে verbatim copy (কোনো recalculation নেই); idempotent rebuild。এটি **Payroll মডিউলের মধ্যে** ৪টি endpoint আছে (build draft-only `payroll.run-create`, list, show, diagnostic divergence) — অর্থাৎ Attendance রুট ফাইলের অংশ নয়।
- Payslip `AttendanceSnapshotServiceInterface::getPayrollAttendanceForRun()` দিয়েই consumption করা আবশ্যক; guard test প্রমাণ করে `attendance_records` ও `monthly_attendance_approvals` payslip path-এ ক্যোয়েরি হয় না। Payslip generation (`8.2a-BE`) **এখনও কোডে নেই** (pending card)।

---

## ১০। এরর আচরণ চিপ-শিট (verified codes/messages)

| পরিস্থিতি | ফলাফল |
|---|---|
| কোম্পানি কনটেক্সট অনুপস্থিত | `PermissionDeniedException('company.context')` |
| unassigned তারিখে punch | 422, `code: unassigned` |
| একই ধরণের দ্বিতীয় ক্রমিক punch | 422 (শেষ punch সময় বার্তায়) |
| Records list-এর range >366 দিন | 422 "Date range cannot exceed 366 days." |
| অবৈধ correction/leave status filter | 422 |
| একই তারিখে দ্বিতীয় pending correction | 409 `DuplicateCorrectionRequestException` |
| approved month approve/rebuild/unlock frozen | 409 |
| unresolved month override ছাড়া approve | 409 with issue list |
| allow_negative ছাড়া negative ব্যালেন্স সমন্বয় | `DomainException` "Adjustment rejected: …" |
| assignment create/update-এ overlap | `AssignmentOverlapException` |
| locked record recalculation / unauthorized live override | `AttendanceRecordLockedException`, `RecalculationUnauthorizedException` |

এর বাইরের যে kোনো thing (exact transport envelope, rate limits, অতিরিক্ত HTTP codes) — **Not specified in the reviewed source files**।

---

## ১১। এই গাইড ইন্টেনশনালি কভার করে না

- Front-end স্ক্রিন/UI flow (কেবলমাত্র backend routes, services, jobs, events রিভিউ করা হয়েছে)
- Role seeding mechanics (§২ উল্লেখের বাদে)
- Exact মান: bulk-assignment sync/queue threshold, base controller-এর pagination envelope internals, queue connection names

এদের জন্য: **Not specified in the reviewed source files**।

---

## আপেন্ডিক্স এ — রিভিউড সূত্র

1. `backend/Modules/Attendance/routes/api.php` (২য়৯১ লাইন, সম্পূর্ণ)
2. Controllers (১৭): AttendanceController, PunchController, AttendanceRecordController, AttendancePolicyController, AttendanceTypeController, Shift/ShiftManagementController, AssignmentController, BulkAssignmentController, AssignmentResolutionController, AttendanceRecalculationController, PolicyGroupController, LeaveBalanceController, LeaveBalanceJobController, CloseDayJobController, LeaveRequest/LeaveRequestManagementController, CorrectionRequestController, MonthlyAttendanceApprovalController
3. Form Requests (৪৫টি ফাইন — সব validation আলাদাভাবে উল্লিখিত)
4. Models: Assignment, AttendancePolicy, AttendancePolicyGroup, AttendancePunch, AttendanceRecord, AttendanceType, CorrectionRequest, LeaveBalance, LeaveBalanceLedger, LeaveRequest, LeaveRequestDay, MonthlyAttendanceApproval, Shift
5. Service contracts (২২টি) এবং implementations: PunchService, AttendanceRecordService, AttendanceRecalculationService, AssignmentService, AssignmentResolutionService, CorrectionRequestService, LeaveRequestService, LeaveBalanceService, MonthlyAttendanceApprovalService, CloseDayService, AttendanceTypeService, AttendancePolicyGroupService, PolicyGroupEligibilityEvaluator, BulkAssignmentService
6. Approval executors: MonthlyAttendanceExecutor, AttendanceCorrectionExecutor, LeaveRequestExecutor
7. Support: EligibilityAttributeRegistry, GenderNormalizer, BatchProgressStore; DTOs (ResolvedContext, EmployeeEligibilityContext, ResolvedPolicyAssignment, LeaveEvaluation, AutoAssignmentOutcome, PolicySnapshotData)
8. Jobs (৮), Events (৭), Resources (১৪), Exports, Console schedulers (৪), Providers (RouteServiceProvider), Migrations (১৭), Seeders (৩)
9. রেফারেন্স ডক: `../attendance-payroll/ATTENDANCE_PAYROLL_MODULE_SPEC.md` (§0, §3, §4, §5, §6, Parts D–F) এবং `../attendance-payroll/ATTENDANCE_PAYROLL_BUILD_SEQUENCE.md`
10. স্টাইল রেফারেন্স: `../employee/EMPLOYEE_MODULE_GUIDE.md` / `../employee/EMPLOYEE_MODULE_GUIDE_BN.md`
