---
title: Attendance
order: 2
updated: 2026-07-12
---

# Attendance

The *Attendance* screen is the hub for everything about who showed up — your own day-by-day record, your team's, the bulk-marking flow HR uses, leave applications and their approvals, and the monthly salary report. The page header reads *Attendance* with the subtitle *Employee attendance, leaves, approvals, and salary reports.* Everything is organised into tabs across the top, and which tabs you see depends on your permissions and whether anyone reports to you.

Two firm-wide settings shape how this whole screen behaves, both configured under [HR settings](/docs/human-resources/hr-settings):

- **Working days** — which weekdays count as working days (default Monday–Friday, i.e. `1,2,3,4,5`). Everything else is a *week off*.
- **Attendance mode** — *automatic* (the default) or *manual*. In automatic mode the system quietly back-fills every past working day as *present* the first time that month is viewed, so you only ever see exceptions (absences, leave, half-days). In manual mode nothing is auto-filled and people (or HR) must mark each day.

## Open the screen

1. Open *Human Resources* in the sidebar.
2. Click *Attendance*. The page opens on the first tab you have access to.

## The tabs

| Tab | Who sees it |
|---|---|
| *My Attendance* | Anyone with *View Own Attendance* (`viewOwnAttendance`), or a firm admin. |
| *Team* | Only if you have people reporting to you **and** hold *View Team Attendance* (`viewTeamAttendance`) or *View All Attendance* (`viewAllAttendance`) — or you are an admin. |
| *Mark* | *Bulk Mark Attendance* (`bulkMarkAttendance`), or admin. |
| *Leaves* | *Apply for Leave* (`applyLeave`), or any leave-viewing/approval/cancellation permission, or (with reports) *Approve Junior Leave* — or admin. |
| *Approvals* | *Approve Any User's Leave* (`approveAllUsersLeave`), or *Approve Junior Leave* (`approveLeave`) while you have reports — or admin. Shows a live count of pending applications. |
| *Salary* | *View Own Salary* (`viewOwnSalary`), *View Others' Salary Reports* (`viewSalaryReport`), *Manage Salary Revisions* (`manageSalary`), or admin. |

Every permission is re-checked on the server for the underlying action, so a hidden tab is never a soft lock you can work around — a forged request to mark, apply, approve, or read salary is rejected with a `403` if you lack the permission.

Sibling how-to pages go deeper on the day-to-day flows:

- [Marking attendance](/docs/human-resources/marking-attendance)
- [Applying for leave](/docs/human-resources/applying-leave)
- [Approvals](/docs/human-resources/approvals)
- [Logging expenses](/docs/human-resources/log-expense) (the parallel *Expenses* screen)

## My Attendance

Your own monthly attendance, opened by default. The tab has six summary tiles, a month calendar, and up to two action buttons.

### Summary tiles

Six tiles sit above the calendar, counted for the month you are viewing:

- *Days in Month* — calendar days in the month (`28`–`31`).
- *Working Days* — days that match the firm's working-day setting, minus any holiday that falls on one. This is the real "expected" day count.
- *Present* — days recorded as present.
- *Absent* — days recorded as absent.
- *On Leave* — days on approved leave.
- *Half Day* — days recorded as a half day.

### Month navigation

The chevron buttons on either side of the month name step back and forward one month at a time. There is no upper or lower bound — you can page into past or future months. The line under the month name shows the first-to-last date range (formatted `dd/mm/yy`).

### Mark Me Present

A *Mark Me Present* button appears next to the month header **only** when *all* of these are true:

- the firm is in **manual** attendance mode;
- you hold *Mark Own Present* (`markOwnPresent`), or are an admin;
- you are viewing the **current** month;
- today is a **working day**; and
- today is **not** a holiday.

Click it to record yourself present for today. Once a record exists for today the button is disabled and its label reflects the state — *Marked Present*, *Half Day Recorded*, *On Leave Today*, or *Marked Absent*.

The server enforces the same conditions and a few more, because this endpoint is a common target for tampering:

