| @ -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 | ||||
|  | 
 | ||||
| @ -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 | ||||
| @ -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, | ||||
|  | } | ||||
| @ -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> | ||||
| @ -0,0 +1,6 @@ | |||||
|  | ## Module <hr_zk_attendance> | ||||
|  | 
 | ||||
|  | #### 02.11.2019 | ||||
|  | #### Version 13.0.1.0.0 | ||||
|  | ##### ADD | ||||
|  | - Initial commit | ||||
| @ -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 | ||||
|  | 
 | ||||
| @ -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) | ||||
|  | 
 | ||||
|  | 
 | ||||
| @ -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.')) | ||||
| @ -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 | ||||
| @ -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 ) | ||||
| @ -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 | ||||
|  |      | ||||
| @ -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 | ||||
| @ -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:] | ||||
| @ -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:] | ||||
| @ -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 | ||||
|  | 
 | ||||
| @ -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) | ||||
| @ -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 | ||||
| @ -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 | ||||
|  |      | ||||
| @ -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 | ||||
|  | 
 | ||||
| @ -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 | ||||
|  |      | ||||
| @ -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 | ||||
|  |      | ||||
| @ -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 | ||||
| @ -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 | ||||
| @ -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 | ||||
|  | 
 | ||||
| @ -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 | ||||
|  | 
 | ||||
| 
 | 
| After Width: | Height: | Size: 537 KiB | 
| After Width: | Height: | Size: 50 KiB | 
| After Width: | Height: | Size: 123 KiB | 
| After Width: | Height: | Size: 132 KiB | 
| After Width: | Height: | Size: 128 KiB | 
| After Width: | Height: | Size: 166 KiB | 
| After Width: | Height: | Size: 234 KiB | 
| After Width: | Height: | Size: 161 KiB | 
| After Width: | Height: | Size: 13 KiB | 
| @ -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> | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | 
 | ||||
| @ -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', '>=', datetime.datetime.now().replace(hour=0, minute=0, second=0)),('punching_time', '<=', datetime.datetime.now().replace(hour=23, minute=59, second=59))]" /> | ||||
|  |                 <filter string="Current Month" name="month" domain="[('punching_time', '>=', 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> | ||||
| @ -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> | ||||
|  | 
 | ||||