Browse Source

[ADD] Initial Commit 'hr_zk_attendance'

pull/134/merge
Ajmalcybrosys 6 years ago
parent
commit
16117dba2e
  1. 37
      hr_zk_attendance/README.rst
  2. 22
      hr_zk_attendance/__init__.py
  3. 45
      hr_zk_attendance/__manifest__.py
  4. 14
      hr_zk_attendance/data/download_data.xml
  5. 6
      hr_zk_attendance/doc/RELEASE_NOTES.md
  6. 25
      hr_zk_attendance/models/__init__.py
  7. 106
      hr_zk_attendance/models/machine_analysis.py
  8. 279
      hr_zk_attendance/models/zk_machine.py
  9. 121
      hr_zk_attendance/models/zkattendance.py
  10. 63
      hr_zk_attendance/models/zkconnect.py
  11. 71
      hr_zk_attendance/models/zkconst.py
  12. 82
      hr_zk_attendance/models/zkdevice.py
  13. 56
      hr_zk_attendance/models/zkextendfmt.py
  14. 61
      hr_zk_attendance/models/zkextendoplog.py
  15. 44
      hr_zk_attendance/models/zkface.py
  16. 156
      hr_zk_attendance/models/zklib.py
  17. 23
      hr_zk_attendance/models/zkos.py
  18. 23
      hr_zk_attendance/models/zkpin.py
  19. 43
      hr_zk_attendance/models/zkplatform.py
  20. 23
      hr_zk_attendance/models/zkserialnumber.py
  21. 23
      hr_zk_attendance/models/zkssr.py
  22. 50
      hr_zk_attendance/models/zktime.py
  23. 140
      hr_zk_attendance/models/zkuser.py
  24. 23
      hr_zk_attendance/models/zkversion.py
  25. 23
      hr_zk_attendance/models/zkworkcode.py
  26. 4
      hr_zk_attendance/security/ir.model.access.csv
  27. BIN
      hr_zk_attendance/static/description/banner.gif
  28. BIN
      hr_zk_attendance/static/description/cybro_logo.png
  29. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_1.png
  30. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_2.png
  31. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_3.png
  32. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_4.png
  33. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_5.png
  34. BIN
      hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_6.png
  35. BIN
      hr_zk_attendance/static/description/icon.png
  36. 412
      hr_zk_attendance/static/description/index.html
  37. 69
      hr_zk_attendance/views/zk_machine_attendance_view.xml
  38. 68
      hr_zk_attendance/views/zk_machine_view.xml

37
hr_zk_attendance/README.rst

@ -0,0 +1,37 @@
Biometric Device Integration v12
================================
This Cybrosys's module integrates Odoo attendance with biometric device attendance.
Features
========
* Integrates biometric device(Face+Thumb) with HR attendance.
* Managing attendance automatically
* Keeps zk machine history in Odoo
* Option to configure multiple zk devices
* Option to clear all zk history from both device and Odoo
Technical Notes
===============
Used Libraries:
*This integration is only applicable for the the device ZKteco model 'uFace 202' & 'iFace990'
* zklib
you can install zklib library using "sudo pip install zklib"
Compatible Devices
*ZKteco model 'uFace 202'
*ZKteco model 'iFace990'
Author
=======
* Cybrosys Techno Solutions <https://www.cybrosys.com>
Credits
=======
Developer: Niyas Raphy @ Cybrosys, odoo@cybrosys.com V11
Developer: Jesni Banu @ cybrosys, odoo@cybrosys.com V10
Developer: Basith @ Cybrosys, odoo@cybrosys.com V12
Developer: Varsha Vivek @ Cybrosys, odoo@cybrosys.com V13
Developer: Mostafa Shokiel , mostafa.shokiel@gmail.com

22
hr_zk_attendance/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from . import models

45
hr_zk_attendance/__manifest__.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
{
'name': 'Biometric Device Integration',
'version': '13.0.1.0.0',
'summary': """Integrating Biometric Device (Model: ZKteco uFace 202) With HR Attendance (Face + Thumb)""",
'description': """This module integrates Odoo with the biometric device(Model: ZKteco uFace 202),odoo13,odd,hr,attendance""",
'category': 'Generic Modules/Human Resources',
'author': 'Cybrosys Techno Solutions, Mostafa Shokiel',
'company': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com",
'depends': ['base_setup', 'hr_attendance'],
'data': [
'security/ir.model.access.csv',
'views/zk_machine_view.xml',
'views/zk_machine_attendance_view.xml',
'data/download_data.xml'
],
'images': ['static/description/banner.gif'],
'license': 'AGPL-3',
'demo': [],
'installable': True,
'auto_install': False,
'application': False,
}

14
hr_zk_attendance/data/download_data.xml

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<odoo noupdate="1">
<record forcecreate="True" id="cron_download_data" model="ir.cron">
<field name="name">Download Data</field>
<field eval="True" name="active" />
<field name="user_id" ref="base.user_root" />
<field name="interval_number">10</field>
<field name="interval_type">minutes</field>
<field name="numbercall">-1</field>
<field name="model_id" ref="hr_zk_attendance.model_zk_machine" />
<field name="state">code</field>
<field name="code">model.cron_download()</field>
</record>
</odoo>

6
hr_zk_attendance/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <hr_zk_attendance>
#### 02.11.2019
#### Version 13.0.1.0.0
##### ADD
- Initial commit

25
hr_zk_attendance/models/__init__.py

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from . import zk_machine
from . import machine_analysis
from . import zklib

106
hr_zk_attendance/models/machine_analysis.py

