@ -0,0 +1,73 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Biometric Device Integration |
|||
============================ |
|||
This Cybrosys's module integrates Odoo attendance with biometric device attendance. |
|||
|
|||
Configuration |
|||
============= |
|||
|
|||
*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 |
|||
---------------- |
|||
This module support with the following machines : |
|||
|
|||
uFace202 (ZKteco) |
|||
* iFace990 (ZKteco) |
|||
|
|||
Clients have reported that the module works well with the following machine : |
|||
|
|||
* K40 Pro (ZKteco) |
|||
* SFace900 (ZKteco) |
|||
* FR1500 (ZKteco) |
|||
* UA760 (ZKteco) |
|||
* MB10 (ZKteco |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (AGPL-3). |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
======= |
|||
* Developers: (V10) Jesni Banu, |
|||
(V11) Niyas Raphy, |
|||
(V12) Basith, |
|||
(V13) Varsha Vivek, |
|||
(V14) Ijaz Ahammed, |
|||
(V15) Noushid Khan, |
|||
(V16) Minhaj T, |
|||
(V17) Ammu Raj, |
|||
(V18) Bhagyadev |
|||
* Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from . import models |
@ -0,0 +1,49 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
{ |
|||
'name': 'Biometric Device Integration', |
|||
'version': '18.0.1.0.0', |
|||
'category': 'Human Resources', |
|||
'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),odoo18,odoo,hr,attendance", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': "https://www.cybrosys.com", |
|||
'depends': ['base_setup', 'hr_attendance'], |
|||
'external_dependencies': { |
|||
'python': ['pyzk'], }, |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'views/biometric_device_details_views.xml', |
|||
'views/hr_employee_views.xml', |
|||
'views/daily_attendance_views.xml', |
|||
'views/biometric_device_attendance_menus.xml', |
|||
], |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,7 @@ |
|||
## Module <hr_zk_attendance> |
|||
|
|||
#### 26.03.2025 |
|||
#### Version 18.0.1.0.0 |
|||
##### ADD |
|||
|
|||
- Initial commit for Biometric Device Integration |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from . import biometric_device_details |
|||
from . import zk_machine_attendance |
|||
from . import daily_attendance |
|||
from . import hr_employee |
@ -0,0 +1,260 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
import datetime |
|||
import logging |
|||
import pytz |
|||
from odoo import api, fields, models, _ |
|||
from odoo.exceptions import UserError, ValidationError |
|||
|
|||
_logger = logging.getLogger(__name__) |
|||
try: |
|||
from zk import ZK, const |
|||
except ImportError: |
|||
_logger.error("Please Install pyzk library.") |
|||
|
|||
|
|||
class BiometricDeviceDetails(models.Model): |
|||
"""Model for configuring and connect the biometric device with odoo""" |
|||
_name = 'biometric.device.details' |
|||
_description = 'Biometric Device Details' |
|||
|
|||
name = fields.Char(string='Name', required=True, help='Record Name') |
|||
device_ip = fields.Char(string='Device IP', required=True, |
|||
help='The IP address of the Device') |
|||
port_number = fields.Integer(string='Port Number', required=True, |
|||
help="The Port Number of the Device") |
|||
address_id = fields.Many2one('res.partner', string='Working Address', |
|||
help='Working address of the partner') |
|||
company_id = fields.Many2one('res.company', string='Company', |
|||
default=lambda |
|||
self: self.env.user.company_id.id, |
|||
help='Current Company') |
|||
|
|||
def device_connect(self, zk): |
|||
"""Function for connecting the device with Odoo""" |
|||
try: |
|||
conn = zk.connect() |
|||
return conn |
|||
except Exception: |
|||
return False |
|||
|
|||
def action_test_connection(self): |
|||
"""Checking the connection status""" |
|||
zk = ZK(self.device_ip, port=self.port_number, timeout=30, |
|||
password=False, ommit_ping=False) |
|||
try: |
|||
if zk.connect(): |
|||
return { |
|||
'type': 'ir.actions.client', |
|||
'tag': 'display_notification', |
|||
'params': { |
|||
'message': 'Successfully Connected', |
|||
'type': 'success', |
|||
'sticky': False |
|||
} |
|||
} |
|||
except Exception as error: |
|||
raise ValidationError(f'{error}') |
|||
|
|||
def action_set_timezone(self): |
|||
"""Function to set user's timezone to device""" |
|||
for info in self: |
|||
machine_ip = info.device_ip |
|||
zk_port = info.port_number |
|||
try: |
|||
# Connecting with the device with the ip and port provided |
|||
zk = ZK(machine_ip, port=zk_port, timeout=15, |
|||
password=0, |
|||
force_udp=False, ommit_ping=False) |
|||
except NameError: |
|||
raise UserError( |
|||
_("Pyzk module not Found. Please install it" |
|||
"with 'pip3 install pyzk'.")) |
|||
conn = self.device_connect(zk) |
|||
if conn: |
|||
user_tz = self.env.context.get( |
|||
'tz') or self.env.user.tz or 'UTC' |
|||
user_timezone_time = pytz.utc.localize(fields.Datetime.now()) |
|||
user_timezone_time = user_timezone_time.astimezone( |
|||
pytz.timezone(user_tz)) |
|||
conn.set_time(user_timezone_time) |
|||
return { |
|||
'type': 'ir.actions.client', |
|||
'tag': 'display_notification', |
|||
'params': { |
|||
'message': 'Successfully Set the Time', |
|||
'type': 'success', |
|||
'sticky': False |
|||
} |
|||
} |
|||
else: |
|||
raise UserError(_( |
|||
"Please Check the Connection")) |
|||
|
|||
def action_clear_attendance(self): |
|||
"""Methode to clear record from the zk.machine.attendance model and |
|||
from the device""" |
|||
for info in self: |
|||
try: |
|||
machine_ip = info.device_ip |
|||
zk_port = info.port_number |
|||
try: |
|||
# Connecting with the device |
|||
zk = ZK(machine_ip, port=zk_port, timeout=30, |
|||
password=0, force_udp=False, ommit_ping=False) |
|||
except NameError: |
|||
raise UserError(_( |
|||
"Please install it with 'pip3 install pyzk'.")) |
|||
conn = self.device_connect(zk) |
|||
if conn: |
|||
conn.enable_device() |
|||
clear_data = zk.get_attendance() |
|||
if clear_data: |
|||
# Clearing data in the device |
|||
conn.clear_attendance() |
|||
# Clearing data from attendance log |
|||
self._cr.execute( |
|||
"""delete from zk_machine_attendance""") |
|||
conn.disconnect() |
|||
else: |
|||
raise UserError( |
|||
_('Unable to clear Attendance log.Are you sure ' |
|||
'attendance log is not empty.')) |
|||
else: |
|||
raise UserError( |
|||
_('Unable to connect to Attendance Device. Please use ' |
|||
'Test Connection button to verify.')) |
|||
except Exception as error: |
|||
raise ValidationError(f'{error}') |
|||
|
|||
@api.model |
|||
def cron_download(self): |
|||
machines = self.env['biometric.device.details'].search([]) |
|||
for machine in machines: |
|||
machine.action_download_attendance() |
|||
|
|||
def action_download_attendance(self): |
|||
"""Function to download attendance records from the device""" |
|||
_logger.info("++++++++++++Cron Executed++++++++++++++++++++++") |
|||
zk_attendance = self.env['zk.machine.attendance'] |
|||
hr_attendance = self.env['hr.attendance'] |
|||
for info in self: |
|||
machine_ip = info.device_ip |
|||
zk_port = info.port_number |
|||
try: |
|||
# Connecting with the device with the ip and port provided |
|||
zk = ZK(machine_ip, port=zk_port, timeout=15, |
|||
password=0, |
|||
force_udp=False, ommit_ping=False) |
|||
except NameError: |
|||
raise UserError( |
|||
_("Pyzk module not Found. Please install it" |
|||
"with 'pip3 install pyzk'.")) |
|||
conn = self.device_connect(zk) |
|||
self.action_set_timezone() |
|||
if conn: |
|||
conn.disable_device() # Device Cannot be used during this time. |
|||
user = conn.get_users() |
|||
attendance = conn.get_attendance() |
|||
if attendance: |
|||
for each in attendance: |
|||
atten_time = each.timestamp |
|||
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.datetime.strptime( |
|||
utc_dt, "%Y-%m-%d %H:%M:%S") |
|||
atten_time = fields.Datetime.to_string(atten_time) |
|||
for uid in user: |
|||
if uid.user_id == each.user_id: |
|||
get_user_id = self.env['hr.employee'].search( |
|||
[('device_id_num', '=', each.user_id)]) |
|||
if get_user_id: |
|||
duplicate_atten_ids = zk_attendance.search( |
|||
[('device_id_num', '=', each.user_id), |
|||
('punching_time', '=', atten_time)]) |
|||
if not duplicate_atten_ids: |
|||
zk_attendance.create({ |
|||
'employee_id': get_user_id.id, |
|||
'device_id_num': each.user_id, |
|||
'attendance_type': str(each.status), |
|||
'punch_type': str(each.punch), |
|||
'punching_time': atten_time, |
|||
'address_id': info.address_id.id |
|||
}) |
|||
att_var = hr_attendance.search([( |
|||
'employee_id', '=', get_user_id.id), |
|||
('check_out', '=', False)]) |
|||
if each.punch == 0: # check-in |
|||
if not att_var: |
|||
hr_attendance.create({ |
|||
'employee_id': |
|||
get_user_id.id, |
|||
'check_in': atten_time |
|||
}) |
|||
if each.punch == 1: # check-out |
|||
if len(att_var) == 1: |
|||
att_var.write({ |
|||
'check_out': atten_time |
|||
}) |
|||
else: |
|||
att_var1 = hr_attendance.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_num': each.user_id, |
|||
'name': uid.name |
|||
}) |
|||
zk_attendance.create({ |
|||
'employee_id': employee.id, |
|||
'device_id_num': each.user_id, |
|||
'attendance_type': str(each.status), |
|||
'punch_type': str(each.punch), |
|||
'punching_time': atten_time, |
|||
'address_id': info.address_id.id |
|||
}) |
|||
hr_attendance.create({ |
|||
'employee_id': employee.id, |
|||
'check_in': atten_time |
|||
}) |
|||
conn.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.')) |
|||
|
|||
def action_restart_device(self): |
|||
"""For restarting the device""" |
|||
zk = ZK(self.device_ip, port=self.port_number, timeout=15, |
|||
password=0, |
|||
force_udp=False, ommit_ping=False) |
|||
self.device_connect(zk).restart() |
@ -0,0 +1,73 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models, tools |
|||
|
|||
|
|||
class DailyAttendance(models.Model): |
|||
"""Model to hold data from the biometric device""" |
|||
_name = 'daily.attendance' |
|||
_description = 'Daily Attendance Report' |
|||
_auto = False |
|||
_order = 'punching_day desc' |
|||
|
|||
employee_id = fields.Many2one('hr.employee', string='Employee', |
|||
help='Employee Name') |
|||
punching_day = fields.Datetime(string='Date', help='Date of punching') |
|||
address_id = fields.Many2one('res.partner', string='Working Address', |
|||
help='Working address of the employee') |
|||
attendance_type = fields.Selection([('1', 'Finger'), ('15', 'Face'), |
|||
('2', 'Type_2'), ('3', 'Password'), |
|||
('4', 'Card')], string='Category', |
|||
help='Attendance detecting methods') |
|||
punch_type = fields.Selection([('0', 'Check In'), ('1', 'Check Out'), |
|||
('2', 'Break Out'), ('3', 'Break In'), |
|||
('4', 'Overtime In'), ('5', 'Overtime Out')], |
|||
string='Punching Type', |
|||
help='The Punching Type of attendance') |
|||
punching_time = fields.Datetime(string='Punching Time', |
|||
help='Punching time in the device') |
|||
|
|||
def init(self): |
|||
"""Retrieve the data's for attendance report""" |
|||
tools.drop_view_if_exists(self._cr, 'daily_attendance') |
|||
query = """ |
|||
create or replace view daily_attendance as ( |
|||
select |
|||
min(z.id) as id, |
|||
z.employee_id as employee_id, |
|||
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,30 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class HrEmployee(models.Model): |
|||
"""Inherit the model to add field""" |
|||
_inherit = 'hr.employee' |
|||
|
|||
device_id_num = fields.Char(string='Biometric Device ID', |
|||
help="Give the biometric device id") |
@ -0,0 +1,52 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Bhagyadev KP (odoo@cybrosys.com) |
|||
# |
|||
# This program is free software: you can modify |
|||
# it under the terms of the GNU Affero General Public License (AGPL) as |
|||
# published by the Free Software Foundation, either version 3 of the |
|||
# License, or (at your option) any later version. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU Affero General Public License for more details. |
|||
# |
|||
# You should have received a copy of the GNU Affero General Public License |
|||
# along with this program. If not, see <https://www.gnu.org/licenses/>. |
|||
# |
|||
################################################################################ |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ZkMachineAttendance(models.Model): |
|||
"""Model to hold data from the biometric device""" |
|||
_name = 'zk.machine.attendance' |
|||
_description = '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_num = fields.Char(string='Biometric Device ID', |
|||
help="The ID of the Biometric Device") |
|||
punch_type = fields.Selection([('0', 'Check In'), ('1', 'Check Out'), |
|||
('2', 'Break Out'), ('3', 'Break In'), |
|||
('4', 'Overtime In'), ('5', 'Overtime Out'), |
|||
('255', 'Duplicate')], |
|||
string='Punching Type', |
|||
help='Punching type of the attendance') |
|||
attendance_type = fields.Selection([('1', 'Finger'), ('15', 'Face'), |
|||
('2', 'Type_2'), ('3', 'Password'), |
|||
('4', 'Card'), ('255', 'Duplicate')], |
|||
string='Category', |
|||
help="Attendance detecting methods") |
|||
punching_time = fields.Datetime(string='Punching Time', |
|||
help="Punching time in the device") |
|||
address_id = fields.Many2one('res.partner', string='Working Address', |
|||
help="Working address of the employee") |
|
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 28 KiB |
After Width: | Height: | Size: 628 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 738 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 767 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 760 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 697 KiB |
After Width: | Height: | Size: 1.1 MiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 39 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 185 KiB |
After Width: | Height: | Size: 880 KiB |
After Width: | Height: | Size: 740 KiB |
After Width: | Height: | Size: 56 KiB |
@ -0,0 +1,16 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Defined all menus here --> |
|||
<menuitem id="biometric_device_details_menu" |
|||
name="Biometric Device" |
|||
parent="hr_attendance.menu_hr_attendance_root" |
|||
sequence="21"/> |
|||
<menuitem id="biometric_device_details_sub_menu" |
|||
action="biometric_device_details_action" |
|||
parent="biometric_device_details_menu" |
|||
sequence="21"/> |
|||
<menuitem id="daily_attendance_menu" |
|||
action="daily_attendance_action" |
|||
parent="biometric_device_details_menu" |
|||
groups="hr_attendance.group_hr_attendance_officer"/> |
|||
</odoo> |
@ -0,0 +1,58 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!--Biometric device configuration list view--> |
|||
<record id="biometric_device_details_view_list" model="ir.ui.view"> |
|||
<field name="name">biometric.device.details.view.list</field> |
|||
<field name="model">biometric.device.details</field> |
|||
<field name="arch" type="xml"> |
|||
<list> |
|||
<field name="name"/> |
|||
<field name="device_ip"/> |
|||
<field name="port_number"/> |
|||
</list> |
|||
</field> |
|||
</record> |
|||
<!--Biometric device configuration form view--> |
|||
<record id="biometric_device_details_view_form" model="ir.ui.view"> |
|||
<field name="name">biometric.device.details.view.form</field> |
|||
<field name="model">biometric.device.details</field> |
|||
<field name="arch" type="xml"> |
|||
<form> |
|||
<header> |
|||
<button name="action_set_timezone" string=" Set Time" |
|||
type="object" class="oe_highlight"/> |
|||
<button name="action_download_attendance" |
|||
string="Download Data" |
|||
type="object" class="oe_highlight"/> |
|||
<button name="action_clear_attendance" string="Clear Data" |
|||
type="object" class="oe_highlight" |
|||
confirm="Are you sure you want to clear all |
|||
attendance records from the Device and Odoo?"/> |
|||
<button name="action_restart_device" string="Restart" |
|||
type="object" class="oe_highlight" |
|||
confirm="Are you sure you want Restart the Biometric |
|||
Device?"/> |
|||
</header> |
|||
<sheet> |
|||
<group> |
|||
<field name="name"/> |
|||
<field name="device_ip"/> |
|||
<field name="port_number"/> |
|||
<field name="address_id"/> |
|||
</group> |
|||
<button name="action_test_connection" |
|||
type="object" class="btn btn-secondary"> |
|||
<i class="fa fa-fw o_button_icon fa-television"/> |
|||
Test Connection |
|||
</button> |
|||
</sheet> |
|||
</form> |
|||
</field> |
|||
</record> |
|||
<!-- Action for the biometric device--> |
|||
<record id="biometric_device_details_action" model="ir.actions.act_window"> |
|||
<field name="name">Biometric Device</field> |
|||
<field name="res_model">biometric.device.details</field> |
|||
<field name="view_mode">list,form</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,25 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Daily attendance list view--> |
|||
<record id="daily_attendance_view_list" model="ir.ui.view"> |
|||
<field name="name">daily.attendance.view.list</field> |
|||
<field name="model">daily.attendance</field> |
|||
<field name="arch" type="xml"> |
|||
<list string="Attendance" create="false" delete="false"> |
|||
<field name="punching_day"/> |
|||
<field name="employee_id"/> |
|||
<field name="punch_type"/> |
|||
<field name="attendance_type"/> |
|||
<field name="punching_time"/> |
|||
<field name="address_id"/> |
|||
</list> |
|||
</field> |
|||
</record> |
|||
<!-- Attendance analysis action--> |
|||
<record id="daily_attendance_action" model="ir.actions.act_window"> |
|||
<field name="name">Attendance Analysis</field> |
|||
<field name="res_model">daily.attendance</field> |
|||
<field name="view_mode">list</field> |
|||
<field name="context">{}</field> |
|||
</record> |
|||
</odoo> |
@ -0,0 +1,15 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<odoo> |
|||
<!-- Inherited hr employee for adding new field--> |
|||
<record id="view_employee_form" model="ir.ui.view"> |
|||
<field name="name">hr.employee.view.form.inherit.hr.zk.attendance</field> |
|||
<field name="model">hr.employee</field> |
|||
<field name="inherit_id" ref="hr.view_employee_form"/> |
|||
<field name="arch" type="xml"> |
|||
<xpath expr="//field[@name='employee_type']" |
|||
position="after"> |
|||
<field name="device_id_num"/> |
|||
</xpath> |
|||
</field> |
|||
</record> |
|||
</odoo> |