diff --git a/document_quick_access_folder_auto_classification/README.rst b/document_quick_access_folder_auto_classification/README.rst new file mode 100644 index 0000000000..20bc71a097 --- /dev/null +++ b/document_quick_access_folder_auto_classification/README.rst @@ -0,0 +1,106 @@ +================================================ +Document Quick Access Folder Auto Classification +================================================ + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:14edc6aa1a5f02796dcc54b02bbaae83f3915aeb6d90a1d30ee32988de5f7170 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/16.0/document_quick_access_folder_auto_classification + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-16-0/server-ux-16-0-document_quick_access_folder_auto_classification + :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/server-ux&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module creates a job that scans all files from a storage and attaches them +to its record. The record is found using the document quick access rules. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +# Create an storage on your system using the storage project from OCA + +Usage +===== + +Users can drop the files on the folder (You may be able to configure your +scanner to send the files directly). +Then, they will be able to see the files attached to the expected record. +If two records matches the rules, it will be attached to both (two QRs). + +If the file matches no rules, it will be attached as a non processed documents. +Users should be able to assign which record to use + +# Access `Documents to process` +# Select a non processed document +# Assign or reject the document. When assigning it, the record will be asked. + +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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella +* Foram Shah + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +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. + +.. |maintainer-etobella| image:: https://github.com/etobella.png?size=40px + :target: https://github.com/etobella + :alt: etobella + +Current `maintainer `__: + +|maintainer-etobella| + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/document_quick_access_folder_auto_classification/__init__.py b/document_quick_access_folder_auto_classification/__init__.py new file mode 100644 index 0000000000..79dbb94082 --- /dev/null +++ b/document_quick_access_folder_auto_classification/__init__.py @@ -0,0 +1,3 @@ +from . import components +from . import models +from . import wizards diff --git a/document_quick_access_folder_auto_classification/__manifest__.py b/document_quick_access_folder_auto_classification/__manifest__.py new file mode 100644 index 0000000000..9502dc616b --- /dev/null +++ b/document_quick_access_folder_auto_classification/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Document Quick Access Folder Auto Classification", + "summary": """ + Auto classification of Documents after reading a QR""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "author": "Creu Blanca,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/server-ux", + "depends": ["document_quick_access", "edi_oca"], + "external_dependencies": { + "deb": ["libzbar0", "poppler-utils"], + "python": ["pyzbar", "pdf2image"], + }, + "data": [ + "data/edi_data.xml", + "security/security.xml", + "security/ir.model.access.csv", + "wizards/document_quick_access_missing_assign.xml", + "views/edi_exchange_record.xml", + ], + "maintainers": ["etobella"], +} diff --git a/document_quick_access_folder_auto_classification/components/__init__.py b/document_quick_access_folder_auto_classification/components/__init__.py new file mode 100644 index 0000000000..9f2b513d61 --- /dev/null +++ b/document_quick_access_folder_auto_classification/components/__init__.py @@ -0,0 +1 @@ +from . import document_quick_access_process diff --git a/document_quick_access_folder_auto_classification/components/document_quick_access_process.py b/document_quick_access_folder_auto_classification/components/document_quick_access_process.py new file mode 100644 index 0000000000..64235b0b9e --- /dev/null +++ b/document_quick_access_folder_auto_classification/components/document_quick_access_process.py @@ -0,0 +1,87 @@ +# Copyright 2021 Creu Blanca +# @author: Enric Tobella +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 +import logging +import traceback +from io import StringIO + +from odoo import _ +from odoo.exceptions import UserError + +from odoo.addons.component.core import Component + +_logger = logging.getLogger(__name__) + +try: + from pyzbar.pyzbar import ZBarSymbol, decode +except (ImportError, IOError) as err: + _logger.warning(err) +try: + import pdf2image + from pdf2image.exceptions import ( + PDFInfoNotInstalledError, + PDFPageCountError, + PDFSyntaxError, + ) +except (ImportError, IOError) as err: + _logger.warning(err) + + +class EdiDocumentQuickAccessProcess(Component): + _name = "edi.input.process.document.quick.access" + _usage = "input.process" + _backend_type = "document_quick_access" + _exchange_type = "document_quick_access" + _inherit = "edi.component.input.mixin" + + def process(self): + data = self.exchange_record.exchange_file + records = self._search_document_pdf(data) + for record in records: + self.env["ir.attachment"].create(self._get_attachment_vals(record)) + if records: + record = records[0] + self.exchange_record.write({"res_id": record.id, "model": record._name}) + elif self.env.context.get("document_quick_access_reject_file"): + return + else: + raise UserError(_("No file found")) + + def _get_attachment_vals(self, record): + return { + "name": self.exchange_record.exchange_filename, + "datas": self.exchange_record.exchange_file, + "res_id": record.id, + "res_model": record._name, + } + + def _search_document_pdf(self, datas): + if self.env.context.get("document_quick_access_reject_file"): + return [] + if self.env.context.get("force_object_process"): + return [self.env.context["force_object_process"]] + records = [] + try: + images = pdf2image.convert_from_bytes(base64.b64decode(datas)) + except (PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError) as e: + buff = StringIO() + traceback.print_exc(file=buff) + _logger.warning(buff.getvalue()) + raise UserError(str(e)) from e + for im in images: + records += self._search_pil_image(im) + return records + + def _search_pil_image(self, image): + results = decode(image, symbols=[ZBarSymbol.QRCODE]) + records = [] + rule_obj = self.env["document.quick.access.rule"] + for result in results: + record = rule_obj.with_context(no_raise_document_access=True).read_code( + result.data.decode("utf-8") + ) + if record and record not in records: + records += record + return records diff --git a/document_quick_access_folder_auto_classification/data/edi_data.xml b/document_quick_access_folder_auto_classification/data/edi_data.xml new file mode 100644 index 0000000000..c2d363ea23 --- /dev/null +++ b/document_quick_access_folder_auto_classification/data/edi_data.xml @@ -0,0 +1,18 @@ + + + + Document quick access + document_quick_access + + + Document quick access + document_quick_access + + input + + + + Document quick access auto classification + + + diff --git a/document_quick_access_folder_auto_classification/i18n/document_quick_access_folder_auto_classification.pot b/document_quick_access_folder_auto_classification/i18n/document_quick_access_folder_auto_classification.pot new file mode 100644 index 0000000000..97b7e69a05 --- /dev/null +++ b/document_quick_access_folder_auto_classification/i18n/document_quick_access_folder_auto_classification.pot @@ -0,0 +1,137 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * document_quick_access_folder_auto_classification +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_assign_form_view +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_form_view +msgid "Assign" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:res.groups,name:document_quick_access_folder_auto_classification.group_missing_document +msgid "Assigner" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_assign_form_view +msgid "Cancel" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__create_uid +msgid "Created by" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__create_date +msgid "Created on" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__display_name +msgid "Display Name" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.actions.act_window,name:document_quick_access_folder_auto_classification.document_quick_access_missing_act_window +msgid "Document Quick Access Missing" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.actions.act_window,name:document_quick_access_folder_auto_classification.document_quick_access_missing_assign_act_window +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_assign_form_view +msgid "Document Quick Access Missing Assign" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model,name:document_quick_access_folder_auto_classification.model_document_quick_access_rule +msgid "Document Quick Access Rule" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.ui.menu,name:document_quick_access_folder_auto_classification.document_quick_access_missing_menu +msgid "Documents to process" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__exchange_record_id +msgid "Exchange Record" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__id +msgid "ID" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign____last_update +msgid "Last Modified on" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__write_date +msgid "Last Updated on" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.module.category,name:document_quick_access_folder_auto_classification.ir_module_category_missing_document +msgid "Missing Documents" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: code:addons/document_quick_access_folder_auto_classification/components/document_quick_access_process.py:0 +#, python-format +msgid "No file found" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign__object_id +msgid "Object" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_search_view +msgid "Pending" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_search_view +msgid "Processed" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_form_view +msgid "Reject" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_search_view +msgid "Rejected" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model_terms:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_form_view +msgid "Related record" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model,name:document_quick_access_folder_auto_classification.model_document_quick_access_missing_assign +msgid "document.quick.access.missing.assign" +msgstr "" diff --git a/document_quick_access_folder_auto_classification/models/__init__.py b/document_quick_access_folder_auto_classification/models/__init__.py new file mode 100644 index 0000000000..3c5d0a0a66 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/__init__.py @@ -0,0 +1 @@ +from . import document_quick_access_rule diff --git a/document_quick_access_folder_auto_classification/models/document_quick_access_rule.py b/document_quick_access_folder_auto_classification/models/document_quick_access_rule.py new file mode 100644 index 0000000000..8fe88741fb --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/document_quick_access_rule.py @@ -0,0 +1,25 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo import api, models + +try: + from odoo.addons.queue_job.job import job +except ImportError: + job = lambda *args, **kwargs: lambda func: func # noqa: E731 + +_logger = logging.getLogger(__name__) + + +class DocumentQuickAccessRule(models.Model): + _inherit = "document.quick.access.rule" + + @api.model + def read_code(self, code): + try: + return super().read_code(code) + except Exception: + if self.env.context.get("no_raise_document_access", False): + return False + raise diff --git a/document_quick_access_folder_auto_classification/readme/CONFIGURE.rst b/document_quick_access_folder_auto_classification/readme/CONFIGURE.rst new file mode 100644 index 0000000000..a361274237 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/CONFIGURE.rst @@ -0,0 +1 @@ +# Create an storage on your system using the storage project from OCA diff --git a/document_quick_access_folder_auto_classification/readme/CONTRIBUTORS.rst b/document_quick_access_folder_auto_classification/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..d02190e2e4 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Enric Tobella +* Foram Shah diff --git a/document_quick_access_folder_auto_classification/readme/DESCRIPTION.rst b/document_quick_access_folder_auto_classification/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..dd561a3a07 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This module creates a job that scans all files from a storage and attaches them +to its record. The record is found using the document quick access rules. diff --git a/document_quick_access_folder_auto_classification/readme/USAGE.rst b/document_quick_access_folder_auto_classification/readme/USAGE.rst new file mode 100644 index 0000000000..3dd10e6829 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/USAGE.rst @@ -0,0 +1,11 @@ +Users can drop the files on the folder (You may be able to configure your +scanner to send the files directly). +Then, they will be able to see the files attached to the expected record. +If two records matches the rules, it will be attached to both (two QRs). + +If the file matches no rules, it will be attached as a non processed documents. +Users should be able to assign which record to use + +# Access `Documents to process` +# Select a non processed document +# Assign or reject the document. When assigning it, the record will be asked. diff --git a/document_quick_access_folder_auto_classification/security/ir.model.access.csv b/document_quick_access_folder_auto_classification/security/ir.model.access.csv new file mode 100644 index 0000000000..aff97561fe --- /dev/null +++ b/document_quick_access_folder_auto_classification/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_document_quick_access_missing_assign,access_document_quick_access_missing_assign,model_document_quick_access_missing_assign,base.group_user,1,1,1,1 diff --git a/document_quick_access_folder_auto_classification/security/security.xml b/document_quick_access_folder_auto_classification/security/security.xml new file mode 100644 index 0000000000..c40d257407 --- /dev/null +++ b/document_quick_access_folder_auto_classification/security/security.xml @@ -0,0 +1,16 @@ + + + + Missing Documents + + + Assigner + + + + + + diff --git a/document_quick_access_folder_auto_classification/static/description/icon.png b/document_quick_access_folder_auto_classification/static/description/icon.png new file mode 100644 index 0000000000..4426a82282 Binary files /dev/null and b/document_quick_access_folder_auto_classification/static/description/icon.png differ diff --git a/document_quick_access_folder_auto_classification/static/description/icon.svg b/document_quick_access_folder_auto_classification/static/description/icon.svg new file mode 100644 index 0000000000..eedade162a --- /dev/null +++ b/document_quick_access_folder_auto_classification/static/description/icon.svg @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff --git a/document_quick_access_folder_auto_classification/static/description/index.html b/document_quick_access_folder_auto_classification/static/description/index.html new file mode 100644 index 0000000000..328e97d0f0 --- /dev/null +++ b/document_quick_access_folder_auto_classification/static/description/index.html @@ -0,0 +1,443 @@ + + + + + + +Document Quick Access Folder Auto Classification + + + +
+

