@ -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> | 
				
			|||