attendance overview
🧩 Syntax:
WITH
date_series AS (
SELECT generate_series(
date_trunc('month', TO_DATE('2025-05', 'YYYY-MM'))::date,
(date_trunc('month', TO_DATE('2025-05', 'YYYY-MM')) + interval '1 month - 1 day')::date,
interval '1 day'
)::date AS date
)
,
calendar_info AS (
SELECT
ds.date,
date_trunc('week', ds.date)::date AS week_start,
(date_trunc('week', ds.date) + interval '6 days')::date AS week_end,
date_trunc('month', ds.date)::date AS month_start,
(date_trunc('month', ds.date) + interval '1 month - 1 day')::date AS month_end,
CASE
WHEN ds.date < MIN(CASE WHEN EXTRACT(DOW FROM ds.date) = 1 THEN ds.date END) OVER () THEN 1
ELSE FLOOR((ds.date - MIN(CASE WHEN EXTRACT(DOW FROM ds.date) = 1 THEN ds.date END) OVER ()) / 7) + 2
END AS week_of_month
FROM date_series ds
)
,
attendance_logs AS (
SELECT
eal.date,
eal.employee_id,
MIN(eal.in_time AT TIME ZONE 'UTC') AS min_in_time,
MAX(eal.out_time AT TIME ZONE 'UTC') AS max_out_time,
MAX(eal.in_time AT TIME ZONE 'UTC') AS max_in_time,
BOOL_OR(eal.in_time IS NULL OR eal.out_time IS NULL) AS has_incomplete_logs,
json_agg(
json_build_object(
'in_time', eal.in_time,
'out_time', eal.out_time,
'status_type','present'
) ORDER BY eal.in_time ASC
) AS log_entries
FROM employee_attendance_logs eal
WHERE eal.employee_id = 'bf81eae2-8f92-4e51-86e0-90a0dff83964'
AND eal.is_deleted = false
GROUP BY eal.date, eal.employee_id
)
,
leave_info AS (
SELECT
lrt.transaction_date,
json_agg(
json_build_object(
'leave_type', lt.leave_type,
'change_duration', lrt.change_duration
)
)::json AS leave_details
FROM "leave_requests" lr
JOIN calendar_info ci ON ci.date = date_trunc('month', TO_DATE('2025-05-01', 'YYYY-MM'))::date
INNER JOIN "leave_request_transaction" lrt
ON lr.id = lrt.leave_request_id
AND lrt.transaction_date BETWEEN ci.month_start AND ci.month_end
LEFT JOIN "leave_plan_leave_types" lplt
ON lr.leave_plan_leave_type_id = lplt.id
LEFT JOIN "leave_types" lt
ON lplt.leave_type_id = lt.id
WHERE lr.employee_id = 'bf81eae2-8f92-4e51-86e0-90a0dff83964'
AND ((lr.from_date BETWEEN ci.month_start AND ci.month_end)
OR (lr.to_date BETWEEN ci.month_start AND ci.month_end))
AND lr.status = 'approved'
GROUP BY lrt.transaction_date
),
attendance_details AS (
SELECT
ds.date,
esh.shift_type_id,
sta.min_half_day_hours,
sta.min_full_day_hours,
-- Work hours
CASE
WHEN aa.date IS NOT NULL
AND aa.has_incomplete_logs = false
AND sta.hours_calculation_method = 'clock_in_clock_out' THEN
COALESCE(
(
SELECT TO_CHAR(
GREATEST(
SUM(
CASE
WHEN eal.out_time < eal.in_time THEN
(eal.out_time + INTERVAL '24 hours' - eal.in_time)::interval
ELSE
(eal.out_time - eal.in_time)::interval
END
),
INTERVAL '00:00:00'
),
'HH24:MI:SS'
)
FROM "employee_attendance_logs" eal
WHERE eal.employee_id = e.id AND eal.is_deleted = false
AND eal.date = ds.date
),
'00:00:00'
)
WHEN aa.date IS NOT NULL
AND sta.hours_calculation_method = 'first_in_last_out' THEN
COALESCE(
(
SELECT TO_CHAR(
GREATEST(
SUM(
CASE
WHEN eal.out_time < eal.in_time THEN
(eal.out_time + INTERVAL '24 hours' - eal.in_time)::interval
ELSE
(eal.out_time - eal.in_time)::interval
END
),
INTERVAL '00:00:00'
),
'HH24:MI:SS'
)
FROM "employee_attendance_logs" eal
WHERE eal.employee_id = e.id AND eal.is_deleted = false
AND eal.date = ds.date
),
'00:00:00'
)
ELSE '00:00:00'
END AS work_hours,
CASE
WHEN ds.date < esh.effective_from IS NULL THEN ARRAY[]::text[]
WHEN ds.date >= '2025-05-01'::date
AND hd.holiday_date IS NULL
AND wo.day_of_week IS NULL
AND aa.date IS NULL THEN ARRAY[]::text[]
WHEN ds.date >= '2025-05-01'::date AND aa.date IS NULL THEN
CASE
WHEN wo.day_of_week = LOWER(TO_CHAR(ds.date, 'FMDay'))::day_of_week_enum OR hd.holiday_date IS NOT NULL THEN
CASE
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'full_day' THEN ARRAY['full_day_weekly_off']
WHEN hd.holiday_date IS NOT NULL THEN ARRAY['holiday']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'first_half' THEN ARRAY['first_half_weekly_off']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'second_half' THEN ARRAY['second_half_weekly_off']
ELSE ARRAY[]::TEXT[]
END
ELSE ARRAY[]::TEXT[]
END
WHEN aa.date IS NOT NULL THEN
CASE
WHEN wo.day_of_week = LOWER(TO_CHAR(ds.date, 'FMDay'))::day_of_week_enum OR hd.holiday_date IS NOT NULL THEN
CASE
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'full_day' THEN ARRAY['full_day_weekly_off', 'present']
WHEN hd.holiday_date IS NOT NULL THEN ARRAY['holiday', 'present']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'first_half' THEN ARRAY['first_half_weekly_off', 'present']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'second_half' THEN ARRAY['second_half_weekly_off', 'present']
ELSE ARRAY['present']
END
ELSE ARRAY['present']
END
ELSE
CASE
WHEN wo.day_of_week = LOWER(TO_CHAR(ds.date, 'FMDay'))::day_of_week_enum OR hd.holiday_date IS NOT NULL THEN
CASE
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'full_day' THEN ARRAY['full_day_weekly_off']
WHEN hd.holiday_date IS NOT NULL THEN ARRAY['holiday']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'first_half' THEN ARRAY['first_half_weekly_off', 'absent']
WHEN CASE
WHEN ci.week_of_month = 1 THEN wo.first
WHEN ci.week_of_month = 2 THEN wo.second
WHEN ci.week_of_month = 3 THEN wo.third
WHEN ci.week_of_month = 4 THEN wo.fourth
WHEN ci.week_of_month = 5 THEN wo.fifth
ELSE wo.last
END = 'second_half' THEN ARRAY['second_half_weekly_off', 'absent']
ELSE ARRAY['absent']
END
ELSE ARRAY['absent']
END
END AS attendance_status,
li.leave_details
FROM date_series ds
LEFT JOIN "employees" e ON e.id = 'bf81eae2-8f92-4e51-86e0-90a0dff83964'
LEFT JOIN "employee_shift_type_history" esh ON
esh.employee_id = e.id AND
esh.effective_from <= ds.date AND
(esh.effective_to IS NULL OR esh.effective_to >= ds.date)
LEFT JOIN "shift_type_applicable_criteria" sta ON
sta.shift_type_id = esh.shift_type_id AND
sta.is_deleted = false
LEFT JOIN "employee_holiday_group_history" ehg ON
ehg.employee_id = e.id AND
ehg.effective_from <= ds.date AND
(ehg.effective_to IS NULL OR ehg.effective_to >= ds.date)
LEFT JOIN "holiday_groups" hg ON hg.id = ehg.holiday_group_id
LEFT JOIN "holiday_group_holidays" hd ON
hd.holiday_group_id = hg.id AND
hd.holiday_date = ds.date AND
hd.is_deleted = false
LEFT JOIN "employee_weekly_off_type_history" ewo ON
ewo.employee_id = e.id AND
ewo.effective_from <= ds.date AND
(ewo.effective_to IS NULL OR ewo.effective_to >= ds.date)
LEFT JOIN "weekly_off_type_config" wo ON
wo.weekly_off_type_id = ewo.weekly_off_type_id AND
wo.day_of_week = LOWER(TO_CHAR(ds.date, 'FMDay'))::day_of_week_enum AND
wo.is_deleted = false
LEFT JOIN calendar_info ci ON ci.date = ds.date
LEFT JOIN attendance_logs aa ON aa.date = ds.date
LEFT JOIN leave_info li ON li.transaction_date = ds.date
),
leave_calculations AS (
SELECT
ad.date,
ad.attendance_status,
COALESCE(
(SELECT SUM(
CASE WHEN
(
leave_detail->>'leave_type') != 'unpaid'
AND NOT (ad.attendance_status @> ARRAY['present'])
THEN (leave_detail->>'change_duration')::NUMERIC
ELSE 0
END
)
FROM json_array_elements(COALESCE(ad.leave_details, '[]'::json)) AS leave_detail),
0
) AS paid_leave_duration,
COALESCE(
(SELECT SUM(
CASE WHEN
(
leave_detail->>'leave_type') = 'unpaid'
AND NOT (ad.attendance_status @> ARRAY['present'])
THEN (leave_detail->>'change_duration')::numeric
ELSE 0
END
)
FROM json_array_elements(COALESCE(ad.leave_details, '[]'::json)) AS leave_detail),
0
) AS unpaid_leave_duration
FROM attendance_details ad
),
day_status_on_logs AS (
SELECT
ad.date,
CASE
WHEN ad.work_hours::interval >= ad.min_full_day_hours
THEN 'present'
WHEN ad.work_hours::interval >= ad.min_half_day_hours
THEN 'half_day'
ELSE 'absent'
END AS day_status
FROM date_series ds
LEFT JOIN attendance_details ad ON ds.date = ad.date
)
-- final_attendance_statuses_calculation AS (
SELECT
ad.date,
CASE WHEN ad.attendance_status @> ARRAY['present'] THEN
CASE
WHEN ad.attendance_status = ARRAY['present'] THEN
CASE
WHEN dsl.day_status = 'half_day' THEN ARRAY['first_half_weekly_off', 'present']
WHEN dsl.day_status = 'absent' THEN ARRAY['absent']
ELSE ad.attendance_status
END
WHEN ad.attendance_status = ARRAY['holiday', 'present'] THEN
CASE
WHEN dsl.day_status = 'half_day' THEN ARRAY['half_holiday_off', 'present']
WHEN dsl.day_status = 'absent' THEN ARRAY['holiday']
ELSE ad.attendance_status
END
WHEN ad.attendance_status = ARRAY['full_day_weekly_off', 'present'] THEN
CASE
WHEN dsl.day_status = 'half_day' THEN ARRAY['first_half_weekly_off', 'present']
WHEN dsl.day_status = 'absent' THEN ARRAY['full_day_weekly_off']
ELSE ad.attendance_status
END
WHEN ad.attendance_status = ARRAY['first_half_weekly_off', 'present'] THEN
CASE
WHEN dsl.day_status = 'absent' THEN ARRAY['first_half_weekly_off', 'absent']
ELSE ad.attendance_status
END
WHEN ad.attendance_status = ARRAY['second_half_weekly_off', 'present'] THEN
CASE
WHEN dsl.day_status = 'absent' THEN ARRAY['second_half_weekly_off', 'absent']
ELSE ad.attendance_status
END
ELSE ad.attendance_status
END
ELSE ad.attendance_status
END AS final_attendance_status
FROM date_series ds
LEFT JOIN attendance_details ad ON ds.date = ad.date
LEFT JOIN day_status_on_logs dsl ON ds.date = dsl.date