- Self check-in works **only in manual mode**. In automatic mode the request is rejected (`403`) — the system already back-fills your present days, so a self-mark would only rewrite the audit trail.
- Self check-in can set status `present` **only**. You cannot flip yourself to absent, half-day, or on-leave by forging the request.
- If you are already *on leave* today, self check-in is refused (`409`) so it can't silently cancel your leave.
- In automatic mode the button is hidden entirely; the system handles your daily attendance.

### The calendar

A seven-column month grid. Each cell is a day, colour-coded by status: green *present*, red *absent*, blue *on leave*, amber *half day*, and red for *holiday / week off*. A small dot in the corner of a cell marks a day that was **auto-marked** by the system (only relevant in automatic mode — the legend shows an *Auto-marked* key there). A half-day cell also shows a `½`.

Click any day (or press Enter/Space on it) to open its **day detail**.

### Day detail

The day-detail modal reads the day back in plain language ("You were present.", "You were on leave.", "Holiday — you had the day off.", "This day hasn't happened yet.", and so on) with a matching status pill, then adds context cards where relevant:

- **Holiday** — the holiday's reason.
- **Leave** — leave type (name, code, Paid/Unpaid), the leave period, the reason, and the half-day session if it was a half-day leave.
- **Half day** — which half you were present ("Present in the morning, off in the evening" or the reverse) plus any note.
- **Note / source** — a free-text note on the record, and an "Auto-marked by the system" line for auto-generated days.
- **Marked by** — who entered the record and when, shown only for **manually** marked present/absent/half-day days (auto-marked days have no marker). *Arrival* and *departure* times appear here when they were captured.

### View Analytics

*View Analytics* opens the analytics modal — a wider, date-range breakdown described under [Attendance analytics](#attendance-analytics).

## Team

A snapshot of the people who report to you (your direct and indirect reports in the reporting hierarchy). The tab only exists when you have at least one report and the right permission.

### Month selector

A dropdown offering the **last 12 months** (including the current one). Pick a month to reload every teammate's numbers for it.

### Search

Filter the list by name or username as you type.

### The roster

Each person is a row showing their avatar, name, `@username`, and role. On wider screens three quick counts appear — *Present*, *Absent*, *Leave* — for the selected month, plus a pill for their status *today*. Three actions sit on the right:

- *View* — opens that person's month calendar (the same grid as *My Attendance*, read-only, with month navigation). Days are clickable for the day detail.
- *Analytics* — opens the analytics modal scoped to that person.
- *Mark* — opens the single-person mark modal. This button appears only if you hold *Mark Others Present* (`markOthersPresent`) or are an admin.

Viewing another person's attendance is server-scoped: even an admin or a *View All Attendance* holder can only open the calendar of someone **inside their own downline**. A request for anyone outside your team is rejected (`403`).

### Mark one person (the Mark modal)

The per-person *Mark Attendance* modal (reached from the *Mark* button) is where back-dated corrections happen, because it has a working date picker:

- *Date* — any date. Marking a date **earlier than today** requires the *Mark Attendance in Past Days* (`markAttendanceInPastDays`) permission; without it the modal shows a "Past-date marking is locked" notice and the save is blocked (and the server rejects it too).
- *Status* — *Present*, *Absent*, *Half (AM)*, or *Half (PM)*. Half (AM) means present in the morning, off in the afternoon; Half (PM) is the reverse.
- *Arrival Time* / *Departure Time* — optional `HH:mm` (24-hour) times, shown only for present/half-day. They are silently dropped on absent days so a stale pick can't lie.
- *Note* — optional free text (e.g. "Late arrival", "WFH").

Saving upserts the day's record and stamps you as the marker. If the person was marked *present* (or half-day) on a day that is a holiday for them, a comp-off credit is created automatically (see [HR settings](/docs/human-resources/hr-settings)).

## Mark

The bulk-mark screen, for HR or supervisors who set many people's attendance at once. Gated by *Bulk Mark Attendance* (`bulkMarkAttendance`).

This screen marks attendance for **today** — the card header reads *For Today*. (Back-dating a single person is done from the *Team → Mark* modal above.)

### The people list

The list is your team — everyone under you in the hierarchy — with **yourself injected at the top** so you can bulk-mark your own day here too. Search filters by name or username.

### Set all visible

A shortcut row applies one status to every currently visible (filtered) row at once:

- *Present* — sets all to present.
- *Absent* — sets all to absent.
- *Clear* — resets all to *Skip* (leaves them untouched on submit).

### Per-person status

Each row offers *Present*, *Absent*, *Half (AM)*, *Half (PM)*, and *Skip*. *Skip* means "leave this row as it already is". The screen pre-loads whatever is **already marked** for today: a green/red/amber *Already marked* badge shows the current state, and rows you change are flagged *· pending change* and outlined.

Rows already recorded as *On Leave* or *Holiday* are **Locked** and cannot be overwritten from this tab.

### Submitting

Only rows that **differ** from what is already marked are sent — re-submitting an identical status is skipped so it never pollutes the "marked by / marked at" audit trail. The footer shows the change count, and the button reads *Submit N Changes*. After a successful save the rows flip into their new "already marked" state.

Server-side rules the UI mirrors:

- You need *Bulk Mark Attendance*.
- You can only mark **yourself and people under you** — even an admin cannot bulk-mark someone outside their downline (`403`).
- Every user id is validated to exist in the firm.
- Marking a past date needs *Mark Attendance in Past Days*.
- Present/half-day on someone's applicable holiday earns them a comp-off credit.

## Leaves

Your leave applications, and — where your role allows — your team's. The tab opens on *My Leaves*; a *Team's Leaves* sub-tab appears next to it if you can view others' applications (any of *View All Leave Applications*, *Approve Junior Leave*, *Approve Any User's Leave*, or the various cancel permissions).

