Browse Source

Aug 23: [ADD] Initital Commits 'hr_biometric_attendance'

pull/332/merge
Cybrosys Technologies 8 months ago
parent
commit
9975ca6f10
  1. 64
      hr_biometric_attendance/README.rst
  2. 23
      hr_biometric_attendance/__init__.py
  3. 59
      hr_biometric_attendance/__manifest__.py
  4. 14
      hr_biometric_attendance/data/ir_action_data.xml
  5. 14
      hr_biometric_attendance/data/ir_cron_data.xml
  6. 6
      hr_biometric_attendance/doc/RELEASE_NOTES.md
  7. 27
      hr_biometric_attendance/models/__init__.py
  8. 635
      hr_biometric_attendance/models/biometric_device_details.py
  9. 73
      hr_biometric_attendance/models/daily_attendance.py
  10. 37
      hr_biometric_attendance/models/fingerprint_templates.py
  11. 48
      hr_biometric_attendance/models/hr_employee.py
  12. 68
      hr_biometric_attendance/models/res_config_settings.py
  13. 52
      hr_biometric_attendance/models/zk_machine_attendance.py
  14. 7
      hr_biometric_attendance/security/ir.model.access.csv
  15. BIN
      hr_biometric_attendance/static/description/assets/cybro-odoo.png
  16. BIN
      hr_biometric_attendance/static/description/assets/icons/check.png
  17. BIN
      hr_biometric_attendance/static/description/assets/icons/chevron.png
  18. BIN
      hr_biometric_attendance/static/description/assets/icons/cogs.png
  19. BIN
      hr_biometric_attendance/static/description/assets/icons/consultation.png
  20. BIN
      hr_biometric_attendance/static/description/assets/icons/ecom-black.png
  21. BIN
      hr_biometric_attendance/static/description/assets/icons/education-black.png
  22. BIN
      hr_biometric_attendance/static/description/assets/icons/faq.png
  23. BIN
      hr_biometric_attendance/static/description/assets/icons/feature.png
  24. BIN
      hr_biometric_attendance/static/description/assets/icons/hotel-black.png
  25. BIN
      hr_biometric_attendance/static/description/assets/icons/license.png
  26. BIN
      hr_biometric_attendance/static/description/assets/icons/lifebuoy.png
  27. BIN
      hr_biometric_attendance/static/description/assets/icons/manufacturing-black.png
  28. BIN
      hr_biometric_attendance/static/description/assets/icons/notes.png
  29. BIN
      hr_biometric_attendance/static/description/assets/icons/pos-black.png
  30. BIN
      hr_biometric_attendance/static/description/assets/icons/puzzle.png
  31. BIN
      hr_biometric_attendance/static/description/assets/icons/restaurant-black.png
  32. BIN
      hr_biometric_attendance/static/description/assets/icons/screenshot.png
  33. BIN
      hr_biometric_attendance/static/description/assets/icons/service-black.png
  34. BIN
      hr_biometric_attendance/static/description/assets/icons/skype.png
  35. 53
      hr_biometric_attendance/static/description/assets/icons/star-1.svg
  36. BIN
      hr_biometric_attendance/static/description/assets/icons/support.png
  37. BIN
      hr_biometric_attendance/static/description/assets/icons/test-1.png
  38. BIN
      hr_biometric_attendance/static/description/assets/icons/trading-black.png
  39. BIN
      hr_biometric_attendance/static/description/assets/icons/training.png
  40. BIN
      hr_biometric_attendance/static/description/assets/icons/update.png
  41. BIN
      hr_biometric_attendance/static/description/assets/icons/user.png
  42. BIN
      hr_biometric_attendance/static/description/assets/icons/whatsapp.png
  43. BIN
      hr_biometric_attendance/static/description/assets/icons/wrench.png
  44. BIN
      hr_biometric_attendance/static/description/assets/misc/categories.png
  45. BIN
      hr_biometric_attendance/static/description/assets/misc/check-box.png
  46. BIN
      hr_biometric_attendance/static/description/assets/misc/compass.png
  47. BIN
      hr_biometric_attendance/static/description/assets/misc/corporate.png
  48. BIN
      hr_biometric_attendance/static/description/assets/misc/customer-support.png
  49. BIN
      hr_biometric_attendance/static/description/assets/misc/cybrosys-logo.png
  50. BIN
      hr_biometric_attendance/static/description/assets/misc/features.png
  51. BIN
      hr_biometric_attendance/static/description/assets/misc/logo.png
  52. BIN
      hr_biometric_attendance/static/description/assets/misc/pictures.png
  53. BIN
      hr_biometric_attendance/static/description/assets/misc/pie-chart.png
  54. BIN
      hr_biometric_attendance/static/description/assets/misc/right-arrow.png
  55. BIN
      hr_biometric_attendance/static/description/assets/misc/star.png
  56. BIN
      hr_biometric_attendance/static/description/assets/misc/support.png
  57. BIN
      hr_biometric_attendance/static/description/assets/misc/whatsapp.png
  58. BIN
      hr_biometric_attendance/static/description/assets/modules/1.png
  59. BIN
      hr_biometric_attendance/static/description/assets/modules/2.png
  60. BIN
      hr_biometric_attendance/static/description/assets/modules/3.png
  61. BIN
      hr_biometric_attendance/static/description/assets/modules/4.png
  62. BIN
      hr_biometric_attendance/static/description/assets/modules/5.png
  63. BIN
      hr_biometric_attendance/static/description/assets/modules/6.png
  64. BIN
      hr_biometric_attendance/static/description/assets/screenshots/1.png
  65. BIN
      hr_biometric_attendance/static/description/assets/screenshots/10.png
  66. BIN
      hr_biometric_attendance/static/description/assets/screenshots/11.png
  67. BIN
      hr_biometric_attendance/static/description/assets/screenshots/12.png
  68. BIN
      hr_biometric_attendance/static/description/assets/screenshots/13.png
  69. BIN
      hr_biometric_attendance/static/description/assets/screenshots/14.png
  70. BIN
      hr_biometric_attendance/static/description/assets/screenshots/15.png
  71. BIN
      hr_biometric_attendance/static/description/assets/screenshots/16.png
  72. BIN
      hr_biometric_attendance/static/description/assets/screenshots/17.png
  73. BIN
      hr_biometric_attendance/static/description/assets/screenshots/18.png
  74. BIN
      hr_biometric_attendance/static/description/assets/screenshots/19.png
  75. BIN
      hr_biometric_attendance/static/description/assets/screenshots/2.png
  76. BIN
      hr_biometric_attendance/static/description/assets/screenshots/20.png
  77. BIN
      hr_biometric_attendance/static/description/assets/screenshots/21.png
  78. BIN
      hr_biometric_attendance/static/description/assets/screenshots/3.png
  79. BIN
      hr_biometric_attendance/static/description/assets/screenshots/4.png
  80. BIN
      hr_biometric_attendance/static/description/assets/screenshots/5.png
  81. BIN
      hr_biometric_attendance/static/description/assets/screenshots/7.png
  82. BIN
      hr_biometric_attendance/static/description/assets/screenshots/8.png
  83. BIN
      hr_biometric_attendance/static/description/assets/screenshots/9.png
  84. BIN
      hr_biometric_attendance/static/description/assets/screenshots/hero.gif
  85. BIN
      hr_biometric_attendance/static/description/banner.png
  86. BIN
      hr_biometric_attendance/static/description/icon.png
  87. 1274
      hr_biometric_attendance/static/description/index.html
  88. 62
      hr_biometric_attendance/static/src/js/stopwatch.js
  89. 10
      hr_biometric_attendance/static/src/xml/stopwatch_view.xml
  90. 16
      hr_biometric_attendance/views/biometric_device_attendance_menus.xml
  91. 88
      hr_biometric_attendance/views/biometric_device_details_views.xml
  92. 25
      hr_biometric_attendance/views/daily_attendance_views.xml
  93. 25
      hr_biometric_attendance/views/hr_employee_views.xml
  94. 52
      hr_biometric_attendance/views/res_config_settings_views.xml
  95. 23
      hr_biometric_attendance/wizards/__init__.py
  96. 66
      hr_biometric_attendance/wizards/employee_biometric.py
  97. 40
      hr_biometric_attendance/wizards/employee_biometric_views.xml
  98. 87
      hr_biometric_attendance/wizards/user_management.py
  99. 41
      hr_biometric_attendance/wizards/user_management_views.xml

64
hr_biometric_attendance/README.rst

@ -0,0 +1,64 @@
.. 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
HR Biometric Device Integration
===============================
This Cybrosys's module integrates Odoo attendance with biometric device attendance.
Configuration
============
* This integration is applicable for the the ZK Devices.
* 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: (V17) Mufeeda Shirin,
(V16) Nihala KP
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
--------
This module is maintained by Cybrosys Technologies.
For support and more information, please visit https://www.cybrosys.com
.. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com"
Further Information
-----------
HTML Description: `<static/description/index.html>`__

23
hr_biometric_attendance/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from . import models
from . import wizards

59
hr_biometric_attendance/__manifest__.py

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
{
'name': "HR Biometric Device Integration",
'version': "16.0.1.0.0",
'summary': "Integrating Zk Biometric Devices With HR Attendance",
'description': '''This module integrates Odoo with ZK biometric devices,
incorporating features such as live capturing and user management''',
'category': 'Human Resources',
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com",
'depends': ['base_setup', 'hr_attendance', 'web'],
'data': [
'security/ir.model.access.csv',
'data/ir_cron_data.xml',
'data/ir_action_data.xml',
'wizards/user_management_views.xml',
'wizards/employee_biometric_views.xml',
'views/biometric_device_details_views.xml',
'views/hr_employee_views.xml',
'views/daily_attendance_views.xml',
'views/res_config_settings_views.xml',
'views/biometric_device_attendance_menus.xml',
],
'assets': {
'web.assets_backend': [
'hr_biometric_attendance/static/src/xml/stopwatch_view.xml',
'hr_biometric_attendance/static/src/js/stopwatch.js',
]
},
'external_dependencies': {
'python': ['pyzk'], },
'images': ['static/description/banner.png'],
'license': 'AGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

14
hr_biometric_attendance/data/ir_action_data.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Server actions for Biometric Device in hr.employee-->
<record id="biometric_device_user" model="ir.actions.server">
<field name="name">Biometric Device</field>
<field name="model_id" ref="model_hr_employee"/>
<field name="binding_model_id" ref="model_hr_employee"/>
<field name="binding_view_types">form</field>
<field name="state">code</field>
<field name="code">
action = records.action_biometric_device()
</field>
</record>
</odoo>

14
hr_biometric_attendance/data/ir_cron_data.xml

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Schedule Job for Attendance downloading-->
<record id="ir_cron_schedule_attendance_action" model="ir.cron">
<field name="name">Schedule Attendance Downloading</field>
<field name="model_id" ref="model_biometric_device_details"/>
<field name="state">code</field>
<field name="code">model.schedule_attendance()</field>
<field name="interval_number">1</field>
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False"/>
</record>
</odoo>

6
hr_biometric_attendance/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <hr_biometric_attendance>
#### 25.07.2024
#### Version 16.0.1.0.0
#### ADD
- Initial commit for HR Biometric Device Integration

27
hr_biometric_attendance/models/__init__.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from . import biometric_device_details
from . import zk_machine_attendance
from . import daily_attendance
from . import fingerprint_templates
from . import hr_employee
from . import res_config_settings

635
hr_biometric_attendance/models/biometric_device_details.py

@ -0,0 +1,635 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
import base64
import binascii
import datetime
import logging
import threading
from threading import Thread
import time
import pytz
from zk.exception import ZKErrorResponse
from odoo import api, fields, models, registry, _
from odoo.exceptions import UserError, ValidationError
live_capture_thread = None
_logger = logging.getLogger(__name__)
try:
from zk import const, ZK
from zk.finger import Finger
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'
_inherit = ['mail.thread', 'mail.activity.mixin']
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')
is_live_capture = fields.Boolean('Live Capturing',
help="if enabled, gets the live capture "
"from the device",
readonly=True)
company_id = fields.Many2one('res.company', string='Company',
default=lambda
self: self.env.user.company_id.id,
help='Current Company')
stopwatch_time = fields.Float('Stopwatch timer',
help='Time from Live capture enabled')
device_name = fields.Char(String='Device Name', readonly=True,
help='Device Name')
device_firmware = fields.Char(String='Device Firmware Version',
readonly=True, help='Device Firmware')
device_serial_no = fields.Char(String='Device Serial No', readonly=True,
help='Device serial No')
device_platform = fields.Char(String='Device Platform', readonly=True,
help='Device platform')
device_mac = fields.Char(String='Device Mac ID', readonly=True,
help='Device Mac')
live_capture_start_time = fields.Datetime('Live Capture Time',
help='The Time When Live '
'Capture Enabled')
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():
zk.test_voice(index=0)
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_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""")
current_time = fields.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S')
message = (f'Attendances Are cleared from the Device on'
f' {current_time} By {self.env.user.name}')
self.message_post(body=message)
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}')
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.get_device_information()
if conn:
conn.disable_device()
self.get_all_users()
self.action_set_timezone()
user = conn.get_users()
# get All Fingerprints
fingers = conn.get_templates()
for use in user:
for finger in fingers:
if finger.uid == use.uid:
templates = conn.get_user_template(uid=use.uid,
temp_id=finger.fid,
user_id=use.user_id)
hex_data = templates.template.hex()
# Convert hex data to binary
binary_data = binascii.unhexlify(hex_data)
base64_data = base64.b64encode(binary_data).decode(
'utf-8')
employee = self.env['hr.employee'].search(
[('device_id_num', '=', use.user_id)])
employee.write({
'device_id': self.id,
})
if str(finger.fid) in employee.fingerprint_ids.mapped(
'finger_id'):
employee.fingerprint_ids.search(
[('finger_id', '=', finger.fid)]).update({
'finger_template': base64_data,
})
else:
employee.fingerprint_ids.create({
'finger_template': base64_data,
'finger_id': finger.fid,
'employee_id': employee.id,
'filename': f'{employee.name}-finger-{finger.fid}'
})
# get all attendances
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,
'device_id': self.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
})
if not self.is_live_capture:
current_time = fields.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S')
message = (f'Downloaded data from the device on '
f'{current_time} by {self.env.user.name}')
self.message_post(body=message)
conn.disconnect()
return True
else:
zk.test_voice(index=4)
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)
if self.device_connect(zk):
if self.is_live_capture:
self.action_stop_live_capture()
self.device_connect(zk).restart()
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': 'Successfully Device Restarted',
'type': 'success',
'sticky': False
}
}
else:
raise UserError(_(
"Please Check the Connection"))
def schedule_attendance(self):
"""Schedule action for attendance downloading"""
for record in self.search([]):
if record.is_live_capture:
record.action_stop_live_capture()
record.action_download_attendance()
record.action_live_capture()
else:
record.action_download_attendance()
def action_live_capture(self):
""" Enable Live capture With Thread"""
for info in self:
machine_ip = info.device_ip
zk_port = info.port_number
try:
self.is_live_capture = True
self.action_set_timezone()
instance = ZKBioAttendance(machine_ip, zk_port, info)
global live_capture_thread
live_capture_thread = instance
live_capture_thread.start()
self.live_capture_start_time = fields.datetime.now()
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
except NameError:
raise UserError(_(
"Please install it with 'pip3 install pyzk'."))
def action_stop_live_capture(self):
"""Function to stop Live capture"""
try:
self.is_live_capture = False
if live_capture_thread:
live_capture_thread.stop()
return {
'type': 'ir.actions.client',
'tag': 'reload',
}
except NameError:
raise UserError(_(
"Please install it with 'pip3 install pyzk'."))
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 get_all_users(self):
"""Function to get all user's details"""
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:
users = conn.get_users()
for user in users:
employee = self.env['hr.employee'].search(
[('device_id_num', '=', user.user_id),
('device_id', '=', self.id)])
if employee:
employee.write({
'name': user.name,
})
else:
self.env['hr.employee'].create({
'name': user.name,
'device_id_num': user.user_id,
'device_id': self.id,
})
else:
raise UserError(_(
"Please Check the Connection"))
def set_user(self, employee_id):
"""Function to create or update users"""
for info in self:
machine_ip = info.device_ip
zk_port = info.port_number
employee = self.env['hr.employee'].browse(int(employee_id))
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:
last_user = conn.get_users()[-1]
privilege = 0
password = ''
group_id = ''
user_id = ''
card = 0
try:
uids = [user.uid for user in conn.get_users()]
candidate_uid = last_user.uid + 1
while candidate_uid in uids:
candidate_uid += 1
conn.set_user(candidate_uid, employee.name, privilege,
password, group_id, user_id, card)
except ZKErrorResponse as e:
_logger.error("Failed to set user on the device: %s",
str(e))
if conn.get_users()[-1].name == employee.name:
employee.write({
'device_id': self.id,
'device_id_num': conn.get_users()[-1].user_id
})
current_time = fields.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S')
message = (f'New User {employee.name} Created on '
f'{current_time} by {self.env.user.name}')
self.message_post(body=message)
else:
raise UserError(_(
"Please Check the Connection"))
def delete_user(self, employee_id, delete_user_selection):
"""Function to Delete a user"""
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:
employee = self.env['hr.employee'].browse(int(employee_id))
employee_name = employee.name
conn.delete_user(uid=None, user_id=employee.device_id_num)
employee.write({
'device_id_num': False,
'device_id': False
})
employee.fingerprint_ids.unlink()
if delete_user_selection == 'both_device':
employee.unlink()
current_time = fields.datetime.now().strftime(
'%Y-%m-%d %H:%M:%S')
message = (f'Deleted User {employee_name} on '
f'{current_time} by {self.env.user.name}')
self.message_post(body=message)
else:
raise UserError(_(
"Please Check the Connection"))
def update_user(self, employee_id):
"""Function to Update a user"""
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:
employee = self.env['hr.employee'].browse(int(employee_id))
for line in conn.get_users():
if line.user_id == employee.device_id_num:
privilege = 0
password = ''
group_id = ''
user_id = employee.device_id_num
card = 0
conn.set_user(line.uid, employee.name, privilege,
password, group_id, user_id, card)
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'message': 'Successfully Updated User',
'type': 'success',
'sticky': False
}
}
else:
raise UserError(_(
"Please Check the Connection"))
def get_device_information(self):
"""Gets device Information"""
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:
self.device_name = conn.get_device_name()
self.device_firmware = conn.get_firmware_version()
self.device_serial_no = conn.get_serialnumber()
self.device_platform = conn.get_platform()
self.device_mac = conn.get_mac()
else:
raise UserError(_(
"Please Check the Connection"))
class ZKBioAttendance(Thread):
"""
Represents a thread for capturing live attendance data from a ZKTeco
biometric device.
Attributes: - machine_ip: The IP address of the ZKTeco biometric device.
- port_no: The port number for communication with the ZKTeco biometric
device. - conn: The connection object to the ZKTeco biometric device.
Methods: - run(): Overrides the run method of the Thread class to capture
live attendance data.
"""
def __init__(self, machine_ip, port_no, record):
"""Function to Initialize the thread"""
Thread.__init__(self)
self.machine_ip = machine_ip
self.port_no = port_no
self.record = record
self.env = record.env
self.stop_event = threading.Event()
zk_device = ZK(
machine_ip,
port=port_no,
timeout=5,
password=0,
force_udp=False,
ommit_ping=False,
)
conn = zk_device.connect()
if conn:
self.conn = conn
else:
raise UserError(_(
"Please Check the Connection"))
def run(self):
"""Function to run the Thread"""
while not self.stop_event.is_set():
try:
if not self.conn.end_live_capture:
for attendance in self.conn.live_capture(2000):
self._data_live_capture()
time.sleep(10)
except Exception as e:
self.env.cr.rollback() # Rollback the current transaction
time.sleep(1)
def stop(self):
"""Stops the live capture and stops the thread"""
if self.conn:
self.conn.end_live_capture = True
self.stop_event.set()
def _data_live_capture(self):
"""Updated the Live Capture real time"""
with registry(self.env.cr.dbname).cursor() as new_cr:
new_env = api.Environment(new_cr, self.env.uid, self.env.context)
if self.conn.get_attendance():
self.record.with_env(new_env).action_download_attendance()
new_cr.commit()

73
hr_biometric_attendance/models/daily_attendance.py

@ -0,0 +1,73 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://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)

37
hr_biometric_attendance/models/fingerprint_templates.py

@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import fields, models
class FingerprintTemplates(models.Model):
"""Inherit the model to add field"""
_name = 'fingerprint.templates'
_description = 'Finger Print Templates for Employee'
employee_id = fields.Many2one('hr.employee', string='Employee',
help='The Employee ')
finger_id = fields.Char(string='Finger Id',
help='The Number that refers the Finger')
filename = fields.Char(string='Finger File Name',
help='File Name of the Uploaded Finger Print')
finger_template = fields.Binary(string='Finger Template',
help='The Uploaded Finger Print file')

48
hr_biometric_attendance/models/hr_employee.py

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://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", copy=False)
device_id = fields.Many2one('biometric.device.details', copy=False,
readonly=True,
help='The biometric device details')
fingerprint_ids = fields.One2many('fingerprint.templates', 'employee_id',
help='Store finger print templates of '
'an employee')
def action_biometric_device(self):
"""Server Action for Biometric Device which open a wizard with
several options"""
return {
'type': 'ir.actions.act_window',
'target': 'new',
'name': _('Biometric Management'),
'view_mode': 'form',
'res_model': 'employee.biometric',
'context': {'default_employee_id': self.id},
}

68
hr_biometric_attendance/models/res_config_settings.py

