Compare commits

...

50 Commits

Author SHA1 Message Date
administrator 2cabd2df96 Initial commit 2025-07-01 17:21:16 +05:30
administrator dc817254cc Initial commit 2025-07-01 17:21:16 +05:30
administrator d61ee6455e Initial commit 2025-07-01 17:21:16 +05:30
administrator db3083e9b9 Initial commit 2025-07-01 17:21:16 +05:30
administrator 733decd14f Initial commit 2025-07-01 17:21:16 +05:30
administrator f41bf0e0bc Initial commit 2025-07-01 17:21:16 +05:30
administrator 398f27188a Initial commit 2025-07-01 17:21:16 +05:30
administrator d1e8cec626 Initial commit 2025-07-01 17:21:16 +05:30
administrator b7c06d2062 Initial commit 2025-07-01 17:21:16 +05:30
administrator a03a472c78 Initial commit 2025-07-01 17:21:16 +05:30
administrator 66ac001dce Initial commit 2025-07-01 17:21:16 +05:30
administrator 3c80eae8f0 Initial commit 2025-07-01 17:21:16 +05:30
administrator e1d6f6b669 Initial commit 2025-07-01 17:21:16 +05:30
administrator a126df1274 Initial commit 2025-07-01 17:21:16 +05:30
administrator 0a440b9c87 Initial commit 2025-07-01 17:21:16 +05:30
administrator d11c74ecee Initial commit 2025-07-01 17:21:16 +05:30
administrator cf6af5239f Initial commit 2025-07-01 17:21:16 +05:30
administrator e3947a927e Initial commit 2025-07-01 17:21:16 +05:30
administrator 716be65c65 Initial commit 2025-07-01 17:21:16 +05:30
administrator e3edf042bd Initial commit 2025-07-01 17:21:15 +05:30
administrator 6b5975d2f5 Initial commit 2025-07-01 17:21:15 +05:30
administrator a43d6c2e96 pull commit 2025-07-01 17:21:15 +05:30
administrator 70ea396840 Initial commit 2025-07-01 17:21:15 +05:30
Pranay 4062fed0de TimeOff Fix 2025-07-01 17:21:15 +05:30
Pranay 55daddcb33 time-off FIX 2025-07-01 17:21:15 +05:30
Pranay d3356bde83 Recruitment Changes 2025-07-01 17:21:15 +05:30
Pranay 9cabc99092 fix whatsapp 2025-07-01 17:21:15 +05:30
Pranay 5e279ca14e update whatsapp code 2025-07-01 17:21:15 +05:30
administrator 82d43000f5 Initial commit 2025-07-01 17:21:15 +05:30
administrator 15141c3a70 Initial commit 2025-07-01 17:21:15 +05:30
administrator 68063edf95 Initial commit 2025-07-01 17:21:15 +05:30
administrator bfea4e033a Initial commit 2025-07-01 17:21:15 +05:30
administrator 0ac4ea2957 Initial commit 2025-07-01 17:21:15 +05:30
administrator 0af6c6c0b8 Initial commit 2025-07-01 17:21:15 +05:30
administrator 3388c6b091 Initial commit 2025-07-01 17:21:15 +05:30
administrator 459b3a4be1 Initial commit 2025-07-01 17:21:15 +05:30
administrator 9f46b4cf21 Initial commit 2025-07-01 17:21:15 +05:30
administrator d20318d8c3 Initial commit 2025-07-01 17:21:15 +05:30
administrator eb16684b6c Initial commit 2025-07-01 17:21:15 +05:30
administrator 4fdbc336a7 Initial commit 2025-07-01 17:21:15 +05:30
administrator bb46aafa95 Initial commit 2025-07-01 17:21:15 +05:30
administrator 0019297afc Initial commit 2025-07-01 17:21:15 +05:30
administrator 0d649dd8bf Initial commit 2025-07-01 17:21:15 +05:30
administrator ac53c7ca8d Initial commit 2025-07-01 17:21:14 +05:30
administrator fccfe56b41 Initial commit 2025-07-01 17:21:14 +05:30
administrator eca1a38294 Initial commit 2025-07-01 17:21:14 +05:30
administrator f2897086a2 Initial commit 2025-07-01 17:21:14 +05:30
administrator 555da86614 Initial commit 2025-07-01 17:21:14 +05:30
Pranay ab3163978d duplication issue employer history, eudcation history 2025-07-01 17:19:07 +05:30
Pranay 08e93e44c5 attendance changes 2025-07-01 16:59:55 +05:30
7 changed files with 423 additions and 128 deletions

