添加功能
This commit is contained in:
@@ -4,6 +4,8 @@ import calendar
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.tools.float_utils import float_round
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
from datetime import timedelta
|
||||
from datetime import date
|
||||
|
||||
|
||||
@@ -84,7 +86,7 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
department_id = fields.Many2one('hr.department', string="部门", related="employee_id.department_id", readonly=True, store=True)
|
||||
# userid = fields.Char(string='userid')
|
||||
attendance_year_id = fields.Many2one('hr.attendance.year', string="统计年份")
|
||||
current_year = fields.Char(string='统计年份', related="attendance_year_id.year")
|
||||
current_year = fields.Char(string='统计年份', related="attendance_year_id.year", store=True)
|
||||
current_month = fields.Selection([
|
||||
('1', '1月份'),
|
||||
('2', '2月份'),
|
||||
@@ -98,7 +100,7 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
('10', '10月份'),
|
||||
('11', '11月份'),
|
||||
('12', '12月份')
|
||||
], string="考勤月份")
|
||||
], string="考勤月份", store=True)
|
||||
required_attendance_days = fields.Integer(string="应出勤天数")
|
||||
actual_attendance_days = fields.Integer(string="实际出勤天数")
|
||||
compensatory_leave = fields.Integer(string="调休")
|
||||
@@ -318,24 +320,18 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
is_no_card += 1
|
||||
return is_late_card, is_early_card, is_no_card
|
||||
|
||||
def _hr_attendance_month_detail_action(self):
|
||||
def _hr_attendance_month_detail_action(self, processed_employee_ids):
|
||||
"""
|
||||
返回视图 view_hr_attendance_month_detail_tree
|
||||
:return: ir.actions.act_window 视图 view_hr_attendance_month_detail_tree
|
||||
"""
|
||||
self.ensure_one()
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'hr.attendance.month.detail',
|
||||
'name': "考勤统计",
|
||||
'view_type': 'tree',
|
||||
'view_mode': 'list,form',
|
||||
'context': {
|
||||
"current_year": self.current_year,
|
||||
"current_month": self.current_month,
|
||||
},
|
||||
'domain': [('current_year', '=', self.current_year), ('current_month', '=', self.current_month)],
|
||||
'target': 'main',
|
||||
'domain': [('employee_id', 'in', processed_employee_ids)] if processed_employee_ids else [],
|
||||
'views': [[self.env.ref('yuthon_hr_attendance.view_hr_attendance_month_detail_tree').id, 'list'],
|
||||
[False, 'form']],
|
||||
}
|
||||
@@ -358,36 +354,127 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
compensatory_leave = overtime_summary.compensatory_leave
|
||||
return work_overtime, compensatory_leave
|
||||
|
||||
def _get_hr_leave_type(self, employee, type_list):
|
||||
def _get_hr_leave_type(self, employee_id, type_list):
|
||||
"""
|
||||
请假类型判断是否请假
|
||||
:param employee: 员工
|
||||
:param type_list: 请假类型列表
|
||||
:return: 天,小时
|
||||
:return: 天
|
||||
"""
|
||||
self.ensure_one()
|
||||
days = calendar.monthrange(int(self.current_year), int(self.current_month))[1]
|
||||
begin_of_month = datetime.date(int(self.current_year), int(self.current_month), 1)
|
||||
end_of_month = datetime.date(int(self.current_year), int(self.current_month), days)
|
||||
date_start = fields.Datetime.from_string(fields.Date.to_date(begin_of_month).strftime('%Y-%m-%d 00:00:00')) - datetime.timedelta(hours=8)
|
||||
date_end = fields.Datetime.from_string(fields.Date.to_date(end_of_month).strftime('%Y-%m-%d 23:59:59')) - datetime.timedelta(hours=8)
|
||||
domain = [
|
||||
('employee_id', '=', employee.id),
|
||||
('date_from', '<=', date_end),
|
||||
('date_to', '>=', date_start),
|
||||
('date_to', '>=', date_start)
|
||||
]
|
||||
if type_list:
|
||||
domain.append(('holiday_status_id.name', 'in', type_list))
|
||||
hr_leaves = self.env['hr.leave'].sudo().search(domain)
|
||||
hours = 0.0
|
||||
days = 0.0
|
||||
for hr_leave in hr_leaves:
|
||||
if hr_leave.leave_type_request_unit == 'hour':
|
||||
hours += float_round(hr_leave.number_of_hours_display, precision_digits=2)
|
||||
leave_days = {}
|
||||
year = int(self.current_year)
|
||||
month = int(self.current_month)
|
||||
if month == 12:
|
||||
date_to = date(year, 12, 31)
|
||||
else:
|
||||
next_month = month + 1
|
||||
date_to = date(year, next_month, 1) - datetime.timedelta(days=1)
|
||||
|
||||
date_from = date(year, month, 1)
|
||||
for leave_type in type_list:
|
||||
holiday_status_id = self.env['hr.leave.type'].search([('name', '=', leave_type)])
|
||||
leave_records = self.env['hr.leave'].search([
|
||||
('employee_id', '=', employee_id.id),
|
||||
('holiday_status_id', '=', holiday_status_id.id),
|
||||
('date_from', '<', date_to),
|
||||
('date_to', '>', date_from),
|
||||
('state', '=', 'validate')
|
||||
])
|
||||
leave_days[leave_type] = 0
|
||||
for leave in leave_records:
|
||||
leave_start = leave.date_from.date()
|
||||
leave_end = leave.date_to.date()
|
||||
start_in_month = max(leave_start, date_from)
|
||||
end_in_month = min(leave_end, date_to)
|
||||
# 判断是否跨月
|
||||
is_cross_month = leave_start.year != leave_end.year or leave_start.month != leave_end.month
|
||||
if is_cross_month:
|
||||
if leave_start.year == year and leave_start.month == month:
|
||||
start_date = leave_start
|
||||
if leave_end.year == year and leave_end.month == month:
|
||||
end_date = leave_end
|
||||
else:
|
||||
end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1)
|
||||
end_date = end_date - datetime.timedelta(days=1) # 取到当月最后一天
|
||||
elif leave_end.year == year and leave_end.month == month:
|
||||
start_date = date(year, month, 1)
|
||||
end_date = leave_end
|
||||
else:
|
||||
start_date = start_in_month
|
||||
end_date = end_in_month
|
||||
days = self._compute_duration(holiday_status_id, start_date, end_date)
|
||||
else:
|
||||
days = self._compute_duration(holiday_status_id, start_in_month, end_in_month)
|
||||
|
||||
if leave_type in leave_days:
|
||||
leave_days[leave_type] += days
|
||||
else:
|
||||
leave_days[leave_type] = days
|
||||
if leave_type not in leave_days:
|
||||
leave_days[leave_type] = 0
|
||||
|
||||
return leave_days
|
||||
|
||||
def _compute_duration(self, holiday_status_id, start_date, end_date):
|
||||
"""
|
||||
计算请假时长
|
||||
:param holiday_status_id: 请假类型
|
||||
:param start_date: 开始时间
|
||||
:param end_date: 结束时间
|
||||
:return:天
|
||||
"""
|
||||
# 如果是自然日:
|
||||
if holiday_status_id.type_of_work == "1":
|
||||
if start_date and end_date:
|
||||
days = (end_date - start_date).days + 1
|
||||
else:
|
||||
days += float_round(hr_leave.number_of_days_display, precision_digits=2)
|
||||
return days, hours
|
||||
days = 0
|
||||
return days
|
||||
if holiday_status_id.type_of_work == 0:
|
||||
calendar_leaves = self.env['resource.calendar.leaves'].search([("calendar_id", "=", "行政考勤工作日")])
|
||||
holidays_list = []
|
||||
shift_days_list = []
|
||||
# 提取节假日和调休日
|
||||
for leave in calendar_leaves:
|
||||
if leave.holidays:
|
||||
holidays_list.extend(leave.holidays.split(','))
|
||||
if leave.alternate_rest_day:
|
||||
holidays_list.extend(leave.alternate_rest_day.split(','))
|
||||
if leave.shift_replacement_day:
|
||||
shift_days_list.extend(leave.shift_replacement_day.split(','))
|
||||
|
||||
holidays_list = [self.remove_leading_zero(date) for date in holidays_list]
|
||||
shift_days_list = [self.remove_leading_zero(date) for date in shift_days_list]
|
||||
|
||||
days = 0 # 初始化工作日计数
|
||||
current_date = start_date
|
||||
|
||||
while current_date <= end_date:
|
||||
current_date_str = current_date.strftime('%m月%d日')
|
||||
current_date_str = self.remove_leading_zero(current_date_str)
|
||||
is_weekend = current_date.weekday() in [5, 6]
|
||||
|
||||
if current_date_str in holidays_list:
|
||||
# 如果是法定节假日,不计为工作日
|
||||
pass
|
||||
elif current_date_str in shift_days_list:
|
||||
# 如果是调休日,无论是否周末都算作工作日
|
||||
days += 1
|
||||
elif not is_weekend:
|
||||
# 如果不是周末且不是节假日,算作工作日
|
||||
days += 1
|
||||
|
||||
current_date += timedelta(days=1)
|
||||
else:
|
||||
delta = end_date - start_date
|
||||
days = delta.days + 1
|
||||
return days
|
||||
|
||||
def remove_leading_zero(self, date_str):
|
||||
month, day = date_str.split('月')
|
||||
month = month.lstrip('0')
|
||||
day = day.rstrip('日')
|
||||
day = day.lstrip('0')
|
||||
return f'{month}月{day}日'
|
||||
|
||||
def analysis_attendance_month_detail(self):
|
||||
"""
|
||||
@@ -403,8 +490,8 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
# 迟到, 早退, 缺卡
|
||||
self.come_late, self.is_early_card, self.is_no_card = self._get_come_late(self.employee_id)
|
||||
# 无薪假:hr.leave表
|
||||
days, hours = self._get_hr_leave_type(self.employee_id, ['事假', '年假'])
|
||||
self.unpaid_leave = hours
|
||||
days, = self._get_hr_leave_type(self.employee_id, ['事假', '年假'])
|
||||
self.unpaid_leave = days
|
||||
|
||||
# 行政的进行统计
|
||||
if not self.employee_id.default_planning_role_id:
|
||||
@@ -412,8 +499,10 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
# 迟到, 早退, 缺卡
|
||||
self.come_late, self.is_early_card, self.is_no_card = self._get_come_late(self.employee_id)
|
||||
# 无薪假:hr.leave表
|
||||
days, hours = self._get_hr_leave_type(self.employee_id, ['事假', '年假'])
|
||||
self.unpaid_leave = hours
|
||||
leave_days = self._get_hr_leave_type(self.employee_id,['事假', '年假', '产假'])
|
||||
self.leave_of_absence = leave_days.get('事假', 0)
|
||||
self.annual_leave = leave_days.get('年假', 0)
|
||||
self.maternity_leave = leave_days.get('产假', 0)
|
||||
|
||||
return True
|
||||
|
||||
@@ -650,7 +739,6 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
获取出勤月度汇总记录结果表
|
||||
:return: obj
|
||||
"""
|
||||
self.ensure_one()
|
||||
attendance_month_obj = self.env["hr.attendance.month"].search([
|
||||
('current_year', '=', self.current_year),
|
||||
('current_month', '=', self.current_month)
|
||||
@@ -665,29 +753,28 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
})
|
||||
return attendance_month_obj
|
||||
|
||||
def _get_attendance_month_detail(self, employee):
|
||||
def _get_attendance_month_detail(self, employee, current_year, current_month):
|
||||
"""
|
||||
判断当前是否已经创建了考勤明细
|
||||
:param employee: 员工
|
||||
:return: 考情明细
|
||||
"""
|
||||
self.ensure_one()
|
||||
attendance_month_detail_obj = self.env["hr.attendance.month.detail"].search(
|
||||
[('employee_id', '=', employee.id),
|
||||
('current_year', '=', self.current_year),
|
||||
('current_month', '=', self.current_month),
|
||||
('current_year', '=', current_year),
|
||||
('current_month', '=', current_month),
|
||||
], limit=1)
|
||||
if not attendance_month_detail_obj:
|
||||
attendance_month_detail_obj = self.env["hr.attendance.month.detail"].sudo().create({
|
||||
"employee_id": employee.id,
|
||||
"attendance_year_id": self.attendance_year_id.id,
|
||||
"current_year": self.current_year,
|
||||
"current_month": self.current_month,
|
||||
"current_year": current_year,
|
||||
"current_month": current_month,
|
||||
})
|
||||
return attendance_month_detail_obj
|
||||
|
||||
def button_compute_all(self):
|
||||
self.ensure_one()
|
||||
processed_employee_ids = []
|
||||
attendance_month_obj = self._get_attendance_month_obj()
|
||||
# 查询所有的当前员工? 是否需要判断当前员工是否关联企业微信??预留
|
||||
calendars = self.env['resource.calendar'].search([])
|
||||
@@ -699,15 +786,17 @@ class HrAttendanceMonthDetail(models.Model):
|
||||
for detail_id in detail_ids:
|
||||
if detail_id.employee_id not in all_employee_ids:
|
||||
# 判断当前是否已经创建了考勤明细
|
||||
attendance_month_detail_obj = self._get_attendance_month_detail(detail_id.employee_id)
|
||||
attendance_month_detail_obj = self._get_attendance_month_detail(detail_id.employee_id, detail_id.current_year, detail_id.current_month)
|
||||
attendance_month_detail_obj.hr_attendance_month_id = attendance_month_obj.id
|
||||
# 判断当前用户是否已经确认签字,签字后不再更新
|
||||
if attendance_month_detail_obj.state == 'confirmed' and attendance_month_detail_obj.signature:
|
||||
continue
|
||||
# 更新用户的考勤明细
|
||||
attendance_month_detail_obj.analysis_attendance_month_detail()
|
||||
# 返回action动作
|
||||
action = self._hr_attendance_month_detail_action()
|
||||
if detail_id.employee_id.id not in processed_employee_ids:
|
||||
processed_employee_ids.append(detail_id.employee_id.id)
|
||||
# 返回包含处理过的员工ID的action动作
|
||||
action = self._hr_attendance_month_detail_action(processed_employee_ids)
|
||||
return action
|
||||
|
||||
def attendance_month_detail(self):
|
||||
|
||||
@@ -88,6 +88,7 @@ class HrAttendanceMonthWizard(models.TransientModel):
|
||||
"current_month": self.current_month,
|
||||
},
|
||||
'domain': [
|
||||
('company_id', '=', self.company_id.id),
|
||||
('current_year', '=', self.current_year),
|
||||
('current_month', '=', self.current_month),
|
||||
],
|
||||
@@ -115,7 +116,7 @@ class HrAttendanceMonthWizard(models.TransientModel):
|
||||
self.ensure_one()
|
||||
attendance_month_obj = self._get_attendance_month_obj()
|
||||
# 查询所有的当前员工? 是否需要判断当前员工是否关联企业微信??预留
|
||||
user_obj = self.env['res.users'].sudo().search([])
|
||||
user_obj = self.env['res.users'].sudo().search([('company_id', '=', self.company_id.id)])
|
||||
calendars = self.env['resource.calendar'].search([])
|
||||
all_employee_ids = set()
|
||||
for calendar in calendars:
|
||||
@@ -139,7 +140,6 @@ class HrAttendanceMonthWizard(models.TransientModel):
|
||||
continue
|
||||
# 更新用户的考勤明细
|
||||
attendance_month_detail_obj.analysis_attendance_month_detail()
|
||||
self.get_leave_and_overtime_info(employee.id)
|
||||
# 返回action动作
|
||||
action = self._hr_attendance_month_detail_action()
|
||||
return action
|
||||
@@ -160,84 +160,6 @@ class HrAttendanceMonthWizard(models.TransientModel):
|
||||
})
|
||||
wizard.search_attendance_month()
|
||||
|
||||
def get_leave_and_overtime_info(self, employee_id):
|
||||
leave_types = {
|
||||
'年假': 'annual_leave',
|
||||
'事假': 'unpaid_leave',
|
||||
'补休': 'cop_leave',
|
||||
'产假': 'maternity_leave',
|
||||
'病假(普通)': 'general_sick_leave',
|
||||
'病假(住院期)': 'hospital_sick_leave',
|
||||
'病假(出院恢复期)': 'recovery_sick_leave',
|
||||
'陪产假': 'paternity_leave',
|
||||
'计生假': 'family_planning_leave',
|
||||
'丧假': 'bereavement_leave',
|
||||
'探亲假' : 'home_visit_leave',
|
||||
'婚假': 'marriage_leave',
|
||||
'考试假': 'exam_leave',
|
||||
'家长会假': 'parents_meeting_leave',
|
||||
}
|
||||
leave_days = {}
|
||||
year = int(self.current_year)
|
||||
month = int(self.current_month)
|
||||
if month == 12:
|
||||
date_to = date(year, 12, 31)
|
||||
else:
|
||||
next_month = month + 1
|
||||
date_to = date(year, next_month, 1) - datetime.timedelta(days=1)
|
||||
|
||||
date_from = date(year, month, 1)
|
||||
for leave_type in leave_types:
|
||||
total_days = 0
|
||||
holiday_status_id = self.env['hr.leave.type'].search([('name', '=', leave_type)])
|
||||
leave_records = self.env['hr.leave'].search([
|
||||
('employee_id', '=', employee_id),
|
||||
('holiday_status_id', '=', holiday_status_id.id),
|
||||
('date_from', '<', date_to),
|
||||
('date_to', '>', date_from),
|
||||
('state', '=', 'validate')
|
||||
])
|
||||
for leave in leave_records:
|
||||
leave_start = leave.date_from.date()
|
||||
leave_end = leave.date_to.date()
|
||||
start_in_month = max(leave_start, date_from)
|
||||
end_in_month = min(leave_end, date_to)
|
||||
# 判断是否跨月
|
||||
is_cross_month = leave_start.year != leave_end.year or leave_start.month != leave_end.month
|
||||
if is_cross_month:
|
||||
if leave_start.year == year and leave_start.month == month:
|
||||
start_date = leave_start
|
||||
if leave_end.year == year and leave_end.month == month:
|
||||
end_date = leave_end
|
||||
else:
|
||||
end_date = date(year, month + 1, 1) if month < 12 else date(year + 1, 1, 1)
|
||||
end_date = end_date - datetime.timedelta(days=1) # 取到当月最后一天
|
||||
elif leave_end.year == year and leave_end.month == month:
|
||||
start_date = date(year, month, 1)
|
||||
end_date = leave_end
|
||||
else:
|
||||
start_date = start_in_month
|
||||
end_date = end_in_month
|
||||
|
||||
days = self._create_leave_record(holiday_status_id, start_date, end_date)
|
||||
leave_days[leave_type] = days
|
||||
else:
|
||||
for leave in leave_records:
|
||||
total_days += leave.number_of_days_display
|
||||
leave_days[leave_type] = total_days
|
||||
|
||||
employee = self.env['hr.employee'].browse(employee_id)
|
||||
all_overtime_hours = employee.all_overtime_hours
|
||||
attendance_detail = self.env['hr.attendance.month.detail'].search([
|
||||
('employee_id', '=', employee_id),
|
||||
('current_year', '=', self.current_year),
|
||||
('current_month', '=', self.current_month)
|
||||
], limit=1)
|
||||
|
||||
for field_name, days in leave_days.items():
|
||||
setattr(attendance_detail, leave_types[field_name], days)
|
||||
setattr(attendance_detail, 'work_overtime', all_overtime_hours)
|
||||
|
||||
# 创建用户记录传入日期来获取天数
|
||||
def _create_leave_record(self, holiday_status_id, start_date, end_date):
|
||||
if holiday_status_id.type_of_work == 0:
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="考勤统计">
|
||||
<group>
|
||||
<field name="company_id" invisible="1"/>
|
||||
<field name="company_id"/>
|
||||
<field name="attendance_year_id"/>
|
||||
<field name="current_month" />
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user