@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from odoo import tools
from odoo import models, fields, api, _
class HrEmployee(models.Model):
_inherit = 'hr.employee'
device_id = fields.Char(string='Biometric Device ID')
class ZkMachine(models.Model):
_name = 'zk.machine.attendance'
_inherit = 'hr.attendance'
@api.constrains('check_in', 'check_out', 'employee_id')
def _check_validity(self):
"""overriding the __check_validity function for employee attendance."""
pass
device_id = fields.Char(string='Biometric Device ID')
punch_type = fields.Selection([('0', 'Check In'),
('1', 'Check Out'),
('2', 'Break Out'),
('3', 'Break In'),
('4', 'Overtime In'),
('5', 'Overtime Out')],
string='Punching Type')
attendance_type = fields.Selection([('1', 'Finger'),
('15', 'Face'),
('2','Type_2'),
('3','Password'),
('4','Card')], string='Category')
punching_time = fields.Datetime(string='Punching Time')
address_id = fields.Many2one('res.partner', string='Working Address')
class ReportZkDevice(models.Model):
_name = 'zk.report.daily.attendance'
_auto = False
_order = 'punching_day desc'
name = fields.Many2one('hr.employee', string='Employee')
punching_day = fields.Datetime(string='Date')
address_id = fields.Many2one('res.partner', string='Working Address')
attendance_type = fields.Selection([('1', 'Finger'),
('15', 'Face'),
('2','Type_2'),
('3','Password'),
('4','Card')],
string='Category')
punch_type = fields.Selection([('0', 'Check In'),
('1', 'Check Out'),
('2', 'Break Out'),
('3', 'Break In'),
('4', 'Overtime In'),
('5', 'Overtime Out')], string='Punching Type')
punching_time = fields.Datetime(string='Punching Time')
def init(self):
tools.drop_view_if_exists(self._cr, 'zk_report_daily_attendance')
query = """
create or replace view zk_report_daily_attendance as (
select
min(z.id) as id,
z.employee_id as name,
z.write_date as punching_day,
z.address_id as address_id,
z.attendance_type as attendance_type,
z.punching_time as punching_time,
z.punch_type as punch_type
from zk_machine_attendance z
join hr_employee e on (z.employee_id=e.id)
GROUP BY
z.employee_id,
z.write_date,
z.address_id,
z.attendance_type,
z.punch_type,
z.punching_time
)
"""
self._cr.execute(query)

279
hr_zk_attendance/models/zk_machine.py

@ -0,0 +1,279 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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
import logging
import binascii
from . import zklib
from .zkconst import *
from struct import unpack
from odoo import api, fields, models
from odoo import _
from odoo.exceptions import UserError, ValidationError
_logger = logging.getLogger(__name__)
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)
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
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]
print("size", size)
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 = zk.userdata[0]
userdata = userdata[11:]
while len(userdata) > 72:
uid, role, password, name, userid = unpack('2s2s8s28sx31s', userdata.ljust(72)[:72])
uid = int(binascii.hexlify(uid), 16)
# print("uid",uid)
# Clean up some messy characters from the user name
password = password.split(b'\x00', 1)[0]
password = str(password.strip(b'\x00|\x01\x10x|\x000').decode('utf-8'))
# uid = uid.split('\x00', 1)[0]
userid = str(userid.strip(b'\x00|\x01\x10x|\x000|\x9aC').decode('utf-8'))
name = name.split(b'\x00', 1)[0].decode('utf-8')
if name.strip() == "":
name = uid
users[uid] = (userid, name, int(binascii.hexlify(role), 16), password)
userdata = userdata[72:]
# print(users)
return users
except:
return False
@api.model
def cron_download(self):
machines = self.env['zk.machine'].search([])
for machine in machines :
machine.download_attendance()
def download_attendance(self):
_logger.info("++++++++++++Cron Executed++++++++++++++++++++++")
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
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 range(len(zk.attendancedata)):
if x > 0:
zk.attendancedata[x] = zk.attendancedata[x][8:]
attendancedata = b''.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(b'\x00', 1)[0].decode('utf-8')
tmp = ''
for i in reversed(range(int(len(binascii.hexlify(timestamp)) / 2))):
tmp += binascii.hexlify(timestamp).decode('utf-8')[i * 2:(i * 2) + 2]
attendance.append((uid, int(binascii.hexlify(state), 16),
decode_time(int(tmp, 16)), unpack('HHHH', space[:8])[0]))
attendancedata = attendancedata[40:]
except Exception as e:
_logger.info("++++++++++++Exception++++++++++++++++++++++", e)
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: #check-in
if not att_var:
att_obj.create({'employee_id': get_user_id.id,
'check_in': atten_time})
if each[3] == 1: #check-out
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.'))

121
hr_zk_attendance/models/zkattendance.py

