From f82c96c3cadbd1d191ee059ecd670098a125d407 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: Thu, 22 Jan 2026 17:55:58 +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 | 2 + .../models/overtime_approval.py | 11 ++- .../models/yuthon_holiday_statement.py | 1 + .../models/yuthon_vacation_reports.py | 68 ++++++++++++++++- .../static/src/js/button_vacation_confirm.js | 23 ++++++ .../static/src/js/button_vacation_put.js | 54 ++++++++++--- .../src/xml/button_vacation_confirm.xml | 10 +++ .../views/yuthon_holiday_statement.xml | 15 +++- .../views/yuthon_vacation_reports_views.xml | 76 +++++++++++++++---- yuthon_work_brand/views/hr_menus.xml | 29 +++++++ 10 files changed, 257 insertions(+), 32 deletions(-) create mode 100644 yuthon_hr_attendance/static/src/js/button_vacation_confirm.js create mode 100644 yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml diff --git a/yuthon_hr_attendance/__manifest__.py b/yuthon_hr_attendance/__manifest__.py index 9afb7c4d6..092754072 100644 --- a/yuthon_hr_attendance/__manifest__.py +++ b/yuthon_hr_attendance/__manifest__.py @@ -95,6 +95,8 @@ 'yuthon_hr_attendance/static/src/xml/button_detail_wechat.xml', 'yuthon_hr_attendance/static/src/components/*/*.xml', 'yuthon_hr_attendance/static/src/css/*.css', + 'yuthon_hr_attendance/static/src/js/button_vacation_confirm.js', + 'yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml', 'yuthon_hr_attendance/static/src/js/planning_gantt_renderer.js', 'yuthon_hr_attendance/static/src/xml/planning_gantt_renderer.xml', 'yuthon_hr_attendance/static/src/js/button_vacation_put.js', diff --git a/yuthon_hr_attendance/models/overtime_approval.py b/yuthon_hr_attendance/models/overtime_approval.py index f31fd0a63..179b35065 100644 --- a/yuthon_hr_attendance/models/overtime_approval.py +++ b/yuthon_hr_attendance/models/overtime_approval.py @@ -219,9 +219,14 @@ class OvertimeApprovals(models.Model): if company_id: company = self.env['res.company'].browse(company_id) if company.name == '天河物管': - if vals.get('duration'): - if (vals['duration']) % 1 != 0.5: - raise ValidationError("时长不对!请重新编辑") + duration = vals.get('duration') + if duration is None: + raise ValidationError("时长不能为空!请填写有效的加班时长") + if duration < 1: + raise ValidationError(f"时长不能小于1!当前填写的是{duration},请重新编辑") + if (duration * 2) % 1 != 0: + raise ValidationError( + f"时长格式错误!请填写1、1.5、2、2.5...等以0.5递增的数值,当前填写的是{duration}") return super(OvertimeApprovals, self).create(vals_list) @api.onchange('is_issues') diff --git a/yuthon_hr_attendance/models/yuthon_holiday_statement.py b/yuthon_hr_attendance/models/yuthon_holiday_statement.py index c8965af07..4841522a4 100644 --- a/yuthon_hr_attendance/models/yuthon_holiday_statement.py +++ b/yuthon_hr_attendance/models/yuthon_holiday_statement.py @@ -12,6 +12,7 @@ class InheritHrLeave(models.Model): caption = fields.Char(string="标题", compute="_compute_application_reason") employee_id = fields.Many2one('hr.employee', string="发起人") leave_stats = fields.Char(string="请假统计", compute="_compute_leave_stats") + is_filing = fields.Boolean(string="归档") application_reason = fields.Char(string="申请原因", compute="_compute_application_reason") @api.depends('batch_id', 'hour_batch_id') diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index 48eee60e9..e14f9dcfa 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -85,7 +85,7 @@ class YuthonVacationReports(models.Model): attendance_message_log_ids = fields.One2many('attendance.message.log', 'vacation_id') admin_confirms = fields.Boolean(string="是否管理员确认") - + is_filing = fields.Boolean(string="归档") vacation_state = fields.Selection( selection=[ ('not_sent', "未发送"), @@ -986,7 +986,7 @@ class VacationOverline(models.Model): carry_forward_quantity = fields.Float(string="结转时长") validity = fields.Char(string="有效期") carry_result = fields.Selection([('success', '成功'), ('failed', '失败')], string="结转结果") - is_active = fields.Boolean(string="归档") + is_filing = fields.Boolean(string="归档") carry_forward_state = fields.Selection([('pending', '待结转'),('completed', '已结转'), ('partial', '部分结转') ],string='结转状态', default='pending', @@ -994,7 +994,28 @@ class VacationOverline(models.Model): remaining_quantity = fields.Float(string='剩余数', compute="_compute_remaining_quantity", store=True) def confirm(self): - pass + domain = [ + '|', + ['is_initial_number', '=', True], + '&', + ['is_initial_number', '=', False], + '&', + ['update_type', '=', 'add'], + '&', + ['workflow_record_id', '!=', False], + '!', + '&', + ['start_time', '>=', '2025-12-01 00:00:00'], + ['start_time', '<', '2026-01-01 00:00:00'] + ] + filtered_records = self.search(domain) + if not filtered_records: + return + records_to_update = filtered_records.filtered( + lambda r: fields.float_compare(r.total, r.carry_forward_quantity) == 0 + ) + if records_to_update: + records_to_update.write({'is_filing': True}) def update_line(self): over_ids = self.env["vacation.over.line"].browse( @@ -1181,9 +1202,15 @@ class VacationMealLine(models.Model): _description = '误餐明细' vacation_id = fields.Many2one('yuthon.vacation.reports', string='关联假期额度记录') - employee_id = fields.Many2one('hr.employee', string='员工') + is_filing = fields.Boolean(string="归档") + employee_id = fields.Many2one('hr.employee', string='员工', compute='_compute_employee_id', store=True) + department_id = fields.Many2one('hr.department', related='employee_id.department_id', string="部门", store=True) + company_id = fields.Many2one('res.company', related='employee_id.company_id', string="公司", store=True) workflow_record_id = fields.Many2one('soong.workflow.record', string='流程') + vacation_date = fields.Date(related='vacation_id.vacation_date', string='误餐日期') update_type= fields.Selection([('add', '增加'), ('reduce', '减少')], string='更新类型') + meal_name = fields.Char(string="标题", compute="_compute_overtime", store=True) + start_date = fields.Date(string="日期", compute="_compute_overtime", store=True) update_date = fields.Date(string='更新时间') update_lines = fields.Float(string='更新额度') is_soon = fields.Boolean(string="流程修改") @@ -1194,6 +1221,39 @@ class VacationMealLine(models.Model): text = fields.Text(string='备注') is_confirm = fields.Boolean(string="是否确认") + def update_line(self): + meal_ids = self.env["vacation.meal.line"].browse( + self._context.get('active_ids', self._context.get('active_id'))) + for rec in meal_ids: + rec._compute_employee_id() + rec._compute_overtime() + + @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 + res_id = rec.workflow_record_id.res_id + if res_model and res_id and res_model in self.env: + if res_model == 'overtime.approval': + overtime_id = rec.env[res_model].search([('id', '=', res_id)], limit=1) + if overtime_id: + rec.start_date = overtime_id.start_time.date() or '' + else: + go_approval_id = rec.env[res_model].search([('id', '=', res_id)], limit=1) + if go_approval_id: + rec.start_date = go_approval_id.date_start.date() or '' + rec.meal_name = rec.workflow_record_id.name or '' + + + @api.depends('vacation_id') + def _compute_employee_id(self): + for rec in self: + if rec.vacation_id: + rec.employee_id = rec.vacation_id.employee_id.id + elif not rec.employee_id: + rec.employee_id = False + def action_void(self): self.is_void = True diff --git a/yuthon_hr_attendance/static/src/js/button_vacation_confirm.js b/yuthon_hr_attendance/static/src/js/button_vacation_confirm.js new file mode 100644 index 000000000..42d44f38c --- /dev/null +++ b/yuthon_hr_attendance/static/src/js/button_vacation_confirm.js @@ -0,0 +1,23 @@ +/** @odoo-module */ +import { ListController } from "@web/views/list/list_controller"; +import { registry } from '@web/core/registry'; +import { listView } from '@web/views/list/list_view'; + +export class CustomAttendanceVacationConfirmController extends ListController { + setup() { + super.setup(); + } + async openConfirm() { + await this.model.orm.call( + 'vacation.over.line', + 'confirm', + [] + ); + } +} + +registry.category("views").add("custom_vacation_confirm_button", { + ...listView, + Controller: CustomAttendanceVacationConfirmController, + buttonTemplate: "vacation_confirm_module.ListView.Buttons", +}); \ No newline at end of file diff --git a/yuthon_hr_attendance/static/src/js/button_vacation_put.js b/yuthon_hr_attendance/static/src/js/button_vacation_put.js index 965dbcbb8..9d1cfd6e1 100644 --- a/yuthon_hr_attendance/static/src/js/button_vacation_put.js +++ b/yuthon_hr_attendance/static/src/js/button_vacation_put.js @@ -9,6 +9,19 @@ export class CustomAttendanceVacationPutController extends ListController { } openHrLeaveWizard() { + const now = new Date(); + const lastMonthYear = now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear(); + const lastMonth = now.getMonth() === 0 ? 11 : now.getMonth() - 1; + const lastMonthFirstDay = new Date(lastMonthYear, lastMonth, 1); + const currentMonthFirstDay = new Date(now.getFullYear(), now.getMonth(), 1); + const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day} 00:00:00`; + }; + const lastMonthStart = formatDate(lastMonthFirstDay); + const lastMonthEnd = formatDate(currentMonthFirstDay); this.actionService.doAction({ type: 'ir.actions.act_window', res_model: 'hr.leave', @@ -18,6 +31,11 @@ export class CustomAttendanceVacationPutController extends ListController { views: [[false, 'tree']], target: 'new', // 弹窗形式打开 context: {'tree_view_ref': "yuthon_hr_attendance.view_hr_leave_statement_tree"}, + domain: [ + '&', + ['date_from', '>=', lastMonthStart], + ['date_from', '<', lastMonthEnd] + ], }); } @@ -33,17 +51,14 @@ export class CustomAttendanceVacationPutController extends ListController { context: {'tree_view_ref': "yuthon_hr_attendance.view_yuthon_vacation_overtime_briefly_tree"}, domain: [ '|', - // 条件1:is_initial_number=true → 无视时间,直接显示 ['is_initial_number', '=', true], - // 条件组2:is_initial_number=false + add + workflow_record_id≠空 + 非12月 - '&', // 且:作用于「条件2」和「运算符&的结果」 + '&', ['is_initial_number', '=', false], - '&', // 且:作用于「条件3」和「运算符&的结果」 + '&', ['update_type', '=', 'add'], - '&', // 且:作用于「条件4」和「运算符!的结果」 + '&', ['workflow_record_id', '!=', false], - // 时间过滤:只作用于第二类记录 - '!', // 取反:不是2025年12月 + '!', '&', ['start_time', '>=', '2025-12-01 00:00:00'], ['start_time', '<', '2026-01-01 00:00:00'] @@ -52,16 +67,35 @@ export class CustomAttendanceVacationPutController extends ListController { } openYuthonVacationWizard() { + const now = new Date(); + const lastMonthYear = now.getMonth() === 0 ? now.getFullYear() - 1 : now.getFullYear(); + const lastMonth = now.getMonth() === 0 ? 11 : now.getMonth() - 1; + const lastMonthFirstDay = new Date(lastMonthYear, lastMonth, 1); + const currentMonthFirstDay = new Date(now.getFullYear(), now.getMonth(), 1); + const formatDate = (date) => { + const year = date.getFullYear(); + const month = String(date.getMonth() + 1).padStart(2, '0'); + const day = String(date.getDate()).padStart(2, '0'); + return `${year}-${month}-${day} 00:00:00`; + }; + const lastMonthStart = formatDate(lastMonthFirstDay); + const lastMonthEnd = formatDate(currentMonthFirstDay); this.actionService.doAction({ type: 'ir.actions.act_window', - res_model: 'yuthon.vacation.reports', + res_model: 'vacation.meal.line', name: '误餐情况', view_mode: 'tree', view_type: 'form', views: [[false, 'tree']], target: 'new', - context: {'tree_view_ref': "yuthon_hr_attendance.view_yuthon_vacation_meal_tree"}, - domain: [['meal_fee', '!=', 0]] + context: {'tree_view_ref': "yuthon_hr_attendance.view_vacation_meal_line_tree", + 'group_by': ['employee_id'], + }, + domain: [ + '&', + ['start_date', '>=', lastMonthStart], + ['start_date', '<', lastMonthEnd] + ], }); } } diff --git a/yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml b/yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml new file mode 100644 index 000000000..dfe90d6ce --- /dev/null +++ b/yuthon_hr_attendance/static/src/xml/button_vacation_confirm.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml index b70095a71..e6fab2b51 100644 --- a/yuthon_hr_attendance/views/yuthon_holiday_statement.xml +++ b/yuthon_hr_attendance/views/yuthon_holiday_statement.xml @@ -4,17 +4,18 @@ hr.leave.statement.tree hr.leave - + - + + @@ -40,6 +41,16 @@ 请假情况 hr.leave tree,search + [('is_filing','=', False)] + {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} + + + + + 请假归档 + hr.leave + tree,search + [('is_filing','=', True)] {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} diff --git a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml index 8f0308028..c962ec04c 100644 --- a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml +++ b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml @@ -1,15 +1,36 @@ - - yuthon.vacation.reports.tree - yuthon.vacation.reports + + vacation.meal.line.tree + vacation.meal.line - + - + + + + + + vacation.meal.line.search + vacation.meal.line + + + + + + + + + + + + + + yuthon.vacation.reports.tree yuthon.vacation.reports @@ -518,7 +539,7 @@ vacation.over.line.overtime.briefly.tree vacation.over.line - + @@ -526,6 +547,7 @@ + @@ -540,7 +562,7 @@ - + @@ -588,7 +610,7 @@ 加班情况 vacation.over.line tree - ['|', ('workflow_record_id','!=', False), ('is_initial_number', '=', True), ('update_type', '=', 'add')] + ['|', ('workflow_record_id','!=', False), ('is_initial_number', '=', True), ('update_type', '=', 'add'), ('is_filing','=', False)] {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} @@ -597,7 +619,7 @@ 加班情况(归档) vacation.over.line tree - ['|', ('workflow_record_id','!=', False), ('is_initial_number', '=', True), ('update_type', '=', 'add'), ('is_active', '=', True)] + ['|', ('workflow_record_id','!=', False), ('is_initial_number', '=', True), ('update_type', '=', 'add'), ('is_filing','=', True)] {'search_default_group_by_employee_id':1} @@ -611,18 +633,46 @@ 误餐情况 - yuthon.vacation.reports + vacation.meal.line tree - [('meal_fee', '!=', 0)] + [('is_filing','=', False)] {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} - + + (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_vacation_meal_line_tree')}),]"/> + + + 触发计算逻辑 + + + code + action = model.update_line() + + + 误餐归档 + vacation.meal.line + tree + [('is_filing','=', True)] + {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} + + 考勤确认单台账 yuthon.vacation.reports tree,form + [('is_filing','=', False)] + {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} + + + + 考勤确认单归档 + yuthon.vacation.reports + tree,form + [('is_filing','=', True)] {'search_default_now_month': 1, 'search_default_group_by_employee_id':1} + + + + + +