diff --git a/export_stockinfo_xls/README.rst b/export_stockinfo_xls/README.rst new file mode 100644 index 000000000..aca5f79c3 --- /dev/null +++ b/export_stockinfo_xls/README.rst @@ -0,0 +1,41 @@ +Export Product Stock in Excel v15 +================================= +This module helps you to take current stock report for all products in each warehouse. + +Configuration +============= +* No additional configurations needed + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +* Developers: Cybrosys Techno Solutions odoo@cybrosys.com + Version 15: Midilaj V K @cybrosys + + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ + + diff --git a/export_stockinfo_xls/__init__.py b/export_stockinfo_xls/__init__.py new file mode 100644 index 000000000..750dd7172 --- /dev/null +++ b/export_stockinfo_xls/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import models +from . import controllers diff --git a/export_stockinfo_xls/__manifest__.py b/export_stockinfo_xls/__manifest__.py new file mode 100644 index 000000000..c67d1a750 --- /dev/null +++ b/export_stockinfo_xls/__manifest__.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2021-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': 'Export Product Stock in Excel', + 'version': '15.0.1.0.0', + 'live_test_url': 'https://www.youtube.com/watch?v=9ae4GkApHQM', + 'summary': "Current Stock Report for all Products in each Warehouse", + 'description': "Current Stock Report for all Products in each Warehouse, Odoo 13,Odoo13", + 'category': 'Warehouse', + 'author': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': [ + 'base', + 'stock', + 'sale', + 'purchase', + ], + 'data': [ + 'views/wizard_view.xml', + 'security/ir.model.access.csv', + ], + 'images': ['static/description/banner.png'], + 'assets': { + 'web.assets_backend': [ + 'export_stockinfo_xls/static/src/js/action_manager.js', + ], + }, + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'auto_install': False, +} diff --git a/export_stockinfo_xls/controllers/__init__.py b/export_stockinfo_xls/controllers/__init__.py new file mode 100644 index 000000000..b13d77771 --- /dev/null +++ b/export_stockinfo_xls/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import main diff --git a/export_stockinfo_xls/controllers/main.py b/export_stockinfo_xls/controllers/main.py new file mode 100644 index 000000000..04b46d71f --- /dev/null +++ b/export_stockinfo_xls/controllers/main.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +import json +from odoo import http +from odoo.http import content_disposition, request +from odoo.addons.web.controllers.main import _serialize_exception +from odoo.tools import html_escape + + +class XLSXReportController(http.Controller): + + @http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], csrf=False) + def get_report_xlsx(self, model, options, output_format, report_name, **kw): + uid = request.session.uid + report_obj = request.env[model].with_user(uid) + options = json.loads(options) + token = 'dummy-because-api-expects-one' + try: + if output_format == 'xlsx': + response = request.make_response( + None, + headers=[ + ('Content-Type', 'application/vnd.ms-excel'), + ('Content-Disposition', content_disposition(report_name + '.xlsx')) + ] + ) + report_obj.get_xlsx_report(options, response) + response.set_cookie('fileToken', token) + return response + except Exception as e: + se = _serialize_exception(e) + error = { + 'code': 200, + 'message': 'Odoo Server Error', + 'data': se + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/export_stockinfo_xls/doc/RELEASE_NOTES.md b/export_stockinfo_xls/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..44a257dc0 --- /dev/null +++ b/export_stockinfo_xls/doc/RELEASE_NOTES.md @@ -0,0 +1,11 @@ +## Module + +#### 04.10.2021 +#### Version 15.0.1.0.0 +#### ADD +Initial Commit Export Product Stock in Excel + + + + + diff --git a/export_stockinfo_xls/models/__init__.py b/export_stockinfo_xls/models/__init__.py new file mode 100644 index 000000000..321ab5c22 --- /dev/null +++ b/export_stockinfo_xls/models/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import res_partner +from . import wizard diff --git a/export_stockinfo_xls/models/res_partner.py b/export_stockinfo_xls/models/res_partner.py new file mode 100644 index 000000000..c2e9f2ad6 --- /dev/null +++ b/export_stockinfo_xls/models/res_partner.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import models, fields + + +class Partner(models.Model): + _inherit = 'res.partner' + + supplier_id = fields.Many2many('wizard.stock.history', 'supp_wiz_rel', 'wiz', 'supp', invisible=True) + + +class Category(models.Model): + _inherit = 'product.category' + + obj = fields.Many2many('wizard.stock.history', 'categ_wiz_rel', 'wiz', 'categ', invisible=True) + + +class Warehouse(models.Model): + _inherit = 'stock.warehouse' + + obj = fields.Many2many('wizard.stock.history', 'wh_wiz_rel', 'wiz', 'wh', invisible=True) diff --git a/export_stockinfo_xls/models/wizard.py b/export_stockinfo_xls/models/wizard.py new file mode 100644 index 000000000..64a900da2 --- /dev/null +++ b/export_stockinfo_xls/models/wizard.py @@ -0,0 +1,249 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2020-TODAY Cybrosys Technologies() +# Author: Midilaj () +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import time +from datetime import date, datetime +import pytz +import json +import datetime +import io +from odoo import api, fields, models, _ +from odoo.tools import date_utils +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class StockReport(models.TransientModel): + _name = "wizard.stock.history" + _description = "Current Stock History" + + warehouse = fields.Many2many('stock.warehouse', 'wh_wiz_rel', 'wh', 'wiz', string='Warehouse', required=True) + category = fields.Many2many('product.category', 'categ_wiz_rel', 'categ', 'wiz', string='Warehouse') + + def export_xls(self): + data = { + 'ids': self.ids, + 'model': self._name, + 'warehouse': self.warehouse.ids, + 'category': self.category.ids, + + } + return { + 'type': 'ir.actions.report', + 'data': {'model': 'wizard.stock.history', + 'options': json.dumps(data, default=date_utils.json_default), + 'output_format': 'xlsx', + 'report_name': 'Current Stock History', + }, + 'report_type': 'stock_xlsx' + } + + def get_warehouse(self, data): + wh = data.warehouse.mapped('id') + obj = self.env['stock.warehouse'].search([('id', 'in', wh)]) + l1 = [] + l2 = [] + for j in obj: + l1.append(j.name) + l2.append(j.id) + return l1, l2 + + def get_lines(self, data, warehouse): + lines = [] + categ_id = data.mapped('id') + if categ_id: + categ_products = self.env['product.product'].search([('categ_id', 'in', categ_id)]) + + else: + categ_products = self.env['product.product'].search([]) + product_ids = tuple([pro_id.id for pro_id in categ_products]) + sale_query = """ + SELECT sum(s_o_l.product_uom_qty) AS product_uom_qty, s_o_l.product_id FROM sale_order_line AS s_o_l + JOIN sale_order AS s_o ON s_o_l.order_id = s_o.id + WHERE s_o.state IN ('sale','done') + AND s_o.warehouse_id = %s + AND s_o_l.product_id in %s group by s_o_l.product_id""" + purchase_query = """ + SELECT sum(p_o_l.product_qty) AS product_qty, p_o_l.product_id FROM purchase_order_line AS p_o_l + JOIN purchase_order AS p_o ON p_o_l.order_id = p_o.id + INNER JOIN stock_picking_type AS s_p_t ON p_o.picking_type_id = s_p_t.id + WHERE p_o.state IN ('purchase','done') + AND s_p_t.warehouse_id = %s AND p_o_l.product_id in %s group by p_o_l.product_id""" + params = warehouse, product_ids if product_ids else (0, 0) + self._cr.execute(sale_query, params) + sol_query_obj = self._cr.dictfetchall() + self._cr.execute(purchase_query, params) + pol_query_obj = self._cr.dictfetchall() + for obj in categ_products: + sale_value = 0 + purchase_value = 0 + for sol_product in sol_query_obj: + if sol_product['product_id'] == obj.id: + sale_value = sol_product['product_uom_qty'] + for pol_product in pol_query_obj: + if pol_product['product_id'] == obj.id: + purchase_value = pol_product['product_qty'] + virtual_available = obj.with_context({'warehouse': warehouse}).virtual_available + outgoing_qty = obj.with_context({'warehouse': warehouse}).outgoing_qty + incoming_qty = obj.with_context({'warehouse': warehouse}).incoming_qty + available_qty = virtual_available + outgoing_qty - incoming_qty + value = available_qty * obj.standard_price + vals = { + 'sku': obj.default_code, + 'name': obj.name, + 'category': obj.categ_id.name, + 'cost_price': obj.standard_price, + 'available': available_qty, + 'virtual': virtual_available, + 'incoming': incoming_qty, + 'outgoing': outgoing_qty, + 'net_on_hand': obj.with_context({'warehouse': warehouse}).qty_available, + 'total_value': value, + 'sale_value': sale_value, + 'purchase_value': purchase_value, + } + lines.append(vals) + return lines + + def get_xlsx_report(self, data, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + lines = self.browse(data['ids']) + d = lines.category + get_warehouse = self.get_warehouse(lines) + count = len(get_warehouse[0]) * 11 + 6 + comp = self.env.user.company_id.name + sheet = workbook.add_worksheet('Stock Info') + format0 = workbook.add_format({'font_size': 20, 'align': 'center', 'bold': True}) + format1 = workbook.add_format({'font_size': 14, 'align': 'vcenter', 'bold': True}) + format11 = workbook.add_format({'font_size': 12, 'align': 'center', 'bold': True}) + format21 = workbook.add_format({'font_size': 10, 'align': 'center', 'bold': True}) + format3 = workbook.add_format({'bottom': True, 'top': True, 'font_size': 12}) + format4 = workbook.add_format({'font_size': 12, 'align': 'left', 'bold': True}) + font_size_8 = workbook.add_format({'font_size': 8, 'align': 'center'}) + font_size_8_l = workbook.add_format({'font_size': 8, 'align': 'left'}) + font_size_8_r = workbook.add_format({'font_size': 8, 'align': 'right'}) + red_mark = workbook.add_format({'font_size': 8, 'bg_color': 'red'}) + justify = workbook.add_format({'font_size': 12}) + format3.set_align('center') + justify.set_align('justify') + format1.set_align('center') + red_mark.set_align('center') + sheet.merge_range(1, 7, 2, 10, 'Product Stock Info', format0) + sheet.merge_range(3, 7, 3, 10, comp, format11) + w_house = ', ' + cat = ', ' + c = [] + d1 = d.mapped('id') + if d1: + for i in d1: + c.append(self.env['product.category'].browse(i).name) + cat = cat.join(c) + sheet.merge_range(4, 0, 4, 1, 'Category(s) : ', format4) + sheet.merge_range(4, 2, 4, 3 + len(d1), cat, format4) + sheet.merge_range(5, 0, 5, 1, 'Warehouse(s) : ', format4) + w_house = w_house.join(get_warehouse[0]) + sheet.merge_range(5, 2, 5, 3 + len(get_warehouse[0]), w_house, format4) + user = self.env['res.users'].browse(self.env.uid) + tz = pytz.timezone(user.tz if user.tz else 'UTC') + times = pytz.utc.localize(datetime.datetime.now()).astimezone(tz) + sheet.merge_range('A8:G8', 'Report Date: ' + str(times.strftime("%Y-%m-%d %H:%M %p")), format1) + sheet.merge_range(7, 7, 7, count, 'Warehouses', format1) + sheet.merge_range('A9:G9', 'Product Information', format11) + w_col_no = 6 + w_col_no1 = 7 + for i in get_warehouse[0]: + w_col_no = w_col_no + 11 + sheet.merge_range(8, w_col_no1, 8, w_col_no, i, format11) + w_col_no1 = w_col_no1 + 11 + sheet.write(9, 0, 'SKU', format21) + sheet.merge_range(9, 1, 9, 3, 'Name', format21) + sheet.merge_range(9, 4, 9, 5, 'Category', format21) + sheet.write(9, 6, 'Cost Price', format21) + p_col_no1 = 7 + for i in get_warehouse[0]: + sheet.write(9, p_col_no1, 'Available', format21) + sheet.write(9, p_col_no1 + 1, 'Virtual', format21) + sheet.write(9, p_col_no1 + 2, 'Incoming', format21) + sheet.write(9, p_col_no1 + 3, 'Outgoing', format21) + sheet.merge_range(9, p_col_no1 + 4, 9, p_col_no1 + 5, 'Net On Hand', format21) + sheet.merge_range(9, p_col_no1 + 6, 9, p_col_no1 + 7, 'Total Sold', format21) + sheet.merge_range(9, p_col_no1 + 8, 9, p_col_no1 + 9, 'Total Purchased', format21) + sheet.write(9, p_col_no1 + 10, 'Valuation', format21) + p_col_no1 = p_col_no1 + 11 + prod_row = 10 + prod_col = 0 + for i in get_warehouse[1]: + get_line = self.get_lines(d, i) + for each in get_line: + sheet.write(prod_row, prod_col, each['sku'], font_size_8) + sheet.merge_range(prod_row, prod_col + 1, prod_row, prod_col + 3, each['name'], font_size_8_l) + sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['category'], font_size_8_l) + sheet.write(prod_row, prod_col + 6, each['cost_price'], font_size_8_r) + prod_row = prod_row + 1 + break + prod_row = 10 + prod_col = 7 + for i in get_warehouse[1]: + get_line = self.get_lines(d, i) + for each in get_line: + if each['available'] < 0: + sheet.write(prod_row, prod_col, each['available'], red_mark) + else: + sheet.write(prod_row, prod_col, each['available'], font_size_8) + if each['virtual'] < 0: + sheet.write(prod_row, prod_col + 1, each['virtual'], red_mark) + else: + sheet.write(prod_row, prod_col + 1, each['virtual'], font_size_8) + if each['incoming'] < 0: + sheet.write(prod_row, prod_col + 2, each['incoming'], red_mark) + else: + sheet.write(prod_row, prod_col + 2, each['incoming'], font_size_8) + if each['outgoing'] < 0: + sheet.write(prod_row, prod_col + 3, each['outgoing'], red_mark) + else: + sheet.write(prod_row, prod_col + 3, each['outgoing'], font_size_8) + if each['net_on_hand'] < 0: + sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['net_on_hand'], red_mark) + else: + sheet.merge_range(prod_row, prod_col + 4, prod_row, prod_col + 5, each['net_on_hand'], font_size_8) + if each['sale_value'] < 0: + sheet.merge_range(prod_row, prod_col + 6, prod_row, prod_col + 7, each['sale_value'], red_mark) + else: + sheet.merge_range(prod_row, prod_col + 6, prod_row, prod_col + 7, each['sale_value'], font_size_8) + if each['purchase_value'] < 0: + sheet.merge_range(prod_row, prod_col + 8, prod_row, prod_col + 9, each['purchase_value'], red_mark) + else: + sheet.merge_range(prod_row, prod_col + 8, prod_row, prod_col + 9, each['purchase_value'], + font_size_8) + if each['total_value'] < 0: + sheet.write(prod_row, prod_col + 10, each['total_value'], red_mark) + else: + sheet.write(prod_row, prod_col + 10, each['total_value'], font_size_8_r) + prod_row = prod_row + 1 + prod_row = 10 + prod_col = prod_col + 11 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/export_stockinfo_xls/security/ir.model.access.csv b/export_stockinfo_xls/security/ir.model.access.csv new file mode 100644 index 000000000..1c1a4d4a1 --- /dev/null +++ b/export_stockinfo_xls/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_wizard_stock_history_user,wizard.stock.history,model_wizard_stock_history,base.group_user,1,1,1,1 + diff --git a/export_stockinfo_xls/static/description/assets/icons/check.png b/export_stockinfo_xls/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/check.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/chevron.png b/export_stockinfo_xls/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/chevron.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/cogs.png b/export_stockinfo_xls/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/cogs.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/consultation.png b/export_stockinfo_xls/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/consultation.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/ecom-black.png b/export_stockinfo_xls/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/ecom-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/education-black.png b/export_stockinfo_xls/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/education-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/hotel-black.png b/export_stockinfo_xls/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/hotel-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/license.png b/export_stockinfo_xls/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/license.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png b/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/lifebuoy.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/logo.png b/export_stockinfo_xls/static/description/assets/icons/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/logo.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png b/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/manufacturing-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/pos-black.png b/export_stockinfo_xls/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/pos-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/puzzle.png b/export_stockinfo_xls/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/puzzle.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png b/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/restaurant-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/service-black.png b/export_stockinfo_xls/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/service-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/trading-black.png b/export_stockinfo_xls/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/trading-black.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/training.png b/export_stockinfo_xls/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/training.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/update.png b/export_stockinfo_xls/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/update.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/user.png b/export_stockinfo_xls/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/user.png differ diff --git a/export_stockinfo_xls/static/description/assets/icons/wrench.png b/export_stockinfo_xls/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/icons/wrench.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/approval_image.png b/export_stockinfo_xls/static/description/assets/modules/approval_image.png new file mode 100644 index 000000000..84fe94e80 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/approval_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/budget_image.png b/export_stockinfo_xls/static/description/assets/modules/budget_image.png new file mode 100644 index 000000000..fe6aa6fe4 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/budget_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/export_image.png b/export_stockinfo_xls/static/description/assets/modules/export_image.png new file mode 100644 index 000000000..4e4ea0e51 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/export_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/magento_image.png b/export_stockinfo_xls/static/description/assets/modules/magento_image.png new file mode 100644 index 000000000..39de0820f Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/magento_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/pos_image.png b/export_stockinfo_xls/static/description/assets/modules/pos_image.png new file mode 100644 index 000000000..c5932894b Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/pos_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/modules/shopify_image.png b/export_stockinfo_xls/static/description/assets/modules/shopify_image.png new file mode 100644 index 000000000..c6d92c16d Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/modules/shopify_image.png differ diff --git a/export_stockinfo_xls/static/description/assets/screenshots/hero.png b/export_stockinfo_xls/static/description/assets/screenshots/hero.png new file mode 100644 index 000000000..c3a884bea Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/screenshots/hero.png differ diff --git a/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx1.png b/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx1.png new file mode 100644 index 000000000..416af8f17 Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx1.png differ diff --git a/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx2.png b/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx2.png new file mode 100644 index 000000000..1badd952d Binary files /dev/null and b/export_stockinfo_xls/static/description/assets/screenshots/stock_xlsx2.png differ diff --git a/export_stockinfo_xls/static/description/banner.png b/export_stockinfo_xls/static/description/banner.png new file mode 100644 index 000000000..492980ad0 Binary files /dev/null and b/export_stockinfo_xls/static/description/banner.png differ diff --git a/export_stockinfo_xls/static/description/icon.png b/export_stockinfo_xls/static/description/icon.png new file mode 100644 index 000000000..b036176f2 Binary files /dev/null and b/export_stockinfo_xls/static/description/icon.png differ diff --git a/export_stockinfo_xls/static/description/index.html b/export_stockinfo_xls/static/description/index.html new file mode 100644 index 000000000..484db2bc9 --- /dev/null +++ b/export_stockinfo_xls/static/description/index.html @@ -0,0 +1,602 @@ +
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+ +
+
+
+
+ +
+
+
+