Document Quick Access Folder Auto Classification

+ + +

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

+

This module creates a job that scans all files from a storage and attaches them +to its record. The record is found using the document quick access rules.

+

Table of contents

+ +
+

Configuration

+

# Create an storage on your system using the storage project from OCA

+
+
+

Usage

+

Users can drop the files on the folder (You may be able to configure your +scanner to send the files directly). +Then, they will be able to see the files attached to the expected record. +If two records matches the rules, it will be attached to both (two QRs).

+

If the file matches no rules, it will be attached as a non processed documents. +Users should be able to assign which record to use

+

# Access Documents to process +# Select a non processed document +# Assign or reject the document. When assigning it, the record will be asked.

+
+
+

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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Creu Blanca
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

Current maintainer:

+

etobella

+

This module is part of the OCA/server-ux project on GitHub.

+

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

+
+
+
+ + diff --git a/document_quick_access_folder_auto_classification/tests/__init__.py b/document_quick_access_folder_auto_classification/tests/__init__.py new file mode 100644 index 0000000000..9248511c0e --- /dev/null +++ b/document_quick_access_folder_auto_classification/tests/__init__.py @@ -0,0 +1 @@ +from . import test_document_quick_access_auto_classification diff --git a/document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py b/document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py new file mode 100644 index 0000000000..c60034e523 --- /dev/null +++ b/document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py @@ -0,0 +1,240 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 + +from mock import patch + +from odoo import tools +from odoo.tools import mute_logger + +from odoo.addons.component.tests.common import TransactionComponentRegistryCase + + +class Encoded: + __slots__ = "data" + + def __init__(self, data): + self.data = data + + +class TestDocumentQuickAccessClassification(TransactionComponentRegistryCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, tracking_disable=True, test_queue_job_no_delay=True + ) + ) + + self = cls + self.exchange_model = self.env["edi.exchange.record"] + + self._setup_registry(self) + self._load_module_components(self, "component_event") + self._load_module_components(self, "edi") + self._load_module_components( + self, "document_quick_access_folder_auto_classification" + ) + self.backend = self.env["edi.backend"].create( + { + "name": "Demo Backend", + "backend_type_id": self.env.ref( + "document_quick_access_folder_auto_classification.backend_type" + ).id, + } + ) + self.model_id = self.env.ref("base.model_res_partner") + + def test_ok_pdf_multi(self): + partners = self.env["res.partner"].create({"name": "Partner 1"}) + partners |= self.env["res.partner"].create({"name": "Partner 2"}) + partners |= self.env["res.partner"].create({"name": "Partner 3"}) + partners |= self.env["res.partner"].create({"name": "Partner 4"}) + self.test_ok_pdf(partners) + + @mute_logger("odoo.addons.queue_job.models.base") + def test_ok_pdf(self, partners=False): + """Assign automatically PDFs to their assigned place""" + if not partners: + partners = self.env["res.partner"].create({"name": "Partner"}) + file = tools.file_open( + "addons/document_quick_access_folder_auto_classification/tests/test_file.pdf", + mode="rb", + ).read() + + self.env["document.quick.access.rule"].create( + { + "model_id": self.model_id.id, + "name": "PARTNER", + "priority": 1, + "barcode_format": "standard", + } + ) + code = [ + Encoded(partner.get_quick_access_code().encode("utf-8")) + for partner in partners + ] + with patch( + "odoo.addons.document_quick_access_folder_auto_classification." + "components.document_quick_access_process.decode" + ) as ptch: + ptch.return_value = code + self.backend.create_record( + "document_quick_access", + { + "exchange_filename": "test_file.pdf", + "exchange_file": base64.b64encode(file), + "edi_exchange_state": "input_received", + }, + ) + self.backend._cron_check_input_exchange_sync() + self.assertEqual(ptch.call_count, 1) + self.assertTrue(partners) + for partner in partners: + self.assertTrue( + self.env["ir.attachment"].search( + [("res_model", "=", partner._name), ("res_id", "=", partner.id)] + ) + ) + + @mute_logger("odoo.addons.queue_job.models.base") + def test_no_ok_assign(self): + """Assign failed files""" + file = tools.file_open( + "addons/document_quick_access_folder_auto_classification/tests/test_file.pdf", + mode="rb", + ).read() + self.backend.create_record( + "document_quick_access", + { + "exchange_filename": "test_file.pdf", + "exchange_file": base64.b64encode(file), + "edi_exchange_state": "input_received", + }, + ) + self.backend._cron_check_input_exchange_sync() + self.assertTrue( + self.exchange_model.search( + [ + ("backend_id", "=", self.backend.id), + ("exchange_filename", "=", "test_file.pdf"), + ("edi_exchange_state", "=", "input_processed_error"), + ] + ) + ) + partner = self.env["res.partner"].create({"name": "Partner"}) + missing = self.exchange_model.search( + [ + ("exchange_filename", "=", "test_file.pdf"), + ("edi_exchange_state", "=", "input_processed_error"), + ("backend_id", "=", self.backend.id), + ] + ) + self.assertTrue(missing) + action = missing.action_open_related_record() + self.assertFalse(action.keys()) + self.env["document.quick.access.rule"].create( + { + "model_id": self.model_id.id, + "name": "PARTNER", + "priority": 1, + "barcode_format": "standard", + } + ) + wizard = self.env["document.quick.access.missing.assign"].create( + { + "object_id": "{},{}".format(partner._name, partner.id), + "exchange_record_id": missing.id, + } + ) + wizard.manually_assign() + self.assertEqual(missing.edi_exchange_state, "input_processed") + action = missing.action_open_related_record() + self.assertEqual(partner._name, action["res_model"]) + self.assertEqual(partner.id, action["res_id"]) + + @mute_logger("odoo.addons.queue_job.models.base") + def test_failure(self): + """We will check that if a major exception raises all is handled""" + file = tools.file_open( + "addons/document_quick_access_folder_auto_classification/tests/test_file.pdf", + mode="rb", + ).read() + with self.assertRaises(TypeError): + with patch( + "odoo.addons.document_quick_access_folder_auto_classification." + "components.document_quick_access_process.decode" + ) as ptch: + ptch.return_value = 1 + self.backend.create_record( + "document_quick_access", + { + "exchange_filename": "test_file.pdf", + "exchange_file": base64.b64encode(file), + "edi_exchange_state": "input_received", + }, + ) + self.backend._cron_check_input_exchange_sync() + + @mute_logger("odoo.addons.queue_job.models.base") + def test_no_ok_reject(self): + """We will check that we can manage and reject failed files""" + file = tools.file_open( + "addons/document_quick_access_folder_auto_classification/tests/test_file.pdf", + mode="rb", + ).read() + self.backend.create_record( + "document_quick_access", + { + "exchange_filename": "test_file.pdf", + "exchange_file": base64.b64encode(file), + "edi_exchange_state": "input_received", + }, + ) + self.backend._cron_check_input_exchange_sync() + missing = self.exchange_model.search( + [ + ("exchange_filename", "=", "test_file.pdf"), + ("edi_exchange_state", "=", "input_processed_error"), + ("backend_id", "=", self.backend.id), + ] + ) + self.assertTrue(missing) + missing.with_context( + document_quick_access_reject_file=True + ).action_exchange_process() + missing.refresh() + self.assertEqual(missing.edi_exchange_state, "input_processed") + self.assertFalse(missing.model) + + @mute_logger("odoo.addons.queue_job.models.base") + def test_corrupted(self): + """We will check that corrupted files are stored also""" + file = tools.file_open( + "document_quick_access_folder_auto_classification/tests/test_file.pdf", + mode="rb", + ).read() + with mute_logger( + "odoo.addons.document_quick_access_folder_auto_classification." + "components.document_quick_access_process", + ): + self.backend.create_record( + "document_quick_access", + { + "exchange_filename": "test_file.pdf", + "exchange_file": base64.b64encode(file), + "edi_exchange_state": "input_received", + }, + ) + self.backend._cron_check_input_exchange_sync() + self.assertTrue( + self.exchange_model.search( + [ + ("backend_id", "=", self.backend.id), + ("exchange_filename", "=", "test_file.pdf"), + ("edi_exchange_state", "=", "input_processed_error"), + ] + ) + ) diff --git a/document_quick_access_folder_auto_classification/tests/test_file.pdf b/document_quick_access_folder_auto_classification/tests/test_file.pdf new file mode 100644 index 0000000000..95b437c7b7 Binary files /dev/null and b/document_quick_access_folder_auto_classification/tests/test_file.pdf differ diff --git a/document_quick_access_folder_auto_classification/views/edi_exchange_record.xml b/document_quick_access_folder_auto_classification/views/edi_exchange_record.xml new file mode 100644 index 0000000000..b3fdc43122 --- /dev/null +++ b/document_quick_access_folder_auto_classification/views/edi_exchange_record.xml @@ -0,0 +1,121 @@ + + + + + + edi.exchange.record.form (in document_quick_access_folder_auto_classification) + edi.exchange.record + 99 + +
+
+
+ +