@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from datetime import timedelta
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
""" Inherited res.config.settings to add new fields """
_inherit = 'res.config.settings'
schedule_attendance_downloads = fields.Boolean(string="Schedule Downloads",
config_parameter='hr_biometric_attendance.schedule_downloads',
default=False,
help='If Enabled we can '
'schedule attendance '
'downloading from '
'device')
schedule_time_interval = fields.Integer(string="Schedule Time Interval",
config_parameter='hr_biometric_attendance.schedule_time_interval',
default=1,
help='We can set Time interval '
'for the Scheduling')
schedule_time_period = fields.Selection(
selection=[('hours', 'Hours'), ('days', 'Days')],
string="Schedule Time Period",
config_parameter='hr_biometric_attendance.schedule_time_period',
default='days', help='We can set Time Period for the Scheduling')
def set_values(self):
""" Super the function to set the values from settings to the
cron.job"""
super().set_values()
if self.schedule_attendance_downloads:
self.env['ir.cron'].search(
[('name', '=', 'Schedule Attendance Downloading')]).write({
'active': True,
'interval_type': self.schedule_time_period,
'interval_number': self.schedule_time_interval,
'nextcall': fields.datetime.now() + timedelta(
hours=self.schedule_time_interval) if
self.schedule_time_period == 'hours' else
fields.datetime.now() + timedelta(
days=self.schedule_time_interval),
})
else:
self.env['ir.cron'].search(
[('name', '=', 'Schedule Attendance Downloading')]).write({
'active': False
})

52
hr_biometric_attendance/models/zk_machine_attendance.py

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://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")

7
hr_biometric_attendance/security/ir.model.access.csv

@ -0,0 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_biometric_device_details,access.biometric.device.details,model_biometric_device_details,base.group_user,1,1,1,1
access_daily_attendance,access.daily.attendance,model_daily_attendance,base.group_user,1,1,1,1
access_zk_machine_attendance,access.zk.machine.attendance,model_zk_machine_attendance,base.group_user,1,1,1,1
access_zk_user_management,access.zk.user.management,model_zk_user_management,base.group_user,1,1,1,1
access_employee_biometric,access.employee.biometric,model_employee_biometric,base.group_user,1,1,1,1
access_clear_fingerprint_templates,access.fingerprint.templates,model_fingerprint_templates,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_biometric_device_details access.biometric.device.details model_biometric_device_details base.group_user 1 1 1 1
3 access_daily_attendance access.daily.attendance model_daily_attendance base.group_user 1 1 1 1
4 access_zk_machine_attendance access.zk.machine.attendance model_zk_machine_attendance base.group_user 1 1 1 1
5 access_zk_user_management access.zk.user.management model_zk_user_management base.group_user 1 1 1 1
6 access_employee_biometric access.employee.biometric model_employee_biometric base.group_user 1 1 1 1
7 access_clear_fingerprint_templates access.fingerprint.templates model_fingerprint_templates base.group_user 1 1 1 1

BIN
hr_biometric_attendance/static/description/assets/cybro-odoo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
hr_biometric_attendance/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
hr_biometric_attendance/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
hr_biometric_attendance/static/description/assets/icons/faq.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/feature.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
hr_biometric_attendance/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
hr_biometric_attendance/static/description/assets/icons/notes.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
hr_biometric_attendance/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
hr_biometric_attendance/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
hr_biometric_attendance/static/description/assets/icons/screenshot.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
hr_biometric_attendance/static/description/assets/icons/skype.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

