添加功能

This commit is contained in:
李鹏宇
2025-12-28 21:13:10 +08:00
parent 3bce339ece
commit 01f4720422
8 changed files with 127 additions and 7 deletions
+1
View File
@@ -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',
+2 -1
View File
@@ -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
from . import yuthon_salary_party
from . import yuthon_individual_tax
@@ -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
+3 -1
View File
@@ -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
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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
37 access_salary_import_wizard access_salary_import_wizard model_salary_import_wizard base.group_user 1 1 1 1
38 access_yuthon_individual_tax access_yuthon_individual_tax model_yuthon_individual_tax base.group_user 1 1 1 1
39
40
41
42
@@ -5,6 +5,11 @@
<field name="model_id" ref="model_hr_payroll_structure_type"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
<record model="ir.rule" id="hr_yuthon_individual_tax_rule">
<field name="name">个税多公司</field>
<field name="model_id" ref="model_yuthon_individual_tax"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
<record model="ir.rule" id="salary_payroll_distribution_rule">
<field name="name">薪资方法多公司</field>
<field name="model_id" ref="model_salary_payroll_distribution"/>
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_yuthon_individual_tax_tree" model="ir.ui.view">
<field name="name">yuthon.individual.tax.tree</field>
<field name="model">yuthon.individual.tax</field>
<field name="arch" type="xml">
<tree string="个税" editable="bottom">
<field name="name"/>
<field name="company_id"/>
<field name="start_date"/>
<field name="end_date"/>
<field name="create_time" column_invisible="1"/>
</tree>
</field>
</record>
<record id="view_yuthon_individual_tax_action" model="ir.actions.act_window">
<field name="name">个税</field>
<field name="res_model">yuthon.individual.tax</field>
<field name="view_mode">tree</field>
</record>
<record id="view_yuthon_individual_tax_server" model="ir.actions.server">
<field name="name">更新薪资常用项</field>
<field name="model_id" ref="model_yuthon_individual_tax"/>
<field name="binding_model_id" ref="model_yuthon_individual_tax"/>
<field name="state">code</field>
<field name="code">action = model.create_salary_temporary()</field>
</record>
</odoo>
+30 -5
View File
@@ -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:
+5
View File
@@ -428,6 +428,11 @@
parent="human_payroll_items_root_board"
action="yuthon_salary.view_yuthon_payroll_temporary_action"
sequence="30"/>
<menuitem id="hr_payroll_individual_tax_board"
name="个税"
parent="human_payroll_items_root_board"
action="yuthon_salary.view_yuthon_individual_tax_action"
sequence="35"/>
<menuitem id="human_pay_setting_work_setting"
name="配置"
parent="human_resources_salary_root_board"