+ +

+ + + + + + +
+
+
+
+ + document.quick.access.missing.search (in document_quick_access_folder_auto_classification) + edi.exchange.record + 99 + + + + + + + + + + + + document.quick.access.missing.tree (in document_quick_access_folder_auto_classification) + edi.exchange.record + 99 + + + + + + + + + + + Document Quick Access Missing + edi.exchange.record + tree,form + [("type_id.code", "=", "document_quick_access")] + {'search_default_pending': 1} + + + + +
diff --git a/document_quick_access_folder_auto_classification/wizards/__init__.py b/document_quick_access_folder_auto_classification/wizards/__init__.py new file mode 100644 index 0000000000..4d34fe7d8a --- /dev/null +++ b/document_quick_access_folder_auto_classification/wizards/__init__.py @@ -0,0 +1 @@ +from . import document_quick_access_missing_assign diff --git a/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.py b/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.py new file mode 100644 index 0000000000..4e10a66ba2 --- /dev/null +++ b/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.py @@ -0,0 +1,29 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class DocumentQuickAccessMissingAssign(models.TransientModel): + _name = "document.quick.access.missing.assign" + _description = "document.quick.access.missing.assign" + + @api.model + def document_quick_access_models(self): + models = self.env["document.quick.access.rule"].search([]).mapped("model_id") + res = [] + for model in models: + res.append((model.model, model.name)) + return res + + object_id = fields.Reference( + selection=lambda r: r.document_quick_access_models(), required=True + ) + exchange_record_id = fields.Many2one("edi.exchange.record", required=True) + + def manually_assign(self): + self.ensure_one() + self.exchange_record_id.backend_id.with_context( + force_object_process=self.object_id + ).exchange_process(self.exchange_record_id) + return True diff --git a/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.xml b/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.xml new file mode 100644 index 0000000000..e3e01e8565 --- /dev/null +++ b/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.xml @@ -0,0 +1,38 @@ + + + + + document.quick.access.missing.assign.form (in document_quick_access_folder_auto_classification) + document.quick.access.missing.assign + +
+ + + + +
+
+
+
+
+ + Document Quick Access Missing Assign + document.quick.access.missing.assign + form + {} + new + +
diff --git a/setup/document_quick_access_folder_auto_classification/odoo/addons/document_quick_access_folder_auto_classification b/setup/document_quick_access_folder_auto_classification/odoo/addons/document_quick_access_folder_auto_classification new file mode 120000 index 0000000000..63faa2ca59 --- /dev/null +++ b/setup/document_quick_access_folder_auto_classification/odoo/addons/document_quick_access_folder_auto_classification @@ -0,0 +1 @@ +../../../../document_quick_access_folder_auto_classification \ No newline at end of file diff --git a/setup/document_quick_access_folder_auto_classification/setup.py b/setup/document_quick_access_folder_auto_classification/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/document_quick_access_folder_auto_classification/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)