+ Current Stock XLS

+

+ Current Stock Report for all Products in each Warehouse +

+ +
+
+ + + + +
+
+

+ Overview +

+
+ +
+

+ This module helps to print Current Stock Report for all Products in each Warehouse with XLS

+ +
+
+ + +
+
+

+ Features +

+
+ +
+
+ +
+
+

+ Community & Enterprise Support

+

+ Available in Odoo 14.0 Community and Enterprise.

+
+
+
+
+ +
+
+

+ Current Stock XLS

+

+ Current Stock Report for all Products in each Warehouse.

+
+
+ +
+
+ +
+
+

+ Select category for products

+
+
+ +
+
+ +
+
+

+ Get your stock valuation details

+
+
+ +
+
+ +
+
+

+ Negative stock will be highlighted in "red" cells.

+
+
+ +
+ +
+
+

+ Screenshots +

+
+
+

+ Export Stock Info Wizard

+

+ Go to Inventory -> Reports -> Current stock in Excel. Now a wizard will appear on your screen. + Please enter the warehouses which you want to take the report. + You can also select category for products(It is Optional). Then Click "Export Product with Stock + Info" button. Then You will get the corresponding report in XLS.

+ +
+ +
+

+ Stock Info Excel Report

+

+ Per warehouse you can get Available Qty, Virtual Qty, Incoming Qty, Outgoing Qty, Net On Hand Qty, + Total Sold & Total Purchased Qty. + You can get your stock valuation details too. + Negative stock will be highlighted in "red" cells. +

