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 @@
-
+
-
+