GoodERP
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

503 行
26KB

  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  3. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
  4. import datetime
  5. from odoo import fields, models, api, _
  6. from odoo.exceptions import UserError, ValidationError
  7. change = [('time', '计时'),
  8. ('piece', '计件'),
  9. ('efficiency', '计效')]
  10. class StaffWages(models.Model):
  11. _name = 'staff.wages'
  12. _description = '员工工资'
  13. _inherit = ['mail.thread']
  14. _order = "name"
  15. date = fields.Date('记帐日期', required=True)
  16. name = fields.Many2one(
  17. 'finance.period',
  18. '会计期间',
  19. compute='compute_period_id', ondelete='restrict', store=True)
  20. state = fields.Selection([('draft', '草稿'),
  21. ('done', '已完成')], '状态', default='draft',
  22. index=True,
  23. )
  24. line_ids = fields.One2many('wages.line', 'order_id', '工资明细行',
  25. copy=True)
  26. payment = fields.Many2one('bank.account', '付款方式')
  27. wage_other_money_order = fields.Many2one('other.money.order', '工资对应付款单', readonly=True, ondelete='restrict',
  28. help='确认时生成的对应付款单', copy=False)
  29. housing_other_money_order = fields.Many2one('other.money.order', '住房公积金对应付款单', readonly=True,
  30. ondelete='restrict', help='确认时生成的对应付款单', copy=False)
  31. social_security_other_money_order = fields.Many2one('other.money.order', '社保对应付款单', readonly=True,
  32. ondelete='restrict', help='确认时生成的对应付款单', copy=False)
  33. voucher_id = fields.Many2one(
  34. 'voucher', '计提工资凭证', readonly=True, ondelete='set null', copy=False)
  35. change_voucher_id = fields.Many2one(
  36. 'voucher', '修正计提凭证', readonly=True, ondelete='set null', copy=False)
  37. note = fields.Char('备注', help='本月备注')
  38. # 汇总字段
  39. total_wage = fields.Float('应发工资合计', digits=(12, 2))
  40. total_endowment = fields.Float('应扣养老合计', digits=(12, 2))
  41. total_health = fields.Float('应扣医疗合计', digits=(12, 2))
  42. total_unemployment = fields.Float('应扣失业合计', digits=(12, 2))
  43. total_housing_fund = fields.Float('应扣住房合计', digits=(12, 2))
  44. total_personal_tax = fields.Float('应扣个税合计', digits=(12, 2))
  45. total_amount = fields.Float('实发工资合计', digits=(12, 2))
  46. total_endowment_co = fields.Float('应扣公司养老合计', digits=(12, 2))
  47. total_health_co = fields.Float('应扣公司医疗合计', digits=(12, 2))
  48. total_unemployment_co = fields.Float('应扣公司失业合计', digits=(12, 2))
  49. total_housing_fund_co = fields.Float('应扣公司住房合计', digits=(12, 2))
  50. total_injury = fields.Float('应扣公司工伤保险合计', digits=(12, 2), help='公司承担的工伤保险合计')
  51. total_maternity = fields.Float('应扣公司生育保险合计', digits=(12, 2), help='公司承担的生育保险合计')
  52. @api.depends('date')
  53. def compute_period_id(self):
  54. for record in self:
  55. record.name = self.env['finance.period'].get_period(record.date)
  56. @api.onchange('line_ids')
  57. def _total_amount_wage(self):
  58. for record in self:
  59. record.total_amount = sum(line.amount_wage for line in record.line_ids)
  60. record.total_wage = sum(line.all_wage for line in record.line_ids)
  61. record.total_endowment = sum(line.endowment for line in record.line_ids)
  62. record.total_health = sum(line.health for line in record.line_ids)
  63. record.total_unemployment = sum(line.unemployment for line in record.line_ids)
  64. record.total_housing_fund = sum(line.housing_fund for line in record.line_ids)
  65. record.total_personal_tax = sum(line.personal_tax for line in record.line_ids)
  66. record.total_endowment_co = sum(line.endowment_co for line in record.line_ids)
  67. record.total_health_co = sum(line.health_co for line in record.line_ids)
  68. record.total_unemployment_co = sum(line.unemployment_co for line in record.line_ids)
  69. record.total_housing_fund_co = sum(line.housing_fund_co for line in record.line_ids)
  70. record.total_injury = sum(line.injury for line in record.line_ids)
  71. record.total_maternity = sum(line.maternity for line in record.line_ids)
  72. def staff_wages_accrued(self):
  73. """计提工资"""
  74. for record in self:
  75. if not record.line_ids:
  76. raise UserError('明细行不能为空')
  77. if record.voucher_id:
  78. # 如计提凭证已存在,判断当前期间是否与工资期间相同
  79. date = fields.Date.context_today(self)
  80. if self.voucher_id.period_id == self.env['finance.period'].get_period(date):
  81. # 如相同,反审核计提凭证并重新生成计提凭证
  82. voucher, self.voucher_id = self.voucher_id, False
  83. if voucher.state == 'done':
  84. voucher.voucher_draft()
  85. voucher.unlink()
  86. voucher = self.create_voucher(self.date)
  87. voucher.voucher_done()
  88. self.voucher_id = voucher
  89. else:
  90. # 如不同,生成并审核修正凭证
  91. self.change_voucher()
  92. else:
  93. voucher = self.create_voucher(self.date)
  94. voucher.voucher_done()
  95. self.voucher_id = voucher
  96. def change_voucher(self):
  97. """
  98. 生成并审核修正计提凭证
  99. :return:
  100. """
  101. before_voucher = self.voucher_id
  102. if self.change_voucher_id:
  103. # 如果修正计提凭证存在,则删除后重新生成修正计提凭证
  104. change_voucher, self.change_voucher_id = self.change_voucher_id, False
  105. if change_voucher.state == 'done':
  106. change_voucher.voucher_draft()
  107. change_voucher.unlink()
  108. change_voucher = self.create_voucher()
  109. for change_line in change_voucher.line_ids:
  110. for before_line in before_voucher.line_ids:
  111. if change_line.account_id == before_line.account_id and change_line.auxiliary_id == before_line.auxiliary_id:
  112. change_line.credit -= before_line.credit
  113. change_line.debit -= before_line.debit
  114. continue
  115. for change_line in change_voucher.line_ids:
  116. if change_line.credit == 0 and change_line.debit == 0:
  117. change_line.unlink()
  118. if not change_voucher.line_ids:
  119. change_voucher.unlink()
  120. else:
  121. change_voucher.voucher_done()
  122. self.write({'change_voucher_id': change_voucher.id})
  123. def create_credit_line(self, voucher, name, account, credit, auxiliary_id):
  124. """
  125. 生成贷方行
  126. :param voucher: 凭证
  127. :param name: 摘要
  128. :param account: 借方科目
  129. :param credit: 贷方金额
  130. :return:
  131. """
  132. if credit:
  133. vals = {
  134. 'voucher_id': voucher.id,
  135. 'name': name,
  136. 'account_id': account.id,
  137. 'credit': credit,
  138. 'auxiliary_id': auxiliary_id and auxiliary_id.id,
  139. }
  140. self.env['voucher.line'].create(vals)
  141. def create_voucher(self, date=datetime.date.today()):
  142. """
  143. 生成并审核计提凭证
  144. :param date: 一个日期
  145. :return:
  146. """
  147. self.ensure_one()
  148. vouch_obj = self.env['voucher'].create({'date': date, 'ref': '%s,%s' % (self._name, self.id)})
  149. credit_account = self.env.ref('staff_wages.staff_wages')
  150. res = {}
  151. for line in self.line_ids:
  152. staff = self.env['staff.contract'].search([('staff_id', '=', line.name.id)])
  153. debit_account = staff.job_id and staff.job_id.account_id or self.env.ref('finance.small_business_chart5602001')
  154. if debit_account not in res:
  155. res[debit_account] = {'debit': 0}
  156. val = res[debit_account]
  157. debit = val.get('debit') + line.all_wage + line.housing_fund_co + line.endowment_co + line.health_co + line.unemployment_co + line.injury + line.maternity
  158. if debit:
  159. val.update({
  160. 'debit': val.get('debit') + line.all_wage + line.housing_fund_co
  161. + line.endowment_co + line.health_co + line.unemployment_co
  162. + line.injury + line.maternity,
  163. 'voucher_id': vouch_obj.id,
  164. 'account_id': debit_account.id,
  165. 'name': u'提本月工资'})
  166. # 生成借方凭证行
  167. for account_id, val in res.items():
  168. self.env['voucher.line'].create(
  169. dict(val, account_id=account_id.id))
  170. # 生成贷方凭证行
  171. endowment_co = self.env.ref('staff_wages.categ_endowment_co') # 公司缴纳养老类别
  172. health_co = self.env.ref('staff_wages.categ_health_co') # 公司缴纳医疗类别
  173. unemployment_co = self.env.ref('staff_wages.categ_unemployment_co') # 公司缴纳失业类别
  174. maternity = self.env.ref('staff_wages.categ_maternity') # 公司缴纳生育类别
  175. injury = self.env.ref('staff_wages.categ_injury') # 公司缴纳工伤类别
  176. housing_co = self.env.ref('staff_wages.categ_housing_fund_co') # 公司缴纳住房公积金类别
  177. for line in self.line_ids:
  178. self.create_credit_line(vouch_obj, u'提本月工资', credit_account.account_id, line.all_wage, line.name.auxiliary_id)
  179. self.create_credit_line(vouch_obj, u'提本月养老保险', endowment_co.account_id, self.total_endowment_co, False)
  180. self.create_credit_line(vouch_obj, u'提本月医疗保险', health_co.account_id, self.total_health_co, False)
  181. self.create_credit_line(vouch_obj, u'提本月失业保险', unemployment_co.account_id, self.total_unemployment_co, False)
  182. self.create_credit_line(vouch_obj, u'提本月生育保险', maternity.account_id, self.total_maternity, False)
  183. self.create_credit_line(vouch_obj, u'提本月工伤保险', injury.account_id, self.total_injury, False)
  184. self.create_credit_line(vouch_obj, u'提本月公积金', housing_co.account_id, self.total_housing_fund_co, False)
  185. return vouch_obj
  186. def staff_wages_confirm(self):
  187. """确认工资单,生成付款单"""
  188. for record in self:
  189. if record.state == 'done':
  190. raise UserError('请不要重复确认')
  191. if not record.voucher_id:
  192. raise UserError('工资单还未计提,请先计提')
  193. record.create_other_pay_wage()
  194. record.create_other_pay_housing_fund()
  195. record.create_other_pay_social_security()
  196. record.state = 'done'
  197. def create_other_pay_wage(self):
  198. '''选择结算账户,生成其他支出单 '''
  199. self.ensure_one()
  200. StaffWages = self.env.ref('staff_wages.staff_wages')
  201. endowment = self.env.ref('staff_wages.endowment')
  202. unemployment = self.env.ref('staff_wages.unemployment')
  203. housing_fund = self.env.ref('staff_wages.housing_fund')
  204. health = self.env.ref('staff_wages.health')
  205. personal_tax = self.env.ref('staff_wages.personal_tax')
  206. other_money_order = self.env['other.money.order'].create({
  207. 'state': 'draft',
  208. 'type': 'other_pay',
  209. 'date': self.date,
  210. 'bank_id': self.payment.id,
  211. 'ref': '%s,%s' % (self._name, self.id)
  212. })
  213. for line in self.line_ids:
  214. self.env['other.money.order.line'].create({
  215. 'other_money_id': other_money_order.id,
  216. 'amount': line.all_wage,
  217. 'category_id': StaffWages and StaffWages.id,
  218. 'auxiliary_id': line.name.auxiliary_id.id,
  219. })
  220. if self.total_endowment:
  221. self.env['other.money.order.line'].create({
  222. 'other_money_id': other_money_order.id,
  223. 'amount': -1 * self.total_endowment,
  224. 'category_id': endowment and endowment.id
  225. })
  226. if self.total_unemployment:
  227. self.env['other.money.order.line'].create({
  228. 'other_money_id': other_money_order.id,
  229. 'amount': -1 * self.total_unemployment,
  230. 'category_id': unemployment and unemployment.id
  231. })
  232. if self.total_housing_fund:
  233. self.env['other.money.order.line'].create({
  234. 'other_money_id': other_money_order.id,
  235. 'amount': -1 * self.total_housing_fund,
  236. 'category_id': housing_fund and housing_fund.id
  237. })
  238. if self.total_health:
  239. self.env['other.money.order.line'].create({
  240. 'other_money_id': other_money_order.id,
  241. 'amount': -1 * self.total_health,
  242. 'category_id': health and health.id
  243. })
  244. if self.total_personal_tax:
  245. self.env['other.money.order.line'].create({
  246. 'other_money_id': other_money_order.id,
  247. 'amount': -1 * self.total_personal_tax,
  248. 'category_id': personal_tax and personal_tax.id
  249. })
  250. self.wage_other_money_order = other_money_order
  251. def create_other_pay_housing_fund(self):
  252. """
  253. 生成住房公积金的其他支出单
  254. 审核时生成凭证:
  255. 借:应付职工薪酬-公积金 公司公积金
  256. 其他应付款-代扣公积金 个人公积金
  257. 贷: 付款方式对应的科目
  258. :return: 其他支出单
  259. """
  260. self.ensure_one()
  261. housing_co = self.env.ref('staff_wages.categ_housing_fund_co') # 公司缴纳住房公积金类别
  262. housing = self.env.ref('staff_wages.housing_fund') # 个人缴纳住房公积金类别
  263. order = self.env['other.money.order'].create({
  264. 'state': 'draft',
  265. 'type': 'other_pay',
  266. 'date': self.date,
  267. 'bank_id': self.payment.id,
  268. 'ref': '%s,%s' % (self._name, self.id)
  269. })
  270. self.create_other_order_line(order, housing_co, self.total_housing_fund_co)
  271. self.create_other_order_line(order, housing, self.total_housing_fund)
  272. self.housing_other_money_order = order
  273. def create_other_pay_social_security(self):
  274. """
  275. 生成社保的其他支出单
  276. 审核时生成凭证:
  277. 借: 5个公司的,3个个人的
  278. 贷: 付款方式对应的科目
  279. :return: 其他支出单
  280. """
  281. for record in self:
  282. other_order = self.env['other.money.order'].create({
  283. 'type': 'other_pay',
  284. 'date': record.date,
  285. 'bank_id': record.payment.id,
  286. 'state': 'draft',
  287. 'ref': '%s,%s' % (record._name, record.id)
  288. })
  289. endowment_co = self.env.ref('staff_wages.categ_endowment_co') # 公司缴纳养老类别
  290. health_co = self.env.ref('staff_wages.categ_health_co') # 公司缴纳医疗类别
  291. unemployment_co = self.env.ref('staff_wages.categ_unemployment_co') # 公司缴纳失业类别
  292. maternity = self.env.ref('staff_wages.categ_maternity') # 公司缴纳生育类别
  293. injury = self.env.ref('staff_wages.categ_injury') # 公司缴纳工伤类别
  294. endowment = self.env.ref('staff_wages.endowment') # 个人缴纳养老类别
  295. health = self.env.ref('staff_wages.health') # 个人缴纳医疗类别
  296. unemployment = self.env.ref('staff_wages.unemployment') # 个人缴纳失业类别
  297. record.create_other_order_line(other_order, endowment_co, record.total_endowment_co)
  298. record.create_other_order_line(other_order, health_co, record.total_health_co)
  299. record.create_other_order_line(other_order, unemployment_co, record.total_unemployment_co)
  300. record.create_other_order_line(other_order, maternity, record.total_maternity)
  301. record.create_other_order_line(other_order, injury, record.total_injury)
  302. # 个人的
  303. record.create_other_order_line(other_order, endowment, record.total_endowment)
  304. record.create_other_order_line(other_order, health, record.total_health)
  305. record.create_other_order_line(other_order, unemployment, record.total_unemployment)
  306. record.social_security_other_money_order = other_order
  307. def create_other_order_line(self, order, category_id, amount):
  308. """
  309. 生成其他支出单明细行
  310. :param order: 其他支出单
  311. :param category_id: 类别
  312. :param amount: 金额
  313. :return:
  314. """
  315. if amount:
  316. self.env['other.money.order.line'].create({
  317. 'other_money_id': order.id,
  318. 'amount': amount,
  319. 'category_id': category_id and category_id.id
  320. })
  321. def staff_wages_draft(self):
  322. """撤销确认"""
  323. for record in self:
  324. if self.state == 'draft':
  325. raise UserError(u'请不要重复撤销')
  326. if record.housing_other_money_order:
  327. housing_other_money_order, record.housing_other_money_order = record.housing_other_money_order, False
  328. if housing_other_money_order.state == 'done':
  329. housing_other_money_order.other_money_draft()
  330. # 草稿状态的其他付款单,清除 ref 后删除
  331. housing_other_money_order.write({'ref': False})
  332. housing_other_money_order.unlink()
  333. if record.social_security_other_money_order:
  334. social_security_other_money_order, record.social_security_other_money_order = record.social_security_other_money_order, False
  335. if social_security_other_money_order.state == 'done':
  336. social_security_other_money_order.other_money_draft()
  337. social_security_other_money_order.write({'ref': False})
  338. social_security_other_money_order.unlink()
  339. if record.wage_other_money_order:
  340. wage_other_money_order, record.wage_other_money_order = record.wage_other_money_order, False
  341. if wage_other_money_order.state == 'done':
  342. wage_other_money_order.other_money_draft()
  343. wage_other_money_order.write({'ref': False})
  344. wage_other_money_order.unlink()
  345. record.state = 'draft'
  346. def unlink(self):
  347. for record in self:
  348. # 先解除凭证和工资单关系,再将它们删除
  349. voucher, record.voucher_id = record.voucher_id, False
  350. if voucher:
  351. voucher, record.voucher_id = record.voucher_id, False
  352. if voucher.state == 'done':
  353. voucher.voucher_draft()
  354. voucher.unlink()
  355. if change_voucher:
  356. change_voucher, record.change_voucher_id = record.change_voucher_id, False
  357. if change_voucher.state == 'done':
  358. change_voucher.voucher_draft()
  359. change_voucher.unlink()
  360. return super(StaffWages, self).unlink()
  361. class WagesLine(models.Model):
  362. _name = 'wages.line'
  363. _description = '工资明细行'
  364. order_id = fields.Many2one('staff.wages', '工资单', required=True, ondelete='cascade')
  365. name = fields.Many2one('staff', '员工', required=True)
  366. basic_wage = fields.Float('基本工资', digits=(12, 2), required=True)
  367. basic_date = fields.Integer('月计薪天数', required=True, default=21.75)
  368. date_number = fields.Integer('出勤天数', required=True)
  369. add_hour = fields.Integer('加班小时数', default=0)
  370. add_wage = fields.Float('加班工资', digits=(12, 2), default=0)
  371. other_wage = fields.Float('补助', digits=(12, 2), default=0)
  372. deduction = fields.Float('扣款', digits=(12, 2), default=0)
  373. wage = fields.Float('出勤工资金额', digits=(12, 2), compute='_all_wage_value', store=True)
  374. all_wage = fields.Float('应发工资', digits=(12, 2), compute='_all_wage_value', store=True)
  375. endowment = fields.Float('个人养老保险', digits=(12, 2), default=0)
  376. health = fields.Float('个人医疗保险', digits=(12, 2), default=0)
  377. unemployment = fields.Float('个人失业保险', digits=(12, 2), default=0)
  378. housing_fund = fields.Float('个人住房公积金', digits=(12, 2), default=0)
  379. endowment_co = fields.Float('公司养老保险', digits=(12, 2), default=0, help='公司承担的养老保险')
  380. health_co = fields.Float('公司医疗保险', digits=(12, 2), default=0, help='公司承担的医疗保险')
  381. unemployment_co = fields.Float('公司失业保险', digits=(12, 2), default=0, help='公司承担的失业保险')
  382. housing_fund_co = fields.Float('公司住房公积金', digits=(12, 2), default=0, help='公司承担的住房公积金')
  383. injury = fields.Float('公司工伤保险', digits=(12, 2), default=0, help='公司承担的工伤保险')
  384. maternity = fields.Float('公司生育保险', digits=(12, 2), default=0, help='公司承担的生育保险')
  385. # 因为个人所得税的计算不确定,暂改为手动录入。
  386. # personal_tax = fields.Float('个人所得税', digits=(12, 2), compute='_personal_tax_value', store=True)
  387. personal_tax = fields.Float('个人所得税', digits=(12, 2))
  388. amount_wage = fields.Float('实发工资', digits=(12, 2), compute='_amount_wage_value', store=True)
  389. wages_change = fields.Selection(change, '记工类型')
  390. _sql_constraints = [
  391. ('staff_uniq', 'unique(order_id, name)', '同一个工资单不能出现重名员工工资行')
  392. ]
  393. @api.onchange('date_number', 'basic_wage', 'basic_date')
  394. def change_wage_addhour(self):
  395. for record in self:
  396. if record.date_number > 31 or record.basic_date > 31:
  397. raise UserError('一个月不可能超过31天')
  398. if record.date_number >= record.basic_date:
  399. record.add_hour = 8 * (record.date_number - record.basic_date)
  400. record.date_number = record.basic_date
  401. record.add_wage = round(
  402. 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
  403. @api.onchange('add_hour')
  404. def change_add_wage(self):
  405. for record in self:
  406. if record.add_hour:
  407. record.add_wage = round(
  408. 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
  409. @api.onchange('name')
  410. def change_social_security(self):
  411. """选择员工后,自动带出五险一金"""
  412. for record in self:
  413. social_security = self.env['staff.contract'].search(
  414. [('staff_id', '=', record.name.id)], limit=1)
  415. if social_security:
  416. record.basic_wage = social_security.basic_wage
  417. record.endowment = social_security.endowment
  418. record.health = social_security.health
  419. record.unemployment = social_security.unemployment
  420. record.housing_fund = social_security.housing_fund
  421. record.endowment_co = social_security.endowment_co
  422. record.health_co = social_security.health_co
  423. record.unemployment_co = social_security.unemployment_co
  424. record.housing_fund_co = social_security.housing_fund_co
  425. record.injury = social_security.injury
  426. record.maternity = social_security.maternity
  427. @api.depends('date_number', 'basic_date', 'add_wage', 'other_wage', 'basic_wage', 'deduction')
  428. def _all_wage_value(self):
  429. for record in self:
  430. if record.date_number >= record.basic_date:
  431. record.wage = record.basic_wage
  432. else:
  433. record.wage = round(
  434. (record.date_number / record.basic_date or 1) * record.basic_wage, 2)
  435. record.all_wage = record.wage + record.add_wage + record.other_wage - record.deduction
  436. @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund')
  437. def _personal_tax_value(self):
  438. for record in self:
  439. total = record.all_wage - record.endowment - \
  440. record.health - record.unemployment - record.housing_fund
  441. amount = total - 3500
  442. if amount > 80000:
  443. record.personal_tax = round(amount * 0.45 - 13505, 2)
  444. elif amount > 55000:
  445. record.personal_tax = round(amount * 0.35 - 5505, 2)
  446. elif amount > 35000:
  447. record.personal_tax = round(amount * 0.3 - 2755, 2)
  448. elif amount > 9000:
  449. record.personal_tax = round(amount * 0.25 - 1005, 2)
  450. elif amount > 4500:
  451. record.personal_tax = round(amount * 0.2 - 555, 2)
  452. elif amount > 1500:
  453. record.personal_tax = round(amount * 0.1 - 105, 2)
  454. elif amount >= 0:
  455. record.personal_tax = round(amount * 0.03, 2)
  456. else:
  457. record.personal_tax = 0
  458. @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund', 'personal_tax')
  459. def _amount_wage_value(self):
  460. for record in self:
  461. record.amount_wage = record.all_wage - record.endowment - record.health - \
  462. record.unemployment - record.housing_fund - record.personal_tax
  463. class AddWagesChange(models.Model):
  464. _inherit = 'staff.contract'
  465. wages_change = fields.Selection(change, '记工类型')
上海开阖软件有限公司 沪ICP备12045867号-1