+ +
+ +
+ + +
+
+

Suggested Products

+
+ + +
+
+ + + +
+
+
+

Our Services

+
+
+ +
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+
+ + + +
+
+
+

Our Industries

+
+
+ +
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

+
+
+ +
+
+ + + + + +
+
+
+

Need Help?

+
+
+
+ + +
+ +
+ +
+ +
+ WhatsApp +
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
\ No newline at end of file diff --git a/export_stockinfo_xls/static/src/js/action_manager.js b/export_stockinfo_xls/static/src/js/action_manager.js new file mode 100644 index 000000000..e9e276eb8 --- /dev/null +++ b/export_stockinfo_xls/static/src/js/action_manager.js @@ -0,0 +1,21 @@ +/** @odoo-module */ + +import { registry } from "@web/core/registry"; +import { download } from "@web/core/network/download"; +import framework from 'web.framework'; +import session from 'web.session'; + +registry.category("ir.actions.report handlers").add("stock_xlsx", async (action) => { + if (action.report_type === 'stock_xlsx') { + framework.blockUI(); + var def = $.Deferred(); + session.get_file({ + url: '/xlsx_reports', + data: action.data, + success: def.resolve.bind(def), + error: (error) => this.call('crash_manager', 'rpc_error', error), + complete: framework.unblockUI, + }); + return def; + } +}); diff --git a/export_stockinfo_xls/views/wizard_view.xml b/export_stockinfo_xls/views/wizard_view.xml new file mode 100644 index 000000000..7008b8bbd --- /dev/null +++ b/export_stockinfo_xls/views/wizard_view.xml @@ -0,0 +1,49 @@ + + + + + + wizard.stock.history.form + wizard.stock.history + +
+ + + + + + + + + + + + +
+
+
+
+
+ + + Export product stock in Excel + wizard.stock.history + ir.actions.act_window + form + + new + + + + +
+ +
diff --git a/hr_zk_attendance/README.rst b/hr_zk_attendance/README.rst new file mode 100644 index 000000000..38161a28e --- /dev/null +++ b/hr_zk_attendance/README.rst @@ -0,0 +1,39 @@ +Biometric Device Integration v15 +================================ +This Cybrosys's module integrates Odoo attendance with biometric device attendance. + +Features +======== +* Integrates biometric device(Face+Thumb) with HR attendance. +* Managing attendance automatically +* Keeps zk machine history in Odoo +* Option to configure multiple zk devices +* Option to clear all zk history from both device and Odoo + +Technical Notes +=============== +Used Libraries: + +*This integration is only applicable for the the device ZKteco model 'uFace 202' & 'iFace990' +* zklib +you can install zklib library using "sudo pip install zklib" + +Compatible Devices + +*ZKteco model 'uFace 202' +*ZKteco model 'iFace990' + +Author +======= +* Cybrosys Techno Solutions + +Credits +======= +Developer: Niyas Raphy @ Cybrosys, odoo@cybrosys.com V11 +Developer: Jesni Banu @ cybrosys, odoo@cybrosys.com V10 +Developer: Basith @ Cybrosys, odoo@cybrosys.com V12 +Developer: Varsha Vivek @ Cybrosys, odoo@cybrosys.com V13 +Developer: Ijaz Ahammed @ Cybrosys, odoo@cybrosys.com V14 +Developer: Noushid Khan @ Cybrosys, odoo@cybrosys.com V15 +Developer: Mostafa Shokiel , mostafa.shokiel@gmail.com + diff --git a/hr_zk_attendance/__init__.py b/hr_zk_attendance/__init__.py new file mode 100644 index 000000000..9df61f52c --- /dev/null +++ b/hr_zk_attendance/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from . import models diff --git a/hr_zk_attendance/__manifest__.py b/hr_zk_attendance/__manifest__.py new file mode 100644 index 000000000..452379785 --- /dev/null +++ b/hr_zk_attendance/__manifest__.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2021-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +{ + 'name': 'Biometric Device Integration', + 'version': '15.0.1.0.0', + 'summary': """Integrating Biometric Device (Model: ZKteco uFace 202) With HR Attendance (Face + Thumb)""", + 'description': """This module integrates Odoo with the biometric device(Model: ZKteco uFace 202),odoo15,odoo,hr,attendance""", + 'category': 'Generic Modules/Human Resources', + 'author': 'Cybrosys Techno Solutions, Mostafa Shokiel', + 'company': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['base_setup', 'hr_attendance'], + 'data': [ + 'security/ir.model.access.csv', + 'views/zk_machine_view.xml', + 'views/zk_machine_attendance_view.xml', + 'data/download_data.xml' + ], + 'images': ['static/description/banner.png'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/hr_zk_attendance/data/download_data.xml b/hr_zk_attendance/data/download_data.xml new file mode 100644 index 000000000..c4edd2e49 --- /dev/null +++ b/hr_zk_attendance/data/download_data.xml @@ -0,0 +1,14 @@ + + + + Download Data + + + 10 + minutes + -1 + + code + model.cron_download() + + diff --git a/hr_zk_attendance/doc/RELEASE_NOTES.md b/hr_zk_attendance/doc/RELEASE_NOTES.md new file mode 100755 index 000000000..52445f4e3 --- /dev/null +++ b/hr_zk_attendance/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 03.10.2021 +#### Version 15.0.1.0.0 +##### ADD +- Initial commit + diff --git a/hr_zk_attendance/models/__init__.py b/hr_zk_attendance/models/__init__.py new file mode 100644 index 000000000..8b72b54dd --- /dev/null +++ b/hr_zk_attendance/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from . import zk_machine +from . import machine_analysis +from . import zklib + diff --git a/hr_zk_attendance/models/machine_analysis.py b/hr_zk_attendance/models/machine_analysis.py new file mode 100644 index 000000000..a81a60647 --- /dev/null +++ b/hr_zk_attendance/models/machine_analysis.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from odoo import tools +from odoo import models, fields, api, _ + + +class HrEmployee(models.Model): + _inherit = 'hr.employee' + + device_id = fields.Char(string='Biometric Device ID') + + +class ZkMachine(models.Model): + _name = 'zk.machine.attendance' + _inherit = 'hr.attendance' + + @api.constrains('check_in', 'check_out', 'employee_id') + def _check_validity(self): + """overriding the __check_validity function for employee attendance.""" + pass + + device_id = fields.Char(string='Biometric Device ID') + punch_type = fields.Selection([('0', 'Check In'), + ('1', 'Check Out'), + ('2', 'Break Out'), + ('3', 'Break In'), + ('4', 'Overtime In'), + ('5', 'Overtime Out')], + string='Punching Type') + + attendance_type = fields.Selection([('1', 'Finger'), + ('15', 'Face'), + ('2','Type_2'), + ('3','Password'), + ('4','Card')], string='Category') + punching_time = fields.Datetime(string='Punching Time') + address_id = fields.Many2one('res.partner', string='Working Address') + + +class ReportZkDevice(models.Model): + _name = 'zk.report.daily.attendance' + _auto = False + _order = 'punching_day desc' + + name = fields.Many2one('hr.employee', string='Employee') + punching_day = fields.Datetime(string='Date') + address_id = fields.Many2one('res.partner', string='Working Address') + attendance_type = fields.Selection([('1', 'Finger'), + ('15', 'Face'), + ('2','Type_2'), + ('3','Password'), + ('4','Card')], + string='Category') + punch_type = fields.Selection([('0', 'Check In'), + ('1', 'Check Out'), + ('2', 'Break Out'), + ('3', 'Break In'), + ('4', 'Overtime In'), + ('5', 'Overtime Out')], string='Punching Type') + punching_time = fields.Datetime(string='Punching Time') + + def init(self): + tools.drop_view_if_exists(self._cr, 'zk_report_daily_attendance') + query = """ + create or replace view zk_report_daily_attendance as ( + select + min(z.id) as id, + z.employee_id as name, + z.write_date as punching_day, + z.address_id as address_id, + z.attendance_type as attendance_type, + z.punching_time as punching_time, + z.punch_type as punch_type + from zk_machine_attendance z + join hr_employee e on (z.employee_id=e.id) + GROUP BY + z.employee_id, + z.write_date, + z.address_id, + z.attendance_type, + z.punch_type, + z.punching_time + ) + """ + self._cr.execute(query) + + diff --git a/hr_zk_attendance/models/zk_machine.py b/hr_zk_attendance/models/zk_machine.py new file mode 100644 index 000000000..34447b1d7 --- /dev/null +++ b/hr_zk_attendance/models/zk_machine.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2020-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +import pytz +import sys +import datetime +import logging +import binascii + +from . import zklib +from .zkconst import * +from struct import unpack +from odoo import api, fields, models +from odoo import _ +from odoo.exceptions import UserError, ValidationError +_logger = logging.getLogger(__name__) +try: + from zk import ZK, const +except ImportError: + _logger.error("Please Install pyzk library.") + +_logger = logging.getLogger(__name__) + + +class HrAttendance(models.Model): + _inherit = 'hr.attendance' + + device_id = fields.Char(string='Biometric Device ID') + + +class ZkMachine(models.Model): + _name = 'zk.machine' + + name = fields.Char(string='Machine IP', required=True) + port_no = fields.Integer(string='Port No', required=True) + address_id = fields.Many2one('res.partner', string='Working Address') + company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id.id) + + def device_connect(self, zk): + try: + conn = zk.connect() + return conn + except: + return False + + def clear_attendance(self): + for info in self: + try: + machine_ip = info.name + zk_port = info.port_no + timeout = 30 + try: + zk = ZK(machine_ip, port=zk_port, timeout=timeout, 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: + # conn.clear_attendance() + self._cr.execute("""delete from zk_machine_attendance""") + conn.disconnect() + raise UserError(_('Attendance Records Deleted.')) + 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: + raise ValidationError( + 'Unable to clear Attendance log. Are you sure attendance device is connected & record is not empty.') + + def getSizeUser(self, zk): + """Checks a returned packet to see if it returned CMD_PREPARE_DATA, + indicating that data packets are to be sent + + Returns the amount of bytes that are going to be sent""" + command = unpack('HHHH', zk.data_recv[:8])[0] + if command == CMD_PREPARE_DATA: + size = unpack('I', zk.data_recv[8:12])[0] + print("size", size) + return size + else: + return False + + def zkgetuser(self, zk): + """Start a connection with the time clock""" + try: + users = zk.get_users() + print(users) + return users + except: + return False + + @api.model + def cron_download(self): + machines = self.env['zk.machine'].search([]) + for machine in machines : + machine.download_attendance() + + def download_attendance(self): + _logger.info("++++++++++++Cron Executed++++++++++++++++++++++") + zk_attendance = self.env['zk.machine.attendance'] + att_obj = self.env['hr.attendance'] + for info in self: + machine_ip = info.name + zk_port = info.port_no + timeout = 15 + try: + zk = ZK(machine_ip, port=zk_port, timeout=timeout, 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: + # conn.disable_device() #Device Cannot be used during this time. + try: + user = conn.get_users() + except: + user = False + try: + attendance = conn.get_attendance() + except: + attendance = False + if attendance: + for each in attendance: + atten_time = each.timestamp + atten_time = datetime.strptime(atten_time.strftime('%Y-%m-%d %H:%M:%S'), '%Y-%m-%d %H:%M:%S') + local_tz = pytz.timezone( + self.env.user.partner_id.tz or 'GMT') + local_dt = local_tz.localize(atten_time, is_dst=None) + utc_dt = local_dt.astimezone(pytz.utc) + utc_dt = utc_dt.strftime("%Y-%m-%d %H:%M:%S") + atten_time = datetime.strptime( + utc_dt, "%Y-%m-%d %H:%M:%S") + atten_time = fields.Datetime.to_string(atten_time) + if user: + for uid in user: + if uid.user_id == each.user_id: + get_user_id = self.env['hr.employee'].search( + [('device_id', '=', each.user_id)]) + if get_user_id: + duplicate_atten_ids = zk_attendance.search( + [('device_id', '=', each.user_id), ('punching_time', '=', atten_time)]) + if duplicate_atten_ids: + continue + else: + zk_attendance.create({'employee_id': get_user_id.id, + 'device_id': 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 = att_obj.search([('employee_id', '=', get_user_id.id), + ('check_out', '=', False)]) + print('ddfcd', str(each.status)) + if each.punch == 0: #check-in + if not att_var: + att_obj.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 = att_obj.search([('employee_id', '=', get_user_id.id)]) + if att_var1: + att_var1[-1].write({'check_out': atten_time}) + + else: + print('ddfcd', str(each.status)) + print('user', uid.name) + employee = self.env['hr.employee'].create( + {'device_id': each.user_id, 'name': uid.name}) + zk_attendance.create({'employee_id': employee.id, + 'device_id': each.user_id, + 'attendance_type': str(each.status), + 'punch_type': str(each.punch), + 'punching_time': atten_time, + 'address_id': info.address_id.id}) + att_obj.create({'employee_id': employee.id, + 'check_in': atten_time}) + else: + pass + # zk.enableDevice() + conn.disconnect + return True + else: + raise UserError(_('Unable to get the attendance log, please try again later.')) + else: + raise UserError(_('Unable to connect, please check the parameters and network connections.')) diff --git a/hr_zk_attendance/models/zkattendance.py b/hr_zk_attendance/models/zkattendance.py new file mode 100644 index 000000000..3d34f1802 --- /dev/null +++ b/hr_zk_attendance/models/zkattendance.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +import binascii +from struct import pack, unpack +from .zkconst import * + + +def getSizeAttendance(self): + """Checks a returned packet to see if it returned CMD_PREPARE_DATA, + indicating that data packets are to be sent + + Returns the amount of bytes that are going to be sent""" + command = unpack('HHHH', self.data_recv[:8])[0] + if command == CMD_PREPARE_DATA: + size = unpack('I', self.data_recv[8:12])[0] + return size + else: + return False + + +def reverseHex(hexstr): + tmp = '' + for i in reversed(range(int(len(hexstr)/2))): + tmp += hexstr[i*2:(i*2)+2] + + return tmp + +def zkgetattendance(self): + """Start a connection with the time clock""" + command = CMD_ATTLOG_RRQ + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + + if getSizeAttendance(self): + bytes = getSizeAttendance(self) + while bytes > 0: + data_recv, addr = self.zkclient.recvfrom(1032) + self.attendancedata.append(data_recv) + bytes -= 1024 + + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + data_recv = self.zkclient.recvfrom(8) + + attendance = [] + if len(self.attendancedata) > 0: + # The first 4 bytes don't seem to be related to the user + for x in range(len(self.attendancedata)): + if x > 0: + self.attendancedata[x] = self.attendancedata[x][8:] + + attendancedata = b''.join( self.attendancedata ) + + attendancedata = attendancedata[14:] + + while len(attendancedata) > 40: + + uid, state, timestamp, space = unpack( '24s1s4s11s', attendancedata.ljust(40)[:40] ) + + + # Clean up some messy characters from the user name + #uid = unicode(uid.strip('\x00|\x01\x10x'), errors='ignore') + uid = uid.split(b'\x00', 1)[0].decode('utf-8') + #print "%s, %s, %s" % (uid, state, decode_time( int( reverseHex( timestamp.encode('hex') ), 16 ) ) ) + + attendance.append( ( uid, int( binascii.hexlify(state), 16 ), decode_time( int( reverseHex( binascii.hexlify(timestamp).decode('utf-8')), 16 ) ) ) ) + + attendancedata = attendancedata[40:] + + return attendance + except: + return False + + +def zkclearattendance(self): + """Start a connection with the time clock""" + command = CMD_CLEAR_ATTLOG + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False diff --git a/hr_zk_attendance/models/zkconnect.py b/hr_zk_attendance/models/zkconnect.py new file mode 100644 index 000000000..4ee98595b --- /dev/null +++ b/hr_zk_attendance/models/zkconnect.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from struct import pack, unpack +from .zkconst import * + + +def zkconnect(self): + """Start a connection with the time clock""" + command = CMD_CONNECT + command_string = '' + chksum = 0 + session_id = 0 + reply_id = -1 + USHRT_MAX + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + + self.zkclient.sendto(buf, self.address) + + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + + return self.checkValid( self.data_recv ) + except: + return False + + +def zkdisconnect(self): + """Disconnect from the clock""" + command = CMD_EXIT + command_string = '' + chksum = 0 + session_id = self.session_id + + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + + self.zkclient.sendto(buf, self.address) + + self.data_recv, addr = self.zkclient.recvfrom(1024) + return self.checkValid( self.data_recv ) diff --git a/hr_zk_attendance/models/zkconst.py b/hr_zk_attendance/models/zkconst.py new file mode 100644 index 000000000..febfa2ab1 --- /dev/null +++ b/hr_zk_attendance/models/zkconst.py @@ -0,0 +1,71 @@ +from datetime import datetime, date + +USHRT_MAX = 65535 + + +CMD_CONNECT = 1000 +CMD_EXIT = 1001 +CMD_ENABLEDEVICE = 1002 +CMD_DISABLEDEVICE = 1003 + +CMD_ACK_OK = 2000 +CMD_ACK_ERROR = 2001 +CMD_ACK_DATA = 2002 + +CMD_PREPARE_DATA = 1500 +CMD_DATA = 1501 + +CMD_USERTEMP_RRQ = 9 +CMD_ATTLOG_RRQ = 13 +CMD_CLEAR_DATA = 14 +CMD_CLEAR_ATTLOG = 15 + +CMD_WRITE_LCD = 66 + +CMD_GET_TIME = 201 +CMD_SET_TIME = 202 + +CMD_VERSION = 1100 +CMD_DEVICE = 11 + +CMD_CLEAR_ADMIN = 20 +CMD_SET_USER = 8 + +LEVEL_USER = 0 +LEVEL_ADMIN = 14 + +def encode_time(t): + """Encode a timestamp send at the timeclock + + copied from zkemsdk.c - EncodeTime""" + d = ( (t.year % 100) * 12 * 31 + ((t.month - 1) * 31) + t.day - 1) *\ + (24 * 60 * 60) + (t.hour * 60 + t.minute) * 60 + t.second + + return d + + +def decode_time(t): + """Decode a timestamp retrieved from the timeclock + + copied from zkemsdk.c - DecodeTime""" + second = t % 60 + t = t / 60 + + minute = t % 60 + t = t / 60 + + hour = t % 24 + t = t / 24 + + day = t % 31+1 + t = t / 31 + + month = t % 12+1 + t = t / 12 + + year = t + 2000 + + d = datetime(int(year), int(month), int(day), int(hour), int(minute), int(second)) + + return d + diff --git a/hr_zk_attendance/models/zkdevice.py b/hr_zk_attendance/models/zkdevice.py new file mode 100644 index 000000000..cc8b4b468 --- /dev/null +++ b/hr_zk_attendance/models/zkdevice.py @@ -0,0 +1,82 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from struct import pack, unpack +from .zkconst import * + + +def zkdevicename(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~DeviceName' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + + +def zkenabledevice(self): + """Start a connection with the time clock""" + command = CMD_ENABLEDEVICE + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + +def zkdisabledevice(self): + """Start a connection with the time clock""" + command = CMD_DISABLEDEVICE + command_string = '\x00\x00' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False diff --git a/hr_zk_attendance/models/zkextendfmt.py b/hr_zk_attendance/models/zkextendfmt.py new file mode 100644 index 000000000..0b2eaf4ef --- /dev/null +++ b/hr_zk_attendance/models/zkextendfmt.py @@ -0,0 +1,56 @@ +def zkextendfmt(self): + try: + test = self.exttrynumber + except: + self.exttrynumber = 1 + + data_seq=[ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ] + #print data_seq + if self.exttrynumber == 1: + plus1 = 0 + plus2 = 0 + else: + plus1 = -1 + plus2 = +1 + + + desc = ": +"+hex( int('99', 16)+plus1 ).lstrip('0x')+", +"+hex(int('b1', 16)+plus2).lstrip("0x") + self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '99', 16 ) + plus1 ).lstrip("0x") + self.data_seq2 = hex( int( data_seq[1], 16 ) + int( 'b1', 16 ) + plus2 ).lstrip("0x") + + if len(self.data_seq1) >= 3: + #self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x") + self.data_seq1 = self.data_seq1[-2:] + + if len(self.data_seq2) >= 3: + #self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x") + self.data_seq2 = self.data_seq2[-2:] + + + if len(self.data_seq1) <= 1: + self.data_seq1 = "0"+self.data_seq1 + + if len(self.data_seq2) <= 1: + self.data_seq2 = "0"+self.data_seq2 + + + counter = hex( self.counter ).lstrip("0x") + if len(counter): + counter = "0" + counter + #print self.data_seq1+" "+self.data_seq2+desc + data = "0b00"+self.data_seq1+self.data_seq2+self.id_com+counter+"007e457874656e64466d7400" + self.zkclient.sendto(data.decode("hex"), self.address) + #print data + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + except: + if self.exttrynumber == 1: + self.exttrynumber = 2 + tmp = zkextendfmt(self) + if len(tmp) < 1: + self.exttrynumber = 1 + + self.id_com = self.data_recv.encode("hex")[8:12] + self.counter = self.counter+1 + #print self.data_recv.encode("hex") + return self.data_recv[8:] diff --git a/hr_zk_attendance/models/zkextendoplog.py b/hr_zk_attendance/models/zkextendoplog.py new file mode 100644 index 000000000..19b5a7e06 --- /dev/null +++ b/hr_zk_attendance/models/zkextendoplog.py @@ -0,0 +1,61 @@ +def zkextendoplog(self, index=0): + try: + test = self.extlogtrynumber + except: + self.extlogtrynumber = 1 + + data_seq = [ self.data_recv.encode("hex")[4:6], self.data_recv.encode("hex")[6:8] ] + + if index==0: + self.data_seq1 = hex( int( data_seq[0], 16 ) + int( '104', 16 ) ).lstrip("0x") + self.data_seq2 = hex( int( data_seq[1], 16 ) + int( '19', 16 ) ).lstrip("0x") + desc = ": +104, +19" + header="0b00" + elif index==1: + self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x") + self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x") + desc = ": -2c, -2" + header="d107" + elif index>=2: + self.data_seq1 = hex( abs( int( data_seq[0], 16 ) - int( '2c', 16 ) ) ).lstrip("0x") + self.data_seq2 = hex( abs( int( data_seq[1], 16 ) - int( '2', 16 ) ) ).lstrip("0x") + desc = ": -2c, -2" + header="ffff" + + + #print self.data_seq1+" "+self.data_seq2 + if len(self.data_seq1) >= 3: + self.data_seq2 = hex( int( self.data_seq2, 16 ) + int( self.data_seq1[:1], 16) ).lstrip("0x") + self.data_seq1 = self.data_seq1[-2:] + + if len(self.data_seq2) >= 3: + self.data_seq1 = hex( int( self.data_seq1, 16 ) + int( self.data_seq2[:1], 16) ).lstrip("0x") + self.data_seq2 = self.data_seq2[-2:] + + if len(self.data_seq1) <= 1: + self.data_seq1 = "0"+self.data_seq1 + + if len(self.data_seq2) <= 1: + self.data_seq2 = "0"+self.data_seq2 + + + counter = hex( self.counter ).lstrip("0x") + if len(counter): + counter = "0" + counter + + #print self.data_seq1+" "+self.data_seq2+desc + data = header+self.data_seq1+self.data_seq2+self.id_com+counter+"00457874656e644f504c6f6700" + self.zkclient.sendto(data.decode("hex"), self.address) + #print data + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + except: + bingung=1 + if self.extlogtrynumber == 1: + self.extlogtrynumber = 2 + zkextendoplog(self) + + self.id_com = self.data_recv.encode("hex")[8:12] + self.counter = self.counter+1 + #print self.data_recv.encode("hex") + return self.data_recv[8:] diff --git a/hr_zk_attendance/models/zkface.py b/hr_zk_attendance/models/zkface.py new file mode 100644 index 000000000..08e657816 --- /dev/null +++ b/hr_zk_attendance/models/zkface.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +################################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: cybrosys() +# +# This program is free software: you can modify +# it under the terms of the GNU Affero General Public License (AGPL) as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +################################################################################### +from struct import pack, unpack +from .zkconst import * + + +def zkfaceon(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = 'FaceFunOn' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zklib.py b/hr_zk_attendance/models/zklib.py new file mode 100644 index 000000000..3fb7201eb --- /dev/null +++ b/hr_zk_attendance/models/zklib.py @@ -0,0 +1,156 @@ +from socket import * +from .zkconnect import * +from .zkversion import * +from .zkos import * +from .zkextendfmt import * +from .zkextendoplog import * +from .zkplatform import * +from .zkworkcode import * +from .zkssr import * +from .zkpin import * +from .zkface import * +from .zkserialnumber import * +from .zkdevice import * +from .zkuser import * +from .zkattendance import * +from .zktime import * + +class ZKLib: + + def __init__(self, ip, port): + self.address = (ip, port) + self.zkclient = socket(AF_INET, SOCK_DGRAM) + self.zkclient.settimeout(3) + self.session_id = 0 + self.userdata = [] + self.attendancedata = [] + + + def createChkSum(self, p): + """This function calculates the chksum of the packet to be sent to the + time clock + + Copied from zkemsdk.c""" + l = len(p) + chksum = 0 + while l > 1: + chksum += unpack('H', pack('BB', p[0], p[1]))[0] + + p = p[2:] + if chksum > USHRT_MAX: + chksum -= USHRT_MAX + l -= 2 + + + if l: + chksum = chksum + p[-1] + + while chksum > USHRT_MAX: + chksum -= USHRT_MAX + + chksum = ~chksum + + while chksum < 0: + chksum += USHRT_MAX + + return pack('H', chksum) + + + def createHeader(self, command, chksum, session_id, reply_id, + command_string): + """This function puts a the parts that make up a packet together and + packs them into a byte string""" + buf = pack('HHHH', command, chksum, session_id, reply_id) + command_string.encode(encoding='utf_8', errors='strict') + + buf = unpack('8B'+'%sB' % len(command_string), buf) + + chksum = unpack('H', self.createChkSum(buf))[0] + #print unpack('H', self.createChkSum(buf)) + reply_id += 1 + if reply_id >= USHRT_MAX: + reply_id -= USHRT_MAX + + buf = pack('HHHH', command, chksum, session_id, reply_id) + return buf + command_string.encode(encoding='utf_8', errors='strict') + + + def checkValid(self, reply): + """Checks a returned packet to see if it returned CMD_ACK_OK, + indicating success""" + command = unpack('HHHH', reply[:8])[0] + if command == CMD_ACK_OK: + return True + else: + return False + + def connect(self): + return zkconnect(self) + + def disconnect(self): + return zkdisconnect(self) + + def version(self): + return zkversion(self) + + def osversion(self): + return zkos(self) + + def extendFormat(self): + return zkextendfmt(self) + + def extendOPLog(self, index=0): + return zkextendoplog(self, index) + + def platform(self): + return zkplatform(self) + + def fmVersion(self): + return zkplatformVersion(self) + + def workCode(self): + return zkworkcode(self) + + def ssr(self): + return zkssr(self) + + def pinWidth(self): + return zkpinwidth(self) + + def faceFunctionOn(self): + return zkfaceon(self) + + def serialNumber(self): + return zkserialnumber(self) + + def deviceName(self): + return zkdevicename(self) + + def disableDevice(self): + return zkdisabledevice(self) + + def enableDevice(self): + return zkenabledevice(self) + + def getUser(self): + return zkgetuser(self) + + def setUser(self, uid, userid, name, password, role): + return zksetuser(self, uid, userid, name, password, role) + + def clearUser(self): + return zkclearuser(self) + + def clearAdmin(self): + return zkclearadmin(self) + + def getAttendance(self): + return zkgetattendance(self) + + def clearAttendance(self): + return zkclearattendance(self) + + def setTime(self, t): + return zksettime(self, t) + + def getTime(self): + return zkgettime(self) diff --git a/hr_zk_attendance/models/zkos.py b/hr_zk_attendance/models/zkos.py new file mode 100644 index 000000000..b3f11dd74 --- /dev/null +++ b/hr_zk_attendance/models/zkos.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkos(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~OS' + chksum = 0 + session_id = self.session_id + + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False diff --git a/hr_zk_attendance/models/zkpin.py b/hr_zk_attendance/models/zkpin.py new file mode 100644 index 000000000..f25cd0b85 --- /dev/null +++ b/hr_zk_attendance/models/zkpin.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkpinwidth(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~PIN2Width' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zkplatform.py b/hr_zk_attendance/models/zkplatform.py new file mode 100644 index 000000000..7068dc63c --- /dev/null +++ b/hr_zk_attendance/models/zkplatform.py @@ -0,0 +1,43 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkplatform(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~Platform' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + + +def zkplatformVersion(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~ZKFPVersion' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zkserialnumber.py b/hr_zk_attendance/models/zkserialnumber.py new file mode 100644 index 000000000..cd1098f98 --- /dev/null +++ b/hr_zk_attendance/models/zkserialnumber.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkserialnumber(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~SerialNumber' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zkssr.py b/hr_zk_attendance/models/zkssr.py new file mode 100644 index 000000000..740a22119 --- /dev/null +++ b/hr_zk_attendance/models/zkssr.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkssr(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = '~SSR' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zktime.py b/hr_zk_attendance/models/zktime.py new file mode 100644 index 000000000..e2d69b78d --- /dev/null +++ b/hr_zk_attendance/models/zktime.py @@ -0,0 +1,50 @@ +from struct import pack, unpack +from .zkconst import * + + +def reverseHex(hexstr): + tmp = '' + for i in reversed(range(len(hexstr)/2)): + tmp += hexstr[i*2:(i*2)+2] + + return tmp + + +def zksettime(self, t): + """Start a connection with the time clock""" + command = CMD_SET_TIME + command_string = pack('I',encode_time(t)) + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + + +def zkgettime(self): + """Start a connection with the time clock""" + command = CMD_GET_TIME + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return decode_time( int( reverseHex( self.data_recv[8:].encode("hex") ), 16 ) ) + except: + return False diff --git a/hr_zk_attendance/models/zkuser.py b/hr_zk_attendance/models/zkuser.py new file mode 100644 index 000000000..08756fd5d --- /dev/null +++ b/hr_zk_attendance/models/zkuser.py @@ -0,0 +1,140 @@ +from struct import pack, unpack +from .zkconst import * + + +def getSizeUser(self): + """Checks a returned packet to see if it returned CMD_PREPARE_DATA, + indicating that data packets are to be sent + + Returns the amount of bytes that are going to be sent""" + command = unpack('HHHH', self.data_recv[:8])[0] + if command == CMD_PREPARE_DATA: + size = unpack('I', self.data_recv[8:12])[0] + return size + else: + return False + + +def zksetuser(self, uid, userid, name, password, role): + """Start a connection with the time clock""" + command = CMD_SET_USER + command_string = pack('sxs8s28ss7sx8s16s', chr( uid ), chr(role), password, name, chr(1), '', userid, '' ) + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + + +def zkgetuser(self): + """Start a connection with the time clock""" + command = CMD_USERTEMP_RRQ + command_string = '\x05' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + + + if getSizeUser(self): + bytes = getSizeUser(self) + + while bytes > 0: + data_recv, addr = self.zkclient.recvfrom(1032) + self.userdata.append(data_recv) + bytes -= 1024 + + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + data_recv = self.zkclient.recvfrom(8) + + users = {} + if len(self.userdata) > 0: + # The first 4 bytes don't seem to be related to the user + for x in range(len(self.userdata)): + if x > 0: + self.userdata[x] = self.userdata[x][8:] + + userdata = ''.join( self.userdata ) + + userdata = userdata[11:] + + while len(userdata) > 72: + + uid, role, password, name, userid = unpack( '2s2s8s28sx31s', userdata.ljust(72)[:72] ) + + uid = int( uid.encode("hex"), 16) + # Clean up some messy characters from the user name + password = password.split('\x00', 1)[0] + password = unicode(password.strip('\x00|\x01\x10x'), errors='ignore') + + #uid = uid.split('\x00', 1)[0] + userid = unicode(userid.strip('\x00|\x01\x10x'), errors='ignore') + + name = name.split('\x00', 1)[0] + + if name.strip() == "": + name = uid + + users[uid] = (userid, name, int( role.encode("hex"), 16 ), password) + + #print("%d, %s, %s, %s, %s" % (uid, userid, name, int( role.encode("hex"), 16 ), password)) + userdata = userdata[72:] + + return users + except: + return False + + +def zkclearuser(self): + """Start a connection with the time clock""" + command = CMD_CLEAR_DATA + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + + +def zkclearadmin(self): + """Start a connection with the time clock""" + command = CMD_CLEAR_ADMIN + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False diff --git a/hr_zk_attendance/models/zkversion.py b/hr_zk_attendance/models/zkversion.py new file mode 100644 index 000000000..0ee6a1693 --- /dev/null +++ b/hr_zk_attendance/models/zkversion.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkversion(self): + """Start a connection with the time clock""" + command = CMD_VERSION + command_string = '' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/models/zkworkcode.py b/hr_zk_attendance/models/zkworkcode.py new file mode 100644 index 000000000..10660e530 --- /dev/null +++ b/hr_zk_attendance/models/zkworkcode.py @@ -0,0 +1,23 @@ +from struct import pack, unpack +from .zkconst import * + + +def zkworkcode(self): + """Start a connection with the time clock""" + command = CMD_DEVICE + command_string = 'WorkCode' + chksum = 0 + session_id = self.session_id + reply_id = unpack('HHHH', self.data_recv[:8])[3] + + buf = self.createHeader(command, chksum, session_id, + reply_id, command_string) + self.zkclient.sendto(buf, self.address) + #print buf.encode("hex") + try: + self.data_recv, addr = self.zkclient.recvfrom(1024) + self.session_id = unpack('HHHH', self.data_recv[:8])[2] + return self.data_recv[8:] + except: + return False + diff --git a/hr_zk_attendance/security/ir.model.access.csv b/hr_zk_attendance/security/ir.model.access.csv new file mode 100644 index 000000000..438279985 --- /dev/null +++ b/hr_zk_attendance/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_zk_machine_user,zk.machine.hr_biometric_machine,model_zk_machine,hr_attendance.group_hr_attendance_user,1,1,1,1 +access_hr_zk_machine_user1,zk.machine.hr_biometric_machine1,model_zk_machine_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1 +access_hr_zk_machine_user2,zk.machine.hr_biometric_machine2,model_zk_report_daily_attendance,hr_attendance.group_hr_attendance_user,1,1,1,1 diff --git a/hr_zk_attendance/static/description/assets/icons/check.png b/hr_zk_attendance/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/check.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/chevron.png b/hr_zk_attendance/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/chevron.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/cogs.png b/hr_zk_attendance/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/cogs.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/consultation.png b/hr_zk_attendance/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/consultation.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/ecom-black.png b/hr_zk_attendance/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/ecom-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/education-black.png b/hr_zk_attendance/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/education-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/hotel-black.png b/hr_zk_attendance/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/hotel-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/license.png b/hr_zk_attendance/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/license.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/lifebuoy.png b/hr_zk_attendance/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/lifebuoy.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/manufacturing-black.png b/hr_zk_attendance/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/manufacturing-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/pos-black.png b/hr_zk_attendance/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/pos-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/puzzle.png b/hr_zk_attendance/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/puzzle.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/restaurant-black.png b/hr_zk_attendance/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/restaurant-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/service-black.png b/hr_zk_attendance/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/service-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/trading-black.png b/hr_zk_attendance/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/trading-black.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/training.png b/hr_zk_attendance/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/training.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/update.png b/hr_zk_attendance/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/update.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/user.png b/hr_zk_attendance/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/user.png differ diff --git a/hr_zk_attendance/static/description/assets/icons/wrench.png b/hr_zk_attendance/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/hr_zk_attendance/static/description/assets/icons/wrench.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/approval_image.png b/hr_zk_attendance/static/description/assets/modules/approval_image.png new file mode 100644 index 000000000..84fe94e80 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/approval_image.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/budget_image.png b/hr_zk_attendance/static/description/assets/modules/budget_image.png new file mode 100644 index 000000000..fe6aa6fe4 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/budget_image.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/gantt_image.png b/hr_zk_attendance/static/description/assets/modules/gantt_image.png new file mode 100644 index 000000000..4810fc34d Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/gantt_image.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/library_image.png b/hr_zk_attendance/static/description/assets/modules/library_image.png new file mode 100644 index 000000000..77be44d63 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/library_image.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/pos_order_image.png b/hr_zk_attendance/static/description/assets/modules/pos_order_image.png new file mode 100644 index 000000000..1217263a6 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/pos_order_image.png differ diff --git a/hr_zk_attendance/static/description/assets/modules/whatsapp_image.gif b/hr_zk_attendance/static/description/assets/modules/whatsapp_image.gif new file mode 100644 index 000000000..4c0c52982 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/modules/whatsapp_image.gif differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/hero.gif b/hr_zk_attendance/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..faee183b3 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/hero.gif differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/hero.png b/hr_zk_attendance/static/description/assets/screenshots/hero.png new file mode 100644 index 000000000..b72936c85 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/hero.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/machine-IP.png b/hr_zk_attendance/static/description/assets/screenshots/machine-IP.png new file mode 100644 index 000000000..26adfdd77 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/machine-IP.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/screenshot1.png b/hr_zk_attendance/static/description/assets/screenshots/screenshot1.png new file mode 100644 index 000000000..6a7e2972f Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/screenshot1.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/screenshot2.png b/hr_zk_attendance/static/description/assets/screenshots/screenshot2.png new file mode 100644 index 000000000..45ae8f4ff Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/screenshot2.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/screenshot3.png b/hr_zk_attendance/static/description/assets/screenshots/screenshot3.png new file mode 100644 index 000000000..e23997baa Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/screenshot3.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/screenshot4.png b/hr_zk_attendance/static/description/assets/screenshots/screenshot4.png new file mode 100644 index 000000000..225495f52 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/screenshot4.png differ diff --git a/hr_zk_attendance/static/description/assets/screenshots/screenshot5.png b/hr_zk_attendance/static/description/assets/screenshots/screenshot5.png new file mode 100644 index 000000000..0b0c99943 Binary files /dev/null and b/hr_zk_attendance/static/description/assets/screenshots/screenshot5.png differ diff --git a/hr_zk_attendance/static/description/banner.png b/hr_zk_attendance/static/description/banner.png new file mode 100644 index 000000000..a0969fcb5 Binary files /dev/null and b/hr_zk_attendance/static/description/banner.png differ diff --git a/hr_zk_attendance/static/description/cybro_logo.png b/hr_zk_attendance/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/hr_zk_attendance/static/description/cybro_logo.png differ diff --git a/hr_zk_attendance/static/description/icon.png b/hr_zk_attendance/static/description/icon.png new file mode 100644 index 000000000..884ae49cd Binary files /dev/null and b/hr_zk_attendance/static/description/icon.png differ diff --git a/hr_zk_attendance/static/description/index.html b/hr_zk_attendance/static/description/index.html new file mode 100644 index 000000000..2f733e216 --- /dev/null +++ b/hr_zk_attendance/static/description/index.html @@ -0,0 +1,680 @@ +
+
+
+