@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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 binascii
from struct import pack, unpack
from gevent.hub import xrange
from .zkconst import *
def getSizeAttendance(self):
"""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', self.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', self.data_recv[8:12])[0]
return size
else:
return False
def reverseHex(hexstr):
tmp = ''
for i in reversed( xrange( int(len(hexstr)/2) ) ):
tmp += hexstr[i*2:(i*2)+2]
return tmp
def zkgetattendance(self):
"""Start a connection with the time clock"""
command = CMD_ATTLOG_RRQ
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
if getSizeAttendance(self):
bytes = getSizeAttendance(self)
while bytes > 0:
data_recv, addr = self.zkclient.recvfrom(1032)
self.attendancedata.append(data_recv)
bytes -= 1024
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
data_recv = self.zkclient.recvfrom(8)
attendance = []
if len(self.attendancedata) > 0:
# The first 4 bytes don't seem to be related to the user
for x in xrange(len(self.attendancedata)):
if x > 0:
self.attendancedata[x] = self.attendancedata[x][8:]
attendancedata = b''.join( self.attendancedata )
attendancedata = attendancedata[14:]
while len(attendancedata) > 40:
uid, state, timestamp, space = unpack( '24s1s4s11s', attendancedata.ljust(40)[:40] )
# Clean up some messy characters from the user name
#uid = unicode(uid.strip('\x00|\x01\x10x'), errors='ignore')
uid = uid.split(b'\x00', 1)[0].decode('utf-8')
#print "%s, %s, %s" % (uid, state, decode_time( int( reverseHex( timestamp.encode('hex') ), 16 ) ) )
attendance.append( ( uid, int( binascii.hexlify(state), 16 ), decode_time( int( reverseHex( binascii.hexlify(timestamp).decode('utf-8')), 16 ) ) ) )
attendancedata = attendancedata[40:]
return attendance
except:
return False
def zkclearattendance(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_ATTLOG
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

63
hr_zk_attendance/models/zkconnect.py

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkconnect(self):
"""Start a connection with the time clock"""
command = CMD_CONNECT
command_string = ''
chksum = 0
session_id = 0
reply_id = -1 + USHRT_MAX
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.checkValid( self.data_recv )
except:
return False
def zkdisconnect(self):
"""Disconnect from the clock"""
command = CMD_EXIT
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
self.data_recv, addr = self.zkclient.recvfrom(1024)
return self.checkValid( self.data_recv )

71
hr_zk_attendance/models/zkconst.py

@ -0,0 +1,71 @@
from datetime import datetime, date
USHRT_MAX = 65535
CMD_CONNECT = 1000
CMD_EXIT = 1001
CMD_ENABLEDEVICE = 1002
CMD_DISABLEDEVICE = 1003
CMD_ACK_OK = 2000
CMD_ACK_ERROR = 2001
CMD_ACK_DATA = 2002
CMD_PREPARE_DATA = 1500
CMD_DATA = 1501
CMD_USERTEMP_RRQ = 9
CMD_ATTLOG_RRQ = 13
CMD_CLEAR_DATA = 14
CMD_CLEAR_ATTLOG = 15
CMD_WRITE_LCD = 66
CMD_GET_TIME = 201
CMD_SET_TIME = 202
CMD_VERSION = 1100
CMD_DEVICE = 11
CMD_CLEAR_ADMIN = 20
CMD_SET_USER = 8
LEVEL_USER = 0
LEVEL_ADMIN = 14
def encode_time(t):
"""Encode a timestamp send at the timeclock
copied from zkemsdk.c - EncodeTime"""
d = ( (t.year % 100) * 12 * 31 + ((t.month - 1) * 31) + t.day - 1) *\
(24 * 60 * 60) + (t.hour * 60 + t.minute) * 60 + t.second
return d
def decode_time(t):
"""Decode a timestamp retrieved from the timeclock
copied from zkemsdk.c - DecodeTime"""
second = t % 60
t = t / 60
minute = t % 60
t = t / 60
hour = t % 24
t = t / 24
day = t % 31+1
t = t / 31
month = t % 12+1
t = t / 12
year = t + 2000
d = datetime(int(year), int(month), int(day), int(hour), int(minute), int(second))
return d

82
hr_zk_attendance/models/zkdevice.py

@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkdevicename(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~DeviceName'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkenabledevice(self):
"""Start a connection with the time clock"""
command = CMD_ENABLEDEVICE
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkdisabledevice(self):
"""Start a connection with the time clock"""
command = CMD_DISABLEDEVICE
command_string = '\x00\x00'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

56
hr_zk_attendance/models/zkextendfmt.py

@ -0,0 +1,56 @@
def zkextendfmt(self):
try:
test = self.exttrynumber
except:
self.exttrynumber = 1
data_seq=[ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ]
#print data_seq
if self.exttrynumber == 1:
plus1 = 0
plus2 = 0
else:
plus1 = -1
plus2 = +1
desc = ": +"+hex( int('99', 16)+plus1 ).lstrip('0x')+", +"+hex(int('b1', 16)+plus2).lstrip("0x")
self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '99', 16 ) + plus1 ).lstrip("0x")
self.data_seq2 = hex( int( data_seq[1], 16 ) + int( 'b1', 16 ) + plus2 ).lstrip("0x")
if len(self.data_seq1) >= 3:
#self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x")
self.data_seq1 = self.data_seq1[-2:]
if len(self.data_seq2) >= 3:
#self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x")
self.data_seq2 = self.data_seq2[-2:]
if len(self.data_seq1) <= 1:
self.data_seq1 = "0"+self.data_seq1
if len(self.data_seq2) <= 1:
self.data_seq2 = "0"+self.data_seq2
counter = hex( self.counter ).lstrip("0x")
if len(counter):
counter = "0" + counter
#print self.data_seq1+" "+self.data_seq2+desc
data = "0b00"+self.data_seq1+self.data_seq2+self.id_com+counter+"007e457874656e64466d7400"
self.zkclient.sendto(data.decode("hex"), self.address)
#print data
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
except:
if self.exttrynumber == 1:
self.exttrynumber = 2
tmp = zkextendfmt(self)
if len(tmp) < 1:
self.exttrynumber = 1
self.id_com = self.data_recv.encode("hex")[8:12]
self.counter = self.counter+1
#print self.data_recv.encode("hex")
return self.data_recv[8:]

61
hr_zk_attendance/models/zkextendoplog.py

@ -0,0 +1,61 @@
def zkextendoplog(self, index=0):
try:
test = self.extlogtrynumber
except:
self.extlogtrynumber = 1
data_seq = [ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ]
if index==0:
self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '104', 16 ) ).lstrip("0x")
self.data_seq2 = hex( int( data_seq[1], 16 ) + int( '19', 16 ) ).lstrip("0x")
desc = ": +104, +19"
header="0b00"
elif index==1:
self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x")
self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x")
desc = ": -2c, -2"
header="d107"
elif index>=2:
self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x")
self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x")
desc = ": -2c, -2"
header="ffff"
#print self.data_seq1+" "+self.data_seq2
if len(self.data_seq1) >= 3:
self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x")
self.data_seq1 = self.data_seq1[-2:]
if len(self.data_seq2) >= 3:
self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x")
self.data_seq2 = self.data_seq2[-2:]
if len(self.data_seq1) <= 1:
self.data_seq1 = "0"+self.data_seq1
if len(self.data_seq2) <= 1:
self.data_seq2 = "0"+self.data_seq2
counter = hex( self.counter ).lstrip("0x")
if len(counter):
counter = "0" + counter
#print self.data_seq1+" "+self.data_seq2+desc
data = header+self.data_seq1+self.data_seq2+self.id_com+counter+"00457874656e644f504c6f6700"
self.zkclient.sendto(data.decode("hex"), self.address)
#print data
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
except:
bingung=1
if self.extlogtrynumber == 1:
self.extlogtrynumber = 2
zkextendoplog(self)
self.id_com = self.data_recv.encode("hex")[8:12]
self.counter = self.counter+1
#print self.data_recv.encode("hex")
return self.data_recv[8:]

44
hr_zk_attendance/models/zkface.py

@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-
###################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
# Copyright (C) 2018-TODAY Cybrosys Technologies(<http://www.cybrosys.com>).
# Author: cybrosys(<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/>.
#
###################################################################################
from struct import pack, unpack
from .zkconst import *
def zkfaceon(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = 'FaceFunOn'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

156
hr_zk_attendance/models/zklib.py

@ -0,0 +1,156 @@
from socket import *
from .zkconnect import *
from .zkversion import *
from .zkos import *
from .zkextendfmt import *
from .zkextendoplog import *
from .zkplatform import *
from .zkworkcode import *
from .zkssr import *
from .zkpin import *
from .zkface import *
from .zkserialnumber import *
from .zkdevice import *
from .zkuser import *
from .zkattendance import *
from .zktime import *
class ZKLib:
def __init__(self, ip, port):
self.address = (ip, port)
self.zkclient = socket(AF_INET, SOCK_DGRAM)
self.zkclient.settimeout(3)
self.session_id = 0
self.userdata = []
self.attendancedata = []
def createChkSum(self, p):
"""This function calculates the chksum of the packet to be sent to the
time clock
Copied from zkemsdk.c"""
l = len(p)
chksum = 0
while l > 1:
chksum += unpack('H', pack('BB', p[0], p[1]))[0]
p = p[2:]
if chksum > USHRT_MAX:
chksum -= USHRT_MAX
l -= 2
if l:
chksum = chksum + p[-1]
while chksum > USHRT_MAX:
chksum -= USHRT_MAX
chksum = ~chksum
while chksum < 0:
chksum += USHRT_MAX
return pack('H', chksum)
def createHeader(self, command, chksum, session_id, reply_id,
command_string):
"""This function puts a the parts that make up a packet together and
packs them into a byte string"""
buf = pack('HHHH', command, chksum, session_id, reply_id) + command_string.encode(encoding='utf_8', errors='strict')
buf = unpack('8B'+'%sB' % len(command_string), buf)
chksum = unpack('H', self.createChkSum(buf))[0]
#print unpack('H', self.createChkSum(buf))
reply_id += 1
if reply_id >= USHRT_MAX:
reply_id -= USHRT_MAX
buf = pack('HHHH', command, chksum, session_id, reply_id)
return buf + command_string.encode(encoding='utf_8', errors='strict')
def checkValid(self, reply):
"""Checks a returned packet to see if it returned CMD_ACK_OK,
indicating success"""
command = unpack('HHHH', reply[:8])[0]
if command == CMD_ACK_OK:
return True
else:
return False
def connect(self):
return zkconnect(self)
def disconnect(self):
return zkdisconnect(self)
def version(self):
return zkversion(self)
def osversion(self):
return zkos(self)
def extendFormat(self):
return zkextendfmt(self)
def extendOPLog(self, index=0):
return zkextendoplog(self, index)
def platform(self):
return zkplatform(self)
def fmVersion(self):
return zkplatformVersion(self)
def workCode(self):
return zkworkcode(self)
def ssr(self):
return zkssr(self)
def pinWidth(self):
return zkpinwidth(self)
def faceFunctionOn(self):
return zkfaceon(self)
def serialNumber(self):
return zkserialnumber(self)
def deviceName(self):
return zkdevicename(self)
def disableDevice(self):
return zkdisabledevice(self)
def enableDevice(self):
return zkenabledevice(self)
def getUser(self):
return zkgetuser(self)
def setUser(self, uid, userid, name, password, role):
return zksetuser(self, uid, userid, name, password, role)
def clearUser(self):
return zkclearuser(self)
def clearAdmin(self):
return zkclearadmin(self)
def getAttendance(self):
return zkgetattendance(self)
def clearAttendance(self):
return zkclearattendance(self)
def setTime(self, t):
return zksettime(self, t)
def getTime(self):
return zkgettime(self)

23
hr_zk_attendance/models/zkos.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkos(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~OS'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
hr_zk_attendance/models/zkpin.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkpinwidth(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~PIN2Width'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

43
hr_zk_attendance/models/zkplatform.py

@ -0,0 +1,43 @@
from struct import pack, unpack
from .zkconst import *
def zkplatform(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~Platform'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkplatformVersion(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~ZKFPVersion'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
hr_zk_attendance/models/zkserialnumber.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkserialnumber(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~SerialNumber'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
hr_zk_attendance/models/zkssr.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkssr(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = '~SSR'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

50
hr_zk_attendance/models/zktime.py

@ -0,0 +1,50 @@
from struct import pack, unpack
from .zkconst import *
def reverseHex(hexstr):
tmp = ''
for i in reversed( xrange( len(hexstr)/2 ) ):
tmp += hexstr[i*2:(i*2)+2]
return tmp
def zksettime(self, t):
"""Start a connection with the time clock"""
command = CMD_SET_TIME
command_string = pack('I',encode_time(t))
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkgettime(self):
"""Start a connection with the time clock"""
command = CMD_GET_TIME
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return decode_time( int( reverseHex( self.data_recv[8:].encode("hex") ), 16 ) )
except:
return False

140
hr_zk_attendance/models/zkuser.py

@ -0,0 +1,140 @@
from struct import pack, unpack
from .zkconst import *
def getSizeUser(self):
"""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', self.data_recv[:8])[0]
if command == CMD_PREPARE_DATA:
size = unpack('I', self.data_recv[8:12])[0]
return size
else:
return False
def zksetuser(self, uid, userid, name, password, role):
"""Start a connection with the time clock"""
command = CMD_SET_USER
command_string = pack('sxs8s28ss7sx8s16s', chr( uid ), chr(role), password, name, chr(1), '', userid, '' )
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkgetuser(self):
"""Start a connection with the time clock"""
command = CMD_USERTEMP_RRQ
command_string = '\x05'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
if getSizeUser(self):
bytes = getSizeUser(self)
while bytes > 0:
data_recv, addr = self.zkclient.recvfrom(1032)
self.userdata.append(data_recv)
bytes -= 1024
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
data_recv = self.zkclient.recvfrom(8)
users = {}
if len(self.userdata) > 0:
# The first 4 bytes don't seem to be related to the user
for x in xrange(len(self.userdata)):
if x > 0:
self.userdata[x] = self.userdata[x][8:]
userdata = ''.join( self.userdata )
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'), errors='ignore')
#uid = uid.split('\x00', 1)[0]
userid = unicode(userid.strip('\x00|\x01\x10x'), errors='ignore')
name = name.split('\x00', 1)[0]
if name.strip() == "":
name = uid
users[uid] = (userid, name, int( role.encode("hex"), 16 ), password)
#print("%d, %s, %s, %s, %s" % (uid, userid, name, int( role.encode("hex"), 16 ), password))
userdata = userdata[72:]
return users
except:
return False
def zkclearuser(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_DATA
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False
def zkclearadmin(self):
"""Start a connection with the time clock"""
command = CMD_CLEAR_ADMIN
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
hr_zk_attendance/models/zkversion.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkversion(self):
"""Start a connection with the time clock"""
command = CMD_VERSION
command_string = ''
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

23
hr_zk_attendance/models/zkworkcode.py

@ -0,0 +1,23 @@
from struct import pack, unpack
from .zkconst import *
def zkworkcode(self):
"""Start a connection with the time clock"""
command = CMD_DEVICE
command_string = 'WorkCode'
chksum = 0
session_id = self.session_id
reply_id = unpack('HHHH', self.data_recv[:8])[3]
buf = self.createHeader(command, chksum, session_id,
reply_id, command_string)
self.zkclient.sendto(buf, self.address)
#print buf.encode("hex")
try:
self.data_recv, addr = self.zkclient.recvfrom(1024)
self.session_id = unpack('HHHH', self.data_recv[:8])[2]
return self.data_recv[8:]
except:
return False

4
hr_zk_attendance/security/ir.model.access.csv

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_zk_machine_user,zk.machine.hr_biometric_machine,model_zk_machine,hr_attendance.group_hr_attendance_user,1,1,1,1
access_hr_zk_machine_user1,zk.machine.hr_biometric_machine1,model_zk_machine_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1
access_hr_zk_machine_user2,zk.machine.hr_biometric_machine2,model_zk_report_daily_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_zk_machine_user zk.machine.hr_biometric_machine model_zk_machine hr_attendance.group_hr_attendance_user 1 1 1 1
3 access_hr_zk_machine_user1 zk.machine.hr_biometric_machine1 model_zk_machine_attendance hr_attendance.group_hr_attendance_user 1 1 1 1
4 access_hr_zk_machine_user2 zk.machine.hr_biometric_machine2 model_zk_report_daily_attendance hr_attendance.group_hr_attendance_user 1 1 1 1

BIN
hr_zk_attendance/static/description/banner.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 537 KiB

BIN
hr_zk_attendance/static/description/cybro_logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 KiB

BIN
hr_zk_attendance/static/description/hr_zk_attendance_cybrosys_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
hr_zk_attendance/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

412
hr_zk_attendance/static/description/index.html

@ -0,0 +1,412 @@
<section class="oe_container" style="background-image:url(https://www.cybrosys.com/images/odoo-index-header-banner.png);background-repeat:no-repeat;background-size:100%;padding: 4% 0% 2% 15%;background-position-y: -107px;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="font-size: 35px;color: #fff;font-weight: 900;text-transform: uppercase;text-align: left;margin: 0;margin-bottom: 16px;">
HR Biometric Device Integration
</h2>
<h3 class="oe_slogan" style="font-size: 25px;color: #fff;font-weight: 600;text-align: left;opacity: 1;margin: 0 !important;">
This Module Integrates Biometric Device With HR Attendance
</h3>
<h5 class="oe_slogan" style="text-align: left;background: #fff;width: 293px;padding: 10px;color: #080808 !important;opacity: 1 !important;font-weight: 600;font-size: 20px;">
<a style="color: #080808 !important;" href="https://www.cybrosys.com" target="_blank">Cybrosys Technologies</a>
</h5>
<a style="color: #080808 !important;" href="https://www.cybrosys.com" target="_blank">
<div style="width: 215px;margin-left: 57%;text-align: center;background: #ffffff;height: 215px;border-radius: 100%;display: flex;justify-content: center;align-items: center;box-shadow: 0 0 12px 4px #00000059;">
<img src="https://www.cybrosys.com/images/cybro-logo-oca.png" alt="cybrosys technologies" style="width: 180px;"/>
</div>
</a>
</div>
</section>
<section class="oe_container" style="padding: 1% 0% 3% 15%;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Overview
</h2>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
Automation is an implementation factor for a successful ERP. Using this module, HR attendance can be automated via integrating Thumb / Face detection device with Odoo.
One can configure a user both from thumbing device or Odoo employee form.
</h3>
</div>
</section>
<section class="oe_container" style="background-image:url(https://www.cybrosys.com/images/odoo-index-banner.png); background-repeat:no-repeat; background-size:cover;padding: 10% 0% 25% 15%;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Features
</h2>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Integrates biometric device(Face+Thumb) with HR attendance.
</h3>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Option to keep the device attendance log in Odoo.
</h3>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Option to clear the device attendance log from both device and Odoo.
</h3>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Automating HR attendance.
</h3>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Option to configure multiple devices.
</h3>
<h3 class="oe_slogan" style="text-align: left;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 18px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
This module will support with ZKteco model 'uFace 202', ZKteco model 'iFace990'
</h3>
</div>
</section>
<section class="oe_container" style="padding: 3% 0% 0% 15%;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Biometric Device Configuration
</h2>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<div>
<img src="https://www.cybrosys.com/images/ico-tick.png">
Here you can configure your all devices with it's IP address and port number.
</div>
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_1.png" alt="" style="width: 95%;"/>
</div>
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Download/Clear Device Attendance Log
</h2>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<div>
<img src="https://www.cybrosys.com/images/ico-tick.png">
After configuration, you can download your device attendance log into Odoo through
'Download' button.If the device is connected, then the Odoo will download all device
attendance log.
Otherwise, the Odoo will display you a warning message as follow.
</div>
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_2.png" alt="" style="width: 95%;"/>
</div>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<div><img src="https://www.cybrosys.com/images/ico-tick.png">
You can also clear all attendance log from both Odoo and device via 'Clear' button.
If the device is not connected it will display you a warning message as follow.
</div>
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_3.png" alt="" style="width: 95%;"/>
</div>
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Biometric Device Attendance Log
</h2>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<div>
<img src="https://www.cybrosys.com/images/ico-tick.png">
Here you can see all device attendance log
</div>
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_4.png" alt="" style="width: 95%;"/>
</div>
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
HR Attendance
</h2>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<div><img src="https://www.cybrosys.com/images/ico-tick.png">
Here, Odoo automatically generates HR attendance log while downloading the device attendance.
</div>
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_5.png" alt="" style="width: 95%;"/>
</div>
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Employee Configuration
</h2>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
You can update existing employees with the 'Device Id' which are the id in the biometric device.
If there is no match with the biometric device id then system will automatically create corresponding employee.
</h3>
<div class="oe_row oe_spaced">
<img src="hr_zk_attendance_cybrosys_6.png" alt="" style="width: 95%;"/>
</div>
<h3 class="oe_slogan" style="text-align: left;padding: 5% 0% 0% 0%;font-size: 16px;width: 90%;margin: 0;margin-top: 14px;color: #000 !important;opacity: 1 !important;line-height: 24px;">
<img src="https://www.cybrosys.com/images/ico-tick.png">
Note:- This integration is only applicable for the device ZKteco model 'uFace 202'<br/>
Please install zklib library (sudo pip install zklib)
</h3>
</div>
</section>
<section class="oe_container" style="padding: 7px 0% 0% 3%;">
<div class="oe_row oe_spaced">
<a style="color: #080808 !important;" href="https://apps.odoo.com/apps/modules/browse?search=cybrosys" target="_blank"><img src="https://www.cybrosys.com/images/view-more-apps.jpg" alt="cybrosys technologies" style="width: 100%;margin-bottom: 50px;"/></a>
</div>
</section>
<section class="oe_container" style="padding: 1% 0% 0% 3%;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Our Services
</h2>
<div style="display:flex;padding-top: 20px;justify-content: space-between;">
<div style="flex-basis: 18%;">
<div style="width:75px;height:75px;background:#fff; border-radius:100%;margin: auto;">
<a href="https://www.cybrosys.com/odoo-customization-and-installation/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-customization.png" style="width: 100%;border-radius: 100%;"/>
</a>
</div>
<h3 class="oe_slogan" style="font-weight: 800;text-align: center;font-size: 14px;width: 100%;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;">
<a href="https://www.cybrosys.com/odoo-customization-and-installation/" target="_blank">
Odoo Customization
</a>
</h3>
</div>
<div style="flex-basis: 18%;">
<div style="width:75px;height:75px;background:#fff; border-radius:100%;margin: auto;">
<a href="https://www.cybrosys.com/odoo-erp-implementation/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-erp-implementation.png" style="width: 100%;border-radius: 100%;"/>
</a>
</div>
<h3 class="oe_slogan" style="font-weight: 800;text-align: center;font-size: 14px;width: 100%;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;">
<a href="https://www.cybrosys.com/odoo-erp-implementation/" target="_blank">
Odoo Implementation </a>
</h3>
</div>
<div style="flex-basis: 18%;">
<div style="width:75px;height:75px;background:#fff; border-radius:100%;margin: auto;">
<a href="https://www.cybrosys.com/odoo-erp-integration/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-erp-integration.png" style="width: 100%;border-radius: 100%;"/>
</a>
</div>
<h3 class="oe_slogan" style="font-weight: 800;text-align: center;font-size: 14px;width: 100%;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;">
<a href="https://www.cybrosys.com/odoo-erp-integration/" target="_blank">
Odoo Integration
</a>
</h3>
</div>
<div style="flex-basis: 18%;">
<div style="width:75px;height:75px;background:#fff; border-radius:100%;margin: auto;">
<a href="https://www.cybrosys.com/odoo-erp-support/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-erp-support.png" style="width: 100%;border-radius: 100%;"/>
</a>
</div>
<h3 class="oe_slogan" style="font-weight: 800;text-align: center;font-size: 14px;width: 100%;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;">
<a href="https://www.cybrosys.com/odoo-erp-support/" target="_blank">
Odoo Support</a>
</h3>
</div>
<div style="flex-basis: 18%;">
<div style="width:75px;height:75px;background:#fff; border-radius:100%;margin: auto;">
<a href="https://www.cybrosys.com/hire-odoo-developer/" target="_blank">
<img src="https://www.cybrosys.com/images/hire-odoo-developer.png" style="width: 100%;border-radius: 100%;"/>
</a>
</div>
<h3 class="oe_slogan" style="font-weight: 800;text-align: center;font-size: 14px;width: 100%;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;">
<a href="https://www.cybrosys.com/hire-odoo-developer/" target="_blank">
Hire Odoo Developers</a>
</h3>
</a>
</div>
</div>
</div>
</section>
<section class="oe_container" style="padding: 1% 0% 0% 3%;">
<div class="oe_row oe_spaced">
<h2 class="oe_slogan" style="text-align: left;font-size: 28px;font-weight: 600;margin: 0px !important;">
Our Industries
</h2>
<div style="display:flex;justify-content: space-between;flex-wrap:wrap;">
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/best-trading-erp/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-1.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/best-trading-erp/" target="_blank">
Trading
</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Easily procure and sell your products.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/manufacturing-erp-software/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-2.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/manufacturing-erp-software/" target="_blank">
Manufacturing</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Plan, track and schedule your operations.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/restaurant-management/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-3.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/restaurant-management/" target="_blank">
Restaurant</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Run your bar or restaurant methodical.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/pos/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-4.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/pos/" target="_blank">
POS</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Easy configuring and convivial selling.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/ecommerce-website/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-5.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 0px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/ecommerce-website/" target="_blank">
E-commerce & Website</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Mobile friendly, awe-inspiring product pages.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/hotel-management-erp/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-6.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/hotel-management-erp/" target="_blank">
Hotel Management</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
An all-inclusive hotel management application.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/education-erp-software/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-7.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/education-erp-software/" target="_blank">
Education</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
A Collaborative platform for educational management.
</h3>
</div>
</div>
<div style="flex-basis: 32%;padding-top: 20px;">
<div style="width:30%; float:left;">
<div style="width:75px;height:75px;background:#CE2D48; border-radius:100%;float: left;text-align: left;">
<a href="https://www.cybrosys.com/odoo/industries/service-management/" target="_blank">
<img src="https://www.cybrosys.com/images/odoo-index-industry-8.png" alt="Odoo Industry" style=" border-radius: 100%;width:100%;"/>
</a>
</div>
</div>
<div style="width:70%;float:left;">
<h3 class="oe_slogan" style=" text-align: left;font-size: 14px;font-weight:800;width: auto;margin: 0;margin-top: 14px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 4px;margin-left: 16px;">
<a href="https://www.cybrosys.com/odoo/industries/service-management/" target="_blank">
Service Management</a>
</h3>
<h3 class="oe_slogan" style=" text-align: left;font-size: 13px;width: auto;margin: 0;margin-top:5px;color: #000 !important;margin-top: 5px;opacity: 1 !important;line-height: 17px;float: left;margin-top: 5px;margin-left: 16px;">
Keep track of services and invoice accordingly.
</h3>
</div>
</div>
</div>
</div>
</section>
<section class="oe_container" style="background-image:url(https://www.cybrosys.com/images/odoo-index-footer-bg.png); background-repeat:no-repeat; background-size:100%;padding: 13% 0% 6% 0%;">
<div class="oe_slogan" style="margin-top:10px !important;margin-bottom: 0px;">
<div>
<a style="color: #5c5c5c !important;border-radius: 0;background: none;border: none;background: #fff;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.05);border-radius: 30px;font-size: 12px;padding: 9px 26px;margin-right: 9px;width: 200px;text-transform: capitalize;" class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" href="mailto:odoo@cybrosys.com"><i class="fa fa-envelope"></i> Email us </a>
<a style="color: #5c5c5c !important;border-radius: 0;background: none;border: none;background: #fff;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.05);border-radius: 30px;font-size: 12px;padding: 9px 26px;margin-right: 9px;width: 200px;text-transform: capitalize;" class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com/contact/"><i class="fa fa-phone"></i> Contact Us </a>
<a style="color: #5c5c5c !important;border-radius: 0;background: none;border: none;background: #fff;box-shadow: 0 10px 40px 0 rgba(62,57,107,0.07), 0 2px 9px 0 rgba(62, 57, 107, 0.05);border-radius: 30px;font-size: 12px;padding: 9px 26px;margin-right: 9px;width: 200px;text-transform: capitalize;" class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com/contact/"><i class="fa fa-check-square"></i> Request Customization </a>
</div>
<br>
<img src="https://www.cybrosys.com/images/logo.png" style="width: 190px; margin-bottom: 25px;margin-top: 30px;" class="center-block">
<div>
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;height: 35px;padding-top: 7px;font-size: 21px;margin-right: 6px;border-radius: 100%;"></i></a></td>
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;height: 35px;padding-top: 7px;font-size: 21px;margin-right: 6px;border-radius: 100%;"></i></a></td>
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px; ;height: 35px;padding-top: 7px;font-size: 21px;margin-right: 6px;border-radius: 100%;"></i></a></td>
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;height: 35px;padding-top: 7px;font-size: 21px;margin-right: 6px;border-radius: 100%;"></i></a></td>
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;height: 35px;padding-top: 7px;font-size: 21px;margin-right: 6px;border-radius: 100%;"></i></a></td>
</div>
</div>
</section>

69
hr_zk_attendance/views/zk_machine_attendance_view.xml

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Customize Attendance Report -->
<record id="inherited_view_attendance_tree" model="ir.ui.view">
<field name="name">inherited_hr.attendance.tree</field>
<field name="model">hr.attendance</field>
<field name="inherit_id" ref="hr_attendance.view_attendance_tree"/>
<field name="arch" type="xml">
<tree string="Employee attendances">
<field name="worked_hours" type="measure"/>
</tree>
</field>
</record>
<record id="inherited_hr_attendance_view_filter" model="ir.ui.view">
<field name="name">inherited_hr_attendance_view_filter</field>
<field name="model">hr.attendance</field>
<field name="inherit_id" ref="hr_attendance.hr_attendance_view_filter"/>
<field name="arch" type="xml">
<search string="Hr Attendance Search">
<group expand="0" string="Group By">
<separator/>
<filter name="date" string="Date" context="{'group_by':'check_in:day'}"/>
</group>
</search>
</field>
</record>
<record id="view_zk_report_daily_attendance_search" model="ir.ui.view">
<field name="name">zk.report.daily.attendance.search</field>
<field name="model">zk.report.daily.attendance</field>
<field name="arch" type="xml" >
<search string="Hr Attendance Search">
<filter icon="terp-stock_align_left_24" string="My Attendance" name="my_attendance" domain="[('name.user_id.id', '=', uid)]" />
<filter name="today" string="Today" domain="[('punching_time', '&gt;=', datetime.datetime.now().replace(hour=0, minute=0, second=0)),('punching_time', '&lt;=', datetime.datetime.now().replace(hour=23, minute=59, second=59))]" />
<filter string="Current Month" name="month" domain="[('punching_time', '&gt;=', datetime.datetime.now().strftime('%Y-%m-01'))]" />
<separator/>
<field name="name" string="Name"/>
</search>
</field>
</record>
<record id="view_zk_report_daily_attendance_tree" model="ir.ui.view">
<field name="name">zk.report.daily.attendance.tree</field>
<field name="model">zk.report.daily.attendance</field>
<field name="arch" type="xml" >
<tree string="Attendance" create="false" delete="false" >
<field name="punching_day"/>
<field name="name"/>
<field name="punch_type"/>
<field name="attendance_type"/>
<field name="punching_time"/>
<field name="address_id"/>
</tree>
</field>
</record>
<record id="action_zk_report_daily_attendance" model="ir.actions.act_window">
<field name="name">Attendance Analysis</field>
<field name="res_model">zk.report.daily.attendance</field>
<field name="view_mode">tree</field>
<field name="context">{'search_default_my_attendance':1}</field>
<field name="search_view_id" ref="view_zk_report_daily_attendance_search" />
</record>
<menuitem id="menu_zk_attendance_view" name="Attendance log" action="action_zk_report_daily_attendance" parent="zk_machine_menu"
sequence="2" groups="hr_attendance.group_hr_attendance_user"/>
</odoo>

68
hr_zk_attendance/views/zk_machine_view.xml

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_zk_machine_form" model="ir.ui.view">
<field name="name">zk.machine.form</field>
<field name="model">zk.machine</field>
<field name="arch" type="xml">
<form string="Biometric Device">
<header>
<button name="clear_attendance" type="object" string="Clear Data" class="oe_highlight"
icon="fa-remove " confirm="Are you sure you want to do this?"/>
<button name="download_attendance" type="object" string="Download Data" class="oe_highlight"
icon="fa-download " confirm="Are you sure you want to do this?" />
</header>
<sheet>
<div class="oe_title">
<label for="name" />
<h1>
<field name="name" placeholder="Machine IP"/>
</h1>
</div>
<group>
<group>
<field name="port_no"/>
<field name="address_id"/>
</group>
<group>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="view_zk_machine_tree" model="ir.ui.view">
<field name="name">zk.machine.tree</field>
<field name="model">zk.machine</field>
<field name="arch" type="xml">
<tree string="Biometric Machine">
<field name="name"/>
<field name="port_no"/>
<field name="address_id"/>
<field name="company_id" groups="base.group_multi_company"/>
</tree>
</field>
</record>
<record id="zk_machine_action" model="ir.actions.act_window">
<field name="name">Attendances</field>
<field name="res_model">zk.machine</field>
<field name="view_mode">tree,form</field>
</record>
<record id="hr_employee_inherit_form_view" model="ir.ui.view">
<field name="name">hr.employee.form</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<field name="user_id" position="after">
<field name="device_id"/>
</field>
</field>
</record>
<menuitem id="zk_machine_menu" parent="hr_attendance.menu_hr_attendance_root" sequence="50" name="Biometric Manager" />
<menuitem id="zk_machine_sub_menu" parent="zk_machine_menu" name="Device Configuration" action="zk_machine_action" sequence="1"/>
</odoo>
Loading…
Cancel
Save