From 2af4ab87d0a2204de720d51fbe832bf9f825c0b9 Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 5 Jun 2019 14:17:22 +0200 Subject: [PATCH 01/29] [ADD] document_quick_access_folder_classification --- .../README.rst | 97 ++++ .../__init__.py | 2 + .../__manifest__.py | 33 ++ .../data/config_parameter.xml | 25 + .../data/cron_data.xml | 21 + .../models/__init__.py | 3 + .../models/document_quick_access_missing.py | 64 +++ .../models/document_quick_access_rule.py | 129 +++++ .../models/ir_attachment.py | 82 ++++ .../readme/CONFIGURE.rst | 4 + .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 2 + .../readme/USAGE.rst | 11 + .../security/ir.model.access.csv | 2 + .../security/security.xml | 17 + .../static/description/icon.png | Bin 0 -> 7280 bytes .../static/description/icon.svg | 178 +++++++ .../static/description/index.html | 443 ++++++++++++++++++ .../tests/__init__.py | 1 + ...cument_quick_access_auto_classification.py | 266 +++++++++++ .../tests/test_file.pdf | Bin 0 -> 1138 bytes .../views/document_quick_access_missing.xml | 80 ++++ .../wizards/__init__.py | 1 + .../document_quick_access_missing_assign.py | 35 ++ .../document_quick_access_missing_assign.xml | 38 ++ 25 files changed, 1535 insertions(+) create mode 100644 document_quick_access_folder_auto_classification/README.rst create mode 100644 document_quick_access_folder_auto_classification/__init__.py create mode 100644 document_quick_access_folder_auto_classification/__manifest__.py create mode 100644 document_quick_access_folder_auto_classification/data/config_parameter.xml create mode 100644 document_quick_access_folder_auto_classification/data/cron_data.xml create mode 100644 document_quick_access_folder_auto_classification/models/__init__.py create mode 100644 document_quick_access_folder_auto_classification/models/document_quick_access_missing.py create mode 100644 document_quick_access_folder_auto_classification/models/document_quick_access_rule.py create mode 100644 document_quick_access_folder_auto_classification/models/ir_attachment.py create mode 100644 document_quick_access_folder_auto_classification/readme/CONFIGURE.rst create mode 100644 document_quick_access_folder_auto_classification/readme/CONTRIBUTORS.rst create mode 100644 document_quick_access_folder_auto_classification/readme/DESCRIPTION.rst create mode 100644 document_quick_access_folder_auto_classification/readme/USAGE.rst create mode 100644 document_quick_access_folder_auto_classification/security/ir.model.access.csv create mode 100644 document_quick_access_folder_auto_classification/security/security.xml create mode 100644 document_quick_access_folder_auto_classification/static/description/icon.png create mode 100644 document_quick_access_folder_auto_classification/static/description/icon.svg create mode 100644 document_quick_access_folder_auto_classification/static/description/index.html create mode 100644 document_quick_access_folder_auto_classification/tests/__init__.py create mode 100644 document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py create mode 100644 document_quick_access_folder_auto_classification/tests/test_file.pdf create mode 100644 document_quick_access_folder_auto_classification/views/document_quick_access_missing.xml create mode 100644 document_quick_access_folder_auto_classification/wizards/__init__.py create mode 100644 document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.py create mode 100644 document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.xml 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..e53774afae --- /dev/null +++ b/document_quick_access_folder_auto_classification/README.rst @@ -0,0 +1,97 @@ +================================================ +Document Quick Access Folder Auto Classification +================================================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/11.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-11-0/server-ux-11-0-document_quick_access_folder_auto_classification + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/250/11.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module creates a job that scans all files from a folder and attaches them +to its record. The record is found using the document quick access rules. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +# Create 3 folders on your odoo system. Odoo will use them for Preprocessing, + Store processed (not required), Store failed (not required) +# Access your system parameters and edit the parameters in order to match your + folders + +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 smashing 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 + +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. + +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..aee8895e7a --- /dev/null +++ b/document_quick_access_folder_auto_classification/__init__.py @@ -0,0 +1,2 @@ +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..b9f9b9968d --- /dev/null +++ b/document_quick_access_folder_auto_classification/__manifest__.py @@ -0,0 +1,33 @@ +# 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': '11.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', + 'queue_job', + ], + 'data': [ + 'security/security.xml', + 'security/ir.model.access.csv', + 'wizards/document_quick_access_missing_assign.xml', + 'views/document_quick_access_missing.xml', + 'data/config_parameter.xml', + 'data/cron_data.xml', + ], + 'external_dependencies': { + 'python': [ + 'pyzbar', + 'pdf2image', + ], + }, + 'maintainers': [ + 'etobella', + ], +} diff --git a/document_quick_access_folder_auto_classification/data/config_parameter.xml b/document_quick_access_folder_auto_classification/data/config_parameter.xml new file mode 100644 index 0000000000..5f3e0313d6 --- /dev/null +++ b/document_quick_access_folder_auto_classification/data/config_parameter.xml @@ -0,0 +1,25 @@ + + + + + document_quick_access_auto_classification.path + /opt/qr_data + + + document_quick_access_auto_classification.ok_path + /opt/ok_qr_data + + + document_quick_access_auto_classification.failure_path + /opt/ko_qr_data + + + document_quick_access_auto_classification.process_path + /opt/process_data + + + diff --git a/document_quick_access_folder_auto_classification/data/cron_data.xml b/document_quick_access_folder_auto_classification/data/cron_data.xml new file mode 100644 index 0000000000..7e669999c2 --- /dev/null +++ b/document_quick_access_folder_auto_classification/data/cron_data.xml @@ -0,0 +1,21 @@ + + + + + + + Process documents + + + code + model.cron_folder_auto_classification() + 15 + minutes + -1 + + + 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..a525671c0f --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/__init__.py @@ -0,0 +1,3 @@ +from . import document_quick_access_rule +from . import document_quick_access_missing +from . import ir_attachment diff --git a/document_quick_access_folder_auto_classification/models/document_quick_access_missing.py b/document_quick_access_folder_auto_classification/models/document_quick_access_missing.py new file mode 100644 index 0000000000..dc3d760297 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/document_quick_access_missing.py @@ -0,0 +1,64 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class DocumentQuickAccessMissing(models.Model): + _name = 'document.quick.access.missing' + _description = 'Missing Document' + + name = fields.Char(required=True, readonly=True) + data = fields.Binary(related='attachment_id.datas', readonly=True) + state = fields.Selection([ + ('pending', 'Pending'), + ('processed', 'Processed'), + ('deleted', 'Rejected') + ], default='pending') + model = fields.Char(readonly=True) + res_id = fields.Integer(readonly=True) + attachment_id = fields.Many2one('ir.attachment', readonly=True) + + @api.multi + def assign_model(self, model, res_id): + records = self.filtered(lambda r: r.state == 'pending') + res = self.env[model].browse(res_id) + res.ensure_one() + for record in records: + record.attachment_id.write({ + 'res_model': model, + 'res_id': res_id + }) + records.write(self._processed_values(model, res_id)) + + def _processed_values(self, model, res_id): + return { + 'state': 'processed', + 'model': model, + 'res_id': res_id + } + + def _deleted_values(self): + return { + 'state': 'deleted' + } + + def access_resource(self): + self.ensure_one() + if not self.model: + return {} + record = self.env[self.model].browse(self.res_id).exists() + if not record: + return {} + return { + "type": "ir.actions.act_window", + "res_model": record._name, + "views": [[record.get_formview_id(), "form"]], + "res_id": record.id, + } + + @api.multi + def reject_assign_document(self): + self.filtered(lambda r: r.state == 'pending').write( + self._deleted_values() + ) 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..4009f04425 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/document_quick_access_rule.py @@ -0,0 +1,129 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import os +import base64 +import shutil +from odoo import api, models +from odoo.addons.queue_job.job import job +from odoo.modules.registry import Registry +import logging +_logger = logging.getLogger(__name__) + + +class OCRException(Exception): + def __init__(self, name): + self.name = name + + +class DocumentQuickAccessRule(models.Model): + _inherit = 'document.quick.access.rule' + + @api.model + def cron_folder_auto_classification( + self, path=False, processing_path=False, limit=False + ): + if not path: + path = self.env['ir.config_parameter'].sudo().get_param( + 'document_quick_access_auto_classification.path', + default=False) + if not path: + return False + if not processing_path and not self.env.context.get( + 'ignore_process_path' + ): + processing_path = self.env[ + 'ir.config_parameter' + ].sudo().get_param( + 'document_quick_access_auto_classification.process_path', + default=False) + elements = [os.path.join( + path, f + ) for f in os.listdir(path) if os.path.isfile(os.path.join(path, f))] + if limit: + elements = elements[:limit] + for element in elements: + obj = self + new_element = element + if processing_path: + new_cr = Registry(self.env.cr.dbname).cursor() + try: + if processing_path: + new_element = os.path.join( + processing_path, os.path.basename(element)) + shutil.copy(element, new_element) + obj = api.Environment( + new_cr, self.env.uid, self.env.context + )[self._name].browse().with_delay(**self._delay_vals()) + obj._process_document(new_element) + if processing_path: + new_cr.commit() + except Exception: + if processing_path: + os.unlink(new_element) + new_cr.rollback() + raise + finally: + if processing_path: + new_cr.close() + if processing_path: + os.unlink(element) + return True + + @api.model + def _delay_vals(self): + return {} + + @api.model + def process_document(self, filename, datas): + attachment = self.env['ir.attachment'].create( + self._get_attachment_vals(filename, datas) + ) + return attachment._process_quick_access_rules() + + @api.model + @job(default_channel='root.document_quick_access_classification') + def _process_document(self, element): + try: + filename = os.path.basename(element) + datas = base64.b64encode(open(element, 'rb').read()) + results = self.process_document(filename, datas) + return self._postprocess_document(element, results) + except OCRException: + _logger.warning('Element %s was corrupted' % element) + os.unlink(element) + + @api.model + def _postprocess_document(self, path, results): + filename = os.path.basename(path) + if any(result.res_id for result in results): + new_path = self.env['ir.config_parameter'].sudo().get_param( + 'document_quick_access_auto_classification.ok_path', + default=False) + else: + new_path = self.env['ir.config_parameter'].sudo().get_param( + 'document_quick_access_auto_classification.failure_path', + default=False) + self.env['document.quick.access.missing'].create({ + 'name': filename, + 'attachment_id': results.id + }) + if new_path: + shutil.copy(path, os.path.join(new_path, filename)) + os.unlink(path) + return bool(results) + + def _get_attachment_vals(self, filename, datas): + return { + 'name': filename, + 'datas': datas, + 'datas_fname': filename, + } + + @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/models/ir_attachment.py b/document_quick_access_folder_auto_classification/models/ir_attachment.py new file mode 100644 index 0000000000..aaaba75732 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/ir_attachment.py @@ -0,0 +1,82 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import base64 +from io import StringIO +import logging +import traceback +from .document_quick_access_rule import OCRException +from odoo import api, models + +_logger = logging.getLogger(__name__) + +try: + from pyzbar.pyzbar import decode, ZBarSymbol +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 IrAttachment(models.Model): + _inherit = 'ir.attachment' + + def _search_document_pdf(self): + self.ensure_one() + records = [] + try: + images = pdf2image.convert_from_bytes(base64.b64decode(self.datas)) + except ( + PDFInfoNotInstalledError, PDFPageCountError, PDFSyntaxError + ) as e: + buff = StringIO() + traceback.print_exc(file=buff) + _logger.warning(buff.getvalue()) + raise OCRException(str(e)) + for im in images: + records += self._search_pil_image(im) + return records + + @api.model + 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 + + def _search_document(self): + if self.mimetype == 'application/pdf': + return self._search_document_pdf() + return [] + + def _process_quick_access_rules(self): + self.ensure_one() + results = self.env['ir.attachment'] + records = self._search_document() + for record in records: + if not results: + result = self + result.write({ + 'res_id': record.id, + 'res_model': record._name + }) + else: + result = self.copy({ + 'res_id': record.id, + 'res_model': record._name + }) + results |= result + return results or self 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..1402bc6781 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +# Create 3 folders on your odoo system. Odoo will use them for Preprocessing, + Store processed (not required), Store failed (not required) +# Access your system parameters and edit the parameters in order to match your + folders \ No newline at end of file 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..93ec993e04 --- /dev/null +++ b/document_quick_access_folder_auto_classification/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Enric Tobella 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..c930885f67 --- /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 folder 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..696fe2cd87 --- /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,access_document_quick_access_missing,model_document_quick_access_missing,group_missing_document,1,1,1,0 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..0193f6e793 --- /dev/null +++ b/document_quick_access_folder_auto_classification/security/security.xml @@ -0,0 +1,17 @@ + + + + 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 0000000000000000000000000000000000000000..4426a8228250d68e6f033e2274b692405c5f43cf GIT binary patch literal 7280 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 + + + + + + + + + + + + + + + + + + + + + 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..dfc9b568a2 --- /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 Runbot

