You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
13 KiB
270 lines
13 KiB
# -*- coding: utf-8 -*-
|
|
###################################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
|
|
# Author: Jesni Banu(<https://www.cybrosys.com>)
|
|
#
|
|
# This program is free software: you can modify
|
|
# it under the terms of the GNU Affero General Public License (AGPL) as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
###################################################################################
|
|
import pytz
|
|
import sys
|
|
import datetime
|
|
try:
|
|
from zklib import zklib
|
|
from zklib.zkconst import *
|
|
sys.path.append("zklib")
|
|
except ImportError:
|
|
pass
|
|
from struct import unpack
|
|
from odoo import api, fields, models
|
|
from odoo import _
|
|
from odoo.exceptions import UserError, ValidationError
|
|
|
|
|
|
class HrAttendance(models.Model):
|
|
_inherit = 'hr.attendance'
|
|
|
|
device_id = fields.Char(string='Biometric Device ID')
|
|
|
|
|
|
class ZkMachine(models.Model):
|
|
_name = 'zk.machine'
|
|
|
|
name = fields.Char(string='Machine IP', required=True)
|
|
port_no = fields.Integer(string='Port No', required=True)
|
|
address_id = fields.Many2one('res.partner', string='Working Address')
|
|
company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id.id)
|
|
|
|
@api.multi
|
|
def device_connect(self, zk):
|
|
command = CMD_CONNECT
|
|
command_string = ''
|
|
chksum = 0
|
|
session_id = 0
|
|
reply_id = -1 + USHRT_MAX
|
|
buf = zk.createHeader(command, chksum, session_id,
|
|
reply_id, command_string)
|
|
zk.zkclient.sendto(buf, zk.address)
|
|
try:
|
|
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
|
|
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
|
|
command = unpack('HHHH', zk.data_recv[:8])[0]
|
|
if command == 2005:
|
|
conn = True
|
|
else:
|
|
conn = False
|
|
except:
|
|
conn = False
|
|
return conn
|
|
|
|
@api.multi
|
|
def clear_attendance(self):
|
|
for info in self:
|
|
try:
|
|
machine_ip = info.name
|
|
port = info.port_no
|
|
zk = zklib.ZKLib(machine_ip, port)
|
|
conn = self.device_connect(zk)
|
|
if conn:
|
|
zk.enableDevice()
|
|
clear_data = zk.getAttendance()
|
|
if clear_data:
|
|
zk.clearAttendance()
|
|
self._cr.execute("""delete from zk_machine_attendance""")
|
|
else:
|
|
raise UserError(_('Unable to get the attendance log, please try again later.'))
|
|
else:
|
|
raise UserError(_('Unable to connect, please check the parameters and network connections.'))
|
|
except:
|
|
raise ValidationError('Warning !!! Machine is not connected')
|
|
|
|
def getSizeUser(self, zk):
|
|
"""Checks a returned packet to see if it returned CMD_PREPARE_DATA,
|
|
indicating that data packets are to be sent
|
|
|
|
Returns the amount of bytes that are going to be sent"""
|
|
command = unpack('HHHH', zk.data_recv[:8])[0]
|
|
if command == CMD_PREPARE_DATA:
|
|
size = unpack('I', zk.data_recv[8:12])[0]
|
|
return size
|
|
else:
|
|
return False
|
|
|
|
def zkgetuser(self, zk):
|
|
"""Start a connection with the time clock"""
|
|
command = CMD_USERTEMP_RRQ
|
|
command_string = '\x05'
|
|
chksum = 0
|
|
session_id = zk.session_id
|
|
reply_id = unpack('HHHH', zk.data_recv[:8])[3]
|
|
|
|
buf = zk.createHeader(command, chksum, session_id, reply_id, command_string)
|
|
zk.zkclient.sendto(buf, zk.address)
|
|
try:
|
|
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
|
|
|
|
if self.getSizeUser(zk):
|
|
bytes = self.getSizeUser(zk)
|
|
|
|
while bytes > 0:
|
|
data_recv, addr = zk.zkclient.recvfrom(1032)
|
|
zk.userdata.append(data_recv)
|
|
bytes -= 1024
|
|
|
|
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
|
|
data_recv = zk.zkclient.recvfrom(8)
|
|
|
|
users = {}
|
|
if len(zk.userdata) > 0:
|
|
userdata = ''.join(zk.userdata[0])
|
|
userdata = userdata[11:]
|
|
while len(userdata) > 72:
|
|
uid, role, password, name, userid = unpack('2s2s8s28sx31s', userdata.ljust(72)[:72])
|
|
uid = int(uid.encode("hex"), 16)
|
|
# Clean up some messy characters from the user name
|
|
password = password.split('\x00', 1)[0]
|
|
password = unicode(password.strip('\x00|\x01\x10x|\x000'), errors='ignore')
|
|
# uid = uid.split('\x00', 1)[0]
|
|
userid = unicode(userid.strip('\x00|\x01\x10x|\x000|\x9aC'), errors='ignore')
|
|
name = name.split('\x00', 1)[0]
|
|
if name.strip() == "":
|
|
name = uid
|
|
users[uid] = (userid, name, int(role.encode("hex"), 16), password)
|
|
userdata = userdata[72:]
|
|
return users
|
|
except:
|
|
return False
|
|
|
|
@api.multi
|
|
def download_attendance(self):
|
|
zk_attendance = self.env['zk.machine.attendance']
|
|
att_obj = self.env['hr.attendance']
|
|
for info in self:
|
|
machine_ip = info.name
|
|
port = info.port_no
|
|
zk = zklib.ZKLib(machine_ip, port)
|
|
conn = self.device_connect(zk)
|
|
if conn:
|
|
zk.enableDevice()
|
|
user = self.zkgetuser(zk)
|
|
command = CMD_ATTLOG_RRQ
|
|
command_string = ''
|
|
chksum = 0
|
|
session_id = zk.session_id
|
|
reply_id = unpack('HHHH', zk.data_recv[:8])[3]
|
|
buf = zk.createHeader(command, chksum, session_id,
|
|
reply_id, command_string)
|
|
zk.zkclient.sendto(buf, zk.address)
|
|
try:
|
|
zk.data_recv, addr = zk.zkclient.recvfrom(1024)
|
|
command = unpack('HHHH', zk.data_recv[:8])[0]
|
|
if command == CMD_PREPARE_DATA:
|
|
size = unpack('I', zk.data_recv[8:12])[0]
|
|
zk_size = size
|
|
else:
|
|
zk_size = False
|
|
if zk_size:
|
|
bytes = zk_size
|
|
while bytes > 0:
|
|
data_recv, addr = zk.zkclient.recvfrom(1032)
|
|
zk.attendancedata.append(data_recv)
|
|
bytes -= 1024 # 1024
|
|
zk.session_id = unpack('HHHH', zk.data_recv[:8])[2]
|
|
data_recv = zk.zkclient.recvfrom(8)
|
|
attendance = []
|
|
if len(zk.attendancedata) > 0:
|
|
# The first 4 bytes don't seem to be related to the user
|
|
for x in xrange(len(zk.attendancedata)):
|
|
if x > 0:
|
|
zk.attendancedata[x] = zk.attendancedata[x][8:]
|
|
attendancedata = ''.join(zk.attendancedata)
|
|
attendancedata = attendancedata[14:]
|
|
while len(attendancedata) > 0:
|
|
uid, state, timestamp, space = unpack('24s1s4s11s', attendancedata.ljust(40)[:40])
|
|
pls = unpack('c', attendancedata[29:30])
|
|
uid = uid.split('\x00', 1)[0]
|
|
tmp = ''
|
|
for i in reversed(xrange(len(timestamp.encode('hex')) / 2)):
|
|
tmp += timestamp.encode('hex')[i * 2:(i * 2) + 2]
|
|
attendance.append((uid, int(state.encode('hex'), 16),
|
|
decode_time(int(tmp, 16)), unpack('HHHH', space[:8])[0]))
|
|
attendancedata = attendancedata[40:]
|
|
except:
|
|
attendance = False
|
|
if attendance:
|
|
for each in attendance:
|
|
atten_time = each[2]
|
|
atten_time = datetime.strptime(
|
|
atten_time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S')
|
|
local_tz = pytz.timezone(
|
|
self.env.user.partner_id.tz or 'GMT')
|
|
local_dt = local_tz.localize(atten_time, is_dst=None)
|
|
utc_dt = local_dt.astimezone(pytz.utc)
|
|
utc_dt = utc_dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
atten_time = datetime.strptime(
|
|
utc_dt, "%Y-%m-%d %H:%M:%S")
|
|
atten_time = fields.Datetime.to_string(atten_time)
|
|
if user:
|
|
for uid in user:
|
|
if user[uid][0] == str(each[0]):
|
|
get_user_id = self.env['hr.employee'].search(
|
|
[('device_id', '=', str(each[0]))])
|
|
if get_user_id:
|
|
duplicate_atten_ids = zk_attendance.search(
|
|
[('device_id', '=', str(each[0])), ('punching_time', '=', atten_time)])
|
|
if duplicate_atten_ids:
|
|
continue
|
|
else:
|
|
zk_attendance.create({'employee_id': get_user_id.id,
|
|
'device_id': each[0],
|
|
'attendance_type': str(each[1]),
|
|
'punch_type': str(each[3]),
|
|
'punching_time': atten_time,
|
|
'address_id': info.address_id.id})
|
|
att_var = att_obj.search([('employee_id', '=', get_user_id.id),
|
|
('check_out', '=', False)])
|
|
if each[3] == 0:
|
|
if not att_var:
|
|
att_obj.create({'employee_id': get_user_id.id,
|
|
'check_in': atten_time})
|
|
if each[3] == 1:
|
|
if len(att_var) == 1:
|
|
att_var.write({'check_out': atten_time})
|
|
else:
|
|
att_var1 = att_obj.search([('employee_id', '=', get_user_id.id)])
|
|
if att_var1:
|
|
att_var1[-1].write({'check_out': atten_time})
|
|
|
|
else:
|
|
employee = self.env['hr.employee'].create(
|
|
{'device_id': str(each[0]), 'name': user[uid][1]})
|
|
zk_attendance.create({'employee_id': employee.id,
|
|
'device_id': each[0],
|
|
'attendance_type': str(each[1]),
|
|
'punch_type': str(each[3]),
|
|
'punching_time': atten_time,
|
|
'address_id': info.address_id.id})
|
|
att_obj.create({'employee_id': employee.id,
|
|
'check_in': atten_time})
|
|
else:
|
|
pass
|
|
zk.enableDevice()
|
|
zk.disconnect()
|
|
return True
|
|
else:
|
|
raise UserError(_('Unable to get the attendance log, please try again later.'))
|
|
else:
|
|
raise UserError(_('Unable to connect, please check the parameters and network connections.'))
|
|
|