1
This commit is contained in:
@@ -0,0 +1,3 @@
|
||||
from . import controllers
|
||||
from . import models
|
||||
from . import wizard
|
||||
@@ -0,0 +1,25 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# email:pengyuthon@163.com
|
||||
{
|
||||
'name': 'Yuthon Iot Attendance',
|
||||
'summary': '',
|
||||
'description': '''
|
||||
''',
|
||||
'author': 'pengyuthon',
|
||||
'version': '17.0.1.2.1',
|
||||
'category': 'Human Resources/Attendances',
|
||||
'sequence': 240,
|
||||
'website': 'https://www.pengyuthon.com',
|
||||
'depends': ['hr_attendance', 'base', 'hr'],
|
||||
'data': [
|
||||
'security/ir.model.access.csv',
|
||||
'views/attendance_iot_datas.xml',
|
||||
'views/attendance_iot_manager_views.xml',
|
||||
'views/attendance_iot_views.xml',
|
||||
'views/inherit_hr_employee_views.xml',
|
||||
'wizard/attendance_iot_manager_wizard_views.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'license': 'LGPL-3',
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import kaoqin_test
|
||||
from . import http
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
from odoo.http import JsonRPCDispatcher
|
||||
|
||||
|
||||
class CustomJsonRPCDispatcher(JsonRPCDispatcher):
|
||||
"""
|
||||
继承odoo底层源码http.py
|
||||
因为odoo本身会给对应的接口请求进行自动包装,通过判断做设备区分
|
||||
"""
|
||||
def _response(self, result=None, error=None):
|
||||
if 'api/v1' in self.request.httprequest.headers.environ.get('PATH_INFO'):
|
||||
return self.request.make_json_response(result)
|
||||
response = {'jsonrpc': '2.0', 'id': self.request_id}
|
||||
if error is not None:
|
||||
response['error'] = error
|
||||
if result is not None:
|
||||
response['result'] = result
|
||||
return self.request.make_json_response(response)
|
||||
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
from odoo.http import Controller, request, route
|
||||
from datetime import datetime
|
||||
from odoo import models, fields, api, http, SUPERUSER_ID, _
|
||||
|
||||
|
||||
class BusController(Controller):
|
||||
def get_equipment_company(self, sn):
|
||||
sn_address = {
|
||||
'RLM-00111018': '地址1',
|
||||
}
|
||||
return sn_address[sn]
|
||||
|
||||
@route('/api/v1/record/face', methods=['POST'], type='json', auth='none', csrf=False)
|
||||
def get_model_definitions(self):
|
||||
"""
|
||||
用户通过考勤机识别,写入考勤数据
|
||||
:return:
|
||||
"""
|
||||
data = request.httprequest.json['logs'][0]
|
||||
user_name = data.get('user_name')
|
||||
employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('name', '=', user_name)])
|
||||
request.env(user=SUPERUSER_ID)["hr.attendance"].create({
|
||||
'employee_id': employee_id.id,
|
||||
'check_in': datetime.strptime(data.get('recog_time'), '%Y-%m-%d %H:%M:%S')
|
||||
})
|
||||
print("---------------------------------------------测试成功---------------------------------------")
|
||||
return {"Result": 0, "Msg": "添加成功"}
|
||||
|
||||
@route('/api/v1/user/inf_photo', methods=['POST'], type='json', auth='public')
|
||||
def get_model_definitions(self):
|
||||
name = request.jsonrequest.get('sn') # 姓名
|
||||
phone = request.jsonrequest.get('content') # 电话
|
||||
print(name, phone)
|
||||
return {
|
||||
"Result": 0,
|
||||
"Msg": "更新成功"
|
||||
}
|
||||
|
||||
# 员工识别接口
|
||||
@route('/api/v1/stranger', methods=['POST'], type='json', auth='none')
|
||||
def get_model_definitions(self):
|
||||
data = request.httprequest.json.get('logs')[0]
|
||||
user_name = data['user_name']
|
||||
request.env["hr.employee"].create({
|
||||
'name': user_name + 'stranger',
|
||||
})
|
||||
print("---------------------------------------------测试成功---------------------------------------")
|
||||
return {
|
||||
"Result": 0,
|
||||
"Msg": "更新成功"
|
||||
}
|
||||
|
||||
@route('/api/v1/addFace', methods=['POST'], type='json', auth='none', csrf=False)
|
||||
def get_model_kaoqin(self):
|
||||
"""
|
||||
获取用户录入考勤机数据,和系统内用户绑定关联
|
||||
:return:
|
||||
"""
|
||||
data = request.httprequest.json
|
||||
user_name = data.get('name')
|
||||
# validity_period = datetime.strptime(data.get('validity_period'), '%Y-%m-%d').date()
|
||||
employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('name', '=', user_name)])
|
||||
attendance_iot = request.env(user=SUPERUSER_ID)["attendance.iot.manager"]
|
||||
if not attendance_iot.search([('employee_id', '=', employee_id.id), ('attendance_number', '=', data.get('sn'))]):
|
||||
employee_id.write({
|
||||
'image_1920': data.get('photo'),
|
||||
})
|
||||
attendance_iot.create({
|
||||
'data_selection': "api",
|
||||
'attendance_number': data.get('sn'),
|
||||
'attendance_address': self.get_equipment_company(data.get('sn')),
|
||||
'employee_id': employee_id.id,
|
||||
})
|
||||
print("---------------------------------------------测试成功---------------------------------------")
|
||||
return {"Result": 0, "Msg": "添加成功"}
|
||||
|
||||
@route('/api/v1/record/face', methods=['POST'], type='json', auth='none', csrf=False)
|
||||
def get_model_identify(self):
|
||||
"""
|
||||
获取上传识别成功记录
|
||||
:return:
|
||||
"""
|
||||
data = request.httprequest.json
|
||||
user_name = data.get('logs').get('user_id')
|
||||
employee_id = request.env(user=SUPERUSER_ID)["hr.employee"].search([('user_id', '=', user_name.id)])
|
||||
attendance_iot = request.env(user=SUPERUSER_ID)["attendance.iot.datas"]
|
||||
if not attendance_iot.search(
|
||||
[('employee_id', '=', employee_id.id), ('attendance_number', '=', data.get('sn'))]):
|
||||
attendance_iot.create({
|
||||
'attendance_number': data.get('sn'),
|
||||
'attendance_date': data.get('logs').get('recog_time'),
|
||||
'employee_id': employee_id.id,
|
||||
})
|
||||
print("---------------------------------------------测试成功---------------------------------------")
|
||||
return {"Result": 0, "Msg": "添加成功"}
|
||||
@@ -0,0 +1,4 @@
|
||||
from . import hr_employee
|
||||
from . import attendance_iot
|
||||
from . import attendance_iot_datas
|
||||
from . import attendance_iot_manager
|
||||
@@ -0,0 +1,13 @@
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AttendanceIot(models.Model):
|
||||
_name = 'attendance.iot'
|
||||
_description = '考勤机设备'
|
||||
_rec_name = 'name'
|
||||
|
||||
name = fields.Char(string="设备号")
|
||||
attendance_address = fields.Char(string="设备地址")
|
||||
broker = fields.Char(string='服务器地址/ip')
|
||||
port = fields.Char(string='端口号')
|
||||
@@ -0,0 +1,11 @@
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AttendanceIotDatas(models.Model):
|
||||
_name = 'attendance.iot.datas'
|
||||
_description = '上传识别记录'
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string='人员')
|
||||
attendance_date = fields.Datetime(string='打卡时间')
|
||||
attendance_iot_id = fields.Many2one('attendance.iot', string="设备号")
|
||||
@@ -0,0 +1,90 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models, tools, _
|
||||
from datetime import datetime, time, timedelta
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import UserError
|
||||
import paho.mqtt.client as mqtt
|
||||
import logging
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AttendanceIotManager(models.Model):
|
||||
_name = 'attendance.iot.manager'
|
||||
_description = "考勤机管理"
|
||||
|
||||
attendance_iot_id = fields.Many2one('attendance.iot', string="设备号")
|
||||
attendance_address = fields.Char(string="设备地址")
|
||||
employee_id = fields.Many2one('hr.employee', string="员工")
|
||||
id_valid = fields.Date(string="数据有效期")
|
||||
data_selection = fields.Selection([('mqtt', 'mqtt'), ('api', 'api')], default="mqtt", string="数据类型")
|
||||
|
||||
def action_attendance_iot_manager(self):
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"name": "批量导入考勤机",
|
||||
"res_model": "attendance.iot.manager.wizard",
|
||||
"view_mode": "form",
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
for vals in vals_list:
|
||||
if vals['data_selection'] == 'mqtt':
|
||||
self.mqtt_request(self, vals['attendance_number'], 'addUser')
|
||||
return super().create(vals_list)
|
||||
|
||||
def mqtt_request(self, attendance_number, data):
|
||||
reuqired_params = self.get_required_params(data)
|
||||
if any(reuqired_param not in data.keys() for reuqired_param in reuqired_params):
|
||||
raise UserError(f'缺少必填参数,请确认: {",".join(reuqired_params)}')
|
||||
mqtt_config = self.get_mqtt_config()
|
||||
self.start_mqtt(mqtt_config)
|
||||
self.publish_message(mqtt_config, f'iot/device/{attendance_number}', data)
|
||||
|
||||
def get_required_params(self, cmd):
|
||||
params_dict = {
|
||||
'addUser': ['cmd', 'user_id', 'name', 'id_valid'],
|
||||
'editUser': ['cmd', 'user_id', 'edit_mode'],
|
||||
'delUser': ['cmd'],
|
||||
'delMultiUser': ['cmd', 'user_ids'],
|
||||
'setPasswordVerify': ['cmd', 'value'],
|
||||
'setOpenDoorPassword': ['cmd', 'value'],
|
||||
}
|
||||
if cmd not in params_dict.keys():
|
||||
raise UserError(f'指令不存在,请确认,目前支持: {",".join(params_dict.keys())}')
|
||||
else:
|
||||
return params_dict[cmd]
|
||||
|
||||
@api.model
|
||||
def get_mqtt_config(self):
|
||||
mqtt_config = self.search([], limit=1)
|
||||
return mqtt_config
|
||||
|
||||
@api.model
|
||||
def start_mqtt(self, mqtt_config):
|
||||
def on_connect(client, userdata, flags, rc):
|
||||
if rc == 0:
|
||||
_logger.info("Connected to MQTT Broker")
|
||||
else:
|
||||
_logger.info(f"Failed to connect, return code {rc}", )
|
||||
|
||||
def on_message(client, userdata, message):
|
||||
_logger.info(f"Received message: {str(message.payload.decode('utf-8'))}", )
|
||||
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "OdooMQTT")
|
||||
client.on_connect = on_connect
|
||||
client.on_message = on_message
|
||||
client.connect(mqtt_config.broker, mqtt_config.port, 60)
|
||||
client.loop_start()
|
||||
|
||||
@api.model
|
||||
def publish_message(self, mqtt_config, topic, message):
|
||||
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION1, "OdooMQTT")
|
||||
client.connect(mqtt_config.broker, mqtt_config.port, 60)
|
||||
res = client.publish(topic, message)
|
||||
if res.get('code') == 0:
|
||||
_logger.info("操作成功")
|
||||
else:
|
||||
_logger.info(f"操作失败,返回错误码: {res.get('code')}")
|
||||
client.disconnect()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import models, fields, api, exceptions, _
|
||||
|
||||
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = "hr.employee"
|
||||
_description = "考勤机和用户信息的绑定"
|
||||
|
||||
attendance_iot_ids = fields.One2many('attendance.iot.manager', 'employee_id', string="考勤机信息")
|
||||
@@ -0,0 +1,5 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_attendance_iot_manager,attendance_iot_manager,model_attendance_iot_manager,base.group_user,1,1,1,1
|
||||
access_attendance_iot_manager_wizard,attendance_iot_manager_wizard,model_attendance_iot_manager_wizard,base.group_user,1,1,1,1
|
||||
access_attendance_iot_datas,attendance_iot_datas,model_attendance_iot_datas,base.group_user,1,1,1,1
|
||||
access_attendance_iot,attendance_iot,model_attendance_iot,base.group_user,1,1,1,1
|
||||
|
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="attendance_iot_datas_tree" model="ir.ui.view">
|
||||
<field name="name">attendance.iot.datas.tree</field>
|
||||
<field name="model">attendance.iot.datas</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="上传记录">
|
||||
<field name="attendance_date"/>
|
||||
<field name="attendance_iot_id"/>
|
||||
<field name="employee_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="attendance_iot_datas_action" model="ir.actions.act_window">
|
||||
<field name="name">上传记录</field>
|
||||
<field name="res_model">attendance.iot.datas</field>
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="attendance_iot_manager_tree" model="ir.ui.view">
|
||||
<field name="name">attendance.iot.manager.tree</field>
|
||||
<field name="model">attendance.iot.manager</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="考勤机设备管理" editable="bottom">
|
||||
<field name="attendance_iot_id"/>
|
||||
<field name="attendance_address"/>
|
||||
<field name="employee_id" required="1"/>
|
||||
<field name="id_valid" required="1"/>
|
||||
<field name="data_selection" required="1"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="attendance_iot_manager_filter" model="ir.ui.view">
|
||||
<field name="name">attendance.iot.manager.search</field>
|
||||
<field name="model">attendance.iot.manager</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="考勤机设备管理">
|
||||
<group expand="0">
|
||||
<filter name="attendance_iot_id" string="考勤机" context="{'group_by': 'attendance_iot_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="attendance_iot_manager_action" model="ir.actions.act_window">
|
||||
<field name="name">考勤机设备管理</field>
|
||||
<field name="res_model">attendance.iot.manager</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="attendance_iot_manager_filter"/>
|
||||
<field name="context">
|
||||
{ "search_default_attendance_iot_id":1, }
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="attendance_iot_manager_wizard_action" model="ir.actions.server">
|
||||
<field name="name">批量录入考勤机</field>
|
||||
<field name="model_id" ref="model_attendance_iot_manager"/>
|
||||
<field name="binding_model_id" ref="model_attendance_iot_manager"/>
|
||||
<field name="state">code</field>
|
||||
<field name="code">action = model.action_attendance_iot_manager()</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="attendance_iot_tree" model="ir.ui.view">
|
||||
<field name="name">考勤机设备管理</field>
|
||||
<field name="model">attendance.iot</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree editable="bottom">
|
||||
<field name="name"/>
|
||||
<field name="attendance_address"/>
|
||||
<field name="broker"/>
|
||||
<field name="port"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="attendance_iot_action" model="ir.actions.act_window">
|
||||
<field name="name">考勤机设备管理</field>
|
||||
<field name="res_model">attendance.iot</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_employee_inherit_attendance_form" model="ir.ui.view">
|
||||
<field name="name">hr.employee.inherit.attendance.form</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="inherit_id" ref="hr.view_employee_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='hr_settings']" position="after">
|
||||
<page string="考勤机信息">
|
||||
<field name="attendance_iot_ids">
|
||||
<tree>
|
||||
<field name="attendance_iot_id"/>
|
||||
<field name="attendance_address"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="id_valid"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -0,0 +1 @@
|
||||
from . import attendance_iot_manager_wizard
|
||||
@@ -0,0 +1,33 @@
|
||||
from odoo import api, fields, models, tools
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class AttendanceIotManagerWizard(models.TransientModel):
|
||||
_name = 'attendance.iot.manager.wizard'
|
||||
_description = '批量导入考勤用户数据弹窗'
|
||||
|
||||
attendance_iot_id = fields.Many2one('attendance.iot', string="设备号")
|
||||
department_ids = fields.Many2many('hr.department', string='部门范围')
|
||||
employee_ids = fields.Many2many('hr.employee', string='人员范围')
|
||||
users_role_ids = fields.Many2many('soong.user.role', string='角色范围', domain=[('user_type', '!=', 'lv_role')])
|
||||
iot_users_ids = fields.Many2many('res.users', string="人员", compute="compute_iot_users_ids", store=True)
|
||||
|
||||
@api.depends('department_ids', 'employee_ids', 'users_role_ids')
|
||||
def compute_iot_users_ids(self):
|
||||
users_role_list = department_users_list = []
|
||||
# 部门人员
|
||||
for depart in self.department_ids:
|
||||
department_users_data = self.env['hr.employee'].search([('department_id', '=', depart.id)])
|
||||
department_users_list = [de.user_id.id for de in department_users_data]
|
||||
# 人员
|
||||
employee_users_list = self.employee_ids.user_id.ids
|
||||
# 角色人员
|
||||
for user_id in self.users_role_ids:
|
||||
department_users_data = self.env['hr.employee'].search([('user_role_id', '=', user_id.id)])
|
||||
users_role_list = [ro.user_id.id for ro in department_users_data]
|
||||
publish_user_list = department_users_list + employee_users_list + users_role_list
|
||||
self.iot_users_ids = [(6, 0, publish_user_list)]
|
||||
|
||||
def confirm(self):
|
||||
pass
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="attendance_iot_manager_wizard_form" model="ir.ui.view">
|
||||
<field name="name">批量导入考勤用户数据弹窗</field>
|
||||
<field name="model">attendance.iot.manager.wizard</field>
|
||||
<field name="arch" type="xml">
|
||||
<form>
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="attendance_iot_id"/>
|
||||
<field name="department_ids" widget="many2many_tags"/>
|
||||
<field name="employee_ids" widget="many2many_tags"/>
|
||||
<field name="users_role_ids" widget="many2many_tags"/>
|
||||
<field name="iot_users_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
<group>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
<footer>
|
||||
<button name="confirm" string="确认" type="object" class="oe_highlight"/>
|
||||
<button string="返回" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user