@@ -13,12 +13,58 @@ class VacationOvertimeActive(models.Model):
|
||||
_name = 'vacation.overtime.active'
|
||||
_description = '加班情况归档'
|
||||
|
||||
employee_id = fields.Many2one('hr.employee', string='员工')
|
||||
department_id = fields.Many2one('hr.department', related='employee_id.department_id', string="部门")
|
||||
company_id = fields.Many2one('res.company', related='employee_id.company_id', string="公司")
|
||||
workflow_record_id = fields.Many2one('soong.workflow.record', string='流程')
|
||||
overtime_name = fields.Char(string="标题")
|
||||
carry_forward_date = fields.Date(string="结转日期")
|
||||
employee_id = fields.Many2one('hr.employee', string="员工")
|
||||
select_month = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'),
|
||||
('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'),
|
||||
('9', '九月'), ('10', '十月'), ('11', '十一月'),
|
||||
('12', '十二月')], string='月份',
|
||||
default=lambda self: str(datetime.now().month))
|
||||
select_year = fields.Selection(selection=[(str(i), str(i)) for i in range(2024, 2060)], string='年份',
|
||||
default=lambda self: str(datetime.now().year))
|
||||
last_balance = fields.Float(string="上年结余")
|
||||
valid_date = fields.Date(string="有效至")
|
||||
workday = fields.Float(string='工作日')
|
||||
public_holidays = fields.Float(string='公休日')
|
||||
vacation_sum = fields.Float(string='合计数')
|
||||
carry_forward_quantity = fields.Float(string="结转时长")
|
||||
carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果")
|
||||
carry_forward_date = fields.Date(string="结转日期")
|
||||
remaining_quantity = fields.Float(string="剩余数")
|
||||
|
||||
def open_record(self):
|
||||
self.ensure_one()
|
||||
current_year = int(self.select_year)
|
||||
current_month = int(self.select_month)
|
||||
cutoff_date = date(current_year, current_month, 1) - timedelta(days=1)
|
||||
cutoff_dt = datetime.combine(cutoff_date, datetime.min.time())
|
||||
dec_2025_end = datetime(2025, 12, 31, 23, 59, 59)
|
||||
domain = [
|
||||
('start_time', '<', cutoff_dt),
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
('employee_id', '=', self.employee_id.id)
|
||||
]
|
||||
if self.employee_id.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('employee_id', '=', self.employee_id.id),
|
||||
'|',
|
||||
('carry_forward_date', '=', False),
|
||||
('carry_forward_date', '<', cutoff_dt),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
'&', ('update_type', '=', 'add'),
|
||||
'&', ('workflow_record_id', '!=', False),
|
||||
'&', ('start_time', '>=', dec_2025_end),
|
||||
'&', ('vacation_id', '!=', False),
|
||||
('start_time', '<', cutoff_dt)
|
||||
]
|
||||
|
||||
tree_id = self.env.ref("yuthon_hr_attendance.view_yuthon_vacation_overtime_tree").id
|
||||
return {
|
||||
'views': [[tree_id, 'tree']],
|
||||
'name': f'加班明细',
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': 'vacation.over.line',
|
||||
'view_mode': 'tree,form',
|
||||
'domain': domain,
|
||||
'target': 'current'
|
||||
}
|
||||
|
||||
@@ -21,22 +21,23 @@ class YuthonOvertimeLine(models.Model):
|
||||
valid_date = fields.Date(string="有效至")
|
||||
workday = fields.Float(string='工作日')
|
||||
public_holidays = fields.Float(string='公休日')
|
||||
vacation_sum = fields.Float(string='合计数', compute='_compute_vacation_sum')
|
||||
vacation_sum = fields.Float(string='合计数', compute='_compute_vacation_sum', store=True)
|
||||
carry_forward_quantity = fields.Float(string="结转时长")
|
||||
carry_forward_date = fields.Date(string="结转日期")
|
||||
archive_state = fields.Selection([('to_do', '待归档'), ('done', '已归档')], default='to_do', string='状态')
|
||||
remaining_quantity = fields.Float(string="剩余数", compute='_compute_vacation_sum')
|
||||
|
||||
@api.depends('workday', 'public_holidays', 'last_balance', 'carry_forward_quantity')
|
||||
def _compute_vacation_sum(self):
|
||||
for rec in self:
|
||||
rec.vacation_sum = rec.public_holidays + rec.workday + rec.last_balance
|
||||
rec.remaining_quantity = rec.vacation_sum - rec.carry_forward_quantity
|
||||
rec.remaining_quantity = max((rec.vacation_sum - rec.carry_forward_quantity), 0)
|
||||
|
||||
def open_record(self):
|
||||
self.ensure_one()
|
||||
current_year = int(self.select_year)
|
||||
current_month = int(self.select_month)
|
||||
cutoff_date = date(current_year, current_month, 1) + relativedelta(months=1) - timedelta(days=1)
|
||||
cutoff_date = date(current_year, current_month, 1) - timedelta(days=1)
|
||||
cutoff_dt = datetime.combine(cutoff_date, datetime.min.time())
|
||||
dec_2025_end = datetime(2025, 12, 31, 23, 59, 59)
|
||||
domain = [
|
||||
@@ -47,7 +48,9 @@ class YuthonOvertimeLine(models.Model):
|
||||
if self.employee_id.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('employee_id', '=', self.employee_id.id),
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
'|',
|
||||
('carry_forward_date', '=', False),
|
||||
('carry_forward_date', '<', cutoff_dt),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
@@ -67,4 +70,126 @@ class YuthonOvertimeLine(models.Model):
|
||||
'view_mode': 'tree,form',
|
||||
'domain': domain,
|
||||
'target': 'current'
|
||||
}
|
||||
}
|
||||
|
||||
def carryover_active(self):
|
||||
overtime_ids = self.env["yuthon.overtime.line"].browse(
|
||||
self._context.get('active_ids', self._context.get('active_id'))
|
||||
)
|
||||
for overtime_id in overtime_ids:
|
||||
self.env["vacation.overtime.active"].create({
|
||||
'employee_id': overtime_id.employee_id.id,
|
||||
'select_month': overtime_id.select_month,
|
||||
'select_year': overtime_id.select_year,
|
||||
'last_balance': overtime_id.last_balance,
|
||||
'valid_date': overtime_id.valid_date,
|
||||
'workday': overtime_id.workday,
|
||||
'public_holidays': overtime_id.public_holidays,
|
||||
'vacation_sum': overtime_id.vacation_sum,
|
||||
'carry_forward_quantity': overtime_id.carry_forward_quantity,
|
||||
'carry_forward_date': overtime_id.carry_forward_date,
|
||||
'remaining_quantity': overtime_id.remaining_quantity,
|
||||
})
|
||||
overtime_id.archive_state = 'done'
|
||||
remaining_to_allocate = overtime_id.carry_forward_quantity
|
||||
current_year = int(overtime_id.select_year)
|
||||
current_month = int(overtime_id.select_month)
|
||||
cutoff_date = date(current_year, current_month, 1) - timedelta(days=1)
|
||||
cutoff_dt = datetime.combine(cutoff_date, datetime.min.time())
|
||||
dec_2025_end = datetime(2025, 12, 31, 23, 59, 59)
|
||||
domain = [
|
||||
('start_time', '<', cutoff_dt),
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
('employee_id', '=', overtime_id.employee_id.id)
|
||||
]
|
||||
|
||||
if overtime_id.employee_id.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('employee_id', '=', overtime_id.employee_id.id),
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
'&', ('update_type', '=', 'add'),
|
||||
'&', ('workflow_record_id', '!=', False),
|
||||
'&', ('start_time', '>=', dec_2025_end),
|
||||
'&', ('vacation_id', '!=', False),
|
||||
('start_time', '<', cutoff_dt)
|
||||
]
|
||||
over_lines = self.env['vacation.over.line'].search(domain, order='start_time ASC')
|
||||
# 4. 按顺序分配结转时长
|
||||
for rec in over_lines:
|
||||
if remaining_to_allocate <= 0:
|
||||
break # 分配完毕,退出循环
|
||||
|
||||
# 计算当前记录可分配的最大时长(不超过剩余时长)
|
||||
max_allocation = rec.duration - rec.carry_forward_quantity
|
||||
if max_allocation <= 0:
|
||||
continue # 该记录已无剩余可分配时长
|
||||
|
||||
if rec.is_initial_number and rec.last_balance > 1e-6:
|
||||
need_deduct_total = min(remaining_to_allocate, rec.last_balance)
|
||||
remaining_deduct = need_deduct_total
|
||||
|
||||
# 1. 优先扣减 vacation_id.last_workday
|
||||
if rec.vacation_id and rec.vacation_id.last_workday > 1e-6:
|
||||
deduct_workday = min(remaining_deduct, rec.vacation_id.last_workday)
|
||||
rec.vacation_id.last_workday -= deduct_workday
|
||||
remaining_deduct -= deduct_workday # 剩余待扣减金额减少
|
||||
rec.vacation_id.write({'last_workday': rec.vacation_id.last_workday})
|
||||
|
||||
# 2.扣减 vacation_id.last_holiday
|
||||
if remaining_deduct > 1e-6 and rec.vacation_id and rec.vacation_id.last_holiday > 1e-6:
|
||||
deduct_holiday = min(remaining_deduct, rec.vacation_id.last_holiday)
|
||||
rec.vacation_id.last_holiday -= deduct_holiday
|
||||
remaining_deduct -= deduct_holiday
|
||||
rec.vacation_id.write({'last_holiday': rec.vacation_id.last_holiday})
|
||||
|
||||
# 确定本次分配的时长
|
||||
if remaining_to_allocate >= max_allocation:
|
||||
# 足够分配完当前记录的剩余时长
|
||||
allocate_amount = max_allocation
|
||||
new_state = 'completed'
|
||||
remaining_to_allocate -= allocate_amount
|
||||
else:
|
||||
# 只能分配部分时长
|
||||
allocate_amount = remaining_to_allocate
|
||||
new_state = 'partial'
|
||||
remaining_to_allocate = 0
|
||||
|
||||
rec.write({
|
||||
'carry_forward_quantity': rec.carry_forward_quantity + allocate_amount,
|
||||
'carry_forward_state': new_state,
|
||||
'carry_forward_date': overtime_id.carry_forward_date
|
||||
})
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'type': 'success',
|
||||
'message': '归档成功',
|
||||
'sticky': False,
|
||||
}
|
||||
}
|
||||
|
||||
@api.model
|
||||
def carryover_details(self):
|
||||
domain = [('vacation_sum', '>', 40),
|
||||
('remaining_quantity', '!=', 0.0)
|
||||
]
|
||||
overtime_lines = self.env['yuthon.overtime.line'].search(domain)
|
||||
for overtime in overtime_lines:
|
||||
overtime.carry_forward_date = fields.Date.today()
|
||||
overtime.carry_forward_quantity += overtime.remaining_quantity - 40
|
||||
tree_id = self.env.ref("yuthon_hr_attendance.view_yuthon_overtime_line_tree1").id
|
||||
return {
|
||||
"name": _("结转明细"),
|
||||
"type": "ir.actions.act_window",
|
||||
"views": [(tree_id, "tree")],
|
||||
"res_model": "yuthon.overtime.line",
|
||||
"target": "current",
|
||||
"domain": domain
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1032,15 +1032,15 @@ class YuthonVacationReports(models.Model):
|
||||
base_month = int(base_line.select_month)
|
||||
base_date = date(base_year, base_month, 1)
|
||||
self.env['yuthon.overtime.line'].search([]).unlink()
|
||||
# 上上个月第一天(用于select_month赋值)
|
||||
last_month_first = (base_date - relativedelta(months=2)).replace(day=1)
|
||||
# 上上个月最后一天(时间筛选的截止点)
|
||||
current_month_last = (base_date - relativedelta(months=1)).replace(day=1) - timedelta(days=1)
|
||||
current_date = base_date - timedelta(days=1)
|
||||
current_month_last = base_date - relativedelta(months=1) - timedelta(days=1)
|
||||
current_month_last_dt = datetime.combine(current_month_last, datetime.min.time())
|
||||
dec_2025_end = datetime(2025, 12, 31, 23, 59, 59)
|
||||
if self.env.user.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
'|',
|
||||
('carry_forward_date', '=', False),
|
||||
('carry_forward_date', '<', current_month_last_dt),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
@@ -1057,7 +1057,8 @@ class YuthonVacationReports(models.Model):
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
('start_time', '<', current_month_last_dt)
|
||||
]
|
||||
over_lines = self.env['vacation.over.line'].search(domain)
|
||||
all_records = self.env['vacation.over.line'].search(domain)
|
||||
over_lines = all_records.filtered(lambda r: abs(r.remaining_quantity) > 1e-6)
|
||||
if not over_lines:
|
||||
return
|
||||
|
||||
@@ -1077,8 +1078,8 @@ class YuthonVacationReports(models.Model):
|
||||
'carry_forward_quantity': 0.0,
|
||||
'carry_forward_date': line.carry_forward_date,
|
||||
'remaining_quantity': 0.0,
|
||||
'select_month': str(last_month_first.month),
|
||||
'select_year': str(last_month_first.year)
|
||||
'select_month': str(current_date.month),
|
||||
'select_year': str(current_date.year)
|
||||
}
|
||||
|
||||
summary = employee_summary[emp_id]
|
||||
@@ -1103,13 +1104,15 @@ class YuthonVacationReports(models.Model):
|
||||
base_month = int(base_line.select_month)
|
||||
base_date = date(base_year, base_month, 1)
|
||||
self.env['yuthon.overtime.line'].search([]).unlink()
|
||||
next_month_first = (base_date + relativedelta(months=1)).replace(day=1)
|
||||
next_month_last = (base_date + relativedelta(months=2)).replace(day=1) - timedelta(days=1)
|
||||
current_date = (base_date + relativedelta(months=2)).replace(day=1) - timedelta(days=1)
|
||||
next_month_last = (base_date + relativedelta(months=1)).replace(day=1) - timedelta(days=1)
|
||||
next_month_last_dt = datetime.combine(next_month_last, datetime.min.time())
|
||||
dec_2025_end = datetime(2025, 12, 31, 23, 59, 59)
|
||||
if self.env.user.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
'|',
|
||||
('carry_forward_date', '=', False),
|
||||
('carry_forward_date', '<', next_month_last_dt),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
@@ -1126,7 +1129,8 @@ class YuthonVacationReports(models.Model):
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
('start_time', '<', next_month_last_dt)
|
||||
]
|
||||
over_lines = self.env['vacation.over.line'].search(domain)
|
||||
all_records = self.env['vacation.over.line'].search(domain)
|
||||
over_lines = all_records.filtered(lambda r: abs(r.remaining_quantity) > 1e-6)
|
||||
if not over_lines:
|
||||
return
|
||||
|
||||
@@ -1144,8 +1148,8 @@ class YuthonVacationReports(models.Model):
|
||||
'public_holidays': 0.0,
|
||||
'carry_forward_quantity': 0.0,
|
||||
'carry_forward_date': line.carry_forward_date,
|
||||
'select_month': str(next_month_first.month),
|
||||
'select_year': str(next_month_first.year)
|
||||
'select_month': str(current_date.month),
|
||||
'select_year': str(current_date.year)
|
||||
}
|
||||
|
||||
summary = employee_summary[emp_id]
|
||||
@@ -1177,7 +1181,9 @@ class YuthonVacationReports(models.Model):
|
||||
# 原有domain(先筛选出基础数据)
|
||||
if self.env.user.company_id.name == '集团本部':
|
||||
domain = [
|
||||
('remaining_quantity', '!=', 0.0),
|
||||
'|',
|
||||
('carry_forward_date', '=', False),
|
||||
('carry_forward_date', '<', current_month_first_dt),
|
||||
'|',
|
||||
('is_initial_number', '=', True),
|
||||
'&', ('is_initial_number', '=', False),
|
||||
@@ -1195,7 +1201,8 @@ class YuthonVacationReports(models.Model):
|
||||
('start_time', '<', current_month_first_dt)
|
||||
]
|
||||
|
||||
over_lines = self.env['vacation.over.line'].search(domain)
|
||||
all_records = self.env['vacation.over.line'].search(domain)
|
||||
over_lines = all_records.filtered(lambda r: abs(r.remaining_quantity) > 1e-6)
|
||||
if not over_lines:
|
||||
return
|
||||
|
||||
|
||||
@@ -8,8 +8,9 @@ export class CustomAttendanceVacationConfirmController extends ListController {
|
||||
super.setup();
|
||||
}
|
||||
|
||||
async getAction(lock_type) {
|
||||
await this.orm.call('vacation.over.line', 'confirm', []);
|
||||
async getAction() {
|
||||
const res = await this.orm.call('yuthon.overtime.line', 'carryover_details', []);
|
||||
this.actionService.doAction(res)
|
||||
}
|
||||
async getNextAction(lock_type) {
|
||||
await this.orm.call('yuthon.vacation.reports', 'next_month', []);
|
||||
|
||||
@@ -5,13 +5,18 @@
|
||||
<field name="model">vacation.overtime.active</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="加班情况(归档)" editable="bottom">
|
||||
<field name="overtime_name"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<field name="carry_forward_quantity"/>
|
||||
<field name="remaining_quantity"/>
|
||||
<field name="select_year"/>
|
||||
<field name="select_month"/>
|
||||
<field name="last_balance"/>
|
||||
<field name="valid_date"/>
|
||||
<field name="workday"/>
|
||||
<field name="public_holidays"/>
|
||||
<field name="vacation_sum" options='{ "fg_color": "red: vacation_sum > 40"}'/>
|
||||
<field name="carry_forward_quantity" options='{ "fg_color": "blue: carry_forward_quantity > 0"}'/>
|
||||
<field name="carry_forward_date"/>
|
||||
<field name="carry_result"/>
|
||||
<field name="remaining_quantity"/>
|
||||
<button name="open_record" type="object" string="详情" class="oe_highlight"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<odoo>
|
||||
<record id="view_yuthon_overtime_line_tree1" model="ir.ui.view">
|
||||
<field name="name">yuthon.overtime.line.tree</field>
|
||||
<field name="model">yuthon.overtime.line</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="加班结转报表" editable="bottom" create="0">
|
||||
<header>
|
||||
<button name="carryover_active" type="object" string="归档" dclass="oe_highlight"/>
|
||||
</header>
|
||||
<field name="employee_id"/>
|
||||
<field name="select_year"/>
|
||||
<field name="select_month"/>
|
||||
<field name="last_balance"/>
|
||||
<field name="valid_date"/>
|
||||
<field name="workday"/>
|
||||
<field name="public_holidays"/>
|
||||
<field name="vacation_sum" options='{ "fg_color": "red: vacation_sum > 40"}'/>
|
||||
<field name="carry_forward_quantity" options='{ "fg_color": "blue: carry_forward_quantity > 0"}'/>
|
||||
<field name="carry_forward_date"/>
|
||||
<field name="remaining_quantity"/>
|
||||
<field name="archive_state"/>
|
||||
<button name="open_record" type="object" string="详情" class="oe_highlight"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_overtime_line_tree" model="ir.ui.view">
|
||||
<field name="name">yuthon.overtime.line.tree</field>
|
||||
<field name="model">yuthon.overtime.line</field>
|
||||
@@ -12,17 +36,13 @@
|
||||
<field name="valid_date"/>
|
||||
<field name="workday"/>
|
||||
<field name="public_holidays"/>
|
||||
<field name="vacation_sum"/>
|
||||
<field name="carry_forward_quantity"/>
|
||||
<field name="vacation_sum" options='{ "fg_color": "red: vacation_sum > 40"}'/>
|
||||
<field name="carry_forward_quantity" options='{ "fg_color": "blue: carry_forward_quantity > 0"}'/>
|
||||
<field name="carry_forward_date"/>
|
||||
<field name="remaining_quantity"/>
|
||||
<field name="archive_state"/>
|
||||
<button name="open_record" type="object" string="详情" class="oe_highlight"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_yuthon_overtime_line_action" model="ir.actions.act_window">
|
||||
<field name="name">加班合计数报表</field>
|
||||
<field name="res_model">yuthon.overtime.line</field>
|
||||
<field name="view_mode">tree</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -651,11 +651,6 @@
|
||||
<filter string="员工" name="group_by_employee_id" domain="[]" context="{'group_by': 'employee_id'}"/>
|
||||
<filter name="now_month" string="当月" domain="[('start_time', '>=', (context_today() + relativedelta(day=1)).strftime('%Y-%m-%d')),
|
||||
('start_time', '<', (context_today() + relativedelta(months=+1, day=1)).strftime('%Y-%m-%d'))]"/>
|
||||
<header_search class="header-search-inline">
|
||||
<field name="start_time"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
</header_search>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
Reference in New Issue
Block a user