diff --git a/yuthon_hr_attendance/__manifest__.py b/yuthon_hr_attendance/__manifest__.py index 131fa301b..4236e0996 100644 --- a/yuthon_hr_attendance/__manifest__.py +++ b/yuthon_hr_attendance/__manifest__.py @@ -71,6 +71,7 @@ 'wizard/attendance_controls_wizard.xml', 'wizard/attendance_correct_wizard.xml', 'wizard/vacation_put_wizard_views.xml', + 'wizard/vacation_reports_wizard.xml', 'wizard/vacation_confirm_wizard_views.xml', 'wizard/planning_slot_update_display_name_wizard.xml', 'wizard/yuthon_run_wizard.xml', diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index 49f57093e..86bfeea2a 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -33,7 +33,7 @@ class YuthonVacationReports(models.Model): year_workday = fields.Float(string="本年工作日", digits=(16, 3)) year_sum = fields.Float(string="本年应休合计", digits=(16, 3), compute="_compute_year_sum") used_this_year1 = fields.Float(string="本年已休", digits=(16, 3), compute="_compute_year_sum") - remainder_year1 = fields.Float(string="本年剩余(补休)", digits=(16, 3)) + remainder_year1 = fields.Float(string="本年剩余(补休)", digits=(16, 3), compute="_compute_year_sum") company_id = fields.Many2one(related="employee_id.company_id", string="公司", store=True) late_allowed = fields.Float(related="employee_id.late_allowed", string="允许迟到次数") @@ -133,7 +133,6 @@ class YuthonVacationReports(models.Model): record.year_sum = record.last_holiday + record.last_workday + record.year_holiday + record.year_workday record.remainder_year1 = record.year_sum - record.used_this_year1 - @api.constrains('employee_id', 'vacation_date') def _check_duplicate_monthly_record(self): """检查是否存在同一员工当月的重复记录""" @@ -176,6 +175,38 @@ class YuthonVacationReports(models.Model): def confirm_state(self): for rec in self: + current_user = self.env.user + is_admin = current_user.has_group('base.group_system') or current_user.has_group('base.group_admin') + if not is_admin: + if rec.vacation_date.month == 1: + last_month_year = rec.vacation_date.year - 1 + last_month = 12 + else: + last_month_year = rec.vacation_date.year + last_month = rec.vacation_date.month - 1 + last_day_of_last_month = calendar.monthrange(last_month_year, last_month)[1] + last_month_start = fields.Date.from_string(f'{last_month_year}-{last_month:02d}-01') + last_month_end = fields.Date.from_string(f'{last_month_year}-{last_month:02d}-{last_day_of_last_month}') + leave_batch_domain = [ + ('users_id', '=', current_user.id), + ('start_date', '>=', last_month_start), + ('end_date', '<=', last_month_end), + ('work_end', '!=', True) + ] + invalid_leave_batches = self.env['hr.leave.batch'].search(leave_batch_domain) + if invalid_leave_batches: + raise UserError('有假单未结束, 请先结束假期批次') + current_employee_id = self.env['hr.employee'].search([('user_id', '=', current_user.id)], limit=1).id + overtime_domain = [ + ('employee_ids', 'in', current_employee_id), + ('start_time', '>=', last_month_start), + ('start_time', '<=', last_month_end), + ('work_end', '!=', True) + ] + invalid_overtimes = self.env['overtime.approval'].search(overtime_domain) + if invalid_overtimes: + raise UserError('有加班单未结束,请先结束加班批次') + rec.manage_reject() @api.depends('vacation_date') @@ -292,22 +323,44 @@ class YuthonVacationReports(models.Model): records = self.env["yuthon.vacation.reports"].browse( self._context.get('active_ids', self._context.get('active_id', self.ids)) ) - for record in records: - last_month_last = record.vacation_date - timedelta(days=1) - last_month_first = last_month_last.replace(day=1) - last_month_record = self.search([ - ('employee_id', '=', record.employee_id.id), - ('vacation_date', '>=', fields.Date.to_string(last_month_first)), - ('vacation_date', '<=', fields.Date.to_string(last_month_last)), - ('vacation_state', '=', 'confirmed') - ], limit=1) - inherit_vals = { - 'previous_year': last_month_record.remainder_year, - 'last_workday': last_month_record.remainder_year1, - 'remainder_year': 0.0, - 'remainder_year1': 0.0, - } - record.write(inherit_vals) + for rec in records: + leave_line_domain = [ + ('vacation_id', '=', rec.id), + ('text', '=', "上月额度继承"), + ('employee_id', '=', rec.employee_id.id) + ] + existing_leave_line = self.env['vacation.leave.line'].search(leave_line_domain, limit=1) + if existing_leave_line: + existing_leave_line.unlink() + if rec.year_distribution: + self.env['vacation.leave.line'].create({ + 'vacation_id': rec.id, + 'employee_id': rec.employee_id.id, + 'update_type': 'add', + 'update_date': fields.Date.today(), + 'update_lines': rec.year_distribution * 8, + 'is_void': False, + 'allocation_type': 'allocation', + 'text': "上月额度继承" + }) + over_line_domain = [ + ('vacation_id', '=', rec.id), + ('employee_id', '=', rec.employee_id.id) + ] + existing_over_lines = self.env['vacation.over.line'].search(over_line_domain) + if existing_over_lines: + existing_over_lines.unlink() + if rec.year_holiday or rec.year_workday: + over_line_create_vals = { + 'vacation_id': rec.id, + 'employee_id': rec.employee_id.id, + 'update_type': 'add', + 'workday': rec.year_workday * 8, + 'public_holidays': rec.year_holiday * 8, + 'legal_day': 0.0, + 'text': "上月额度继承" + } + self.env['vacation.over.line'].create(over_line_create_vals) def _compute_attendance_fields1(self): records = self.env["yuthon.vacation.reports"].browse( @@ -320,11 +373,13 @@ class YuthonVacationReports(models.Model): }) def update_data(self): - records = self.env["yuthon.vacation.reports"].browse( - self._context.get('active_ids', self._context.get('active_id', self.ids)) - ) - for rec in records: - rec._compute_working_overtime() + return { + "type": "ir.actions.act_window", + "name": "导入", + "res_model": "vacation.reports.wizard", + "view_mode": "form", + "target": "new", + } @api.depends('other_fee_ids', 'other_fee_ids.holiday_type', 'other_fee_ids.lines', 'other_fee_ids.is_void', 'other_fee_ids.update_type') @@ -466,38 +521,6 @@ class YuthonVacationReports(models.Model): :return: ''' for rec in self: - current_user = self.env.user - is_admin = current_user.has_group('base.group_system') or current_user.has_group('base.group_admin') - if not is_admin: - if rec.vacation_date.month == 1: - last_month_year = rec.vacation_date.year - 1 - last_month = 12 - else: - last_month_year = rec.vacation_date.year - last_month = rec.vacation_date.month - 1 - last_day_of_last_month = calendar.monthrange(last_month_year, last_month)[1] - last_month_start = fields.Date.from_string(f'{last_month_year}-{last_month:02d}-01') - last_month_end = fields.Date.from_string(f'{last_month_year}-{last_month:02d}-{last_day_of_last_month}') - leave_batch_domain = [ - ('users_id', '=', current_user.id), - ('start_date', '>=', last_month_start), - ('end_date', '<=', last_month_end), - ('work_end', '!=', True) - ] - invalid_leave_batches = self.env['hr.leave.batch'].search(leave_batch_domain) - if invalid_leave_batches: - raise UserError('有假单未结束, 请先结束假期批次') - current_employee_id = self.env['hr.employee'].search([('user_id', '=', current_user.id)], limit=1).id - overtime_domain = [ - ('employee_ids', 'in', current_employee_id), - ('start_time', '>=', last_month_start), - ('start_time', '<=', last_month_end), - ('work_end', '!=', True) - ] - invalid_overtimes = self.env['overtime.approval'] .search(overtime_domain) - if invalid_overtimes: - raise UserError('有加班单未结束,请先结束加班批次') - last_month_last = rec.vacation_date - timedelta(days=1) last_month_first = last_month_last.replace(day=1) last_month_record = self.search([ @@ -512,15 +535,12 @@ class YuthonVacationReports(models.Model): last_month_year = last_month_last.year is_cross_year = current_year != last_month_year if is_cross_year: - last_month_record.write({ - 'remainder_year': last_month_record.leave, - 'remainder_year1': last_month_record.working_overtime, - }) inherit_vals = { 'previous_year': last_month_record.remainder_year, - 'last_workday': last_month_record.remainder_year1, - 'remainder_year': 0.0, - 'remainder_year1': 0.0, + 'last_workday': last_month_record.year_workday, + 'last_holiday': last_month_record.year_holiday, + 'year_holiday': 0.0, + 'year_workday': 0.0, } rec.write(inherit_vals) diff --git a/yuthon_hr_attendance/security/ir.model.access.csv b/yuthon_hr_attendance/security/ir.model.access.csv index e932198ee..8f6eb2365 100644 --- a/yuthon_hr_attendance/security/ir.model.access.csv +++ b/yuthon_hr_attendance/security/ir.model.access.csv @@ -80,4 +80,6 @@ access_staff_dining_report,staff.dining.report,model_staff_dining_report,base.gr access_vacation_confirm_wizard,vacation.confirm.wizard,model_vacation_confirm_wizard,base.group_user,1,1,1,1 access_vacation_put_wizard,vacation.put.wizard,model_vacation_put_wizard,base.group_user,1,1,1,1 access_message_reminder_tasks,message.reminder.tasks,model_message_reminder_tasks,base.group_user,1,1,1,1 -access_applet_security_quick_perm_wizard,access_applet_security_quick_perm_wizard,model_applet_security_quick_perm_wizard,base.group_user,1,1,1,1 \ No newline at end of file +access_applet_security_quick_perm_wizard,access_applet_security_quick_perm_wizard,model_applet_security_quick_perm_wizard,base.group_user,1,1,1,1 + +access_vacation_reports_wizard,vacation.reports.wizard,model_vacation_reports_wizard,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_hr_attendance/wizard/__init__.py b/yuthon_hr_attendance/wizard/__init__.py index 68b633658..64fc3344d 100644 --- a/yuthon_hr_attendance/wizard/__init__.py +++ b/yuthon_hr_attendance/wizard/__init__.py @@ -9,3 +9,4 @@ from . import planning_slot_update_display_name_wizard from . import vacation_confirm_wizard from . import vacation_put_wizard from . import applet_security_quick_perm_wizard +from . import vacation_reports_wizard diff --git a/yuthon_hr_attendance/wizard/vacation_reports_wizard.py b/yuthon_hr_attendance/wizard/vacation_reports_wizard.py new file mode 100644 index 000000000..0c1e67b33 --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_reports_wizard.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +from odoo import models, fields, api, _, SUPERUSER_ID +from odoo.exceptions import ValidationError, UserError +from pypinyin import pinyin, Style +import xlrd +import base64 +import datetime +from datetime import datetime + + +class VacationReportsWizard(models.TransientModel): + _name = 'vacation.reports.wizard' + _description = '考勤报表导入' + + excel_file = fields.Binary(string="上传Excel表") + excel_name = fields.Char(string="文件名") + report_ids = fields.Many2many('yuthon.vacation.reports', string='报表') + + def confirm(self): + """ + 导入excel文件,按行读取数据并处理 + :return: 导入员工信息表并更新 + """ + if not self.excel_file: + raise ValidationError('请上传文件。') + book = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel_file)) + sh = book.sheet_by_index(0) + for rx in range(1, sh.nrows): + row = sh.row(rx) + self.leave_import(row) + + def leave_import(self, row): + employee_id = self.env['hr.employee'].search([('name', '=', row[0].value)]) + year_distribution = row[1].value + start_date = fields.Date.from_string("2025-12-1") + report_id = self.env['yuthon.vacation.reports'].search([('employee_id', '=', employee_id.id), + ('vacation_date', '=', start_date) + ]) + if report_id: + report_id.write({'year_distribution': year_distribution}) + + + def over_import(self, row): + employee_id = self.env['hr.employee'].search([('name', '=', row[0].value)]) + year_holiday = float(row[12].value) + year_workday = float(row[11].value) + if not year_workday and not year_holiday: + year_workday = float(row[10].value) + start_date = fields.Date.from_string("2025-12-1") + report_id = self.env['yuthon.vacation.reports'].search([('employee_id', '=', employee_id.id), + ('vacation_date', '=', start_date) + ]) + if report_id: + report_id.write( + {'year_holiday': year_holiday / 8 + ,'year_workday': year_workday / 8 + }) + + + diff --git a/yuthon_hr_attendance/wizard/vacation_reports_wizard.xml b/yuthon_hr_attendance/wizard/vacation_reports_wizard.xml new file mode 100644 index 000000000..226f6093a --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_reports_wizard.xml @@ -0,0 +1,25 @@ + + + + 导入 + vacation.reports.wizard + +
+ + + + + + + + + + + +
+
+
+
\ No newline at end of file