diff --git a/tx_planning_resource/models/planning_slot.py b/tx_planning_resource/models/planning_slot.py index f35f55379..e16d79116 100644 --- a/tx_planning_resource/models/planning_slot.py +++ b/tx_planning_resource/models/planning_slot.py @@ -5,6 +5,7 @@ # @Author :zzw from odoo import models, fields, api, _ from typing import Dict, List +from datetime import timedelta class TxPlanningSlot(models.Model): @@ -29,19 +30,44 @@ class TxPlanningSlot(models.Model): def action_compensatory_leave(self): """ - 补休按钮 - 将当前排班转为补休记录 - TODO: 具体业务逻辑由客户确认后补充 + 补休按钮 - 打开假期分配表单,填写补休信息后验证写入考勤确认单台账 """ self.ensure_one() - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'title': '补休', - 'message': f'已为 {self.resource_id.name} 创建补休申请(待实现)', - 'sticky': False, - 'type': 'success', + # 从排班资源获取员工 + employee = self.resource_id.employee_id if self.resource_id else None + if not employee: + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': '提示', + 'message': '该排班未关联有效员工,无法创建补休申请', + 'sticky': False, + 'type': 'warning', + } } + # 获取排班日期 + slot_date = (self.start_datetime + timedelta(hours=8)).date() if self.start_datetime else None + # 获取"补休"假期类型 + holiday_status_id = self.env['hr.leave.type'].search([('name', '=', '补休')]) + # 获取 hr.leave.allocation 的 form 视图 + form_id = self.env.ref('hr_holidays.hr_leave_allocation_view_form_manager').id + return { + 'type': 'ir.actions.act_window', + 'name': '计划班调补休申请', + 'res_model': 'hr.leave.allocation', + 'view_mode': 'form', + 'views': [(form_id, 'form')], + 'target': 'current', + 'context': { + 'default_holiday_status_id': holiday_status_id.id, + 'default_employee_ids': [(6, 0, [employee.id])], + 'default_employee_id': employee.id, + 'default_allocation_type': 'regular', + 'default_name': '计划班调补休', + 'default_number_of_days_display': 8, + 'default_date_from': slot_date, + }, } @api.depends('role_id', 'employee_id') diff --git a/yuthon_holiday/models/inherit_hr_leave_allocation.py b/yuthon_holiday/models/inherit_hr_leave_allocation.py index ccff80585..1bea1d66e 100644 --- a/yuthon_holiday/models/inherit_hr_leave_allocation.py +++ b/yuthon_holiday/models/inherit_hr_leave_allocation.py @@ -8,6 +8,12 @@ class HrLeaveAllocation(models.Model): HOURS_PER_DAY = 8.0 + is_reduce_compensatory = fields.Boolean( + string='减少补休', + default=False, + help='标识此补休分配是否为扣减操作。填写负数时自动勾选,写入考勤确认单时走 reduce 逻辑。' + ) + @api.depends('holiday_status_id', 'allocation_type', 'number_of_hours_display', 'number_of_days_display', 'date_to') def _compute_from_holiday_status_id(self): super()._compute_from_holiday_status_id() @@ -34,6 +40,30 @@ class HrLeaveAllocation(models.Model): start_time = fields.Date(string="开始时间") end_time = fields.Date(string="结束时间") + @api.model_create_multi + def create(self, vals_list): + """拦截补休的负数天数,绕过 SQL CHECK 约束""" + for vals in vals_list: + if vals.get('number_of_days', 0) < 0: + holiday_status_id = vals.get('holiday_status_id') + if holiday_status_id: + status = self.env['hr.leave.type'].browse(holiday_status_id) + if status.name == '补休': + vals['is_reduce_compensatory'] = True + vals['number_of_days'] = abs(vals['number_of_days']) + return super().create(vals_list) + + def write(self, vals): + """拦截补休的负数天数,绕过 SQL CHECK 约束(allocation_type=regular 时 number_of_days 必须 > 0)""" + if vals.get('number_of_days', 0) < 0: + for rec in self: + if rec.holiday_status_id.name == '补休': + vals = dict(vals) + vals['is_reduce_compensatory'] = True + vals['number_of_days'] = abs(vals['number_of_days']) + break + return super().write(vals) + @api.onchange('users_role_ids') def onchange_employee_ids(self): users_role_list = [] @@ -285,9 +315,9 @@ class HrLeaveAllocation(models.Model): logger.info("按旧方式删除年假明细行并重新计算") elif leave.holiday_status_id.name == '补休': - # 精确查找并删除当前表单添加的补休明细行 + # 精确查找并删除当前表单添加的补休明细行(增加和减少类型都处理) lines_to_delete = reports_id.overtime_ids.filtered( - lambda o: o.update_type == 'add' and o.allocation_id.id == leave.id + lambda o: o.update_type in ('add', 'reduce') and o.allocation_id.id == leave.id ) logger.info(f"找到需要删除的补休明细行数量: {len(lines_to_delete)}") if lines_to_delete: @@ -358,15 +388,23 @@ class HrLeaveAllocation(models.Model): if reports_id: rest_hours = leave.number_of_days_display * 8 + # 根据 is_reduce_compensatory 判断增加还是减少 + # (number_of_days 已被 write 拦截转为正数以绕过 SQL CHECK 约束) + if leave.is_reduce_compensatory: + update_type = 'reduce' + workday = rest_hours + else: + update_type = 'add' + workday = rest_hours reports_id.write({ 'valid_date1': leave.date_to, 'overtime_ids': [(0, 0, { 'vacation_id': reports_id.id, 'allocation_id': leave.id, - 'update_type': 'add', + 'update_type': update_type, 'update_date': fields.Datetime.now(), 'valid_date': leave.date_to, - 'workday': rest_hours, + 'workday': workday, 'text': leave.name or '补休分配' })] }) diff --git a/yuthon_hr_attendance/views/inherit_planning_slot_views.xml b/yuthon_hr_attendance/views/inherit_planning_slot_views.xml index a9ee57c84..72f47822f 100644 --- a/yuthon_hr_attendance/views/inherit_planning_slot_views.xml +++ b/yuthon_hr_attendance/views/inherit_planning_slot_views.xml @@ -27,11 +27,11 @@ - +
-