53
hr_biometric_attendance/static/description/assets/icons/star-1.svg

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1024px" height="1024px" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" xmlns:xlink="http://www.w3.org/1999/xlink">
<g><path style="opacity:0.968" fill="#6802dd" d="M 872.5,351.5 C 869.316,353.414 865.649,354.08 861.5,353.5C 861.068,352.29 860.401,351.29 859.5,350.5C 855.884,340.601 851.217,331.268 845.5,322.5C 841.319,314.792 836.652,307.458 831.5,300.5C 825.654,289.989 818.654,280.322 810.5,271.5C 804.551,263.217 797.884,255.55 790.5,248.5C 783.833,241.167 776.833,234.167 769.5,227.5C 763.245,221.369 756.578,215.702 749.5,210.5C 738.906,201.529 727.572,193.529 715.5,186.5C 706.018,180.041 696.018,174.374 685.5,169.5C 673.375,162.774 660.708,157.107 647.5,152.5C 631.495,146.499 615.162,141.499 598.5,137.5C 507.572,117.423 420.572,128.089 337.5,169.5C 326.51,174.993 315.843,180.993 305.5,187.5C 296.223,193.444 287.223,199.778 278.5,206.5C 269.849,213.154 261.516,220.154 253.5,227.5C 246.167,234.167 239.167,241.167 232.5,248.5C 225.116,255.55 218.449,263.217 212.5,271.5C 204.346,280.322 197.346,289.989 191.5,300.5C 186.348,307.458 181.681,314.792 177.5,322.5C 171.798,331.237 167.132,340.57 163.5,350.5C 162.599,351.29 161.932,352.29 161.5,353.5C 157.351,354.08 153.684,353.414 150.5,351.5C 183.041,278.449 233.041,220.282 300.5,177C 398.817,117.525 503.817,101.859 615.5,130C 722.993,161.19 804.16,225.357 859,322.5C 864.059,331.948 868.559,341.615 872.5,351.5 Z"/></g>
<g><path style="opacity:1" fill="#b779fc" d="M 598.5,137.5 C 615.162,141.499 631.495,146.499 647.5,152.5C 640.391,154.014 633.058,154.847 625.5,155C 625.957,155.414 626.291,155.914 626.5,156.5C 616.183,157.332 605.849,157.832 595.5,158C 595.957,158.414 596.291,158.914 596.5,159.5C 591.533,160.329 586.533,160.829 581.5,161C 581.957,161.414 582.291,161.914 582.5,162.5C 572.833,163.167 563.167,163.833 553.5,164.5C 554.624,165.914 555.624,167.414 556.5,169C 540.819,169.345 525.152,170.011 509.5,171C 510.167,171.667 510.833,172.333 511.5,173C 501.5,173.333 491.5,173.667 481.5,174C 482.167,175 482.833,176 483.5,177C 474.176,177.744 464.843,178.244 455.5,178.5C 419.098,172.35 382.431,169.683 345.5,170.5C 358.362,169.511 371.362,168.344 384.5,167C 383.833,166 383.167,165 382.5,164C 401.944,162.442 421.944,161.109 442.5,160C 441.833,159.333 441.167,158.667 440.5,158C 445.833,157.667 451.167,157.333 456.5,157C 455.833,156.333 455.167,155.667 454.5,155C 473.944,153.442 493.944,152.109 514.5,151C 513.833,150 513.167,149 512.5,148C 517.533,147.829 522.533,147.329 527.5,146.5C 527.291,145.914 526.957,145.414 526.5,145C 536.849,144.832 547.183,144.332 557.5,143.5C 557.291,142.914 556.957,142.414 556.5,142C 566.184,141.832 575.851,141.332 585.5,140.5C 585.291,139.914 584.957,139.414 584.5,139C 589.351,138.826 594.018,138.326 598.5,137.5 Z"/></g>
<g><path style="opacity:1" fill="#ba7efd" d="M 598.5,137.5 C 594.018,138.326 589.351,138.826 584.5,139C 584.957,139.414 585.291,139.914 585.5,140.5C 575.851,141.332 566.184,141.832 556.5,142C 556.957,142.414 557.291,142.914 557.5,143.5C 547.183,144.332 536.849,144.832 526.5,145C 526.957,145.414 527.291,145.914 527.5,146.5C 522.533,147.329 517.533,147.829 512.5,148C 513.167,149 513.833,150 514.5,151C 493.944,152.109 473.944,153.442 454.5,155C 455.167,155.667 455.833,156.333 456.5,157C 451.167,157.333 445.833,157.667 440.5,158C 441.167,158.667 441.833,159.333 442.5,160C 421.944,161.109 401.944,162.442 382.5,164C 383.167,165 383.833,166 384.5,167C 371.362,168.344 358.362,169.511 345.5,170.5C 342.615,170.806 339.948,170.473 337.5,169.5C 420.572,128.089 507.572,117.423 598.5,137.5 Z"/></g>
<g><path style="opacity:1" fill="#b373fb" d="M 647.5,152.5 C 660.708,157.107 673.375,162.774 685.5,169.5C 678.679,170.329 671.679,170.829 664.5,171C 664.957,171.414 665.291,171.914 665.5,172.5C 655.851,173.332 646.184,173.832 636.5,174C 637.167,175 637.833,176 638.5,177C 633.467,177.171 628.467,177.671 623.5,178.5C 623.709,179.086 624.043,179.586 624.5,180C 603.944,181.109 583.944,182.442 564.5,184C 565.357,184.689 566.023,185.522 566.5,186.5C 561.5,187.167 556.5,187.833 551.5,188.5C 551.709,189.086 552.043,189.586 552.5,190C 542.167,190.333 531.833,190.667 521.5,191C 521.957,191.414 522.291,191.914 522.5,192.5C 519.914,193.48 517.247,193.813 514.5,193.5C 508.102,192.033 501.768,190.366 495.5,188.5C 482.32,184.531 468.987,181.198 455.5,178.5C 464.843,178.244 474.176,177.744 483.5,177C 482.833,176 482.167,175 481.5,174C 491.5,173.667 501.5,173.333 511.5,173C 510.833,172.333 510.167,171.667 509.5,171C 525.152,170.011 540.819,169.345 556.5,169C 555.624,167.414 554.624,165.914 553.5,164.5C 563.167,163.833 572.833,163.167 582.5,162.5C 582.291,161.914 581.957,161.414 581.5,161C 586.533,160.829 591.533,160.329 596.5,159.5C 596.291,158.914 595.957,158.414 595.5,158C 605.849,157.832 616.183,157.332 626.5,156.5C 626.291,155.914 625.957,155.414 625.5,155C 633.058,154.847 640.391,154.014 647.5,152.5 Z"/></g>
<g><path style="opacity:1" fill="#b06dfa" d="M 685.5,169.5 C 696.018,174.374 706.018,180.041 715.5,186.5C 712.642,187.166 709.642,187.833 706.5,188.5C 706.709,189.086 707.043,189.586 707.5,190C 697.151,190.168 686.817,190.668 676.5,191.5C 676.709,192.086 677.043,192.586 677.5,193C 662.518,194.032 647.518,194.698 632.5,195C 643.515,195.168 654.515,195.668 665.5,196.5C 645.225,197.916 624.892,199.416 604.5,201C 605.167,202 605.833,203 606.5,204C 601.833,204.333 597.167,204.667 592.5,205C 592.957,205.414 593.291,205.914 593.5,206.5C 583.183,207.332 572.849,207.832 562.5,208C 565.194,210.552 564.527,211.719 560.5,211.5C 557.166,210.166 553.833,208.833 550.5,207.5C 538.736,202.245 526.736,197.578 514.5,193.5C 517.247,193.813 519.914,193.48 522.5,192.5C 522.291,191.914 521.957,191.414 521.5,191C 531.833,190.667 542.167,190.333 552.5,190C 552.043,189.586 551.709,189.086 551.5,188.5C 556.5,187.833 561.5,187.167 566.5,186.5C 566.023,185.522 565.357,184.689 564.5,184C 583.944,182.442 603.944,181.109 624.5,180C 624.043,179.586 623.709,179.086 623.5,178.5C 628.467,177.671 633.467,177.171 638.5,177C 637.833,176 637.167,175 636.5,174C 646.184,173.832 655.851,173.332 665.5,172.5C 665.291,171.914 664.957,171.414 664.5,171C 671.679,170.829 678.679,170.329 685.5,169.5 Z"/></g>
<g><path style="opacity:1" fill="#af6bfb" d="M 337.5,169.5 C 339.948,170.473 342.615,170.806 345.5,170.5C 382.431,169.683 419.098,172.35 455.5,178.5C 468.987,181.198 482.32,184.531 495.5,188.5C 431.998,188.832 368.664,188.499 305.5,187.5C 315.843,180.993 326.51,174.993 337.5,169.5 Z"/></g>
<g><path style="opacity:1" fill="#ac66fa" d="M 305.5,187.5 C 368.664,188.499 431.998,188.832 495.5,188.5C 501.768,190.366 508.102,192.033 514.5,193.5C 526.736,197.578 538.736,202.245 550.5,207.5C 459.665,207.833 368.998,207.499 278.5,206.5C 287.223,199.778 296.223,193.444 305.5,187.5 Z"/></g>
<g><path style="opacity:1" fill="#ac66f9" d="M 715.5,186.5 C 727.572,193.529 738.906,201.529 749.5,210.5C 739.992,211.167 730.325,211.833 720.5,212.5C 720.709,213.086 721.043,213.586 721.5,214C 711.151,214.168 700.817,214.668 690.5,215.5C 690.833,216.167 691.167,216.833 691.5,217.5C 687.167,218.167 682.833,218.833 678.5,219.5C 678.709,220.086 679.043,220.586 679.5,221C 669.151,221.168 658.817,221.668 648.5,222.5C 648.977,223.478 649.643,224.311 650.5,225C 645.167,225.333 639.833,225.667 634.5,226C 635.167,226.667 635.833,227.333 636.5,228C 626.5,228.333 616.5,228.667 606.5,229C 609.145,232.164 608.145,233.331 603.5,232.5C 600.848,230.418 597.848,229.085 594.5,228.5C 583.446,222.306 572.112,216.639 560.5,211.5C 564.527,211.719 565.194,210.552 562.5,208C 572.849,207.832 583.183,207.332 593.5,206.5C 593.291,205.914 592.957,205.414 592.5,205C 597.167,204.667 601.833,204.333 606.5,204C 605.833,203 605.167,202 604.5,201C 624.892,199.416 645.225,197.916 665.5,196.5C 654.515,195.668 643.515,195.168 632.5,195C 647.518,194.698 662.518,194.032 677.5,193C 677.043,192.586 676.709,192.086 676.5,191.5C 686.817,190.668 697.151,190.168 707.5,190C 707.043,189.586 706.709,189.086 706.5,188.5C 709.642,187.833 712.642,187.166 715.5,186.5 Z"/></g>
<g><path style="opacity:1" fill="#a85ff8" d="M 278.5,206.5 C 368.998,207.499 459.665,207.833 550.5,207.5C 553.833,208.833 557.166,210.166 560.5,211.5C 572.112,216.639 583.446,222.306 594.5,228.5C 480.665,228.833 366.999,228.5 253.5,227.5C 261.516,220.154 269.849,213.154 278.5,206.5 Z"/></g>
<g><path style="opacity:1" fill="#a760f7" d="M 749.5,210.5 C 756.578,215.702 763.245,221.369 769.5,227.5C 767.031,228.317 764.365,228.817 761.5,229C 762.167,230 762.833,231 763.5,232C 753.151,232.168 742.817,232.668 732.5,233.5C 732.709,234.086 733.043,234.586 733.5,235C 728.467,235.171 723.467,235.671 718.5,236.5C 718.833,237.167 719.167,237.833 719.5,238.5C 709.833,239.167 700.167,239.833 690.5,240.5C 690.977,241.478 691.643,242.311 692.5,243C 687.167,243.333 681.833,243.667 676.5,244C 677.167,244.667 677.833,245.333 678.5,246C 668.151,246.168 657.817,246.668 647.5,247.5C 647.833,248.167 648.167,248.833 648.5,249.5C 643.208,250.495 637.875,250.828 632.5,250.5C 632.158,249.662 631.492,249.328 630.5,249.5C 621.669,243.583 612.669,237.917 603.5,232.5C 608.145,233.331 609.145,232.164 606.5,229C 616.5,228.667 626.5,228.333 636.5,228C 635.833,227.333 635.167,226.667 634.5,226C 639.833,225.667 645.167,225.333 650.5,225C 649.643,224.311 648.977,223.478 648.5,222.5C 658.817,221.668 669.151,221.168 679.5,221C 679.043,220.586 678.709,220.086 678.5,219.5C 682.833,218.833 687.167,218.167 691.5,217.5C 691.167,216.833 690.833,216.167 690.5,215.5C 700.817,214.668 711.151,214.168 721.5,214C 721.043,213.586 720.709,213.086 720.5,212.5C 730.325,211.833 739.992,211.167 749.5,210.5 Z"/></g>
<g><path style="opacity:1" fill="#a45af7" d="M 253.5,227.5 C 366.999,228.5 480.665,228.833 594.5,228.5C 597.848,229.085 600.848,230.418 603.5,232.5C 612.669,237.917 621.669,243.583 630.5,249.5C 592.663,249.168 554.996,249.502 517.5,250.5C 515.511,246.904 513.511,243.237 511.5,239.5C 509.203,243.1 507.203,246.767 505.5,250.5C 414.5,249.833 323.5,249.167 232.5,248.5C 239.167,241.167 246.167,234.167 253.5,227.5 Z"/></g>
<g><path style="opacity:1" fill="#a45af6" d="M 769.5,227.5 C 776.833,234.167 783.833,241.167 790.5,248.5C 779.674,249.33 768.674,249.83 757.5,250C 757.957,250.414 758.291,250.914 758.5,251.5C 764.167,252.167 769.833,252.833 775.5,253.5C 761.379,256.056 746.379,257.556 730.5,258C 731.167,259 731.833,260 732.5,261C 718.167,262.667 703.833,264.333 689.5,266C 690.293,267.085 690.96,268.252 691.5,269.5C 680.849,270.666 670.182,270.832 659.5,270C 650.433,263.478 641.433,256.978 632.5,250.5C 637.875,250.828 643.208,250.495 648.5,249.5C 648.167,248.833 647.833,248.167 647.5,247.5C 657.817,246.668 668.151,246.168 678.5,246C 677.833,245.333 677.167,244.667 676.5,244C 681.833,243.667 687.167,243.333 692.5,243C 691.643,242.311 690.977,241.478 690.5,240.5C 700.167,239.833 709.833,239.167 719.5,238.5C 719.167,237.833 718.833,237.167 718.5,236.5C 723.467,235.671 728.467,235.171 733.5,235C 733.043,234.586 732.709,234.086 732.5,233.5C 742.817,232.668 753.151,232.168 763.5,232C 762.833,231 762.167,230 761.5,229C 764.365,228.817 767.031,228.317 769.5,227.5 Z"/></g>
<g><path style="opacity:1" fill="#fefefe" d="M 517.5,250.5 C 520.659,258.151 524.325,265.484 528.5,272.5C 532.659,282.151 537.325,291.484 542.5,300.5C 546.263,309.024 550.263,317.358 554.5,325.5C 558.325,334.484 562.659,343.151 567.5,351.5C 571.325,360.484 575.659,369.151 580.5,377.5C 584.105,385.875 588.105,394.042 592.5,402C 615.123,405.491 637.79,408.658 660.5,411.5C 697.139,417.139 733.806,422.639 770.5,428C 766.296,432.036 762.296,436.202 758.5,440.5C 746.473,452.194 734.473,463.861 722.5,475.5C 708.807,488.861 695.14,502.194 681.5,515.5C 668.506,528.327 655.339,540.994 642,553.5C 641.26,554.749 641.427,555.749 642.5,556.5C 643.974,570.68 646.308,584.68 649.5,598.5C 652.98,621.718 656.98,644.718 661.5,667.5C 664.668,688.343 668.001,709.176 671.5,730C 671.167,730.833 670.833,731.667 670.5,732.5C 617.658,704.245 564.658,676.245 511.5,648.5C 458.5,676.5 405.5,704.5 352.5,732.5C 352.167,731.667 351.833,730.833 351.5,730C 354.999,709.176 358.332,688.343 361.5,667.5C 366.02,644.718 370.02,621.718 373.5,598.5C 376.692,584.68 379.026,570.68 380.5,556.5C 381.573,555.749 381.74,554.749 381,553.5C 366.327,539.66 351.827,525.66 337.5,511.5C 324.527,498.861 311.527,486.194 298.5,473.5C 287.193,462.527 275.86,451.527 264.5,440.5C 260.704,436.202 256.704,432.036 252.5,428C 289.455,422.173 326.455,416.673 363.5,411.5C 386.197,408.256 408.863,404.756 431.5,401C 433.974,395.886 436.307,390.72 438.5,385.5C 444.675,374.484 450.341,363.151 455.5,351.5C 460.341,343.151 464.675,334.484 468.5,325.5C 472.737,317.358 476.737,309.024 480.5,300.5C 485.675,291.484 490.341,282.151 494.5,272.5C 498.675,265.484 502.341,258.151 505.5,250.5C 507.203,246.767 509.203,243.1 511.5,239.5C 513.511,243.237 515.511,246.904 517.5,250.5 Z"/></g>
<g><path style="opacity:1" fill="#a054f5" d="M 232.5,248.5 C 323.5,249.167 414.5,249.833 505.5,250.5C 502.341,258.151 498.675,265.484 494.5,272.5C 400.332,272.833 306.332,272.499 212.5,271.5C 218.449,263.217 225.116,255.55 232.5,248.5 Z"/></g>
<g><path style="opacity:1" fill="#a054f5" d="M 790.5,248.5 C 797.884,255.55 804.551,263.217 810.5,271.5C 802.677,272.329 794.677,272.829 786.5,273C 787.357,273.689 788.023,274.522 788.5,275.5C 778.183,276.332 767.849,276.832 757.5,277C 758.167,278 758.833,279 759.5,280C 754.467,280.171 749.467,280.671 744.5,281.5C 744.833,282.167 745.167,282.833 745.5,283.5C 735.833,284.167 726.167,284.833 716.5,285.5C 716.977,286.478 717.643,287.311 718.5,288C 713.167,288.333 707.833,288.667 702.5,289C 703.357,289.689 704.023,290.522 704.5,291.5C 699.147,292.211 693.814,292.878 688.5,293.5C 680.015,286.426 671.348,279.592 662.5,273C 617.835,272.5 573.168,272.333 528.5,272.5C 524.325,265.484 520.659,258.151 517.5,250.5C 554.996,249.502 592.663,249.168 630.5,249.5C 631.492,249.328 632.158,249.662 632.5,250.5C 641.433,256.978 650.433,263.478 659.5,270C 670.182,270.832 680.849,270.666 691.5,269.5C 690.96,268.252 690.293,267.085 689.5,266C 703.833,264.333 718.167,262.667 732.5,261C 731.833,260 731.167,259 730.5,258C 746.379,257.556 761.379,256.056 775.5,253.5C 769.833,252.833 764.167,252.167 758.5,251.5C 758.291,250.914 757.957,250.414 757.5,250C 768.674,249.83 779.674,249.33 790.5,248.5 Z"/></g>
<g><path style="opacity:1" fill="#9c4df4" d="M 212.5,271.5 C 306.332,272.499 400.332,272.833 494.5,272.5C 490.341,282.151 485.675,291.484 480.5,300.5C 384.167,300.5 287.833,300.5 191.5,300.5C 197.346,289.989 204.346,280.322 212.5,271.5 Z"/></g>
<g><path style="opacity:1" fill="#9b4df3" d="M 810.5,271.5 C 818.654,280.322 825.654,289.989 831.5,300.5C 822.143,300.168 812.81,300.502 803.5,301.5C 803.833,302.167 804.167,302.833 804.5,303.5C 794.183,304.332 783.849,304.832 773.5,305C 774.167,306 774.833,307 775.5,308C 770.467,308.171 765.467,308.671 760.5,309.5C 760.833,310.167 761.167,310.833 761.5,311.5C 756.533,312.329 751.533,312.829 746.5,313C 747.167,314 747.833,315 748.5,316C 738.816,316.168 729.149,316.668 719.5,317.5C 721.167,319.167 720.833,320.167 718.5,320.5C 710.578,314.916 703.245,308.583 696.5,301.5C 645.171,300.5 593.838,300.167 542.5,300.5C 537.325,291.484 532.659,282.151 528.5,272.5C 573.168,272.333 617.835,272.5 662.5,273C 671.348,279.592 680.015,286.426 688.5,293.5C 693.814,292.878 699.147,292.211 704.5,291.5C 704.023,290.522 703.357,289.689 702.5,289C 707.833,288.667 713.167,288.333 718.5,288C 717.643,287.311 716.977,286.478 716.5,285.5C 726.167,284.833 735.833,284.167 745.5,283.5C 745.167,282.833 744.833,282.167 744.5,281.5C 749.467,280.671 754.467,280.171 759.5,280C 758.833,279 758.167,278 757.5,277C 767.849,276.832 778.183,276.332 788.5,275.5C 788.023,274.522 787.357,273.689 786.5,273C 794.677,272.829 802.677,272.329 810.5,271.5 Z"/></g>
<g><path style="opacity:1" fill="#9847f2" d="M 191.5,300.5 C 287.833,300.5 384.167,300.5 480.5,300.5C 476.737,309.024 472.737,317.358 468.5,325.5C 371.329,325.166 274.329,324.166 177.5,322.5C 181.681,314.792 186.348,307.458 191.5,300.5 Z"/></g>
<g><path style="opacity:1" fill="#9847f2" d="M 542.5,300.5 C 593.838,300.167 645.171,300.5 696.5,301.5C 703.245,308.583 710.578,314.916 718.5,320.5C 720.833,320.167 721.167,319.167 719.5,317.5C 729.149,316.668 738.816,316.168 748.5,316C 747.833,315 747.167,314 746.5,313C 751.533,312.829 756.533,312.329 761.5,311.5C 761.167,310.833 760.833,310.167 760.5,309.5C 765.467,308.671 770.467,308.171 775.5,308C 774.833,307 774.167,306 773.5,305C 783.849,304.832 794.183,304.332 804.5,303.5C 804.167,302.833 803.833,302.167 803.5,301.5C 812.81,300.502 822.143,300.168 831.5,300.5C 836.652,307.458 841.319,314.792 845.5,322.5C 836.219,324.312 826.552,325.145 816.5,325C 817.357,325.689 818.023,326.522 818.5,327.5C 808.183,328.332 797.849,328.832 787.5,329C 788.293,330.085 788.96,331.252 789.5,332.5C 784.533,333.329 779.533,333.829 774.5,334C 775.916,336.835 777.583,339.502 779.5,342C 769.5,342.333 759.5,342.667 749.5,343C 750.357,343.689 751.023,344.522 751.5,345.5C 747.541,346.66 743.541,346.827 739.5,346C 733.171,338.671 726.504,331.671 719.5,325C 664.332,324.168 609.332,324.335 554.5,325.5C 550.263,317.358 546.263,309.024 542.5,300.5 Z"/></g>
<g><path style="opacity:1" fill="#9441f0" d="M 177.5,322.5 C 274.329,324.166 371.329,325.166 468.5,325.5C 464.675,334.484 460.341,343.151 455.5,351.5C 358.335,350.501 261.001,350.167 163.5,350.5C 167.132,340.57 171.798,331.237 177.5,322.5 Z"/></g>
<g><path style="opacity:1" fill="#9441f0" d="M 845.5,322.5 C 851.217,331.268 855.884,340.601 859.5,350.5C 850.161,350.334 840.827,350.5 831.5,351C 832.357,351.689 833.023,352.522 833.5,353.5C 828.5,354.167 823.5,354.833 818.5,355.5C 819.167,356.5 819.833,357.5 820.5,358.5C 810.183,359.332 799.849,359.832 789.5,360C 790.167,361 790.833,362 791.5,363C 786.467,363.171 781.467,363.671 776.5,364.5C 777.04,365.748 777.707,366.915 778.5,368C 773.833,368.333 769.167,368.667 764.5,369C 767.388,371.296 766.888,372.462 763,372.5C 761.564,372.441 760.231,372.108 759,371.5C 754.259,364.088 748.759,357.255 742.5,351C 683.999,350.168 625.665,350.335 567.5,351.5C 562.659,343.151 558.325,334.484 554.5,325.5C 609.332,324.335 664.332,324.168 719.5,325C 726.504,331.671 733.171,338.671 739.5,346C 743.541,346.827 747.541,346.66 751.5,345.5C 751.023,344.522 750.357,343.689 749.5,343C 759.5,342.667 769.5,342.333 779.5,342C 777.583,339.502 775.916,336.835 774.5,334C 779.533,333.829 784.533,333.329 789.5,332.5C 788.96,331.252 788.293,330.085 787.5,329C 797.849,328.832 808.183,328.332 818.5,327.5C 818.023,326.522 817.357,325.689 816.5,325C 826.552,325.145 836.219,324.312 845.5,322.5 Z"/></g>
<g><path style="opacity:1" fill="#8f3bee" d="M 859.5,350.5 C 860.401,351.29 861.068,352.29 861.5,353.5C 865.09,360.937 868.09,368.603 870.5,376.5C 863.011,377.329 855.344,377.829 847.5,378C 848.167,379 848.833,380 849.5,381C 844.167,381.333 838.833,381.667 833.5,382C 834.743,383.154 835.743,384.488 836.5,386C 826.151,386.168 815.817,386.668 805.5,387.5C 806.167,388.5 806.833,389.5 807.5,390.5C 802.5,391.167 797.5,391.833 792.5,392.5C 792.977,393.478 793.643,394.311 794.5,395C 789.833,395.333 785.167,395.667 780.5,396C 782.631,397.888 782.631,399.388 780.5,400.5C 774.249,393.525 768.582,386.025 763.5,378C 702.501,377.5 641.501,377.333 580.5,377.5C 575.659,369.151 571.325,360.484 567.5,351.5C 625.665,350.335 683.999,350.168 742.5,351C 748.759,357.255 754.259,364.088 759,371.5C 760.231,372.108 761.564,372.441 763,372.5C 766.888,372.462 767.388,371.296 764.5,369C 769.167,368.667 773.833,368.333 778.5,368C 777.707,366.915 777.04,365.748 776.5,364.5C 781.467,363.671 786.467,363.171 791.5,363C 790.833,362 790.167,361 789.5,360C 799.849,359.832 810.183,359.332 820.5,358.5C 819.833,357.5 819.167,356.5 818.5,355.5C 823.5,354.833 828.5,354.167 833.5,353.5C 833.023,352.522 832.357,351.689 831.5,351C 840.827,350.5 850.161,350.334 859.5,350.5 Z"/></g>
<g><path style="opacity:1" fill="#8f39ee" d="M 163.5,350.5 C 261.001,350.167 358.335,350.501 455.5,351.5C 450.341,363.151 444.675,374.484 438.5,385.5C 341.999,385.833 245.665,385.499 149.5,384.5C 152.713,373.859 156.713,363.526 161.5,353.5C 161.932,352.29 162.599,351.29 163.5,350.5 Z"/></g>
<g><path style="opacity:1" fill="#8b34ec" d="M 149.5,384.5 C 245.665,385.499 341.999,385.833 438.5,385.5C 436.307,390.72 433.974,395.886 431.5,401C 408.863,404.756 386.197,408.256 363.5,411.5C 289.5,410.833 215.5,410.167 141.5,409.5C 143.393,400.825 146.059,392.492 149.5,384.5 Z"/></g>
<g><path style="opacity:1" fill="#8c35ec" d="M 870.5,376.5 C 875.138,387.079 878.804,398.079 881.5,409.5C 871.145,409.168 860.812,409.501 850.5,410.5C 851.248,412.332 852.248,413.999 853.5,415.5C 843.183,416.332 832.849,416.832 822.5,417C 833.358,418.147 844.358,418.98 855.5,419.5C 845.183,420.332 834.849,420.832 824.5,421C 825.357,421.689 826.023,422.522 826.5,423.5C 816.833,424.167 807.167,424.833 797.5,425.5C 797.709,426.086 798.043,426.586 798.5,427C 803.5,427.333 808.5,427.667 813.5,428C 808.833,428.333 804.167,428.667 799.5,429C 800.627,430.753 801.627,432.587 802.5,434.5C 801.167,435.833 799.833,435.833 798.5,434.5C 794.43,426.275 789.764,418.441 784.5,411C 742.998,410.169 701.665,410.335 660.5,411.5C 637.79,408.658 615.123,405.491 592.5,402C 588.105,394.042 584.105,385.875 580.5,377.5C 641.501,377.333 702.501,377.5 763.5,378C 768.582,386.025 774.249,393.525 780.5,400.5C 782.631,399.388 782.631,397.888 780.5,396C 785.167,395.667 789.833,395.333 794.5,395C 793.643,394.311 792.977,393.478 792.5,392.5C 797.5,391.833 802.5,391.167 807.5,390.5C 806.833,389.5 806.167,388.5 805.5,387.5C 815.817,386.668 826.151,386.168 836.5,386C 835.743,384.488 834.743,383.154 833.5,382C 838.833,381.667 844.167,381.333 849.5,381C 848.833,380 848.167,379 847.5,378C 855.344,377.829 863.011,377.329 870.5,376.5 Z"/></g>
<g><path style="opacity:1" fill="#872fea" d="M 141.5,409.5 C 215.5,410.167 289.5,410.833 363.5,411.5C 326.455,416.673 289.455,422.173 252.5,428C 256.704,432.036 260.704,436.202 264.5,440.5C 220.997,440.832 177.663,440.499 134.5,439.5C 135.915,429.179 138.249,419.179 141.5,409.5 Z"/></g>
<g><path style="opacity:1" fill="#8830ea" d="M 881.5,409.5 C 884.57,419.55 886.904,429.883 888.5,440.5C 886.801,440.34 885.134,440.506 883.5,441C 884.743,442.154 885.743,443.488 886.5,445C 881.167,445.333 875.833,445.667 870.5,446C 871.872,447.242 872.872,448.742 873.5,450.5C 868.533,451.329 863.533,451.829 858.5,452C 859.293,453.085 859.96,454.252 860.5,455.5C 850.183,456.332 839.849,456.832 829.5,457C 830.872,458.242 831.872,459.742 832.5,461.5C 828.167,462.167 823.833,462.833 819.5,463.5C 820.04,464.748 820.707,465.915 821.5,467C 818.773,467.818 816.106,467.652 813.5,466.5C 809.805,457.61 805.472,449.11 800.5,441C 786.504,440.5 772.504,440.333 758.5,440.5C 762.296,436.202 766.296,432.036 770.5,428C 733.806,422.639 697.139,417.139 660.5,411.5C 701.665,410.335 742.998,410.169 784.5,411C 789.764,418.441 794.43,426.275 798.5,434.5C 799.833,435.833 801.167,435.833 802.5,434.5C 801.627,432.587 800.627,430.753 799.5,429C 804.167,428.667 808.833,428.333 813.5,428C 808.5,427.667 803.5,427.333 798.5,427C 798.043,426.586 797.709,426.086 797.5,425.5C 807.167,424.833 816.833,424.167 826.5,423.5C 826.023,422.522 825.357,421.689 824.5,421C 834.849,420.832 845.183,420.332 855.5,419.5C 844.358,418.98 833.358,418.147 822.5,417C 832.849,416.832 843.183,416.332 853.5,415.5C 852.248,413.999 851.248,412.332 850.5,410.5C 860.812,409.501 871.145,409.168 881.5,409.5 Z"/></g>
<g><path style="opacity:1" fill="#8328e8" d="M 134.5,439.5 C 177.663,440.499 220.997,440.832 264.5,440.5C 275.86,451.527 287.193,462.527 298.5,473.5C 242.167,473.5 185.833,473.5 129.5,473.5C 130.162,461.889 131.829,450.556 134.5,439.5 Z"/></g>
<g><path style="opacity:1" fill="#8228e8" d="M 758.5,440.5 C 772.504,440.333 786.504,440.5 800.5,441C 805.472,449.11 809.805,457.61 813.5,466.5C 816.106,467.652 818.773,467.818 821.5,467C 820.707,465.915 820.04,464.748 819.5,463.5C 823.833,462.833 828.167,462.167 832.5,461.5C 831.872,459.742 830.872,458.242 829.5,457C 839.849,456.832 850.183,456.332 860.5,455.5C 859.96,454.252 859.293,453.085 858.5,452C 863.533,451.829 868.533,451.329 873.5,450.5C 872.872,448.742 871.872,447.242 870.5,446C 875.833,445.667 881.167,445.333 886.5,445C 885.743,443.488 884.743,442.154 883.5,441C 885.134,440.506 886.801,440.34 888.5,440.5C 891.55,452.608 893.217,464.941 893.5,477.5C 892.391,478.29 891.058,478.79 889.5,479C 890.872,480.242 891.872,481.742 892.5,483.5C 887.533,484.329 882.533,484.829 877.5,485C 878.5,486.667 879.5,488.333 880.5,490C 870.167,490.333 859.833,490.667 849.5,491C 850.5,492.667 851.5,494.333 852.5,496C 847.833,496.333 843.167,496.667 838.5,497C 842.659,501.511 841.325,503.344 834.5,502.5C 831.911,502.473 829.411,502.139 827,501.5C 823.905,492.814 820.405,484.314 816.5,476C 785.168,475.5 753.835,475.333 722.5,475.5C 734.473,463.861 746.473,452.194 758.5,440.5 Z"/></g>
<g><path style="opacity:0.967" fill="#6000cd" d="M 150.5,351.5 C 153.684,353.414 157.351,354.08 161.5,353.5C 156.713,363.526 152.713,373.859 149.5,384.5C 146.059,392.492 143.393,400.825 141.5,409.5C 138.249,419.179 135.915,429.179 134.5,439.5C 131.829,450.556 130.162,461.889 129.5,473.5C 127.987,486.116 127.32,498.782 127.5,511.5C 127.225,525.978 128.225,540.311 130.5,554.5C 131.184,565.251 132.517,575.917 134.5,586.5C 130.498,586.036 126.831,586.702 123.5,588.5C 109.33,512.205 116.163,437.872 144,365.5C 145.942,360.623 148.108,355.956 150.5,351.5 Z"/></g>
<g><path style="opacity:0.968" fill="#6000cd" d="M 872.5,351.5 C 905.029,424.884 914.362,501.217 900.5,580.5C 896.936,581.423 893.269,581.756 889.5,581.5C 892.521,561.606 894.521,541.606 895.5,521.5C 895.817,506.633 895.15,491.966 893.5,477.5C 893.217,464.941 891.55,452.608 888.5,440.5C 886.904,429.883 884.57,419.55 881.5,409.5C 878.804,398.079 875.138,387.079 870.5,376.5C 868.09,368.603 865.09,360.937 861.5,353.5C 865.649,354.08 869.316,353.414 872.5,351.5 Z"/></g>
<g><path style="opacity:1" fill="#7e22e6" d="M 129.5,473.5 C 185.833,473.5 242.167,473.5 298.5,473.5C 311.527,486.194 324.527,498.861 337.5,511.5C 267.5,511.5 197.5,511.5 127.5,511.5C 127.32,498.782 127.987,486.116 129.5,473.5 Z"/></g>
<g><path style="opacity:1" fill="#7e22e5" d="M 722.5,475.5 C 753.835,475.333 785.168,475.5 816.5,476C 820.405,484.314 823.905,492.814 827,501.5C 829.411,502.139 831.911,502.473 834.5,502.5C 841.325,503.344 842.659,501.511 838.5,497C 843.167,496.667 847.833,496.333 852.5,496C 851.5,494.333 850.5,492.667 849.5,491C 859.833,490.667 870.167,490.333 880.5,490C 879.5,488.333 878.5,486.667 877.5,485C 882.533,484.829 887.533,484.329 892.5,483.5C 891.872,481.742 890.872,480.242 889.5,479C 891.058,478.79 892.391,478.29 893.5,477.5C 895.15,491.966 895.817,506.633 895.5,521.5C 894.532,523.607 894.198,525.941 894.5,528.5C 892.076,528.192 889.743,528.526 887.5,529.5C 888.167,530.5 888.833,531.5 889.5,532.5C 884.5,533.167 879.5,533.833 874.5,534.5C 874.709,535.086 875.043,535.586 875.5,536C 870.833,536.333 866.167,536.667 861.5,537C 866.689,538.121 872.022,538.955 877.5,539.5C 867.833,540.167 858.167,540.833 848.5,541.5C 850.043,544.086 851.709,546.586 853.5,549C 849.179,549.499 844.846,549.666 840.5,549.5C 837.496,538.149 834.496,526.816 831.5,515.5C 781.5,515.5 731.5,515.5 681.5,515.5C 695.14,502.194 708.807,488.861 722.5,475.5 Z"/></g>
<g><path style="opacity:1" fill="#7a1be3" d="M 127.5,511.5 C 197.5,511.5 267.5,511.5 337.5,511.5C 351.827,525.66 366.327,539.66 381,553.5C 381.74,554.749 381.573,555.749 380.5,556.5C 297.507,554.503 214.173,553.836 130.5,554.5C 128.225,540.311 127.225,525.978 127.5,511.5 Z"/></g>
<g><path style="opacity:1" fill="#791be2" d="M 681.5,515.5 C 731.5,515.5 781.5,515.5 831.5,515.5C 834.496,526.816 837.496,538.149 840.5,549.5C 844.846,549.666 849.179,549.499 853.5,549C 851.709,546.586 850.043,544.086 848.5,541.5C 858.167,540.833 867.833,540.167 877.5,539.5C 872.022,538.955 866.689,538.121 861.5,537C 866.167,536.667 870.833,536.333 875.5,536C 875.043,535.586 874.709,535.086 874.5,534.5C 879.5,533.833 884.5,533.167 889.5,532.5C 888.833,531.5 888.167,530.5 887.5,529.5C 889.743,528.526 892.076,528.192 894.5,528.5C 894.198,525.941 894.532,523.607 895.5,521.5C 894.521,541.606 892.521,561.606 889.5,581.5C 888.393,585.142 887.393,588.809 886.5,592.5C 873.816,592.832 861.149,592.499 848.5,591.5C 846.274,579.147 843.941,566.813 841.5,554.5C 774.825,553.837 708.492,554.503 642.5,556.5C 641.427,555.749 641.26,554.749 642,553.5C 655.339,540.994 668.506,528.327 681.5,515.5 Z"/></g>
<g><path style="opacity:1" fill="#7414e0" d="M 130.5,554.5 C 214.173,553.836 297.507,554.503 380.5,556.5C 379.026,570.68 376.692,584.68 373.5,598.5C 294.833,598.5 216.167,598.5 137.5,598.5C 136.688,594.552 135.688,590.552 134.5,586.5C 132.517,575.917 131.184,565.251 130.5,554.5 Z"/></g>
<g><path style="opacity:1" fill="#7415df" d="M 886.5,592.5 C 879.597,626.622 867.931,658.955 851.5,689.5C 852.099,681.514 852.432,673.514 852.5,665.5C 853.102,643.007 851.769,620.674 848.5,598.5C 782.167,598.5 715.833,598.5 649.5,598.5C 646.308,584.68 643.974,570.68 642.5,556.5C 708.492,554.503 774.825,553.837 841.5,554.5C 843.941,566.813 846.274,579.147 848.5,591.5C 861.149,592.499 873.816,592.832 886.5,592.5 Z"/></g>
<g><path style="opacity:1" fill="#6f0edc" d="M 137.5,598.5 C 216.167,598.5 294.833,598.5 373.5,598.5C 370.02,621.718 366.02,644.718 361.5,667.5C 294.508,665.503 227.175,664.837 159.5,665.5C 150.164,643.824 142.83,621.491 137.5,598.5 Z"/></g>
<g><path style="opacity:1" fill="#6f0edc" d="M 649.5,598.5 C 715.833,598.5 782.167,598.5 848.5,598.5C 851.769,620.674 853.102,643.007 852.5,665.5C 788.491,664.837 724.825,665.504 661.5,667.5C 656.98,644.718 652.98,621.718 649.5,598.5 Z"/></g>
<g><path style="opacity:0.969" fill="#5a00c0" d="M 134.5,586.5 C 135.688,590.552 136.688,594.552 137.5,598.5C 142.83,621.491 150.164,643.824 159.5,665.5C 173.814,698.423 192.481,728.423 215.5,755.5C 226.153,768.485 237.487,780.819 249.5,792.5C 244.637,792.181 239.97,792.514 235.5,793.5C 177.143,737.155 139.81,668.821 123.5,588.5C 126.831,586.702 130.498,586.036 134.5,586.5 Z"/></g>
<g><path style="opacity:0.971" fill="#5a00c0" d="M 900.5,580.5 C 885.439,663.639 848.106,734.639 788.5,793.5C 783.695,792.513 778.695,792.179 773.5,792.5C 785.57,780.767 796.903,768.433 807.5,755.5C 824.905,735.409 839.572,713.409 851.5,689.5C 867.931,658.955 879.597,626.622 886.5,592.5C 887.393,588.809 888.393,585.142 889.5,581.5C 893.269,581.756 896.936,581.423 900.5,580.5 Z"/></g>
<g><path style="opacity:1" fill="#6907d6" d="M 661.5,667.5 C 724.825,665.504 788.491,664.837 852.5,665.5C 852.432,673.514 852.099,681.514 851.5,689.5C 839.572,713.409 824.905,735.409 807.5,755.5C 610.167,755.5 412.833,755.5 215.5,755.5C 192.481,728.423 173.814,698.423 159.5,665.5C 227.175,664.837 294.508,665.503 361.5,667.5C 358.332,688.343 354.999,709.176 351.5,730C 351.833,730.833 352.167,731.667 352.5,732.5C 405.5,704.5 458.5,676.5 511.5,648.5C 564.658,676.245 617.658,704.245 670.5,732.5C 670.833,731.667 671.167,730.833 671.5,730C 668.001,709.176 664.668,688.343 661.5,667.5 Z"/></g>
<g><path style="opacity:1" fill="#6503d2" d="M 215.5,755.5 C 412.833,755.5 610.167,755.5 807.5,755.5C 796.903,768.433 785.57,780.767 773.5,792.5C 745.16,818.852 713.494,840.518 678.5,857.5C 677.893,857.376 677.56,857.043 677.5,856.5C 682.671,851.159 686.004,844.825 687.5,837.5C 688.648,833.019 688.815,828.352 688,823.5C 686.688,818.884 684.855,814.551 682.5,810.5C 669.58,795.183 653.58,784.016 634.5,777C 628.5,775 622.5,773 616.5,771C 546.5,770.333 476.5,770.333 406.5,771C 385.378,776.727 366.378,786.394 349.5,800C 346.137,803.252 343.137,806.752 340.5,810.5C 338.145,814.551 336.312,818.884 335,823.5C 334.185,828.352 334.352,833.019 335.5,837.5C 336.996,844.825 340.329,851.159 345.5,856.5C 345.44,857.043 345.107,857.376 344.5,857.5C 309.796,840.106 278.13,818.439 249.5,792.5C 237.487,780.819 226.153,768.485 215.5,755.5 Z"/></g>
<g><path style="opacity:1" fill="#6709d1" d="M 682.5,810.5 C 568.5,810.5 454.5,810.5 340.5,810.5C 343.137,806.752 346.137,803.252 349.5,800C 366.378,786.394 385.378,776.727 406.5,771C 476.5,770.333 546.5,770.333 616.5,771C 622.5,773 628.5,775 634.5,777C 653.58,784.016 669.58,795.183 682.5,810.5 Z"/></g>
<g><path style="opacity:1" fill="#5b05bc" d="M 249.5,792.5 C 278.13,818.439 309.796,840.106 344.5,857.5C 345.107,857.376 345.44,857.043 345.5,856.5C 456.167,855.167 566.833,855.167 677.5,856.5C 677.56,857.043 677.893,857.376 678.5,857.5C 713.494,840.518 745.16,818.852 773.5,792.5C 778.695,792.179 783.695,792.513 788.5,793.5C 735.361,845.249 672.361,879.582 599.5,896.5C 619.323,891.277 638.323,883.777 656.5,874C 662.736,870.433 668.403,866.1 673.5,861C 673,860.833 672.5,860.667 672,860.5C 659.298,865.938 646.465,870.938 633.5,875.5C 552.167,875.5 470.833,875.5 389.5,875.5C 375.525,870.833 361.858,865.666 348.5,860C 363.482,872.998 380.482,882.664 399.5,889C 410.834,892.498 422.168,895.664 433.5,898.5C 356.558,883.372 290.558,848.372 235.5,793.5C 239.97,792.514 244.637,792.181 249.5,792.5 Z"/></g>
<g><path style="opacity:1" fill="#6209c4" d="M 687.5,837.5 C 686.004,844.825 682.671,851.159 677.5,856.5C 566.833,855.167 456.167,855.167 345.5,856.5C 340.329,851.159 336.996,844.825 335.5,837.5C 452.833,836.167 570.167,836.167 687.5,837.5 Z"/></g>
<g><path style="opacity:1" fill="#650acb" d="M 340.5,810.5 C 454.5,810.5 568.5,810.5 682.5,810.5C 684.855,814.551 686.688,818.884 688,823.5C 688.815,828.352 688.648,833.019 687.5,837.5C 570.167,836.167 452.833,836.167 335.5,837.5C 334.352,833.019 334.185,828.352 335,823.5C 336.312,818.884 338.145,814.551 340.5,810.5 Z"/></g>
<g><path style="opacity:1" fill="#4d03a0" d="M 389.5,875.5 C 470.821,902.166 552.155,902.166 633.5,875.5C 646.465,870.938 659.298,865.938 672,860.5C 672.5,860.667 673,860.833 673.5,861C 668.403,866.1 662.736,870.433 656.5,874C 638.323,883.777 619.323,891.277 599.5,896.5C 555.413,906.717 510.747,909.217 465.5,904C 454.57,902.901 443.903,901.068 433.5,898.5C 422.168,895.664 410.834,892.498 399.5,889C 380.482,882.664 363.482,872.998 348.5,860C 361.858,865.666 375.525,870.833 389.5,875.5 Z"/></g>
<g><path style="opacity:1" fill="#5805b5" d="M 389.5,875.5 C 470.833,875.5 552.167,875.5 633.5,875.5C 552.155,902.166 470.821,902.166 389.5,875.5 Z"/></g>
</svg>

