|
- # -*- coding: utf-8 -*-
- # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
- # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
- import datetime
-
- from odoo import fields, models, api, _
- from odoo.exceptions import UserError, ValidationError
-
- change = [('time', '计时'),
- ('piece', '计件'),
- ('efficiency', '计效')]
-
-
- class StaffWages(models.Model):
- _name = 'staff.wages'
- _description = '员工工资'
- _inherit = ['mail.thread']
- _order = "name"
-
- date = fields.Date('记帐日期', required=True)
- name = fields.Many2one(
- 'finance.period',
- '会计期间',
- compute='compute_period_id', ondelete='restrict', store=True)
- state = fields.Selection([('draft', '草稿'),
- ('done', '已完成')], '状态', default='draft',
- index=True,
- )
- line_ids = fields.One2many('wages.line', 'order_id', '工资明细行',
- copy=True)
- payment = fields.Many2one('bank.account', '付款方式')
- wage_other_money_order = fields.Many2one('other.money.order', '工资对应付款单', readonly=True, ondelete='restrict',
- help='确认时生成的对应付款单', copy=False)
- housing_other_money_order = fields.Many2one('other.money.order', '住房公积金对应付款单', readonly=True,
- ondelete='restrict', help='确认时生成的对应付款单', copy=False)
- social_security_other_money_order = fields.Many2one('other.money.order', '社保对应付款单', readonly=True,
- ondelete='restrict', help='确认时生成的对应付款单', copy=False)
- voucher_id = fields.Many2one(
- 'voucher', '计提工资凭证', readonly=True, ondelete='set null', copy=False)
- change_voucher_id = fields.Many2one(
- 'voucher', '修正计提凭证', readonly=True, ondelete='set null', copy=False)
- note = fields.Char('备注', help='本月备注')
-
- # 汇总字段
- total_wage = fields.Float('应发工资合计', digits=(12, 2))
- total_endowment = fields.Float('应扣养老合计', digits=(12, 2))
- total_health = fields.Float('应扣医疗合计', digits=(12, 2))
- total_unemployment = fields.Float('应扣失业合计', digits=(12, 2))
- total_housing_fund = fields.Float('应扣住房合计', digits=(12, 2))
- total_personal_tax = fields.Float('应扣个税合计', digits=(12, 2))
- total_amount = fields.Float('实发工资合计', digits=(12, 2))
-
- total_endowment_co = fields.Float('应扣公司养老合计', digits=(12, 2))
- total_health_co = fields.Float('应扣公司医疗合计', digits=(12, 2))
- total_unemployment_co = fields.Float('应扣公司失业合计', digits=(12, 2))
- total_housing_fund_co = fields.Float('应扣公司住房合计', digits=(12, 2))
- total_injury = fields.Float('应扣公司工伤保险合计', digits=(12, 2), help='公司承担的工伤保险合计')
- total_maternity = fields.Float('应扣公司生育保险合计', digits=(12, 2), help='公司承担的生育保险合计')
-
- @api.depends('date')
- def compute_period_id(self):
- for record in self:
- record.name = self.env['finance.period'].get_period(record.date)
-
- @api.onchange('line_ids')
- def _total_amount_wage(self):
- for record in self:
- record.total_amount = sum(line.amount_wage for line in record.line_ids)
- record.total_wage = sum(line.all_wage for line in record.line_ids)
- record.total_endowment = sum(line.endowment for line in record.line_ids)
- record.total_health = sum(line.health for line in record.line_ids)
- record.total_unemployment = sum(line.unemployment for line in record.line_ids)
- record.total_housing_fund = sum(line.housing_fund for line in record.line_ids)
- record.total_personal_tax = sum(line.personal_tax for line in record.line_ids)
- record.total_endowment_co = sum(line.endowment_co for line in record.line_ids)
- record.total_health_co = sum(line.health_co for line in record.line_ids)
- record.total_unemployment_co = sum(line.unemployment_co for line in record.line_ids)
- record.total_housing_fund_co = sum(line.housing_fund_co for line in record.line_ids)
- record.total_injury = sum(line.injury for line in record.line_ids)
- record.total_maternity = sum(line.maternity for line in record.line_ids)
-
- def staff_wages_accrued(self):
- """计提工资"""
- for record in self:
- if not record.line_ids:
- raise UserError('明细行不能为空')
- if record.voucher_id:
- # 如计提凭证已存在,判断当前期间是否与工资期间相同
- date = fields.Date.context_today(self)
- if self.voucher_id.period_id == self.env['finance.period'].get_period(date):
- # 如相同,反审核计提凭证并重新生成计提凭证
- voucher, self.voucher_id = self.voucher_id, False
- if voucher.state == 'done':
- voucher.voucher_draft()
- voucher.unlink()
- voucher = self.create_voucher(self.date)
- voucher.voucher_done()
- self.voucher_id = voucher
- else:
- # 如不同,生成并审核修正凭证
- self.change_voucher()
- else:
- voucher = self.create_voucher(self.date)
- voucher.voucher_done()
- self.voucher_id = voucher
-
- def change_voucher(self):
- """
- 生成并审核修正计提凭证
- :return:
- """
- before_voucher = self.voucher_id
- if self.change_voucher_id:
- # 如果修正计提凭证存在,则删除后重新生成修正计提凭证
- change_voucher, self.change_voucher_id = self.change_voucher_id, False
- if change_voucher.state == 'done':
- change_voucher.voucher_draft()
- change_voucher.unlink()
- change_voucher = self.create_voucher()
- for change_line in change_voucher.line_ids:
- for before_line in before_voucher.line_ids:
- if change_line.account_id == before_line.account_id and change_line.auxiliary_id == before_line.auxiliary_id:
- change_line.credit -= before_line.credit
- change_line.debit -= before_line.debit
- continue
-
- for change_line in change_voucher.line_ids:
- if change_line.credit == 0 and change_line.debit == 0:
- change_line.unlink()
-
- if not change_voucher.line_ids:
- change_voucher.unlink()
- else:
- change_voucher.voucher_done()
- self.write({'change_voucher_id': change_voucher.id})
-
- def create_credit_line(self, voucher, name, account, credit, auxiliary_id):
- """
- 生成贷方行
- :param voucher: 凭证
- :param name: 摘要
- :param account: 借方科目
- :param credit: 贷方金额
- :return:
- """
- if credit:
- vals = {
- 'voucher_id': voucher.id,
- 'name': name,
- 'account_id': account.id,
- 'credit': credit,
- 'auxiliary_id': auxiliary_id and auxiliary_id.id,
- }
- self.env['voucher.line'].create(vals)
-
- def create_voucher(self, date=datetime.date.today()):
- """
- 生成并审核计提凭证
- :param date: 一个日期
- :return:
- """
- self.ensure_one()
- vouch_obj = self.env['voucher'].create({'date': date, 'ref': '%s,%s' % (self._name, self.id)})
- credit_account = self.env.ref('staff_wages.staff_wages')
- res = {}
- for line in self.line_ids:
- staff = self.env['staff.contract'].search([('staff_id', '=', line.name.id)])
- debit_account = staff.job_id and staff.job_id.account_id or self.env.ref('finance.small_business_chart5602001')
- if debit_account not in res:
- res[debit_account] = {'debit': 0}
- val = res[debit_account]
- debit = val.get('debit') + line.all_wage + line.housing_fund_co + line.endowment_co + line.health_co + line.unemployment_co + line.injury + line.maternity
- if debit:
- val.update({
- 'debit': val.get('debit') + line.all_wage + line.housing_fund_co
- + line.endowment_co + line.health_co + line.unemployment_co
- + line.injury + line.maternity,
- 'voucher_id': vouch_obj.id,
- 'account_id': debit_account.id,
- 'name': u'提本月工资'})
-
- # 生成借方凭证行
- for account_id, val in res.items():
- self.env['voucher.line'].create(
- dict(val, account_id=account_id.id))
- # 生成贷方凭证行
- endowment_co = self.env.ref('staff_wages.categ_endowment_co') # 公司缴纳养老类别
- health_co = self.env.ref('staff_wages.categ_health_co') # 公司缴纳医疗类别
- unemployment_co = self.env.ref('staff_wages.categ_unemployment_co') # 公司缴纳失业类别
- maternity = self.env.ref('staff_wages.categ_maternity') # 公司缴纳生育类别
- injury = self.env.ref('staff_wages.categ_injury') # 公司缴纳工伤类别
- housing_co = self.env.ref('staff_wages.categ_housing_fund_co') # 公司缴纳住房公积金类别
- for line in self.line_ids:
- self.create_credit_line(vouch_obj, u'提本月工资', credit_account.account_id, line.all_wage, line.name.auxiliary_id)
- self.create_credit_line(vouch_obj, u'提本月养老保险', endowment_co.account_id, self.total_endowment_co, False)
- self.create_credit_line(vouch_obj, u'提本月医疗保险', health_co.account_id, self.total_health_co, False)
- self.create_credit_line(vouch_obj, u'提本月失业保险', unemployment_co.account_id, self.total_unemployment_co, False)
- self.create_credit_line(vouch_obj, u'提本月生育保险', maternity.account_id, self.total_maternity, False)
- self.create_credit_line(vouch_obj, u'提本月工伤保险', injury.account_id, self.total_injury, False)
- self.create_credit_line(vouch_obj, u'提本月公积金', housing_co.account_id, self.total_housing_fund_co, False)
- return vouch_obj
-
- def staff_wages_confirm(self):
- """确认工资单,生成付款单"""
- for record in self:
- if record.state == 'done':
- raise UserError('请不要重复确认')
- if not record.voucher_id:
- raise UserError('工资单还未计提,请先计提')
- record.create_other_pay_wage()
- record.create_other_pay_housing_fund()
- record.create_other_pay_social_security()
- record.state = 'done'
-
- def create_other_pay_wage(self):
- '''选择结算账户,生成其他支出单 '''
- self.ensure_one()
- StaffWages = self.env.ref('staff_wages.staff_wages')
- endowment = self.env.ref('staff_wages.endowment')
- unemployment = self.env.ref('staff_wages.unemployment')
- housing_fund = self.env.ref('staff_wages.housing_fund')
- health = self.env.ref('staff_wages.health')
- personal_tax = self.env.ref('staff_wages.personal_tax')
-
- other_money_order = self.env['other.money.order'].create({
- 'state': 'draft',
- 'type': 'other_pay',
- 'date': self.date,
- 'bank_id': self.payment.id,
- })
- for line in self.line_ids:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': line.all_wage,
- 'category_id': StaffWages and StaffWages.id,
- })
- if self.total_endowment:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': -1 * self.total_endowment,
- 'category_id': endowment and endowment.id
- })
- if self.total_unemployment:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': -1 * self.total_unemployment,
- 'category_id': unemployment and unemployment.id
- })
- if self.total_housing_fund:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': -1 * self.total_housing_fund,
- 'category_id': housing_fund and housing_fund.id
- })
- if self.total_health:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': -1 * self.total_health,
-
- 'category_id': health and health.id
- })
- if self.total_personal_tax:
- self.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': -1 * self.total_personal_tax,
- 'category_id': personal_tax and personal_tax.id
- })
- self.wage_other_money_order = other_money_order
-
- def create_other_pay_housing_fund(self):
- """
- 生成住房公积金的其他支出单
- 审核时生成凭证:
- 借:应付职工薪酬-公积金 公司公积金
- 其他应付款-代扣公积金 个人公积金
- 贷: 付款方式对应的科目
- :return: 其他支出单
- """
- self.ensure_one()
- housing_co = self.env.ref('staff_wages.categ_housing_fund_co') # 公司缴纳住房公积金类别
- housing = self.env.ref('staff_wages.housing_fund') # 个人缴纳住房公积金类别
- order = self.env['other.money.order'].create({
- 'state': 'draft',
- 'type': 'other_pay',
- 'date': self.date,
- 'bank_id': self.payment.id,
- })
- self.create_other_order_line(order, housing_co, self.total_housing_fund_co)
- self.create_other_order_line(order, housing, self.total_housing_fund)
- self.housing_other_money_order = order
-
- def create_other_pay_social_security(self):
- """
- 生成社保的其他支出单
- 审核时生成凭证:
- 借: 5个公司的,3个个人的
- 贷: 付款方式对应的科目
- :return: 其他支出单
- """
- for record in self:
- other_order = self.env['other.money.order'].create({
- 'type': 'other_pay',
- 'date': record.date,
- 'bank_id': record.payment.id,
- 'state': 'draft',
- })
- endowment_co = self.env.ref('staff_wages.categ_endowment_co') # 公司缴纳养老类别
- health_co = self.env.ref('staff_wages.categ_health_co') # 公司缴纳医疗类别
- unemployment_co = self.env.ref('staff_wages.categ_unemployment_co') # 公司缴纳失业类别
- maternity = self.env.ref('staff_wages.categ_maternity') # 公司缴纳生育类别
- injury = self.env.ref('staff_wages.categ_injury') # 公司缴纳工伤类别
- endowment = self.env.ref('staff_wages.endowment') # 个人缴纳养老类别
- health = self.env.ref('staff_wages.health') # 个人缴纳医疗类别
- unemployment = self.env.ref('staff_wages.unemployment') # 个人缴纳失业类别
- record.create_other_order_line(other_order, endowment_co, record.total_endowment_co)
- record.create_other_order_line(other_order, health_co, record.total_health_co)
- record.create_other_order_line(other_order, unemployment_co, record.total_unemployment_co)
- record.create_other_order_line(other_order, maternity, record.total_maternity)
- record.create_other_order_line(other_order, injury, record.total_injury)
- # 个人的
- record.create_other_order_line(other_order, endowment, record.total_endowment)
- record.create_other_order_line(other_order, health, record.total_health)
- record.create_other_order_line(other_order, unemployment, record.total_unemployment)
- record.social_security_other_money_order = other_order
-
- def create_other_order_line(self, order, category_id, amount):
- """
- 生成其他支出单明细行
- :param order: 其他支出单
- :param category_id: 类别
- :param amount: 金额
- :return:
- """
- if amount:
- self.env['other.money.order.line'].create({
- 'other_money_id': order.id,
- 'amount': amount,
- 'category_id': category_id and category_id.id
- })
-
- def staff_wages_draft(self):
- """撤销确认"""
- for record in self:
- if self.state == 'draft':
- raise UserError(u'请不要重复撤销')
- if record.wage_other_money_order:
- other_money_order, record.wage_other_money_order = record.wage_other_money_order, False
- if other_money_order.state == 'done':
- other_money_order.other_money_draft()
- other_money_order.unlink()
- record.state = 'draft'
-
- def unlink(self):
- for record in self:
- # 先解除凭证和工资单关系,再将它们删除
- voucher, record.voucher_id = record.voucher_id, False
- change_voucher, record.change_voucher_id = record.change_voucher_id, False
- if voucher.state == 'done':
- voucher.voucher_draft()
- if change_voucher.state == 'done':
- change_voucher.voucher_draft()
- voucher.unlink()
- change_voucher.unlink()
- return super(StaffWages, self).unlink()
-
-
- class WagesLine(models.Model):
- _name = 'wages.line'
- _description = '工资明细行'
-
- order_id = fields.Many2one('staff.wages', '工资单', required=True, ondelete='cascade')
- name = fields.Many2one('staff', '员工', required=True)
- basic_wage = fields.Float('基本工资', digits=(12, 2), required=True)
- basic_date = fields.Integer('月计薪天数', required=True, default=21.75)
- date_number = fields.Integer('出勤天数', required=True)
- add_hour = fields.Integer('加班小时数', default=0)
- add_wage = fields.Float('加班工资', digits=(12, 2), default=0)
- other_wage = fields.Float('补助', digits=(12, 2), default=0)
- deduction = fields.Float('扣款', digits=(12, 2), default=0)
- wage = fields.Float('出勤工资金额', digits=(12, 2), compute='_all_wage_value', store=True)
- all_wage = fields.Float('应发工资', digits=(12, 2), compute='_all_wage_value', store=True)
- endowment = fields.Float('个人养老保险', digits=(12, 2), default=0)
- health = fields.Float('个人医疗保险', digits=(12, 2), default=0)
- unemployment = fields.Float('个人失业保险', digits=(12, 2), default=0)
- housing_fund = fields.Float('个人住房公积金', digits=(12, 2), default=0)
- endowment_co = fields.Float('公司养老保险', digits=(12, 2), default=0, help='公司承担的养老保险')
- health_co = fields.Float('公司医疗保险', digits=(12, 2), default=0, help='公司承担的医疗保险')
- unemployment_co = fields.Float('公司失业保险', digits=(12, 2), default=0, help='公司承担的失业保险')
- housing_fund_co = fields.Float('公司住房公积金', digits=(12, 2), default=0, help='公司承担的住房公积金')
- injury = fields.Float('公司工伤保险', digits=(12, 2), default=0, help='公司承担的工伤保险')
- maternity = fields.Float('公司生育保险', digits=(12, 2), default=0, help='公司承担的生育保险')
- # 因为个人所得税的计算不确定,暂改为手动录入。
- # personal_tax = fields.Float('个人所得税', digits=(12, 2), compute='_personal_tax_value', store=True)
- personal_tax = fields.Float('个人所得税', digits=(12, 2))
- amount_wage = fields.Float('实发工资', digits=(12, 2), compute='_amount_wage_value', store=True)
- wages_change = fields.Selection(change, '记工类型')
-
- _sql_constraints = [
- ('staff_uniq', 'unique(order_id, name)', '同一个工资单不能出现重名员工工资行')
- ]
-
- @api.onchange('date_number', 'basic_wage', 'basic_date')
- def change_wage_addhour(self):
- for record in self:
- if record.date_number > 31 or record.basic_date > 31:
- raise UserError('一个月不可能超过31天')
- if record.date_number >= record.basic_date:
- record.add_hour = 8 * (record.date_number - record.basic_date)
- record.date_number = record.basic_date
- record.add_wage = round(
- 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
-
- @api.onchange('add_hour')
- def change_add_wage(self):
- for record in self:
- if record.add_hour:
- record.add_wage = round(
- 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
-
- @api.onchange('name')
- def change_social_security(self):
- """选择员工后,自动带出五险一金"""
- for record in self:
- social_security = self.env['staff.contract'].search(
- [('staff_id', '=', record.name.id)], limit=1)
- if social_security:
- record.basic_wage = social_security.basic_wage
- record.endowment = social_security.endowment
- record.health = social_security.health
- record.unemployment = social_security.unemployment
- record.housing_fund = social_security.housing_fund
- record.endowment_co = social_security.endowment_co
- record.health_co = social_security.health_co
- record.unemployment_co = social_security.unemployment_co
- record.housing_fund_co = social_security.housing_fund_co
- record.injury = social_security.injury
- record.maternity = social_security.maternity
-
- @api.depends('date_number', 'basic_date', 'add_wage', 'other_wage', 'basic_wage', 'deduction')
- def _all_wage_value(self):
- for record in self:
- if record.date_number >= record.basic_date:
- record.wage = record.basic_wage
- else:
- record.wage = round(
- (record.date_number / record.basic_date or 1) * record.basic_wage, 2)
- record.all_wage = record.wage + record.add_wage + record.other_wage - record.deduction
-
- @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund')
- def _personal_tax_value(self):
- for record in self:
- total = record.all_wage - record.endowment - \
- record.health - record.unemployment - record.housing_fund
- amount = total - 3500
- if amount > 80000:
- record.personal_tax = round(amount * 0.45 - 13505, 2)
- elif amount > 55000:
- record.personal_tax = round(amount * 0.35 - 5505, 2)
- elif amount > 35000:
- record.personal_tax = round(amount * 0.3 - 2755, 2)
- elif amount > 9000:
- record.personal_tax = round(amount * 0.25 - 1005, 2)
- elif amount > 4500:
- record.personal_tax = round(amount * 0.2 - 555, 2)
- elif amount > 1500:
- record.personal_tax = round(amount * 0.1 - 105, 2)
- elif amount >= 0:
- record.personal_tax = round(amount * 0.03, 2)
- else:
- record.personal_tax = 0
-
- @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund', 'personal_tax')
- def _amount_wage_value(self):
- for record in self:
- record.amount_wage = record.all_wage - record.endowment - record.health - \
- record.unemployment - record.housing_fund - record.personal_tax
-
-
- class AddWagesChange(models.Model):
- _inherit = 'staff.contract'
- wages_change = fields.Selection(change, '记工类型')
|