diff --git a/account_invoice_import/__openerp__.py b/account_invoice_import/__openerp__.py
index f95987c980..b7755a8ccc 100644
--- a/account_invoice_import/__openerp__.py
+++ b/account_invoice_import/__openerp__.py
@@ -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',
diff --git a/account_invoice_import/wizard/account_invoice_import.py b/account_invoice_import/wizard/account_invoice_import.py
index 291a47dd0f..67790b8df2 100644
--- a/account_invoice_import/wizard/account_invoice_import.py
+++ b/account_invoice_import/wizard/account_invoice_import.py
@@ -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(
@@ -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,
@@ -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'
@@ -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()
@@ -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({
diff --git a/base_business_document_import/README.rst b/base_business_document_import/README.rst
new file mode 100644
index 0000000000..170cc01ea7
--- /dev/null
+++ b/base_business_document_import/README.rst
@@ -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 <alexis.delattre@akretion.com>
+
+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.
diff --git a/base_business_document_import/__init__.py b/base_business_document_import/__init__.py
new file mode 100644
index 0000000000..cde864bae2
--- /dev/null
+++ b/base_business_document_import/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
diff --git a/base_business_document_import/__openerp__.py b/base_business_document_import/__openerp__.py
new file mode 100644
index 0000000000..80e1fff5b2
--- /dev/null
+++ b/base_business_document_import/__openerp__.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+# © 2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
+# 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,
+}
diff --git a/base_business_document_import/models/__init__.py b/base_business_document_import/models/__init__.py
new file mode 100644
index 0000000000..4a3c2e6a58
--- /dev/null
+++ b/base_business_document_import/models/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import business_document_import
diff --git a/base_business_document_import/models/business_document_import.py b/base_business_document_import/models/business_document_import.py
new file mode 100644
index 0000000000..40fe999c67
--- /dev/null
+++ b/base_business_document_import/models/business_document_import.py
@@ -0,0 +1,125 @@
+# -*- coding: utf-8 -*-
+# © 2015-2016 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
+# 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