After

Width:  |  Height:  |  Size: 34 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/test-1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
hr_biometric_attendance/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
hr_biometric_attendance/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
hr_biometric_attendance/static/description/assets/icons/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
hr_biometric_attendance/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
hr_biometric_attendance/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
hr_biometric_attendance/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
hr_biometric_attendance/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
hr_biometric_attendance/static/description/assets/modules/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/17.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/19.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/20.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/21.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

BIN
hr_biometric_attendance/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 670 KiB

BIN
hr_biometric_attendance/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
hr_biometric_attendance/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

1274
hr_biometric_attendance/static/description/index.html

File diff suppressed because it is too large

62
hr_biometric_attendance/static/src/js/stopwatch.js

@ -0,0 +1,62 @@
/** @odoo-module **/
import { registry } from '@web/core/registry';
import { parseFloatTime } from '@web/views/fields/parsers';
import { useInputField } from '@web/views/fields/input_field_hook';
import { Component, useState, onMounted } from '@odoo/owl';
// Function to format minutes into HH:MM:SS format
function formatMinutes(value) {
if (value === false) {
return "";
}
const isNegative = value < 0;
if (isNegative) {
value = Math.abs(value);
}
let hours = Math.floor(value / 60);
let minutes = Math.floor(value % 60);
let seconds = Math.floor((value % 1) * 60);
seconds = `${seconds}`.padStart(2, "0");
minutes = `${minutes}`.padStart(2, "0");
return `${isNegative ? "-" : ""}${hours}:${minutes}:${seconds}`;
}
export class StopWatch extends Component {
static template = "StopwatchTemplate";
setup() {
this.state = useState({
stopwatch: 0,
livecapture: this.props.record.data.is_live_capture
});
useInputField({
getValue: () => this.durationFormatted,
refName: "numpadDecimal",
parse: (v) => parseFloatTime(v),
});
onMounted(async () => {
if (this.state.livecapture) {
const datetimeObj = new Date(this.props.record.data.live_capture_start_time);
const now = new Date();
const timeDiff = now - datetimeObj;
this.state.stopwatch = timeDiff / 1000 / 60;
this._runTimer();
}
});
}
get durationFormatted() {
return formatMinutes(this.state.stopwatch);
}
_runTimer() {
if (!this.state.livecapture) {
clearTimeout(this.timer);
return;
}
this.timer = setTimeout(async () => {
this.state.stopwatch += 1 / 60;
this._runTimer();
}, 1000);
}
}
registry.category("fields").add("stopwatch", StopWatch);
registry.category("formatters").add("stopwatch", formatMinutes);

