Files
gzth/yuthon_salary/models/inherit_hr_payslip.py
T
2025-10-03 17:54:35 +08:00

877 lines
42 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError, UserError
import json
from odoo.tools import date_utils
from dateutil.relativedelta import relativedelta
import re
from datetime import datetime
import logging
_logger = logging.getLogger(__name__)
class InheritHrPayslip(models.Model):
_inherit = 'hr.payslip'
company_id = fields.Many2one('res.company', string="公司", default=lambda self: self.env.company)
employee_id = fields.Many2one('hr.employee', string="姓名")
date_of_entry = fields.Date(related='employee_id.entry_date', string="入职时间", store=True)
base_salary = fields.Float(string='基本工资', compute='_compute_salary', store=True)
bank_account_id = fields.Many2one(string='银行账号', related='employee_id.bank_account_id', store=True)
tax_payable = fields.Float(string='应补(退)税额')
actual_salary = fields.Float(string='实发工资', store=True, compute='_compute_actual_salary')
three_one = fields.Float(string='三险一金', store=True, compute='_compute_salary')
identification_id = fields.Char(string='证件号码', related='employee_id.identification_id', store=True)
card_type = fields.Char(string='证件类型', store=True, default="居民身份证")
salary_group_id = fields.Many2one('yuthon.salary.group', string="工资组", store=True, index=True)
performance_bonuses = fields.Float(string='预发绩效奖金', compute='_compute_salary', store=True)
full_attendance_award = fields.Float(string='全勤奖', compute='_compute_salary', store=True)
commuting_allowance = fields.Float(string='通信补贴', compute='_compute_salary', store=True)
position_subsidy = fields.Monetary(string="岗位补贴", compute="_compute_salary", store=True)
position_salary = fields.Monetary(string="岗位工资", compute="_compute_salary", store=True)
rank_salary = fields.Float(string='职级工资', compute='_compute_salary', store=True)
housing_loan_interest = fields.Float(string='累计住房贷款利息')
elderly_support_deduction = fields.Float(string='累计个人养老金')
housing_rent = fields.Float(string='累计住房租金')
child_education = fields.Float(string='累计子女教育')
tax = fields.Float(string="税金")
retroactive_allowance = fields.Float(string="补发津贴")
condolence_fund = fields.Float(string="儿童节慰问金")
continuing_education = fields.Float(string='累计继续教育')
elderly_support = fields.Float(string='累计赡养老人')
infant_care = fields.Float(string='累计3岁以下婴幼儿照护')
seniority_salary = fields.Float(string='工龄工资', compute='_compute_salary', store=True)
union_dues = fields.Float(string='工会会费')
overtime_pay = fields.Float(string='加班费', compute='_compute_salary', store=True)
meal_misunderstanding_fee = fields.Float(string='误餐费', compute='_compute_salary', store=True)
personal_leave = fields.Float(string='事假', compute='_compute_salary', store=True)
sick_leave = fields.Float(string='病假', compute='_compute_salary', store=True)
maternity_leave = fields.Float(string='产假', compute='_compute_salary', store=True)
marriage_leave = fields.Float(string='婚假', compute='_compute_salary', store=True)
family_planning_leave = fields.Float(string='计生假', compute='_compute_salary', store=True)
bereavement_leave = fields.Float(string='丧假', compute='_compute_salary', store=True)
home_visit_leave = fields.Float(string='探亲假', compute='_compute_salary', store=True)
hospital_sick_leave = fields.Float(string='住院病假', compute='_compute_salary', store=True)
exam_leave = fields.Float(string='考试假', compute='_compute_salary', store=True)
parents_meeting_leave = fields.Float(string='家长会假', compute='_compute_salary', store=True)
basic_medical_insurance = fields.Float(string='基本医疗保险费', compute='_compute_salary', store=True)
basic_endowment_insurance = fields.Float(string='基本养老保险费', compute='_compute_salary', store=True)
unemployment_insurance = fields.Float(string='失业保险费', compute='_compute_salary', store=True)
work_injury_insurance = fields.Float(string='工伤保险费', compute='_compute_salary', store=True)
housing_provident_fund = fields.Float(string='住房公积金', compute='_compute_salary', store=True)
b_year = fields.Float(string='企业(职业)年金', compute='_compute_salary', store=True)
total_before_tax = fields.Float(string='本期应收', compute='_compute_total_before_tax', store=True)
year_compute = fields.Float(string='前年年度清算', store=True)
year_jie = fields.Float(string='前年年度结余', store=True)
children_welfare_fee = fields.Float(string='儿童节慰问金') # 天河
year_gong = fields.Float(string='年功奖', store=True)
study_on = fields.Float(string='学习进步奖')
jing_te = fields.Float(string='经营特别奖')
temporary_performance_bonus = fields.Float(string='临时绩效奖')
last_year_safety = fields.Float(string='上年安全生产工作奖')
last_year_jie = fields.Float(string='上年年度结余')
last_year_performance_evaluation = fields.Float(string='上年绩效评优')
retention_previous_year = fields.Float(string='上年考核保留量')
social_security = fields.Float(string='上年社保补缴')
performance_up = fields.Float(string='上月多扣绩效补回')
individual_tax_refund = fields.Float(string='上年个税退回')
liquidation_year = fields.Float(string='上年年度清算')
safety_incentives = fields.Float(string='安全生产奖')
is_push = fields.Boolean(string='推送')
is_salary_push = fields.Boolean(string='工资推送')
is_salary_release = fields.Boolean(string='薪资发放')
salary_state = fields.Selection([('to_signed', '待签字' ), ('to_release', '待发放'), ('released', '已发放')], string='状态', default='to_signed')
commercial_health_insurance = fields.Float(string='商业健康保险')
tax_deferred_pension_insurance = fields.Float(string='税延养老保险')
others = fields.Float(string='其他扣除')
deductible_donations = fields.Float(string='准予扣除的捐赠额')
total_pre_tax_deductions = fields.Float(string='税前扣除项目合计')
tax_reduction = fields.Float(string='减免税额')
deduction_standard = fields.Float(string='减除费用标准')
paid_tax = fields.Float(string='已缴税额')
based_pay = fields.Float(string='临时绩效工资')
life_salary = fields.Float(string='生活补贴')
january_performance = fields.Float(string='1月份绩效奖金补发')
accumulated_underpayment = fields.Float(string='累计欠收')
cumulative_income = fields.Float(string='其他单位累计收入')
other_cum_deductions = fields.Float(string='其他单位累计扣除')
other_cumulative_deductions = fields.Float(string='其他单位累计减免税额')
other_cumulative_tax_paid = fields.Float(string='其他单位累计已缴税额')
cumulative_taxable = fields.Float(string='累计应纳税所得额')
quick_deductions = fields.Float(string='速算扣除数')
payable = fields.Float(string='累计应纳税额')
tariff = fields.Float(string='税率')
cumulative_deductions = fields.Float(string='累计减免税额')
amount_tax_withheld = fields.Float(string='累计应扣缴税额')
is_bold = fields.Boolean()
confirm_status = fields.Selection([('confirm', '已确认'), ('un_confirm', '未确认')], string='确认状态', default='un_confirm')
remake = fields.Text(string='备注')
# 物管常用项
seniority_allowance = fields.Float(string='工龄补贴')
safety_subsidy = fields.Float(string='安全生产管理补贴')
job_performance = fields.Float(string='岗位绩效')
comprehensive_subsidy = fields.Float(string='综合补贴')
duty = fields.Float(string='值班(含节日加班)')
monthly_overtime = fields.Float(string='月加班')
annuity = fields.Float(string='年金')
spring_festival = fields.Float(string='春节福利')
# 物管临时项
excellent_employees = fields.Float(string='优秀部门经理/员工奖励')
# 粮储常用项
quality_allowance = fields.Float(string='质检员津贴', compute="_compute_salary", store=True)
environmental_allowance = fields.Float(string='环境作业津贴')
last_duty = fields.Float(string='上月值班费')
high_subsidy = fields.Float(string='高温补贴')
# 粮储临时项
medical_fee = fields.Float(string='超出体检费标准')
retroactive_insurance = fields.Float(string='补扣保险')
distribution_part = fields.Float(string='扣除环境作业津贴多发放部分')
# 三家公司
add_year_float = fields.Float(string='补年金差额')
individual_income_tax = fields.Float(string='个税扣发(减)')
add_income_tax = fields.Float(string='个税补发(增)')
maternity_allowance = fields.Float(string='生育津贴')
minus_annuity_difference = fields.Float(string='年金差额(减)')
add_annuity_difference = fields.Float(string='年金差额(增)')
minus_att = fields.Float(string='考勤扣发(减)')
add_att = fields.Float(string='考勤补发(增)')
minus_year_float = fields.Float(string='扣年金差额')
# 五险一金
# 基本医疗
basic_pension_base = fields.Float(string='缴费基数')
basic_pension_payable = fields.Float(string='应缴金额')
one_basic_pension_base = fields.Float(string='缴费基数')
one_basic_pension_payable = fields.Float(string='应缴金额')
# 失业
employment_insurance = fields.Float(string='缴费基数')
employment_insurance_payable = fields.Float(string='应缴金额')
one_employment_insurance = fields.Float(string='缴费基数')
one_employment_insurance_payable = fields.Float(string='应缴金额')
# 医疗
medical_insurance_base = fields.Float(string='缴费基数')
medical_insurance_payable = fields.Float(string='应缴金额')
one_medical_insurance_base = fields.Float(string='缴费基数')
one_medical_insurance_payable = fields.Float(string='应缴金额')
# 工伤
injury_insurance = fields.Float(string='缴费基数')
injury_insurance_payable = fields.Float(string='应缴金额')
# 合计
total_unit_part = fields.Float(string='单位部分合计')
total_personal_part = fields.Float(string='个人部分合计')
total_payable = fields.Float(string='应缴金额合计')
performance_deduction = fields.Float(string='扣发绩效奖金')
commuting_deduction = fields.Float(string='扣发通信补贴', store=True)
base_salary_deduction = fields.Float(string='扣发基本工资', store=True)
specialized_deduction = fields.Float(string='累计专项扣除')
@api.onchange('minus_att', 'individual_income_tax', 'minus_annuity_difference')
def _onchange_ensure_negative(self):
for record in self:
if record.minus_att > 0:
record.minus_att = -record.minus_att
if record.individual_income_tax > 0:
record.individual_income_tax = -record.individual_income_tax
if record.minus_annuity_difference > 0:
record.minus_annuity_difference = -record.minus_annuity_difference
def _process_fields(self, values):
fields_to_process = [
'minus_annuity_difference',
'individual_income_tax',
'minus_att',
'retroactive_insurance',
'tax_payable',
'one_basic_pension_payable',
'employment_insurance_payable',
'basic_pension_payable',
'medical_insurance_payable',
'one_employment_insurance_payable',
'one_medical_insurance_base',
'social_security',
'injury_insurance_payable',
'total_unit_part',
'total_personal_part',
'total_payable',
'one_medical_insurance_payable',
'performance_deduction',
'commuting_deduction',
'base_salary_deduction'
]
for field in fields_to_process:
if field in values:
# 处理逻辑:取绝对值后减1
values[field] = abs(values[field]) * -1
return values
def write(self, values):
values = self._process_fields(values)
return super(InheritHrPayslip, self).write(values)
def disposable(self):
# 处理2025年1月至5月的数据
year = 2025
# 要处理的月份范围
months = range(1, 6) # 1到5月
for month in months:
payslip_run_name = f"{year}年-{self._get_chinese_month(month)}"
payslip_run = self.env['hr.payslip.run'].search(
[('name', '=', payslip_run_name)],
limit=1
)
if payslip_run:
# 查找该批次下的所有工资单记录
payslips = self.env['hr.payslip'].search([
('payslip_run_id', '=', payslip_run.id)
])
if payslips:
# 定义需要处理的所有字段(用于触发write)
all_fields = [
'minus_annuity_difference',
'individual_income_tax',
'minus_att',
'retroactive_insurance',
'tax_payable',
'one_basic_pension_payable',
'employment_insurance_payable',
'basic_pension_payable',
'medical_insurance_payable',
'one_employment_insurance_payable',
'one_medical_insurance_base',
'social_security',
'injury_insurance_payable',
'total_unit_part',
'total_personal_part',
'total_payable',
'one_medical_insurance_payable',
]
fields_to_modify = [
'housing_provident_fund',
'union_dues'
'basic_medical_insurance',
'basic_endowment_insurance',
'unemployment_insurance',
'work_injury_insurance',
'three_one',
]
# 处理每个工资单
for payslip in payslips:
# 只修改指定的两个字段
for field in fields_to_modify:
if hasattr(payslip, field) and getattr(payslip, field) is not None:
original_value = getattr(payslip, field)
new_value = abs(original_value) * -1
setattr(payslip, field, new_value)
for payslip in payslips:
write_vals = {}
for field in all_fields:
if hasattr(payslip, field):
write_vals[field] = getattr(payslip, field)
payslip.write(write_vals)
def _get_chinese_month(self, month):
chinese_months = ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月',
'十二月']
return chinese_months[month - 1]
@api.onchange('line_ids')
def _onchange_line_ids(self):
for line in self.line_ids:
negative_items = [
'考勤扣发(减)',
'个税扣发(减)',
'年金差额(减)',
'扣发绩效奖金',
'三险一金',
'企业(职业)年金',
'应补(退)税额',
'工会会费',
'补扣保险',
]
if line.name in negative_items and line.total > 0:
line.total = -line.total
@api.onchange('payslip_run_id')
def _onchange_payslip_run_id(self):
if not self.payslip_run_id:
self.date_from = False
self.date_to = False
return
batch_name = self.payslip_run_id.name
pattern = r'(\d{4})年-([一二三四五六七八九十]{1,2})月'
match = re.search(pattern, batch_name)
if match:
year = int(match.group(1))
month_name = match.group(2)
month_mapping = {
'一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6,
'七': 7, '八': 8, '九': 9, '十': 10, '十一': 11, '十二': 12
}
month = month_mapping.get(month_name)
if month:
self.date_from = datetime(year, month, 1).date()
self.date_to = (datetime(year, month, 1) + relativedelta(months=1, days=-1)).date()
@api.model_create_multi
def create(self, vals_list):
# 存储每个工资单的检查结果
existing_payslips = {}
for vals in vals_list:
employee_id = vals.get('employee_id')
payslip_run_id = vals.get('payslip_run_id')
if not employee_id or not payslip_run_id:
continue
# 检查工资单是否存在于各个状态
conditions = [
(_('薪资审定'), [
('employee_id', '=', employee_id),
('payslip_run_id', '=', payslip_run_id),
('is_salary_push', '=', True),
('is_salary_release', '=', False)
]),
(_('薪资归档'), [
('employee_id', '=', employee_id),
('payslip_run_id', '=', payslip_run_id),
('is_salary_push', '=', True),
('is_salary_release', '=', True)
]),
(_('薪资计算'), [
('employee_id', '=', employee_id),
('payslip_run_id', '=', payslip_run_id),
('is_push', '=', False)
]),
(_('个税计算'), [
('employee_id', '=', employee_id),
('payslip_run_id', '=', payslip_run_id),
('is_push', '=', True),
('is_salary_push', '=', False)
])
]
for state_name, domain in conditions:
existing = self.search(domain, limit=1)
if existing:
# 获取员工和批次名称用于错误提示
employee = self.env['hr.employee'].browse(employee_id)
payslip_run = self.env['hr.payslip.run'].browse(payslip_run_id)
existing_payslips[(employee_id, payslip_run_id)] = {
'employee_name': employee.name,
'batch_name': payslip_run.name,
'state': state_name
}
break
if existing_payslips:
error_messages = []
for key, info in existing_payslips.items():
msg = _("员工 %(employee)s 在批次 '%(batch)s' 的工资单已存在于'%(state)s'中!") % {
'employee': info['employee_name'],
'batch': info['batch_name'],
'state': info['state']
}
error_messages.append(msg)
raise UserError('\n'.join(error_messages))
res = super(InheritHrPayslip, self).create(vals_list)
return res
@api.model
def create(self, vals):
"""
为了解决部分薪资生成未生效,单独处理计算公式
工会会费:round(abs(payslip.base_salary * 0.005) * -1, 2) --union_dues
:param vals:
:return:
"""
record = super().create(vals)
employee_id = self.env['hr.employee'].search([('id', '=', vals['employee_id'])])
payroll = self.env['hr.contract'].search([('employee_id', '=', employee_id.id), ('state', '=', 'open')])[-1].structure_type_id.default_struct_id
if payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)]):
base_salary = payroll.rule_ids.search([('name', '=', '基本工资'), ('struct_id', '=', payroll.id)])[-1].amount_fix
# if record.worked_days_line_ids.search([('name', '=', '工会会费')]):
# record.line_ids.search([('name', '=', '工会会费')]).total = round(abs(base_salary * 0.005) * -1, 2)
record.write({'union_dues': round(abs(base_salary * 0.005) * -1, 2)})
return record
@api.ondelete(at_uninstall=False)
def _unlink_if_draft_or_cancel(self):
if any(payslip.state not in ('draft', 'cancel') for payslip in self):
raise UserError(_('确定是否删除?手工填写的临时工资项数据也将被删除!\n 工资条只有草稿和取消状态才可以删除,请批量取消后操作'))
def action_refresh_from_work_entries(self):
payslip_records = self.browse(self._context.get('active_ids', self._context.get('active_id')))
payslips = payslip_records.filtered(lambda slip: slip.employee_id.name != '王鸿怡')
for rec in payslips:
rec.compute_sheet()
@api.onchange('employee_id')
def _onchange_salary_group_id(self):
self.salary_group_id = self.employee_id.contract_id.salary_group_id
@api.depends('line_ids', 'social_security')
def _compute_salary(self):
for rec in self:
if rec.line_ids:
for line in rec.line_ids:
if line.name == '学习进步奖':
rec.study_on = line.total
if line.name == '临时绩效奖金':
rec.temporary_performance_bonus = line.total
if line.name == '上年安全生产工作奖':
rec.last_year_safety = line.total
if line.name == '生育津贴':
rec.allowance = line.total
if line.name == '儿童节慰问金':
rec.condolence_fund = line.total
if line.name == '春节福利':
rec.spring_festival = line.total
if line.name == '临时绩效工资':
rec.based_pay = line.total
if line.name == '上年绩效评优':
rec.last_year_performance_evaluation = line.total
if line.name == '上年社保补缴':
rec.social_security = line.total
if line.name == '上年年度清算':
rec.liquidation_year = line.total
if line.name == '年功奖':
rec.year_gong = line.total
if line.name == '生活补贴':
rec.life_salary = line.total
if line.name == '安全生产奖':
rec.safety_incentives = line.total
if line.name == '前年年度清算':
rec.year_compute = line.total
if line.name == '前年年度结余':
rec.year_jie = line.total
if line.name == '上月多扣绩效补回':
rec.performance_up = line.total
if line.name == '上年考核保留量':
rec.retention_previous_year = line.total
if line.name == '上年年度结余':
rec.last_year_jie = line.total
if line.name == '上年个税退回':
rec.individual_tax_refund = line.total
if line.name == '经营特别奖':
rec.jing_te = line.total
if line.name == '补发津贴':
rec.retroactive_allowance = line.total
if line.name == '个税扣发(减)':
rec.individual_income_tax = line.total
if line.name == '个税补发(增)':
rec.add_income_tax = line.total
if line.name == '年金差额(减)':
rec.minus_annuity_difference = line.total
if line.name == '年金差额(增)':
rec.add_annuity_difference = line.total
if line.name == '考勤扣发(减)':
rec.minus_att = line.total
if line.name == '考勤补发(增)':
rec.add_att = line.total
if line.name == '综合补贴':
rec.comprehensive_subsidy = line.total
if line.name == '安全生产管理补贴':
rec.safety_subsidy = line.total
if line.name == '职级工资':
rec.rank_salary = line.total
if line.name == '岗位补贴':
rec.position_subsidy = line.total
if line.name == '工龄补贴':
rec.seniority_allowance = line.total
if line.name == '岗位工资':
rec.position_salary = line.total
if line.name == '基本工资':
rec.base_salary = line.total
if line.name == '预发绩效奖金':
rec.performance_bonuses = line.total
if line.name == '工龄工资':
rec.seniority_salary = line.total
if line.name == '通讯补贴':
rec.commuting_allowance = line.total
if line.name == '全勤奖':
rec.full_attendance_award = line.total
if line.name == '加班费':
rec.overtime_pay = line.total
if line.name == '质检员津贴':
rec.quality_allowance = line.total
if line.name == '误餐费':
rec.meal_misunderstanding_fee = line.total
if line.name == '事假':
rec.personal_leave = line.total
if line.name == '病假':
rec.sick_leave = line.total
if line.name == '产假':
rec.maternity_leave = line.total
if line.name == '婚假':
rec.marriage_leave = line.total
if line.name == '上月值班费':
rec.last_duty = line.total
if line.name == '计生假':
rec.family_planning_leave = line.total
if line.name == '丧假':
rec.bereavement_leave = line.total
if line.name == '探亲假':
rec.home_visit_leave = line.total
if line.name == '住院病假':
rec.hospital_sick_leave = line.total
if line.name == '考试假':
rec.exam_leave = line.total
if line.name == '企业(职业)年金':
rec.b_year = line.total
if line.name == '家长会假':
rec.parents_meeting_leave = line.total
if line.name == '基本医疗保险费':
rec.basic_medical_insurance = line.total
if line.name == '基本养老保险费':
rec.basic_endowment_insurance = line.total
if line.name == '失业保险费':
rec.unemployment_insurance = line.total
if line.name == '工伤保险费':
rec.work_injury_insurance = line.total
if line.name == '住房公积金':
rec.housing_provident_fund = line.total
if line.name == '扣发绩效奖金':
rec.performance_deduction = line.total
if line.name == '扣发通信补贴':
rec.commuting_deduction = line.total
if line.name == '扣发基本工资':
rec.base_salary_deduction = line.total
if line.name == '优秀部门经理/员工奖励':
rec.excellent_employees = line.total
if line.name == '环境作业津贴':
rec.environmental_allowance = line.total
if line.name == '高温补贴':
rec.high_subsidy = line.total
if line.name == '超出体检标准的费用':
rec.medical_fee = line.total
if line.name == '补扣保险':
rec.retroactive_insurance = line.total
if line.name == '扣除环境作业津贴多发放部分':
rec.distribution_part = line.total
if line.name == '岗位绩效':
rec.job_performance = line.total
if line.name == '值班(含节日加班)':
rec.duty = line.total
if line.name == '月加班':
rec.monthly_overtime = line.total
if line.name == '年金':
rec.annuity = line.total
rec.three_one = rec.one_basic_pension_payable + rec.one_employment_insurance_payable + rec.one_medical_insurance_payable + rec.social_security + rec.housing_provident_fund
def basic_pension_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "五险一金导入",
"res_model": "basic.pension.wizard",
"view_mode": "form",
"target": "new",
}
def put_payslip(self):
menu_id = self.env['ir.ui.menu'].search([('name', '=', '工资条'), ('parent_id', '!=', False)])
url = f'https://oa.thtzjt.com/web#id={self.id}&cids=2&action={menu_id.action.id}&model=hr.payslip&view_type=list&menu_id={menu_id.id}'
we_id = self.env['hr.employee'].search([('id', '=', self.employee_id)]).wecom_userid
self.sudo().env["wecom.apps"].sync_send_message_textcard(category='payslip', user_id=we_id, title="薪资确认",
description=self.employee_id.name + '请确认你的薪资:',
url=url, btntxt="详细信息")
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success',
'message': '确认通知已发送',
'sticky': False,
}
}
def payslip_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "工资条推送",
"res_model": "payslip.send.wizard",
"view_mode": "form",
"target": "new",
"context": {
'default_employee_id': self.id.employee_id.id
}
}
def action_salary_payslip_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "累计专项导入",
"res_model": "salary.payslip.wizard",
"view_mode": "form",
"target": "new",
}
def action_temporary_payslip_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "临时工资项导入",
"res_model": "temporary.payslip.wizard",
"view_mode": "form",
"target": "new",
}
@api.depends('year_compute', 'year_jie', 'year_gong', 'jing_te', 'study_on', 'seniority_allowance',
'position_salary', 'liquidation_year', 'rank_salary',
'quality_allowance', 'high_subsidy', 'environmental_allowance', 'last_duty', 'seniority_salary',
'commuting_allowance', 'base_salary', 'performance_bonuses', 'comprehensive_subsidy',
'full_attendance_award', 'overtime_pay', 'based_pay',
'january_performance', 'position_subsidy', 'safety_incentives', 'temporary_performance_bonus',
'performance_deduction', 'commuting_deduction', 'retroactive_insurance',
'base_salary_deduction', 'last_year_performance_evaluation', 'safety_subsidy', 'job_performance',
'distribution_part', 'minus_att', 'add_att', 'spring_festival',
'duty', 'monthly_overtime', 'annuity', 'excellent_employees', 'condolence_fund',
'retention_previous_year')
def _compute_total_before_tax(self):
for record in self:
record.total_before_tax = (
record.rank_salary +
record.condolence_fund +
record.spring_festival +
record.liquidation_year +
record.add_att +
record.year_compute +
record.year_jie +
record.year_gong +
record.jing_te +
record.study_on +
record.safety_incentives +
record.temporary_performance_bonus +
record.last_year_performance_evaluation +
record.retention_previous_year +
record.based_pay +
record.january_performance -
record.distribution_part +
record.seniority_allowance +
record.position_salary +
record.quality_allowance +
record.high_subsidy +
record.environmental_allowance +
record.last_duty +
record.seniority_salary +
record.commuting_allowance +
record.base_salary +
record.performance_bonuses +
record.comprehensive_subsidy +
record.full_attendance_award +
record.overtime_pay +
record.position_subsidy +
record.safety_subsidy +
record.job_performance +
record.minus_att +
record.duty +
record.performance_deduction +
record.monthly_overtime +
record.excellent_employees +
record.commuting_deduction +
record.retroactive_insurance +
record.base_salary_deduction
)
# 本期应收 - 三险一金 - 年金 - 应补退税 - 工会会费 - 个税退回
# total_before_tax - three_one - b_year - union_dues - tax_payable - individual_tax_refund
@api.depends('total_before_tax', 'three_one', 'b_year', 'union_dues', 'tax_payable', 'individual_tax_refund',
'medical_fee', 'individual_income_tax', 'add_income_tax', 'minus_annuity_difference',
'add_annuity_difference', 'maternity_allowance')
def _compute_actual_salary(self):
for record in self:
record.actual_salary = (record.total_before_tax +
record.three_one -
record.social_security -
record.medical_fee +
record.b_year +
record.union_dues +
record.tax_payable +
record.maternity_allowance +
record.individual_income_tax +
record.add_income_tax +
record.minus_annuity_difference +
record.individual_tax_refund +
record.life_salary +
record.add_annuity_difference
)
if record.employee_id.name == '罗梦烨':
_logger.info("罗梦烨实发{} 本期应:{},31:{},年金:{}应补:{}工会{}个税{}".format(record.actual_salary, record.total_before_tax,
record.three_one, record.b_year, record.union_dues,
record.tax_payable, record.individual_tax_refund))
def put_button(self):
for record in self:
record.write({'is_push': True})
def return_button(self):
for record in self:
record.write({'is_push': False})
def put_salary_button(self):
for record in self:
record.write({'is_salary_push': True})
def action_put_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "推送个税计算",
"res_model": "put.wizard",
"view_mode": "form",
"target": "new",
}
def action_return_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "退回薪资计算",
"res_model": "return.wizard",
"view_mode": "form",
"target": "new",
}
def action_put_salary_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "推送薪资审定",
"res_model": "put.salary.wizard",
"view_mode": "form",
"target": "new",
}
def action_return_salary_wizard(self):
return {
"type": "ir.actions.act_window",
"name": "退回个税计算",
"res_model": "return.salary.wizard",
"view_mode": "form",
"target": "new",
}
def compute_sheet(self):
payslips = self.filtered(lambda slip: slip.state in ['draft', 'verify'])
payslips.line_ids.unlink()
self.env.flush_all()
for payslip in payslips:
number = payslip.number or self.env['ir.sequence'].next_by_code('salary.slip')
payslip.write({
'number': number,
'state': 'verify',
'compute_date': '2025-01-25'
})
self.env['hr.payslip.line'].create(payslips._get_payslip_lines())
return True
# 薪资归档
def put_release(self):
for record in self:
record.write({
'is_salary_release': True,
'salary_state': 'to_release',
})
# 薪资归档
def all_put_release(self):
payslip_ids = self.env["hr.payslip"].browse(self._context.get('active_ids', self._context.get('active_id')))
for record in payslip_ids:
record.write({
'is_salary_release': True,
'salary_state': 'to_release',
})
def return_salary_button(self):
for record in self:
record.write({'is_salary_release': False,
'salary_state': 'to_signed'})
def all_return_release(self):
payslip_ids = self.env["hr.payslip"].browse(self._context.get('active_ids', self._context.get('active_id')))
for record in payslip_ids:
record.write({
'is_salary_release': False,
'salary_state': 'to_signed',
})
def button_salary_print(self):
return {
"type": "ir.actions.act_window",
"name": "打印薪资",
"res_model": "salary.print.wizard",
"view_mode": "form",
"target": "new",
}
def open_archive_record(self):
return {
"type": "ir.actions.act_window",
"res_model": "hr.payslip.line",
"name": "薪资明细",
"view_mode": "tree",
"views": [(False, "tree")],
"domain": [("slip_id", "=", self.id), ("amount", "!=", 0)],
"target": "new",
"context": {
"tree_view_ref": "yuthon_salary.view_hr_payslip_line_tree_custom",
"group_by": ["category_id"], # 按 category_id 分组
},
}
def open_record(self):
return {
"type": "ir.actions.act_window",
"res_model": 'hr.payslip',
"res_id": self.id,
"name": "薪资详情",
"view_mode": "form",
}
def update_record(self):
payslip_ids = self.env["hr.payslip"].browse(
self._context.get('active_ids', self._context.get('active_id')))
for record in payslip_ids:
record.write({
'salary_group_id': record.employee_id.contract_id.salary_group_id.id,
})
@api.model
def get_view(self, view_id=None, view_type='form', **options):
# 检查是否通过特定动作触发
if options.get('action_id') == 3666:
# 获取当前用户相关的未确认工资单
domain = [
('employee_id.user_id', '=', self.env.uid),
]
unconfirmed_slips = self.search(domain)
if unconfirmed_slips:
unconfirmed_slips.write({'confirm_status': 'confirm'})
return super().get_view(view_id, view_type, **options)
def initialize_payroll_wizard(self):
payslip_ids = self.env["hr.payslip"].browse(
self._context.get('active_ids', self._context.get('active_id')))
payslip_list = []
for record in payslip_ids:
payslip_list.append([4, record.id])
return {
'name': '初始化工资单',
'type': 'ir.actions.act_window',
'view_mode': 'form',
'res_model': 'initialize.payroll.wizard',
'target': 'new',
'context': {
'default_payslip_ids': payslip_list
}
}