Skip to content

Commit

Permalink
Add module base_business_document_import and start to move some code …
Browse files Browse the repository at this point in the history
…to it.
  • Loading branch information
alexis-via authored and adrienpeiffer committed Oct 3, 2016
1 parent 8077c88 commit 0ca09d5
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 114 deletions.
2 changes: 1 addition & 1 deletion account_invoice_import/__openerp__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
'summary': 'Import supplier invoices/refunds as PDF or XML files',
'author': 'Akretion,Odoo Community Association (OCA)',
'website': 'http://www.akretion.com',
'depends': ['account', 'base_iban', 'base_vat_sanitized'],
'depends': ['account', 'base_iban', 'base_business_document_import'],
'external_dependencies': {'python': ['lxml']},
'data': [
'security/ir.model.access.csv',
Expand Down
118 changes: 5 additions & 113 deletions account_invoice_import/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

class AccountInvoiceImport(models.TransientModel):
_name = 'account.invoice.import'
_inherit = ['business.document.import']
_description = 'Wizard to import supplier invoices/refunds'

invoice_file = fields.Binary(
Expand Down Expand Up @@ -101,53 +102,14 @@ def fallback_parse_pdf_invoice(self, file_data):
# }],
# }

@api.model
def _select_partner(self, parsed_inv, partner_type='supplier'):
if parsed_inv.get('partner'):
return parsed_inv['partner']
if parsed_inv.get('partner_vat'):
vat = parsed_inv['partner_vat'].replace(' ', '').upper()
# use base_vat_sanitized
partners = self.env['res.partner'].search([
(partner_type, '=', True),
('parent_id', '=', False),
('sanitized_vat', '=', vat)])
if partners:
return partners[0]
else:
# TODO: update error msg
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"partner VAT number. But there are no supplier "
"with this VAT number in Odoo.") % vat)
if parsed_inv.get('partner_email'):
partners = self.env['res.partner'].search([
('email', '=ilike', parsed_inv['partner_email']),
(partner_type, '=', True)])
if partners:
return partners[0].commercial_partner_id
if parsed_inv.get('partner_name'):
partners = self.env['res.partner'].search([
('name', '=ilike', parsed_inv['partner_name']),
('is_company', '=', True),
(partner_type, '=', True)])
if partners:
return partners[0]
raise UserError(_(
"Invoice parsing didn't return the VAT number of the "
"supplier. In this case, invoice parsing should return the "
"email or the name of the partner, but it was not returned "
"or it was returned but it didn't match any "
"existing supplier."))

