From d8b00a613e43ced03115eb7244e2b6258b45fd5a Mon Sep 17 00:00:00 2001 From: zhou <123@> Date: Thu, 7 Aug 2025 17:12:54 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_holiday/models/inherit_hr_leave.py | 91 +++++++------ .../models/inherit_hr_leave_allocation.py | 65 ++++----- .../models/yuthon_vacation_reports.py | 124 ++++++++++++------ .../views/yuthon_vacation_reports_views.xml | 9 +- 4 files changed, 166 insertions(+), 123 deletions(-) diff --git a/yuthon_holiday/models/inherit_hr_leave.py b/yuthon_holiday/models/inherit_hr_leave.py index f331e5ac1..a5ebafc17 100644 --- a/yuthon_holiday/models/inherit_hr_leave.py +++ b/yuthon_holiday/models/inherit_hr_leave.py @@ -509,8 +509,6 @@ class HolidaysRequestBatch(models.Model): # 提交 def action_submit(self): """提交请假申请,区分年假和其他假期处理""" - _logger.info(f"开始处理请假申请:{self.code},申请人:{self.users_id.employee_ids.name}") - self._process_specific_leaves('年假', is_annual_leave=True) other_leave_types = [ '产假', '事假', '法定假', '调休', '补休', '值班调休', @@ -537,45 +535,31 @@ class HolidaysRequestBatch(models.Model): date_ranges = self._split_by_month(leave['start'], leave['end']) if not is_annual_leave else [ {'start': leave['start'], 'end': leave['end']}] for period in date_ranges: - days = self._compute_duration_day(leave['type_id'], period['start'], period['end']) - if days > 0: - if is_annual_leave: - self._create_annual_leave_line(period['start'], leave, days, period) - else: - self._create_other_leave_line(period['start'], leave, days, period, leave_type_name) + if leave_type_name == '补休': + # 计算小时数(天数 * 8) + hours = self._compute_duration_day(leave['type_id'], period['start'], period['end']) * 8 + if hours > 0: + self._create_overtime_leave_line(period_start=period['start'], leave_data=leave, hours=hours, period=period, leave_type_name=leave_type_name) + else: + days = self._compute_duration_day(leave['type_id'], period['start'], period['end']) + if days > 0: + if is_annual_leave: + # 年假以小时为单位(天数 * 8) + self._create_annual_leave_line(period_start=period['start'], leave_data=leave, hours=days * 8, period=period) + else: + self._create_other_leave_line(period_start=period['start'], leave_data=leave, days=days, period=period, leave_type_name=leave_type_name) # 处理小时假 for leave in hourly_leaves: leave_date = leave['date'] - days = leave['days'] - if days > 0: + hours = leave['days'] * 8 + if hours > 0: if is_annual_leave: - self._create_annual_leave_line(leave_date, leave, days, {'start': leave_date, 'end': leave_date}) + self._create_annual_leave_line(period_start=leave_date, leave_data=leave, hours=hours, period={'start': leave_date, 'end': leave_date}) + elif leave_type_name == '补休': + self._create_overtime_leave_line(period_start=leave_date, leave_data=leave, hours=hours, period={'start': leave_date, 'end': leave_date}, leave_type_name=leave_type_name) else: - self._create_other_leave_line(leave_date, leave, days,{'start': leave_date, 'end': leave_date}, leave_type_name) - - def _create_other_leave_line(self, period_start, leave_data, days, period, leave_type_name): - """为非年假类型创建vacation.other.line记录""" - # 查找关联的假期额度记录 - report_month = date(period_start.year, period_start.month, 1) - vacation_report = self.env['yuthon.vacation.reports'].search([ - ('employee_id', '=', self.users_id.employee_ids.id), - ('vacation_date', '>=', fields.Date.to_string(report_month)), - ('vacation_date', '<=', fields.Date.to_string( - date(period_start.year, period_start.month, - calendar.monthrange(period_start.year, period_start.month)[1])) - ) - ], limit=1) - - self.env['vacation.other.line'].create({ - 'vacation_id': vacation_report.id if vacation_report else False, - 'holiday_type': leave_data['type_id'].id, - 'employee_id': self.users_id.employee_ids.id, - 'workflow_record_id': leave_data['workflow_id'], - 'update_date': fields.Date.today(), - 'update_type': 'reduce', - 'lines': days, - 'text': f"请假审批" - }) + # 其他类型小时假,使用天数 + self._create_other_leave_line(period_start=leave_date, leave_data=leave,days=leave['days'], period={'start': leave_date, 'end': leave_date}, leave_type_name=leave_type_name) def _get_day_leaves(self, leave_type_name): workflow_id = self.env['soong.workflow.record'].search([('code', '=', self.code)], limit=1).id @@ -601,7 +585,7 @@ class HolidaysRequestBatch(models.Model): 'date': fields.Date.from_string(line.request_date_from), 'type_id': line.holiday_status_id, 'workflow_id': workflow_id, - 'days': hours / 8 # 转换为天数 + 'days': hours / 8 }) return hourly_records @@ -628,8 +612,33 @@ class HolidaysRequestBatch(models.Model): date_ranges.append({'start': date(end_date.year, end_date.month, 1), 'end': end_date}) return date_ranges - def _create_annual_leave_line(self, period_start, leave_data, days, period): - """更新年假记录""" + def _create_overtime_leave_line(self, period_start, leave_data, hours, period, leave_type_name): + """为补休类型创建加班明细行(vacation.over.line),类型为减少,值赋给workday""" + report_month = date(period_start.year, period_start.month, 1) + vacation_report = self.env['yuthon.vacation.reports'].search([ + ('employee_id', '=', self.users_id.employee_ids.id), + ('vacation_date', '>=', fields.Date.to_string(report_month)), + ('vacation_date', '<=', fields.Date.to_string( + date(period_start.year, period_start.month, + calendar.monthrange(period_start.year, period_start.month)[1])) + ) + ], limit=1) + self.env['vacation.over.line'].create({ + 'vacation_id': vacation_report.id if vacation_report else False, + 'employee_id': self.users_id.employee_ids.id, + 'workflow_record_id': leave_data['workflow_id'], + 'update_type': 'reduce', + 'workday': hours, + 'legal_day': 0, + 'public_holidays': 0, + 'meal_number': 0, + 'update_date': fields.Date.today(), + 'is_void': False, + 'text': f"请假审批" + }) + + def _create_annual_leave_line(self, period_start, leave_data, hours, period): + """更新年假记录,使用小时作为单位""" report_month = date(period_start.year, period_start.month, 1) vacation_report = self.env['yuthon.vacation.reports'].search([ ('employee_id', '=', self.users_id.employee_ids.id), @@ -639,20 +648,18 @@ class HolidaysRequestBatch(models.Model): calendar.monthrange(period_start.year, period_start.month)[1])) ) ], limit=1) - # 创建年假明细记录 self.env['vacation.leave.line'].create({ 'vacation_id': vacation_report.id if vacation_report else False, 'employee_id': self.users_id.employee_ids.id, 'workflow_record_id': leave_data['workflow_id'], 'update_type': 'reduce', 'update_date': fields.Date.today(), - 'update_lines': days, + 'update_lines': hours, 'is_void': False, 'allocation_type': 'vacation', 'text': f"请假审批" }) - def _compute_duration_day(self, holiday_status_id, start_date, end_date): """ 计算请假时长 diff --git a/yuthon_holiday/models/inherit_hr_leave_allocation.py b/yuthon_holiday/models/inherit_hr_leave_allocation.py index 706932089..d39fe88b9 100644 --- a/yuthon_holiday/models/inherit_hr_leave_allocation.py +++ b/yuthon_holiday/models/inherit_hr_leave_allocation.py @@ -100,38 +100,39 @@ class HrLeaveAllocation(models.Model): for leave in leave_ids: leave.employee_id.write({'quota_issued': leave.number_of_hours_display}) - @api.model - def create(self, vals): - allocation = super(HrLeaveAllocation, self).create(vals) - employee = allocation.employee_id - hours = allocation.number_of_hours_display - if allocation.holiday_status_id.name == '年假' and allocation.state == 'validate': - employee.write({'quota_issued': employee.quota_issued + hours}) - reports_id = self.env['yuthon.vacation.reports'].search([('employee_id', '=', employee.id)], limit=1) - # 创建假期明细记录 - self.env['vacation.leave.line'].create({ - 'vacation_id': reports_id.id, - 'update_type': 'add', - 'update_date': fields.Datetime.now(), - 'update_lines': hours, - 'allocation_type': 'allocation', - 'text': allocation.name - }) - elif allocation.holiday_status_id.name == '补休': - reports_id = self.env['yuthon.vacation.reports'].search([ - ('employee_id', '=', employee.id) - ], limit=1) - self.env['vacation.over.line'].create({ - 'vacation_id': reports_id.id, - 'update_type': 'add', - 'update_date': fields.Datetime.now(), - 'workday': hours, - 'legal_day': 0, - 'public_holidays': 0, - 'meal_number': 0, - 'text': allocation.name - }) - return allocation + # @api.model + # def create(self, vals): + # allocation = super(HrLeaveAllocation, self).create(vals) + # employee = allocation.employee_id + # hours = allocation.number_of_hours_display + # date_from = allocation.date_from.replace(day=1) + # if allocation.holiday_status_id.name == '年假' and allocation.state == 'validate': + # employee.write({'quota_issued': employee.quota_issued + hours}) + # reports_id = self.env['yuthon.vacation.reports'].search([('employee_id', '=', employee.id), ('vacation_date', '=', date_from)], limit=1) + # # 创建假期明细记录 + # self.env['vacation.leave.line'].create({ + # 'vacation_id': reports_id.id, + # 'update_type': 'add', + # 'update_date': fields.Datetime.now(), + # 'update_lines': hours, + # 'allocation_type': 'allocation', + # 'text': allocation.name + # }) + # elif allocation.holiday_status_id.name == '补休': + # reports_id = self.env['yuthon.vacation.reports'].search([ + # ('employee_id', '=', employee.id), ('vacation_date', '=', date_from) + # ], limit=1) + # self.env['vacation.over.line'].create({ + # 'vacation_id': reports_id.id, + # 'update_type': 'add', + # 'update_date': fields.Datetime.now(), + # 'workday': hours, + # 'legal_day': 0, + # 'public_holidays': 0, + # 'meal_number': 0, + # 'text': allocation.name + # }) + # return allocation def unlink(self): """删除记录时同步删除对应的假期明细行""" diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index 9b20082d2..10ada9685 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -156,7 +156,6 @@ class YuthonVacationReports(models.Model): '调休': 'compensatory_leave', '事假': 'leave_of_absence', '法定假': 'statutory_leave', - '补休': 'cop_leave', '值班调休': 'duty_off_leave', '婚假': 'marriage_leave', '产假': 'maternity_leave', @@ -284,6 +283,9 @@ class YuthonVacationReports(models.Model): def monthly_report(self): today = fields.Date.today() employees = self.env['hr.employee'].search([]) + current_month_first = today.replace(day=1) + last_month_last = current_month_first - timedelta(days=1) + last_month_first = last_month_last.replace(day=1) for employee in employees: resource_user_ids = employee.resource_calendar_id.resource_user_ids.ids is_resource_user = True if employee.id in resource_user_ids else False @@ -292,10 +294,23 @@ class YuthonVacationReports(models.Model): ('vacation_date', '=', today) ], limit=1) if not existing_record and not is_resource_user: + last_month_record = self.search([ + ('employee_id', '=', employee.id), + ('vacation_date', '>=', fields.Date.to_string(last_month_first)), + ('vacation_date', '<=', fields.Date.to_string(last_month_last)) + ], limit=1) + inherit_vals = {} + if last_month_record: + inherit_vals = { + 'leave': last_month_record.leave, + 'working_overtime': last_month_record.working_overtime, + 'medical_fee': last_month_record.medical_fee, + } + self.create({ 'employee_id': employee.id, 'vacation_date': today, - # 明细行和计算字段留空,由后续逻辑处理 + **inherit_vals }) def disposable_report(self): @@ -403,15 +418,17 @@ class YuthonVacationReports(models.Model): ) def _compute_working_overtime(self): for record in self: - overtime_hours = 0.0 + total_add = 0.0 + total_reduce = 0.0 for line in record.overtime_ids: if not line.is_void: line_total = (line.workday or 0) + (line.legal_day or 0) + (line.public_holidays or 0) if line.update_type == 'add': - overtime_hours += line_total + total_add += line_total elif line.update_type == 'reduce': - overtime_hours -= line_total - record.working_overtime = overtime_hours / 8 + total_reduce += line_total + record.working_overtime = total_add / 8 + record.cop_leave = total_reduce / 8 @api.depends('meal_fee_ids.update_lines', 'meal_fee_ids.update_type', 'meal_fee_ids.is_void') def _compute_meal_fee(self): @@ -426,46 +443,71 @@ class YuthonVacationReports(models.Model): record.meal_fee = meal_total def update_vacation(self): - employees = self.env['hr.employee'].search([ - ('is_salary', '=', True), - ('contract_id.state', '!=', 'cancel'), - ('contract_id', '!=', False), - ]) + """ + 从hr.leave.allocation中汇总年假和补休数据,更新到8月份的假期报表 + """ + # 定义目标月份:8月1号(当年) + current_year = fields.Date.today().year + target_month = date(current_year, 8, 4) + target_month_str = fields.Date.to_string(target_month) + # 获取所有员工 + employees = self.env['hr.employee'].search([]) for employee in employees: - vacation_id = self.env['yuthon.vacation.reports'].search([ + # 1. 处理年假分配 + annual_allocations = self.env['hr.leave.allocation'].search([ ('employee_id', '=', employee.id), + ('holiday_status_id.name', '=', '年假'), + ('state', '=', 'validate') ]) - if not vacation_id: - self.env['yuthon.vacation.reports'].create({ - 'employee_id': employee.id, - 'leave': employee.remainder_year, - 'working_overtime': employee.overtime_this_month, - 'meal_fee': employee.meal_number, - }) - else: - vacation_id.write({ - 'leave': employee.remainder_year, - 'working_overtime': employee.overtime_this_month, - 'meal_fee': employee.meal_number, - }) - def update_log(self): - vacation_ids = self.env["yuthon.vacation.reports"].browse( - self._context.get('active_ids', self._context.get('active_id'))) - for vacation_id in vacation_ids: - vacation_id.leave_ids.unlink() - vacation_id.overtime_ids.unlink() - vacation_id.meal_fee_ids.unlink() + # 累加年假总小时数 + total_annual_hours = sum(alloc.number_of_hours_display for alloc in annual_allocations) + + # 2. 处理补休分配 + comp_allocations = self.env['hr.leave.allocation'].search([ + ('employee_id', '=', employee.id), + ('holiday_status_id.name', '=', '补休'), + ('state', '=', 'validate') # 只处理已验证的分配 + ]) + + # 累加补休总小时数 + total_comp_hours = sum(alloc.number_of_hours_display for alloc in comp_allocations) + # 3. 查找或创建8月份的报表记录 + vacation_report = self.search([ + ('employee_id', '=', employee.id), + ('vacation_date', '=', target_month_str) + ], limit=1) + self._update_leave_lines(vacation_report, annual_allocations, total_annual_hours) + self._update_over_lines(vacation_report, comp_allocations, total_comp_hours) + + def _update_leave_lines(self, report, allocations, number_of_hours_display): + for alloc in allocations: + self.env['vacation.leave.line'].create({ + 'vacation_id': report.id, + 'employee_id': report.employee_id.id, + 'update_type': 'add', + 'update_date': fields.Date.today(), + 'update_lines': number_of_hours_display, + 'is_void': False, + 'allocation_type': 'allocation', + 'text': f"年假分配" + }) + + def _update_over_lines(self, report, allocations, number_of_hours_display): + """更新补休明细行""" + for alloc in allocations: + self.env['vacation.over.line'].create({ + 'vacation_id': report.id, + 'employee_id': report.employee_id.id, + 'update_type': 'add', + 'update_date': fields.Date.today(), + 'workday': number_of_hours_display, + 'legal_day': 0, + 'public_holidays': 0, + 'meal_number': 0, + 'text': f"补休分配" + }) - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'type': 'success', - 'message': '删除日志成功', - 'sticky': False, - } - } def log(self): return { diff --git a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml index ce9fc5ee4..a296de08e 100644 --- a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml +++ b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml @@ -410,13 +410,6 @@ code action = model.update_vacation() - - - 初始化日志 - - - code - action = model.update_log() 批量发送 @@ -426,7 +419,7 @@ action = model.update_log() - 清零 + 初始化日志 code