diff --git a/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py b/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py
index 7b5ae2ab6..c7eaf2a68 100644
--- a/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py
+++ b/addons_extensions/hr_attendance_extended/models/hr_attendance_report.py
@@ -61,111 +61,116 @@ class AttendanceReport(models.Model):
# Define the query
query = """
WITH date_range AS (
- SELECT generate_series(
- %s::date,
- %s::date,
- interval '1 day'
- )::date AS date
- ),
- employee_dates AS (
- SELECT
- emp.id AS employee_id,
- emp.name AS employee_name,
- dr.date,
- TO_CHAR(dr.date, 'Day') AS day_name,
- EXTRACT(WEEK FROM dr.date) AS week_number,
- TO_CHAR(date_trunc('week', dr.date), 'MON DD') || ' - ' ||
- TO_CHAR(date_trunc('week', dr.date) + interval '6 days', 'MON DD') AS week_range,
- dep.name->>'en_US' AS department
- FROM
- hr_employee emp
- CROSS JOIN
- date_range dr
- LEFT JOIN
- hr_department dep ON emp.department_id = dep.id
- WHERE
- emp.active = true
- """ + (" AND " + " AND ".join(emp_date_conditions) if emp_date_conditions else "") + """
- ),
- daily_checkins AS (
- SELECT
- emp.id,
- emp.name,
- DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') AS date,
- at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_in,
- at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out,
- at.worked_hours,
- ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS first_checkin_row,
- ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in DESC) AS last_checkout_row,
- dep.name->>'en_US' AS department
- FROM
- hr_attendance at
- LEFT JOIN
- hr_employee emp ON at.employee_id = emp.id
- LEFT JOIN
- hr_department dep ON emp.department_id = dep.id
- WHERE
- """ + " AND ".join(checkin_conditions) + """
- ),
- attendance_summary AS (
- SELECT
- id,
- name,
- date,
- MAX(CASE WHEN first_checkin_row = 1 THEN check_in END) AS first_check_in,
- MAX(CASE WHEN last_checkout_row = 1 THEN check_out END) AS last_check_out,
- SUM(worked_hours) AS total_worked_hours,
- department
- FROM
- daily_checkins
- GROUP BY
- id, name, date, department
- ),
- leave_data AS (
- SELECT
- hl.employee_id,
- hl.date_from AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_start,
- hl.date_to AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_end,
- hlt.name->>'en_US' AS leave_type,
- hl.request_unit_half AS is_half_day,
- hl.request_date_from,
- hl.request_date_to
- FROM
- hr_leave hl
- JOIN
- hr_leave_type hlt ON hl.holiday_status_id = hlt.id
- WHERE
- hl.state IN ('validate', 'confirm', 'validate1')
- AND (hl.date_from, hl.date_to) OVERLAPS (%s::timestamp, %s::timestamp)
- )
- SELECT
- ed.employee_id AS id,
- ed.employee_name AS name,
- ed.date,
- 'Week ' || ed.week_number || ' (' || ed.week_range || ')' AS week_info,
- TRIM(ed.day_name) AS day_name,
- COALESCE(ats.first_check_in, NULL) AS first_check_in,
- COALESCE(ats.last_check_out, NULL) AS last_check_out,
- COALESCE(ats.total_worked_hours, 0) AS total_worked_hours,
- ed.department,
- CASE
- WHEN ld.leave_type IS NOT NULL AND ld.is_half_day THEN 'on Half day ' || ld.leave_type
- WHEN ld.leave_type IS NOT NULL THEN 'on ' || ld.leave_type
- WHEN ats.first_check_in IS NOT NULL THEN 'Present'
- ELSE 'NA'
- END AS status
- FROM
- employee_dates ed
- LEFT JOIN
- attendance_summary ats ON ed.employee_id = ats.id AND ed.date = ats.date
- LEFT JOIN
- leave_data ld ON ed.employee_id = ld.employee_id
- AND ed.date >= DATE(ld.leave_start)
- AND ed.date <= DATE(ld.leave_end)
- ORDER BY
- ed.employee_id, ed.date;
+ SELECT generate_series(
+ %s::date,
+ %s::date,
+ interval '1 day'
+ )::date AS date
+),
+employee_dates AS (
+ SELECT
+ emp.id AS employee_id,
+ emp.name AS employee_name,
+ dr.date,
+ TO_CHAR(dr.date, 'Day') AS day_name,
+ EXTRACT(WEEK FROM dr.date) AS week_number,
+ TO_CHAR(date_trunc('week', dr.date), 'MON DD') || ' - ' ||
+ TO_CHAR(date_trunc('week', dr.date) + interval '6 days', 'MON DD') AS week_range,
+ dep.name->>'en_US' AS department
+ FROM
+ hr_employee emp
+ CROSS JOIN
+ date_range dr
+ LEFT JOIN
+ hr_department dep ON emp.department_id = dep.id
+ WHERE
+ emp.active = true
+ """ + (" AND " + " AND ".join(emp_date_conditions) if emp_date_conditions else "") + """
+),
+daily_checkins AS (
+ SELECT
+ emp.id,
+ emp.name,
+ DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') AS date,
+ at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_in,
+ at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out,
+ at.worked_hours,
+ ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS first_checkin_row,
+ ROW_NUMBER() OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in DESC) AS last_checkout_row,
+ dep.name->>'en_US' AS department,
+ LEAD(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') OVER (PARTITION BY emp.id, DATE(at.check_in AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata') ORDER BY at.check_in) AS next_check_in
+ FROM
+ hr_attendance at
+ LEFT JOIN
+ hr_employee emp ON at.employee_id = emp.id
+ LEFT JOIN
+ hr_department dep ON emp.department_id = dep.id
+ WHERE
+ """ + " AND ".join(checkin_conditions) + """
+),
+attendance_summary AS (
+ SELECT
+ id,
+ name,
+ date,
+ MAX(CASE WHEN first_checkin_row = 1 THEN check_in END) AS first_check_in,
+ MAX(CASE WHEN last_checkout_row = 1 THEN check_out END) AS last_check_out,
+ SUM(worked_hours) AS total_worked_hours,
+ -- 👇 Calculate total break time (sum of gaps between check_out and next check_in)
+ SUM(
+ EXTRACT(EPOCH FROM (next_check_in - check_out)) / 3600
+ ) FILTER (WHERE next_check_in IS NOT NULL AND check_out IS NOT NULL) AS break_hours,
+ department
+ FROM
+ daily_checkins
+ GROUP BY
+ id, name, date, department
+),
+leave_data AS (
+ SELECT
+ hl.employee_id,
+ hl.date_from AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_start,
+ hl.date_to AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS leave_end,
+ hlt.name->>'en_US' AS leave_type,
+ hl.request_unit_half AS is_half_day,
+ hl.request_date_from,
+ hl.request_date_to
+ FROM
+ hr_leave hl
+ JOIN
+ hr_leave_type hlt ON hl.holiday_status_id = hlt.id
+ WHERE
+ hl.state IN ('validate', 'confirm', 'validate1')
+ AND (hl.date_from, hl.date_to) OVERLAPS (%s::timestamp, %s::timestamp)
+)
+SELECT
+ ed.employee_id AS id,
+ ed.employee_name AS name,
+ ed.date,
+ 'Week ' || ed.week_number || ' (' || ed.week_range || ')' AS week_info,
+ TRIM(ed.day_name) AS day_name,
+ COALESCE(ats.first_check_in, NULL) AS first_check_in,
+ COALESCE(ats.last_check_out, NULL) AS last_check_out,
+ COALESCE(ats.total_worked_hours, 0) AS total_worked_hours,
+ COALESCE(ats.break_hours, 0) AS total_break_hours,
+ ed.department,
+ CASE
+ WHEN ld.leave_type IS NOT NULL AND ld.is_half_day THEN 'on Half day ' || ld.leave_type
+ WHEN ld.leave_type IS NOT NULL THEN 'on ' || ld.leave_type
+ WHEN ats.first_check_in IS NOT NULL THEN 'Present'
+ ELSE 'NA'
+ END AS status
+FROM
+ employee_dates ed
+LEFT JOIN
+ attendance_summary ats ON ed.employee_id = ats.id AND ed.date = ats.date
+LEFT JOIN
+ leave_data ld ON ed.employee_id = ld.employee_id
+ AND ed.date >= DATE(ld.leave_start)
+ AND ed.date <= DATE(ld.leave_end)
+ORDER BY
+ ed.employee_id, ed.date;
"""
-
# Combine all parameters in the correct order:
# 1. date_range params (start_date_str, end_date_str)
# 2. employee_dates params (emp_date_params)
@@ -196,6 +201,7 @@ class AttendanceReport(models.Model):
'check_in': r['first_check_in'],
'check_out': r['last_check_out'],
'worked_hours': float(r['total_worked_hours']) if r['total_worked_hours'] is not None else 0.0,
+ 'break_hours': float(r['total_break_hours']) if r['total_break_hours'] is not None else 0.0,
'status': r['status']
})
@@ -213,7 +219,6 @@ class AttendanceReport(models.Model):
attendance_data = self.get_attendance_report(department_id, employee_id, start_date, end_date)
if not attendance_data:
raise UserError("No data to export!")
-
# Create workbook and sheet
workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('Attendance Report')
@@ -281,28 +286,26 @@ class AttendanceReport(models.Model):
)
# Set column widths (in units of 1/256 of a character width)
- col_widths = [6000, 8000, 7000, 3000, 4000, 5000, 5000, 4000, 5000]
+ col_widths = [6000, 8000, 7000, 3000, 4000, 5000, 5000, 4000, 4000, 5000]
for i, width in enumerate(col_widths):
sheet.col(i).width = width
# Write title
- sheet.write_merge(0, 0, 0, 8, 'ATTENDANCE REPORT', title_style)
-
+ sheet.write_merge(0, 0, 0, 9, 'ATTENDANCE REPORT', title_style)
# Write date range
date_range = f"From: {start_date} To: {end_date}"
- sheet.write_merge(1, 1, 0, 8, date_range, xlwt.easyxf(
+ sheet.write_merge(1, 1, 0, 9, date_range, xlwt.easyxf(
'font: italic on; align: horiz center'
))
# Write headers
headers = [
'Department', 'Employee Name','Week', 'Date', 'Day',
- 'Check-in', 'Check-out', 'Worked Hours', 'Status'
+ 'Check-in', 'Check-out', 'Worked Hours', 'Break Hours', 'Status'
]
for col_num, header in enumerate(headers):
sheet.write(2, col_num, header, header_style)
-
# Write data rows
current_employee = None
for row_num, record in enumerate(attendance_data, start=3):
@@ -345,11 +348,16 @@ class AttendanceReport(models.Model):
else:
sheet.write(row_num, 7, str(record['worked_hours']), data_style)
- sheet.write(row_num, 8, record['status'], status_style)
+ # Break hours formatting
+ if isinstance(record['break_hours'], (float, int)):
+ sheet.write(row_num, 8, float(record['break_hours']), hours_style)
+ else:
+ sheet.write(row_num, 8, str(record['break_hours']), data_style)
+ sheet.write(row_num, 9, record['status'], status_style)
# Add freeze panes (headers will stay visible when scrolling)
sheet.set_panes_frozen(True)
- sheet.set_horz_split_pos(4) # After row 3 (headers)
+ sheet.set_horz_split_pos(3) # After row 3 (headers)
sheet.set_vert_split_pos(0) # No vertical split
# Save to buffer
diff --git a/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js b/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js
index 92faba293..9c495baee 100644
--- a/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js
+++ b/addons_extensions/hr_attendance_extended/static/src/js/attendance_report.js
@@ -206,7 +206,7 @@ export default class AttendanceReport extends Component {
}
async generateReport() {
-
+ debugger;
let { startDate, endDate, selectedEmployeeIds } = this.state;
startDate = $('#from_date').val()
endDate = $('#to_date').val()
@@ -231,7 +231,7 @@ export default class AttendanceReport extends Component {
// Fetch the attendance data based on the date range and selected employees
// const attendanceData = await this.orm.searchRead('hr.attendance', domain, ['employee_id', 'check_in', 'check_out', 'worked_hours']);
const attendanceData = await this.orm.call('attendance.report','get_attendance_report',[$('#dept').val(),$('#emp').val(),startDate,endDate]);
-
+ debugger;
// Group data by employee_id
const rawGroups = this.groupDataByEmployee(attendanceData);
diff --git a/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml b/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml
index f19d0884a..f22ace0f5 100644
--- a/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml
+++ b/addons_extensions/hr_attendance_extended/static/src/xml/attendance_report.xml
@@ -65,6 +65,7 @@
+
+
\ No newline at end of file
diff --git a/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml b/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml
index f528ef4b9..494fabcb6 100644
--- a/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml
+++ b/third_party_addons/ks_dashboard_ninja/static/src/xml/custom_dialog_ai.xml
@@ -6,17 +6,17 @@