@api.model
def _prepare_create_invoice_vals(self, parsed_inv):
aio = self.env['account.invoice']
ailo = self.env['account.invoice.line']
company = self.env.user.company_id
assert parsed_inv.get('amount_total'), 'Missing amount_total'
partner = self._select_partner(parsed_inv)
currency = self._get_currency(parsed_inv)
partner = self._match_partner(parsed_inv)
currency = self._match_currency(parsed_inv)
vals = {
'partner_id': partner.id,
'currency_id': currency.id,
Expand Down Expand Up @@ -274,48 +236,6 @@ def _prepare_create_invoice_vals(self, parsed_inv):
line_dict['account_analytic_id'] = aacount_id
return vals

@api.model
def _match_product(self, parsed_line, partner=False):
"""This method is designed to be inherited"""
ppo = self.env['product.product']
if parsed_line.get('product'):
return parsed_line['product']
if parsed_line.get('product_ean13'):
# Don't filter on purchase_ok = 1 because we don't depend
# on the purchase module
products = ppo.search([
('ean13', '=', parsed_line['product_ean13'])])
if products:
return products[0]
if parsed_line.get('product_code'):
# Should probably be modified to match via the supplier code
products = ppo.search(
[('default_code', '=', parsed_line['product_code'])])
if products:
return products[0]
# WARNING: Won't work for multi-variant products
# because product.supplierinfo is attached to product template
if partner:
sinfo = self.env['product.supplierinfo'].search([
('name', '=', partner.id),
('product_code', '=', parsed_line['product_code']),
])
if (
sinfo and
sinfo[0].product_tmpl_id.product_variant_ids and
len(
sinfo[0].product_tmpl_id.product_variant_ids) == 1
):
return sinfo[0].product_tmpl_id.product_variant_ids[0]
raise UserError(_(
"Could not find any corresponding product in the Odoo database "
"with EAN13 '%s' or Default Code '%s' or "
"Supplier Product Code '%s' with supplier '%s'.") % (
parsed_line.get('product_ean13'),
parsed_line.get('product_code'),
parsed_line.get('product_code'),
partner and partner.name or 'None'))

@api.model
def set_1line_price_unit_and_quantity(self, il_vals, parsed_inv):
"""For the moment, we only take into account the 'price_include'
Expand All @@ -341,34 +261,6 @@ def set_1line_start_end_dates(self, il_vals, parsed_inv):
il_vals['start_date'] = parsed_inv.get('date_start')
il_vals['end_date'] = parsed_inv.get('date_end')

@api.model
def _get_currency(self, parsed_inv):
if parsed_inv.get('currency'):
return parsed_inv['currency']
if parsed_inv.get('currency_iso'):
currency_iso = parsed_inv['currency_iso'].upper()
currencies = self.env['res.currency'].search(
[('name', '=', currency_iso)])
if currencies:
return currencies[0]
else:
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"the currency ISO code. But there are no currency "
"with that name in Odoo.") % currency_iso)
if parsed_inv.get('currency_symbol'):
cur_symbol = parsed_inv['currency_symbol']
currencies = self.env['res.currency'].search(
[('symbol', '=', cur_symbol)])
if currencies:
return currencies[0]
else:
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"the currency symbol. But there are no currency "
"with that symbol in Odoo.") % cur_symbol)
return self.env.user.company_id.currency_id

@api.multi
def parse_invoice(self):
self.ensure_one()
Expand Down Expand Up @@ -441,8 +333,8 @@ def import_invoice(self):
aio = self.env['account.invoice']
iaao = self.env['ir.actions.act_window']
parsed_inv = self.parse_invoice()
partner = self._select_partner(parsed_inv)
currency = self._get_currency(parsed_inv)
partner = self._match_partner(parsed_inv)
currency = self._match_currency(parsed_inv)
parsed_inv['partner'] = partner
parsed_inv['currency'] = currency
self.write({
Expand Down
56 changes: 56 additions & 0 deletions base_business_document_import/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

=============================
Base Business Document Import
=============================

This is a technical module ; it doesn't bring any useful feature by itself. This module is the base modules for 2 other modules :

* *account_invoice_import* which imports supplier invoices as PDF or XML files (this module also requires some additionnal modules such as *account_invoice_import_invoice2data*, *account_invoice_import_ubl*, etc... to support specific invoice formats),

* *sale_invoice_import* which imports sale orders as CSV, XML or PDF files (this module also requires some additionnal modules such as *sale_invoice_import_csv* or *sale_invoice_import_ubl* to support specific order formats)

Configuration
=============

No configuration needed.

Usage
=====

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/95/8.0

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/account-invoicing/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed feedback.

Credits
=======

Contributors
------------

* Alexis de Lattre <[email protected]>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit https://odoo-community.org.
3 changes: 3 additions & 0 deletions base_business_document_import/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import models
17 changes: 17 additions & 0 deletions base_business_document_import/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# © 2016 Akretion (Alexis de Lattre <[email protected]>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
'name': 'Base Business Document Import',
'version': '8.0.1.0.0',
'category': 'Hidden',
'license': 'AGPL-3',
'summary': 'Provides technical tools to import sale orders or supplier '
'invoices',
'author': 'Akretion,Odoo Community Association (OCA)',
'website': 'http://www.akretion.com',
'depends': ['product', 'base_vat_sanitized'],
'external_dependencies': {'python': ['PyPDF2']},
'installable': True,
}
3 changes: 3 additions & 0 deletions base_business_document_import/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-

from . import business_document_import
125 changes: 125 additions & 0 deletions base_business_document_import/models/business_document_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
# © 2015-2016 Akretion (Alexis de Lattre <[email protected]>)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from openerp import models, api, _
from openerp.exceptions import Warning as UserError
import logging

logger = logging.getLogger(__name__)


class BusinessDocumentImport(models.AbstractModel):
_name = 'business.document.import'
_description = 'Common methods to import business documents'

@api.model
def _match_partner(self, parsed_inv, partner_type='supplier'):
if parsed_inv.get('partner'):
return parsed_inv['partner']
if parsed_inv.get('partner_vat'):
vat = parsed_inv['partner_vat'].replace(' ', '').upper()
# use base_vat_sanitized
partners = self.env['res.partner'].search([
(partner_type, '=', True),
('parent_id', '=', False),
('sanitized_vat', '=', vat)])
if partners:
return partners[0]
else:
# TODO: update error msg
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"partner VAT number. But there are no supplier "
"with this VAT number in Odoo.") % vat)
if parsed_inv.get('partner_email'):
partners = self.env['res.partner'].search([
('email', '=ilike', parsed_inv['partner_email']),
(partner_type, '=', True)])
if partners:
return partners[0].commercial_partner_id
if parsed_inv.get('partner_name'):
partners = self.env['res.partner'].search([
('name', '=ilike', parsed_inv['partner_name']),
('is_company', '=', True),
(partner_type, '=', True)])
if partners:
return partners[0]
raise UserError(_(
"Invoice parsing didn't return the VAT number of the "
"partner. In this case, invoice parsing should return the "
"email or the name of the partner, but it was not returned "
"or it was returned but it didn't match any "
"existing partner."))
# TODO : now that we use it for sale order, we may not want to
# always return a parent partner

@api.model
def _match_product(self, parsed_line, partner=False):
"""This method is designed to be inherited"""
ppo = self.env['product.product']
if parsed_line.get('product'):
return parsed_line['product']
if parsed_line.get('product_ean13'):
# Don't filter on purchase_ok = 1 because we don't depend
# on the purchase module
products = ppo.search([
('ean13', '=', parsed_line['product_ean13'])])
if products:
return products[0]
if parsed_line.get('product_code'):
# Should probably be modified to match via the supplier code
products = ppo.search(
[('default_code', '=', parsed_line['product_code'])])
if products:
return products[0]
# WARNING: Won't work for multi-variant products
# because product.supplierinfo is attached to product template
if partner:
sinfo = self.env['product.supplierinfo'].search([
('name', '=', partner.id),
('product_code', '=', parsed_line['product_code']),
])
if (
sinfo and
sinfo[0].product_tmpl_id.product_variant_ids and
len(
sinfo[0].product_tmpl_id.product_variant_ids) == 1
):
return sinfo[0].product_tmpl_id.product_variant_ids[0]
raise UserError(_(
"Could not find any corresponding product in the Odoo database "
"with EAN13 '%s' or Default Code '%s' or "
"Supplier Product Code '%s' with supplier '%s'.") % (
parsed_line.get('product_ean13'),
parsed_line.get('product_code'),
parsed_line.get('product_code'),
partner and partner.name or 'None'))

@api.model
def _match_currency(self, parsed_inv):
if parsed_inv.get('currency'):
return parsed_inv['currency']
if parsed_inv.get('currency_iso'):
currency_iso = parsed_inv['currency_iso'].upper()
currencies = self.env['res.currency'].search(
[('name', '=', currency_iso)])
if currencies:
return currencies[0]
else:
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"the currency ISO code. But there are no currency "
"with that name in Odoo.") % currency_iso)
if parsed_inv.get('currency_symbol'):
cur_symbol = parsed_inv['currency_symbol']
currencies = self.env['res.currency'].search(
[('symbol', '=', cur_symbol)])
if currencies:
return currencies[0]
else:
raise UserError(_(
"The analysis of the invoice returned '%s' as "
"the currency symbol. But there are no currency "
"with that symbol in Odoo.") % cur_symbol)
return self.env.user.company_id.currency_id

0 comments on commit 0ca09d5

Please sign in to comment.