10
hr_biometric_attendance/static/src/xml/stopwatch_view.xml

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Template of the Stopwatch widget -->
<templates id="template" xml:space="preserve">
<t t-name="StopwatchTemplate" owl="1">
<span t-if="props.readonly" t-esc="durationFormatted" class="disable"/>
<input t-else="" t-att-id="props.id" t-ref="numpadDecimal" t-model="state.stopwatch"
t-att-placeholder="props.placeholder" inputmode="numeric"
class="o_input timer" readonly="1" />
</t>
</templates>

16
hr_biometric_attendance/views/biometric_device_attendance_menus.xml

@ -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_user"/>
</odoo>

88
hr_biometric_attendance/views/biometric_device_details_views.xml

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!--Biometric device configuration tree view-->
<record id="biometric_device_details_view_tree" model="ir.ui.view">
<field name="name">biometric.device.details.view.tree</field>
<field name="model">biometric.device.details</field>
<field name="arch" type="xml">
<tree>
<field name="name"/>
<field name="device_ip"/>
<field name="port_number"/>
</tree>
</field>
</record>
<!--Biometric device configuration tree 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_download_attendance"
string="Download Data"
type="object" class="oe_highlight" attrs="{'invisible':[('is_live_capture','=',True)]}"/>
<button name="action_clear_attendance" string="Clear Data"
type="object" class="oe_highlight"
attrs="{'invisible':[('is_live_capture','=',True)]}"/>
<button name="action_restart_device" string="Restart"
type="object" class="oe_highlight"
confirm="Are you sure you want Restart the Biometric Device?"/>
<button name="action_live_capture" string="Live Capture"
type="object" class="oe_highlight" attrs="{'invisible':[('is_live_capture','=',True)]}"/>
<button name="action_stop_live_capture" string=" Stop Live Capture"
type="object" class="btn btn-secondary" attrs="{'invisible':[('is_live_capture','=',False)]}"/>
<button name="action_set_timezone" string=" Set Time"
type="object" class="oe_highlight"/>
<button name="%(hr_biometric_attendance.action_view_zk_user_management)d" string="User Management"
type="action" class="oe_highlight"/>
</header>
<sheet>
<div>
<label for="is_live_capture" name="is_live_capture" string="Live Capture"/>
<field name="is_live_capture" widget="boolean_toggle" string="Live Capture"/>
<field name="stopwatch_time" widget="stopwatch" readonly="True"
style="font-size: 15px; font-weight: 600;font-family: monospace;"
attrs="{'invisible':[('is_live_capture','=',False)]}"/>
<field name="live_capture_start_time" invisible="1"/>
</div>
<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>
<notebook>
<page string="Device Information">
<group>
<field name="device_name"/>
<field name="device_firmware"/>
<field name="device_serial_no"/>
<field name="device_platform"/>
<field name="device_mac"/>
</group>
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" groups="base.group_user"/>
<field name="activity_ids"/>
<field name="message_ids"/>
</div>
</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">tree,form</field>
</record>
</odoo>

