Skip to content

Commit

Permalink
Check the VAT number of the destination partner, to make sure the bus…
Browse files Browse the repository at this point in the history
…iness document is imported in the right company
  • Loading branch information
alexis-via authored and Alexis de Lattre committed Sep 16, 2018
1 parent c2c17ef commit 486b974
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 9 deletions.
24 changes: 20 additions & 4 deletions account_invoice_import/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# © 2015-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2015-2018 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models, fields, api, _
from odoo import api, fields, models, _
import odoo.addons.decimal_precision as dp
from odoo.tools import float_compare, float_round, float_is_zero
from odoo.tools import float_compare, float_round, float_is_zero, config
from odoo.exceptions import UserError
from lxml import etree
import logging
Expand Down Expand Up @@ -119,7 +120,10 @@ def fallback_parse_pdf_invoice(self, file_data):
# 'email': 'support@browserstack.com',
# 'name': 'Capitaine Train',
# },
# 'partner': res.partner recordset,
# 'company': {'vat': 'FR12123456789'}, # Rarely set in invoices
# # Only used to check we are not
# # importing the invoice in the
# # wrong company by mistake
# 'invoice_number': 'I1501243',
# 'description': 'TGV Paris-Lyon',
# 'attachments': {'file1.pdf': base64data1, 'file2.pdf': base64data2},
Expand Down Expand Up @@ -457,6 +461,18 @@ def pre_process_parsed_inv(self, parsed_inv):
line['price_unit'] = float_round(
line['price_unit'], precision_digits=prec_pp)
logger.debug('Result of invoice parsing parsed_inv=%s', parsed_inv)
# the 'company' dict in parsed_inv is NOT used to auto-detect
# the company, but to check that we are not importing an
# invoice for another company by mistake
# The advantage of doing the check here is that it will be run
# in all scenarios (create/update/...), but it's not related
# to invoice parsing...
if (
parsed_inv.get('company') and
not config['test_enable'] and
not self._context.get('edi_skip_company_check')):
self.env['business.document.import']._check_company(
parsed_inv['company'], parsed_inv['chatter_msg'])
return parsed_inv

@api.model
Expand Down
12 changes: 12 additions & 0 deletions account_invoice_import_factur-x/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ def prepare_facturx_xpath_dict(self):
"/ram:URIID", # ZUGFeRD
],
},
'company': {
'vat': [
"//ram:ApplicableHeaderTradeAgreement"
"/ram:BuyerTradeParty"
"/ram:SpecifiedTaxRegistration"
"/ram:ID[@schemeID='VA']", # Factur-X
"//ram:ApplicableSupplyChainTradeAgreement"
"/ram:BuyerTradeParty"
"/ram:SpecifiedTaxRegistration"
"/ram:ID[@schemeID='VA']", # ZUGFeRD
],
},
'invoice_number': [
'//rsm:ExchangedDocument/ram:ID', # Factur-X
'//rsm:HeaderExchangedDocument/ram:ID', # ZUGFeRD
Expand Down
10 changes: 10 additions & 0 deletions account_invoice_import_ubl/wizard/account_invoice_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,15 @@ def parse_ubl_invoice(self, xml_root):
namespaces=namespaces)
supplier_dict = self.ubl_parse_supplier_party(
supplier_xpath[0], namespaces)
customer_xpath_party = xml_root.xpath(
'/inv:Invoice/cac:AccountingCustomerParty/cac:Party',
namespaces=namespaces)
company_dict_full = self.ubl_parse_party(
customer_xpath_party[0], namespaces)
company_dict = {}
# We only take the "official references" for company_dict
if company_dict_full.get('vat'):
company_dict = {'vat': company_dict_full['vat']}
date_xpath = xml_root.xpath(
'/inv:Invoice/cbc:IssueDate', namespaces=namespaces)
date_dt = datetime.strptime(date_xpath[0].text, '%Y-%m-%d')
Expand Down Expand Up @@ -236,6 +245,7 @@ def parse_ubl_invoice(self, xml_root):
res = {
'type': inv_type,
'partner': supplier_dict,
'company': company_dict,
'invoice_number': inv_number_xpath[0].text,
'origin': origin,
'date': fields.Date.to_string(date_dt),
Expand Down
27 changes: 27 additions & 0 deletions base_business_document_import/models/business_document_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,33 @@ def _match_journal(self, journal_dict, chatter_msg, speed_dict=None):
"following information extracted from the business document: "
"Journal code: %s") % journal_dict.get('code'))

@api.model
def _check_company(self, company_dict, chatter_msg):
if not company_dict:
company_dict = {}
rco = self.env['res.company']
if self._context.get('force_company'):
company = rco.browse(self._context['force_company'])
else:
company = self.env.user.company_id
if company_dict.get('vat'):
parsed_company_vat = company_dict['vat'].replace(
' ', '').upper()
if company.partner_id.sanitized_vat:
if company.partner_id.sanitized_vat != parsed_company_vat:
raise self.user_error_wrap(_(
"The VAT number of the customer written in the "
"business document (%s) doesn't match the VAT number "
"of the company '%s' (%s) in which you are trying to "
"import this document.") % (
parsed_company_vat,
company.display_name,
company.partner_id.sanitized_vat))
else:
chatter_msg.append(_(
"Missing VAT number on company '%s'")
% company.display_name)

