From 741db154c70137b71aea4ce1c585e71983cfec47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E9=B9=8F=E5=AE=87?= <9664676+pengyuthon@user.noreply.gitee.com> Date: Sun, 8 Feb 2026 21:37:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- yuthon_hr_attendance/__manifest__.py | 1 + yuthon_hr_attendance/models/__init__.py | 1 + .../models/vacation_meal_active.py | 52 +++++++++++ .../models/yuthon_vacation_reports.py | 92 ++++++++++++++----- .../security/ir.model.access.csv | 3 +- .../views/vacation_meal_active_views.xml | 34 +++++++ .../views/yuthon_vacation_reports_views.xml | 35 ++----- yuthon_work_brand/views/hr_menus.xml | 2 +- 8 files changed, 170 insertions(+), 50 deletions(-) create mode 100644 yuthon_hr_attendance/models/vacation_meal_active.py create mode 100644 yuthon_hr_attendance/views/vacation_meal_active_views.xml diff --git a/yuthon_hr_attendance/__manifest__.py b/yuthon_hr_attendance/__manifest__.py index b423024e2..9942613fd 100644 --- a/yuthon_hr_attendance/__manifest__.py +++ b/yuthon_hr_attendance/__manifest__.py @@ -70,6 +70,7 @@ 'views/yuthon_parenting_allocate_views.xml', 'views/all_attendance_menu.xml', 'views/overtime_active_line_views.xml', + 'views/vacation_meal_active_views.xml', 'views/overtime_approval_inherit_header.xml', diff --git a/yuthon_hr_attendance/models/__init__.py b/yuthon_hr_attendance/models/__init__.py index feb5cc8d5..98a88c0cb 100644 --- a/yuthon_hr_attendance/models/__init__.py +++ b/yuthon_hr_attendance/models/__init__.py @@ -38,6 +38,7 @@ from . import yuthon_parenting_allocate from . import vacation_overtime_active from . import yuthon_overtime_line from . import overtime_active_line +from . import vacation_meal_active # from odoo.tools import SQL # import odoo # odoo.models.READ_GROUP_AGGREGATE.update({ diff --git a/yuthon_hr_attendance/models/vacation_meal_active.py b/yuthon_hr_attendance/models/vacation_meal_active.py new file mode 100644 index 000000000..b8b453263 --- /dev/null +++ b/yuthon_hr_attendance/models/vacation_meal_active.py @@ -0,0 +1,52 @@ +from datetime import datetime, timedelta, time +from email.policy import default +from operator import index +from odoo import models, fields, api, _ +from odoo.exceptions import ValidationError +import calendar +from datetime import date +from dateutil.relativedelta import relativedelta +from odoo.exceptions import UserError, ValidationError + + +class VacationMealActive(models.Model): + _name = 'vacation.meal.active' + _description = '误餐费归档' + + meal_name = fields.Char(string='标题') + line_id = fields.Many2one('vacation.meal.line', string='关联误餐明细') + employee_id = fields.Many2one('hr.employee', string='员工') + workflow_id = fields.Many2one('soong.workflow', string='审批类型') + start_time = fields.Datetime(string='开始时间') + end_time = fields.Datetime(string='结束时间') + duration = fields.Float(string='时长') + update_lines = fields.Float(string='额度') + reasons = fields.Text(string='申请事由') + meal = fields.Selection([('yes', '是'), ('no', '否')], string="误餐费", default=None) + select_revoke = fields.Selection([('to_do', '待撤销'), ('done', '已撤销')], string='状态') + carry_forward_date = fields.Date(string="结转日期") + carry_forward_state = fields.Selection([('pending', '待结转'), ('completed', '已结转') + ], string='结转状态', + default='pending', + ) + + def revoke(self): + self.ensure_one() + self.select_revoke = 'done' + self.line_id.is_filing = True + report_id = self.env['yuthon.vacation.reports'].search([('employee_id', '=', self.employee_id.id), + ('vacation_date', '=', (self.start_time.date()).replace(day=1)), + ]) + meal_id = self.env['vacation.meal.line'].search([('vacation_id', '=', report_id.id), + ('update_type', '=', 'reduce') + ]) + if meal_id: + meal_id.unlink() + + def all_revoke(self): + meal_ids = self.env["vacation.meal.active"].browse( + self._context.get('active_ids', self._context.get('active_id'))) + for meal_id in meal_ids: + if meal_id.select_revoke == 'done': + raise UserError("该记录已撤销") + meal_id.revoke() diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index 8bdfc11cd..85336cdee 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -994,6 +994,8 @@ class YuthonVacationReports(models.Model): 'used_this_year1': last_month_record.used_this_year1, } rec.write(inherit_vals) + if rec.meal_number: + self._update_meal_lines(rec, [rec], rec.meal_number) if rec.leave: leave_hours = rec.leave * 8 self._update_leave_lines(rec, [rec], leave_hours) @@ -1325,6 +1327,24 @@ class YuthonVacationReports(models.Model): 'text': f"上月额度继承", }) + def _update_meal_lines(self, report, allocations, number_of_hours_display): + for alloc in allocations: + existing_line = self.env['vacation.meal.line'].search([ + ('vacation_id', '=', report.id), + ('employee_id', '=', report.employee_id.id), + ('text', '=', '上月额度继承'), + ], limit=1) + if existing_line: + continue + self.env['vacation.meal.line'].sudo().create({ + 'vacation_id': report.id, + 'employee_id': report.employee_id.id, + 'update_type': 'add', + 'update_date': fields.Date.today(), + 'update_lines': number_of_hours_display, + 'text': f"上月额度继承", + }) + def log(self): return { "type": "ir.actions.act_window", @@ -1883,34 +1903,64 @@ class VacationMealLine(models.Model): rec._compute_employee_id() rec._compute_overtime() - @api.model def confirm(self): - now = datetime.now() - current_month = now.month - current_year = now.year - if current_month == 1: - last_month = 12 - last_month_year = current_year - 1 - else: - last_month = current_month - 1 - last_month_year = current_year - last_month_first_day = datetime(last_month_year, last_month, 1) - current_month_first_day = datetime(current_year, current_month, 1) - valid_leaves = self.search([ - ('start_date', '>=', fields.Datetime.to_string(last_month_first_day)), - ('start_date', '<', fields.Datetime.to_string(current_month_first_day)), - ('id', 'in', self.ids) - ]) - if valid_leaves: - valid_leaves.write({'is_filing': True}) + valid_leaves = self.env['vacation.meal.line'].browse( + self._context.get('active_ids', self._context.get('active_id'))) + emp_month_summary = {} + for rec in valid_leaves: + rec_year = rec.start_time.year + rec_month = rec.start_time.month + rec.write({'is_filing': True}) + emp_id = rec.employee_id.id + meal_active_vals = { + 'line_id': rec.id, + 'meal_name': rec.meal_name, + 'employee_id': emp_id, + 'workflow_id': rec.workflow_id.id if rec.workflow_id else False, + 'start_time': rec.start_time, + 'end_time': rec.end_time, + 'duration': rec.duration, + 'update_lines': rec.update_lines, + 'reasons': rec.reasons, + 'meal': rec.meal, + 'carry_forward_date': fields.Date.today(), + 'carry_forward_state': 'completed', + 'select_revoke': 'to_do' + } + self.env['vacation.meal.active'].create(meal_active_vals) + + summary_key = (emp_id, rec_year, rec_month) + if summary_key not in emp_month_summary: + emp_month_summary[summary_key] = 0.0 + emp_month_summary[summary_key] += rec.update_lines or 0.0 + + # 4. 第二步:遍历汇总结果,为每个【员工+年月】创建一条vacation.over.line + for (emp_id, rec_year, rec_month), total_amount in emp_month_summary.items(): + if total_amount <= 1e-6: + continue + report_id = self.env['yuthon.vacation.reports'].search([ + ('employee_id', '=', emp_id), + ('vacation_date', '=', fields.Date.from_string(f'{rec_year}-{rec_month}-01')) + ], limit=1) + if report_id: + self.env['vacation.meal.line'].create([{ + 'vacation_id': report_id.id, + 'employee_id': emp_id, + 'update_type': 'reduce', + 'update_lines': total_amount, + 'update_date': fields.Date.today(), + 'is_void': False, + 'text': f'{rec_month}月份误餐结转', + }]) @api.depends('workflow_record_id') def _compute_overtime(self): for rec in self: rec.start_date = False res_model = rec.workflow_record_id.res_model + rec.meal = 'yes' res_id = rec.workflow_record_id.res_id - if res_model and res_id and res_model in self.env: + if res_model and res_id and res_model in self.env and rec.update_type == 'add': if res_model == 'overtime.approval': overtime_id = rec.env[res_model].search([('id', '=', res_id)], limit=1) if overtime_id: @@ -1940,7 +1990,7 @@ class VacationMealLine(models.Model): rec.start_time = start_time rec.end_time = end_time rec.meal = go_approval_id.meal - + rec.workflow_id = rec.workflow_record_id.workflow_id.id rec.meal_name = rec.workflow_record_id.name or '' diff --git a/yuthon_hr_attendance/security/ir.model.access.csv b/yuthon_hr_attendance/security/ir.model.access.csv index 3adf884a5..b0da28464 100644 --- a/yuthon_hr_attendance/security/ir.model.access.csv +++ b/yuthon_hr_attendance/security/ir.model.access.csv @@ -91,4 +91,5 @@ access_yuthon_parenting_allocate,yuthon.parenting.allocate,model_yuthon_parentin access_vacation_overtime_active,vacation.overtime.active,model_vacation_overtime_active,base.group_user,1,1,1,1 access_yuthon_overtime_line,yuthon.overtime.line,model_yuthon_overtime_line,base.group_user,1,1,1,1 -access_overtime_active_line,overtime.active.line,model_overtime_active_line,base.group_user,1,1,1,1 \ No newline at end of file +access_overtime_active_line,overtime.active.line,model_overtime_active_line,base.group_user,1,1,1,1 +access_vacation_meal_active,vacation.meal.active,model_vacation_meal_active,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_hr_attendance/views/vacation_meal_active_views.xml b/yuthon_hr_attendance/views/vacation_meal_active_views.xml new file mode 100644 index 000000000..2d35b2b08 --- /dev/null +++ b/yuthon_hr_attendance/views/vacation_meal_active_views.xml @@ -0,0 +1,34 @@ + + + + vacation.meal.active.tree + vacation.meal.active + + +
+
+ + + + + + + + + + + + + +