117 lines
6.2 KiB
Python
117 lines
6.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
import base64
|
|
|
|
from odoo import models, fields, api, exceptions, _, tools
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class HrEmployee(models.Model):
|
|
_inherit = "hr.employee"
|
|
_description = "考勤机和用户信息的绑定"
|
|
|
|
attendance_iot_ids = fields.One2many('attendance.iot.user', 'employee_id', string="考勤机信息")
|
|
# 图片需要压缩
|
|
# 1、下发的人脸图片压缩成 jpg 格式,大小不超过 300 kb ( 尺寸不超过 1280 * 720 ) ,我们正常是 50 kb 左右
|
|
# 2、把压缩后的图片转换成完整的 Base64 格式(必须加上前缀 “data:image/jpeg;base64,”)
|
|
# 3、把完整的 Base64 图片使用 UrlEncode 方法进行 UTF-8 格式的编码。
|
|
|
|
avatar_image = fields.Image(string='头像')
|
|
|
|
compress_image = fields.Image(string='压缩后的图片', compute='_compute_compress_image')
|
|
|
|
@api.depends('avatar_image', 'image_1920')
|
|
def _compute_compress_image(self):
|
|
for record in self:
|
|
# 优先用 avatar_image(设备上传的),没有则用 image_1920(表单上传的)
|
|
source = record.avatar_image or record.image_1920
|
|
if source:
|
|
origin_300 = 400 * 1024
|
|
if len(source) < origin_300:
|
|
record.compress_image = source
|
|
else:
|
|
image = base64.b64decode(source)
|
|
compress_image = tools.image_process(image, size=(1280, 720),
|
|
output_format='JPEG', verify_resolution=True)
|
|
if len(compress_image) <= origin_300:
|
|
record.compress_image = base64.b64encode(compress_image)
|
|
else:
|
|
quality = 95
|
|
image_value = tools.image_process(image, size=(1280, 720), output_format='JPEG',
|
|
quality=quality, verify_resolution=True)
|
|
while quality > 10 and len(image_value) > origin_300:
|
|
image_value = tools.image_process(base64.b64decode(image), size=(1280, 720), output_format='JPEG',
|
|
quality=quality, verify_resolution=True)
|
|
quality -= 5
|
|
record.compress_image = base64.b64encode(image_value)
|
|
else:
|
|
record.compress_image = False
|
|
|
|
def write(self, values):
|
|
res = super(HrEmployee,self).write(values)
|
|
# 更新数据 - 只要姓名或任意照片字段有变化就同步
|
|
if 'name' in values or 'avatar_image' in values or 'image_1920' in values:
|
|
self.attendance_iot_ids.action_update_iot_info()
|
|
return res
|
|
|
|
def _get_attendance_iot_copy_action(self, sync_to_all=False):
|
|
# 打开「拷贝考勤机记录」弹窗(供员工列表按钮调用,支持多选员工)
|
|
# sync_to_all=True 时弹窗默认带出选中员工公司的全部考勤机
|
|
# 取选中员工的 id 列表(列表可勾选多条)
|
|
employee_ids = self.ids
|
|
# 源公司:取第一个选中员工的公司
|
|
company = self[:1].company_id
|
|
# 返回弹窗动作,context 带上员工、公司、是否同步全部设备
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': '拷贝考勤机记录',
|
|
'res_model': 'attendance.iot.import.user.wizard',
|
|
'view_mode': 'form',
|
|
'view_id': self.env.ref('attendance_iot.attendance_iot_import_user_wizard_form').id,
|
|
'target': 'new',
|
|
'context': {
|
|
# 选中要同步的员工 id 列表
|
|
'default_employee_ids': employee_ids,
|
|
# 选中员工所在公司(决定带出哪家公司的考勤机)
|
|
'default_company_id': company.id,
|
|
# 是否默认带出公司全部考勤机
|
|
'default_sync_to_all': sync_to_all,
|
|
},
|
|
}
|
|
|
|
def action_attendance_iot_batch_copy(self):
|
|
# 「批量拷贝」按钮:打开弹窗,由用户手动勾选要同步到哪些考勤机
|
|
# 没勾选任何员工时直接提示,避免进入向导后 default_get 空记录报错
|
|
if not self:
|
|
raise UserError(_('请先在员工列表中勾选要同步的员工,再点击「批量拷贝」。'))
|
|
return self._get_attendance_iot_copy_action(sync_to_all=False)
|
|
|
|
def action_attendance_iot_sync_all(self):
|
|
# 「考勤机更新」按钮:直接把选中员工的人脸数据同步到其关联的全部设备
|
|
# 逻辑与 attendance.iot.user.tree 的 action_batch_sync_to_all_devices 一致(直接推送,不走弹窗)
|
|
if not self:
|
|
raise UserError(_('请先在员工列表中勾选要同步的员工,再点击「考勤机更新」。'))
|
|
# 收集所有选中员工的设备绑定记录(attention: attendance_iot_ids 是绑定记录的一对多)
|
|
bindings = self.env['attendance.iot.user'].search([('employee_id', 'in', self.ids)])
|
|
if bindings:
|
|
# action_update_iot_info 内部会跳过 iot_sync 上下文,这里显式传 False 确保推送
|
|
bindings.with_context(iot_sync=False).action_update_iot_info()
|
|
# 在每位员工的消息面板(花名册 form 右侧 chatter)生成同步日志
|
|
# hr.employee 继承 mail.thread,可直接 message_post;用内部备注子类型,不触发邮件
|
|
for employee in self:
|
|
emp_bindings = bindings.filtered(lambda b: b.employee_id == employee)
|
|
iot_names = '、'.join(emp_bindings.mapped('attendance_iot_id.name')) or _('(无)')
|
|
employee.message_post(
|
|
body=_('考勤机记录已同步到关联的全部设备:%s') % iot_names,
|
|
subtype_xmlid='mail.mt_note',
|
|
)
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': '同步完成',
|
|
'message': _('已向 %s 名员工关联的全部设备推送同步指令') % len(self),
|
|
'sticky': False,
|
|
'type': 'success',
|
|
}
|
|
}
|