From 01f4720422994f165e436924057791282f5fa44b 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, 28 Dec 2025 21:13:10 +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_salary/__manifest__.py | 1 + yuthon_salary/models/__init__.py | 3 +- yuthon_salary/models/yuthon_individual_tax.py | 53 +++++++++++++++++++ yuthon_salary/security/ir.model.access.csv | 4 +- .../security/salary_security_rule.xml | 5 ++ .../views/yuthon_individual_tax_views.xml | 28 ++++++++++ yuthon_salary/wizard/salary_import_wizard.py | 35 ++++++++++-- yuthon_work_brand/views/hr_menus.xml | 5 ++ 8 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 yuthon_salary/models/yuthon_individual_tax.py create mode 100644 yuthon_salary/views/yuthon_individual_tax_views.xml diff --git a/yuthon_salary/__manifest__.py b/yuthon_salary/__manifest__.py index a2511b537..9979fc76a 100644 --- a/yuthon_salary/__manifest__.py +++ b/yuthon_salary/__manifest__.py @@ -23,6 +23,7 @@ 'views/yuthon_payroll_project_views.xml', 'views/yuthon_payroll_type_setting_views.xml', 'views/yuthon_salary_party_views.xml', + 'views/yuthon_individual_tax_views.xml', 'views/yuthon_salary_asset_views.xml', 'views/salary_payroll_distribution_viwes.xml', 'views/yuthon_salary_project_views.xml', diff --git a/yuthon_salary/models/__init__.py b/yuthon_salary/models/__init__.py index 0b050ac71..8864f9ba9 100644 --- a/yuthon_salary/models/__init__.py +++ b/yuthon_salary/models/__init__.py @@ -14,4 +14,5 @@ from . import salary_payroll_distribution from . import yuthon_welfare_expenses from . import inherit_hr_payslip_run from . import inherit_hr_payslip_line -from . import yuthon_salary_party \ No newline at end of file +from . import yuthon_salary_party +from . import yuthon_individual_tax \ No newline at end of file diff --git a/yuthon_salary/models/yuthon_individual_tax.py b/yuthon_salary/models/yuthon_individual_tax.py new file mode 100644 index 000000000..06ebc7c3d --- /dev/null +++ b/yuthon_salary/models/yuthon_individual_tax.py @@ -0,0 +1,53 @@ +from email.policy import default + +from odoo import api, fields, models, _ +from pypinyin import pinyin, Style + + +class YuthonIndividualTax(models.Model): + _name = 'yuthon.individual.tax' + _description = '个税' + + name =fields.Char(string="名称") + company_id = fields.Many2one('res.company', string="公司", default=lambda self: self.env.company) + start_date = fields.Date(string="生效日期", default=fields.Date.today()) + end_date = fields.Date(string="失效日期") + create_time = fields.Date(string="创建日期", default=fields.Datetime.now) + + def create_salary_temporary(self): + """ + 更新薪资个税 + :return: + """ + individual_ids = self.env["yuthon.individual.tax"].browse( + self._context.get('active_ids', self._context.get('active_id'))) + category_id = self.env['hr.salary.rule.category'].search([('name', '=', '个税')],limit=1) + if not category_id: + category_id = self.env['hr.salary.rule.category'].create({'name': '个税', + 'code': 'gs' + }) + for ins in individual_ids: + for sal in self.env["hr.payroll.structure"].search([('company_id', '=', ins.company_id.id)]): + rule_list = [] + if ins.name not in sal.rule_ids.mapped('name'): + rule_list.append([0, 0, { + 'name': ins.name, + 'category_id': category_id.id, + 'code': "".join([p[0][0] for p in pinyin(ins.name, style=Style.FIRST_LETTER)]).upper(), + 'amount_select': 'fix', + 'amount_fix': 0, + 'struct_id': sal.id, + }]) + sal.write({'rule_ids': rule_list}) + + + @api.depends('start_date', 'end_date') + def compute_is_enable(self): + for rec in self: + if rec.start_date and rec.end_date: + if rec.start_date <= fields.Date.today() < rec.end_date: + rec.is_enable = True + else: + rec.is_enable = False + + diff --git a/yuthon_salary/security/ir.model.access.csv b/yuthon_salary/security/ir.model.access.csv index bfa5c3a25..34e5dc719 100644 --- a/yuthon_salary/security/ir.model.access.csv +++ b/yuthon_salary/security/ir.model.access.csv @@ -37,4 +37,6 @@ access_rank_log_wizard,access_rank_log_wizard,model_rank_log_wizard,base.group_u access_basic_pension_wizard,access_basic_pension_wizard,model_basic_pension_wizard,base.group_user,1,1,1,1 access_yuthon_salary_party,access_yuthon_salary_party,model_yuthon_salary_party,base.group_user,1,1,1,1 access_salary_party_wizard,access_salary_party_wizard,model_salary_party_wizard,base.group_user,1,1,1,1 -access_salary_import_wizard,access_salary_import_wizard,model_salary_import_wizard,base.group_user,1,1,1,1 \ No newline at end of file +access_salary_import_wizard,access_salary_import_wizard,model_salary_import_wizard,base.group_user,1,1,1,1 + +access_yuthon_individual_tax,access_yuthon_individual_tax,model_yuthon_individual_tax,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/yuthon_salary/security/salary_security_rule.xml b/yuthon_salary/security/salary_security_rule.xml index ec308053c..298668358 100644 --- a/yuthon_salary/security/salary_security_rule.xml +++ b/yuthon_salary/security/salary_security_rule.xml @@ -5,6 +5,11 @@ [('company_id', 'in', company_ids)] + + 个税多公司 + + [('company_id', 'in', company_ids)] + 薪资方法多公司 diff --git a/yuthon_salary/views/yuthon_individual_tax_views.xml b/yuthon_salary/views/yuthon_individual_tax_views.xml new file mode 100644 index 000000000..16e4017f1 --- /dev/null +++ b/yuthon_salary/views/yuthon_individual_tax_views.xml @@ -0,0 +1,28 @@ + + + + yuthon.individual.tax.tree + yuthon.individual.tax + + + + + + + + + + + + 个税 + yuthon.individual.tax + tree + + + 更新薪资常用项 + + + code + action = model.create_salary_temporary() + + diff --git a/yuthon_salary/wizard/salary_import_wizard.py b/yuthon_salary/wizard/salary_import_wizard.py index 3bc17bea4..e892c3f60 100644 --- a/yuthon_salary/wizard/salary_import_wizard.py +++ b/yuthon_salary/wizard/salary_import_wizard.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from odoo import models, fields, api, _, SUPERUSER_ID +from odoo import models, fields, api, _, SUPERUSER_ID, exceptions from odoo.exceptions import ValidationError, UserError from pypinyin import pinyin, Style import xlrd @@ -7,6 +7,7 @@ import base64 import datetime from datetime import datetime, timedelta + class SalaryImportWizard(models.TransientModel): _name = 'salary.import.wizard' _description = '薪资导入弹窗' @@ -22,12 +23,31 @@ class SalaryImportWizard(models.TransientModel): sh = book.sheet_by_index(0) header_row = sh.row(0) excel_headers = [str(cell.value).strip() for cell in header_row if cell.value] - invalid_headers = [header for header in excel_headers] + global_valid_line_names = set() for rx in range(1, sh.nrows): - row = sh.row(rx) - self.salary_party_data(row, excel_headers) + row = sh.row(rx) + self.salary_party_data(row, excel_headers, global_valid_line_names) + invalid_headers = [header for header in excel_headers if header not in global_valid_line_names] + if invalid_headers: + invalid_headers_str = ', '.join(invalid_headers) + message = _(f"薪资导入完成!以下表头未匹配到任何有效薪资项目,未进行处理:\n{invalid_headers_str}") + notification_type = 'warning' + else: + message = _("薪资导入完成!所有表头均匹配有效薪资项目。") + notification_type = 'success' - def salary_party_data(self, row, excel_headers): + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { + 'title': _("薪资导入结果提示"), + 'message': message, + 'type': notification_type, + 'sticky': False, + } + } + + def salary_party_data(self, row, excel_headers, global_valid_line_names): employee_name = row[0].value employee = self.env['hr.employee'].search([('name', '=', employee_name)], limit=1) if employee: @@ -43,6 +63,7 @@ class SalaryImportWizard(models.TransientModel): 'name': employee.name, 'employee_id': employee.id, 'contract_id': employee.contract_id.id, + 'salary_group_id': employee.contract_id.salary_group_id.id, 'payslip_run_id': payslip_run_id.id, 'date_from': datetime(2025, 8, 1).date() , 'date_to': datetime(2025, 8, 31).date(), @@ -55,6 +76,10 @@ class SalaryImportWizard(models.TransientModel): payslip.is_salary_push = True payslip.is_salary_release = False + current_payslip_lines = payslip.line_ids.filtered(lambda l: l.name) + current_valid_names = {line.name.strip() for line in current_payslip_lines} + global_valid_line_names.update(current_valid_names) + for line in payslip.line_ids: line_name = line.name if line_name not in excel_headers: diff --git a/yuthon_work_brand/views/hr_menus.xml b/yuthon_work_brand/views/hr_menus.xml index 8c3be860f..7ed5061c5 100644 --- a/yuthon_work_brand/views/hr_menus.xml +++ b/yuthon_work_brand/views/hr_menus.xml @@ -428,6 +428,11 @@ parent="human_payroll_items_root_board" action="yuthon_salary.view_yuthon_payroll_temporary_action" sequence="30"/> +