中国本土应用
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.

442 lines
21KB

  1. # -*- coding: utf-8 -*-
  2. ##############################################################################
  3. #
  4. # Copyright (C) 2016 德清武康开源软件().
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU Affero General Public License as
  8. # published by the Free Software Foundaption, either version 3 of the
  9. # License, or (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU Affero General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Affero General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #
  19. ##############################################################################
  20. from odoo import api, fields, models, tools, _
  21. import odoo.addons.decimal_precision as dp
  22. import datetime
  23. import re
  24. from odoo.exceptions import UserError
  25. import xlrd
  26. import base64
  27. #定意发票todo add confirm_month
  28. class cn_account_invoice(models.Model):
  29. _name = 'cn.account.invoice'
  30. _description = '中国发票'
  31. _rec_name= 'name'
  32. partner_name_in = fields.Char('供应商名称', copy=False)
  33. partner_code_in = fields.Char('供应商税号', copy=False)
  34. partner_address_in = fields.Char('供应商地址及电话', copy=False)
  35. partner_bank_number_in = fields.Char('供应商银行及帐号', copy=False)
  36. partner_name_out = fields.Char('客户名称', copy=False)
  37. partner_code_out = fields.Char('客户税号', copy=False)
  38. partner_address_out = fields.Char('客户地址及电话', copy=False)
  39. partner_bank_number_out = fields.Char('客户银行及帐号', copy=False)
  40. type = fields.Selection([('in', '进项发票'),
  41. ('out', '销项发票'),
  42. ('all', '内部发票')], '进/出发票', copy=False)
  43. invoice_type = fields.Many2one('cn.invoice.type', '发票类型', copy=False)
  44. use_heck_code = fields.Boolean('是否需要检验码')
  45. is_export = fields.Boolean('是否出口退税')
  46. invoice_code = fields.Char('发票代码', copy=False)
  47. name = fields.Char('发票号码', copy=False)
  48. invoice_export_amount = fields.Float('外币', copy=False)
  49. invoice_amount = fields.Float('金额', copy=False)
  50. invoice_tax = fields.Float('税额', copy=False)
  51. invoice_heck_code = fields.Char("发票校验码", copy=False)
  52. invoice_date = fields.Date('开票日期', copy=False)
  53. tax_rate = fields.Float('税率', digits=(12, 0),copy=False)
  54. is_deductible = fields.Boolean('是否抵扣')
  55. is_verified = fields.Boolean('已核验')
  56. line_ids=fields.One2many('cn.account.invoice.line', 'order_id', '发票明细行',
  57. copy=False)
  58. attachment_number = fields.Integer(compute='_compute_attachment_number', string='附件号')
  59. note = fields.Text(u"备注")
  60. color = fields.Integer('颜色', related='invoice_type.color')
  61. _sql_constraints = [
  62. ('unique_invoice_code_name', 'unique (invoice_code, name)', '发票代码+发票号码不能相同!'),
  63. ]
  64. @api.onchange('invoice_type')
  65. def _use_heck_code(self):
  66. for order in self:
  67. if order.invoice_type and order.invoice_type.code in ['pp', 'dzfp']:
  68. order.use_heck_code = True
  69. else:
  70. order.use_heck_code = False
  71. def action_get_attachment_view(self):
  72. res = self.env['ir.actions.act_window']._for_xml_id('base.action_attachment')
  73. res['domain'] = [('res_model', '=', 'cn.account.invoice'), ('res_id', 'in', self.ids)]
  74. res['context'] = {'default_res_model': 'cn.account.invoice', 'default_res_id': self.id}
  75. return res
  76. def _compute_attachment_number(self):
  77. attachment_data = self.env['ir.attachment'].read_group(
  78. [('res_model', '=', 'cn.account.invoice'), ('res_id', 'in', self.ids)], ['res_id'], ['res_id'])
  79. attachment = dict((data['res_id'], data['res_id_count']) for data in attachment_data)
  80. for expense in self:
  81. expense.attachment_number = attachment.get(expense.id, 0)
  82. def create_uom(self):
  83. for mx in self.line_ids:
  84. uom = mx.product_unit
  85. if uom:
  86. uom_id = self.env['uom'].search([('name', '=', uom)])
  87. if not uom_id:
  88. uom_id = self.env['uom'].create({
  89. 'name': uom,
  90. 'active': 1})
  91. def create_category(self):
  92. for mx in self.line_ids:
  93. category = mx.tax_type
  94. if category:
  95. category_id = self.env['core.category'].search([
  96. '&', ('type', '=', 'goods'), ('tax_category_id.print_name', '=', category)])
  97. if not category_id:
  98. if self.type == 'in':
  99. account_id = self.env['ir.values'].get_default('tax.config.settings', 'default_buy_goods_account')
  100. if self.type == 'out':
  101. account_id = self.env['ir.values'].get_default('tax.config.settings',
  102. 'default_sell_goods_account')
  103. category_id = self.env['core.category'].create({
  104. 'type': 'goods',
  105. 'name': category,
  106. 'account_id': account_id,
  107. 'tax_category_id': self.env['tax.category'].search([('print_name', '=', category)],limit=1).id,
  108. 'note': '由系统自动增加'
  109. })
  110. def create_product(self):
  111. for mx in self.line_ids:
  112. goods = mx.product_name
  113. uom = mx.product_unit
  114. uom_id = self.env['uom'].search([('name', '=', uom)])
  115. category = mx.tax_type
  116. category_id = self.env['core.category'].search([
  117. '&', ('type', '=', 'goods'), ('tax_category_id.print_name', '=', category)])
  118. if category_id and category_id.tax_category_id.code[0] == '1':
  119. no_stock = False
  120. else:
  121. no_stock = True
  122. if goods:
  123. goods_id = self.env['goods'].search([('name', '=', goods)])
  124. if not goods_id:
  125. self.env['goods'].create({
  126. 'name': goods,
  127. 'uom_id': uom_id.id or '',
  128. 'uos_id': uom_id.id or '',
  129. # 'tax_rate': float(in_xls_data.get('税率')),
  130. 'category_id': category_id and category_id.id,
  131. 'computer_import': True,
  132. 'no_stock': no_stock,
  133. 'cost_method': 'average',
  134. })
  135. # 创建供应商
  136. def create_buy_partner(self):
  137. if self.partner_code_in:
  138. partner_id = self.env['partner'].search([
  139. ('tax_num', '=', self.partner_code_in)])
  140. if self.partner_name_in:
  141. partner_id = self.env['partner'].search([
  142. ('name', '=', self.partner_name_in)])
  143. default_goods_supplier = self.env['ir.values'].get_default('tax.config.settings', 'default_goods_supplier')
  144. if not default_goods_supplier:
  145. raise UserError('请设置默认产品供应商!')
  146. if not partner_id:
  147. partner_id = self.env['partner'].create({
  148. 'name': self.partner_name_in,
  149. 'main_mobile': self.partner_code_in,
  150. 'tax_num': self.partner_code_in,
  151. 's_category_id': default_goods_supplier,
  152. 'computer_import': True,
  153. })
  154. # 补银行帐号等信息
  155. if self.partner_address_in and partner_id.main_mobile == partner_id.tax_num:
  156. main_mobile = self.split_number(self.partner_address_in)
  157. partner_id.write({'main_mobile': main_mobile})
  158. if self.partner_address_in and not partner_id.main_address:
  159. if partner_id.main_mobile and partner_id.main_mobile != partner_id.tax_num:
  160. to_del_mobile = len(partner_id.main_mobile)
  161. else:
  162. to_del_mobile = 0
  163. main_address = self.partner_address_in[:-to_del_mobile]
  164. partner_id.write({'main_address': main_address})
  165. if self.partner_bank_number_in and not partner_id.bank_num:
  166. bank_number = self.split_number(self.partner_bank_number_in)
  167. partner_id.write({'bank_num': bank_number})
  168. if self.partner_bank_number_in and not (partner_id.bank_num or partner_id.bank_name):
  169. if self.bank_num:
  170. to_del_bank_number = len(self.bank_num)
  171. else:
  172. to_del_bank_number = 0
  173. bank_name = self.partner_bank_number_in[:-to_del_bank_number]
  174. partner_id.write({'bank_name': bank_name})
  175. # 创建客户
  176. def create_sell_partner(self):
  177. if self.partner_code_out:
  178. partner_id = self.env['partner'].search([
  179. ('tax_num', '=', self.partner_code_out)])
  180. elif self.partner_name_out:
  181. partner_id = self.env['partner'].search([
  182. ('name', '=', self.partner_name_out)])
  183. default_customer = self.env['ir.values'].get_default('tax.config.settings', 'default_customer')
  184. if not default_customer:
  185. raise UserError('请设置默认产品供应商!')
  186. if not partner_id:
  187. partner_id = self.env['partner'].create({
  188. 'name': self.partner_name_out,
  189. 'main_mobile': self.partner_code_out,
  190. 'tax_num': self.partner_code_out,
  191. 'c_category_id':default_customer,
  192. 'computer_import': True,
  193. })
  194. # 补银行帐号等信息
  195. if self.partner_address_out and partner_id.main_mobile == partner_id.tax_num:
  196. main_mobile = self.split_number(self.partner_address_out)
  197. partner_id.write({'main_mobile': main_mobile})
  198. if self.partner_address_out and not partner_id.main_address:
  199. if partner_id.main_mobile and partner_id.main_mobile != partner_id.tax_num:
  200. to_del_mobile = len(partner_id.main_mobile)
  201. else:
  202. to_del_mobile = 0
  203. main_address = self.partner_address_out[:-to_del_mobile]
  204. partner_id.write({'main_address': main_address})
  205. if self.partner_bank_number_out and not partner_id.bank_num:
  206. bank_number = self.split_number(self.partner_bank_number_out)
  207. partner_id.write({'bank_num': bank_number})
  208. if self.partner_bank_number_out and not partner_id.bank_name:
  209. if partner_id.bank_num:
  210. to_del_bank_number = len(partner_id.bank_num)
  211. else:
  212. to_del_bank_number = 0
  213. bank_name = self.partner_bank_number_out[:-to_del_bank_number]
  214. partner_id.write({'bank_name': bank_name})
  215. # 跟据帐号和电话都在后面的特性,使用倒转后从头直至有字母出现为此都是帐号和电话。
  216. def split_number(self, str):
  217. str1 = str[::-1] #
  218. changdu = len(str1) # 取长度
  219. num = ''
  220. i = 0
  221. while i < changdu:
  222. if str1[i].isdigit() or str1[i] == '-' or str1[i] == ' ':
  223. num += str1[i]
  224. i += 1
  225. else:
  226. return num[::-1]
  227. #定义发票明细行
  228. class cn_account_invoice_line(models.Model):
  229. _name = 'cn.account.invoice.line'
  230. _description = '中国发票明细'
  231. _rec_name='product_name'
  232. order_id = fields.Many2one('cn.account.invoice', '发票',help='关联发票',copy=False)
  233. product_name = fields.Char(u"货物名称",copy=False)
  234. product_type = fields.Char(u"规格型号",copy=False)
  235. product_unit = fields.Char(u"单位",copy=False)
  236. product_count = fields.Float(u"数量",copy=False)
  237. product_price = fields.Float(u"价格",copy=False)
  238. product_amount = fields.Float(u"金额",copy=False)
  239. product_tax_rate = fields.Integer(u"税率",copy=False)
  240. product_tax = fields.Float(u"税额",copy=False)
  241. tax_type = fields.Char('税收分类编码',help='20170101以后使用的税收分类编码,这个很重要',copy=False)
  242. note = fields.Char(u"备注",copy=False)
  243. class create_cn_invoice_wizard(models.TransientModel):
  244. _name = 'create.cn.invoice.wizard'
  245. _description = '导入发票'
  246. excel = fields.Binary(u'导入认证系统导出的excel文件',)
  247. type = fields.Selection([('in', '进项发票'),
  248. ('out', '销项发票'),], '进/出发票', copy=False)
  249. company_id = fields.Many2one(
  250. 'res.company',
  251. string='公司',
  252. change_default=True)
  253. def create_tax_invoice(self):
  254. not_input, is_input = self.create_invoice()
  255. self.create_invoice_line(not_input)
  256. return {
  257. 'name': _('导入发票'),
  258. 'view_mode': 'tree,form',
  259. 'domain': [('id', 'in', is_input)],
  260. 'res_model': 'cn.account.invoice',
  261. 'type': 'ir.actions.act_window',
  262. 'context': {'create': False, 'active_test': False},
  263. }
  264. def create_invoice_line(self,not_input):
  265. xls_data = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel))
  266. all = xls_data.sheets()[1]
  267. ncows = all.nrows
  268. ncols = 0
  269. colnames = all.row_values(1)
  270. list = []
  271. for rownum in range(2, ncows):
  272. row = all.row_values(rownum)
  273. if row:
  274. app = {}
  275. for i in range(len(colnames)):
  276. app[colnames[i]] = row[i]
  277. list.append(app)
  278. ncols += 1
  279. in_xls_data = {}
  280. for data in range(0, ncols):
  281. in_xls_data = list[data]
  282. product_name = in_xls_data.get('货物或应税劳务名称') or in_xls_data.get('货物或应税劳务、服务名称')
  283. invoice_name = in_xls_data.get('发票号码')
  284. if product_name == "(详见销货清单)" or product_name == '详见对应正数发票及清单' or product_name == "(详见销货清单)":
  285. continue
  286. if str(invoice_name) in not_input:
  287. continue
  288. invoice_code = in_xls_data.get(u'发票代码')
  289. company_in_sys_invoice = self.env['cn.account.invoice'].search([
  290. ('invoice_code', '=', str(invoice_code)),
  291. ('name', '=', str(invoice_name)),
  292. ('type', '=', self.type)])
  293. amount = float(in_xls_data.get('金额'))
  294. tax = float(in_xls_data.get('税额'))
  295. tax_rate = 0
  296. if tax:
  297. tax_rate = round(tax / amount, 2) * 100
  298. if product_name:
  299. goods_name = product_name.split('*')[-1]
  300. if '*' in product_name:
  301. tax_type = product_name.split('*')[1]
  302. else:
  303. goods_name = product_name
  304. tax_type = ''
  305. if company_in_sys_invoice:
  306. self.env['cn.account.invoice.line'].create({
  307. 'order_id': company_in_sys_invoice.id,
  308. 'product_name': goods_name.strip() or '',
  309. 'product_type': in_xls_data.get('规格型号').strip() or '',
  310. 'product_unit': in_xls_data.get('单位').strip() or '',
  311. 'product_count': in_xls_data.get('数量') or '',
  312. 'product_price': in_xls_data.get('单价') or '',
  313. 'product_amount': amount or '0',
  314. 'product_tax_rate': tax_rate or '0',
  315. 'product_tax': tax or '0',
  316. 'tax_type': tax_type,
  317. })
  318. company_in_sys_invoice.is_verified = True
  319. def create_invoice(self):
  320. not_input = is_input = []
  321. xls_data = xlrd.open_workbook(file_contents=base64.decodebytes(self.excel))
  322. all = xls_data.sheets()[0]
  323. ncows = all.nrows
  324. ncols = 0
  325. colnames = all.row_values(1)
  326. list = []
  327. top = all.row_values(0)
  328. invoice_type = self.env['cn.invoice.type'].search([('name', '=', top[0])], limit=1)
  329. # 数据读入,过滤没有开票日期的行
  330. for rownum in range(2, ncows):
  331. row = all.row_values(rownum)
  332. if row:
  333. app = {}
  334. for i in range(len(colnames)):
  335. app[colnames[i]] = row[i]
  336. if app['发票状态'] == '正常':
  337. list.append(app)
  338. ncols += 1
  339. # 数据处理
  340. in_xls_data = {}
  341. for data in range(0, ncols):
  342. in_xls_data = list[data]
  343. invoice_code = str(int(in_xls_data.get('发票代码'))).zfill(len(in_xls_data.get('发票代码')))
  344. if self.type == "in":
  345. partner_name = in_xls_data.get('销售方名称')
  346. company_tax = in_xls_data.get('购买方税号')
  347. else:
  348. partner_name = in_xls_data.get('购买方名称')
  349. company_tax = in_xls_data.get('销售方税号')
  350. if company_tax != self.company_id.vat:
  351. raise UserError('购买/销售方税号与公司税号不一致!请检查!')
  352. invoice_name= str(int(in_xls_data.get('发票号码'))).zfill(len(in_xls_data.get('发票号码')))
  353. company_in_sys_invoice = self.env['cn.account.invoice'].search([
  354. ('invoice_code', '=', invoice_code),
  355. ('name', '=', invoice_name),
  356. ('type', '=', self.type)])
  357. amount = float(in_xls_data.get('金额'))
  358. tax = float(in_xls_data.get('税额'))
  359. if self.company_id and self.company_id.vat:
  360. if self.company_id.vat != company_tax:
  361. raise UserError('文件购买方税号为(%s)与公司税号(%s)不一致!'%(company_tax,self.company_id.vat))
  362. tax_rate = 0.0
  363. if tax:
  364. tax_rate = round(tax / amount,2) * 100
  365. if company_in_sys_invoice:
  366. not_input.append(invoice_name)
  367. continue
  368. else:
  369. note = in_xls_data.get(u'备注')
  370. if self.type == 'in':
  371. invoice_id = self.env['cn.account.invoice'].create({
  372. 'type': self.type,
  373. 'partner_name_in': partner_name,
  374. 'partner_code_in': str(in_xls_data.get(u'销售方税号')),
  375. 'invoice_code': str(invoice_code),
  376. 'name': str(invoice_name),
  377. 'invoice_amount': amount,
  378. 'invoice_tax': tax,
  379. 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
  380. 'invoice_type': invoice_type.id,
  381. 'tax_rate': tax_rate,
  382. 'is_verified': True,
  383. 'note': note,
  384. })
  385. else:
  386. invoice_id = self.env['cn.account.invoice'].create({
  387. 'type': self.type,
  388. 'partner_name_out': partner_name,
  389. 'partner_code_out': str(in_xls_data.get(u'购买方税号')),
  390. 'invoice_code': str(invoice_code),
  391. 'name': str(invoice_name),
  392. 'invoice_amount': amount,
  393. 'invoice_tax': tax,
  394. 'invoice_date': self.excel_date(in_xls_data.get(u'开票日期')),
  395. 'invoice_type': invoice_type.id,
  396. 'invoice_in_id': '',
  397. 'tax_rate': tax_rate,
  398. 'is_verified': True,
  399. 'note': note,
  400. })
  401. is_out = re.findall('出口业务', in_xls_data.get(u'备注'))
  402. if is_out:
  403. for s in note.split(';'):
  404. invoice_export_amount = re.search(r'\d+(\.\d+)', s)
  405. invoice_id.write({
  406. 'is_export': True,
  407. 'invoice_export_amount': invoice_export_amount.group() or 0.0,
  408. })
  409. is_input.append(invoice_id.id)
  410. return not_input, is_input
  411. def excel_date(self, data):
  412. # 将excel日期改为正常日期
  413. if type(data) in (int, float):
  414. year, month, day, hour, minute, second = xlrd.xldate_as_tuple(data, 0)
  415. py_date = datetime.datetime(year, month, day, hour, minute, second)
  416. else:
  417. py_date = data
  418. return py_date
上海开阖软件有限公司 沪ICP备12045867号-1