diff --git a/hr_attendance_ext/__init__.py b/hr_attendance_ext/__init__.py deleted file mode 100644 index 80275e612..000000000 --- a/hr_attendance_ext/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import models -from . import utils - - diff --git a/hr_attendance_ext/__manifest__.py b/hr_attendance_ext/__manifest__.py deleted file mode 100644 index 9cfa8a9e3..000000000 --- a/hr_attendance_ext/__manifest__.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -{ - 'name': '出勤拓展', - 'version': '17.0.0.1', - 'category': '出勤拓展', - 'author': 'pengyuthon->ljm', - 'sequence': 190, - 'description': """ -功能介绍 -============== - -* 新增 定时任务 和 抓取企业微信的出勤数据的核心模块 -* 新增 企业微信出勤记录的原始数据 -* 新增 企业微信出勤记录和出勤模块的融合数据 -* 新增 月度汇总明细结果表 -* 新增 月度汇总明细表,附加权限 -* 新增 月度汇总向导,年份维护表 -* 新增 员工出勤以及判断是否应该出勤的逻辑等 - """, - 'depends': ['hr_attendance', 'yuthon_wechat'], - 'data': [ - 'security/ir.model.access.csv', - 'data/hr_attendance_time.xml', - 'views/hr_attendance_views.xml', - 'views/hr_attendance_detail_views.xml', - 'views/wx_work_views.xml', - ], - 'application': True, - 'installable': True, - 'auto_install': False, -} diff --git a/hr_attendance_ext/data/hr_attendance_time.xml b/hr_attendance_ext/data/hr_attendance_time.xml deleted file mode 100644 index 08ec7083c..000000000 --- a/hr_attendance_ext/data/hr_attendance_time.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - 定时任务:定时抓取上下班打卡信息 - - code - model.get_hr_attendance_time() - - - 120 - months - - -1 - - - - \ No newline at end of file diff --git a/hr_attendance_ext/models/__init__.py b/hr_attendance_ext/models/__init__.py deleted file mode 100644 index 8bfa2eeed..000000000 --- a/hr_attendance_ext/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import hr_attendance -from . import hr_attendance_detail -from . import wx_work diff --git a/hr_attendance_ext/models/hr_attendance.py b/hr_attendance_ext/models/hr_attendance.py deleted file mode 100644 index a490ecb1a..000000000 --- a/hr_attendance_ext/models/hr_attendance.py +++ /dev/null @@ -1,342 +0,0 @@ -# -*- coding: utf-8 -*- -import pytz -from datetime import datetime, timedelta -from odoo import api, fields, models, tools, _ - - -class HrAttendance(models.Model): - _inherit = "hr.attendance" - _order = 'current_data desc, check_in desc' - - userid = fields.Char(string='userid') - groupname = fields.Char(string='打卡规则名称') - current_data = fields.Date(string='打卡日期', tracking=True) - sch_check_in_time = fields.Datetime(string='上班正常时间') - sch_check_out_time = fields.Datetime(string='下班正常时间') - sch_check_noon_time = fields.Datetime(string='中午正常时间') - check_noon = fields.Datetime(string='中午打卡', tracking=True) - check_in = fields.Datetime(string="上班打卡", required=False, default=False, tracking=True) - check_out = fields.Datetime(string="下班打卡", tracking=True) - attendance_type = fields.Selection([('wx_work', '企业微信')], readonly=True, string='来源') - # field_work_check = fields.Datetime(string="外勤打卡") - - attendance_detail_ids = fields.One2many(string="打卡详情", comodel_name='hr.attendance.detail', inverse_name='hr_attendance_id') - replacement_approval_ids = fields.One2many(string="补卡详情", comodel_name='approval2.request', compute='_compute_replacement_approval_ids') - go_out_approval_ids = fields.One2many(string="外出详情", comodel_name='approval2.request', compute='_compute_go_out_approval_ids') - work_overtime_approval_ids = fields.One2many(string="加班详情", comodel_name='approval2.request', compute='_compute_work_overtime_approval_ids') - hr_leave_approval_ids = fields.One2many(string="请假详情", comodel_name='hr.leave', compute='_compute_hr_leave_approval_ids') - - def action_show_details(self): - self.ensure_one() - view = self.env.ref("hr_attendance_ext.view_hr_attendance_detail_tree") - form_view = self.env.ref("hr_attendance_ext.view_hr_attendance_detail_form") - return { - "name": _("出勤详情"), - "type": "ir.actions.act_window", - "view_mode": "tree,form", - "res_model": "hr.attendance.detail", - "views": [(view.id, "tree"), (form_view.id, "form")], - "view_id": view.id, - "target": "current", - "res_id": self.id, - "domain": [('hr_attendance_id', '=', self.id)], - "context": {'hr_attendance_id': self.id} - } - - def replacement_approval_action_show_details(self): - self.ensure_one() - view = self.env.ref("approvals2.approval_request_view_tree") - form_view = self.env.ref("approvals2.approval_request_view_form") - if self.current_data: - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", self.employee_id.user_id.id), - ("approvals_request_type", "=", "card_replacement"), - ("replacement_date", "=", self.current_data), - # ("state", "=", "approved") - ]) - else: - approval_request = self.env["approval2.request"].sudo().search([('id', '<', 0)]) - return { - "name": _("补卡详情"), - "type": "ir.actions.act_window", - "view_mode": "tree,form", - "res_model": "approval2.request", - "views": [(view.id, "tree"), (form_view.id, "form")], - "view_id": view.id, - "target": "current", - "res_id": self.id, - "domain": [('id', 'in', approval_request.ids)], - "context": {'id': approval_request.ids} - } - - def go_out_approval_action_show_details(self): - self.ensure_one() - view = self.env.ref("approvals2.approval_request_view_tree") - form_view = self.env.ref("approvals2.approval_request_view_form") - if self.current_data: - date_start = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", self.employee_id.user_id.id), - ("approvals_request_type", "=", "go_out_approval"), - ("approval_end_time", ">=", date_start), - ("approval_start_time", "<=", date_end), - # ("state", "=", "approved") - ]) - else: - approval_request = self.env["approval2.request"].sudo().search([('id', '<', 0)]) - - return { - "name": _("外出详情"), - "type": "ir.actions.act_window", - "view_mode": "tree,form", - "res_model": "approval2.request", - "views": [(view.id, "tree"), (form_view.id, "form")], - "view_id": view.id, - "target": "current", - "res_id": self.id, - "domain": [('id', 'in', approval_request.ids)], - "context": {'id': approval_request.ids} - } - - def work_overtime_action_show_details(self): - self.ensure_one() - view = self.env.ref("approvals2.approval_request_view_tree") - form_view = self.env.ref("approvals2.approval_request_view_form") - if self.current_data: - date_start = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", self.employee_id.user_id.id), - ("approvals_request_type", "=", "work_overtime"), - ("approval_end_time", ">=", date_start), - ("approval_start_time", "<=", date_end), - # ("state", "=", "approved") - ]) - else: - approval_request = self.env["approval2.request"].sudo().search([('id', '<', 0)]) - return { - "name": _("加班详情"), - "type": "ir.actions.act_window", - "view_mode": "tree,form", - "res_model": "approval2.request", - "views": [(view.id, "tree"), (form_view.id, "form")], - "view_id": view.id, - "target": "current", - "res_id": self.id, - "domain": [('id', 'in', approval_request.ids)], - "context": {'id': approval_request.ids} - } - - def hr_leave_action_show_details(self): - self.ensure_one() - view = self.env.ref("hr_holidays.hr_leave_view_tree_my") - form_view = self.env.ref("hr_holidays.hr_leave_view_form") - if self.current_data: - date_start = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string(fields.Date.to_date(self.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["hr.leave"].sudo().search([ - ('employee_id', '=', self.employee_id.id), - ('date_from', '<=', date_end), - ('date_to', '>=', date_start), - # ("state", "=", "validate") - ]) - else: - approval_request = self.env["hr.leave"].sudo().search([('id', '<', 0)]) - - return { - "name": _("请假详情"), - "type": "ir.actions.act_window", - "view_mode": "tree,form", - "res_model": "hr.leave", - "views": [(view.id, "tree"), (form_view.id, "form")], - "view_id": view.id, - "target": "current", - "res_id": self.id, - "domain": [('id', 'in', approval_request.ids)], - "context": {'id': approval_request.ids, 'short_name': 1, 'search_default_active_time_off': 1} - } - - @api.constrains('check_in', 'check_out', 'employee_id') - def _check_validity(self): - for attendance in self: - if attendance.attendance_type != 'wx_work': - super(HrAttendance, attendance)._check_validity() - - # 审批补卡详情 approval2 - def _compute_replacement_approval_ids(self): - for record in self: - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", record.employee_id.user_id.id), - ("approvals_request_type", "=", "card_replacement"), - ("replacement_date", "=", record.current_data), - # ("state", "=", "approved") - ]) - record.replacement_approval_ids = approval_request.ids if approval_request.ids else None - - # 审批请假详情 hr.leave - def _compute_hr_leave_approval_ids(self): - for record in self: - record.hr_leave_approval_ids = None - if record.current_data: - date_start = fields.Datetime.from_string( - fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string( - fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["hr.leave"].sudo().search([ - ('employee_id', '=', record.employee_id.id), - ('date_from', '<=', date_end), - ('date_to', '>=', date_start), - # ("state", "=", "validate") - ]) - record.hr_leave_approval_ids = approval_request.ids if approval_request.ids else None - - # 审批外出详情 approval2 - def _compute_go_out_approval_ids(self): - for record in self: - record.go_out_approval_ids = None - if record.current_data: - date_start = fields.Datetime.from_string(fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string(fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", record.employee_id.user_id.id), - ("approvals_request_type", "=", "go_out_approval"), - ("approval_end_time", ">=", date_start), - ("approval_start_time", "<=", date_end), - # ("state", "=", "approved") - ]) - record.go_out_approval_ids = approval_request.ids if approval_request.ids else None - - # 审批加班详情 approval2 - def _compute_work_overtime_approval_ids(self): - for record in self: - record.work_overtime_approval_ids = None - if record.current_data: - date_start = fields.Datetime.from_string( - fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 00:00:00')) - timedelta(hours=8) - date_end = fields.Datetime.from_string( - fields.Date.to_date(record.current_data).strftime('%Y-%m-%d 23:59:59')) - timedelta(hours=8) - - approval_request = self.env["approval2.request"].sudo().search([ - ("request_owner_id", "=", record.employee_id.user_id.id), - ("approvals_request_type", "=", "work_overtime"), - ("approval_end_time", ">=", date_start), - ("approval_start_time", "<=", date_end), - # ("state", "=", "approved") - ]) - record.work_overtime_approval_ids = approval_request.ids if approval_request.ids else None - - def get_work_time(self, attendance_data, current_date, tz): - """ - 当前时间的工作时间段 - :param attendance_data: - :param current_date: - :param tz: - :return: - """ - work_time_list = [] - for work_time in attendance_data: - work_time_list.append(self.time_to_utc_time(self.str_to_time(current_date, work_time.hour_from), tz)) - work_time_list.append(self.time_to_utc_time(self.str_to_time(current_date, work_time.hour_to), tz)) - work_time_sorted = sorted(work_time_list) - go_from, go_to, off_from, off_to = work_time_sorted - return go_from, go_to, off_from, off_to, current_date - - @api.depends('check_in', 'sch_check_in_time') - def is_come_late(self): - self.ensure_one() - if self.check_in and self.sch_check_in_time and self.check_in > self.sch_check_in_time: - return True - return False - - def get_employee_work_time(self, employee, current_week, current_date, tz): - """ - 获取员工的当天是否应该出勤以及出勤时间 - :param employee: 员工 - :param current_week: 当天周 - :param current_date: 当前天 - :param tz: 时区 - :return: 出勤时间 - """ - # 获取员工的工作信息 - resource_calendar = employee.resource_calendar_id - # 获取工作表 - attendance_ids = resource_calendar.attendance_ids - # 获取公休息日表 - global_leave = resource_calendar.global_leave_ids - # 有没有工作规则 - if not attendance_ids: - return False - # 是不是工作时间 - attendance_data = resource_calendar.attendance_ids.filtered(lambda s: s.dayofweek == str(current_week)) - if not attendance_data: - return False - # 获取工作时间 - work_time = self.get_work_time(attendance_data, current_date, tz) - if not work_time: - return False - go_from, go_to, off_from, off_to, current_date = work_time - # 早上和下午上班 判断当天 是不是公休日 - if self.get_leave_day(global_leave, go_from, tz) and self.get_leave_day(global_leave, off_from, tz): - return False - return go_from, go_to, off_from, off_to, current_date - - def get_leave_day(self, global_leave, current_time, tz): - """ - 判断是否是公休日 - :param global_leave: - :param current_time: - :param tz: - :return: - """ - if not global_leave: - return False - for leave in global_leave: - if self.utc_time_to_tz(tz, leave.date_from) <= current_time.strftime("%Y-%m-%d %H:%M:%S") <= self.utc_time_to_tz(tz, leave.date_to): - return True - return False - - @staticmethod - def utc_time_to_tz(tz, chin): - """ - utc时间的时区转换tz时间 - :param tz: 时区 - :param chin: 时间 - :return: 转换时间 - """ - time_timezone = pytz.timezone('utc').localize(chin, is_dst=False) - time_to_utc = time_timezone.astimezone(pytz.timezone(tz)) - utc_time_str = time_to_utc.strftime("%Y-%m-%d %H:%M:%S") - return utc_time_str - - @staticmethod - def time_to_utc_time(data, tz): - """ - 本地时间转换成tz时间 - :param data: 时间 - :param tz: 时区 - :return: 转换时间 - """ - time_timezone = pytz.timezone(tz).localize(data, is_dst=False) - time_to_utc = time_timezone.astimezone(pytz.timezone("utc")) - utc_time_str = time_to_utc.strftime("%Y-%m-%d %H:%M:%S") - utc_time = datetime.strptime(utc_time_str, "%Y-%m-%d %H:%M:%S") - return utc_time - - @staticmethod - def str_to_time(current_data, data): - """ - %d:%d 的 str类型的时间转换成时间 - :param current_data: 当前日期 - :param data: str类型时间 - :return: 转换后的时间 - """ - from_time = "%d:%d" % (int(data), round((data - int(data)) * 60)) - from_times = str(current_data) + " " + from_time - from_ti = datetime.strptime(from_times, "%Y-%m-%d %H:%M") - return from_ti diff --git a/hr_attendance_ext/models/hr_attendance_detail.py b/hr_attendance_ext/models/hr_attendance_detail.py deleted file mode 100644 index 6eba2507b..000000000 --- a/hr_attendance_ext/models/hr_attendance_detail.py +++ /dev/null @@ -1,318 +0,0 @@ -# -*- coding: utf-8 -*- -import os -import base64 -import datetime -import logging -import traceback -from odoo import api, fields, models, tools, _ -from ...hr_attendance_ext.utils.common import utils_list_page - -_logger = logging.getLogger(__name__) - - -class HrAttendanceDetailImage(models.Model): - _name = "hr.attendance.detail.image" - _description = "打卡原始抓取数据图片" - - attendance_detail_id = fields.Many2one('hr.attendance.detail', string="原始打卡记录", ondelete="cascade", readonly=True) - media_url = fields.Char(string='附件地址') - media_file = fields.Binary(string='附件文件', compute="_compute_media_file") - - @api.depends('media_url') - def _compute_media_file(self): - for record in self: - record.media_file = None - wx_work_auth = self.env['auth.oauth.provider'].search([('name', '=', 'Wechat Card')], limit=1) - if record.media_url: - image_path = os.path.join(wx_work_auth.wx_work_image_path, record.media_url) - record.media_file = base64.b64encode(open(image_path, 'rb').read()) if image_path else False - - -class HrAttendanceDetail(models.Model): - _name = "hr.attendance.detail" - _description = "打卡原始抓取数据" - _order = 'checkin_time desc' - - name = fields.Char(related='employee_id.name') - employee_id = fields.Many2one('hr.employee', string="员工", readonly=True) - department_id = fields.Many2one('hr.department', string="部门", related="employee_id.department_id", readonly=True) - userid = fields.Char(string='userid') - groupname = fields.Char(string='打卡规则名称') - checkin_type = fields.Char(string='打卡类型') - exception_type = fields.Char(string='异常类型') - checkin_time = fields.Datetime(string='打卡时间') - location_title = fields.Char(string='打卡地点') - location_detail = fields.Char(string='打卡地点详情') - wifiname = fields.Char(string='打卡wifi名称') - notes = fields.Char(string='打卡备注') - wifimac = fields.Char(string='打卡的MAC地址') - mediaids = fields.Char(string='打卡的附件media_id') - lat = fields.Integer(string='位置打卡地点纬度') - lng = fields.Integer(string='位置打卡地点经度') - deviceid = fields.Char(string='打卡设备id') - sch_checkin_time = fields.Datetime(string='标准打卡时间') - groupid = fields.Integer(string='规则id') - schedule_id = fields.Char(string='班次id') - timeline_id = fields.Integer(string='时段id') - - attendance_detail_image_ids = fields.One2many(string="打卡记录图片", comodel_name='hr.attendance.detail.image', inverse_name='attendance_detail_id') - hr_attendance_id = fields.Many2one('hr.attendance', string="出勤记录", ondelete="cascade", readonly=True) - - def action_show_details(self): - self.ensure_one() - view = self.env.ref("hr_attendance_ext.view_hr_attendance_detail_image_tree") - return { - "name": _("图片详情"), - "type": "ir.actions.act_window", - "view_mode": "form", - "res_model": "hr.attendance.detail.image", - "views": [(view.id, "tree")], - "view_id": view.id, - "target": "new", - "res_id": self.id, - "domain": [('attendance_detail_id', '=', self.id)], - "context": {'default_attendance_detail_id': self.id} - } - - def _get_wx_attendance_user_list(self): - """ - 获取企业微信的关联用户 - :return: 用户列表 - """ - # userid 从用户列表里获取企业微信的用户 - user_obj = self.env['res.users'].search([]) - # 暂时先用for循环,如果后期加大频率,需改成并发 - user_list = [] - for user in user_obj: - if user.oauth_uid: - user_list.append(user.oauth_uid) - return user_list - - @staticmethod - def _get_wx_attendance_data(userid_limit): - """ - 获取企业微信的打卡信息 - :param wx_work_checkin_api: 企业微信api - :param userid_limit: 用户列表 - :return: - """ - # 先按照天抓取,时间戳 - now_time = datetime.datetime.now() + datetime.timedelta(hours=8) - start_time = now_time - datetime.timedelta(days=30) - start_time_stamp = int(start_time.timestamp()) - end_time_stamp = int(now_time.timestamp()) - # 整理请求信息 - data = { - "opencheckindatatype": 3, - "starttime": start_time_stamp, - "endtime": end_time_stamp, - "need_photo": 1, - "useridlist": userid_limit - } - # 重写一个类处理result获取想要的结果 - - return data - - @staticmethod - def _check_wx_attendance_data(response, userid_limit): - """ - 检查请求返回的信息是否正确 - :param response: 请求返回信息 - :param userid_limit: 用户列表 - :return: 检查结果 - """ - # 对返回信息进行整理 - if response.status_code != 200: - _logger.error('抓取出勤数据请求失败,异常:{0},用户列表{1}'.format(str(response.json()), str(userid_limit))) - return [] - result = response.json() - if result.get('errcode') != 0: - _logger.error('获取外出打卡数据失败,异常:{0},用户列表{1}'.format(str(result.get('errmsg')), str(userid_limit))) - return [] - checkin_data = result.get('checkindata', []) - if not checkin_data: - _logger.error('没有找到外出打卡记录,异常:{0},用户列表{1}'.format(str(response.json()), str(userid_limit))) - return [] - return checkin_data - - def _check_wx_attendance_time(self, data, userid_limit): - """ - 检查保存之前的数据是否准确,并且分析出想要的打卡时间 - :param data: 原始数据 - :param userid_limit: 用户列表 - :return: 分析后的打卡数据 - """ - userid = data.get("userid", "") - checkin_time_stamp = data.get("checkin_time", "") - sch_checkin_time_stamp = data.get("sch_checkin_time", None) - if not userid or not checkin_time_stamp: - _logger.error('没有抓取到用户出勤信息,异常:{0},用户列表{1}'.format(str(data), str(userid_limit))) - return False - checkin_time = datetime.datetime.fromtimestamp(checkin_time_stamp) if checkin_time_stamp else None - sch_checkin_time = datetime.datetime.fromtimestamp(sch_checkin_time_stamp) if sch_checkin_time_stamp else None - return checkin_time, sch_checkin_time - - def _check_wx_attendance_media(self, data): - """ - 分析返回来的数据获取图片原图 - :param data: 原始数据 - :param userid_limit: 用户列表 - :return: 获取后的图片 - """ - media_list = [] - userid = data.get("userid", "") - mediaids_list = data.get("mediaids", []) - if not mediaids_list: - return media_list - - for media_id in mediaids_list: - data = { - "media_id": media_id - } - try: - result, response = self.sudo().env["wecom.apps"].sync_media_get(data) - if result: - filename = response.headers['Content-Disposition'].split('; ')[2].replace('filename=', '').replace('"', '') - wx_work_auth = self.env['auth.oauth.provider'].search([('name', '=', 'Wechat Card')], limit=1) - wx_work_image = os.path.join(wx_work_auth.wx_work_image_path, str(filename)) - with open(wx_work_image, 'wb') as f: - f.write(response.content) - media_list.append((0, 0, { - 'media_url': filename, - })) - else: - _logger.error('检查保存之前的数据分析出想要的数据失败,异常:{0},数据信息{1}'.format(str(response), str(data))) - except Exception as e: - _logger.error('请求原始图片失败,异常:{0},数据信息{1},异常详情:{2}'.format(str(e), str(data), str(traceback.format_exc()))) - - return media_list - - def _save_wx_attendance_detail_data(self, data, checkin_time, sch_checkin_time): - """ - 保存相关的企业微信的原始打卡信息 - :param data: 请求返回后的处理信息 - :param checkin_time: 打卡时间 - :param sch_checkin_time: 应打卡时间 - :param media_list: 图片列表 - :return: 保存结果 - """ - userid = data.get("userid", "") - # 查询是否存在数据 - if self.env['hr.attendance.detail'].sudo().search_count([("userid", "=", userid), ("checkin_time", "=", checkin_time)]) <= 0: - res_user_obj = self.env['res.users'].sudo().search([("oauth_uid", "=", userid)], limit=1) - hr_employee_obj = self.env['hr.employee'].sudo().search([("user_id", "=", res_user_obj.id)], limit=1) - if not res_user_obj or not hr_employee_obj: - _logger.error('检查保存之前的数据异常,异常:无关联的员工数据,数据信息{0}'.format(str(data))) - return False - attendance_detail_obj = self.env['hr.attendance.detail'].sudo().create({ - 'employee_id': hr_employee_obj.id, - "userid": userid, - "groupname": data.get("groupname", ""), - "checkin_type": data.get("checkin_type", ""), - "exception_type": data.get("exception_type", ""), - "checkin_time": checkin_time, - "location_title": data.get("location_title", ""), - "location_detail": data.get("location_detail", ""), - "wifiname": data.get("wifiname", ""), - "notes": data.get("notes", ""), - "wifimac": data.get("wifimac", ""), - "mediaids": str(data.get("mediaids", "")), - "lat": data.get("lat", 0), - "lng": data.get("lng", 0), - "deviceid": data.get("deviceid", ""), - "sch_checkin_time": sch_checkin_time, - "groupid": data.get("groupid", 0), - "schedule_id": data.get("schedule_id", ""), - "timeline_id": data.get("timeline_id", 0), - # "attendance_detail_image_ids": media_list - }) - - # 分析返回来的数据获取图片原图 - try: - media_list = self._check_wx_attendance_media(data) - attendance_detail_obj.attendance_detail_image_ids = media_list - except Exception as e: - _logger.error('检查保存之前的数据分析出想要的数据失败,异常:{0},数据信息{1},异常详情:{2}'.format(str(e), str(data), str(traceback.format_exc()))) - - userid = data.get("userid", "") - # 区分当天的上班,下班,中午打卡,合并一个 - checkin_time_now = checkin_time + datetime.timedelta(hours=8) if checkin_time else None - sch_checkin_time_now = sch_checkin_time + datetime.timedelta(hours=8) if sch_checkin_time else None - current_data = (sch_checkin_time + datetime.timedelta(hours=8)).strftime("%Y-%m-%d") if sch_checkin_time_now else checkin_time_now.strftime("%Y-%m-%d") - # 同步整理当天出勤数据 - attendance_obj = self.env['hr.attendance'].search([("userid", "=", userid), ("current_data", "=", current_data)], limit=1) - if not attendance_obj: - res_user_obj = self.env['res.users'].search([("oauth_uid", "=", userid)], limit=1) - hr_employee_obj = self.env['hr.employee'].sudo().search([("user_id", "=", res_user_obj.id)], limit=1) - attendance_obj = self.env['hr.attendance'].sudo().create({ - 'employee_id': hr_employee_obj.id, - "userid": userid, - "current_data": current_data, - "attendance_type": "wx_work" - }) - # 赋值企业微信打卡数据 - attendance_obj.attendance_type = "wx_work" - - checkin_type = data.get("checkin_type", "") - exception_type = data.get("exception_type", "") - if sch_checkin_time_now and str(sch_checkin_time_now.strftime("%H")) == "13" and checkin_type == "上班打卡" and exception_type != "未打卡": - attendance_obj.sudo().write({ - "sch_check_noon_time": sch_checkin_time, - "check_noon": checkin_time - }) - elif checkin_type == "上班打卡" and exception_type != "未打卡": - attendance_obj.sudo().write({ - "sch_check_in_time": sch_checkin_time, - "check_in": checkin_time - }) - elif checkin_type == "下班打卡" and exception_type != "未打卡": - attendance_obj.sudo().write({ - "sch_check_out_time": sch_checkin_time, - "check_out": checkin_time - }) - # elif checkin_type == "外出打卡": - # attendance_obj.sudo().write({ - # "field_work_check": checkin_time, - # }) - # 进行关联 - attendance_detail_obj.hr_attendance_id = attendance_obj.id - return True - - def get_hr_attendance_time(self): - """ - 获取企业微信打卡数据的核心逻辑 - :return: 处理结果 - """ - # 从用户列表里获取企业微信的用户 - user_list = self._get_wx_attendance_user_list() - # 企业微信获取打卡数据,要求用户分组100,分批获取打卡记录 - for userid_limit in utils_list_page(user_list, 100): - # 获取企业微信的打卡信息 - try: - attendance_data = self._get_wx_attendance_data(userid_limit) - attendance_result, response = self.sudo().env["wecom.apps"].sync_checkin_data(attendance_data) - except Exception as e: - _logger.error('获取企业微信的打卡信息失败,异常:{0},用户列表{1},异常详情:{2}'.format(str(e), str(userid_limit), str(traceback.format_exc()))) - continue - # 检查请求返回的信息是否正确 - try: - checkin_data = self._check_wx_attendance_data(response, userid_limit) - except Exception as e: - _logger.error('检查请求返回的信息失败,异常:{0},用户列表{1},异常详情:{2}'.format(str(e), str(userid_limit), str(traceback.format_exc()))) - continue - # 将原始数据存储 - for data in checkin_data: - # 检查保存之前的数据是否准确,并且分析出想要的打卡时间 - try: - checkin_time, sch_checkin_time = self._check_wx_attendance_time(data, userid_limit) - except Exception as e: - _logger.error('检查保存之前的数据分析出想要的数据失败,异常:{0},数据信息{1},异常详情:{2}'.format(str(e), str(data), str(traceback.format_exc()))) - continue - # 保存相关的企业微信的原始打卡信息 - try: - self._save_wx_attendance_detail_data(data, checkin_time, sch_checkin_time) - except Exception as e: - _logger.error('保存相关的企业微信的原始打卡信息失败,异常:{0},数据信息{1},异常详情:{2}'.format(str(e), str(data), str(traceback.format_exc()))) - return True - - diff --git a/hr_attendance_ext/models/wx_work.py b/hr_attendance_ext/models/wx_work.py deleted file mode 100644 index 24dead8d8..000000000 --- a/hr_attendance_ext/models/wx_work.py +++ /dev/null @@ -1,12 +0,0 @@ -import logging -import json -from datetime import datetime, timedelta -from odoo import models, fields - -_logger = logging.getLogger(__name__) - - -class AuthOAuthProvider(models.Model): - _inherit = "auth.oauth.provider" - - wx_work_image_path = fields.Char(string="企业微信图片保存地址") diff --git a/hr_attendance_ext/security/ir.model.access.csv b/hr_attendance_ext/security/ir.model.access.csv deleted file mode 100644 index cf875294b..000000000 --- a/hr_attendance_ext/security/ir.model.access.csv +++ /dev/null @@ -1,7 +0,0 @@ -id,name,model_id/id,group_id/id,perm_read,perm_write,perm_create,perm_unlink - -access_hr_attendance_detail_user,hr.attendance.detail.user,model_hr_attendance_detail,base.group_user,1,0,0,0 -access_hr_attendance_detail_manager,hr.attendance.detail.manager,model_hr_attendance_detail,base.group_user,1,1,1,1 - -access_hr_attendance_detail_image_user,hr.attendance.detail.image.user,model_hr_attendance_detail_image,base.group_user,1,0,0,0 -access_hr_attendance_detail_image_manager,hr.attendance.detail.image.manager,model_hr_attendance_detail_image,base.group_user,1,1,1,1 diff --git a/hr_attendance_ext/static/description/icon.png b/hr_attendance_ext/static/description/icon.png deleted file mode 100644 index 5cca7ac8b..000000000 Binary files a/hr_attendance_ext/static/description/icon.png and /dev/null differ diff --git a/hr_attendance_ext/utils/__init__.py b/hr_attendance_ext/utils/__init__.py deleted file mode 100644 index aa9c3f5d9..000000000 --- a/hr_attendance_ext/utils/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import common diff --git a/hr_attendance_ext/utils/common.py b/hr_attendance_ext/utils/common.py deleted file mode 100644 index c58026e43..000000000 --- a/hr_attendance_ext/utils/common.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -import os - -def utils_list_page(data, limit): - """ - 列表平分 - :param data: list - :param limit: 每组多少个 - :return: 平分结果 - """ - for start in range(0, len(data), limit): - if start + limit > len(data): - end = len(data) - else: - end = start + limit - yield data[start:end] - - -def os_path_join(path, filename): - """ - 默认传入的是debian的路径 /xx/xx/xx/转换成多系统兼容 - :param path: - :param filename: - :return: - """ - image_path_list = path.split('/') - image_path = "" - for path in image_path_list: - image_path = os.path.join(str(image_path), str(path)) - image = os.path.join(image_path, filename) - return image diff --git a/hr_attendance_ext/views/hr_attendance_detail_views.xml b/hr_attendance_ext/views/hr_attendance_detail_views.xml deleted file mode 100644 index f1c3b70f6..000000000 --- a/hr_attendance_ext/views/hr_attendance_detail_views.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - hr.attendance.detail.image.tree - hr.attendance.detail.image - - - - - - - - - - hr.attendance.detail.image.form - hr.attendance.detail.image - -
- - - - - - -
-
-
- - - hr.attendance.detail.tree - hr.attendance.detail - - - - - - - - - - - - -