+ HR Biometric Device Integration

+

+ This Module Integrates Biometric Device With HR Attendance +

+ +
+
+ + + + +
+
+

+ Overview +

+
+ +
+

+ Automation is an implementation factor for a successful ERP. Using this module, HR attendance can be + automated via integrating Thumb / Face detection device with Odoo. One can configure a user both + from thumbing device or Odoo employee form.

+ +
+
+ +
+
+

+ Features +

+
+ +
+
+ +
+
+

+ Biometric Device Integration

+

+ Integrates biometric device (Face+Thumb) with HR attendance.

+
+
+ +
+
+ +
+
+

+ Keep Attendance Log in Odoo

+

+ Option to keep the device attendance log in Odoo.

+
+
+ +
+
+ +
+
+

+ Clear Attendance Log from Both Device and Odoo

+

+ Option to clear the device attendance log from both device and Odoo.

+
+
+ +
+
+ +
+
+

+ Automates HR Attendance

+

+ Automating HR attendance.

+
+
+ +
+
+ +
+
+

+ Configure multiple devices

+

+ Option to configure multiple devices.

+
+
+ +
+
+ +
+
+

+ Support ZKteco uFace 202, iFace990

+

+ This module will support with ZKteco model 'uFace 202', ZKteco model 'iFace990'.

