添加功能

This commit is contained in:
李鹏宇
2026-03-13 11:24:41 +08:00
parent 1c17a72057
commit df19c6f30f
10 changed files with 228 additions and 66 deletions
+39 -42
View File
@@ -474,6 +474,7 @@ class HolidaysRequestBatch(models.Model):
def _get_day_leaves(self, leave_type_name):
workflow_id = self.env['soong.workflow.record'].search([('code', '=', self.code)], limit=1).id
return [{
'leave_id': leave.id,
'start': fields.Date.from_string(leave.request_date_from),
'end': fields.Date.from_string(leave.request_date_to),
'type_id': leave.holiday_status_id,
@@ -492,6 +493,7 @@ class HolidaysRequestBatch(models.Model):
hours_match = re.search(r'(\d+(?:\.\d+)?)', line.number_of_hours_text or '')
hours = float(hours_match.group(1)) if hours_match else 0
hourly_records.append({
'leave_id': line.id,
'date': fields.Date.from_string(line.request_date_from),
'type_id': line.holiday_status_id,
'workflow_id': workflow_id,
@@ -532,6 +534,7 @@ class HolidaysRequestBatch(models.Model):
:param leave_type_name: 假期类型名称
"""
employee_id = self.users_id.employee_ids.id if self.users_id.employee_ids else False
leave_id = leave_data.get('leave_id', False)
if 'date' in leave_data:
leave_start = leave_data['date']
leave_end = leave_data['date']
@@ -604,7 +607,7 @@ class HolidaysRequestBatch(models.Model):
)
# 4. 只有当两个报表中都没有该记录时,才创建新记录
if not existing_line_in_vacation_report and not existing_line_in_vacation_report1:
report_id = self.env['vacation.over.line'].create({
over_id = self.env['vacation.over.line'].create({
'vacation_id': actual_vacation_report.id,
'employee_id': employee_id,
'logging_data': '666',
@@ -618,45 +621,15 @@ class HolidaysRequestBatch(models.Model):
'is_void': False,
'text': f"请假审批"
})
report_id.update_line()
report_id._compute_working_overtime()
if self.env.user.company_id.name == '集团本部':
domain = [
('employee_id', '=', employee_id),
"|",
('is_initial_number', '=', True),
'&', ('is_initial_number', '=', False),
'&', ('update_type', '=', 'add'),
'&', ('workflow_record_id', '!=', False),
'&', ('start_time', '>=', datetime(2025, 12, 31, 23, 59, 59)),
'&', ('vacation_id', '!=', False),
('employee_id.is_annual_salary', '!=', True)
]
else:
domain = [
('workflow_record_id', '!=', False),
('update_type', '=', 'add'),
('remaining_quantity', '!=', 0.0),
]
overtime_records = self.env['vacation.over.line'].search(domain, order='start_time ASC')
remaining_deduct = hours
for record in overtime_records:
if remaining_deduct <= 0:
break
current_remaining = record.remaining_quantity # 剩余数
deduct_amount = min(remaining_deduct, current_remaining)
if remaining_deduct < current_remaining:
carry_state = 'partial'
else:
carry_state = 'completed'
record.write({
'carry_forward_date': fields.Date.to_string(period_start),
'carry_forward_quantity': record.carry_forward_quantity + deduct_amount,
'carry_forward_state': carry_state,
'logging_data': '113',
})
remaining_deduct -= deduct_amount
actual_vacation_report.update_line()
actual_vacation_report._compute_working_overtime()
if leave_id and over_id:
self._create_overtime_carryover(
over_line=over_id,
deduct_amount=interval_hours,
carry_date=period_start,
leave_id=leave_id
)
def _create_annual_leave_line(self, period_start, leave_data, hours, period):
"""
@@ -736,7 +709,7 @@ class HolidaysRequestBatch(models.Model):
limit=1
)
if not existing_line_in_vacation_report and not existing_line_in_vacation_report1:
report_id = self.env['vacation.leave.line'].create({
self.env['vacation.leave.line'].create({
'vacation_id': actual_vacation_report.id,
'employee_id': employee_id,
'workflow_record_id': leave_data['workflow_id'],
@@ -747,7 +720,7 @@ class HolidaysRequestBatch(models.Model):
'allocation_type': 'vacation',
'text': f"请假审批"
})
report_id.update_line()
actual_vacation_report.update_line()
def _create_other_leave_line(self, period_start, leave_data, days, period, leave_type_name):
"""
@@ -823,6 +796,30 @@ class HolidaysRequestBatch(models.Model):
'text': f"请假审批"
})
def _create_overtime_carryover(self, over_line, deduct_amount, carry_date, leave_id):
"""
封装创建加班结转明细的逻辑
"""
# 1. 防重复校验:同一假单+同一假期行,只创建一条结转明细
if leave_id and over_line:
existing_carryover = self.env['overtime.carryover'].search([
('leave_id', '=', leave_id),
('over_line_id', '=', over_line.id),
('carry_forward_quantity', '=', deduct_amount)
], limit=1)
if existing_carryover:
return
carry_forward_date = fields.Date.to_date(carry_date) if carry_date else fields.Date.today()
self.env['overtime.carryover'].create({
'over_line_id': over_line.id,
'leave_id': leave_id,
'carry_forward_date': carry_forward_date,
'carry_forward_quantity': deduct_amount,
'carryover_type': 'leave',
'text': '',
})
def update_attendance(self, employee_id, start_date, end_date, code):
"""
更新考勤
+1
View File
@@ -72,6 +72,7 @@
'views/all_attendance_menu.xml',
'views/overtime_active_line_views.xml',
'views/vacation_meal_active_views.xml',
'views/overtime_carryover_views.xml',
'views/overtime_approval_inherit_header.xml',
'wizard/hr_attendance_month_wizard_views.xml',
'wizard/yuthon_vacation_import_wizard_views.xml',
+1
View File
@@ -40,6 +40,7 @@ from . import yuthon_overtime_line
from . import overtime_active_line
from . import vacation_meal_active
from . import duty_adjustment_schedule
from . import overtime_carryover
# from odoo.tools import SQL
# import odoo
# odoo.models.READ_GROUP_AGGREGATE.update({
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
class OvertimeCarryover(models.Model):
_name = 'overtime.carryover'
_description = '加班结转明细'
over_line_id = fields.Many2one('vacation.over.line', string='加班记录')
carry_forward_date = fields.Date(string="结转日期")
carry_forward_quantity = fields.Float(string="结转时长")
carryover_type = fields.Selection([('fee', '加班费结算'), ('leave', '假单结算')], string='加班补偿方式',
default='leave')
text = fields.Text(string='备注')
leave_id = fields.Many2one('hr.leave', string='假单')
class OvertimeCarryoverLine(models.Model):
_name = 'overtime.carryover.line'
class OvertimeUserLine(models.Model):
_name = 'overtime.carryover.wizard.line'
@@ -31,22 +31,88 @@ class VacationOvertimeActive(models.Model):
remaining_quantity = fields.Float(string="剩余数")
select_revoke = fields.Selection([('to_do', '待撤销'), ('done', '已撤销')], string='状态')
def update_vacation(self):
"""
结转时长处理逻辑:
1. 优先匹配 is_initial_number=True 的假期行记录
2. 不足时匹配指定条件的 is_initial_number=False 记录(按start_time升序)
3. 更新结转状态、剩余时长 + 填充对应记录的carry_forward_quantity/date
4. 为每条修改的记录创建加班结转明细(overtime.carryover + overtime.carryover.line)
"""
dec_2025_end = fields.Datetime.to_datetime('2025-12-31 23:59:59')
active_ids = self.env["vacation.overtime.active"].browse(
self._context.get('active_ids', self._context.get('active_id', []))
)
for record in active_ids:
overtime_lines = self.env['vacation.over.line'].search([
('employee_id', '=', record.employee_id.id),
('overtime_methods', '=', 'fee')
])
overtime_lines.write({
'workday': record.carry_forward_quantity,
'update_date': record.carry_forward_date,
'logging_data': 'qq',
})
remaining_to_carry = record.carry_forward_quantity
initial_lines = self.env['vacation.over.line'].search([
('is_initial_number', '=', True),
('employee_id', '=', record.employee_id.id)
])
# ========== 步骤1:处理初始记录 + 创建结转明细 ==========
for initial_line in initial_lines:
if remaining_to_carry <= 0:
break
line_duration = initial_line.duration or 0.0
actual_carry = min(line_duration, remaining_to_carry)
# 1. 更新初始记录的结转字段
initial_line.carry_forward_date = record.carry_forward_date
initial_line.carry_forward_quantity = actual_carry
if line_duration >= remaining_to_carry:
remaining_to_carry = 0.0
initial_line.carry_forward_state = 'partial'
else:
remaining_to_carry -= actual_carry
initial_line.carry_forward_state = 'completed'
# 核心新增:为当前初始记录创建加班结转明细
self._create_carryover_line(initial_line, record)
# ========== 步骤2:处理非初始记录 + 创建结转明细 ==========
if remaining_to_carry > 0:
non_initial_lines = self.env['vacation.over.line'].search([
('remaining_quantity', '!=', 0.0),
('employee_id', '=', record.employee_id.id),
('is_initial_number', '=', False),
('update_type', '=', 'add'),
('workflow_record_id', '!=', False),
('start_time', '>=', dec_2025_end),
('vacation_id', '!=', False)
], order='start_time ASC')
for non_line in non_initial_lines:
if remaining_to_carry <= 0:
break
line_remaining = non_line.remaining_quantity or 0.0
actual_carry = min(line_remaining, remaining_to_carry)
# 1. 更新非初始记录的结转字段
non_line.carry_forward_date = record.carry_forward_date
non_line.carry_forward_quantity = actual_carry
if line_remaining >= remaining_to_carry:
non_line.remaining_quantity -= actual_carry
remaining_to_carry = 0.0
non_line.carry_forward_state = 'partial'
else:
remaining_to_carry -= actual_carry
non_line.remaining_quantity = 0.0
non_line.carry_forward_state = 'completed'
# 核心新增:为当前非初始记录创建加班结转明细
self._create_carryover_line(non_line, record)
def _create_carryover_line(self, over_line, active_record):
self.env['overtime.carryover'].create({
'over_line_id': over_line.id,
'carry_forward_date': active_record.carry_forward_date,
'carry_forward_quantity': over_line.carry_forward_quantity,
'carryover_type': 'fee',
''
'text': '',
})
def all_unlink(self):
active_ids = self.env["vacation.overtime.active"].browse(
@@ -306,8 +306,7 @@ class YuthonVacationReports(models.Model):
for rec in over_ids:
inherit_lines = rec.overtime_ids.filtered(lambda l: l.text == '上月额度继承')
if inherit_lines:
rec.is_initial_number = True
inherit_lines.is_initial_number = True
def all_update_line(self):
records = self.env["yuthon.vacation.reports"].browse(
@@ -2103,7 +2102,6 @@ class VacationOverline(models.Model):
_name = 'vacation.over.line'
_description = '加班明细'
active = fields.Boolean('Active', default=True, tracking=True)
vacation_id = fields.Many2one('yuthon.vacation.reports', string='关联假期额度记录')
employee_id = fields.Many2one('hr.employee', string='员工', compute='_compute_employee_id', store=True)
@@ -2134,18 +2132,49 @@ class VacationOverline(models.Model):
workflow_id = fields.Many2one('soong.workflow', compute="_compute_overtime", string='审批类型', store=True)
start_time = fields.Datetime(string="开始时间", compute="_compute_overtime", store=True)
end_time = fields.Datetime(string="结束时间", compute="_compute_overtime", store=True)
carry_forward_date = fields.Date(string="结转日期")
duration = fields.Float(string="时长", compute="_compute_overtime", store=True)
carry_forward_quantity = fields.Float(string="结转时长")
validity = fields.Char(string="有效期")
carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果")
is_filing = fields.Boolean(string="归档")
carry_forward_state = fields.Selection([('pending', '待结转'),('completed', '已结转'), ('partial', '部分结转')
],string='结转状态',
default='pending',
)
remaining_quantity = fields.Float(string='剩余数', compute="_compute_last_balance")
logging_data = fields.Char(string="日志日志日志")
validity = fields.Char(string="有效期")
is_filing = fields.Boolean(string="归档")
carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果", default='success')
carry_forward_date = fields.Date(string="结转日期", compute='_compute_carry_forward_info', store=True)
carry_forward_quantity = fields.Float(string="结转时长", compute='_compute_carry_forward_info', store=True)
carry_forward_state = fields.Selection([
('pending', '待结转'),
('completed', '已结转'),
('partial', '部分结转')
], string='结转状态', compute='_compute_carry_forward_info', store=True, default='pending')
carryover_ids = fields.One2many('overtime.carryover', 'over_line_id', string='关联结转明细')
@api.depends('carryover_ids.carry_forward_quantity', 'carryover_ids.carry_forward_date', 'remaining_quantity')
def _compute_carry_forward_info(self):
"""
从overtime.carryover明细行自动计算:
1. 结转日期:取最新的结转明细日期
2. 结转时长:汇总所有关联明细的结转时长
3. 结转状态:按剩余时长+结转时长计算(复用原有逻辑)
"""
for rec in self:
rec.carry_forward_date = False
rec.carry_forward_quantity = 0.0
rec.carry_forward_state = 'pending'
if rec.carryover_ids:
# 结转时长:汇总所有明细的结转时长
total_carry = sum(rec.carryover_ids.mapped('carry_forward_quantity'))
rec.carry_forward_quantity = total_carry
# 结转日期:取最新的结转明细日期(无则取最早)
carry_dates = rec.carryover_ids.mapped('carry_forward_date')
if carry_dates:
rec.carry_forward_date = max(carry_dates)
current_remaining = rec.remaining_quantity or 0.0
if total_carry <= 0:
rec.carry_forward_state = 'pending'
elif total_carry >= current_remaining:
rec.carry_forward_state = 'completed'
else:
rec.carry_forward_state = 'partial'
def open_record1(self):
tree_id = self.env.ref("yuthon_hr_attendance.view_yuthon_vacation_overtime_tree").id
@@ -2160,6 +2189,16 @@ class VacationOverline(models.Model):
],
}
def open_overtime_carryover(self):
return {
"type": "ir.actions.act_window",
"name": "结转明细",
"res_model": "overtime.carryover",
"view_mode": "tree",
"target": "new",
'domain': [('over_line_id', '=', self.id)],
}
@api.model
def confirm(self):
# 1. 构建筛选域
@@ -96,4 +96,6 @@ access_export_date_wizard,export.date.wizard,model_export_date_wizard,base.group
access_vacation_meal_active,vacation.meal.active,model_vacation_meal_active,base.group_user,1,1,1,1
access_yuthon_vacation_import_wizard,yuthon.vacation.import.wizard,model_yuthon_vacation_import_wizard,base.group_user,1,1,1,1
access_overtime_change_wizard,overtime.change.wizard,model_overtime_change_wizard,base.group_user,1,1,1,1
access_duty_adjustment_schedule,duty.adjustment.schedule,model_duty_adjustment_schedule,base.group_user,1,1,1,1
access_duty_adjustment_schedule,duty.adjustment.schedule,model_duty_adjustment_schedule,base.group_user,1,1,1,1
access_overtime_carryover,overtime.carryover,model_overtime_carryover,base.group_user,1,1,1,1
access_overtime_carryover_line,overtime.carryover.line,model_overtime_carryover_line,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
96
97
98
99
100
101
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="view_overtime_carryover_tree" model="ir.ui.view">
<field name="name">overtime.carryover.tree</field>
<field name="model">overtime.carryover</field>
<field name="arch" type="xml">
<tree string="结转明细">
<field name="over_line_id"/>
<field name="carry_forward_date"/>
<field name="carry_forward_quantity"/>
<field name="carryover_type"/>
<field name="text"/>
<field name="leave_id"/>
</tree>
</field>
</record>
<record id="view_view_overtime_carryover_action" model="ir.actions.act_window">
<field name="name">结转明细</field>
<field name="res_model">overtime.carryover</field>
<field name="view_mode">tree</field>
</record>
</odoo>
@@ -694,7 +694,7 @@
<field name="name">vacation.over.line.overtime.tree</field>
<field name="model">vacation.over.line</field>
<field name="arch" type="xml">
<tree string="加班明细" editable="bottom" js_class="custom_overtime_export_button" default_order="entry_date asc">
<tree string="加班明细" editable="bottom" js_class="custom_overtime_export_button" default_order="start_time asc entry_date asc">
<field name="active"/>
<field name="overtime_name"/>
<field name="meal" column_invisible="1"/>
@@ -711,6 +711,7 @@
<field name="remaining_quantity"/>
<field name="is_visible" column_invisible="1"/>
<field name="validity"/>
<button name="open_overtime_carryover" string="结转详情" type="object" class="oe_highlight"/>
</tree>
</field>
</record>
+6
View File
@@ -170,6 +170,12 @@
action="yuthon_hr_attendance.view_yuthon_vacation_reports_action"
sequence="14"/>
<menuitem id="menu_view_view_overtime_carryover"
name="结转明细"
parent="hr_attendance_root_board_menu"
action="yuthon_hr_attendance.view_view_overtime_carryover_action"
sequence="1"/>
<menuitem id="menu_yuthon_overtime_report_board_work_brand"
name="加班明细"
parent="hr_attendance_root_board_menu"