osbzr/gooderp_addons

View on GitHub
staff_expense/models/hr_expense.py

Summary

Maintainability
B
6 hrs
Test Coverage
# -*- coding: utf-8 -*-
##############################################################################
#
#    GOODERP, Open Source Management Solution
#    Copyright (C) 2018  德清武康开源软件工作室().
#
#    This program is free software: you can redistribute it and/or modify
#    it under the terms of the GNU Affero General Public License as
#    published by the Free Software Foundation, either version 3 of the
#    License, or (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU Affero General Public License for more details.
#
#    You should have received a copy of the GNU Affero General Public License
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################

from odoo import api, fields, models, tools, _
import odoo.addons.decimal_precision as dp
from odoo.exceptions import UserError

# 字段只读状态
READONLY_STATES = {
        'confirm': [('readonly', True)],
        'done': [('readonly', True)],
    }

class hr_expense(models.Model):
    '''按报销人费用汇总'''
    _name = 'hr.expense'

    name = fields.Char(u"单据编号",copy=False)
    date = fields.Date(string=u'报销日期',
                       default=lambda self: fields.Date.context_today(self),
                       states={'draft': [('readonly', False)]},
                       copy=False,required=True,
                       help=u'报销发生日期')
    staff = fields.Many2one('staff', u'报销员工', required=True,help=u'选择员工')
    invoice_all_total = fields.Float(string=u'费用金额合计', store=True, readonly=True,
                                 compute='_compute_invoice_all_total', track_visibility='always',
                                 digits=dp.get_precision('Amount'))
    state = fields.Selection([('draft', u'草稿'),
                              ('confirm', u'已提交'),
                              ('done', u'已支付')], u'状态', default='draft',store=True,compute='_state_to_done')
    type = fields.Selection([
        ('company', u'付给公司'),
        ('my', u'付给报销人')], string=u'支付方式:',default='my', required=True, help=u'支付给个人时走其他付款单,支付给公司时走结算单')
    line_ids = fields.One2many('hr.expense.line', 'order_id', u'明细发票行',
                               states=READONLY_STATES, copy=False)
    note = fields.Text(u"备注")
    bank_account_id = fields.Many2one('bank.account', u'结算账户',
                                      ondelete='restrict',
                                      help=u'付给个人/公司')
    partner_id = fields.Many2one('partner', u'供应商',
                                 help=u'直接支付给供应商')

    money_invoice = fields.Many2one(
        'money.invoice', u'对应结算单', readonly=True, ondelete='restrict', copy=False)
    other_money_order = fields.Many2one(
        'other.money.order', u'对应其他应付款单', 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'
        '''付给公司走公司对帐了,跟报销人已经没有关系,所以不DONE了吧'''

    @api.depends('line_ids')
    def _compute_invoice_all_total(self):
        total = 0
        for i in self[0].line_ids:
            total += i.invoice_total
        self[0].invoice_all_total = total

    @api.multi
    def check_consistency(self):
        category_id = []
        for line in self.line_ids:
            if line.category_id and line.category_id.id not in category_id:
                category_id.append(line.category_id.id)
            if line.staff.id != self.staff.id:
                raise UserError(u"费用明细必须是同一人!")
        if self.type == 'company':
            if len(category_id) > 1:
                raise UserError(u"申报支付给公司的费用必须同一类别!")

    @api.model
    def create(self, vals):
        res = super(hr_expense, self).create(vals)
        self.check_consistency()
        return res

    @api.multi
    def write(self, vals):
        res = super(hr_expense, self).write(vals)
        self.check_consistency()
        return res

    @api.multi
    def unlink(self):
        for record in self:
            if record.state != 'draft':
                raise UserError(u'只能删除草稿状态的费用报销单')
        super(hr_expense, self).unlink()

    def hr_expense_confirm(self):
        if self.state == 'confirm':
            raise UserError(u'请不要重复确认')
        if self.type == 'company':
            self.to_money_invoice()
        if self.type == 'my':
            self.to_other_money_order()
        self.state = 'confirm'

    def hr_expense_draft(self):
        '''删掉其他应付款单'''
        if self.state == 'draft':
            raise UserError(u'请不要重复撤销')
        if self.other_money_order:
            other_money_order, self.other_money_order = self.other_money_order, False
            if other_money_order.state == 'done':
                other_money_order.other_money_draft()
            other_money_order.unlink()
        '''删掉结算单'''
        if self.money_invoice:
            money_invoice, self.money_invoice = self.money_invoice, False
            if money_invoice.state == 'done':
                money_invoice.money_invoice_draft()
            money_invoice.unlink()

        self.state = 'draft'

    @api.one
    def to_money_invoice(self):
        tax = 0
        bill_number = ''
        for line in self.line_ids:
            if line.invoice_type == 'zy':
                tax += line.invoice_tax
            if line.invoice_name:
                bill_number = '%s, %s' % (line.invoice_name, bill_number)
        # todo 测试一下
        money_invoice = self.env['money.invoice'].create({
            'name': self.name,
            'partner_id': self.partner_id.id,
            'category_id': self.line_ids and self.line_ids[0].category_id.id or '',
            'date': self.date,
            'amount': self.invoice_all_total,
            'reconciled': 0,
            'to_reconcile': self.invoice_all_total,
            'date_due': fields.Date.context_today(self),
            'state': 'draft',
            'tax_amount': tax,
            'bill_number':bill_number
        })
        self.write({'money_invoice': money_invoice.id})

    @api.one
    def to_other_money_order(self):
        other_money_order = self.with_context(type='other_pay').env['other.money.order'].create({
            'state': 'draft',
            'partner_id': self.partner_id and self.partner_id.id or '',
            'date': self.date,
            'bank_id': self.bank_account_id.id,
        })
        self.write({'other_money_order': other_money_order.id})
        for line in self.line_ids:
            #增值税专用发票(ZY)税额可以抵扣!
            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
            self.env['other.money.order.line'].create({
                'other_money_id': other_money_order.id,
                'amount': invoice_amount,
                'tax_rate': round(line.invoice_amount and invoice_tax / line.invoice_amount * 100,0) or 0,
                'tax_amount': invoice_tax,
                'category_id': line.category_id and line.category_id.id
            })

class hr_expense_line(models.Model):
    '''费用明细'''
    _name = 'hr.expense.line'
    _order = "date"
    name = fields.Char(u'单据编号',
                       index=True,
                       copy=False,
                       help=u"报销单的唯一编号,当创建时它会自动生成下一个编号。")
    staff = fields.Many2one('staff', required=True, string = u'报销员工', help=u'用关键字段查找并关联类别')
    invoice_type = fields.Selection([('pt', u'增值税普通发票'),
                              ('zy', u'增值税专用发票'),
                              ('dz',u'电子普通发票')], u'状态', )
    invoice_code = fields.Char(u"发票代码",copy = False)
    invoice_name = fields.Char(u"发票号码",copy = False)
    invoice_amount = fields.Float(string=u'发票金额',digits=dp.get_precision('Amount'), required=True,help=u'如是增值税发票请填不含税金额')
    invoice_tax = fields.Float(string=u'发票税额',digits=dp.get_precision('Amount'), help=u'如是增值税发票请填不含税金额')
    invoice_total = fields.Float(string=u'发票金额合计', store=True, readonly=True,
                        compute='_compute_cost_total', digits=dp.get_precision('Amount'))
    invoice_heck_code = fields.Char(u"发票校验码", copy=False)
    invoice_date = fields.Char(u"发票日期", copy=False)
    note = fields.Text(u"备注")
    state = fields.Selection([('draft', u'草稿'),
                              ('confirm', u'已提交')], u'状态',default='draft',copy = False)
    category_id = fields.Many2one('core.category',
                                  u'类别', ondelete='restrict',required=True,
                                  help=u'类型:运费、咨询费等')
    date = fields.Date(string=u'费用日期',
                       default=lambda self: fields.Date.context_today(self),
                       states={'draft': [('readonly', False)]},
                       copy=False,required=True,
                       help=u'费用发生日期')
    order_id = fields.Many2one('hr.expense', u'报销单号',  index = True, copy = False, readonly = True)
    is_refused = fields.Boolean(string="已被使用", store = True, compute='_compute_is_choose', readonly=True, copy=False)
    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)', u'发票代码+发票号码不能相同!'),
    ]
    attachment_number = fields.Integer(compute='_compute_attachment_number', string=u'附件号')

    def hr_expense_line_confirm(self):
        if self.state == 'confirm':
            raise UserError(u'请不要重复确认')
        self.state = 'confirm'

    def hr_expense_line_draft(self):
        if self.state == 'draft':
            raise UserError(u'请不要重复撤销')
        if self.state == 'confirm' and not self.is_refused:
            self.state = 'draft'
        else:
            raise UserError(u"请先解除关联单据%s"%self.order_id.name)


    @api.depends('invoice_amount', 'invoice_tax')
    def _compute_cost_total(self):
        for expense_line in self:
            expense_line.invoice_total = expense_line.invoice_amount + expense_line.invoice_tax

    @api.depends('order_id')
    def _compute_is_choose(self):
        for expense_line in self:
            if expense_line.order_id:
                expense_line.is_refused = True
            else:
                expense_line.is_refused = False

    @api.model
    def saomiaofapiao(self, model_name, barcode, order_id):
        """
        扫描发票条码
        :param model_name: 模型名
        :param barcode: 二维码识别内容
        :return:

        01,10,033001600211,11255692,349997.85,20180227,62521957050111533932,7DF9,
        01:第一个属性值,尚未搞清楚含义;
        10:第二个属性值,代表发票种类代码,10-电子普通发票,04-增值税普通发票,01-增值税专用发票;
        033001600211:第三个属性值,代表发票代码;
        11255692:第四个属性值,代表发票号码;
        349997.85:第五个属性值,代表开票金额(不含税);
        20180227:第六个属性值,代表开票日期,该值为2016年10月18日;
        62521957050111533932:第七个属性值,代码发票校验码,我们都知道增值税专用发票是没有发票校验码的,没有则为空字符串;

        """
        #中文模式下替換字符
        barcode = barcode.replace(u',', ',')
        barcode = barcode.replace(u'。', '.')
        code = barcode.split(',')
        invoice_heck_code = ''
        if len(code) < 5:
            raise UserError(u"请确认扫描是否正确%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
        })

    @api.multi
    def action_get_attachment_view(self):
        res = self.env['ir.actions.act_window'].for_xml_id('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

    @api.multi
    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 expense in self:
            expense.attachment_number = attachment.get(expense.id, 0)

    @api.multi
    def unlink(self):
        for record in self:
            if record.state != 'draft':
                raise UserError(u'只能删除草稿状态的费用发票')
        super(hr_expense_line, self).unlink()

    @api.one
    @api.depends('order_id.state')
    def _compute_is_pay(self):
        if self.order_id and self.order_id.state == 'done':
            self.is_pay = True
        else:
            self.is_pay = False