GoodERP
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

482 lines
24KB

  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. })
  212. for line in self.line_ids:
  213. self.env['other.money.order.line'].create({
  214. 'other_money_id': other_money_order.id,
  215. 'amount': line.all_wage,
  216. 'category_id': StaffWages and StaffWages.id,
  217. })
  218. if self.total_endowment:
  219. self.env['other.money.order.line'].create({
  220. 'other_money_id': other_money_order.id,
  221. 'amount': -1 * self.total_endowment,
  222. 'category_id': endowment and endowment.id
  223. })
  224. if self.total_unemployment:
  225. self.env['other.money.order.line'].create({
  226. 'other_money_id': other_money_order.id,
  227. 'amount': -1 * self.total_unemployment,
  228. 'category_id': unemployment and unemployment.id
  229. })
  230. if self.total_housing_fund:
  231. self.env['other.money.order.line'].create({
  232. 'other_money_id': other_money_order.id,
  233. 'amount': -1 * self.total_housing_fund,
  234. 'category_id': housing_fund and housing_fund.id
  235. })
  236. if self.total_health:
  237. self.env['other.money.order.line'].create({
  238. 'other_money_id': other_money_order.id,
  239. 'amount': -1 * self.total_health,
  240. 'category_id': health and health.id
  241. })
  242. if self.total_personal_tax:
  243. self.env['other.money.order.line'].create({
  244. 'other_money_id': other_money_order.id,
  245. 'amount': -1 * self.total_personal_tax,
  246. 'category_id': personal_tax and personal_tax.id
  247. })
  248. self.wage_other_money_order = other_money_order
  249. def create_other_pay_housing_fund(self):
  250. """
  251. 生成住房公积金的其他支出单
  252. 审核时生成凭证:
  253. 借:应付职工薪酬-公积金 公司公积金
  254. 其他应付款-代扣公积金 个人公积金
  255. 贷: 付款方式对应的科目
  256. :return: 其他支出单
  257. """
  258. self.ensure_one()
  259. housing_co = self.env.ref('staff_wages.categ_housing_fund_co') # 公司缴纳住房公积金类别
  260. housing = self.env.ref('staff_wages.housing_fund') # 个人缴纳住房公积金类别
  261. order = self.env['other.money.order'].create({
  262. 'state': 'draft',
  263. 'type': 'other_pay',
  264. 'date': self.date,
  265. 'bank_id': self.payment.id,
  266. })
  267. self.create_other_order_line(order, housing_co, self.total_housing_fund_co)
  268. self.create_other_order_line(order, housing, self.total_housing_fund)
  269. self.housing_other_money_order = order
  270. def create_other_pay_social_security(self):
  271. """
  272. 生成社保的其他支出单
  273. 审核时生成凭证:
  274. 借: 5个公司的,3个个人的
  275. 贷: 付款方式对应的科目
  276. :return: 其他支出单
  277. """
  278. for record in self:
  279. other_order = self.env['other.money.order'].create({
  280. 'type': 'other_pay',
  281. 'date': record.date,
  282. 'bank_id': record.payment.id,
  283. 'state': 'draft',
  284. })
  285. endowment_co = self.env.ref('staff_wages.categ_endowment_co') # 公司缴纳养老类别
  286. health_co = self.env.ref('staff_wages.categ_health_co') # 公司缴纳医疗类别
  287. unemployment_co = self.env.ref('staff_wages.categ_unemployment_co') # 公司缴纳失业类别
  288. maternity = self.env.ref('staff_wages.categ_maternity') # 公司缴纳生育类别
  289. injury = self.env.ref('staff_wages.categ_injury') # 公司缴纳工伤类别
  290. endowment = self.env.ref('staff_wages.endowment') # 个人缴纳养老类别
  291. health = self.env.ref('staff_wages.health') # 个人缴纳医疗类别
  292. unemployment = self.env.ref('staff_wages.unemployment') # 个人缴纳失业类别
  293. record.create_other_order_line(other_order, endowment_co, record.total_endowment_co)
  294. record.create_other_order_line(other_order, health_co, record.total_health_co)
  295. record.create_other_order_line(other_order, unemployment_co, record.total_unemployment_co)
  296. record.create_other_order_line(other_order, maternity, record.total_maternity)
  297. record.create_other_order_line(other_order, injury, record.total_injury)
  298. # 个人的
  299. record.create_other_order_line(other_order, endowment, record.total_endowment)
  300. record.create_other_order_line(other_order, health, record.total_health)
  301. record.create_other_order_line(other_order, unemployment, record.total_unemployment)
  302. record.social_security_other_money_order = other_order
  303. def create_other_order_line(self, order, category_id, amount):
  304. """
  305. 生成其他支出单明细行
  306. :param order: 其他支出单
  307. :param category_id: 类别
  308. :param amount: 金额
  309. :return:
  310. """
  311. if amount:
  312. self.env['other.money.order.line'].create({
  313. 'other_money_id': order.id,
  314. 'amount': amount,
  315. 'category_id': category_id and category_id.id
  316. })
  317. def staff_wages_draft(self):
  318. """撤销确认"""
  319. for record in self:
  320. if self.state == 'draft':
  321. raise UserError(u'请不要重复撤销')
  322. if record.wage_other_money_order:
  323. other_money_order, record.wage_other_money_order = record.wage_other_money_order, False
  324. if other_money_order.state == 'done':
  325. other_money_order.other_money_draft()
  326. other_money_order.unlink()
  327. record.state = 'draft'
  328. def unlink(self):
  329. for record in self:
  330. # 先解除凭证和工资单关系,再将它们删除
  331. voucher, record.voucher_id = record.voucher_id, False
  332. change_voucher, record.change_voucher_id = record.change_voucher_id, False
  333. if voucher.state == 'done':
  334. voucher.voucher_draft()
  335. if change_voucher.state == 'done':
  336. change_voucher.voucher_draft()
  337. voucher.unlink()
  338. change_voucher.unlink()
  339. return super(StaffWages, self).unlink()
  340. class WagesLine(models.Model):
  341. _name = 'wages.line'
  342. _description = '工资明细行'
  343. order_id = fields.Many2one('staff.wages', '工资单', required=True, ondelete='cascade')
  344. name = fields.Many2one('staff', '员工', required=True)
  345. basic_wage = fields.Float('基本工资', digits=(12, 2), required=True)
  346. basic_date = fields.Integer('月计薪天数', required=True, default=21.75)
  347. date_number = fields.Integer('出勤天数', required=True)
  348. add_hour = fields.Integer('加班小时数', default=0)
  349. add_wage = fields.Float('加班工资', digits=(12, 2), default=0)
  350. other_wage = fields.Float('补助', digits=(12, 2), default=0)
  351. deduction = fields.Float('扣款', digits=(12, 2), default=0)
  352. wage = fields.Float('出勤工资金额', digits=(12, 2), compute='_all_wage_value', store=True)
  353. all_wage = fields.Float('应发工资', digits=(12, 2), compute='_all_wage_value', store=True)
  354. endowment = fields.Float('个人养老保险', digits=(12, 2), default=0)
  355. health = fields.Float('个人医疗保险', digits=(12, 2), default=0)
  356. unemployment = fields.Float('个人失业保险', digits=(12, 2), default=0)
  357. housing_fund = fields.Float('个人住房公积金', digits=(12, 2), default=0)
  358. endowment_co = fields.Float('公司养老保险', digits=(12, 2), default=0, help='公司承担的养老保险')
  359. health_co = fields.Float('公司医疗保险', digits=(12, 2), default=0, help='公司承担的医疗保险')
  360. unemployment_co = fields.Float('公司失业保险', digits=(12, 2), default=0, help='公司承担的失业保险')
  361. housing_fund_co = fields.Float('公司住房公积金', digits=(12, 2), default=0, help='公司承担的住房公积金')
  362. injury = fields.Float('公司工伤保险', digits=(12, 2), default=0, help='公司承担的工伤保险')
  363. maternity = fields.Float('公司生育保险', digits=(12, 2), default=0, help='公司承担的生育保险')
  364. # 因为个人所得税的计算不确定,暂改为手动录入。
  365. # personal_tax = fields.Float('个人所得税', digits=(12, 2), compute='_personal_tax_value', store=True)
  366. personal_tax = fields.Float('个人所得税', digits=(12, 2))
  367. amount_wage = fields.Float('实发工资', digits=(12, 2), compute='_amount_wage_value', store=True)
  368. wages_change = fields.Selection(change, '记工类型')
  369. _sql_constraints = [
  370. ('staff_uniq', 'unique(order_id, name)', '同一个工资单不能出现重名员工工资行')
  371. ]
  372. @api.onchange('date_number', 'basic_wage', 'basic_date')
  373. def change_wage_addhour(self):
  374. for record in self:
  375. if record.date_number > 31 or record.basic_date > 31:
  376. raise UserError('一个月不可能超过31天')
  377. if record.date_number >= record.basic_date:
  378. record.add_hour = 8 * (record.date_number - record.basic_date)
  379. record.date_number = record.basic_date
  380. record.add_wage = round(
  381. 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
  382. @api.onchange('add_hour')
  383. def change_add_wage(self):
  384. for record in self:
  385. if record.add_hour:
  386. record.add_wage = round(
  387. 2 * (record.add_hour / 8) * (record.basic_wage / (record.basic_date or 1)), 2)
  388. @api.onchange('name')
  389. def change_social_security(self):
  390. """选择员工后,自动带出五险一金"""
  391. for record in self:
  392. social_security = self.env['staff.contract'].search(
  393. [('staff_id', '=', record.name.id)], limit=1)
  394. if social_security:
  395. record.basic_wage = social_security.basic_wage
  396. record.endowment = social_security.endowment
  397. record.health = social_security.health
  398. record.unemployment = social_security.unemployment
  399. record.housing_fund = social_security.housing_fund
  400. record.endowment_co = social_security.endowment_co
  401. record.health_co = social_security.health_co
  402. record.unemployment_co = social_security.unemployment_co
  403. record.housing_fund_co = social_security.housing_fund_co
  404. record.injury = social_security.injury
  405. record.maternity = social_security.maternity
  406. @api.depends('date_number', 'basic_date', 'add_wage', 'other_wage', 'basic_wage', 'deduction')
  407. def _all_wage_value(self):
  408. for record in self:
  409. if record.date_number >= record.basic_date:
  410. record.wage = record.basic_wage
  411. else:
  412. record.wage = round(
  413. (record.date_number / record.basic_date or 1) * record.basic_wage, 2)
  414. record.all_wage = record.wage + record.add_wage + record.other_wage - record.deduction
  415. @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund')
  416. def _personal_tax_value(self):
  417. for record in self:
  418. total = record.all_wage - record.endowment - \
  419. record.health - record.unemployment - record.housing_fund
  420. amount = total - 3500
  421. if amount > 80000:
  422. record.personal_tax = round(amount * 0.45 - 13505, 2)
  423. elif amount > 55000:
  424. record.personal_tax = round(amount * 0.35 - 5505, 2)
  425. elif amount > 35000:
  426. record.personal_tax = round(amount * 0.3 - 2755, 2)
  427. elif amount > 9000:
  428. record.personal_tax = round(amount * 0.25 - 1005, 2)
  429. elif amount > 4500:
  430. record.personal_tax = round(amount * 0.2 - 555, 2)
  431. elif amount > 1500:
  432. record.personal_tax = round(amount * 0.1 - 105, 2)
  433. elif amount >= 0:
  434. record.personal_tax = round(amount * 0.03, 2)
  435. else:
  436. record.personal_tax = 0
  437. @api.depends('all_wage', 'endowment', 'health', 'unemployment', 'housing_fund', 'personal_tax')
  438. def _amount_wage_value(self):
  439. for record in self:
  440. record.amount_wage = record.all_wage - record.endowment - record.health - \
  441. record.unemployment - record.housing_fund - record.personal_tax
  442. class AddWagesChange(models.Model):
  443. _inherit = 'staff.contract'
  444. wages_change = fields.Selection(change, '记工类型')
上海开阖软件有限公司 沪ICP备12045867号-1