|
- # Copyright 2016 上海开阖软件有限公司 (http://www.osbzr.com)
- # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
-
- from odoo import api, fields, models, _
- from odoo.exceptions import UserError
- class HrExpense(models.Model):
- _name = 'hr.expense'
- _inherit = ['mail.thread', 'mail.activity.mixin']
- _description = '报销'
-
- name = fields.Char('单据编号', copy=False)
- date = fields.Date(string='报销日期', default=fields.Date.today, copy=False, required=True, help='报销发生日期')
- staff = fields.Many2one('staff', '报销员工', required=True, help='选择员工')
- invoice_all_total = fields.Float(string='费用金额合计', store=True, readonly=True, compute='_compute_invoice_all_total', tracking=True, digits='Amount')
- state = fields.Selection([('draft', '草稿'), ('confirm', '已提交'), ('done', '已支付')], '状态', default='draft', tracking=True, store=True, compute='_state_to_done')
- line_ids = fields.One2many('hr.expense.line', 'order_id', '明细发票行', copy=False)
- note = fields.Text('备注')
- bank_account_id = fields.Many2one('bank.account', '账号', ondelete='restrict', help='付给个人')
- other_money_order = fields.Many2one('other.money.order', '对应其他付款单', readonly=True, ondelete='restrict', copy=False)
-
- @api.depends('other_money_order.state')
- def _state_to_done(self):
- self.state = self.state or 'draft'
- if self.other_money_order and self.other_money_order.state == 'done':
- self.state = 'done'
- if self.other_money_order and self.other_money_order.state == 'draft':
- self.state = 'confirm'
-
- @api.depends('line_ids')
- def _compute_invoice_all_total(self):
- for bill in self:
- total = sum(i.invoice_total for i in bill.line_ids)
- bill.invoice_all_total = total
-
- def check_consistency(self):
- for bill in self:
- for line in bill.line_ids:
- if line.staff.id != bill.staff.id:
- raise UserError('费用明细必须是同一人!')
-
- @api.model_create_multi
- def create(self, vals_list):
- res = super().create(vals_list)
- res.check_consistency()
- return res
-
- def write(self, vals):
- res = super().write(vals)
- self.check_consistency()
- return res
-
- def unlink(self):
- for record in self:
- if record.state != 'draft':
- raise UserError('只能删除草稿状态的费用报销单')
- # 如果已关联其他付款单,检查其状态
- if record.other_money_order:
- if record.other_money_order.state == 'done':
- raise UserError(
- '报销单 %s 已关联已确认的其他付款单(%s),\n'
- '不能删除。请先作废其他付款单。'
- % (record.name, record.other_money_order.name)
- )
- else:
- # 草稿状态的其他付款单,清除 ref 后删除
- record.other_money_order.write({'ref': False})
- record.other_money_order.unlink()
- return super().unlink()
- def hr_expense_confirm(self):
- for bill in self:
- if bill.state == 'confirm':
- raise UserError('请不要重复确认')
- bill.to_other_money_order()
- bill.state = 'confirm'
- # 报销单审核后,自动将报销行状态改为 confirm
- bill.line_ids.write({'state': 'confirm'})
-
- def hr_expense_draft(self):
- for bill in self:
- if bill.state == 'draft':
- raise UserError('请不要重复撤销 %s' % bill.name)
- if bill.other_money_order:
- other_money_order = bill.other_money_order
- bill.other_money_order = False
- if other_money_order.state == 'done':
- other_money_order.other_money_draft()
- # 弃审时先清空 ref,再删除其他付款单
- other_money_order.write({'ref': False})
- other_money_order.unlink()
- bill.state = 'draft'
- def to_other_money_order(self):
- for bill in self:
- if not bill.staff.partner_id:
- raise UserError(u'员工 %s 未关联往来单位,请先在员工档案中设置往来单位!' % bill.staff.name)
- other_money_order = self.env['other.money.order'].create({
- 'state': 'draft',
- 'partner_id': bill.staff.partner_id.id,
- 'date': bill.date,
- 'name': '/',
- 'type': 'other_pay',
- 'bank_id': bill.bank_account_id.id,
- 'note': bill.note,
- 'ref': '%s,%s' % (bill._name, bill.id)
- })
- bill.write({'other_money_order': other_money_order.id})
- for line in bill.line_ids:
- if line.invoice_type != 'zy':
- invoice_tax = 0
- invoice_amount = line.invoice_amount + line.invoice_tax
- else:
- invoice_tax = line.invoice_tax
- invoice_amount = line.invoice_amount
- bill.env['other.money.order.line'].create({
- 'other_money_id': other_money_order.id,
- 'amount': invoice_amount,
- 'auxiliary_id': line.staff.auxiliary_id.id,
- 'tax_rate': line.invoice_amount and invoice_tax / line.invoice_amount * 100 or 0,
- 'tax_amount': invoice_tax,
- 'category_id': line.category_id and line.category_id.id or False,
- })
-
- class HrExpenseLine(models.Model):
- _name = 'hr.expense.line'
- _inherit = ['mail.thread']
- _description = '报销行'
- _order = 'date'
- name = fields.Char('单据编号', index=True, copy=False, help='报销单的唯一编号')
- staff = fields.Many2one('staff', string='报销员工', related='order_id.staff', store=True, readonly=True, help='自动关联上级报销单的报销员工')
- invoice_type = fields.Selection([('pt', '增值税普通发票'), ('zy', '增值税专用发票'), ('dz', '电子普通发票')], '发票类别')
- invoice_code = fields.Char('发票代码', copy=False)
- invoice_name = fields.Char('发票号码', copy=False)
- invoice_amount = fields.Float(string='发票金额', digits='Amount', required=True, help='如是增值税发票请填不含税金额')
- invoice_tax = fields.Float(string='发票税额', digits='Amount', help='如是增值税发票请填不含税金额')
- invoice_total = fields.Float(string='发票金额合计', store=True, readonly=True, compute='_compute_cost_total', digits='Amount')
- invoice_heck_code = fields.Char('发票校验码', copy=False)
- invoice_date = fields.Char('发票日期', copy=False)
- note = fields.Text('备注')
- state = fields.Selection([('draft', '草稿'), ('confirm', '已提交'), ('done', '已支付')], '状态', default='draft', compute='_compute_state', store=True, copy=False)
- category_id = fields.Many2one('core.category', '类别', ondelete='restrict', required=True, help='类型:运费、咨询费等', domain=[('type', '=', 'other_pay')])
- date = fields.Date(string='费用日期', default=fields.Date.context_today, copy=False, required=True, help='费用发生日期')
- order_id = fields.Many2one('hr.expense', '报销单号', index=True, copy=False, readonly=True)
- is_pay = fields.Boolean(string='已付款', store=True, compute='_compute_is_pay', readonly=True, copy=False)
- _sql_constraints = [('unique_invoice_code_name', 'unique (invoice_code, invoice_name)', '发票代码+发票号码不能相同!')]
- attachment_number = fields.Integer(compute='_compute_attachment_number', string='附件号')
-
- @api.depends('invoice_amount', 'invoice_tax')
- def _compute_cost_total(self):
- for bill in self:
- bill.invoice_total = bill.invoice_amount + bill.invoice_tax
-
- @api.depends('order_id.state')
- def _compute_state(self):
- for line in self:
- if line.order_id:
- line.state = line.order_id.state
- else:
- line.state = 'draft'
-
- @api.model
- def saomiaofapiao(self, model_name, barcode, order_id):
- barcode = barcode.replace(',', ',').replace('。', '.')
- code = barcode.split(',')
- invoice_heck_code = ''
- if len(code) < 5:
- raise UserError('请确认扫描是否正确%s' % code)
- if code[0] == '01':
- if code[1] == '10':
- invoice_type = 'dz'
- invoice_heck_code = code[6]
- if code[1] == '01':
- invoice_type = 'zy'
- if code[1] == '04':
- invoice_type = 'pt'
- invoice_heck_code = code[6]
- invoice_code = code[2]
- invoice_name = code[3]
- invoice_amount = round(float(code[4]), 2)
- invoice_date = code[5]
- invoice_tax = 0
- self.browse(order_id).write({
- 'invoice_type': invoice_type, 'invoice_code': invoice_code, 'invoice_name': invoice_name,
- 'invoice_amount': invoice_amount, 'invoice_tax': invoice_tax,
- 'invoice_heck_code': invoice_heck_code, 'invoice_date': invoice_date,
- })
-
- def action_get_attachment_view(self):
- res = self.env.ref('base.action_attachment')
- res['domain'] = [('res_model', '=', 'hr.expense.line'), ('res_id', 'in', self.ids)]
- res['context'] = {'default_res_model': 'hr.expense.line', 'default_res_id': self.id}
- return res
-
- def _compute_attachment_number(self):
- attachment_data = self.env['ir.attachment'].read_group(
- [('res_model', '=', 'hr.expense.line'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id'])
- attachment = dict((data['res_id'], data['res_id_count']) for data in attachment_data)
- for bill in self:
- bill.attachment_number = attachment.get(bill.id, 0)
-
- def unlink(self):
- for bill in self:
- # 只有关联的报销单已提交或已支付时才限制删除
- if bill.order_id and bill.order_id.state in ['confirm', 'done']:
- raise UserError('关联的报销单已%s,不能删除费用发票' % bill.order_id.state)
- return super().unlink()
-
- @api.depends('order_id.state')
- def _compute_is_pay(self):
- for bill in self:
- if bill.order_id and bill.order_id.state == 'done':
- bill.is_pay = True
- else:
- bill.is_pay = False
|