25
hr_biometric_attendance/views/daily_attendance_views.xml

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Daily attendance tree view-->
<record id="daily_attendance_view_tree" model="ir.ui.view">
<field name="name">daily.attendance.view.tree</field>
<field name="model">daily.attendance</field>
<field name="arch" type="xml">
<tree 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"/>
</tree>
</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">tree</field>
<field name="context">{}</field>
</record>
</odoo>

25
hr_biometric_attendance/views/hr_employee_views.xml

@ -0,0 +1,25 @@
<?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="//page[@name='hr_settings']//field[@name='user_id']"
position="after">
<field name="device_id_num" readonly="1"/>
<field name="device_id"/>
<field name="fingerprint_ids" attrs="{'invisible':[('device_id','=',False)]}">
<tree editable="bottom" create="false">
<field name="employee_id" column_invisible="1"/>
<field name="filename" column_invisible="1"/>
<field name="finger_id" create="false" edit="false"/>
<field name="finger_template" create="false" widget="binary" filename="filename"/>
</tree>
</field>
</xpath>
</field>
</record>
</odoo>

52
hr_biometric_attendance/views/res_config_settings_views.xml

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Adding fields in res.config.settings-->
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">
res.config.settings.view.form.inherit.hr.biometric.attendance
</field>
<field name="model">res.config.settings</field>
<field name="inherit_id"
ref="hr_attendance.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='overtime_settings']" position="after">
<h2>Biometric Device</h2>
<div class="row mt16 o_settings_container" name="schedule_downloads">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane"
title="Activate the count of employees' extra hours.">
<field name="schedule_attendance_downloads"/>
</div>
<div class="o_setting_right_pane">
<label for="hr_attendance_overtime" string="Schedule Downloads"
class="o_form_label">Schedule Downloads
</label>
<div class="text-muted">
If Schedule Download Enabled, You will get all
the attendance and details from the Biometric
device into your Odoo
</div>
<div class="mt16" attrs="{'invisible': [('schedule_attendance_downloads', '=', False)],
'required': [('schedule_attendance_downloads', '=', True)]}">
<div class="mt16 row"
title="Count of extra hours is considered from this date. Potential extra hours prior to this date are not considered.">
<label for="schedule_time_interval"
string="Time Interval"
class="o_light_label col-lg-4"/>
<field name="schedule_time_interval"
class="col-lg-2 w-30 "
style="width:90px;"
attrs="{'required':[('schedule_attendance_downloads','=',True)]}"/>
<field name="schedule_time_period"
class="col-lg-2 w-30"
style="width:190px;"
attrs="{'required':[('schedule_attendance_downloads','=',True)]}"/>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

