FIX BUG
This commit is contained in:
parent
af98a28428
commit
b34dcf6d5b
|
|
@ -1,27 +1,23 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { useEffect, useState, Component ,onMounted, useRef} from "@odoo/owl";
|
||||
import { Component, useEffect, useState, onMounted, useRef, onWillStart } from "@odoo/owl";
|
||||
import { registry } from "@web/core/registry";
|
||||
const actionRegistry = registry.category("actions");
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ActivityMenu } from "@hr_attendance/components/attendance_menu/attendance_menu";
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
|
||||
import { loadJS } from "@web/core/assets";
|
||||
|
||||
export class NetflixProfileContainer extends Component {
|
||||
static template = 'employee_profile_template'; // The template for the profile
|
||||
static template = 'employee_profile_template';
|
||||
static props = ["*"];
|
||||
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
debugger;
|
||||
this.orm = useService("orm");
|
||||
this.effect = useService("effect");
|
||||
this.log_in_out = useRef("log_in_out")
|
||||
this.action = useService("action");
|
||||
this.action = useService("action");
|
||||
this.chart = null;
|
||||
|
||||
|
||||
// Initialize state for storing employee data
|
||||
this.state = useState({
|
||||
login_employee: {
|
||||
name: '',
|
||||
|
|
@ -39,12 +35,30 @@ export class NetflixProfileContainer extends Component {
|
|||
},
|
||||
attendance_lines: [],
|
||||
leaves: []
|
||||
});
|
||||
|
||||
onWillStart(() => loadJS(["/web/static/lib/Chart/Chart.js"]));
|
||||
|
||||
onMounted(async () => {
|
||||
await this.fetchEmployeeData();
|
||||
|
||||
// Make sure Chart is loaded and canvas is available
|
||||
await loadJS(["/web/static/lib/Chart/Chart.js"]);
|
||||
if (typeof Chart === 'undefined') {
|
||||
console.error("Chart.js failed to load");
|
||||
return;
|
||||
}
|
||||
|
||||
const canvas = document.querySelector("#attendanceChart");
|
||||
if (!canvas) {
|
||||
console.warn("Canvas element not found. Skipping chart render.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
onMounted(() => {
|
||||
this.fetchEmployeeData();
|
||||
});
|
||||
}
|
||||
|
||||
hr_timesheets() {
|
||||
this.action.doAction({
|
||||
name: "Timesheets",
|
||||
|
|
@ -52,52 +66,131 @@ export class NetflixProfileContainer extends Component {
|
|||
res_model: 'account.analytic.line',
|
||||
view_mode: 'list,form',
|
||||
views: [[false, 'list'], [false, 'form']],
|
||||
context: {
|
||||
'search_default_month': true,
|
||||
},
|
||||
context: { 'search_default_month': true },
|
||||
domain: [['employee_id.user_id', '=', this.props.action.context.user_id]],
|
||||
target: 'current'
|
||||
});
|
||||
}
|
||||
|
||||
hr_payslip() {
|
||||
this.action.doAction({
|
||||
name: "Employee Payslips",
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hr.payslip',
|
||||
view_mode: 'list,form,calendar',
|
||||
views: [[false, 'list'],[false, 'form']],
|
||||
domain: [['employee_id','=', this.props.action.context.user_id]],
|
||||
target: 'current'
|
||||
});
|
||||
}
|
||||
async hr_contract() {
|
||||
if(this.isHrManager){
|
||||
this.action.doAction({
|
||||
name: _t("Contracts"),
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hr.contract',
|
||||
view_mode: 'tree,form,calendar',
|
||||
views: [[false, 'list'],[false, 'form']],
|
||||
context: {
|
||||
'search_default_employee_id': this.props.action.context.user_id,
|
||||
},
|
||||
target: 'current'
|
||||
})
|
||||
}
|
||||
attendance_sign_in_out() {
|
||||
if (this.state.login_employee.attendance_state == 'checked_out') {
|
||||
this.state.login_employee.attendance_state = 'checked_in'
|
||||
}
|
||||
else{
|
||||
if (this.state.login_employee.attendance_state == 'checked_in') {
|
||||
this.state.login_employee.attendance_state = 'checked_out'
|
||||
|
||||
renderChart() {
|
||||
const labels = this.state.attendance_lines.map(r => r.create_date);
|
||||
const data = this.state.attendance_lines.map(r => parseFloat(r.worked_hours));
|
||||
|
||||
const ctx = document.querySelector("#attendanceChart");
|
||||
if (!ctx) {
|
||||
console.warn("Canvas context not found for chart.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.chart) {
|
||||
this.chart.destroy();
|
||||
}
|
||||
|
||||
this.chart = new Chart(ctx, {
|
||||
type: "bar", // Base type
|
||||
data: {
|
||||
labels,
|
||||
datasets: [
|
||||
{
|
||||
type: 'bar',
|
||||
label: "Hours Worked (Bar)",
|
||||
data,
|
||||
backgroundColor: "rgba(54, 162, 235, 0.6)",
|
||||
borderColor: "rgba(54, 162, 235, 1)",
|
||||
borderWidth: 1
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: "Hours Worked (Line)",
|
||||
data,
|
||||
backgroundColor: "rgba(255, 99, 132, 0.2)",
|
||||
borderColor: "rgba(255, 99, 132, 1)",
|
||||
borderWidth: 2,
|
||||
tension: 0.3,
|
||||
fill: false,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6
|
||||
}
|
||||
]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: { display: true },
|
||||
tooltip: { mode: 'index', intersect: false }
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
title: {
|
||||
display: true,
|
||||
text: "Hours"
|
||||
}
|
||||
},
|
||||
x: {
|
||||
title: {
|
||||
display: true,
|
||||
text: "Date"
|
||||
},
|
||||
reverse: true
|
||||
}
|
||||
}
|
||||
this.update_attendance()
|
||||
}
|
||||
async update_attendance() {
|
||||
var self = this;
|
||||
var result = await this.orm.call('hr.employee', 'attendance_manual',[[this.props.action.context.user_id]])
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
async attendance_sign_in_out() {
|
||||
const result = await this.orm.call('hr.employee', 'attendance_manual', [[this.props.action.context.user_id]]);
|
||||
if (result) {
|
||||
var attendance_state = this.state.login_employee.attendance_state;
|
||||
var message = ''
|
||||
if (attendance_state == 'checked_in'){
|
||||
message = 'Checked In'
|
||||
let current = this.state.login_employee.attendance_state;
|
||||
this.state.login_employee.attendance_state = (current === 'checked_in') ? 'checked_out' : 'checked_in';
|
||||
|
||||
let mode = this.state.login_employee.attendance_state;
|
||||
let message = mode === 'checked_in' ? 'Checked In' : 'Checked Out';
|
||||
|
||||
this.env.bus.trigger('signin_signout', {
|
||||
mode: "checked_in",
|
||||
mode: mode === 'checked_in' ? "checked_in" : false,
|
||||
});
|
||||
}
|
||||
else if (attendance_state == 'checked_out'){
|
||||
message = 'Checked Out'
|
||||
this.env.bus.trigger('signin_signout', {
|
||||
mode: false,
|
||||
});
|
||||
}
|
||||
|
||||
this.effect.add({
|
||||
message: ("Successfully " + message),
|
||||
message: "Successfully " + message,
|
||||
type: 'rainbow_man',
|
||||
fadeout: "fast",
|
||||
})
|
||||
fadeout: "fast"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
add_attendance() {
|
||||
this.action.doAction({
|
||||
name: ("Attendances"),
|
||||
name: "Attendances",
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hr.attendance',
|
||||
view_mode: 'form',
|
||||
|
|
@ -105,9 +198,10 @@ export class NetflixProfileContainer extends Component {
|
|||
target: 'new'
|
||||
});
|
||||
}
|
||||
|
||||
add_leave() {
|
||||
this.action.doAction({
|
||||
name: ("Leave Request"),
|
||||
name: "Leave Request",
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hr.leave',
|
||||
view_mode: 'form',
|
||||
|
|
@ -116,59 +210,115 @@ export class NetflixProfileContainer extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
// Method to fetch employee data from hr.employee model
|
||||
async fetchEmployeeData() {
|
||||
console.log(this.props.action.context.user_id)
|
||||
|
||||
try {
|
||||
const userId = this.props.action.context.user_id;
|
||||
const employeeData = await this.orm.call("hr.employee", 'get_user_employee_details');
|
||||
if (!employeeData.length) return;
|
||||
|
||||
const employee = employeeData[0];
|
||||
|
||||
const now = new Date();
|
||||
const startOfMonth = new Date(now.getFullYear(), now.getMonth(), 1);
|
||||
const endOfMonth = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
||||
|
||||
// Format as Odoo-compatible strings: 'YYYY-MM-DD'
|
||||
const startDateStr = startOfMonth.toISOString().split('T')[0];
|
||||
const endDateStr = endOfMonth.toISOString().split('T')[0];
|
||||
|
||||
const employeeData = await this.orm.call(
|
||||
"hr.employee",'get_user_employee_details');
|
||||
const attendanceLines = await this.orm.searchRead(
|
||||
'hr.attendance',
|
||||
[['employee_id', '=', employeeData[0].id]],
|
||||
['create_date', 'check_in', 'check_out', 'worked_hours']);
|
||||
const Leaves = await this.orm.searchRead('hr.leave',[['employee_id', '=', employeeData[0].id]],
|
||||
['request_date_from', 'request_date_to', 'state','holiday_status_id']);
|
||||
[
|
||||
['employee_id', '=', employee.id],
|
||||
['create_date', '>=', startDateStr],
|
||||
['create_date', '<=', endDateStr]
|
||||
],
|
||||
['create_date', 'check_in', 'check_out', 'worked_hours']
|
||||
);
|
||||
|
||||
Leaves.forEach(line => {
|
||||
// Extract the 'type' from 'holiday_status_id' and assign it
|
||||
line['type'] = line['holiday_status_id'][1];
|
||||
const leaves = await this.orm.searchRead(
|
||||
'hr.leave',
|
||||
[['employee_id', '=', employee.id]],
|
||||
['request_date_from', 'request_date_to', 'state', 'holiday_status_id']
|
||||
);
|
||||
|
||||
// Change state and set color based on the state
|
||||
if (line['state'] === 'confirm') {
|
||||
line['state'] = 'To Approve';
|
||||
line['color'] = 'orange';
|
||||
} else if (line['state'] === 'validate1') {
|
||||
line['state'] = 'Second Approval';
|
||||
line['color'] = '#7CFC00';
|
||||
} else if (line['state'] === 'validate') {
|
||||
line['state'] = 'Approved';
|
||||
line['color'] = 'green';
|
||||
} else if (line['state'] === 'cancel') {
|
||||
line['state'] = 'Cancelled';
|
||||
line['color'] = 'red';
|
||||
const groupedLines = {};
|
||||
|
||||
attendanceLines.forEach(line => {
|
||||
const createDate = new Date(line.create_date);
|
||||
const dateStr = createDate.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' });
|
||||
|
||||
const workedHours = parseFloat(line.worked_hours);
|
||||
|
||||
const checkIn = new Date(line.check_in + 'Z');
|
||||
const checkOut = new Date(line.check_out + 'Z');
|
||||
|
||||
const checkInStr = checkIn.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata' });
|
||||
const checkOutStr = checkOut.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', timeZone: 'Asia/Kolkata' });
|
||||
|
||||
if (!groupedLines[dateStr]) {
|
||||
groupedLines[dateStr] = {
|
||||
create_date: dateStr,
|
||||
worked_hours: workedHours,
|
||||
check_in: checkInStr,
|
||||
check_out: checkOutStr,
|
||||
earliestCheckIn: checkIn,
|
||||
latestCheckOut: checkOut
|
||||
};
|
||||
} else {
|
||||
line['state'] = 'Refused';
|
||||
line['color'] = 'red';
|
||||
groupedLines[dateStr].worked_hours += workedHours;
|
||||
|
||||
// Update earliest check-in
|
||||
if (checkIn < groupedLines[dateStr].earliestCheckIn) {
|
||||
groupedLines[dateStr].check_in = checkInStr;
|
||||
groupedLines[dateStr].earliestCheckIn = checkIn;
|
||||
}
|
||||
|
||||
// Update latest check-out
|
||||
if (checkOut > groupedLines[dateStr].latestCheckOut) {
|
||||
groupedLines[dateStr].check_out = checkOutStr;
|
||||
groupedLines[dateStr].latestCheckOut = checkOut;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Format final result
|
||||
const groupedAttendance = Object.values(groupedLines).map(line => ({
|
||||
create_date: line.create_date,
|
||||
worked_hours: line.worked_hours.toFixed(2),
|
||||
check_in: line.check_in,
|
||||
check_out: line.check_out
|
||||
}));
|
||||
|
||||
if (employeeData.length > 0) {
|
||||
const employee = employeeData[0];
|
||||
attendanceLines.forEach(line => {
|
||||
let createDate = new Date(line.create_date);
|
||||
line.create_date = createDate.toLocaleDateString('en-IN', { timeZone: 'Asia/Kolkata' }); // Format as 'YYYY-MM-DD'
|
||||
let checkIn = new Date(line.check_in + 'Z');
|
||||
line.check_in = checkIn.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', timeZone:'Asia/Kolkata'}); // Format as 'HH:MM'
|
||||
let checkOut = new Date(line.check_out + 'Z');
|
||||
line.check_out = checkOut.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', timeZone:'Asia/Kolkata'}); // Format as 'HH:MM'
|
||||
line.worked_hours = line.worked_hours.toFixed(2);
|
||||
// Assign to state
|
||||
this.state.attendance_lines = groupedAttendance;
|
||||
this.renderChart();
|
||||
|
||||
leaves.forEach(line => {
|
||||
line.type = line.holiday_status_id[1];
|
||||
switch (line.state) {
|
||||
case 'confirm':
|
||||
line.state = 'To Approve';
|
||||
line.color = 'orange';
|
||||
break;
|
||||
case 'validate1':
|
||||
line.state = 'Second Approval';
|
||||
line.color = '#7CFC00';
|
||||
break;
|
||||
case 'validate':
|
||||
line.state = 'Approved';
|
||||
line.color = 'green';
|
||||
break;
|
||||
case 'cancel':
|
||||
line.state = 'Cancelled';
|
||||
line.color = 'red';
|
||||
break;
|
||||
default:
|
||||
line.state = 'Refused';
|
||||
line.color = 'red';
|
||||
break;
|
||||
}
|
||||
});
|
||||
this.state.attendance_lines = attendanceLines,
|
||||
this.state.leaves = Leaves
|
||||
|
||||
this.state.login_employee = {
|
||||
name: employee.name,
|
||||
|
|
@ -184,27 +334,23 @@ export class NetflixProfileContainer extends Component {
|
|||
private_street: employee.private_street,
|
||||
department_id: employee.department_id[1]
|
||||
};
|
||||
}
|
||||
|
||||
this.state.leaves = leaves;
|
||||
} catch (error) {
|
||||
console.error('Error fetching employee data:', error);
|
||||
console.error("Error fetching employee data:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
registry.category("actions").add("NetflixProfileContainer", NetflixProfileContainer)
|
||||
|
||||
actionRegistry.add("NetflixProfileContainer", NetflixProfileContainer);
|
||||
// Patch the attendance menu to update icon on signin/signout
|
||||
patch(ActivityMenu.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
var self = this
|
||||
onMounted(() => {
|
||||
this.env.bus.addEventListener('signin_signout', ({
|
||||
detail
|
||||
}) => {
|
||||
if (detail.mode == 'checked_in') {
|
||||
self.state.checkedIn = detail.mode
|
||||
} else {
|
||||
self.state.checkedIn = false
|
||||
this.env.bus.addEventListener('signin_signout', ({ detail }) => {
|
||||
this.state.checkedIn = detail.mode === "checked_in" ? true : false;
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@
|
|||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stat-att" t-on-click="attendance_sign_in_out">
|
||||
<t t-if="this.state.login_employee.attendance_state == 'checked_out'">
|
||||
<div class="stat-atticon">
|
||||
|
|
@ -114,6 +115,25 @@
|
|||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card" t-on-click="hr_payslip">
|
||||
<div class="stat-icon">
|
||||
<i class="fa fa-id-card">
|
||||
</i>
|
||||
</div>
|
||||
<div class="stat-content">
|
||||
<p>
|
||||
Payslips
|
||||
</p>
|
||||
<h2>
|
||||
<t t-esc="this.state.login_employee['emp_timesheets']" />
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container" >
|
||||
<div style="width: 100%; max-width: 600px; display: block; margin-left: 0;">
|
||||
<canvas id="attendanceChart" width="600" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tables-section">
|
||||
<!-- Attendance Table -->
|
||||
|
|
@ -147,7 +167,8 @@
|
|||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="this.state.attendance_lines" t-as="line" t-key="line['id']">
|
||||
<tr t-foreach="state.attendance_lines" t-as="line" t-key="line.create_date">
|
||||
|
||||
<td t-esc="line.create_date">
|
||||
</td>
|
||||
<td t-esc="line['check_in']">
|
||||
|
|
|
|||
|
|
@ -172,39 +172,39 @@ class BiometricDeviceDetails(models.Model):
|
|||
if conn:
|
||||
conn.disable_device()
|
||||
self.get_all_users()
|
||||
# self.action_set_timezone()
|
||||
user = conn.get_users()
|
||||
# get All Fingerprints
|
||||
fingers = conn.get_templates()
|
||||
for use in user:
|
||||
for finger in fingers:
|
||||
if finger.uid == use.uid:
|
||||
templates = conn.get_user_template(uid=use.uid,
|
||||
temp_id=finger.fid,
|
||||
user_id=use.user_id)
|
||||
hex_data = templates.template.hex()
|
||||
# Convert hex data to binary
|
||||
binary_data = binascii.unhexlify(hex_data)
|
||||
base64_data = base64.b64encode(binary_data).decode(
|
||||
'utf-8')
|
||||
employee = self.env['hr.employee'].search(
|
||||
[('device_id_num', '=', use.user_id),('company_id', '=', self.env.company.id)],limit=1)
|
||||
employee.device_ids |= self
|
||||
if str(finger.fid) in employee.fingerprint_ids.mapped(
|
||||
'finger_id'):
|
||||
employee.fingerprint_ids.search(
|
||||
[('finger_id', '=', finger.fid)]).update({
|
||||
'finger_template': base64_data,
|
||||
})
|
||||
else:
|
||||
employee.fingerprint_ids.create({
|
||||
'finger_template': base64_data,
|
||||
'finger_id': finger.fid,
|
||||
'employee_bio_id': employee.id,
|
||||
'filename': f'{employee.name}-finger-{finger.fid}'
|
||||
})
|
||||
# fingers = conn.get_templates()
|
||||
# for use in user:
|
||||
# for finger in fingers:
|
||||
# if finger.uid == use.uid:
|
||||
# templates = conn.get_user_template(uid=use.uid,
|
||||
# temp_id=finger.fid,
|
||||
# user_id=use.user_id)
|
||||
# hex_data = templates.template.hex()
|
||||
# # Convert hex data to binary
|
||||
# binary_data = binascii.unhexlify(hex_data)
|
||||
# base64_data = base64.b64encode(binary_data).decode(
|
||||
# 'utf-8')
|
||||
# employee = self.env['hr.employee'].search(
|
||||
# [('device_id_num', '=', use.user_id),('company_id', '=', self.env.company.id)],limit=1)
|
||||
# employee.device_ids |= self
|
||||
# if str(finger.fid) in employee.fingerprint_ids.mapped(
|
||||
# 'finger_id'):
|
||||
# employee.fingerprint_ids.search(
|
||||
# [('finger_id', '=', finger.fid)]).update({
|
||||
# 'finger_template': base64_data,
|
||||
# })
|
||||
# else:
|
||||
# employee.fingerprint_ids.create({
|
||||
# 'finger_template': base64_data,
|
||||
# 'finger_id': finger.fid,
|
||||
# 'employee_bio_id': employee.id,
|
||||
# 'filename': f'{employee.name}-finger-{finger.fid}'
|
||||
# })
|
||||
# get all attendances
|
||||
print(help(zk.get_attendance))
|
||||
attendance = conn.get_attendance()
|
||||
print(attendance)
|
||||
if attendance:
|
||||
filtered_attendance = []
|
||||
|
||||
|
|
@ -240,53 +240,50 @@ class BiometricDeviceDetails(models.Model):
|
|||
|
||||
for uid in user:
|
||||
if uid.user_id == each.user_id:
|
||||
get_user_id = self.env['hr.employee'].search(
|
||||
[('device_id_num', '=', each.user_id), ('company_id', '=', self.env.company.id)],limit=1)
|
||||
check_in_today = hr_attendance.search([(
|
||||
'employee_id', '=', get_user_id.id),
|
||||
('check_in', '!=', False),('check_out', '=',False)])
|
||||
from datetime import timedelta
|
||||
employee = self.env['hr.employee'].search([
|
||||
('device_id_num', '=', each.user_id),
|
||||
('company_id', '=', self.env.company.id)
|
||||
], limit=1)
|
||||
|
||||
# Define the tolerance (10 minutes)
|
||||
tolerance = timedelta(minutes=10)
|
||||
if not employee:
|
||||
continue
|
||||
|
||||
# Convert the atten_time string to a datetime object
|
||||
|
||||
# Calculate the lower and upper bounds with the tolerance
|
||||
# Convert atten_time to datetime
|
||||
atten_time_obj = datetime.datetime.strptime(atten_time, "%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# Calculate the lower and upper bounds with the tolerance
|
||||
lower_bound = atten_time_obj - tolerance
|
||||
upper_bound = atten_time_obj + tolerance
|
||||
# Find existing attendance for today
|
||||
start_of_day = atten_time_obj.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = atten_time_obj.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
|
||||
# Ensure the 'check_in' and 'check_out' fields are datetime objects and compare them
|
||||
next_in = hr_attendance.search([
|
||||
('employee_id', '=', get_user_id.id),
|
||||
('check_in', '>=', lower_bound),
|
||||
('check_in', '<=', upper_bound)
|
||||
])
|
||||
attendance_today = self.env['hr.attendance'].search([
|
||||
('employee_id', '=', employee.id),
|
||||
('check_in', '>=', start_of_day),
|
||||
('check_in', '<=', end_of_day)
|
||||
], limit=1)
|
||||
|
||||
next_out = hr_attendance.search([
|
||||
('employee_id', '=', get_user_id.id),
|
||||
('check_out', '>=', lower_bound),
|
||||
('check_out', '<=', upper_bound)
|
||||
])
|
||||
if get_user_id:
|
||||
if self.display_name == 'IN' and not check_in_today:
|
||||
if next_in:
|
||||
continue
|
||||
hr_attendance.create({
|
||||
'employee_id':get_user_id.id,
|
||||
'check_in': atten_time,
|
||||
# IN logic
|
||||
if self.display_name == 'IN':
|
||||
if not attendance_today:
|
||||
# No attendance yet, create new with check_in only
|
||||
self.env['hr.attendance'].create({
|
||||
'employee_id': employee.id,
|
||||
'check_in': atten_time_obj,
|
||||
})
|
||||
get_user_id.attendance_state = 'checked_in'
|
||||
elif check_in_today and self.display_name != 'IN':
|
||||
if fields.Datetime.to_string(check_in_today.check_in) > atten_time or next_out:
|
||||
employee.attendance_state = 'checked_in'
|
||||
else:
|
||||
attendance_today.check_out = False
|
||||
employee.attendance_state = 'checked_in'
|
||||
continue
|
||||
check_in_today.write({
|
||||
'check_out': atten_time,
|
||||
|
||||
# OUT logic
|
||||
elif self.display_name != 'IN':
|
||||
if attendance_today:
|
||||
# Only update checkout if it's not set or is earlier than current atten_time
|
||||
if not attendance_today.check_out or attendance_today.check_out < atten_time_obj:
|
||||
attendance_today.write({
|
||||
'check_out': atten_time_obj,
|
||||
})
|
||||
get_user_id.attendance_state = 'checked_out'
|
||||
employee.attendance_state = 'checked_out'
|
||||
|
||||
else:
|
||||
pass
|
||||
|
|
@ -296,7 +293,7 @@ class BiometricDeviceDetails(models.Model):
|
|||
('company_id', '=', self.env.company.id)])
|
||||
if not duplicate_atten_ids:
|
||||
zk_attendance.create({
|
||||
'employee_id': get_user_id.id,
|
||||
'employee_id': employee.id,
|
||||
'device_id_num': each.user_id,
|
||||
'attendance_type': str(1),
|
||||
'punch_type': '0' if self.display_name == 'IN' else '1',
|
||||
|
|
@ -566,7 +563,7 @@ class BiometricDeviceDetails(models.Model):
|
|||
"group_id: %s\n"
|
||||
"user_id: %s\n"
|
||||
"Here is the debugging information:\n%s\n"
|
||||
"Try Restarting the device")
|
||||
"Try Reqing the device")
|
||||
% (candidate_uid, employee.name, privilege, password,
|
||||
group_id, str(candidate_uid), e))
|
||||
conn.enable_device()
|
||||
|
|
|
|||
Loading…
Reference in New Issue