diff --git a/yuthon_hr_attendance/__manifest__.py b/yuthon_hr_attendance/__manifest__.py index 7d838641d..44d5b403e 100644 --- a/yuthon_hr_attendance/__manifest__.py +++ b/yuthon_hr_attendance/__manifest__.py @@ -68,6 +68,8 @@ 'wizard/yuthon_delete_planning_wizard_views.xml', 'wizard/attendance_controls_wizard.xml', 'wizard/attendance_correct_wizard.xml', + 'wizard/vacation_put_wizard_views.xml', + 'wizard/vacation_confirm_wizard_views.xml', 'wizard/planning_slot_update_display_name_wizard.xml', 'wizard/yuthon_run_wizard.xml', 'views/applet_address_views.xml', diff --git a/yuthon_hr_attendance/data/mothly_report_time.xml b/yuthon_hr_attendance/data/mothly_report_time.xml index df2868ac1..c78564dd0 100644 --- a/yuthon_hr_attendance/data/mothly_report_time.xml +++ b/yuthon_hr_attendance/data/mothly_report_time.xml @@ -28,7 +28,7 @@ - 定时任务:发送我的考勤确认 + 定时任务:批量发送考勤确认 code model.attendance_confirmation() diff --git a/yuthon_hr_attendance/models/hr_attendance.py b/yuthon_hr_attendance/models/hr_attendance.py index 1a42c341f..5bb91438d 100644 --- a/yuthon_hr_attendance/models/hr_attendance.py +++ b/yuthon_hr_attendance/models/hr_attendance.py @@ -416,6 +416,7 @@ class HrAttendance(models.Model): def revoke(self): self.check_in = self.approval_check_in + self.correct_result = None self.check_out = self.approval_check_out self.is_no_card = self.is_approval_no self.is_early_card = self.is_approval_early diff --git a/yuthon_hr_attendance/models/yuthon_vacation_reports.py b/yuthon_hr_attendance/models/yuthon_vacation_reports.py index c820787da..9780b365e 100644 --- a/yuthon_hr_attendance/models/yuthon_vacation_reports.py +++ b/yuthon_hr_attendance/models/yuthon_vacation_reports.py @@ -405,63 +405,16 @@ class YuthonVacationReports(models.Model): } } - def button_all_put(self): - detail_ids = self.env["yuthon.vacation.reports"].browse( - self._context.get('active_ids', self._context.get('active_id')) - ) - unique_employee_ids = list({detail.employee_id.id for detail in detail_ids if detail.employee_id}) - for employee_id in unique_employee_ids: - # 筛选当前员工对应的考勤记录 - employee_details = detail_ids.filtered(lambda d: d.employee_id.id == employee_id) - for detail in employee_details: - url = f'https://oa.thtzjt.com/webh5/#/vacation?id={detail.id}' - we_id = self.env['hr.employee'].browse(employee_id).wecom_userid - if we_id: - self.sudo().env["wecom.apps"].sync_send_message_textcard( - category='attendance', - user_id=we_id, - title="考勤确认", - description=f'{self.env["hr.employee"].browse(employee_id).name} 请确认你的考勤:', - url=url, - btntxt="详细信息" - ) - return { - 'type': 'ir.actions.client', - 'tag': 'display_notification', - 'params': { - 'type': 'success', - 'message': '确认通知已发送', - 'sticky': False, - } - } - def attendance_confirmation(self): - reports = self.env["yuthon.vacation.reports"].search([('employee_id.name', '=', '叶华锋')]) - if not reports: - return - #按员工分组,仅保留每个员工vacation_date最新的记录 - employee_reports_map = {} - for report in reports: - if not report.employee_id: - continue - emp = report.employee_id - if emp not in employee_reports_map: - employee_reports_map[emp] = report - elif report.vacation_date > employee_reports_map[emp].vacation_date: - employee_reports_map[emp] = report + def all_put(self): + return { + 'type': 'ir.actions.act_window', + 'name': '批量发送确认弹窗', + 'res_model': 'vacation.put.wizard', + 'view_mode': 'form', + 'target': 'new', + } - for employee, report in employee_reports_map.items(): - url = f'https://oa.thtzjt.com/webh5/#/vacation?id={report.id}' - we_id = employee.wecom_userid - if we_id: - self.sudo().env["wecom.apps"].sync_send_message_textcard( - category='default', - user_id=we_id, - title="考勤确认", - description=f'{employee.name} 请确认你的考勤:', - url=url, - btntxt="详细信息" - ) def manage_confirmation(self): vacation_ids = self.env["yuthon.vacation.reports"].browse( @@ -662,11 +615,15 @@ class YuthonVacationReports(models.Model): meal_total -= int(line.update_lines or 0) record.meal_fee = meal_total - def batch_confirmation(self): - vacation_ids = self.env["yuthon.vacation.reports"].browse( - self._context.get('active_ids', self._context.get('active_id'))) - for rec in vacation_ids: - rec.vacation_state = 'confirmed' + def all_confirm(self): + return { + 'name': '批量确认弹窗', + 'type': 'ir.actions.act_window', + 'res_model': 'vacation.confirm.wizard', + 'view_mode': 'form', + 'target': 'new', + + } # 自动确认考勤台账 def automatic_confirmation(self): @@ -738,11 +695,11 @@ class YuthonVacationReports(models.Model): self._update_over_lines(new_report, [last_month_record], overtime_hours) else: - # 如果没有已确认的上月记录,创建空的本月报告 - self.create({ - 'employee_id': employee.id, - 'vacation_date': current_month_first, - }) + # 如果没有已确认的上月记录,创建空的本月报告 + self.create({ + 'employee_id': employee.id, + 'vacation_date': current_month_first, + }) def _update_leave_lines(self, report, allocations, number_of_hours_display): diff --git a/yuthon_hr_attendance/security/ir.model.access.csv b/yuthon_hr_attendance/security/ir.model.access.csv index 7a30aef2d..12b42ed7b 100644 --- a/yuthon_hr_attendance/security/ir.model.access.csv +++ b/yuthon_hr_attendance/security/ir.model.access.csv @@ -75,4 +75,7 @@ access_planning_slot_update_display_name_wizard,access_planning_slot_update_disp access_expatriation_approval,expatriation.approval,model_expatriation_approval,base.group_user,1,1,1,1 access_meal_approval,meal.approval,model_meal_approval,base.group_user,1,1,1,1 access_back_approval,back.approval,model_back_approval,base.group_user,1,1,1,1 -access_staff_dining_report,staff.dining.report,model_staff_dining_report,base.group_user,1,1,1,1 \ No newline at end of file +access_staff_dining_report,staff.dining.report,model_staff_dining_report,base.group_user,1,1,1,1 + +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 \ No newline at end of file diff --git a/yuthon_hr_attendance/views/hr_attendance_month_views.xml b/yuthon_hr_attendance/views/hr_attendance_month_views.xml index 8068ae02e..933e9469c 100644 --- a/yuthon_hr_attendance/views/hr_attendance_month_views.xml +++ b/yuthon_hr_attendance/views/hr_attendance_month_views.xml @@ -283,13 +283,6 @@ (0, 0, {'view_mode': 'tree', 'view_id': ref('yuthon_hr_attendance.view_hr_attendance_month_detail_tree1')}), (0, 0, {'view_mode': 'form', 'view_id': ref('yuthon_hr_attendance.view_hr_attendance_month_detail_form1')})]"/> - - 批量发送 - - - code - action = model.button_all_put() - diff --git a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml index 9903be318..936468742 100644 --- a/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml +++ b/yuthon_hr_attendance/views/yuthon_vacation_reports_views.xml @@ -5,6 +5,10 @@ yuthon.vacation.reports + + + + @@ -43,7 +47,6 @@ - @@ -474,13 +477,6 @@ code action = model.update_vacation() - - - 批量发送 - - - code - action = model.update_log() 初始化日志 @@ -496,13 +492,6 @@ code action = model.manage_confirmation() - - 批量确认 - - - code - action = model.batch_confirmation() - 一次性 diff --git a/yuthon_hr_attendance/wizard/__init__.py b/yuthon_hr_attendance/wizard/__init__.py index c2bcd6003..60c5d4376 100644 --- a/yuthon_hr_attendance/wizard/__init__.py +++ b/yuthon_hr_attendance/wizard/__init__.py @@ -5,4 +5,6 @@ from . import yuthon_delete_planning_wizard from . import attendance_controls_wizard from . import attendance_correct_wizard from . import yuthon_run_wizard -from . import planning_slot_update_display_name_wizard \ No newline at end of file +from . import planning_slot_update_display_name_wizard +from . import vacation_confirm_wizard +from . import vacation_put_wizard \ No newline at end of file diff --git a/yuthon_hr_attendance/wizard/vacation_confirm_wizard.py b/yuthon_hr_attendance/wizard/vacation_confirm_wizard.py new file mode 100644 index 000000000..f4f9ecc2f --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_confirm_wizard.py @@ -0,0 +1,19 @@ +# -*- coding: utf-8 -*- +import calendar +from odoo import models, api, fields, _ +from odoo.tools.float_utils import float_round +from odoo.exceptions import ValidationError, UserError +from datetime import date +from datetime import timedelta +from datetime import datetime, time + + +class VacationConfirmWizard(models.TransientModel): + _name = 'vacation.confirm.wizard' + _description = '批量确认弹窗' + + def action_confirm(self): + reports = self.env["yuthon.vacation.reports"].search([('vacation_state', '=', 'unconfirmed')]) + for rec in reports: + rec.vacation_state = 'confirmed' + rec.automatic_confirmation() diff --git a/yuthon_hr_attendance/wizard/vacation_confirm_wizard_views.xml b/yuthon_hr_attendance/wizard/vacation_confirm_wizard_views.xml new file mode 100644 index 000000000..34a916fb4 --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_confirm_wizard_views.xml @@ -0,0 +1,18 @@ + + + + vacation.confirm.wizard.form + vacation.confirm.wizard + + + + 确认要批量确认吗? + + + + + + diff --git a/yuthon_hr_attendance/wizard/vacation_put_wizard.py b/yuthon_hr_attendance/wizard/vacation_put_wizard.py new file mode 100644 index 000000000..8595541f6 --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_put_wizard.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +import calendar +from odoo import models, api, fields, _ +from odoo.tools.float_utils import float_round +from odoo.exceptions import ValidationError, UserError +from datetime import date +from datetime import timedelta +from datetime import datetime, time + + +class VacationPutWizard(models.TransientModel): + _name = 'vacation.put.wizard' + _description = '批量发送弹窗' + + select_time = fields.Selection([('1', '一月'), ('2', '二月'), ('3', '三月'), ('4', '四月'), + ('5', '五月'), ('6', '六月'), ('7', '七月'), ('8', '八月'), + ('9', '九月'), ('10', '十月'), ('11', '十一月'), + ('12', '十二月')], string='选择月份', + default=lambda self: self._get_default_month()) + select_year = fields.Selection(selection=[(str(i), str(i)) for i in range(2024, 2040)], string='选择年份', + default=lambda self: str(datetime.now().year)) + + def _get_default_month(self): + current_month = datetime.now().month + return str(current_month) + + def action_confirm(self): + year = int(self.select_year) + month = int(self.select_time) + vacation_date = datetime(year=year, month=month, day=1).date() + reports = self.env["yuthon.vacation.reports"].search([('vacation_state', '=', 'unconfirmed'), + ('vacation_date', '=', vacation_date) + ]) + if not reports: + return + # 按员工分组,仅保留每个员工vacation_date最新的记录 + employee_reports_map = {} + for report in reports: + if not report.employee_id: + continue + emp = report.employee_id + if emp not in employee_reports_map: + employee_reports_map[emp] = report + elif report.vacation_date > employee_reports_map[emp].vacation_date: + employee_reports_map[emp] = report + + for employee, report in employee_reports_map.items(): + url = f'https://oa.thtzjt.com/webh5/#/vacation?id={report.id}' + we_id = employee.wecom_userid + if we_id: + self.sudo().env["wecom.apps"].sync_send_message_textcard( + category='default', + user_id=we_id, + title="考勤确认", + description=f'{employee.name} 请确认你的考勤:', + url=url, + btntxt="详细信息" + ) \ No newline at end of file diff --git a/yuthon_hr_attendance/wizard/vacation_put_wizard_views.xml b/yuthon_hr_attendance/wizard/vacation_put_wizard_views.xml new file mode 100644 index 000000000..23e36b830 --- /dev/null +++ b/yuthon_hr_attendance/wizard/vacation_put_wizard_views.xml @@ -0,0 +1,22 @@ + + + + vacation.put.wizard.form + vacation.put.wizard + + + + 只针对未确认的考勤确认单台账,确定要发送吗? + + + + + + + + + + \ No newline at end of file diff --git a/yuthon_property/wizard/property_statements_wizard.py b/yuthon_property/wizard/property_statements_wizard.py index 27e57a8a4..747f31112 100644 --- a/yuthon_property/wizard/property_statements_wizard.py +++ b/yuthon_property/wizard/property_statements_wizard.py @@ -42,7 +42,7 @@ class PropertyStatementsWizard(models.Model): remake = row[2].value original_amount = row[3].value your_own_account = row[6].value - cleaned_amount = original_amount.replace(',', '') # 移除逗号 + cleaned_amount = original_amount.replace(',', '') float_amount = float(cleaned_amount) your_own_unit = self.classify_by_last_four(your_own_account) sequence_number = row[5].value