From 10adbcda00b267ed0727e69e6c099937835eb7b2 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Mon, 26 Jul 2021 12:10:56 +0200 Subject: [PATCH 01/67] Initial check-in of account_invoice_import_simple_pdf This new module is an improved alternative to the module account_invoice_import_invoice2data. Refer to README for more information. --- account_invoice_import_simple_pdf/README.rst | 1 + account_invoice_import_simple_pdf/__init__.py | 2 + .../__manifest__.py | 27 ++ .../demo/demo_data.xml | 70 ++++ .../models/__init__.py | 3 + ...ccount_invoice_import_simple_pdf_fields.py | 393 ++++++++++++++++++ ...nvoice_import_simple_pdf_invoice_number.py | 108 +++++ .../models/res_partner.py | 389 +++++++++++++++++ .../readme/CONFIGURE.rst | 1 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 35 ++ .../readme/INSTALL.rst | 19 + .../security/ir.model.access.csv | 5 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../tests/__init__.py | 1 + .../tests/pdf/bouygues_telecom-test.pdf | Bin 0 -> 165905 bytes .../tests/test_invoice_import.py | 392 +++++++++++++++++ ...count_invoice_import_simple_pdf_fields.xml | 83 ++++ ...voice_import_simple_pdf_invoice_number.xml | 68 +++ .../views/res_partner.xml | 127 ++++++ .../wizard/__init__.py | 1 + .../wizard/account_invoice_import.py | 189 +++++++++ .../wizard/account_invoice_import_view.xml | 22 + 23 files changed, 1937 insertions(+) create mode 100644 account_invoice_import_simple_pdf/README.rst create mode 100644 account_invoice_import_simple_pdf/__init__.py create mode 100644 account_invoice_import_simple_pdf/__manifest__.py create mode 100644 account_invoice_import_simple_pdf/demo/demo_data.xml create mode 100644 account_invoice_import_simple_pdf/models/__init__.py create mode 100644 account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py create mode 100644 account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_invoice_number.py create mode 100644 account_invoice_import_simple_pdf/models/res_partner.py create mode 100644 account_invoice_import_simple_pdf/readme/CONFIGURE.rst create mode 100644 account_invoice_import_simple_pdf/readme/CONTRIBUTORS.rst create mode 100644 account_invoice_import_simple_pdf/readme/DESCRIPTION.rst create mode 100644 account_invoice_import_simple_pdf/readme/INSTALL.rst create mode 100644 account_invoice_import_simple_pdf/security/ir.model.access.csv create mode 100644 account_invoice_import_simple_pdf/static/description/icon.png create mode 100644 account_invoice_import_simple_pdf/tests/__init__.py create mode 100644 account_invoice_import_simple_pdf/tests/pdf/bouygues_telecom-test.pdf create mode 100644 account_invoice_import_simple_pdf/tests/test_invoice_import.py create mode 100644 account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_fields.xml create mode 100644 account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_invoice_number.xml create mode 100644 account_invoice_import_simple_pdf/views/res_partner.xml create mode 100644 account_invoice_import_simple_pdf/wizard/__init__.py create mode 100644 account_invoice_import_simple_pdf/wizard/account_invoice_import.py create mode 100644 account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml diff --git a/account_invoice_import_simple_pdf/README.rst b/account_invoice_import_simple_pdf/README.rst new file mode 100644 index 0000000000..262ac82139 --- /dev/null +++ b/account_invoice_import_simple_pdf/README.rst @@ -0,0 +1 @@ +Will be auto-generated from readme subdir diff --git a/account_invoice_import_simple_pdf/__init__.py b/account_invoice_import_simple_pdf/__init__.py new file mode 100644 index 0000000000..9b4296142f --- /dev/null +++ b/account_invoice_import_simple_pdf/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/account_invoice_import_simple_pdf/__manifest__.py b/account_invoice_import_simple_pdf/__manifest__.py new file mode 100644 index 0000000000..4c0bb45cdd --- /dev/null +++ b/account_invoice_import_simple_pdf/__manifest__.py @@ -0,0 +1,27 @@ +# Copyright 2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Account Invoice Import Simple PDF", + "version": "14.0.1.0.0", + "category": "Accounting/Accounting", + "license": "AGPL-3", + "summary": "Import simple PDF vendor bills", + "author": "Akretion,Odoo Community Association (OCA)", + "maintainers": ["alexis-via"], + "website": "https://github.com/OCA/edi", + "depends": ["account_invoice_import"], + "excludes": ["account_invoice_import_invoice2data"], + "external_dependencies": {"python": ["PyMuPDF", "regex", "dateparser"]}, + "data": [ + "security/ir.model.access.csv", + "wizard/account_invoice_import_view.xml", + "views/res_partner.xml", + "views/account_invoice_import_simple_pdf_fields.xml", + "views/account_invoice_import_simple_pdf_invoice_number.xml", + ], + "demo": ["demo/demo_data.xml"], + "installable": True, + "application": True, +} diff --git a/account_invoice_import_simple_pdf/demo/demo_data.xml b/account_invoice_import_simple_pdf/demo/demo_data.xml new file mode 100644 index 0000000000..55a0ae8a21 --- /dev/null +++ b/account_invoice_import_simple_pdf/demo/demo_data.xml @@ -0,0 +1,70 @@ + + + + + Mobile phone + + + + service + + + + Bouygues Telecom + + 1 + 37 rue Boissière + 75116 + Paris + + http://www.bouyguestelecom.fr + FR74397480930 + dd-mm-y4 + slash + + first + coma + space + + + + + digit + 14 + 14 + + + + + amount_total + max + + + + + amount_untaxed + first + Montant de la facture soumis à TVA + + + + + date + first + + + + + invoice_number + first + + + + Bouygues Telecom mobile + + 1line_static_product + Mobile phone: subscription and communication + + + + diff --git a/account_invoice_import_simple_pdf/models/__init__.py b/account_invoice_import_simple_pdf/models/__init__.py new file mode 100644 index 0000000000..7d7aab1c50 --- /dev/null +++ b/account_invoice_import_simple_pdf/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_invoice_import_simple_pdf_fields +from . import account_invoice_import_simple_pdf_invoice_number +from . import res_partner diff --git a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py new file mode 100644 index 0000000000..93c3ec5e3c --- /dev/null +++ b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py @@ -0,0 +1,393 @@ +# Copyright 2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError +from odoo.tools.misc import format_date + +from .res_partner import ERROR_STYLE + +logger = logging.getLogger(__name__) + +try: + import regex +except ImportError: + logger.debug("Cannot import regex") +try: + import dateparser +except ImportError: + logger.debug("Cannot import dateparser") + + +class AccountInvoiceImportSimplePdfFields(models.Model): + _name = "account.invoice.import.simple.pdf.fields" + _description = "Fields for Simple PDF invoice import" + _order = "partner_id, sequence, id" + + partner_id = fields.Many2one("res.partner", string="Vendor", ondelete="cascade") + # the order has no impact, it's just for readability + sequence = fields.Integer(default=10) + name = fields.Selection( + [ + ("amount_total", "Total"), + ("amount_untaxed", "Untaxed Amount"), + ("amount_tax", "Tax Amount"), + ("date", "Invoice Date"), + ("date_due", "Due Date"), + ("date_start", "Start Date"), + ("date_end", "End Date"), + ("invoice_number", "Invoice Number"), + ("description", "Description"), + ], + required=True, + string="Field", + ) + regexp = fields.Char(string="Specific Regular Expression") + date_format = fields.Selection( + "_date_format_sel", + string="Specific Date Format", + help="Leave empty if the format used is the same as the format defined " + "in the global section.", + ) + date_separator = fields.Selection( + "_date_separator_sel", + string="Specific Date Separator", + help="Leave empty if the format used is the same as the format defined " + "in the global section.", + ) + start = fields.Char(string="Start String") + end = fields.Char(string="End String") + extract_rule = fields.Selection( + [ + ("first", "First"), + ("last", "Last"), + ("position_start", "Specific Position from Start"), + ("position_end", "Specific Position from End"), + ("min", "Min"), + ("max", "Max"), + ("position_min", "Specific Position from Min"), + ("position_max", "Specific Position from Max"), + ], + string="Extract Rule", + required=True, + ) + position = fields.Integer(default=2) + + @api.model + def _date_format_sel(self): + return self.env["res.partner"]._simple_pdf_date_format_sel() + + @api.model + def _date_separator_sel(self): + return self.env["res.partner"]._simple_pdf_date_separator_sel() + + _sql_constraints = [ + ( + "position_specific_positive", + "CHECK(position > 0)", + "The position must be strictly positive.", + ), + ( + "partner_field_unique", + "unique(partner_id, name)", + "There is already an entry for that field.", + ), + ] + + @api.constrains("name", "regexp") + def _check_field_config(self): + for field in self: + if field.name == "description" and not field.regexp: + raise ValidationError( + _( + "You must set a Specific Regular Expression on " + "the 'Description' field." + ) + ) + + @api.onchange("name") + def field_change(self): + if not self.extract_rule: + if self.name == "amount_total": + self.extract_rule = "max" + elif self.name in ("invoice_number", "description", "date"): + self.extract_rule = "first" + + def get_value_from_list(self, data_list, test_info, raise_if_none=True): + assert isinstance(data_list, list) + if not data_list: + if raise_if_none: + raise UserError( + _("No valid data extracted for field '%s'.") % self.name + ) + else: + return None + if self.extract_rule in ("min", "max", "position_min", "position_max"): + data_list_sorted = list(data_list) + data_list_sorted.sort() + if self.name.startswith("date"): + test_info[self.name]["sorted_list"] = [ + format_date(self.env, date_dt) for date_dt in data_list_sorted + ] + else: + test_info[self.name]["sorted_list"] = data_list_sorted + if self.extract_rule == "max": + return data_list_sorted[-1] + elif self.extract_rule == "min": + return data_list_sorted[0] + elif self.extract_rule in ("position_min", "position_max"): + if len(data_list) < self.position: + error_msg = _( + "Partner '%s' is configured with an extract rule '%s' with " + "position %d for field '%s' but the list of " + "extracted valid data only has %d entries." + ) % ( + self.partner_id.display_name, + test_info["extract_rule_sel"][self.extract_rule], + self.position, + test_info["field_name_sel"][self.name], + len(data_list), + ) + if raise_if_none: + raise UserError(error_msg) + else: + test_info[self.name]["error_msg"] = error_msg + return None + sign = self.extract_rule == "position_min" and 1 or -1 + position = self.position + if self.extract_rule == "position_min": + position -= 1 + return data_list[position * sign] + elif self.extract_rule == "first": + return data_list[0] + elif self.extract_rule == "last": + return data_list[-1] + elif self.extract_rule in ("position_start", "position_end"): + if len(data_list) < self.position: + error_msg = _( + "Partner '%s' is configured with an extract rule '%s' with " + "position %d for field '%s' but the list of extracted " + "valid data only has %d entries." + ) % ( + self.partner_id.display_name, + test_info["extract_rule_sel"][self.extract_rule], + self.position, + test_info["field_name_sel"][self.name], + len(data_list), + ) + if raise_if_none: + raise UserError(error_msg) + else: + test_info[self.name]["error_msg"] = error_msg + return None + sign = self.extract_rule == "position_start" and 1 or -1 + position = self.position + if self.extract_rule == "position_start": + position -= 1 + return data_list[position * sign] + else: + raise UserError(_("Bad configuration")) + + def restrict_text(self, raw_text, test_info): + self.ensure_one() + restrict_text = raw_text + start = self.start and self.start.strip() or False + end = self.end and self.end.strip() or False + if start: + position = restrict_text.rfind(start) + if position >= 0: + restrict_text = restrict_text[position + len(start) :] + test_info[self.name]["start"] = _("Successful cut on '%s'") % start + else: + error_msg = _("String '%s' not found") % start + test_info[self.name]["start"] = "%s" % (ERROR_STYLE, error_msg) + if end: + position = restrict_text.rfind(end) + if position >= 0: + restrict_text = restrict_text[:position] + test_info[self.name]["end"] = _("Successful cut on '%s'") % end + else: + error_msg = _("String '%s' not found") % end + test_info[self.name]["end"] = "%s" % (ERROR_STYLE, error_msg) + return restrict_text + + def _get_date(self, parsed_inv, raw_text, partner_config, test_info): + date_format = self.date_format or partner_config["date_format"] + error_arg = ( + partner_config["display_name"], + test_info["field_name_sel"][self.name], + ) + if not date_format: + raise UserError( + _("No date format configured on partner '%s' nor on the field '%s'.") + % error_arg + ) + date_separator = self.date_separator or partner_config["date_separator"] + if not date_separator: + raise UserError( + _("No date separator configured on partner '%s' nor on the field '%s'.") + % error_arg + ) + date_separator_char = partner_config["separator2char"][date_separator] + + if self.regexp: + pattern = self.regexp + else: + pattern = date_format + for src, dest in partner_config["date_format2regex"].items(): + pattern = pattern.replace(src, dest) + + if date_separator_char == chr(32): + date_separator_regex = ",?%s+" % test_info["space_pattern"] + else: + date_separator_regex = regex.escape(date_separator_char) + + pattern = pattern.replace("-", date_separator_regex) + test_info[self.name] = { + "pattern": pattern, + "date_format": test_info["date_format_sel"][date_format].replace( + " ", date_separator_char + ), + } + restrict_text = self.restrict_text(raw_text, test_info) + res_regex = regex.findall(pattern, restrict_text) + valid_dates_dt = [] + date_formatdt = date_format + for src, dest in partner_config["date_format2dt"].items(): + date_formatdt = date_formatdt.replace(src, dest) + date_formatdt = date_formatdt.replace("-", date_separator_char) + languages = ( + partner_config["lang_short"] and [partner_config["lang_short"]] or None + ) + for date_raw in res_regex: + date_dt = dateparser.parse( + date_raw, date_formats=[date_formatdt], languages=languages + ) + if date_dt: + valid_dates_dt.append(date_dt) + else: + logger.debug( + "Failed to parse date %s using format %s and language %s", + date_raw, + date_formatdt, + partner_config["lang_short"], + ) + test_info[self.name].update( + { + "res_regex": res_regex, + "valid_list": [ + format_date(self.env, vdate_dt) for vdate_dt in valid_dates_dt + ], + } + ) + if self.name == "date_due" or test_info["test_mode"]: + raise_if_none = False + else: + raise_if_none = True + date_dt = self.get_value_from_list( + valid_dates_dt, test_info, raise_if_none=raise_if_none + ) + if date_dt: + parsed_inv[self.name] = date_dt + else: + parsed_inv["failed_fields"].append(self.name) + + def _get_amount(self, parsed_inv, raw_text, partner_config, test_info): + thousand_sep = partner_config["thousand_sep"] + if not thousand_sep: + thousand_sep_pattern = "" + elif thousand_sep == chr(32): + thousand_sep_pattern = test_info["space_pattern"] + else: + thousand_sep_pattern = regex.escape(thousand_sep) + decimal_sep = partner_config["decimal_sep"] + decimal_sep_pattern = regex.escape(decimal_sep) + decimal_places = partner_config["currency"].decimal_places + if self.regexp: + pattern = self.regexp + else: + if decimal_places: + pattern = r"(?:\d{1,3}%s)*\d{1,3}%s\d{%d}" % ( + thousand_sep_pattern, + decimal_sep_pattern, + decimal_places, + ) + else: + pattern = r"(?:\d{1,3}%s)*\d{1,3}" % thousand_sep_pattern + test_info[self.name] = {"pattern": pattern} + # don't take if followed by a % ? => means it's a rate + restrict_text = self.restrict_text(raw_text, test_info) + # I don't move the code that filters out percentrages and capital + # to simple_pdf_text_extraction() because I want to have raw + # test for start/end cut + # filter out percentage with decimal like VAT rates or discounts + # for example '5.5 %' or '20.0%' + restrict_text_filtered = regex.sub( + r"\d{1,2}%s\d{1,2}\s?%%" % regex.escape(decimal_sep), "", restrict_text + ) + # filter out discounts or VAT rates without decimal e.g. 20% + restrict_text_filtered = regex.sub(r"\d{1,3}\s?%", "", restrict_text_filtered) + # Try to filter out capital amounts + # Yes, this is a hack :) + # Works in EN and FR... what about other languages ? + restrict_text_filtered = regex.sub( + r"[Cc]apital.{1,30}(?:\d{1,3}%s)*\d{1,3}" % regex.escape(thousand_sep), + "", + restrict_text_filtered, + ) + res_regex = regex.findall(pattern, restrict_text_filtered) + valid_amounts = [] + for amount_raw in res_regex: + if thousand_sep_pattern: + amount_raw = regex.sub(thousand_sep_pattern, "", amount_raw) + if decimal_places: + amount_raw_list = list(amount_raw) + amount_raw_list[-decimal_places - 1] = "." + amount_raw = "".join(amount_raw_list) + try: + valid_amounts.append(float(amount_raw)) + except ValueError: + logger.debug("%s is an invalid float", amount_raw) + test_info[self.name].update( + { + "res_regex": res_regex, + "valid_list": valid_amounts, + } + ) + raise_if_none = not test_info["test_mode"] and True or False + amount = self.get_value_from_list( + valid_amounts, test_info, raise_if_none=raise_if_none + ) + parsed_inv[self.name] = amount + + def _get_invoice_number(self, parsed_inv, raw_text, partner_config, test_info): + partner = partner_config["recordset"] + pattern = self.regexp or partner._prepare_simple_pdf_invoice_number_regex() + test_info[self.name] = {"pattern": pattern} + restrict_text = self.restrict_text(raw_text, test_info) + res_regex = regex.findall(pattern, restrict_text) + test_info[self.name]["res_regex"] = res_regex + + inv_number = self.get_value_from_list(res_regex, test_info, raise_if_none=False) + if inv_number: + parsed_inv[self.name] = inv_number.strip() + else: + parsed_inv["failed_fields"].append(self.name) + + def _get_description(self, parsed_inv, raw_text, partner_config, test_info): + self.ensure_one() + pattern = self.regexp + test_info[self.name] = {"pattern": pattern} + restrict_text = self.restrict_text(raw_text, test_info) + res_regex = regex.findall(pattern, restrict_text) + test_info[self.name]["res_regex"] = res_regex + description = self.get_value_from_list( + res_regex, test_info, raise_if_none=False + ) + if description: + parsed_inv[self.name] = description.strip() + else: + parsed_inv["failed_fields"].append(self.name) diff --git a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_invoice_number.py b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_invoice_number.py new file mode 100644 index 0000000000..c45ef6b1f8 --- /dev/null +++ b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_invoice_number.py @@ -0,0 +1,108 @@ +# Copyright 2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError + +logger = logging.getLogger(__name__) + +MAX_PAST_YEARS = 5 + +try: + import regex +except ImportError: + logger.debug("Cannot import regex") + + +class AccountInvoiceImportSimplePdfInvoiceNumber(models.Model): + _name = "account.invoice.import.simple.pdf.invoice.number" + _description = "Invoice number format for Simple PDF invoice import" + _order = "sequence, id" + + partner_id = fields.Many2one("res.partner", string="Vendor", ondelete="cascade") + sequence = fields.Integer(default=10) + string_type = fields.Selection("_string_type_sel", string="Type", required=True) + fixed_char = fields.Char() + occurrence_min = fields.Integer(string="Minimum Occurence", default=1) + occurrence_max = fields.Integer(string="Maximum Occurence", default=1) + + _sql_constraints = [ + ( + "occurrence_min_positive", + "CHECK(occurrence_min > 0)", + "The minimum occurence must be strictly positive.", + ), + ( + "occurrence_max_positive", + "CHECK(occurrence_max > 0)", + "The maximum occurence must be strictly positive.", + ), + ] + + @api.model + def _string_type_sel(self): + return [ + ("fixed", "Fixed"), + ("letter_upper", "Upper Letter"), + ("letter_lower", "Lower Letter"), + ("digit", "Digit(s)"), + ("space", "Space"), + ("year2", "Year on 2 digits"), + ("year4", "Year on 4 digits"), + ("month", "Month (2 digits)"), + ] + + @api.constrains("string_type", "fixed_char", "occurrence_min", "occurrence_max") + def _check_invoice_number_format(self): + for rec in self: + if rec.string_type == "fixed": + fixed_char_stripped = rec.fixed_char and rec.fixed_char.strip() + if not fixed_char_stripped: + raise ValidationError(_("Missing fixed char.")) + elif rec.string_type in ("letter_upper", "letter_lower", "digit", "space"): + if rec.occurrence_max < rec.occurrence_min: + raise ValidationError( + _( + "The maximum occurence (%d) must be equal to or above " + "the minimum occurence (%d)." + ) + % (rec.occurrence_max, rec.occurrence_min) + ) + + @api.onchange("occurrence_min") + def occurrence_min_change(self): + if self.occurrence_min > self.occurrence_max: + self.occurrence_max = self.occurrence_min + + def _prepare_invoice_number_regex(self, regex_list): + self.ensure_one() + type2regex = { + "letter_upper": "[A-Z]", + "letter_lower": "[a-z]", + "digit": r"\d", + "space": r"\s", + } + if self.string_type == "fixed": + regex_list.append(regex.escape(self.fixed_char.strip())) + elif self.string_type in ("letter_upper", "letter_lower", "digit", "space"): + if self.occurrence_min == self.occurrence_max: + suffix = "{%d}" % self.occurrence_min + else: + suffix = "{%d,%d}" % (self.occurrence_min, self.occurrence_max) + + regex_list.append(type2regex[self.string_type] + suffix) + elif self.string_type in ("year2", "year4"): + # match on current and previous year only + current_year = fields.Date.context_today(self).year + + years = [current_year - y for y in range(MAX_PAST_YEARS + 1)] + if self.string_type == "year2": + years_str = [str(y)[-2:] for y in years] + else: + years_str = [str(y) for y in years] + regex_list.append("(?:%s)" % "|".join(years_str)) + elif self.string_type == "month": + regex_list.append("(?:01|02|03|04|05|06|07|08|09|10|11|12)") diff --git a/account_invoice_import_simple_pdf/models/res_partner.py b/account_invoice_import_simple_pdf/models/res_partner.py new file mode 100644 index 0000000000..b423ec80dd --- /dev/null +++ b/account_invoice_import_simple_pdf/models/res_partner.py @@ -0,0 +1,389 @@ +# Copyright 2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import base64 +import logging + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError +from odoo.tools.misc import format_amount, format_date, format_datetime + +logger = logging.getLogger(__name__) +ERROR_STYLE = ' style="color:red;"' + +try: + import regex +except ImportError: + logger.debug("Cannot import regex") + + +class ResPartner(models.Model): + _inherit = "res.partner" + + @api.model + def _simple_pdf_date_format_sel(self): + return [ + ("dd-mm-y4", _("DD MM YYYY")), + ("dd-month-y4", _("DD Month YYYY")), + ("month-dd-y4", _("Month DD YYYY")), + ("mm-dd-y4", _("MM DD YYYY")), + ("y4-mm-dd", _("YYYY MM DD")), + ("dd-mm-y2", _("DD MM YY")), + ("dd-month-y2", _("DD Month YY")), + ("month-dd-y2", _("Month DD YY")), + ("mm-dd-y2", _("MM DD YY")), + ] + + @api.model + def _simple_pdf_date_separator_sel(self): + return [ + ("slash", "/"), + ("dash", _("dash")), + ("dot", _("dot")), + ("space", _("space")), + ] + + simple_pdf_keyword = fields.Char( + help="If empty, Odoo will use the VAT number to identify the partner. " + "To match on several keywords, separate them with '|' (pipe)." + ) + simple_pdf_date_format = fields.Selection( + "_simple_pdf_date_format_sel", + string="Date Format", + default="dd-mm-y4", + help="If the date format uses 'Month', check that the language is " + "properly configured on the partner. 'Month' works both in full and " + "short version ('January' and 'Jan.').", + ) + simple_pdf_date_separator = fields.Selection( + "_simple_pdf_date_separator_sel", + default="slash", + string="Date Separator", + help="If the date looks like 'Sep. 4, 2021', use 'space' as date " + "separator (Odoo will ignore the dot and coma).", + ) + simple_pdf_decimal_separator = fields.Selection( + [ + ("dot", "dot"), + ("coma", "coma"), + ], + string="Decimal Separator", + help="If empty, Odoo will use the decimal separator configured on " + "the language of the partner.", + ) + simple_pdf_thousand_separator = fields.Selection( + [ + ("none", "none"), + ("space", "space"), + ("dot", "dot"), + ("coma", "coma"), + ], + string="Thousand Separator", + help="If empty, Odoo will use the thousand separator configured on " + "the language of the partner.", + ) + simple_pdf_pages = fields.Selection( + [ + ("first", "First Page Only"), + ("all", "All Pages"), + ], + default="all", + string="Page Analysis", + ) + simple_pdf_currency_id = fields.Many2one( + "res.currency", + string="Invoice Import Currency", + ondelete="restrict", + help="If empty, Odoo will use the company currency.", + ) + simple_pdf_field_ids = fields.One2many( + "account.invoice.import.simple.pdf.fields", + "partner_id", + string="Fields for PDF Invoice Import", + ) + simple_pdf_invoice_number_ids = fields.One2many( + "account.invoice.import.simple.pdf.invoice.number", + "partner_id", + string="Invoice Import Number Format", + ) + simple_pdf_test_file = fields.Binary( + string="Test PDF Invoice File", attachment=True + ) + simple_pdf_test_filename = fields.Char(string="Test PDF Invoice Filename") + simple_pdf_test_raw_text = fields.Text(string="Test Text Extraction", readonly=True) + simple_pdf_test_results = fields.Html(string="Test Results", readonly=True) + + @api.constrains("simple_pdf_decimal_separator", "simple_pdf_thousand_separator") + def _check_simple_pdf_separator(self): + for partner in self: + if ( + partner.simple_pdf_decimal_separator + and partner.simple_pdf_decimal_separator + == partner.simple_pdf_thousand_separator + ): + raise ValidationError( + _( + "For partner '%s', the decimal separator cannot be " + "the same as the thousand separator." + ) + % partner.display_name + ) + + @api.onchange("simple_pdf_decimal_separator") + def simple_pdf_decimal_separator_change(self): + if ( + self.simple_pdf_decimal_separator == "coma" + and self.simple_pdf_thousand_separator == "coma" + ): + self.simple_pdf_thousand_separator = "dot" + + @api.onchange("simple_pdf_date_format") + def simple_pdf_date_format_change(self): + if self.simple_pdf_date_format and "month" in self.simple_pdf_date_format: + self.simple_pdf_date_separator = "space" + + def _prepare_simple_pdf_invoice_number_regex(self): + self.ensure_one() + if not self.simple_pdf_invoice_number_ids: + raise UserError( + _("Missing invoice number format configuration on partner '%s'.") + % self.display_name + ) + regex = [] + for entry in self.simple_pdf_invoice_number_ids: + entry._prepare_invoice_number_regex(regex) + regex_string = "".join(regex) + return regex_string + + def pdf_simple_generate_default_fields(self): + self.ensure_one() + assert not self.parent_id + assert self.is_company + assert not self.simple_pdf_field_ids + def_fields = [ + {"name": "amount_total", "extract_rule": "max"}, + {"name": "amount_untaxed", "extract_rule": "position_end", "position": 3}, + {"name": "invoice_number", "extract_rule": "first"}, + {"name": "date", "extract_rule": "first"}, + ] + self.write( + { + "simple_pdf_field_ids": [(0, 0, field_val) for field_val in def_fields], + } + ) + + def pdf_simple_test_cleanup(self): + self.ensure_one() + self.write( + { + "simple_pdf_test_raw_text": False, + "simple_pdf_test_results": False, + "simple_pdf_test_filename": False, + "simple_pdf_test_file": False, + } + ) + + def pdf_simple_test_run(self): + self.ensure_one() + aiio = self.env["account.invoice.import"] + rpo = self.env["res.partner"] + vals = {} + test_results = [] + test_results.append("%s
" % _("Errors are in red.")) + test_results.append( + "%s %s" + % (_("Test Date:"), format_datetime(self.env, fields.Datetime.now())) + ) + if not self.simple_pdf_test_file: + raise UserError(_("You must upload a test PDF invoice.")) + test_info = {"test_mode": True} + aiio._simple_pdf_update_test_info(test_info) + file_data = base64.b64decode(self.simple_pdf_test_file) + raw_text_dict = aiio.simple_pdf_text_extraction(file_data, test_info) + if self.simple_pdf_pages == "first": + vals["simple_pdf_test_raw_text"] = raw_text_dict["first"] + else: + vals["simple_pdf_test_raw_text"] = raw_text_dict["all"] + test_results.append("