23
hr_biometric_attendance/wizards/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from . import employee_biometric
from . import user_management

66
hr_biometric_attendance/wizards/employee_biometric.py

@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, fields, models
class EmployeeBiometric(models.TransientModel):
"""Transient model for Biometric device options in Employee"""
_name = 'employee.biometric'
_description = 'Employee Biometric Wizard'
handle_create = fields.Selection(
[('create_user', 'Create User')],
'Handle Data', help='User Management', )
handle_update_delete = fields.Selection(
[('update_user', 'Update User'), ('delete_user', 'Delete User')],
'Handle Data', help='User Management', )
employee_id = fields.Many2one(
'hr.employee', string='Employee', help='Select the Employee')
is_biometric_user = fields.Boolean('Is Already User?',
help='Checking if already a user?',
compute='_compute_is_biometric_user')
biometric_device_id = fields.Many2one('biometric.device.details',
string='Biometric Device',
help='Choose Biometric Device')
@api.depends('employee_id')
def _compute_is_biometric_user(self):
"""Compute if it is already a biometric user or not"""
for record in self:
if record.employee_id.device_id:
record.is_biometric_user = True
else:
record.is_biometric_user = False
def action_confirm_biometric_management(self):
"""Go to the desired functions in biometric.device.details"""
if self.is_biometric_user:
if self.handle_update_delete == 'update_user':
self.employee_id.device_id.update_user(
employee_id=self.employee_id.id)
else:
self.employee_id.device_id.delete_user(
employee_id=self.employee_id.id,
delete_user_selection='device_only')
else:
self.employee_id.device_id = self.biometric_device_id.id
self.biometric_device_id.set_user(employee_id=self.employee_id.id)

40
hr_biometric_attendance/wizards/employee_biometric_views.xml

@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- Action for Employee Biometric Wizard-->
<record id="action_view_employee_biometric" model="ir.actions.act_window">
<field name="name">Biometric Management</field>
<field name="res_model">employee.biometric</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- Define a form view for the Employee Biometric Wizard model -->
<record id="employee_biometric_view_form" model="ir.ui.view">
<field name="name">employee.biometric.view.form</field>
<field name="model">employee.biometric</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="handle_create"
attrs="{'invisible':[('is_biometric_user','=',True)],'required':[('is_biometric_user','=',False)]}"
/>
<field name="biometric_device_id"
attrs="{'invisible':[('handle_create', '!=', 'create_user')]}"/>
<field name="handle_update_delete"
attrs="{'invisible':[('is_biometric_user','=', False)],'required':[('is_biometric_user','=',True)]}"
/>
<field name="employee_id" invisible="1"/>
<field name="is_biometric_user" invisible="1"/>
</group>
</group>
<footer>
<button class="btn btn-primary" string="Confirm"
name="action_confirm_biometric_management"
type="object"/>
<button class="btn btn-secondary" string="DISCARD"
special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>

87
hr_biometric_attendance/wizards/user_management.py

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Cybrosys Techno Solutions (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, fields, models, _
class ZkUserManagement(models.TransientModel):
"""Wizard for managing Employee data In Biometric Device """
_name = 'zk.user.management'
_description = 'ZK User Management Wizard'
manage_users = fields.Selection(
[('get_users', 'Get all Users'), ('create_user', 'Create User'),
('update_user', 'Update User'),
('delete_user', 'Delete User')],
'Manage Users', help='User Management', required=True)
employee_ids = fields.Many2many('hr.employee', string='Employees',
compute='_compute_employee_ids')
employee_id = fields.Many2one(
'hr.employee', string='Employee', help='Select the Employee',
domain="[('id', 'in', employee_ids)]")
delete_user_selection = fields.Selection(
[('device_only', 'From Device Only'),
('both_device', 'From Both Device and Odoo')], string='Delete From',
default='device_only', help='Choose the delete option')
@api.depends('manage_users')
def _compute_employee_ids(self):
"""Compute Employees By the Selected Option"""
for record in self:
if record.manage_users == 'create_user':
record.employee_ids = self.env['hr.employee'].search(
[('device_id', '!=',
int(self.env.context.get('active_id')))]).ids
elif record.manage_users in ['delete_user', 'update_user']:
record.employee_ids = self.env['hr.employee'].search(
[('device_id', '=',
int(self.env.context.get('active_id')))]).ids
else:
record.employee_ids = False
def action_confirm_user_management(self):
"""Function to works according to the selected option"""
if self.manage_users:
if self.manage_users == 'get_users':
self.env['biometric.device.details'].browse(
int(self.env.context.get('active_id'))).get_all_users()
return {
'name': _("ZK Users"),
'type': 'ir.actions.act_window',
'res_model': 'hr.employee',
'context': {'create': False},
'view_mode': 'tree,form',
'domain': [('device_id', '=',
int(self.env.context.get('active_id')))]
}
elif self.manage_users == 'create_user':
self.env['biometric.device.details'].browse(
int(self.env.context.get('active_id'))).set_user(
employee_id=self.employee_id.id)
elif self.manage_users == 'update_user':
self.env['biometric.device.details'].browse(
int(self.env.context.get('active_id'))).update_user(
employee_id=self.employee_id.id)
else:
self.env['biometric.device.details'].browse(
int(self.env.context.get('active_id'))).delete_user(
employee_id=self.employee_id.id,
delete_user_selection=self.delete_user_selection)

41
hr_biometric_attendance/wizards/user_management_views.xml

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<!-- Define a action for the User management wizard model -->
<record id="action_view_zk_user_management" model="ir.actions.act_window">
<field name="name">User Management</field>
<field name="res_model">zk.user.management</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!-- Define a form view for the User management wizard model -->
<record id="zk_user_management_view_form" model="ir.ui.view">
<field name="name">zk.user.management.view.form</field>
<field name="model">zk.user.management</field>
<field name="arch" type="xml">
<form>
<group>
<group>
<field name="manage_users"/>
<field name="employee_ids" invisible="1"/>
<field name="employee_id"
attrs="{'invisible':[('manage_users', '!=', 'create_user')],'required':[('manage_users','==','create_user')]}"
/>
<field name="employee_id"
attrs="{'invisible':[('manage_users', 'not in', ['update_user','delete_user'])],'required':[('manage_users', 'in', ['update_user','delete_user'])]}"
options="{'no_create': True}"/>
<field name="delete_user_selection" widget="radio"
attrs="{'invisible':[('manage_users', '!=', 'delete_user')]}"
confirm="Are you sure to delete the user?"/>
</group>
</group>
<footer>
<button class="btn btn-primary" string="Confirm"
name="action_confirm_user_management"
type="object"/>
<button class="btn btn-secondary" string="DISCARD"
special="cancel"/>
</footer>
</form>
</field>
</record>
</odoo>
Loading…
Cancel
Save