### The table

Columns are *Leave Type* (name + code, with an *Unpaid* tag when the type is unpaid), *Dates* (a single date or a `from → to` range), *Days* (with `(½)` for half-days), *Status*, and a *View* action. On *Team's Leaves* an *Applicant* column is added.

### Apply for leave

*Apply Leave* (top-right, on *My Leaves*, if you hold *Apply for Leave*) opens the *Apply for Leave* modal:

- *Leave Type* — a searchable picker of the firm's **active** leave types. Each shows a Paid/Unpaid tag and its code. If none exist yet, the modal tells you to ask HR to add them in [HR settings](/docs/human-resources/hr-settings).
- *Half-Day Leave* — a checkbox. When ticked, choose *Morning Off* or *Evening Off*, and the leave is forced to a **single day** (the *To* date locks to the *From* date). The other half counts as present.
- *From* / *To* — the leave dates. If *From* is moved past *To*, *To* snaps to match.
- *Reason* — required free text (up to `500` characters, enforced server-side).

A summary card previews the day count and Paid/Unpaid status before you submit. *Submit Application* files it with a *pending* status.

The server re-validates the whole request:

- You must hold *Apply for Leave*.
- The leave type must exist and be **active**.
- *From* must be on or before *To*.
- A half-day must be a single day and must name a session.
- Any attachments must be fully uploaded and belong to your firm.
- The new leave must **not overlap** an existing pending or approved leave of yours — overlaps are rejected.

Applying notifies your approval chain.

### Track, edit, cancel

*View* on any row opens the leave detail with the full record — type, dates, duration, reason, applied-on date, and (once decided) the rejection or cancellation reason. A *Backdated* tag appears when the leave's start date is today or earlier. The available action buttons depend on your relationship to the leave and your permissions:

- *Edit* — only the **owner** can edit, and only while the leave is still **pending**. Editing reuses the apply modal.
- *Approve* / *Reject* — see [Approvals](#approvals). Rejecting always asks for a reason.
- *Cancel Leave* — withdraws the leave. The confirmation warns when the leave has already started, because cancelling then **removes the on-leave attendance entries** for the past days (they fall back to present/absent on the next view).

Who can cancel what is layered on the server:

- The **owner** can always cancel their own **pending** leave, any date.
- Cancelling an **approved** or someone else's leave splits by date: a *future-dated* leave needs *Cancel Junior's Future Leave* (`cancelApprovedLeave`, juniors) or *Cancel Any User's Future Leave* (`cancelAllUsersApprovedLeave`, firm-wide); a *backdated* leave needs *Cancel Junior's Backdated Leave* (`cancelBackDateLeave`) or *Cancel Any User's Backdated Leave* (`cancelAllUsersBackDateLeave`).
- The "junior" variants require the applicant to be inside your downline; the "Any User" variants are firm-wide.

## Approvals

Leave applications waiting on your decision. The tab shows a badge with the pending count and lists only applications you are actually allowed to act on.

### The queue

Columns are *Applicant*, *Leave Type*, *Dates*, *Days*, *Applied*, *Status*, and *View*. *View* opens the same leave detail modal as the *Leaves* tab, here with *Approve* and *Reject* buttons.

### Approving

Approve creates the on-leave attendance entries. For every **working day** in the leave range (skipping week-offs and applicable holidays) the system writes an attendance record — status *on leave*, or *half day* when the application was a half-day — linked back to the approved leave.

If the leave type is a comp-off type (code `COMP` / `COMPOFF`), approval **consumes** that many available comp-off credits; if the balance is short, approval is refused with an "insufficient comp-off balance" message.

### Rejecting

Reject opens a *Reason for rejection* prompt — the reason is **required** and is shown to the applicant on their leave detail.

### Scope

- *Approve Any User's Leave* lets you act on **any** pending application in the firm.
- *Approve Junior Leave* is limited to applicants **below you** in the reporting hierarchy; a request to approve someone outside your downline is rejected (`403`).
- Only **pending** applications can be approved or rejected — acting on an already-decided leave is refused.

Approvals are handled one at a time (there is no bulk approve), so a rejection reason can always be captured. Expense approvals — travel and vouchers — live under the matching *Approvals* tab in the [Expenses](/docs/human-resources/log-expense) screen, not here.

## Salary

Your salary record, and — with the right permission and reports — your team's. Gated by any of *View Own Salary*, *View Others' Salary Reports*, *Manage Salary Revisions*, or admin.

### Employees sidebar

An employee picker appears on the left **only** if you have reports **and** hold *View Others' Salary Reports* (or are an admin). Someone with the permission but no reports just sees their own figures. Picking a person reloads the panel for them. Reading another person's salary is doubly gated on the server: you need the permission **and** the person must be in your downline (no firm-wide salary access, even for admins).

### Summary cards

Three cards at the top:

- *Current Monthly Salary* — the latest revision's monthly figure, with the date it took effect (or *Not set*).
- *{month} Gross* — the computed gross payable for the selected month, with a "present / working days" sub-line.
- *Disbursement Day* — the day of the month salary posts (from the current revision).

### Monthly report

A month dropdown (the **last 12 months, defaulting to the previous month**) drives the report:

- Four stats — *Working Days*, *Present*, *Absent*, *Half Days*. (The *Working Days* figure here is the number the per-day rate divides by; it counts total days in the month, not only the working weekdays.)
- A **leave breakdown** table — for each leave type taken that month: *Allowed* (the monthly allowance from the current revision), *Taken*, *Paid* (capped at the allowance), and *Unpaid* (the overflow, and all of an unpaid type).

If the person has no salary revision yet, the report shows *Salary not configured* instead.

**How the gross is computed** (server-side): the per-day rate is the monthly salary divided by the calendar days in the month. Gross = full month minus deductions, where a deduction day is each *absent* day, *half* a day per half-day, and each *unpaid* leave day. Paid leave within the allowance is not deducted. When a salary revision takes effect mid-month, the month is split into segments and each segment uses the salary that was in force then. Missing records inside the employment window count as absent; days before hiring or after termination stay neutral.

### Quick Salary Action

If you hold *Manage Salary Revisions*, an *Add Revision* button opens the salary-revision modal. (The full revision history lives under *HR Settings → Salary Timeline* — see [HR settings](/docs/human-resources/hr-settings).)

The *Add Salary Revision* modal:

- *Monthly Salary (₹)* — required, `0` or more. Shows an annual estimate (`× 12`).
- *Disbursement Day* — the pay day of the month. Range `1`–`28`, default `7`.
- *Effective Month* / *Effective Year* — when the revision takes effect (year list spans the current year `± 2`). A warning appears if the effective month is in the past, because every salary report from that month onward is recalculated.
- *Reason* — one of `Joining`, `Annual Hike`, `Promotion`, `Correction`, `Termination`, `Other`. Default `Joining`.
- *Leave Allowances (per month)* — tick each **paid** leave type this person is entitled to and set the days-per-month allowed without deduction (whole number, `0` or more). Unpaid leave types can't be ticked — they always deduct.
- *Note* — optional free text.

When **editing** an existing revision, the monthly-salary field is **locked** behind a risk acknowledgement, because changing a past amount rewrites every downstream paycheck, earning, and ledger row. Click the field, confirm the warning, and it unlocks.

## Attendance analytics

The analytics modal (opened from *View Analytics* on *My Attendance*, or *Analytics* on a *Team* row) gives a free date-range breakdown rather than a fixed month.

- *Date range* — pick any start and end. Defaults to the current calendar month.
- *Attendance Rate* — the headline percentage: `(present + half-days × 0.5) ÷ working days` over the range.
- Stat tiles — *Total Days*, *Working Days*, *Paid Holidays* (total days minus working days), *Present*, *Absent + Leave* (hover to split absent vs on-leave), and *Half Day*.

## Common questions

### Why does the *Team* tab not appear for me?

Either no one reports to you, or you lack *View Team Attendance* / *View All Attendance*. The tab needs both a report **and** the permission (admins always see it).

### Why can't I see a colleague in another department?

Attendance, salary, and bulk-marking are all scoped to your **downline** — the people under you in the reporting hierarchy. Even *View All Attendance* and admin rights only widen what you can do within your own team's chain on these screens; someone outside it returns a `403`. Ask an owner to adjust the reporting structure if you should have access.

### The *Mark Me Present* button isn't showing — why?

It only appears in **manual** attendance mode, on the **current** month, when **today is a working day and not a holiday**, and you hold *Mark Own Present*. In automatic mode the system marks your present days for you, so the button is hidden.

### Why can't I back-date attendance?

Marking any date earlier than today needs the *Mark Attendance in Past Days* permission. Without it, marking is limited to today's date, and the server rejects a back-dated request even if you craft one. This stops missed days being quietly filled in later. Ask an owner to grant it for lawful corrections.

### I marked everyone in *Mark* but nothing submitted?

Only rows whose status **differs** from what is already marked are sent. If everyone's chip matches their existing state (or is on *Skip*), there are no changes to submit. Rows locked as *On Leave* or *Holiday* are never overwritten from this tab.

### Why did the Days / Working Days numbers not match my payslip?

The *Working Days* tile on *My Attendance* counts real working days minus holidays. The *Working Days* number inside the **salary** monthly report is instead the calendar-day divisor used for the per-day rate. They measure different things by design.

### Where do public holidays, leave types, and working days come from?

All three are managed by an administrator under [HR settings](/docs/human-resources/hr-settings) and apply firm-wide. Inactive leave types don't appear in the apply picker.

### Can I approve several leaves at once?

No — approvals are one at a time so a rejection reason can be captured when needed. See [Approvals](/docs/human-resources/approvals).

### What happens to attendance if a leave is cancelled after it started?

Cancelling a leave that has already begun removes the on-leave attendance entries for the past days; those days fall back to present/absent on the next calendar view. The cancellation, and who did it, are recorded.