+
+
+ +
+ +
+
+

+ Screenshots +

+
+ +
+

+ Biometric Device Configuration

+

+ Here you can configure your all devices with it's IP address and port number. +

+ + +
+ +
+

+ Download Device Attendance Log

+

+ + After configuration, you can download your device attendance log into Odoo through 'Download' + button.If the device is connected, then the Odoo will download all device attendance log. Otherwise, + the Odoo will display you a warning message as follow. +

+ + +
+ +
+

+ Clear Device Attendance Log

+

+ You can also clear all attendance log from both Odoo and device via 'Clear' button. If the device is + not connected it will display you a warning message as follow. +

+ + +
+ + +
+

+ Biometric Device Attendance Log

+

+ Here you can see all device attendance log. +

+ + +
+ +
+

+ HR Attendance

+

+ + Here, Odoo automatically generates HR attendance log while downloading the device attendance. +

+ + +
+ +
+

+ Employee Configuration

+

+ + + You can update existing employees with the 'Device Id' which are the id in the biometric device. If + there is no match with the biometric device id then system will automatically create corresponding + employee. +

+ + +
+
+ +
+ +
+ + +
+
+

Suggested Products

+
+ + +
+
+ + + +
+
+
+

Our Services

+
+
+ +
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Licensing Consultancy
+
+
+
+ + + +
+
+
+

