@ -0,0 +1,33 @@ |
|||||
|
Biometric Device Integration v10 |
||||
|
================================ |
||||
|
This Cybrosys's module integrates Odoo attendance with biometric device attendance. |
||||
|
|
||||
|
Features |
||||
|
======== |
||||
|
* Ingrates 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: Jesni Banu @ cybrosys, jesni@cybrosys.in |
||||
|
|
@ -0,0 +1,25 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
|
||||
|
from . import models |
||||
|
from . import wizard |
@ -0,0 +1,50 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
{ |
||||
|
'name': 'Open HRMS Biometric Device Integration', |
||||
|
'version': '11.0.1.0.0', |
||||
|
'summary': """Integrating Biometric Device With HR Attendance (Face + Thumb)""", |
||||
|
'description': 'This module integrates Odoo with the biometric device(Model: ZKteco uFace 202)', |
||||
|
'category': 'Generic Modules/Human Resources', |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://www.openhrms.com", |
||||
|
'depends': ['base_setup', 'hr_attendance'], |
||||
|
'data': [ |
||||
|
'security/ir.model.access.csv', |
||||
|
'wizard/generate_attendance.xml', |
||||
|
'views/zk_machine_view.xml', |
||||
|
'views/zk_machine_attendance_view.xml', |
||||
|
], |
||||
|
'images': ['static/description/banner.gif'], |
||||
|
'license': 'AGPL-3', |
||||
|
'external_dependencies': { |
||||
|
'python': ['zklib'] |
||||
|
}, |
||||
|
'demo': [], |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
## Module <oh_hr_zk_attendance> |
||||
|
|
||||
|
#### 24.04.2018 |
||||
|
#### Version 11.0.1.0.0 |
||||
|
##### ADD |
||||
|
- Initial commit for Open HRMS Project |
@ -0,0 +1,26 @@ |
|||||
|
# -*- 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/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
from . import zk_machine |
||||
|
from . import machine_analysis |
||||
|
|
||||
|
|
||||
|
|
@ -0,0 +1,100 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://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', help='ID created in Biometric Device') |
||||
|
|
||||
|
|
||||
|
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', 'Thump'), |
||||
|
('15', 'Face')], 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.Date(string='Date') |
||||
|
address_id = fields.Many2one('res.partner', string='Working Address') |
||||
|
attendance_type = fields.Selection([('1', 'Thump'), |
||||
|
('15', 'Face')], |
||||
|
string='Category', help='Type of Detecting Attendance') |
||||
|
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', help='Type of Punching') |
||||
|
punching_time = fields.Datetime(string='Punching Time') |
||||
|
|
||||
|
def init(self): |
||||
|
tools.drop_view_if_exists(self._cr, 'zk_report_daily_attendance') |
||||
|
self._cr.execute(""" |
||||
|
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 |
||||
|
) |
||||
|
""") |
||||
|
|
||||
|
|
@ -0,0 +1,274 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://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', help='Select the ZK device') |
||||
|
|
||||
|
|
||||
|
class ZkMachine(models.Model): |
||||
|
_name = 'zk.machine' |
||||
|
|
||||
|
name = fields.Char(string='Machine IP', required=True, help='IP address of ZK Machine') |
||||
|
port_no = fields.Integer(string='Port No', required=True, help='Port number of ZK Machine') |
||||
|
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): |
||||
|
try: |
||||
|
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 |
||||
|
except: |
||||
|
pass |
||||
|
|
||||
|
@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.')) |
|
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 221 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 37 KiB |
@ -0,0 +1,107 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row"> |
||||
|
<h2 class="oe_slogan"><a href="https://www.openhrms.com">Open HRMS</a></h2> |
||||
|
<h3 class="oe_slogan">Most advanced open source HR management software</h3> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced oe_mt32"> |
||||
|
<div class="oe_span"> |
||||
|
<div class="oe_demo oe_picture oe_screenshot"> |
||||
|
<a href="https://www.openhrms.com/#request-demo"> |
||||
|
<img src="HRMS-BUTTON.png"> |
||||
|
</a> |
||||
|
<div class="oe_demo_footer oe_centeralign">Online Demo</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h2 class="oe_slogan">Open HRMS Biometric Device Integration</h2> |
||||
|
<h3 class="oe_slogan">This Module Integrates Biometric Device With HR Attendance</h3> |
||||
|
<h4 class="oe_slogan"><a href="https://www.cybrosys.com">Cybrosys Technologies</a> </h4> |
||||
|
</div> |
||||
|
<div class="oe_row oe_spaced" style="padding-left:65px;"> |
||||
|
<div> |
||||
|
<span style="color:green;"> ☑ </span> Ingrates biometric device(Face+Thumb) with HR attendance.<br/> |
||||
|
<span style="color:green;"> ☑ </span> Option to keep the device attendance log in Odoo.<br/> |
||||
|
<span style="color:green;"> ☑ </span> Option to clear the device attendance log from both device and Odoo.<br/> |
||||
|
<span style="color:green;"> ☑ </span> Automating HR attendance.<br/> |
||||
|
<span style="color:green;"> ☑ </span> Option to configure multiple devices.<br/> |
||||
|
</div> |
||||
|
<br/><br/> |
||||
|
<div> |
||||
|
<span align="center" class="fa fa-star fa-spin"></span> |
||||
|
<b><i>This module will support with <br/><t/> |
||||
|
<ul style="padding-left: 36px;padding-top: 2px;"> |
||||
|
<li style="list-style:outside !important;">ZKteco model 'uFace 202'</li> |
||||
|
<li style="list-style:outside !important;">ZKteco model 'iFace990'</li> |
||||
|
</ul> |
||||
|
</i></b> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_picture"> |
||||
|
<h3 class="oe_slogan">Overview</h3> |
||||
|
<p class="oe_mt32 text-justify" style="text-align: center;"> |
||||
|
Automation is an implementation factor for a successful ERP. With this module, |
||||
|
HR attendance can automate by integrating Thumb / Face detection device with Odoo. |
||||
|
We can configure a user both from thumbing device or Odoo employee form. |
||||
|
</p> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="" style="text-align: center"> |
||||
|
<span><b><i>Note:- This integration is only applicable for the the device ZKteco model 'uFace 202'<br/> |
||||
|
Please install zklib library (sudo pip install zklib)</i></b></span> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<div class="row section-content"> |
||||
|
<div class="col-md-6 img-content"> |
||||
|
<h3>Our Odoo Services</h3> |
||||
|
</div> |
||||
|
<div class="bc-span col-md-12"> |
||||
|
<div class="inner-span"> |
||||
|
<a target="_blank" href="https://www.openhrms.com"> |
||||
|
<img class="img-border img-responsive thumbnail" src="cybro-service.png"> |
||||
|
</a> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2> |
||||
|
<div class="oe_slogan" style="margin-top:10px !important;"> |
||||
|
<div> |
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/contact/" target="_blank"><i |
||||
|
class="fa fa-phone"></i> Contact Us </a> |
||||
|
|
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.odoo.com/apps/modules/browse?search=open+hrms" target="_blank"><i |
||||
|
class="fa fa-suitcase"></i> Other Open HRMS Addons </a> |
||||
|
|
||||
|
<a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/odoo-customization-and-installation/" target="_blank"><i |
||||
|
class="fa fa-wrench"></i> Request Customization </a> |
||||
|
|
||||
|
</div> |
||||
|
<br> |
||||
|
<a href="https://www.cybrosys.com/" target="_blank"> |
||||
|
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
||||
|
</a> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
After Width: | Height: | Size: 70 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 9.1 KiB |
@ -0,0 +1,41 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<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)]" /> |
||||
|
<field name="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" colors="green:punch_type in ('0');red:punch_type in ('1');"> |
||||
|
<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_type">form</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> |
@ -0,0 +1,75 @@ |
|||||
|
<?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"> |
||||
|
<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_type">form</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="zk_machine_attendance_action" model="ir.actions.act_window"> |
||||
|
<field name="name">Generate Attendances</field> |
||||
|
<field name="res_model">zk.attendance</field> |
||||
|
<field name="view_type">form</field> |
||||
|
<field name="view_mode">form</field> |
||||
|
<field name="target">new</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="hr_attendance.menu_hr_attendance_settings" name="Attendance Config" parent="hr.menu_human_resources_configuration" |
||||
|
sequence="2" groups="hr_attendance.group_hr_attendance_manager"/> |
||||
|
<menuitem id="zk_menu_hr_attendance_settings" name="Settings" parent="hr_attendance.menu_hr_attendance_settings" |
||||
|
sequence="1" action="hr_attendance.action_hr_attendance_settings" groups="hr_attendance.group_hr_attendance_manager"/> |
||||
|
<menuitem id="zk_machine_config" parent="hr_attendance.menu_hr_attendance_settings" name="Device Configuration" action="zk_machine_action" sequence="2"/> |
||||
|
<menuitem id="zk_machine_menu" parent="hr_attendance.menu_hr_attendance_root" sequence="11" name="Biometric Attendance" /> |
||||
|
<menuitem id="zk_machine_sub_menu" parent="zk_machine_menu" name="Generate Attendance" action="zk_machine_attendance_action" sequence="1"/> |
||||
|
</odoo> |
||||
|
|
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
|
||||
|
from . import generate_attendance |
@ -0,0 +1,36 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################### |
||||
|
# A part of OpenHRMS Project <https://www.openhrms.com> |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2018-TODAY Cybrosys Technologies (<https://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 <https://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
################################################################################### |
||||
|
|
||||
|
from odoo import models, fields |
||||
|
|
||||
|
|
||||
|
class ZkAttendanceConfig(models.TransientModel): |
||||
|
_name = 'zk.attendance' |
||||
|
|
||||
|
device_id = fields.Many2one('zk.machine', string='Device ID') |
||||
|
|
||||
|
def download_attendance(self): |
||||
|
self.device_id.download_attendance() |
||||
|
|
||||
|
def clear_attendance(self): |
||||
|
self.device_id.clear_attendance() |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<record id="view_zk_attendance_generate_form" model="ir.ui.view"> |
||||
|
<field name="name">zk.attendance.form</field> |
||||
|
<field name="model">zk.attendance</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form string="Generate Attendance"> |
||||
|
<group> |
||||
|
<field name="device_id" widget="selection"/> |
||||
|
</group> |
||||
|
<footer> |
||||
|
<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?" /> |
||||
|
</footer> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |