添加功能
This commit is contained in:
@@ -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):
|
||||
"""
|
||||
更新考勤
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user