View File

@ -104,3 +104,10 @@ class AttendanceData(models.Model):
# extra_hours = fields.Float() # extra_hours = fields.Float()
status = fields.Selection([('leave','On Leave'),('present','Present'),('no_info','No Information')]) status = fields.Selection([('leave','On Leave'),('present','Present'),('no_info','No Information')])
attendance_id = fields.Many2one('attendance.attendance') attendance_id = fields.Many2one('attendance.attendance')
class HRAttendnace(models.Model):
_inherit = 'hr.attendance'
employee_id = fields.Many2one(group_expand='')

View File

@ -1,4 +1,4 @@
from odoo import models, fields, api from odoo import models, fields, api,_
from datetime import datetime, timedelta from datetime import datetime, timedelta
import xlwt import xlwt
from io import BytesIO from io import BytesIO
@ -13,15 +13,19 @@ class AttendanceReport(models.Model):
_name = 'attendance.report' _name = 'attendance.report'
_description = 'Attendance Report' _description = 'Attendance Report'
@api.model @api.model
def get_attendance_report(self, employee_id, start_date, end_date): def get_attendance_report(self, department_id, employee_id, start_date, end_date):
# Ensure start_date and end_date are in the correct format (datetime) # Ensure start_date and end_date are in the correct format (datetime)
if employee_id == '-': if employee_id == '-':
employee_id = False employee_id = False
else: else:
employee_id = int(employee_id) employee_id = int(employee_id)
if department_id == '-':
department_id = False
else:
department_id = int(department_id)
if isinstance(start_date, str): if isinstance(start_date, str):
start_date = datetime.strptime(start_date, '%d/%m/%Y') start_date = datetime.strptime(start_date, '%d/%m/%Y')
if isinstance(end_date, str): if isinstance(end_date, str):
@ -31,17 +35,59 @@ class AttendanceReport(models.Model):
start_date_str = start_date.strftime('%Y-%m-%d') start_date_str = start_date.strftime('%Y-%m-%d')
end_date_str = end_date.strftime('%Y-%m-%d') end_date_str = end_date.strftime('%Y-%m-%d')
# Define the base where condition # Initialize parameters list with date range for date_range CTE
if employee_id: params = [start_date_str, end_date_str]
case = """WHERE emp.id = %s AND at.check_in >= %s AND at.check_out <= %s"""
params = (employee_id, start_date_str, end_date_str)
else:
case = """WHERE at.check_in >= %s AND at.check_out <= %s"""
params = (start_date_str, end_date_str)
# Define the query with improved date handling # Build conditions for employee_dates CTE
emp_date_conditions = []
emp_date_params = []
if employee_id:
emp_date_conditions.append("emp.id = %s")
emp_date_params.append(employee_id)
if department_id:
emp_date_conditions.append("emp.department_id = %s")
emp_date_params.append(department_id)
# Build conditions for daily_checkins CTE
checkin_conditions = ["at.check_in >= %s", "at.check_out <= %s"]
checkin_params = [start_date_str, end_date_str]
if employee_id:
checkin_conditions.append("emp.id = %s")
checkin_params.append(employee_id)
if department_id:
checkin_conditions.append("emp.department_id = %s")
checkin_params.append(department_id)
# Define the query
query = """ query = """
WITH daily_checkins AS ( 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 SELECT
emp.id, emp.id,
emp.name, emp.name,
@ -50,97 +96,276 @@ class AttendanceReport(models.Model):
at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out, at.check_out AT TIME ZONE 'UTC' AT TIME ZONE 'Asia/Kolkata' AS check_out,
at.worked_hours, 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) 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 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 FROM
hr_attendance at hr_attendance at
LEFT JOIN LEFT JOIN
hr_employee emp ON at.employee_id = emp.id hr_employee emp ON at.employee_id = emp.id
""" + case + """ 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 SELECT
id, ed.employee_id AS id,
name, ed.employee_name AS name,
date, ed.date,
MAX(CASE WHEN first_checkin_row = 1 THEN check_in END) AS first_check_in, 'Week ' || ed.week_number || ' (' || ed.week_range || ')' AS week_info,
MAX(CASE WHEN last_checkout_row = 1 THEN check_out END) AS last_check_out, TRIM(ed.day_name) AS day_name,
SUM(worked_hours) AS total_worked_hours 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 FROM
daily_checkins employee_dates ed
GROUP BY LEFT JOIN
id, name, date 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 ORDER BY
id, date; ed.employee_id, ed.date;
""" """
# Execute the query with parameters # Combine all parameters in the correct order:
self.env.cr.execute(query, params) # 1. date_range params (start_date_str, end_date_str)
rows = self.env.cr.dictfetchall() # 2. employee_dates params (emp_date_params)
data = [] # 3. daily_checkins params (checkin_params)
a = 0 # 4. leave_data params (start_date_str, end_date_str)
for r in rows: all_params = [
a += 1 start_date_str, end_date_str, # date_range
# Calculate worked hours in Python, but here it's better done in the query itself. *emp_date_params, # employee_dates
worked_hours = r['last_check_out'] - r['first_check_in'] if r['first_check_in'] and r[ *checkin_params, # daily_checkins
'last_check_out'] else 0 start_date_str, end_date_str # leave_data
]
data.append({ try:
'id': a, # Execute the query with parameters
'employee_id': r['id'], self.env.cr.execute(query, all_params)
'employee_name': r['name'], rows = self.env.cr.dictfetchall()
'date': r['date'],
'check_in': r['first_check_in'], data = []
'check_out': r['last_check_out'], for idx, r in enumerate(rows, 1):
'worked_hours': worked_hours, data.append({
}) 'id': idx,
'employee_id': r['id'],
'employee_name': r['name'],
'employee_department': r['department'],
'date': r['date'],
'week_info': r['week_info'],
'day_name': r['day_name'],
'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,
'status': r['status']
})
return data
except Exception as e:
error_msg = f"Error executing attendance report query: {str(e)}"
print(error_msg)
raise UserError(
_("An error occurred while generating the attendance report. Please check the logs for details."))
return data
@api.model @api.model
def export_to_excel(self, employee_id, start_date, end_date): def export_to_excel(self, department_id, employee_id, start_date, end_date):
# Fetch the attendance data (replace with your logic to fetch attendance data) attendance_data = self.get_attendance_report(department_id, employee_id, start_date, end_date)
attendance_data = self.get_attendance_report(employee_id, start_date, end_date)
if not attendance_data: if not attendance_data:
raise UserError("No data to export!") raise UserError("No data to export!")
# Create an Excel workbook and a sheet # Create workbook and sheet
workbook = xlwt.Workbook() workbook = xlwt.Workbook(encoding='utf-8')
sheet = workbook.add_sheet('Attendance Report') sheet = workbook.add_sheet('Attendance Report')
# Define the column headers # Define styles - using only xlwt supported color names
headers = ['Employee Name', 'Check-in', 'Check-out', 'Worked Hours'] title_style = xlwt.easyxf(
'font: bold on, height 300, color white;'
'pattern: pattern solid, fore_color dark_blue;'
'align: vert centre, horiz center;'
)
header_style = xlwt.easyxf(
'font: bold on, height 240, color white;'
'pattern: pattern solid, fore_color dark_blue;' # Changed from navy to dark_blue
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz center'
)
data_style = xlwt.easyxf(
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left'
)
time_style = xlwt.easyxf(
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left',
num_format_str='YYYY-MM-DD HH:MM:SS'
)
date_style = xlwt.easyxf(
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left',
num_format_str='YYYY-MM-DD'
)
hours_style = xlwt.easyxf(
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz right',
num_format_str='0.00'
)
# Status color styles using supported colors
status_present = xlwt.easyxf(
'font: color green;'
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left'
)
status_leave = xlwt.easyxf(
'font: color red;'
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left'
)
status_halfday = xlwt.easyxf(
'font: color orange;'
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left'
)
status_na = xlwt.easyxf(
'font: color blue;'
'borders: left thin, right thin, top thin, bottom thin;'
'align: vert centre, horiz left'
)
# Set column widths (in units of 1/256 of a character width)
col_widths = [6000, 8000, 7000, 3000, 4000, 5000, 5000, 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)
# Write date range
date_range = f"From: {start_date} To: {end_date}"
sheet.write_merge(1, 1, 0, 8, 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'
]
# Write headers to the first row
for col_num, header in enumerate(headers): for col_num, header in enumerate(headers):
sheet.write(0, col_num, header) sheet.write(2, col_num, header, header_style)
# Write the attendance data to the sheet # Write data rows
for row_num, record in enumerate(attendance_data, start=1): current_employee = None
sheet.write(row_num, 0, record['employee_name']) for row_num, record in enumerate(attendance_data, start=3):
sheet.write(row_num, 1, record['check_in'].strftime("%Y-%m-%d %H:%M:%S")) # Highlight employee changes with a subtle border
sheet.write(row_num, 2, record['check_out'].strftime("%Y-%m-%d %H:%M:%S")) if current_employee != record['employee_name']:
if isinstance(record['worked_hours'], timedelta): current_employee = record['employee_name']
hours = record['worked_hours'].seconds // 3600 sheet.row(row_num).height = 400 # Slightly taller row for new employee
minutes = (record['worked_hours'].seconds % 3600) // 60
# Format as "X hours Y minutes" # Apply appropriate status style
worked_hours_str = f"{record['worked_hours'].days * 24 + hours} hours {minutes} minutes" if 'Present' in record['status']:
sheet.write(row_num, 3, worked_hours_str) status_style = status_present
elif 'Half day' in record['status']:
status_style = status_halfday
elif 'on ' in record['status']:
status_style = status_leave
else: else:
sheet.write(row_num, 3, record['worked_hours']) status_style = status_na
# Save the workbook to a BytesIO buffer
# Write data
sheet.write(row_num, 0, record['employee_department'], data_style)
sheet.write(row_num, 1, record['employee_name'], data_style)
sheet.write(row_num, 2, record['week_info'], data_style)
sheet.write(row_num, 3, record['date'], date_style)
sheet.write(row_num, 4, record['day_name'], data_style)
# Check-in/Check-out times
if record['check_in']:
sheet.write(row_num, 5, record['check_in'].strftime("%Y-%m-%d %H:%M:%S"), time_style)
else:
sheet.write(row_num, 5, '', data_style)
if record['check_out']:
sheet.write(row_num, 6, record['check_out'].strftime("%Y-%m-%d %H:%M:%S"), time_style)
else:
sheet.write(row_num, 6, '', data_style)
# Worked hours formatting
if isinstance(record['worked_hours'], (float, int)):
sheet.write(row_num, 7, float(record['worked_hours']), hours_style)
else:
sheet.write(row_num, 7, str(record['worked_hours']), data_style)
sheet.write(row_num, 8, 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_vert_split_pos(0) # No vertical split
# Save to buffer
output = BytesIO() output = BytesIO()
workbook.save(output) workbook.save(output)
# Convert the output to base64 for saving in Odoo
file_data = base64.b64encode(output.getvalue()) file_data = base64.b64encode(output.getvalue())
# Create an attachment record to save the Excel file in Odoo # Create attachment with timestamp
report_date = datetime.now().strftime("%Y-%m-%d_%H-%M")
filename = f"Attendance_Report_{report_date}.xls"
attachment = self.env['ir.attachment'].create({ attachment = self.env['ir.attachment'].create({
'name': 'attendance_report.xls', 'name': filename,
'type': 'binary', 'type': 'binary',
'datas': file_data, 'datas': file_data,
'mimetype': 'application/vnd.ms-excel', 'mimetype': 'application/vnd.ms-excel',
}) })
# Return the attachment's URL to allow downloading in the Odoo UI return '/web/content/%d/%s' % (attachment.id, attachment.name),
return '/web/content/%d/%s' % (attachment.id, attachment.name),

View File

@ -16,7 +16,8 @@ export default class AttendanceReport extends Component {
endDate: "", endDate: "",
attendanceData: [], // Initialized as an empty array attendanceData: [], // Initialized as an empty array
groupedData: [], // To store the grouped attendance data by employee_id groupedData: [], // To store the grouped attendance data by employee_id
employeeIDS: [] // List of employee IDs to bind with select dropdown employeeIDS: [], // List of employee IDs to bind with select dropdown
departmentIDS: []
}); });
onWillStart(async () => { onWillStart(async () => {
@ -34,6 +35,7 @@ export default class AttendanceReport extends Component {
}); });
onMounted( () => { onMounted( () => {
this.loademployeeIDS(); this.loademployeeIDS();
this.loaddepartmentIDS();
}); });
} }
@ -53,19 +55,63 @@ export default class AttendanceReport extends Component {
} }
} }
// Initialize Select2 with error handling and ensuring it's initialized only once async loaddepartmentIDS() {
initializeSelect2() { try {
const employeeIDS = this.state.employeeIDS; const department = await this.orm.searchRead('hr.department', [], ['id', 'display_name']);
this.state.departmentIDS = department;
// Ensure the <select> element is initialized only once
const $empSelect = $('#emp');
const from_date = $("#from_date").datepicker({
dateFormat: "dd/mm/yy", // Date format this.initializeSelect2();
showAnim: "slideDown", // Animation this.render();// Initialize Select2 after data is loaded
changeMonth: true, // Allow month selection this.reload();
changeYear: true, // Allow year selection } catch (error) {
yearRange: "2010:2030", // Year range console.error("Error loading departmentIDS:", error);
// ... other options }
}
annotateWeekRowspan(records) {
let result = [];
let weekMap = {};
// Count occurrences of each week_info
for (let row of records) {
if (!weekMap[row.week_info]) {
weekMap[row.week_info] = [];
}
weekMap[row.week_info].push(row);
}
for (let week in weekMap) {
const rows = weekMap[week];
rows[0]._render_week = true;
rows[0]._rowspan = rows.length;
for (let i = 1; i < rows.length; i++) {
rows[i]._render_week = false;
}
result = result.concat(rows);
}
return result;
}
// Initialize Select2 with error handling and ensuring it's initialized only once
initializeSelect2() {
const departmentIDS = this.state.departmentIDS;
const employeeIDS = this.state.employeeIDS;
// Ensure the <select> element is initialized only once
const $deptSelect = $('#dept');
const $empSelect = $('#emp');
const from_date = $("#from_date").datepicker({
dateFormat: "dd/mm/yy", // Date format
showAnim: "slideDown", // Animation
changeMonth: true, // Allow month selection
changeYear: true, // Allow year selection
yearRange: "2010:2030", // Year range
// ... other options
}); });
const to_date = $("#to_date").datepicker({ const to_date = $("#to_date").datepicker({
@ -78,21 +124,45 @@ export default class AttendanceReport extends Component {
}); });
// Debugging the employeeIDS array to verify its structure // Debugging the employeeIDS array to verify its structure
console.log("employeeIDS:", employeeIDS); console.log("employeeIDS:", employeeIDS);
console.log("departmentIDS:", departmentIDS);
if (Array.isArray(departmentIDS) && departmentIDS.length > 0){
$deptSelect.empty();
$deptSelect.append(
`<option value="-">All</option>`
);
departmentIDS.forEach(dept => {
$deptSelect.append(
`<option value="${dept.id}">${dept.display_name}</option>`
);
});
$deptSelect.on('change', (ev) => {
const selectedDepartmentIds = $(ev.target).val();
console.log('Selected Department IDs: ', selectedDepartmentIds);
const selectedDepartments = departmentIDS.filter(dept => selectedDepartmentIds.includes(dept.id.toString()));
console.log('Selected Department: ', selectedDepartments);
})
} else {
console.error("Invalid department data format:", departmentIDS);
}
// Check if employeeIDS is an array and has the necessary properties // Check if employeeIDS is an array and has the necessary properties
if (Array.isArray(employeeIDS) && employeeIDS.length > 0) { if (Array.isArray(employeeIDS) && employeeIDS.length > 0) {
// Clear the current options (if any) // Clear the current options (if any)
$empSelect.empty(); $empSelect.empty();
$empSelect.append(
`<option value="-">All</option>`
);
// Add options for each employee // Add options for each employee
employeeIDS.forEach(emp => { employeeIDS.forEach(emp => {
$empSelect.append( $empSelect.append(
`<option value="${emp.id}">${emp.display_name}</option>` `<option value="${emp.id}">${emp.display_name}</option>`
); );
}); });
$empSelect.append(
`<option value="-">All</option>`
);
// Initialize the select with the 'multiple' attribute for multi-select // Initialize the select with the 'multiple' attribute for multi-select
// $empSelect.attr('multiple', 'multiple'); // $empSelect.attr('multiple', 'multiple');
@ -126,9 +196,8 @@ export default class AttendanceReport extends Component {
domain.push(['employee_id', '=', parseInt($('#emp').val())]); domain.push(['employee_id', '=', parseInt($('#emp').val())]);
} }
try { try {
debugger;
// Fetch the attendance data based on the date range and selected employees // Fetch the attendance data based on the date range and selected employees
const URL = await this.orm.call('attendance.report', 'export_to_excel', [$('#emp').val(), startdate, enddate]); const URL = await this.orm.call('attendance.report', 'export_to_excel', [$('#dept').val(), $('#emp').val(), startdate, enddate]);
window.open(getOrigin()+URL, '_blank'); window.open(getOrigin()+URL, '_blank');
} catch (error) { } catch (error) {
@ -161,10 +230,13 @@ export default class AttendanceReport extends Component {
try { try {
// Fetch the attendance data based on the date range and selected employees // 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.searchRead('hr.attendance', domain, ['employee_id', 'check_in', 'check_out', 'worked_hours']);
const attendanceData = await this.orm.call('attendance.report','get_attendance_report',[$('#emp').val(),startDate,endDate]); const attendanceData = await this.orm.call('attendance.report','get_attendance_report',[$('#dept').val(),$('#emp').val(),startDate,endDate]);
// Group data by employee_id // Group data by employee_id
const groupedData = this.groupDataByEmployee(attendanceData); const rawGroups = this.groupDataByEmployee(attendanceData);
// Annotate week rows inside each group
const groupedData = rawGroups.map(group => this.annotateWeekRowspan(group));
// Update state with the fetched and grouped data // Update state with the fetched and grouped data
this.state.attendanceData = attendanceData; this.state.attendanceData = attendanceData;

View File

@ -15,6 +15,10 @@
<div class="header pt-5" style="text-align:center"> <div class="header pt-5" style="text-align:center">
<h1>Attendance Report</h1> <h1>Attendance Report</h1>
<div class="navbar navbar-expand-lg container"> <div class="navbar navbar-expand-lg container">
<h4 class="p-3 text-nowrap">Department</h4>
<div class="input-group input-group-lg">
<select type="text" id="dept" class="form-control" />
</div>
<h4 class="p-3 text-nowrap">Employee </h4> <h4 class="p-3 text-nowrap">Employee </h4>
<div class="input-group input-group-lg"> <div class="input-group input-group-lg">
<select type="text" id="emp" class="form-control" /> <select type="text" id="emp" class="form-control" />
@ -42,6 +46,12 @@
<t t-else=""> <t t-else="">
<span>Unknown Employee</span> <span>Unknown Employee</span>
</t> </t>
<t t-if="group[0].employee_department">
(<t t-esc="group[0].employee_department"/>)
</t>
<t t-else="">
<span>(Unknown Department)</span>
</t>
</h3> </h3>
<!-- Scrollable Container for the Table --> <!-- Scrollable Container for the Table -->
@ -49,27 +59,27 @@
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr> <tr>
<th>Week</th>
<th>Date</th> <th>Date</th>
<th>Employee</th> <th>Day</th>
<th>Check In</th> <th>Check In</th>
<th>Check Out</th> <th>Check Out</th>
<th>Worked Hours</th> <th>Worked Hours</th>
<th>Status</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr t-foreach="group" t-as="data" t-key="data.id"> <tr t-foreach="group" t-as="data" t-key="data.id">
<t t-if="data._render_week">
<td t-att-rowspan="data._rowspan"><t t-esc="data.week_info"/></td>
</t>
<t t-else=""></t>
<td><t t-esc="data.date"/></td> <td><t t-esc="data.date"/></td>
<td> <td><t t-esc="data.day_name"/></td>
<t t-if="data.employee_id">
<t t-esc="data.employee_name"/>
</t>
<t t-else="">
<span>Unknown Employee</span>
</t>
</td>
<td><t t-esc="data.check_in"/></td> <td><t t-esc="data.check_in"/></td>
<td><t t-esc="data.check_out"/></td> <td><t t-esc="data.check_out"/></td>
<td><t t-esc="data.worked_hours"/></td> <td><t t-esc="data.worked_hours"/></td>
<td><t t-esc="data.status"/></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -18,8 +18,12 @@
'version': '0.1', 'version': '0.1',
# any module necessary for this one to work correctly # any module necessary for this one to work correctly
'depends': ['base','hr','account','mail','hr_skills', 'hr_contract'], 'depends': ['base','hr','account','mail','hr_skills', 'hr_contract'],
# always loaded # always loaded
'data': [ 'data': [
'security/security.xml', 'security/security.xml',

View File

@ -107,31 +107,6 @@
</field> </field>
<br/> <br/>
<br/> <br/>
<h5>Education Details</h5>
<field name="education_history">
<list string="Education Details">
<field name="education_type"/>
<field name="name"/>
<field name="university"/>
<field name="start_year"/>
<field name="end_year"/>
<field name="marks_or_grade"/>
<field name="employee_id" column_invisible="1"/>
</list>
</field>
<br/>
<br/>
<h5>Employer History</h5>
<field name="employer_history">
<list string="Employer Details">
<field name="company_name"/>
<field name="designation"/>
<field name="date_of_joining"/>
<field name="last_working_day"/>
<field name="ctc"/>
<field name="employee_id" column_invisible="1"/>
</list>
</field>
</xpath> </xpath>
<xpath expr="//field[@name='employee_type']" position="attributes"> <xpath expr="//field[@name='employee_type']" position="attributes">
<attribute name="invisible">1</attribute> <attribute name="invisible">1</attribute>

View File

@ -256,6 +256,8 @@ class HRJobRecruitment(models.Model):
rec.submission_status = 'zero' rec.submission_status = 'zero'
experience = fields.Many2one('candidate.experience', string="Experience")
@api.depends('application_ids.submitted_to_client') @api.depends('application_ids.submitted_to_client')
def _compute_no_of_submissions(self): def _compute_no_of_submissions(self):
counts = dict(self.env['hr.applicant']._read_group( counts = dict(self.env['hr.applicant']._read_group(