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
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- hr.attendance.detail.form
- hr.attendance.detail
-
-
-
-
-
-
- 出勤详情
- hr.attendance.detail
- tree,form
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/hr_attendance_ext/views/hr_attendance_views.xml b/hr_attendance_ext/views/hr_attendance_views.xml
deleted file mode 100644
index 194e9f6b8..000000000
--- a/hr_attendance_ext/views/hr_attendance_views.xml
+++ /dev/null
@@ -1,55 +0,0 @@
-
-
-
- hr.attendance.tree.inherited
- hr.attendance
-
-
-
- false
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- hr.attendance.form.inherited
- hr.attendance
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/hr_attendance_ext/views/wx_work_views.xml b/hr_attendance_ext/views/wx_work_views.xml
deleted file mode 100644
index f863a2472..000000000
--- a/hr_attendance_ext/views/wx_work_views.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
- auth.oauth.provider.form.wwb.inherit
- auth.oauth.provider
-
-
-
-
-
-
-
-
diff --git a/yuthon_contract/__manifest__.py b/yuthon_contract/__manifest__.py
index 0f4e88540..d03f9b8d6 100644
--- a/yuthon_contract/__manifest__.py
+++ b/yuthon_contract/__manifest__.py
@@ -10,14 +10,11 @@
'category': 'Human Resources/Attendances',
'version': '0.1',
'license': 'LGPL-3',
- 'depends': ['hr_contract', 'sale_renting', 'documents'],
+ 'depends': ['hr_contract', 'yuthon_hr_employee'],
'data': [
'security/ir.model.access.csv',
- 'data/sale_order_data.xml',
- 'views/contract_menu_views.xml',
+ 'data/contract_code_data.xml',
'views/inherit_hr_contract_views.xml',
- 'views/inherit_sale_order_views.xml',
- # 'views/inherit_res_partner.xml',
],
'installable': True,
'application': True,
diff --git a/yuthon_contract/data/sale_order_data.xml b/yuthon_contract/data/contract_code_data.xml
similarity index 64%
rename from yuthon_contract/data/sale_order_data.xml
rename to yuthon_contract/data/contract_code_data.xml
index 2144c775b..691b0f896 100644
--- a/yuthon_contract/data/sale_order_data.xml
+++ b/yuthon_contract/data/contract_code_data.xml
@@ -1,8 +1,8 @@
-
+
合同编号
- sale_order_code
+ contract_code
HT%(y)s%(month)s%(day)s
3
diff --git a/yuthon_contract/models/__init__.py b/yuthon_contract/models/__init__.py
index 5b8dab813..b02a0e56e 100644
--- a/yuthon_contract/models/__init__.py
+++ b/yuthon_contract/models/__init__.py
@@ -1,3 +1 @@
from . import inherit_hr_contract
-from . import inherit_res_partner
-from . import inherit_sale_order
diff --git a/yuthon_contract/models/inherit_hr_contract.py b/yuthon_contract/models/inherit_hr_contract.py
index 5275badc5..6027f1866 100644
--- a/yuthon_contract/models/inherit_hr_contract.py
+++ b/yuthon_contract/models/inherit_hr_contract.py
@@ -8,6 +8,12 @@ class HrContract(models.Model):
_description = "员工合同拓展"
job_lv_id = fields.Many2one(related='employee_id.job_lv_id', string="职级")
- account = fields.Char("账户")
- bank = fields.Char("开户行")
+ code = fields.Char(string="编号", default=lambda self: self.env['ir.sequence'].next_by_code('contract_code'))
+ resource_calendar_id = fields.Many2one('resource.calendar', string='考勤时间表')
+
+ @api.onchange('job_lv_id')
+ def onchange_salary(self):
+ for salary in self:
+ salary.wage = salary.job_lv_id.base_wage
+ salary.name = '劳动合同'
diff --git a/yuthon_contract/models/inherit_res_partner.py b/yuthon_contract/models/inherit_res_partner.py
deleted file mode 100644
index 2e701c16d..000000000
--- a/yuthon_contract/models/inherit_res_partner.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8 -*-
-from odoo import api, fields, models, tools
-from odoo.exceptions import ValidationError
-
-
-class ResPartner(models.Model):
- _inherit = 'res.partner'
- _description = "联系人拓展"
-
- sale_order_id = fields.Many2one("sale.order", string='关联租赁合同')
- invoicing_type = fields.Char(string='开票类型')
- billable_header = fields.Char(string='开票抬头')
- address_delivery = fields.Char(string='送达地址')
- rental_purposes = fields.Char(string='出租用途')
diff --git a/yuthon_contract/models/inherit_sale_order.py b/yuthon_contract/models/inherit_sale_order.py
deleted file mode 100644
index f8749f66e..000000000
--- a/yuthon_contract/models/inherit_sale_order.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8 -*-
-from odoo import api, fields, models, tools
-from odoo.exceptions import ValidationError
-
-
-class SaleOrder(models.Model):
- _inherit = "sale.order"
- _description = "租赁合同拓展"
-
- contract_code = fields.Char(string="合同编号", default=lambda self: self.env['ir.sequence'].next_by_code('sale_order_code'))
- res_partner_ids = fields.One2many('res.partner', 'sale_order_id', string='承租方明细')
- contract_documents_ids = fields.Many2many('documents.document', string="附件")
- contract_confirm_date = fields.Datetime(string="合同签定日期")
- handover_date = fields.Datetime(string="交接日期")
-
-
-
diff --git a/yuthon_contract/views/contract_menu_views.xml b/yuthon_contract/views/contract_menu_views.xml
deleted file mode 100644
index 22cec5bfe..000000000
--- a/yuthon_contract/views/contract_menu_views.xml
+++ /dev/null
@@ -1,39 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/yuthon_contract/views/inherit_hr_contract_views.xml b/yuthon_contract/views/inherit_hr_contract_views.xml
index 35d4af66c..145b57b8e 100644
--- a/yuthon_contract/views/inherit_hr_contract_views.xml
+++ b/yuthon_contract/views/inherit_hr_contract_views.xml
@@ -6,7 +6,20 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yuthon_contract/views/inherit_res_partner.xml b/yuthon_contract/views/inherit_res_partner.xml
deleted file mode 100644
index b162da1b3..000000000
--- a/yuthon_contract/views/inherit_res_partner.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
- contract.res.partner.views.form
- res.partner
-
-
-
-
-
-
-
-
-
-
-
diff --git a/yuthon_contract/views/inherit_sale_order_views.xml b/yuthon_contract/views/inherit_sale_order_views.xml
deleted file mode 100644
index b828bbf2b..000000000
--- a/yuthon_contract/views/inherit_sale_order_views.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
- inherit.sale.renting.yuthon.form
- sale.order
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/yuthon_hr_employee/controllers/employee_banner_route.py b/yuthon_hr_employee/controllers/employee_banner_route.py
index ea14c6ca2..ffbeb94ec 100644
--- a/yuthon_hr_employee/controllers/employee_banner_route.py
+++ b/yuthon_hr_employee/controllers/employee_banner_route.py
@@ -29,3 +29,24 @@ class EmployeeBannerRoute(Controller):
"""
}
+
+ @http.route('/hr/employee/create', type='http', auth="none")
+ def hr_employee_create(self, employee_id=None, **kw):
+ values = {k: v for k, v in request.params.items()}
+ if not employee_id:
+ values['error'] = "缺少员工号"
+ response = request.render('yuthon_hr_employee.website_form_employee', values)
+ response.headers['X-Frame-Options'] = 'SAMEORIGIN'
+ response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
+ return response
+
+ values['employee_id'] = int(employee_id)
+ if request.httprequest.method == 'POST':
+ record = request.env['hr.employee'].sudo().browse(values['employee_id'])
+ if "name" in values and values.get("name", ""):
+ record.name = values.get("name", "")
+ values["message"] = "员工信息创建成功"
+ response = request.render('yuthon_hr_employee.website_form_employee', values)
+ response.headers['X-Frame-Options'] = 'SAMEORIGIN'
+ response.headers['Content-Security-Policy'] = "frame-ancestors 'self'"
+ return response
diff --git a/yuthon_hr_employee/views/website_employee_views.xml b/yuthon_hr_employee/views/website_employee_views.xml
new file mode 100644
index 000000000..0b872170c
--- /dev/null
+++ b/yuthon_hr_employee/views/website_employee_views.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+