|
- # -*- coding: utf-8 -*-
- ##############################################################################
- #
- # Copyright (C) 2016 德清武康开源软件().
- #
- # 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 Foundaption, 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
- import datetime
- import re
- from odoo.exceptions import UserError
- import xlrd
- import base64
- import os
- import odoo
-
- class cn_account_invoice(models.Model):
- _name = 'cn.account.invoice'
- _description = '中国发票'
- _rec_name= 'name'
- _inherit = ['mail.thread']
-
- partner_name_in = fields.Char('供应商名称', copy=False)
- partner_code_in = fields.Char('供应商税号', copy=False)
- partner_address_in = fields.Char('供应商地址及电话', copy=False)
- partner_bank_number_in = fields.Char('供应商银行及帐号', copy=False)
- partner_name_out = fields.Char('客户名称', copy=False)
- partner_code_out = fields.Char('客户税号', copy=False)
- partner_address_out = fields.Char('客户地址及电话', copy=False)
- partner_bank_number_out = fields.Char('客户银行及帐号', copy=False)
- type = fields.Selection([('in', '进项发票'),
- ('out', '销项发票'),
- ('all', '内部发票')], '进/出发票', copy=False)
- invoice_type = fields.Many2one('cn.invoice.type', '发票类型', copy=False)
- use_heck_code = fields.Boolean('是否需要检验码')
- is_export = fields.Boolean('是否出口退税')
- invoice_code = fields.Char('发票代码', copy=False)
- name = fields.Char('发票号码', copy=False, index=True)
- invoice_export_amount = fields.Float('外币', copy=False)
- invoice_amount = fields.Float('金额', copy=False)
- invoice_tax = fields.Float('税额', copy=False)
- invoice_heck_code = fields.Char("发票校验码", copy=False)
- invoice_date = fields.Date('开票日期', copy=False)
- tax_rate = fields.Float('税率', compute='_compute_tax_rate',digits=(12, 0), store=True)
- is_deductible = fields.Boolean('是否抵扣')
- line_ids = fields.One2many('cn.account.invoice.line', 'order_id', '发票明细行',
- copy=False)
- line_ids2 = fields.One2many('cn.account.invoice.line', 'order_id', '发票明细行',
- copy=False)
- attachment_number = fields.Integer(compute='_compute_attachment_number', string='附件号')
- note = fields.Text("备注")
- color = fields.Integer('颜色', related='invoice_type.color')
- line_type = fields.Selection([('same', '一般发票'),
- ('transport', '运输'),], string='明细类型', copy=False, index=True, readonly=True, default='same')
- company_id_in = fields.Many2one('res.company', string='关联公司进')
- company_id_out = fields.Many2one('res.company', string='关联公司销')
-
- _sql_constraints = [
- ('unique_invoice_code_name', 'unique (invoice_code, name)', '发票代码+发票号码不能相同!'),
- ]
-
- def _get_full_excel_template_path(self, template_path):
- for path in odoo.addons.__path__:
- if os.path.exists(path + template_path):
- return path + template_path
- return False
-
- @api.depends('invoice_amount', 'invoice_tax', 'is_deductible')
- def _compute_tax_rate(selfs):
- for self in selfs:
- if not self.is_deductible:
- self.tax_rate = 0
- else:
- self.tax_rate = int(self.invoice_tax / self.invoice_amount * 100)
-
- @api.onchange('invoice_type')
- def _use_heck_code(self):
- for order in self:
- if order.invoice_type and order.invoice_type.code in ['pp', 'dzfp']:
- order.use_heck_code = True
- else:
- order.use_heck_code = False
-
- def action_get_attachment_view(self):
- res = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment')
- res['domain'] = [('res_model', '=', 'cn.account.invoice'), ('res_id', 'in', self.ids)]
- res['context'] = {'default_res_model': 'cn.account.invoice', 'default_res_id': self.id}
- return res
-
- def _compute_attachment_number(self):
- attachment_data = self.env['ir.attachment'].read_group(
- [('res_model', '=', 'cn.account.invoice'), ('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)
-
- def create_uom(self):
- for mx in self.line_ids:
- uom = mx.product_unit
- if uom:
- uom_id = self.env['uom'].search([('name', '=', uom)])
- if not uom_id:
- uom_id = self.env['uom'].create({
- 'name': uom,
- 'active': 1})
-
- def create_category(self):
- for mx in self.line_ids:
- category = mx.tax_type
- if category:
- category_id = self.env['core.category'].search([
- '&', ('type', '=', 'goods'), ('tax_category_id.print_name', '=', category)])
- if not category_id:
- if self.type == 'in':
- account_id = self.env['ir.values'].get_default('tax.config.settings', 'default_buy_goods_account')
- if self.type == 'out':
- account_id = self.env['ir.values'].get_default('tax.config.settings',
- 'default_sell_goods_account')
- category_id = self.env['core.category'].create({
- 'type': 'goods',
- 'name': category,
- 'account_id': account_id,
- 'tax_category_id': self.env['tax.category'].search([('print_name', '=', category)],limit=1).id,
- 'note': '由系统自动增加'
- })
-
- def create_product(self):
- for mx in self.line_ids:
- goods = mx.product_name
- uom = mx.product_unit
- uom_id = self.env['uom'].search([('name', '=', uom)])
- category = mx.tax_type
- category_id = self.env['core.category'].search([
- '&', ('type', '=', 'goods'), ('tax_category_id.print_name', '=', category)])
- if category_id and category_id.tax_category_id.code[0] == '1':
- no_stock = False
- else:
- no_stock = True
-
- if goods:
- goods_id = self.env['goods'].search([('name', '=', goods)])
- if not goods_id:
- self.env['goods'].create({
- 'name': goods,
- 'uom_id': uom_id.id or '',
- 'uos_id': uom_id.id or '',
- # 'tax_rate': float(in_xls_data.get('税率')),
- 'category_id': category_id and category_id.id,
- 'computer_import': True,
- 'no_stock': no_stock,
- 'cost_method': 'average',
- })
-
- # 创建供应商
- def create_buy_partner(self):
- if self.partner_code_in:
- partner_id = self.env['partner'].search([
- ('tax_num', '=', self.partner_code_in)])
- if self.partner_name_in:
- partner_id = self.env['partner'].search([
- ('name', '=', self.partner_name_in)])
- default_goods_supplier = self.env['ir.values'].get_default('tax.config.settings', 'default_goods_supplier')
- if not default_goods_supplier:
- raise UserError('请设置默认产品供应商!')
- if not partner_id:
- partner_id = self.env['partner'].create({
- 'name': self.partner_name_in,
- 'main_mobile': self.partner_code_in,
- 'tax_num': self.partner_code_in,
- 's_category_id': default_goods_supplier,
- 'computer_import': True,
- })
- # 补银行帐号等信息
- if self.partner_address_in and partner_id.main_mobile == partner_id.tax_num:
- main_mobile = self.split_number(self.partner_address_in)
- partner_id.write({'main_mobile': main_mobile})
- if self.partner_address_in and not partner_id.main_address:
- if partner_id.main_mobile and partner_id.main_mobile != partner_id.tax_num:
- to_del_mobile = len(partner_id.main_mobile)
- else:
- to_del_mobile = 0
- main_address = self.partner_address_in[:-to_del_mobile]
- partner_id.write({'main_address': main_address})
- if self.partner_bank_number_in and not partner_id.bank_num:
- bank_number = self.split_number(self.partner_bank_number_in)
- partner_id.write({'bank_num': bank_number})
- if self.partner_bank_number_in and not (partner_id.bank_num or partner_id.bank_name):
- if self.bank_num:
- to_del_bank_number = len(self.bank_num)
- else:
- to_del_bank_number = 0
- bank_name = self.partner_bank_number_in[:-to_del_bank_number]
- partner_id.write({'bank_name': bank_name})
-
- # 创建客户
- def create_sell_partner(self):
- if self.partner_code_out:
- partner_id = self.env['partner'].search([
- ('tax_num', '=', self.partner_code_out)])
- elif self.partner_name_out:
- partner_id = self.env['partner'].search([
- ('name', '=', self.partner_name_out)])
- default_customer = self.env['ir.values'].get_default('tax.config.settings', 'default_customer')
- if not default_customer:
- raise UserError('请设置默认产品供应商!')
- if not partner_id:
- partner_id = self.env['partner'].create({
- 'name': self.partner_name_out,
- 'main_mobile': self.partner_code_out,
- 'tax_num': self.partner_code_out,
- 'c_category_id':default_customer,
- 'computer_import': True,
- })
- # 补银行帐号等信息
- if self.partner_address_out and partner_id.main_mobile == partner_id.tax_num:
- main_mobile = self.split_number(self.partner_address_out)
- partner_id.write({'main_mobile': main_mobile})
- if self.partner_address_out and not partner_id.main_address:
- if partner_id.main_mobile and partner_id.main_mobile != partner_id.tax_num:
- to_del_mobile = len(partner_id.main_mobile)
- else:
- to_del_mobile = 0
- main_address = self.partner_address_out[:-to_del_mobile]
- partner_id.write({'main_address': main_address})
- if self.partner_bank_number_out and not partner_id.bank_num:
- bank_number = self.split_number(self.partner_bank_number_out)
- partner_id.write({'bank_num': bank_number})
- if self.partner_bank_number_out and not partner_id.bank_name:
- if partner_id.bank_num:
- to_del_bank_number = len(partner_id.bank_num)
- else:
- to_del_bank_number = 0
- bank_name = self.partner_bank_number_out[:-to_del_bank_number]
- partner_id.write({'bank_name': bank_name})
-
- # 跟据帐号和电话都在后面的特性,使用倒转后从头直至有字母出现为此都是帐号和电话。
- def split_number(self, str):
- str1 = str[::-1] #
- changdu = len(str1) # 取长度
- num = ''
- i = 0
- while i < changdu:
- if str1[i].isdigit() or str1[i] == '-' or str1[i] == ' ':
- num += str1[i]
- i += 1
- else:
- return num[::-1]
-
- #定义发票明细行
- class cn_account_invoice_line(models.Model):
- _name = 'cn.account.invoice.line'
- _description = '中国发票明细'
- _rec_name='product_name'
-
- order_id = fields.Many2one('cn.account.invoice', '发票',help='关联发票',copy=False, required=True,
- readonly=True, index=True, ondelete="cascade",)
- product_name = fields.Char("货物名称",copy=False)
- product_type = fields.Char("规格型号",copy=False)
- product_unit = fields.Char("单位",copy=False)
- product_count = fields.Float("数量",copy=False)
- product_price = fields.Float("价格",copy=False)
- product_amount = fields.Float("金额",copy=False)
- product_tax_rate = fields.Integer("税率",copy=False)
- product_tax = fields.Float("税额",copy=False)
- tax_type = fields.Char('税收分类编码',help='20170101以后使用的税收分类编码,这个很重要',copy=False)
- note = fields.Char("备注",copy=False)
- car_number = fields.Char("车牌号", copy=False)
- car_type = fields.Char("类型", copy=False)
- car_begin_date = fields.Char("通行日期起", copy=False)
- car_end_date = fields.Char("通行日期止", copy=False)
-
- class create_cn_invoice_wizard(models.TransientModel):
- _name = 'create.cn.invoice.wizard'
- _description = '导入发票'
-
- excel = fields.Binary(u'导入认证系统导出的excel文件',)
- type = fields.Selection([('in', '进项发票'),
- ('out', '销项发票'),], '进/出发票', copy=False)
- company_id = fields.Many2one(
- 'res.company',
- string='公司',
- change_default=True)
-
- def create_tax_invoice(self):
- not_input, is_input = self.create_invoice()
- self.create_invoice_line(not_input)
- return {
- 'name': _('导入发票'),
- 'view_mode': 'tree,form',
- 'domain': [('id', 'in', is_input)],
- 'res_model': 'cn.account.invoice',
- 'type': 'ir.actions.act_window',
- 'context': {'create': False, 'active_test': False},
- }
-
- def create_invoice_line(self,not_input):
- xls_data = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel))
- all = xls_data.sheet_by_name('货物清单')
- ncows = all.nrows
- ncols = 0
- colnames = all.row_values(0)
- list = []
- for rownum in range(1, ncows):
- row = all.row_values(rownum)
- if row:
- app = {}
- for i in range(len(colnames)):
- app[colnames[i]] = row[i]
- list.append(app)
- ncols += 1
- in_xls_data = {}
- for data in range(0, ncols):
- in_xls_data = list[data]
- product_name = in_xls_data.get('货物或应税劳务名称') or in_xls_data.get('货物或应税劳务、服务名称')
- invoice_name = in_xls_data.get('发票号码') or in_xls_data.get('数电票号码')
- if product_name == "(详见销货清单)" or product_name == '详见对应正数发票及清单' or product_name == "(详见销货清单)":
- continue
- if str(invoice_name) in not_input:
- continue
- invoice_code = in_xls_data.get(u'发票代码')
- company_in_sys_invoice = self.env['cn.account.invoice'].search([
- ('invoice_code', '=', str(invoice_code)),
- ('name', '=', str(invoice_name)),
- ('type', '=', self.type)])
- amount = float(in_xls_data.get('金额'))
- tax = float(in_xls_data.get('税额'))
- tax_rate = 0
- if tax:
- tax_rate = round(tax / amount, 2) * 100
- if product_name:
- goods_name = product_name.split('*')[-1]
- if '*' in product_name:
- tax_type = product_name.split('*')[1]
- else:
- goods_name = product_name
- tax_type = ''
- if company_in_sys_invoice:
- self.env['cn.account.invoice.line'].create({
- 'order_id': company_in_sys_invoice.id,
- 'product_name': goods_name.strip() or '',
- 'product_type': in_xls_data.get('规格型号').strip() or '',
- 'product_unit': in_xls_data.get('单位').strip() or '',
- 'product_count': in_xls_data.get('数量') or '',
- 'product_price': in_xls_data.get('单价') or '',
- 'product_amount': amount or '0',
- 'product_tax_rate': tax_rate or '0',
- 'product_tax': tax or '0',
- 'tax_type': tax_type,
- })
-
- def create_invoice(self):
- not_input = is_input = []
- xls_data = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel))
- all = xls_data.sheet_by_name('发票基础信息')
- ncows = all.nrows
- ncols = 0
- colnames = all.row_values(0)
- list = []
- # 数据读入,过滤没有发票状态的行
- for rownum in range(1, ncows):
- row = all.row_values(rownum)
- if row:
- app = {}
- for i in range(len(colnames)):
- app[colnames[i]] = row[i]
- if app['发票状态'] == '正常':
- list.append(app)
- ncols += 1
-
- for data in range(0, ncols-1):
- in_xls_data = list[data]
- code = ''
- if in_xls_data.get('数电票号码'):
- name = in_xls_data.get('数电票号码')
- else:
- name = in_xls_data.get('发票号码')
- code = in_xls_data.get('发票代码')
- if code:
- invoice_code = str(int(code)).zfill(len(code))
- else:
- invoice_code = ''
- if self.type == "in":
- partner_name = in_xls_data.get('销方名称')
- company_tax = in_xls_data.get('购方识别号')
- else:
- partner_name = in_xls_data.get('购方名称') or in_xls_data.get('购买方名称')
- company_tax = in_xls_data.get('销方识别号')
- if company_tax != self.company_id.vat:
- raise UserError('购买/销售方税号与公司税号不一致!请检查!%s,%s'%(company_tax, self.company_id.vat))
- invoice_name= str(int(name)).zfill(len(name))
- company_in_sys_invoice = self.env['cn.account.invoice'].search([
- ('invoice_code', '=', invoice_code),
- ('name', '=', invoice_name),
- ('type', '=', self.type)])
- amount = float(in_xls_data.get('金额'))
- tax = float(in_xls_data.get('税额'))
- if self.company_id and self.company_id.vat:
- if self.company_id.vat != company_tax:
- raise UserError('文件购买方税号为(%s)与公司税号(%s)不一致!'%(company_tax,self.company_id.vat))
- tax_rate = 0.0
- if tax:
- tax_rate = round(tax / amount,2) * 100
- invoice_type = self.env['cn.invoice.type'].search([('name', '=', in_xls_data.get('发票票种'))], limit=1)
- if not invoice_type:
- raise UserError('发票票种[%s]未找到!请到发票类型中增加'%(in_xls_data.get('发票票种')))
- if company_in_sys_invoice:
- not_input.append(invoice_name)
- continue
- else:
- note = in_xls_data.get(u'备注')
- if self.type == 'in':
- if amount > 0:
- is_deductible = invoice_type.is_coming_tax
- else:
- is_deductible = False
- invoice_id = self.env['cn.account.invoice'].create({
- 'type': self.type,
- 'partner_name_in': partner_name,
- 'partner_code_in': str(in_xls_data.get(u'销方识别号')),
- 'invoice_code': str(invoice_code),
- 'name': str(invoice_name),
- 'invoice_amount': amount,
- 'invoice_tax': tax,
- 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
- 'invoice_type': invoice_type.id,
- 'tax_rate': tax_rate,
- 'is_deductible': is_deductible,
- 'note': note,
- 'company_id_in': self.company_id.id,
- })
- else:
- invoice_id = self.env['cn.account.invoice'].create({
- 'type': self.type,
- 'partner_name_out': partner_name,
- 'partner_code_out': str(in_xls_data.get(u'购方识别号')),
- 'invoice_code': str(invoice_code),
- 'name': str(invoice_name),
- 'invoice_amount': amount,
- 'invoice_tax': tax,
- 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
- 'invoice_type': invoice_type.id,
- 'invoice_in_id': '',
- 'tax_rate': tax_rate,
- 'note': note,
- 'company_id_out': self.company_id.id,
- })
- is_out = re.findall('出口业务', in_xls_data.get(u'备注'))
- if is_out:
- for s in note.split(';'):
- invoice_export_amount = re.search(r'\d+(\.\d+)', s)
- invoice_id.write({
- 'is_export': True,
- 'invoice_export_amount': invoice_export_amount.group() or 0.0,
- })
- is_input.append(invoice_id.id)
- return not_input, is_input
-
- # def create_invoice2(self):
- # not_input = is_input = []
- # xls_data = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel))
- # all = xls_data.sheets()[0]
- # ncows = all.nrows
- # ncols = 0
- # colnames = all.row_values(1)
- # list = []
- # top = all.row_values(0)
- # invoice_type = self.env['cn.invoice.type'].search([('name', '=', top[0])], limit=1)
- # # 数据读入,过滤没有开票日期的行
- # for rownum in range(2, ncows):
- # row = all.row_values(rownum)
- # if row:
- # app = {}
- # for i in range(len(colnames)):
- # app[colnames[i]] = row[i]
- # if app['发票状态'] == '正常':
- # list.append(app)
- # ncols += 1
- #
- # # 数据处理
- # in_xls_data = {}
- # for data in range(0, ncols):
- # in_xls_data = list[data]
- # invoice_code = str(int(in_xls_data.get('发票代码'))).zfill(len(in_xls_data.get('发票代码')))
- # if self.type == "in":
- # partner_name = in_xls_data.get('销售方名称')
- # company_tax = in_xls_data.get('购买方税号')
- # else:
- # partner_name = in_xls_data.get('购买方名称')
- # company_tax = in_xls_data.get('销售方税号')
- # if company_tax != self.company_id.vat:
- # raise UserError('购买/销售方税号与公司税号不一致!请检查!')
- # invoice_name= str(int(in_xls_data.get('发票号码'))).zfill(len(in_xls_data.get('发票号码')))
- # company_in_sys_invoice = self.env['cn.account.invoice'].search([
- # ('invoice_code', '=', invoice_code),
- # ('name', '=', invoice_name),
- # ('type', '=', self.type)])
- # amount = float(in_xls_data.get('金额'))
- # tax = float(in_xls_data.get('税额'))
- # if self.company_id and self.company_id.vat:
- # if self.company_id.vat != company_tax:
- # raise UserError('文件购买方税号为(%s)与公司税号(%s)不一致!'%(company_tax,self.company_id.vat))
- # tax_rate = 0.0
- # if tax:
- # tax_rate = round(tax / amount,2) * 100
- # if company_in_sys_invoice:
- # not_input.append(invoice_name)
- # continue
- # else:
- # note = in_xls_data.get(u'备注')
- # if self.type == 'in':
- # invoice_id = self.env['cn.account.invoice'].create({
- # 'type': self.type,
- # 'partner_name_in': partner_name,
- # 'partner_code_in': str(in_xls_data.get(u'销售方税号')),
- # 'invoice_code': str(invoice_code),
- # 'name': str(invoice_name),
- # 'invoice_amount': amount,
- # 'invoice_tax': tax,
- # 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
- # 'invoice_type': invoice_type.id,
- # 'tax_rate': tax_rate,
- # 'note': note,
- # })
- # else:
- # invoice_id = self.env['cn.account.invoice'].create({
- # 'type': self.type,
- # 'partner_name_out': partner_name,
- # 'partner_code_out': str(in_xls_data.get(u'购买方税号')),
- # 'invoice_code': str(invoice_code),
- # 'name': str(invoice_name),
- # 'invoice_amount': amount,
- # 'invoice_tax': tax,
- # 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
- # 'invoice_type': invoice_type.id,
- # 'invoice_in_id': '',
- # 'tax_rate': tax_rate,
- # 'note': note,
- # })
- # is_out = re.findall('出口业务', in_xls_data.get(u'备注'))
- # if is_out:
- # for s in note.split(';'):
- # invoice_export_amount = re.search(r'\d+(\.\d+)', s)
- # invoice_id.write({
- # 'is_export': True,
- # 'invoice_export_amount': invoice_export_amount.group() or 0.0,
- # })
- # is_input.append(invoice_id.id)
- # return not_input, is_input
-
- def excel_date(self, data):
- # 将excel日期改为正常日期
- if type(data) in (int, float):
- year, month, day, hour, minute, second = xlrd.xldate_as_tuple(data, 0)
- py_date = datetime.datetime(year, month, day, hour, minute, second)
- else:
- py_date = data
- return py_date
|