+

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

+

Table of contents

+ +
+

Configuration

+
+
# Create 3 folders on your odoo system. Odoo will use them for Preprocessing,
+
Store processed (not required), Store failed (not required)
+
# Access your system parameters and edit the parameters in order to match your
+
folders
+
+
+
+

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 smashing 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.

+

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..f0934e54d4 --- /dev/null +++ b/document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py @@ -0,0 +1,266 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os +from odoo import tools +from odoo.tools import mute_logger +from odoo.tests.common import TransactionCase +from tempfile import TemporaryDirectory +from mock import patch + + +class Encoded: + def __init__(self, data): + self.data = data + + +class TestDocumentQuickAccessClassification(TransactionCase): + + def setUp(self): + super().setUp() + self.tmpdir = TemporaryDirectory() + self.ok_tmpdir = TemporaryDirectory() + self.no_ok_tmpdir = TemporaryDirectory() + self.env['ir.config_parameter'].set_param( + 'document_quick_access_auto_classification.path', + self.tmpdir.name + ) + self.env['ir.config_parameter'].set_param( + 'document_quick_access_auto_classification.ok_path', + self.ok_tmpdir.name + ) + self.env['ir.config_parameter'].set_param( + 'document_quick_access_auto_classification.failure_path', + self.no_ok_tmpdir.name + ) + self.model_id = self.env.ref('base.model_res_partner') + + def tearDown(self): + super().tearDown() + self.tmpdir.cleanup() + self.ok_tmpdir.cleanup() + self.no_ok_tmpdir.cleanup() + + 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) + + def test_ok_pdf_multi_limit(self): + """Limit the number of files to process""" + partner = self.env['res.partner'].create({ + 'name': 'Partner 1', + }) + self.env['document.quick.access.rule'].create({ + 'model_id': self.model_id.id, + 'name': 'PARTNER', + 'priority': 1, + 'barcode_format': 'standard', + }) + file = tools.file_open( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification" + "/tests" + ).read() + self.env['document.quick.access.rule'].create({ + 'model_id': self.model_id.id, + 'name': 'PARTNER', + 'priority': 1, + 'barcode_format': 'standard', + }) + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file) + with open(os.path.join( + self.tmpdir.name, 'test_file_2.pdf' + ), 'wb') as f: + f.write(file) + code = [Encoded(partner.get_quick_access_code().encode('utf-8'))] + with patch( + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.ir_attachment.decode' + ) as ptch: + ptch.return_value = code + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification(limit=1) + ptch.assert_called_once() + attachments = self.env['ir.attachment'].search([ + ('res_model', '=', partner._name), + ('res_id', '=', partner.id) + ]) + self.assertTrue(attachments) + self.assertEqual(1, len(attachments)) + self.assertTrue(os.path.exists( + os.path.join(self.ok_tmpdir.name, 'test_file.pdf'))) + self.assertFalse(os.path.exists( + os.path.join(self.ok_tmpdir.name, 'test_file_2.pdf'))) + self.assertFalse(os.path.exists( + os.path.join(self.tmpdir.name, 'test_file.pdf'))) + self.assertTrue(os.path.exists( + os.path.join(self.tmpdir.name, 'test_file_2.pdf'))) + + 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( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification" + "/tests" + ).read() + + self.env['document.quick.access.rule'].create({ + 'model_id': self.model_id.id, + 'name': 'PARTNER', + 'priority': 1, + 'barcode_format': 'standard', + }) + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file) + code = [ + Encoded(partner.get_quick_access_code().encode('utf-8')) + for partner in partners + ] + with patch( + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.ir_attachment.decode' + ) as ptch: + ptch.return_value = code + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification() + ptch.assert_called() + self.assertTrue(partners) + for partner in partners: + self.assertTrue(self.env['ir.attachment'].search([ + ('res_model', '=', partner._name), + ('res_id', '=', partner.id) + ])) + self.assertTrue(os.path.exists( + os.path.join(self.ok_tmpdir.name, 'test_file.pdf'))) + + def test_no_ok_assign(self): + """Assign failed files""" + file = tools.file_open( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification/" + "tests" + ).read() + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file) + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification() + self.assertTrue(os.path.exists( + os.path.join(self.no_ok_tmpdir.name, 'test_file.pdf'))) + partner = self.env['res.partner'].create({ + 'name': 'Partner', + }) + missing = self.env['document.quick.access.missing'].search([ + ('name', '=', 'test_file.pdf'), + ('state', '=', 'pending') + ]) + self.assertTrue(missing) + action = missing.access_resource() + 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': '%s,%s' % (partner._name, partner.id), + 'missing_document_id': missing.id, + }) + wizard.doit() + self.assertEqual(missing.state, 'processed') + action = missing.access_resource() + self.assertEqual(partner._name, action['res_model']) + self.assertEqual(partner.id, action['res_id']) + + def test_failure(self): + """We will check that if a major exception raises all is handled""" + file = tools.file_open( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification/" + "tests" + ).read() + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file) + with self.assertRaises(TypeError): + with patch( + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.ir_attachment.decode' + ) as ptch: + ptch.return_value = 1 + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification() + + def test_no_ok_reject(self): + """We will check that we can manage and reject failed files""" + file = tools.file_open( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification/" + "tests" + ).read() + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file) + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification() + self.assertTrue(os.path.exists( + os.path.join(self.no_ok_tmpdir.name, 'test_file.pdf'))) + missing = self.env['document.quick.access.missing'].search([ + ('name', '=', 'test_file.pdf'), + ('state', '=', 'pending') + ]) + self.assertTrue(missing) + missing.reject_assign_document() + self.assertEqual(missing.state, 'deleted') + + def test_corrupted(self): + """We will check that corrupted files are removed""" + file = tools.file_open( + 'test_file.pdf', + mode="rb", + subdir="addons/document_quick_access_folder_auto_classification/" + "tests" + ).read() + with open(os.path.join(self.tmpdir.name, 'test_file.pdf'), 'wb') as f: + f.write(file[:int(len(file)/2)]) + with mute_logger( + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.document_quick_access_rule', + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.ir_attachment' + ): + self.env['document.quick.access.rule'].with_context( + ignore_process_path=True + ).cron_folder_auto_classification() + self.assertFalse( + os.path.exists(os.path.join(self.ok_tmpdir.name, 'test_file.pdf')) + ) + self.assertFalse(os.path.exists( + os.path.join(self.no_ok_tmpdir.name, 'test_file.pdf'))) + self.assertFalse( + os.path.exists(os.path.join(self.tmpdir.name, 'test_file.pdf')) + ) 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 0000000000000000000000000000000000000000..95b437c7b76206f2207cdf439da4bf5d8e2ad943 GIT binary patch literal 1138 zcmah|-D=w~6z)wKsF!^K!3b?PT1l4V*dq%|?5LD>v^crg7JA`1YML#zf~|~nx1(<{ z7>r%_a#y>{3+yfS8av8gLpqqjmh|~M-#Pk@RJn-NAv+SNGW{|AHT^mLhCEEMdH(^` zYUE7keS3{LAPG@}q16RZ!_?|nXQrp?byV6yr#EQwmH+0Q{Xfq-bgS7uZNGfFy|Wv= z+`0SSxgQt#4bTXUhCc`YG9Hs@+tn7*pBM4QE-hw6YoE6-JgFA+H%^vW)F+fCduGGo{wTgy=0 z$PlGA9h&~kk#g<@BIhHWH!?dgd3P>F-n@N>^YL&v|4@)VOY{D|E)QGngLS8a2mJ`n zgf&G?wUnY%Rw=D|l=>lz0!Hc9^;ze7<4iAkBARo2;LiBK6A&XC4=aOfObTHI~yP@sDXN^OW7-O;P zFkFa}{K{b04L6T*7uUe4h9_gM2?D-s%2_vt#gI`0uA{mqyPW_ literal 0 HcmV?d00001 diff --git a/document_quick_access_folder_auto_classification/views/document_quick_access_missing.xml b/document_quick_access_folder_auto_classification/views/document_quick_access_missing.xml new file mode 100644 index 0000000000..2dafb3c08c --- /dev/null +++ b/document_quick_access_folder_auto_classification/views/document_quick_access_missing.xml @@ -0,0 +1,80 @@ + + + + + + + document.quick.access.missing.form (in document_quick_access_folder_auto_classification) + document.quick.access.missing + +
+
+
+ +

+ +

+ + + + + + +
+
+
+
+ + + document.quick.access.missing.search (in document_quick_access_folder_auto_classification) + document.quick.access.missing + + + + + + + + + + + + + document.quick.access.missing.tree (in document_quick_access_folder_auto_classification) + document.quick.access.missing + + + + + + + + + + + Document Quick Access Missing + document.quick.access.missing + tree,form + [] + {'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..fa5bb7ee83 --- /dev/null +++ b/document_quick_access_folder_auto_classification/wizards/document_quick_access_missing_assign.py @@ -0,0 +1,35 @@ +# 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' + + @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, + ) + missing_document_id = fields.Many2one( + 'document.quick.access.missing', + required=True, + ) + + @api.multi + def doit(self): + self.ensure_one() + self.missing_document_id.assign_model( + self.object_id._name, self.object_id.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..3986eed4d9 --- /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 + + + +
From e6388527b2c121a0ea7ccd11dd9cef19211a296f Mon Sep 17 00:00:00 2001 From: oca-travis Date: Fri, 29 Nov 2019 15:05:27 +0000 Subject: [PATCH 02/29] [UPD] Update document_quick_access_folder_auto_classification.pot --- ...uick_access_folder_auto_classification.pot | 189 ++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 document_quick_access_folder_auto_classification/i18n/document_quick_access_folder_auto_classification.pot 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..7cf9eae27e --- /dev/null +++ b/document_quick_access_folder_auto_classification/i18n/document_quick_access_folder_auto_classification.pot @@ -0,0 +1,189 @@ +# 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 11.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:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_form_view +msgid "Access" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model: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:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_attachment_id +msgid "Attachment" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model: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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_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: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_data +msgid "File Content" +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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_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___last_update +#: 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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_write_date +msgid "Last Updated on" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model,name:document_quick_access_folder_auto_classification.model_document_quick_access_missing +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_assign_missing_document_id +msgid "Missing Document" +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 +#: model:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_model +msgid "Model" +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_name +msgid "Name" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.ui.view,arch_db:document_quick_access_folder_auto_classification.document_quick_access_missing_assign_form_view +msgid "OK" +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 +#: selection:document.quick.access.missing,state:0 +#: model: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:ir.actions.server,name:document_quick_access_folder_auto_classification.process_documents_ir_actions_server +#: model:ir.cron,cron_name:document_quick_access_folder_auto_classification.process_documents +#: model:ir.cron,name:document_quick_access_folder_auto_classification.process_documents +msgid "Process documents" +msgstr "" + +#. module: document_quick_access_folder_auto_classification +#: selection:document.quick.access.missing,state:0 +#: model: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: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 +#: selection:document.quick.access.missing,state:0 +#: model: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:ir.model.fields,field_description:document_quick_access_folder_auto_classification.field_document_quick_access_missing_res_id +msgid "Res" +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_state +msgid "State" +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 "" + +#. module: document_quick_access_folder_auto_classification +#: model:ir.model,name:document_quick_access_folder_auto_classification.model_ir_attachment +msgid "ir.attachment" +msgstr "" + From 22693125f95cb33551e803d92bd16b0859b77476 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Fri, 29 Nov 2019 15:39:56 +0000 Subject: [PATCH 03/29] [UPD] README.rst --- .../README.rst | 8 ++++++++ .../static/description/index.html | 4 +++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/document_quick_access_folder_auto_classification/README.rst b/document_quick_access_folder_auto_classification/README.rst index e53774afae..f99580abd1 100644 --- a/document_quick_access_folder_auto_classification/README.rst +++ b/document_quick_access_folder_auto_classification/README.rst @@ -92,6 +92,14 @@ 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/static/description/index.html b/document_quick_access_folder_auto_classification/static/description/index.html index dfc9b568a2..9142d5b5f1 100644 --- a/document_quick_access_folder_auto_classification/static/description/index.html +++ b/document_quick_access_folder_auto_classification/static/description/index.html @@ -3,7 +3,7 @@ - + Document Quick Access Folder Auto Classification