%s

    " % _("Searching Partner")) + partner_id = aiio.simple_pdf_match_partner( + raw_text_dict["all_no_space"], test_results + ) + partner_ok = False + if partner_id: + partner = rpo.browse(partner_id) + if partner_id == self.id: + partner_ok = True + partner_result = _("Current partner found") + else: + partner_result = "%s %s" % ( + _("Found another partner:"), + partner.display_name, + ) + else: + partner_result = _("No partner found.") + test_results.append( + "
  • %s %s
" + % (_("Result:"), not partner_ok and ERROR_STYLE or "", partner_result) + ) + if partner_ok: + partner_config = self._simple_pdf_partner_config() + test_results.append("

%s

    " % _("Amount Setup")) + test_results.append( + """
  • %s "%s" (%s)
  • """ + % ( + _("Decimal Separator:"), + partner_config["decimal_sep"], + partner_config["char2separator"].get( + partner_config["decimal_sep"], _("unknown") + ), + ) + ) + test_results.append( + """
  • %s "%s" (%s)
""" + % ( + _("Thousand Separator:"), + partner_config["thousand_sep"], + partner_config["char2separator"].get( + partner_config["thousand_sep"], _("unknown") + ), + ) + ) + parsed_inv = aiio.simple_pdf_parse_invoice(file_data, test_info) + key2label = { + "pattern": _("Regular Expression"), + "date_format": _("Date Format"), + "res_regex": _("Raw List"), + "valid_list": _("Valid-data Filtered List"), + "sorted_list": _("Ordered List"), + "error_msg": _("Error message"), + "start": _("Start String"), + "end": _("End String"), + } + for field in self.simple_pdf_field_ids: + test_results.append( + "

%s

    " % test_info["field_name_sel"][field.name] + ) + extract_method = test_info["extract_rule_sel"][field.extract_rule] + if field.extract_rule.startswith("position_"): + extract_method += _(", Position: %d") % field.position + test_results.append( + "
  • %s %s
  • " % (_("Extract Rule:"), extract_method) + ) + for key, value in test_info[field.name].items(): + if key != "pattern" or self.env.user.has_group("base.group_system"): + test_results.append("
  • %s: %s
  • " % (key2label[key], value)) + + result = parsed_inv.get(field.name) + if "date" in field.name and result: + result = format_date(self.env, result) + if "amount" in field.name and result: + result = format_amount( + self.env, result, parsed_inv["currency"]["recordset"] + ) + test_results.append( + "
  • %s %s
" + % ( + _("Result:"), + not result and ERROR_STYLE or "", + result or _("None"), + ) + ) + vals["simple_pdf_test_results"] = "\n".join(test_results) + self.write(vals) + + def _simple_pdf_partner_config(self): + self.ensure_one() + separator2char = { + "slash": "/", + "dash": "-", + "dot": ".", + "coma": ",", + "space": chr(32), # regular space + "none": "", + } + char2separator = {val: key for key, val in separator2char.items()} + date_format2regex = { + "dd": r"\d{1,2}", # We have to match on July 4, 2021 + "mm": r"\d{1,2}", + "y4": r"\d{4}", + "y2": r"\d{2}", + "month": r"\p{L}{3,15}\.?", # \p{L} : any unicode letter (but not digit) + # option dot for short month (e.g. 'feb.') + } + date_format2dt = { + "dd": "%d", + "mm": "%m", + "month": "%B", + "y4": "%Y", + "y2": "%y", + } + lang = False + if self.lang: + lang = self.env["res.lang"].search([("code", "=", self.lang)], limit=1) + if self.simple_pdf_decimal_separator: + decimal_sep = separator2char[self.simple_pdf_decimal_separator] + elif lang: + decimal_sep = lang.decimal_point + else: + raise UserError( + _( + "Could not get the decimal separator for partner '%s': " + "the fields 'Language' and 'Decimal Separator' are " + "both empty for this partner." + ) + % self.display_name + ) + if self.simple_pdf_thousand_separator: + thousand_sep = separator2char[self.simple_pdf_thousand_separator] + elif lang: + thousand_sep = lang.thousands_sep + # Remplace all white space characters (no-break-space, narrow no-break-space) + # by regular space + if regex.match(r"^\s$", thousand_sep): + thousand_sep = chr(32) # regular space + else: + thousand_sep = "" + logger.debug("decimal_sep=|%s| thousand_sep=|%s|", decimal_sep, thousand_sep) + partner_config = { + "recordset": self, + "display_name": self.display_name, + "date_format": self.simple_pdf_date_format, + "date_separator": self.simple_pdf_date_separator, + "currency": self.simple_pdf_currency_id or self.env.company.currency_id, + "decimal_sep": decimal_sep, + "thousand_sep": thousand_sep, + "separator2char": separator2char, + "char2separator": char2separator, + "date_format2regex": date_format2regex, + "date_format2dt": date_format2dt, + "lang_short": self.lang and self.lang[:2] or None, + } + # Check field list + field_list = [field.name for field in self.simple_pdf_field_ids] + amount_total_count = field_list.count("amount_total") + amount_untaxed_count = field_list.count("amount_untaxed") + amount_tax = field_list.count("amount_tax") + amount_fields_count = amount_total_count + amount_untaxed_count + amount_tax + if "date" not in field_list: + raise UserError( + _( + "You must configure a field extraction rule for " + "field 'Date' for partner '%s'." + ) + % self.display_name + ) + if amount_fields_count == 0: + raise UserError( + _("There is no amount field configured for partner '%s'.") + % self.display_name + ) + if amount_fields_count == 1 and amount_total_count == 0: + raise UserError( + _( + "For partner '%s', only one amount field is configured " + "but it is not 'Amount Total'." + ) + % self.display_name + ) + return partner_config diff --git a/account_invoice_import_simple_pdf/readme/CONFIGURE.rst b/account_invoice_import_simple_pdf/readme/CONFIGURE.rst new file mode 100644 index 0000000000..5b33e96629 --- /dev/null +++ b/account_invoice_import_simple_pdf/readme/CONFIGURE.rst @@ -0,0 +1 @@ +You will find a full demonstration about how to configure and use this module in this `screencast `_. diff --git a/account_invoice_import_simple_pdf/readme/CONTRIBUTORS.rst b/account_invoice_import_simple_pdf/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..ff65d68ce6 --- /dev/null +++ b/account_invoice_import_simple_pdf/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Alexis de Lattre diff --git a/account_invoice_import_simple_pdf/readme/DESCRIPTION.rst b/account_invoice_import_simple_pdf/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..a50c5f4c63 --- /dev/null +++ b/account_invoice_import_simple_pdf/readme/DESCRIPTION.rst @@ -0,0 +1,35 @@ +This module is an extension of the module *account_invoice_import*: it adds support for simple PDF invoices i.e. PDF invoice that don't have an embedded XML file. This module has been developped to solve the drawbacks of the OCA module **account_invoice_import_invoice2data** ; its advantages are the following: + +* Possibility to add support for a new vendor without developper skills: the accountant can do it! +* Adding support for a new vendor is faster. +* More tolerance on vendor invoice layout changes. +* Easier to install. + +With this module, you can import all the invoices that you were able to import with the module *account_invoice_import_invoice2data*. In fact, this module uses the same design when importing a PDF vendor bill: + +1. raw text extraction of the PDF file, +2. identify the partner using the VAT number (if the VAT number is present in the raw text extraction) or some keywords, +3. use regular expressions (regex) to extract the data needed to create the vendor bill in Odoo (single line configuration). + +The main difference with the OCA module *account_invoice_import_invoice2data* is that the regular expressions are auto-generated from the configuration made by the user in Odoo. No need to be a regex expert! But you can still write regex to extract some fields for some very specific needs. + +The module can extract the following fields: + +* Total Amount with taxes +* Total Untaxed Amount +* Total Tax Amount +* Invoice Date +* Due Date +* Start Date +* End Date +* Invoice Number +* Description (for that field, you have to write a regex) + +In this list, only 3 fields are required: + +* Invoice Date +* 2 out of the 3 Amount fields (the 3rd can be deducted from the 2 others: Total Amount = Total Untaxed + Total Tax) + +To take advantage of the fields *Start Date* and *End Date*, you need the OCA module *account_invoice_start_end_dates* from the `account-closing `_ project. + +To know the full story behind the development of this module, read this `blog post `_. diff --git a/account_invoice_import_simple_pdf/readme/INSTALL.rst b/account_invoice_import_simple_pdf/readme/INSTALL.rst new file mode 100644 index 0000000000..ac75273c55 --- /dev/null +++ b/account_invoice_import_simple_pdf/readme/INSTALL.rst @@ -0,0 +1,19 @@ +This module requires several Python libraries: + +* `PyMuPDF `_ which is a Python binding for `MuPDF `_, a lightweight PDF toolkit/viewer/renderer published under the AGPL licence by the company `Artifex Software `_ +* `regex `_ which is backward-compatible with the *re* module of the Python standard library, but has additional functionalities. +* `dateparser `_ which is a powerful date parsing library. + +If you use Debian (Bullseye or higher) or Ubuntu (20.04 or higher), run the following command: + +.. code:: + + sudo apt install python3-fitz python3-regex python3-dateparser + +You can also install these Python librairies via pip: + +.. code:: + + sudo pip3 install --upgrade PyMuPDF regex dateparser + +but beware that *PyMuPDF* is just a binding on MuPDF, so it will require MuPDF and all the development libs required to compile the binding. So, for *PyMuPDF*, it's much easier to install it via the packages of your Linux distribution (package name **python3-fitz** on Debian/Ubuntu, but the package name may be different in other distributions). diff --git a/account_invoice_import_simple_pdf/security/ir.model.access.csv b/account_invoice_import_simple_pdf/security/ir.model.access.csv new file mode 100644 index 0000000000..ce06b9780e --- /dev/null +++ b/account_invoice_import_simple_pdf/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_account_invoice_import_simple_pdf_fields_full,Full access on account.invoice.import.simple.pdf.fields,model_account_invoice_import_simple_pdf_fields,account.group_account_invoice,1,1,1,1 +access_account_invoice_import_simple_pdf_fields_read,Read access on account.invoice.import.simple.pdf.fields,model_account_invoice_import_simple_pdf_fields,account.group_account_readonly,1,0,0,0 +access_account_invoice_import_simple_pdf_invoice_number_full,Full access on account.invoice.import.simple.pdf.invoice.number,model_account_invoice_import_simple_pdf_invoice_number,account.group_account_invoice,1,1,1,1 +access_account_invoice_import_simple_pdf_invoice_number_read,Read access on account.invoice.import.simple.pdf.invoice.number,model_account_invoice_import_simple_pdf_invoice_number,account.group_account_readonly,1,0,0,0 diff --git a/account_invoice_import_simple_pdf/static/description/icon.png b/account_invoice_import_simple_pdf/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z z?n?eq1XP>p-IM$Z^C;2L3itnbJZAip*Zo0aw2bs8@(s^~*8T9go!%dHcAz2lM;`yp zD=7&xjFV$S&5uDaiScyD?B-i1ze`+CoRtz`Wn+Zl&#s4&}MO{@N!ufrzjG$B79)Y2d3tBk&)TxUTw@QS0TEL_?njX|@vq?Uz(nBFK5Pq7*xj#u*R&i|?7+6# z+|r_n#SW&LXhtheZdah{ZVoqwyT{D>MC3nkFF#N)xLi{p7J1jXlmVeb;cP5?e(=f# zuT7fvjSbjS781v?7{)-X3*?>tq?)Yd)~|1{BDS(pqC zC}~H#WXlkUW*H5CDOo<)#x7%RY)A;ShGhI5s*#cRDA8YgqG(HeKDx+#(ZQ?386dv! zlXCO)w91~Vw4AmOcATuV653fa9R$fyK8ul%rG z-wfS zihugoZyr38Im?Zuh6@RcF~t1anQu7>#lPpb#}4cOA!EM11`%f*07RqOVkmX{p~KJ9 z^zP;K#|)$`^Rb{rnHGH{~>1(fawV0*Z#)}M`m8-?ZJV<+e}s9wE# z)l&az?w^5{)`S(%MRzxdNqrs1n*-=jS^_jqE*5XDrA0+VE`5^*p3CuM<&dZEeCjoz zR;uu_H9ZPZV|fQq`Cyw4nscrVwi!fE6ciMmX$!_hN7uF;jjKG)d2@aC4ropY)8etW=xJvni)8eHi`H$%#zn^WJ5NLc-rqk|u&&4Z6fD_m&JfSI1Bvb?b<*n&sfl0^t z=HnmRl`XrFvMKB%9}>PaA`m-fK6a0(8=qPkWS5bb4=v?XcWi&hRY?O5HdulRi4?fN zlsJ*N-0Qw+Yic@s0(2uy%F@ib;GjXt01Fmx5XbRo6+n|pP(&nodMoap^z{~q ziEeaUT@Mxe3vJSfI6?uLND(CNr=#^W<1b}jzW58bIfyWTDle$mmS(|x-0|2UlX+9k zQ^EX7Nw}?EzVoBfT(-LT|=9N@^hcn-_p&sqG z&*oVs2JSU+N4ZD`FhCAWaS;>|wH2G*Id|?pa#@>tyxX`+4HyIArWDvVrX)2WAOQff z0qyHu&-S@i^MS-+j--!pr4fPBj~_8({~e1bfcl0wI1kaoN>mJL6KUPQm5N7lB(ui1 zE-o%kq)&djzWJ}ob<-GfDlkB;F31j-VHKvQUGQ3sp`CwyGJk_i!y^sD0fqC@$9|jO zOqN!r!8-p==F@ZVP=U$qSpY(gQ0)59P1&t@y?5rvg<}E+GB}26NYPp4f2YFQrQtot5mn3wu_qprZ=>Ig-$ zbW26Ws~IgY>}^5w`vTB(G`PTZaDiGBo5o(tp)qli|NeV( z@H_=R8V39rt5J5YB2Ky?4eJJ#b`_iBe2ot~6%7mLt5t8Vwi^Jy7|jWXqa3amOIoRb zOr}WVFP--DsS`1WpN%~)t3R!arKF^Q$e12KEqU36AWwnCBICpH4XCsfnyrHr>$I$4 z!DpKX$OKLWarN7nv@!uIA+~RNO)l$$w}p(;b>mx8pwYvu;dD_unryX_NhT8*Tj>BTrTTL&!?O+%Rv;b?B??gSzdp?6Uug9{ zd@V08Z$BdI?fpoCS$)t4mg4rT8Q_I}h`0d-vYZ^|dOB*Q^S|xqTV*vIg?@fVFSmMpaw0qtTRbx} z({Pg?#{2`sc9)M5N$*N|4;^t$+QP?#mov zGVC@I*lBVrOU-%2y!7%)fAKjpEFsgQc4{amtiHb95KQEwvf<(3T<9-Zm$xIew#P22 zc2Ix|App^>v6(3L_MCU0d3W##AB0M~3D00EWoKZqsJYT(#@w$Y_H7G22M~ApVFTRHMI_3be)Lkn#0F*V8Pq zc}`Cjy$bE;FJ6H7p=0y#R>`}-m4(0F>%@P|?7fx{=R^uFdISRnZ2W_xQhD{YuR3t< z{6yxu=4~JkeA;|(J6_nv#>Nvs&FuLA&PW^he@t(UwFFE8)|a!R{`E`K`i^ZnyE4$k z;(749Ix|oi$c3QbEJ3b~D_kQsPz~fIUKym($a_7dJ?o+40*OLl^{=&oq$<#Q(yyrp z{J-FAniyAw9tPbe&IhQ|a`DqFTVQGQ&Gq3!C2==4x{6EJwiPZ8zub-iXoUtkJiG{} zPaR&}_fn8_z~(=;5lD-aPWD3z8PZS@AaUiomF!G8I}Mf>e~0g#BelA-5#`cj;O5>N Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/account_invoice_import_simple_pdf/tests/__init__.py b/account_invoice_import_simple_pdf/tests/__init__.py new file mode 100644 index 0000000000..d0a138175d --- /dev/null +++ b/account_invoice_import_simple_pdf/tests/__init__.py @@ -0,0 +1 @@ +from . import test_invoice_import diff --git a/account_invoice_import_simple_pdf/tests/pdf/bouygues_telecom-test.pdf b/account_invoice_import_simple_pdf/tests/pdf/bouygues_telecom-test.pdf new file mode 100644 index 0000000000000000000000000000000000000000..02de6cd8e4fe6b15dbaaadb946f9461855eba9b6 GIT binary patch literal 165905 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 szrT4s#qa0-4Y-51sedze*roCL>F)6 +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 + +from odoo import fields +from odoo.tests.common import TransactionCase +from odoo.tools import file_open, float_compare + + +class TestInvoiceImport(TransactionCase): + def setUp(self): + super().setUp() + self.partner = self.env["res.partner"].create( + { + "name": "Test Partner", + "is_company": True, + "country_id": self.env.ref("base.fr").id, + "simple_pdf_date_format": "dd-mm-y4", + "simple_pdf_date_separator": "slash", + "lang": "fr_FR", + } + ) + aiispfo = self.env["account.invoice.import.simple.pdf.fields"] + self.date_field = aiispfo.create( + { + "name": "date", + "extract_rule": "first", + "partner_id": self.partner.id, + } + ) + self.amount_field = aiispfo.create( + { + "name": "amount_total", + "extract_rule": "first", + "partner_id": self.partner.id, + } + ) + self.inv_num_field = aiispfo.create( + { + "name": "invoice_number", + "extract_rule": "first", + "partner_id": self.partner.id, + } + ) + + self.partner_config = self.partner._simple_pdf_partner_config() + self.test_info = {"test_mode": True} + self.env["account.invoice.import"]._simple_pdf_update_test_info(self.test_info) + aiispfo = self.env["account.invoice.import.simple.pdf.fields"] + self.space_chars = list(self.test_info["space_pattern"][1:-1]) + frtax = self.env["account.tax"].create( + { + "name": "French VAT purchase 20.0%", + "description": "FR-VAT-buy-20.0", + "amount": 20, + "amount_type": "percent", + "type_tax_use": "purchase", + } + ) + self.module = "account_invoice_import_simple_pdf" + self.product = self.env.ref("%s.mobile_phone" % self.module) + self.product.supplier_taxes_id = [(6, 0, [frtax.id])] + self.demo_partner = self.env.ref("%s.bouygues_telecom" % self.module) + self.filename = "bouygues_telecom-test.pdf" + with file_open("%s/tests/pdf/%s" % (self.module, self.filename), "rb") as f: + self.pdf_file = f.read() + self.pdf_file_b64 = base64.b64encode(self.pdf_file) + + def test_date_parsing(self): + date_test = { + "14/07/2021": { + "date_format": "dd-mm-y4", + "date_separator": "slash", + "lang": "any", + }, + "14/7/2021": { + "date_format": "dd-mm-y4", + "date_separator": "slash", + "lang": "any", + }, + "14-7-2021": { + "date_format": "dd-mm-y4", + "date_separator": "dash", + "lang": "any", + }, + "7/14/2021": { + "date_format": "mm-dd-y4", + "date_separator": "slash", + "lang": "any", + }, + "7.14.2021": { + "date_format": "mm-dd-y4", + "date_separator": "dot", + "lang": "any", + }, + "14%sJuillet%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "14%s Juillet%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, # with double space + "14%sJuillet,%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "14%sjuillet%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "14%sjuillet,%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "14%sjuil.%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "14%sjuil.,%s2021": { + "date_format": "dd-month-y4", + "date_separator": "space", + "lang": "fr", + }, + "july%s14%s2021": { + "date_format": "month-dd-y4", + "date_separator": "space", + "lang": "en", + }, + "july%s14,%s2021": { + "date_format": "month-dd-y4", + "date_separator": "space", + "lang": "en", + }, + "jul.%s14,%s2021": { + "date_format": "month-dd-y4", + "date_separator": "space", + "lang": "en", + }, + } + for src, config in date_test.items(): + raw_text = "Debit 15.12\n%s\n12.99 Total" % src + if config["date_separator"] == "space": + raw_text_list = [ + raw_text % (space_char, space_char) + for space_char in self.space_chars + ] + else: + raw_text_list = [raw_text] + parsed_inv = {"failed_fields": []} + self.date_field.write( + { + "date_format": config["date_format"], + "date_separator": config["date_separator"], + } + ) + lang_list = config["lang"] == "any" and ["fr", "en"] or [config["lang"]] + for lang in lang_list: + self.partner_config["lang_short"] = lang + for raw_txt in raw_text_list: + self.date_field._get_date( + parsed_inv, raw_txt, self.partner_config, self.test_info + ) + res_date = parsed_inv["date"] + self.assertEqual(fields.Date.to_string(res_date), "2021-07-14") + + def test_amount_parsing(self): + amount_test = { + "$ 1.02": { + "decimal_separator": "dot", + "thousand_separator": "coma", + "result": 1.02, + "currency": "USD", + }, + "$ 459.09": { + "decimal_separator": "dot", + "thousand_separator": "coma", + "result": 459.09, + "currency": "USD", + }, + "$ 1,459.32": { + "decimal_separator": "dot", + "thousand_separator": "coma", + "result": 1459.32, + "currency": "USD", + }, + "1.459,32 €": { + "decimal_separator": "coma", + "thousand_separator": "dot", + "result": 1459.32, + "currency": "EUR", + }, + "59,32 €": { + "decimal_separator": "coma", + "thousand_separator": "space", + "result": 59.32, + "currency": "EUR", + }, + "781.459,32 €": { + "decimal_separator": "coma", + "thousand_separator": "dot", + "result": 781459.32, + "currency": "EUR", + }, + "781%s459,32€": { + "decimal_separator": "coma", + "thousand_separator": "space", + "result": 781459.32, + "currency": "EUR", + }, + "99459,32 €": { + "decimal_separator": "coma", + "thousand_separator": "none", + "result": 99459.32, + "currency": "EUR", + }, + "14%s459 XPF": { + "decimal_separator": "coma", + "thousand_separator": "space", + "result": 14459, + "currency": "XPF", + }, # XPF decimal places = 0 + "14%s459,123 XPF": { + "decimal_separator": "coma", + "thousand_separator": "space", + "result": 14459.123, + "currency": "TND", + }, # TND decimal places = 3 + "88,459.1234 VEF": { + "decimal_separator": "dot", + "thousand_separator": "coma", + "result": 88459.1234, + "currency": "VEF", + }, # VEF decimal places = 4 + } + for src, config in amount_test.items(): + raw_text = ( + "Invoice Date: 05/12/2019\n%s\nSAS with a capital of 1,234,322.23 USD" + % src + ) + if config["thousand_separator"] == "space" and "%s" in raw_text: + raw_text_list = [ + raw_text % space_char for space_char in self.space_chars + ] + else: + raw_text_list = [raw_text] + parsed_inv = {"failed_fields": []} + self.partner.write( + { + "simple_pdf_decimal_separator": config["decimal_separator"], + "simple_pdf_thousand_separator": config["thousand_separator"], + "simple_pdf_currency_id": self.env.ref( + "base.%s" % config["currency"] + ).id, + } + ) + partner_config = self.partner._simple_pdf_partner_config() + if partner_config["currency"].decimal_places == 0: + self.amount_field.write({"extract_rule": "last"}) + else: + self.amount_field.write({"extract_rule": "first"}) + for raw_txt in raw_text_list: + self.amount_field._get_amount( + parsed_inv, raw_txt, partner_config, self.test_info + ) + res_amount = parsed_inv["amount_total"] + self.assertFalse( + partner_config["currency"].compare_amounts( + res_amount, config["result"] + ) + ) + + def test_invoice_number_parsing(self): + inv_num_test = { + "INV/2020/05/0042": [ + {"string_type": "fixed", "fixed_char": "INV/"}, + {"string_type": "year4"}, + {"string_type": "fixed", "fixed_char": "/"}, + {"string_type": "month"}, + {"string_type": "fixed", "fixed_char": "/"}, + {"string_type": "digit", "occurrence_min": 4, "occurrence_max": 5}, + ], + "I210042": [ + {"string_type": "fixed", "fixed_char": "I"}, + {"string_type": "year2"}, + {"string_type": "digit", "occurrence_min": 4, "occurrence_max": 5}, + ], + "INV2107ZB0042": [ + {"string_type": "fixed", "fixed_char": "INV"}, + {"string_type": "year2"}, + {"string_type": "month"}, + { + "string_type": "letter_upper", + "occurrence_min": 2, + "occurrence_max": 2, + }, + {"string_type": "digit", "occurrence_min": 4, "occurrence_max": 5}, + ], + # If you wonder which company can have such a stupid invoice number, + # the answer is... Orange ! I won't say what I think about these guys... + "04B700L456 21A7- 1J01": [ + {"string_type": "fixed", "fixed_char": "04B700L456"}, + {"string_type": "space"}, + {"string_type": "year2"}, + { + "string_type": "letter_upper", + "occurrence_min": 1, + "occurrence_max": 1, + }, + {"string_type": "digit", "occurrence_min": 1, "occurrence_max": 1}, + {"string_type": "fixed", "fixed_char": "-"}, + {"string_type": "space"}, + {"string_type": "fixed", "fixed_char": "1J"}, + {"string_type": "month"}, + ], + } + for src, config_list in inv_num_test.items(): + raw_txt = "Invoice number: %s dated 20/08/2020" % src + self.partner.simple_pdf_invoice_number_ids.unlink() + self.partner.write( + {"simple_pdf_invoice_number_ids": [(0, 0, x) for x in config_list]} + ) + parsed_inv = {"failed_fields": []} + self.inv_num_field._get_invoice_number( + parsed_inv, raw_txt, self.partner_config, self.test_info + ) + self.assertEqual(src, parsed_inv["invoice_number"]) + + def test_raw_extraction(self): + aiio = self.env["account.invoice.import"] + res = aiio.simple_pdf_text_extraction(self.pdf_file, self.test_info) + self.assertIsInstance(res, dict) + self.assertTrue(len(res["all"]) >= len(res["first"])) + self.assertIn("FR 74 397 480 930", res["first"]) + self.assertIn("FR74397480930", res["first_no_space"]) + + def test_complete_import(self): + wiz = self.env["account.invoice.import"].create( + { + "invoice_file": self.pdf_file_b64, + "invoice_filename": self.filename, + } + ) + wiz.import_invoice() + # Check result of invoice creation + invoices = self.env["account.move"].search( + [ + ("partner_id", "=", self.demo_partner.id), + ("state", "=", "draft"), + ("move_type", "=", "in_invoice"), + ("ref", "=", "11608848301659"), + ] + ) + inv_config = self.env.ref("%s.bouygues_telecom_import_config" % self.module) + self.assertEqual(len(invoices), 1) + inv = invoices[0] + self.assertEqual(fields.Date.to_string(inv.invoice_date), "2019-11-02") + self.assertEqual(inv.journal_id.type, "purchase") + self.assertEqual(inv.currency_id, self.env.ref("base.EUR")) + self.assertFalse(inv.currency_id.compare_amounts(inv.amount_total, 12.99)) + self.assertFalse(inv.currency_id.compare_amounts(inv.amount_untaxed, 10.83)) + self.assertEqual(len(inv.invoice_line_ids), 1) + iline = inv.invoice_line_ids[0] + self.assertEqual(iline.name, inv_config.label) + self.assertEqual(iline.product_id, self.product) + self.assertEqual(iline.tax_ids, self.product.supplier_taxes_id) + self.assertEqual(float_compare(iline.quantity, 1.0, precision_digits=2), 0) + self.assertEqual(float_compare(iline.price_unit, 10.83, precision_digits=2), 0) + inv.unlink() + + def test_test_mode(self): + self.demo_partner.write( + { + "simple_pdf_test_file": self.pdf_file_b64, + "simple_pdf_test_filename": self.filename, + } + ) + self.demo_partner.pdf_simple_test_run() + self.assertTrue(self.demo_partner.simple_pdf_test_results) + self.assertTrue(self.demo_partner.simple_pdf_test_raw_text) + self.demo_partner.pdf_simple_test_cleanup() + self.assertFalse(self.demo_partner.simple_pdf_test_results) + self.assertFalse(self.demo_partner.simple_pdf_test_raw_text) + self.assertFalse(self.demo_partner.simple_pdf_test_file) diff --git a/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_fields.xml b/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_fields.xml new file mode 100644 index 0000000000..6a2f292f97 --- /dev/null +++ b/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_fields.xml @@ -0,0 +1,83 @@ + + + + + + + account.invoice.import.simple.pdf.fields + +
+ + + + + + + + + + + + + + + +
+
+
+ + + account.invoice.import.simple.pdf.fields + + + + + + + + + + + + + + + + + + +
diff --git a/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_invoice_number.xml b/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_invoice_number.xml new file mode 100644 index 0000000000..2eb8705392 --- /dev/null +++ b/account_invoice_import_simple_pdf/views/account_invoice_import_simple_pdf_invoice_number.xml @@ -0,0 +1,68 @@ + + + + + + + account.invoice.import.simple.pdf.invoice.number + +
+ + + + + + + +
+
+
+ + + account.invoice.import.simple.pdf.invoice.number + + + + + + + + + + + + + + +
diff --git a/account_invoice_import_simple_pdf/views/res_partner.xml b/account_invoice_import_simple_pdf/views/res_partner.xml new file mode 100644 index 0000000000..f4d80e3686 --- /dev/null +++ b/account_invoice_import_simple_pdf/views/res_partner.xml @@ -0,0 +1,127 @@ + + + + + + + res.partner + + +
+ + +
+ +
+ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + +
+
+ + +
diff --git a/account_invoice_import_simple_pdf/wizard/__init__.py b/account_invoice_import_simple_pdf/wizard/__init__.py new file mode 100644 index 0000000000..9817a01c22 --- /dev/null +++ b/account_invoice_import_simple_pdf/wizard/__init__.py @@ -0,0 +1 @@ +from . import account_invoice_import diff --git a/account_invoice_import_simple_pdf/wizard/account_invoice_import.py b/account_invoice_import_simple_pdf/wizard/account_invoice_import.py new file mode 100644 index 0000000000..bb58e9fbd5 --- /dev/null +++ b/account_invoice_import_simple_pdf/wizard/account_invoice_import.py @@ -0,0 +1,189 @@ +# Copyright 2021 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from tempfile import NamedTemporaryFile + +from odoo import _, api, models +from odoo.exceptions import UserError + +logger = logging.getLogger(__name__) + +try: + import fitz +except ImportError: + logger.debug("Cannot import PyMuPDF") +try: + import regex +except ImportError: + logger.debug("Cannot import regex") + + +class AccountInvoiceImport(models.TransientModel): + _inherit = "account.invoice.import" + + @api.model + def fallback_parse_pdf_invoice(self, file_data): + """This method must be inherited by additional modules with + the same kind of logic as the account_bank_statement_import_* + modules""" + return self.simple_pdf_parse_invoice(file_data) + + @api.model + def simple_pdf_text_extraction(self, file_data, test_info): + res = {} + fileobj = NamedTemporaryFile("wb", prefix="odoo-simple-pdf-", suffix=".pdf") + fileobj.write(file_data) + # Extract text from PDF + # Very interesting reading: + # https://github.com/erfelipe/PDFtextExtraction + res["all"] = "" + doc = fitz.open(fileobj.name) + for page in doc: + res["all"] += page.getText("text") + res["first"] = doc[0].getText("text") + res["all_no_space"] = regex.sub( + "%s+" % test_info["space_pattern"], "", res["all"] + ) + res["first_no_space"] = regex.sub( + "%s+" % test_info["space_pattern"], "", res["first"] + ) + fileobj.close() + return res + + @api.model + def simple_pdf_match_partner(self, raw_text_no_space, test_results=None): + if test_results is None: + test_results = [] + partner_id = False + rpo = self.env["res.partner"] + partners = rpo.search_read( + [ + "|", + ("vat", "!=", False), + ("simple_pdf_keyword", "!=", False), + ("parent_id", "=", False), + ("is_company", "=", True), + ] + ) + for partner in partners: + if partner["simple_pdf_keyword"] and partner["simple_pdf_keyword"].strip(): + keywords = partner["simple_pdf_keyword"].replace(" ", "").split("|") + found_res = [keyword in raw_text_no_space for keyword in keywords] + if all(found_res): + partner_id = partner["id"] + result_label = _("Successful match on %d keywords (%s)") % ( + len(keywords), + ", ".join(keywords), + ) + test_results.append("
  • %s
  • " % result_label) + break + elif partner["vat"]: + if partner["vat"] in raw_text_no_space: + partner_id = partner["id"] + result_label = ( + _("Successful match on VAT number '%s'") % partner["vat"] + ) + test_results.append("
  • %s
  • " % result_label) + break + return partner_id + + @api.model + def _get_space_pattern(self): + # https://en.wikipedia.org/wiki/Whitespace_character + space_ints = [ + 32, + 160, + 8192, + 8193, + 8194, + 8195, + 8196, + 8197, + 8198, + 8199, + 8200, + 8201, + 8202, + 8239, + 8287, + ] + return "[%s]" % "".join([chr(x) for x in space_ints]) + + @api.model + def _simple_pdf_update_test_info(self, test_info): + aiispfo = self.env["account.invoice.import.simple.pdf.fields"] + test_info.update( + { + "date_format_sel": dict( + aiispfo.fields_get("date_format", "selection")["date_format"][ + "selection" + ] + ), + "field_name_sel": dict( + aiispfo.fields_get("name", "selection")["name"]["selection"] + ), + "extract_rule_sel": dict( + aiispfo.fields_get("extract_rule", "selection")["extract_rule"][ + "selection" + ] + ), + "space_pattern": self._get_space_pattern(), + } + ) + + @api.model + def simple_pdf_parse_invoice(self, file_data, test_info=None): + if test_info is None: + test_info = {"test_mode": False} + self._simple_pdf_update_test_info(test_info) + rpo = self.env["res.partner"] + logger.info("Trying to analyze PDF invoice with simple pdf module") + raw_text_dict = self.simple_pdf_text_extraction(file_data, test_info) + partner_id = self.simple_pdf_match_partner(raw_text_dict["all_no_space"]) + if not partner_id: + raise UserError(_("Simple PDF Import: count not find Vendor.")) + partner = rpo.browse(partner_id) + raw_text = ( + partner.simple_pdf_pages and raw_text_dict["first"] or raw_text_dict["all"] + ) + logger.info( + "Simple pdf import found partner %s ID %d", partner.display_name, partner_id + ) + partner_config = partner._simple_pdf_partner_config() + parsed_inv = { + "partner": {"recordset": partner}, + "currency": {"recordset": partner_config["currency"]}, + "failed_fields": [], + "chatter_msg": [], + } + + # Check field config + for field in partner.simple_pdf_field_ids: + logger.debug("Working on field %s", field.name) + if field.name.startswith("date"): + field._get_date(parsed_inv, raw_text, partner_config, test_info) + elif field.name.startswith("amount_"): + field._get_amount(parsed_inv, raw_text, partner_config, test_info) + elif field.name == "invoice_number": + field._get_invoice_number( + parsed_inv, raw_text, partner_config, test_info + ) + elif field.name == "description": + field._get_description(parsed_inv, raw_text, partner_config, test_info) + + failed_fields = parsed_inv.pop("failed_fields") + if failed_fields: + parsed_inv["chatter_msg"].append( + _("Failed to extract the following field(s): %s.") + % ", ".join( + [ + "%s" % test_info["field_name_sel"][failed_field] + for failed_field in failed_fields + ] + ) + ) + + logger.info("simple pdf parsed_inv=%s", parsed_inv) + return parsed_inv diff --git a/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml b/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml new file mode 100644 index 0000000000..c57a04fba5 --- /dev/null +++ b/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml @@ -0,0 +1,22 @@ + + + + + + + account.invoice.import + + +
      +
    • regular PDF file without any embedded XML file via a pre-configuration in Odoo.
    • +
    +
    +
    + + +
    From c04d11af58fb49107af5b3485605d5a459b2818a Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 24 Aug 2021 09:24:37 +0200 Subject: [PATCH 02/67] Temporary workaround of odoo bug https://github.com/odoo/odoo/issues/75492 Make pre-commit green --- .../models/account_invoice_import_simple_pdf_fields.py | 6 +++++- account_invoice_import_simple_pdf/models/res_partner.py | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py index 93c3ec5e3c..ca12750185 100644 --- a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py +++ b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py @@ -116,7 +116,11 @@ def field_change(self): elif self.name in ("invoice_number", "description", "date"): self.extract_rule = "first" - def get_value_from_list(self, data_list, test_info, raise_if_none=True): + # This method is just 1 line over the complexity limit of C901 + # and I don't see a good way to split it + def get_value_from_list( # noqa: C901 + self, data_list, test_info, raise_if_none=True + ): assert isinstance(data_list, list) if not data_list: if raise_if_none: diff --git a/account_invoice_import_simple_pdf/models/res_partner.py b/account_invoice_import_simple_pdf/models/res_partner.py index b423ec80dd..ea94d702c5 100644 --- a/account_invoice_import_simple_pdf/models/res_partner.py +++ b/account_invoice_import_simple_pdf/models/res_partner.py @@ -48,17 +48,21 @@ def _simple_pdf_date_separator_sel(self): help="If empty, Odoo will use the VAT number to identify the partner. " "To match on several keywords, separate them with '|' (pipe)." ) + # Temporary hack: I disable the default values for the fields + # simple_pdf_date_format and simple_pdf_date_separator + # to avoid the following bug: + # https://github.com/odoo/odoo/issues/75492 simple_pdf_date_format = fields.Selection( "_simple_pdf_date_format_sel", string="Date Format", - default="dd-mm-y4", + # default="dd-mm-y4", help="If the date format uses 'Month', check that the language is " "properly configured on the partner. 'Month' works both in full and " "short version ('January' and 'Jan.').", ) simple_pdf_date_separator = fields.Selection( "_simple_pdf_date_separator_sel", - default="slash", + # default="slash", string="Date Separator", help="If the date looks like 'Sep. 4, 2021', use 'space' as date " "separator (Odoo will ignore the dot and coma).", From 4916e4808d8e6eb0e7cc0687a66df7bc25be4ea6 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Tue, 31 Aug 2021 12:42:06 +0200 Subject: [PATCH 03/67] Improve handling of start/end string cut when there are multiplue occurences of cut string Add tests on string cut method --- ...ccount_invoice_import_simple_pdf_fields.py | 24 ++++++++++----- .../tests/test_invoice_import.py | 29 ++++++++++++++++++- 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py index ca12750185..9fff2545b2 100644 --- a/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py +++ b/account_invoice_import_simple_pdf/models/account_invoice_import_simple_pdf_fields.py @@ -201,7 +201,7 @@ def restrict_text(self, raw_text, test_info): start = self.start and self.start.strip() or False end = self.end and self.end.strip() or False if start: - position = restrict_text.rfind(start) + position = restrict_text.find(start) if position >= 0: restrict_text = restrict_text[position + len(start) :] test_info[self.name]["start"] = _("Successful cut on '%s'") % start @@ -209,13 +209,23 @@ def restrict_text(self, raw_text, test_info): error_msg = _("String '%s' not found") % start test_info[self.name]["start"] = "%s" % (ERROR_STYLE, error_msg) if end: - position = restrict_text.rfind(end) - if position >= 0: - restrict_text = restrict_text[:position] - test_info[self.name]["end"] = _("Successful cut on '%s'") % end - else: - error_msg = _("String '%s' not found") % end + if not restrict_text or (restrict_text and not restrict_text.strip()): + error_msg = _( + "No text to cut, maybe because start string " + "was the very end of the document" + ) test_info[self.name]["end"] = "%s" % (ERROR_STYLE, error_msg) + else: + position = restrict_text.find(end) + if position >= 0: + restrict_text = restrict_text[:position] + test_info[self.name]["end"] = _("Successful cut on '%s'") % end + else: + error_msg = _("String '%s' not found") % end + test_info[self.name]["end"] = "%s" % ( + ERROR_STYLE, + error_msg, + ) return restrict_text def _get_date(self, parsed_inv, raw_text, partner_config, test_info): diff --git a/account_invoice_import_simple_pdf/tests/test_invoice_import.py b/account_invoice_import_simple_pdf/tests/test_invoice_import.py index c1bdb331fc..7c88f4fbd7 100644 --- a/account_invoice_import_simple_pdf/tests/test_invoice_import.py +++ b/account_invoice_import_simple_pdf/tests/test_invoice_import.py @@ -19,7 +19,6 @@ def setUp(self): "country_id": self.env.ref("base.fr").id, "simple_pdf_date_format": "dd-mm-y4", "simple_pdf_date_separator": "slash", - "lang": "fr_FR", } ) aiispfo = self.env["account.invoice.import.simple.pdf.fields"] @@ -172,6 +171,34 @@ def test_date_parsing(self): res_date = parsed_inv["date"] self.assertEqual(fields.Date.to_string(res_date), "2021-07-14") + def test_restrict_text(self): + cut_test = { + "T1 ici et là POUET là et par là POUET": { + "start": "POUET", + "end": "POUET", + "res": " là et par là ", + }, + "T2 ici et là POUET là et par là POUET": { + "start": "POUET", + "res": " là et par là POUET", + }, + "T3 ici et là POUET là et par là POUET": { + "end": "POUET", + "res": "T3 ici et là ", + }, + } + # I use the date field, but I could use any other field + self.test_info[self.date_field.name] = {} + for raw_txt, config in cut_test.items(): + self.date_field.write( + { + "start": config.get("start"), + "end": config.get("end"), + } + ) + res = self.date_field.restrict_text(raw_txt, self.test_info) + self.assertEqual(res, config["res"]) + def test_amount_parsing(self): amount_test = { "$ 1.02": { From 9c49f3347759bef4064dee01cde5067fb74d5ccb Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 15 Sep 2021 18:37:07 +0200 Subject: [PATCH 04/67] [FIX] when the invoice has the VAT number of the supplier and also the VAT number of the customer --- .../wizard/account_invoice_import.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/account_invoice_import_simple_pdf/wizard/account_invoice_import.py b/account_invoice_import_simple_pdf/wizard/account_invoice_import.py index bb58e9fbd5..d81f5f9bfc 100644 --- a/account_invoice_import_simple_pdf/wizard/account_invoice_import.py +++ b/account_invoice_import_simple_pdf/wizard/account_invoice_import.py @@ -58,6 +58,9 @@ def simple_pdf_match_partner(self, raw_text_no_space, test_results=None): test_results = [] partner_id = False rpo = self.env["res.partner"] + # Warning: invoices have the VAT number of the supplier, but they often + # also have the VAT number of the customer (i.e. the VAT number of our company) + # So we exclude it from the search partners = rpo.search_read( [ "|", @@ -65,6 +68,7 @@ def simple_pdf_match_partner(self, raw_text_no_space, test_results=None): ("simple_pdf_keyword", "!=", False), ("parent_id", "=", False), ("is_company", "=", True), + ("id", "!=", self.env.company.partner_id.id), ] ) for partner in partners: From 01429512fc40a568b8498a694187b917e58c6408 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 13 Oct 2021 10:02:48 +0200 Subject: [PATCH 05/67] [FIX] Read of the "Page Analysis" parameter Before this fix, Page Analysis was always "First page" in the code. FIX on view inheritance to avoid access right issues --- .../views/res_partner.xml | 11 +++++------ .../wizard/account_invoice_import.py | 4 +++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/account_invoice_import_simple_pdf/views/res_partner.xml b/account_invoice_import_simple_pdf/views/res_partner.xml index f4d80e3686..1c24cd28d9 100644 --- a/account_invoice_import_simple_pdf/views/res_partner.xml +++ b/account_invoice_import_simple_pdf/views/res_partner.xml @@ -13,20 +13,19 @@ name="inherit_id" ref="account_invoice_import.view_partner_property_form" /> +
    -
    +
    -
    @@ -47,6 +27,7 @@ string="Invoice Currency" options="{'no_create': True, 'no_create_edit': True}" /> + @@ -118,7 +99,7 @@ - + diff --git a/account_invoice_import_simple_pdf/migrations/14.0.1.0.1/pre-migration.py b/account_invoice_import_simple_pdf/migrations/14.0.1.0.1/pre-migration.py deleted file mode 100644 index 42fbf8f6ed..0000000000 --- a/account_invoice_import_simple_pdf/migrations/14.0.1.0.1/pre-migration.py +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright 2021 Akretion France (http://www.akretion.com/) -# @author: Alexis de Lattre -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -def migrate(cr, version): - if not version: - return - - # Fix typo coma -> comma - cr.execute( - """ - UPDATE res_partner SET simple_pdf_decimal_separator='comma' - WHERE simple_pdf_decimal_separator='coma' - """ - ) - - cr.execute( - """ - UPDATE res_partner SET simple_pdf_thousand_separator='comma' - WHERE simple_pdf_thousand_separator='coma' - """ - ) diff --git a/account_invoice_import_simple_pdf/wizard/__init__.py b/account_invoice_import_simple_pdf/wizard/__init__.py deleted file mode 100644 index 9817a01c22..0000000000 --- a/account_invoice_import_simple_pdf/wizard/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from . import account_invoice_import diff --git a/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml b/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml deleted file mode 100644 index c57a04fba5..0000000000 --- a/account_invoice_import_simple_pdf/wizard/account_invoice_import_view.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - account.invoice.import - - -
      -
    • regular PDF file without any embedded XML file via a pre-configuration in Odoo.
    • -
    -
    -
    - - -
    diff --git a/setup/account_edi_simple_pdf/odoo/addons/account_edi_simple_pdf b/setup/account_edi_simple_pdf/odoo/addons/account_edi_simple_pdf new file mode 120000 index 0000000000..38411f94ce --- /dev/null +++ b/setup/account_edi_simple_pdf/odoo/addons/account_edi_simple_pdf @@ -0,0 +1 @@ +../../../../account_edi_simple_pdf \ No newline at end of file diff --git a/setup/account_edi_simple_pdf/setup.py b/setup/account_edi_simple_pdf/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/account_edi_simple_pdf/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) From b002d7777774117accee9250eeea12f381004014 Mon Sep 17 00:00:00 2001 From: Holger Brunn Date: Wed, 4 Dec 2024 18:14:46 +0100 Subject: [PATCH 65/67] [MIG] account_edi_simple_pdf: migration to 16.0 --- account_edi_simple_pdf/README.rst | 10 +-- account_edi_simple_pdf/__manifest__.py | 2 +- account_edi_simple_pdf/models/account_move.py | 15 +++-- .../static/description/index.html | 7 +- .../tests/test_invoice_import.py | 3 +- account_edi_simple_pdf/views/res_partner.xml | 67 ++++++++++++------- requirements.txt | 3 + 7 files changed, 66 insertions(+), 41 deletions(-) diff --git a/account_edi_simple_pdf/README.rst b/account_edi_simple_pdf/README.rst index 64f61fc465..d2ab174601 100644 --- a/account_edi_simple_pdf/README.rst +++ b/account_edi_simple_pdf/README.rst @@ -17,13 +17,13 @@ Import Simple PDF :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github - :target: https://github.com/OCA/edi/tree/15.0/account_edi_simple_pdf + :target: https://github.com/OCA/edi/tree/16.0/account_edi_simple_pdf :alt: OCA/edi .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/edi-15-0/edi-15-0-account_edi_simple_pdf + :target: https://translation.odoo-community.org/projects/edi-16-0/edi-16-0-account_edi_simple_pdf :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/edi&target_branch=15.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/edi&target_branch=16.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -184,7 +184,7 @@ Bug Tracker Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -215,6 +215,6 @@ 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. -This module is part of the `OCA/edi `_ project on GitHub. +This module is part of the `OCA/edi `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/account_edi_simple_pdf/__manifest__.py b/account_edi_simple_pdf/__manifest__.py index 8de43d8003..9108952ae5 100644 --- a/account_edi_simple_pdf/__manifest__.py +++ b/account_edi_simple_pdf/__manifest__.py @@ -4,7 +4,7 @@ { "name": "Import Simple PDF", - "version": "15.0.4.0.0", + "version": "16.0.1.0.0", "category": "Accounting/Accounting", "license": "AGPL-3", "summary": "Import simple PDF vendor bills", diff --git a/account_edi_simple_pdf/models/account_move.py b/account_edi_simple_pdf/models/account_move.py index 63b3d14c5b..a8dc069fdf 100644 --- a/account_edi_simple_pdf/models/account_move.py +++ b/account_edi_simple_pdf/models/account_move.py @@ -8,8 +8,8 @@ class AccountMove(models.Model): _inherit = ["account.move", "account.invoice.import.simple.pdf.mixin"] _name = "account.move" - def _get_create_invoice_from_attachment_decoders(self): - return super()._get_create_invoice_from_attachment_decoders() + [ + def _get_create_document_from_attachment_decoders(self): + return super()._get_create_document_from_attachment_decoders() + [ (99, self._simple_pdf_create_invoice_from_attachment), ] @@ -18,8 +18,14 @@ def _simple_pdf_create_invoice_from_attachment(self, attachment): result = self.browse([]) if parsed_values.get("partner"): - journal = self._get_default_journal() - currency = self._get_default_currency() + new = self.new({}) + journal = new.journal_id + currency = ( + parsed_values.get("currency", {}).get( + "recordset", self.env["res.currency"] + ) + or new.currency_id + ) tax = self.env["account.tax"] amount_untaxed = currency.round( parsed_values.get( @@ -41,6 +47,7 @@ def _simple_pdf_create_invoice_from_attachment(self, attachment): ) result = self.create( { + "currency_id": currency.id, "partner_id": parsed_values["partner"] .get("recordset", self.env["res.partner"]) .id, diff --git a/account_edi_simple_pdf/static/description/index.html b/account_edi_simple_pdf/static/description/index.html index 4c2a2251e4..cefea1c6ac 100644 --- a/account_edi_simple_pdf/static/description/index.html +++ b/account_edi_simple_pdf/static/description/index.html @@ -1,4 +1,3 @@ - @@ -369,7 +368,7 @@

    Import Simple PDF

    !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:5ab1ebb7747c3603828622a11eb4d2fe9419bb3ccda3f1bbbcaa733014ddcb54 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

    Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runboat

    +

    Beta License: AGPL-3 OCA/edi Translate me on Weblate Try me on Runboat

    This module extends Odoo’s vendor bill import mechanism with support for simple PDF invoices i.e. PDF invoice that don’t have an embedded XML file.

    • Possibility to add support for a new vendor without developper skills: the accountant can do it!
    • @@ -517,7 +516,7 @@

      Bug Tracker

      Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed -feedback.

      +feedback.

      Do not contact contributors directly about support or help with technical issues.

    @@ -542,7 +541,7 @@

    Maintainers

    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.

    -

    This module is part of the OCA/edi project on GitHub.

    +

    This module is part of the OCA/edi project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    diff --git a/account_edi_simple_pdf/tests/test_invoice_import.py b/account_edi_simple_pdf/tests/test_invoice_import.py index 2c6ba93874..3e590105f6 100644 --- a/account_edi_simple_pdf/tests/test_invoice_import.py +++ b/account_edi_simple_pdf/tests/test_invoice_import.py @@ -5,10 +5,11 @@ import base64 from odoo import fields -from odoo.tests.common import TransactionCase +from odoo.tests.common import TransactionCase, tagged from odoo.tools import file_open, float_compare +@tagged("-at_install", "post_install") class TestInvoiceImport(TransactionCase): def setUp(self): super().setUp() diff --git a/account_edi_simple_pdf/views/res_partner.xml b/account_edi_simple_pdf/views/res_partner.xml index fc978a7d55..bcfa17a796 100644 --- a/account_edi_simple_pdf/views/res_partner.xml +++ b/account_edi_simple_pdf/views/res_partner.xml @@ -13,7 +13,7 @@ - + + + + - - - - +