GoodERP
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
10KB

  1. # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
  2. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
  3. from odoo import api, fields, models, _
  4. from odoo.exceptions import UserError
  5. class HrExpense(models.Model):
  6. _name = 'hr.expense'
  7. _inherit = ['mail.thread', 'mail.activity.mixin']
  8. _description = '报销'
  9. name = fields.Char('单据编号', copy=False)
  10. date = fields.Date(string='报销日期', default=fields.Date.today, copy=False, required=True, help='报销发生日期')
  11. staff = fields.Many2one('staff', '报销员工', required=True, help='选择员工')
  12. invoice_all_total = fields.Float(string='费用金额合计', store=True, readonly=True, compute='_compute_invoice_all_total', tracking=True, digits='Amount')
  13. state = fields.Selection([('draft', '草稿'), ('confirm', '已提交'), ('done', '已支付')], '状态', default='draft', tracking=True, store=True, compute='_state_to_done')
  14. line_ids = fields.One2many('hr.expense.line', 'order_id', '明细发票行', copy=False)
  15. note = fields.Text('备注')
  16. bank_account_id = fields.Many2one('bank.account', '账号', ondelete='restrict', help='付给个人')
  17. other_money_order = fields.Many2one('other.money.order', '对应其他付款单', readonly=True, ondelete='restrict', copy=False)
  18. @api.depends('other_money_order.state')
  19. def _state_to_done(self):
  20. self.state = self.state or 'draft'
  21. if self.other_money_order and self.other_money_order.state == 'done':
  22. self.state = 'done'
  23. if self.other_money_order and self.other_money_order.state == 'draft':
  24. self.state = 'confirm'
  25. @api.depends('line_ids')
  26. def _compute_invoice_all_total(self):
  27. for bill in self:
  28. total = sum(i.invoice_total for i in bill.line_ids)
  29. bill.invoice_all_total = total
  30. def check_consistency(self):
  31. for bill in self:
  32. for line in bill.line_ids:
  33. if line.staff.id != bill.staff.id:
  34. raise UserError('费用明细必须是同一人!')
  35. @api.model_create_multi
  36. def create(self, vals_list):
  37. res = super().create(vals_list)
  38. res.check_consistency()
  39. return res
  40. def write(self, vals):
  41. res = super().write(vals)
  42. self.check_consistency()
  43. return res
  44. def unlink(self):
  45. for record in self:
  46. if record.state != 'draft':
  47. raise UserError('只能删除草稿状态的费用报销单')
  48. # 如果已关联其他付款单,检查其状态
  49. if record.other_money_order:
  50. if record.other_money_order.state == 'done':
  51. raise UserError(
  52. '报销单 %s 已关联已确认的其他付款单(%s),\n'
  53. '不能删除。请先作废其他付款单。'
  54. % (record.name, record.other_money_order.name)
  55. )
  56. else:
  57. # 草稿状态的其他付款单,清除 ref 后删除
  58. record.other_money_order.write({'ref': False})
  59. record.other_money_order.unlink()
  60. return super().unlink()
  61. def hr_expense_confirm(self):
  62. for bill in self:
  63. if bill.state == 'confirm':
  64. raise UserError('请不要重复确认')
  65. bill.to_other_money_order()
  66. bill.state = 'confirm'
  67. # 报销单审核后,自动将报销行状态改为 confirm
  68. bill.line_ids.write({'state': 'confirm'})
  69. def hr_expense_draft(self):
  70. for bill in self:
  71. if bill.state == 'draft':
  72. raise UserError('请不要重复撤销 %s' % bill.name)
  73. if bill.other_money_order:
  74. other_money_order = bill.other_money_order
  75. bill.other_money_order = False
  76. if other_money_order.state == 'done':
  77. other_money_order.other_money_draft()
  78. # 弃审时先清空 ref,再删除其他付款单
  79. other_money_order.write({'ref': False})
  80. other_money_order.unlink()
  81. bill.state = 'draft'
  82. def to_other_money_order(self):
  83. for bill in self:
  84. if not bill.staff.partner_id:
  85. raise UserError(u'员工 %s 未关联往来单位,请先在员工档案中设置往来单位!' % bill.staff.name)
  86. other_money_order = self.env['other.money.order'].create({
  87. 'state': 'draft',
  88. 'partner_id': bill.staff.partner_id.id,
  89. 'date': bill.date,
  90. 'name': '/',
  91. 'type': 'other_pay',
  92. 'bank_id': bill.bank_account_id.id,
  93. 'note': bill.note,
  94. 'ref': '%s,%s' % (bill._name, bill.id)
  95. })
  96. bill.write({'other_money_order': other_money_order.id})
  97. for line in bill.line_ids:
  98. if line.invoice_type != 'zy':
  99. invoice_tax = 0
  100. invoice_amount = line.invoice_amount + line.invoice_tax
  101. else:
  102. invoice_tax = line.invoice_tax
  103. invoice_amount = line.invoice_amount
  104. bill.env['other.money.order.line'].create({
  105. 'other_money_id': other_money_order.id,
  106. 'amount': invoice_amount,
  107. 'auxiliary_id': line.staff.auxiliary_id.id,
  108. 'tax_rate': line.invoice_amount and invoice_tax / line.invoice_amount * 100 or 0,
  109. 'tax_amount': invoice_tax,
  110. 'category_id': line.category_id and line.category_id.id or False,
  111. })
  112. class HrExpenseLine(models.Model):
  113. _name = 'hr.expense.line'
  114. _inherit = ['mail.thread']
  115. _description = '报销行'
  116. _order = 'date'
  117. name = fields.Char('单据编号', index=True, copy=False, help='报销单的唯一编号')
  118. staff = fields.Many2one('staff', string='报销员工', related='order_id.staff', store=True, readonly=True, help='自动关联上级报销单的报销员工')
  119. invoice_type = fields.Selection([('pt', '增值税普通发票'), ('zy', '增值税专用发票'), ('dz', '电子普通发票')], '发票类别')
  120. invoice_code = fields.Char('发票代码', copy=False)
  121. invoice_name = fields.Char('发票号码', copy=False)
  122. invoice_amount = fields.Float(string='发票金额', digits='Amount', required=True, help='如是增值税发票请填不含税金额')
  123. invoice_tax = fields.Float(string='发票税额', digits='Amount', help='如是增值税发票请填不含税金额')
  124. invoice_total = fields.Float(string='发票金额合计', store=True, readonly=True, compute='_compute_cost_total', digits='Amount')
  125. invoice_heck_code = fields.Char('发票校验码', copy=False)
  126. invoice_date = fields.Char('发票日期', copy=False)
  127. note = fields.Text('备注')
  128. state = fields.Selection([('draft', '草稿'), ('confirm', '已提交'), ('done', '已支付')], '状态', default='draft', compute='_compute_state', store=True, copy=False)
  129. category_id = fields.Many2one('core.category', '类别', ondelete='restrict', required=True, help='类型:运费、咨询费等', domain=[('type', '=', 'other_pay')])
  130. date = fields.Date(string='费用日期', default=fields.Date.context_today, copy=False, required=True, help='费用发生日期')
  131. order_id = fields.Many2one('hr.expense', '报销单号', index=True, copy=False, readonly=True)
  132. is_pay = fields.Boolean(string='已付款', store=True, compute='_compute_is_pay', readonly=True, copy=False)
  133. _sql_constraints = [('unique_invoice_code_name', 'unique (invoice_code, invoice_name)', '发票代码+发票号码不能相同!')]
  134. attachment_number = fields.Integer(compute='_compute_attachment_number', string='附件号')
  135. @api.depends('invoice_amount', 'invoice_tax')
  136. def _compute_cost_total(self):
  137. for bill in self:
  138. bill.invoice_total = bill.invoice_amount + bill.invoice_tax
  139. @api.depends('order_id.state')
  140. def _compute_state(self):
  141. for line in self:
  142. if line.order_id:
  143. line.state = line.order_id.state
  144. else:
  145. line.state = 'draft'
  146. @api.model
  147. def saomiaofapiao(self, model_name, barcode, order_id):
  148. barcode = barcode.replace(',', ',').replace('。', '.')
  149. code = barcode.split(',')
  150. invoice_heck_code = ''
  151. if len(code) < 5:
  152. raise UserError('请确认扫描是否正确%s' % code)
  153. if code[0] == '01':
  154. if code[1] == '10':
  155. invoice_type = 'dz'
  156. invoice_heck_code = code[6]
  157. if code[1] == '01':
  158. invoice_type = 'zy'
  159. if code[1] == '04':
  160. invoice_type = 'pt'
  161. invoice_heck_code = code[6]
  162. invoice_code = code[2]
  163. invoice_name = code[3]
  164. invoice_amount = round(float(code[4]), 2)
  165. invoice_date = code[5]
  166. invoice_tax = 0
  167. self.browse(order_id).write({
  168. 'invoice_type': invoice_type, 'invoice_code': invoice_code, 'invoice_name': invoice_name,
  169. 'invoice_amount': invoice_amount, 'invoice_tax': invoice_tax,
  170. 'invoice_heck_code': invoice_heck_code, 'invoice_date': invoice_date,
  171. })
  172. def action_get_attachment_view(self):
  173. res = self.env.ref('base.action_attachment')
  174. res['domain'] = [('res_model', '=', 'hr.expense.line'), ('res_id', 'in', self.ids)]
  175. res['context'] = {'default_res_model': 'hr.expense.line', 'default_res_id': self.id}
  176. return res
  177. def _compute_attachment_number(self):
  178. attachment_data = self.env['ir.attachment'].read_group(
  179. [('res_model', '=', 'hr.expense.line'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id'])
  180. attachment = dict((data['res_id'], data['res_id_count']) for data in attachment_data)
  181. for bill in self:
  182. bill.attachment_number = attachment.get(bill.id, 0)
  183. def unlink(self):
  184. for bill in self:
  185. # 只有关联的报销单已提交或已支付时才限制删除
  186. if bill.order_id and bill.order_id.state in ['confirm', 'done']:
  187. raise UserError('关联的报销单已%s,不能删除费用发票' % bill.order_id.state)
  188. return super().unlink()
  189. @api.depends('order_id.state')
  190. def _compute_is_pay(self):
  191. for bill in self:
  192. if bill.order_id and bill.order_id.state == 'done':
  193. bill.is_pay = True
  194. else:
  195. bill.is_pay = False
上海开阖软件有限公司 沪ICP备12045867号-1