def get_xml_files_from_pdf(self, pdf_file):
"""Returns a dict with key = filename, value = XML file obj"""
logger.info('Trying to find an embedded XML file inside PDF')
Expand Down
16 changes: 13 additions & 3 deletions purchase_order_import/wizard/purchase_order_import.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
# © 2016-2017 Akretion (Alexis de Lattre <alexis.delattre@akretion.com>)
# Copyright 2016-2018 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models, fields, api, _
from odoo.tools import float_is_zero
from odoo import api, fields, models, _
from odoo.tools import float_is_zero, config
from odoo.exceptions import UserError
import logging
import mimetypes
Expand Down Expand Up @@ -69,6 +70,9 @@ def parse_pdf_quote(self, quote_file):
# 'name': 'Camptocamp',
# 'email': 'luc@camptocamp.com',
# },
# 'company': {'vat': 'FR12123456789'}, # Only used to check we are not
# # importing the quote in the
# # wrong company by mistake
# 'currency': {'iso': 'EUR', 'symbol': u'€'},
# 'incoterm': 'EXW',
# 'note': 'some notes',
Expand Down Expand Up @@ -113,6 +117,12 @@ def parse_quote(self, quote_file, quote_filename):
quote_file.encode('base64')
if 'chatter_msg' not in parsed_quote:
parsed_quote['chatter_msg'] = []
if (
parsed_quote.get('company') and
not config['test_enable'] and
not self._context.get('edi_skip_company_check')):
self.env['business.document.import']._check_company(
parsed_quote['company'], parsed_quote['chatter_msg'])
return parsed_quote

@api.model
Expand Down
8 changes: 8 additions & 0 deletions purchase_order_import_ubl/wizard/purchase_order_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ def parse_ubl_quote(self, xml_root):
supplier_xpath = xml_root.xpath(
'/main:Quotation/cac:SellerSupplierParty', namespaces=ns)
supplier_dict = self.ubl_parse_supplier_party(supplier_xpath[0], ns)
customer_xpath_party = xml_root.xpath(
'/main:Quotation/cac:BuyerCustomerParty/cac:Party', namespaces=ns)
company_dict_full = self.ubl_parse_party(customer_xpath_party[0], ns)
company_dict = {}
# We only take the "official references" for company_dict
if company_dict_full.get('vat'):
company_dict = {'vat': company_dict_full['vat']}
delivery_term_xpath = xml_root.xpath(
"/main:Quotation/cac:DeliveryTerms", namespaces=ns)
if delivery_term_xpath:
Expand All @@ -86,6 +93,7 @@ def parse_ubl_quote(self, xml_root):
# TODO : add charges
res = {
'partner': supplier_dict,
'company': company_dict,
'currency': {'iso': currency_code},
'date': date_xpath[0].text,
'incoterm': incoterm_dict,
Expand Down
13 changes: 11 additions & 2 deletions sale_order_import/wizard/sale_order_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models, fields, api, _
from odoo.tools import float_compare, float_is_zero
from odoo.tools import float_compare, float_is_zero, config
from odoo.exceptions import UserError
import logging
import mimetypes
Expand Down Expand Up @@ -132,7 +132,10 @@ def parse_pdf_order(self, order_file, detect_doc_type=False):
# 'country_code': 'FR',
# 'state_code': False,
# 'zip': False,
# }
# },
# 'company': {'vat': 'FR12123456789'}, # Only used to check we are not
# # importing the order in the
# # wrong company by mistake
# 'date': '2016-08-16', # order date
# 'order_ref': 'PO1242', # Customer PO number
# 'currency': {'iso': 'EUR', 'symbol': u'€'},
Expand Down Expand Up @@ -258,6 +261,12 @@ def parse_order(self, order_file, order_filename, partner=False):
order_file.encode('base64')
if 'chatter_msg' not in parsed_order:
parsed_order['chatter_msg'] = []
if (
parsed_order.get('company') and
not config['test_enable'] and
not self._context.get('edi_skip_company_check')):
self.env['business.document.import']._check_company(
parsed_order['company'], parsed_order['chatter_msg'])
return parsed_order

def import_order_button(self):
Expand Down
8 changes: 8 additions & 0 deletions sale_order_import_ubl/wizard/sale_order_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,13 @@ def parse_ubl_sale_order(self, xml_root):
customer_xpath = xml_root.xpath(
'/%s/cac:OriginatorCustomerParty' % root_name, namespaces=ns)
customer_dict = self.ubl_parse_customer_party(customer_xpath[0], ns)
supplier_xpath_party = xml_root.xpath(
'/%s/cac:SellerSupplierParty/cac:Party' % root_name, namespaces=ns)
company_dict_full = self.ubl_parse_party(supplier_xpath_party[0], ns)
company_dict = {}
# We only take the "official references" for company_dict
if company_dict_full.get('vat'):
company_dict = {'vat': company_dict_full['vat']}
delivery_xpath = xml_root.xpath(
'/%s/cac:Delivery' % root_name, namespaces=ns)
shipping_dict = {}
Expand All @@ -117,6 +124,7 @@ def parse_ubl_sale_order(self, xml_root):
# TODO : add charges
res = {
'partner': customer_dict,
'company': company_dict,
'ship_to': shipping_dict,
'currency': {'iso': currency_code},
'date': date_xpath[0].text,
Expand Down

0 comments on commit 486b974

Please sign in to comment.