Our Industries

+
+
+ +
+
+ +
+ Trading +
+

+ Easily procure + and + sell your products

+
+
+ +
+
+ +
+ POS +
+

+ Easy + configuration + and convivial experience

+
+
+ +
+
+ +
+ Education +
+

+ A platform for + educational management

+
+
+ +
+
+ +
+ Manufacturing +
+

+ Plan, track and + schedule your operations

+
+
+ +
+
+ +
+ E-commerce & Website +
+

+ Mobile + friendly, + awe-inspiring product pages

+
+
+ +
+
+ +
+ Service Management +
+

+ Keep track of + services and invoice

+
+
+ +
+
+ +
+ Restaurant +
+

+ Run your bar or + restaurant methodically

+
+
+ +
+
+ +
+ Hotel Management +
+

+ An + all-inclusive + hotel management application

+
+
+ +
+
+ + + + + +
+
+
+

Need Help?

+
+
+
+ + +
+ +
+ + +
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
\ No newline at end of file diff --git a/hr_zk_attendance/views/zk_machine_attendance_view.xml b/hr_zk_attendance/views/zk_machine_attendance_view.xml new file mode 100644 index 000000000..8b5787174 --- /dev/null +++ b/hr_zk_attendance/views/zk_machine_attendance_view.xml @@ -0,0 +1,69 @@ + + + + + inherited_hr.attendance.tree + hr.attendance + + + + + + + + + + inherited_hr_attendance_view_filter + hr.attendance + + + + + + + + + + + + + + zk.report.daily.attendance.search + zk.report.daily.attendance + + + + + + + + + + + + + zk.report.daily.attendance.tree + zk.report.daily.attendance + + + + + + + + + + + + + + Attendance Analysis + zk.report.daily.attendance + tree + {'search_default_my_attendance':1} + + + + + diff --git a/hr_zk_attendance/views/zk_machine_view.xml b/hr_zk_attendance/views/zk_machine_view.xml new file mode 100644 index 000000000..80c5ee941 --- /dev/null +++ b/hr_zk_attendance/views/zk_machine_view.xml @@ -0,0 +1,68 @@ + + + + zk.machine.form + zk.machine + +
+
+
+ +
+
+ + + + + + + + + +
+
+
+
+ + + zk.machine.tree + zk.machine + + + + + + + + + + + + Attendances + zk.machine + tree,form + + + + hr.employee.form + hr.employee + + + + + + + + + + +
+