From f53a8a0ee5ed32aee32588fdcac3b420c831577f Mon Sep 17 00:00:00 2001 From: Enric Tobella Date: Wed, 5 Jun 2019 10:34:16 +0200 Subject: [PATCH] [ADD] document_quick_access_folder_classification --- .travis.yml | 2 + .../README.rst | 97 ++++ .../__init__.py | 2 + .../__manifest__.py | 30 ++ .../data/config_parameter.xml | 25 + .../data/cron_data.xml | 21 + .../models/__init__.py | 2 + .../models/document_quick_access_missing.py | 62 +++ .../models/document_quick_access_rule.py | 182 +++++++ .../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 -> 9455 bytes .../static/description/index.html | 443 ++++++++++++++++++ .../tests/__init__.py | 1 + ...cument_quick_access_auto_classification.py | 160 +++++++ .../tests/test_file.pdf | Bin 0 -> 2186 bytes .../views/document_quick_access_missing.xml | 77 +++ .../wizards/__init__.py | 1 + .../document_quick_access_missing_assign.py | 35 ++ .../document_quick_access_missing_assign.xml | 38 ++ oca_dependencies.txt | 1 + requirements.txt | 2 + 26 files changed, 1218 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/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/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 create mode 100644 requirements.txt diff --git a/.travis.yml b/.travis.yml index 62964d4..0810199 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,8 @@ addons: packages: - expect-dev # provides unbuffer utility - python-lxml # because pip installation is slow + - libzbar-dev + - poppler-utils env: global: 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 0000000..e53774a --- /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 0000000..aee8895 --- /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 0000000..d91c4ee --- /dev/null +++ b/document_quick_access_folder_auto_classification/__manifest__.py @@ -0,0 +1,30 @@ +# 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', + ], + }, +} 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 0000000..5f3e031 --- /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 0000000..d10a8f2 --- /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 0000000..68319b7 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/__init__.py @@ -0,0 +1,2 @@ +from . import document_quick_access_rule +from . import document_quick_access_missing 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 0000000..8e76f1a --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/document_quick_access_missing.py @@ -0,0 +1,62 @@ +# 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(attachment=True, required=True, readonly=True) + state = fields.Selection([ + ('pending', 'Pending'), + ('processed', 'Processed'), + ('deleted', 'Rejected') + ], default='pending') + model = fields.Char(readonly=True) + res_id = fields.Integer(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: + self.env['document.quick.access.rule']._assign_document( + record.name, record.data, res + ) + 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 0000000..2004908 --- /dev/null +++ b/document_quick_access_folder_auto_classification/models/document_quick_access_rule.py @@ -0,0 +1,182 @@ +# Copyright 2019 Creu Blanca +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from io import StringIO +import os +import base64 +import mimetypes +import shutil +from odoo import api, models +from odoo.addons.queue_job.job import job +from odoo.modules.registry import Registry +import logging +import traceback +_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 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 + @job(default_channel='root.document_quick_access_classification') + def _process_document(self, element): + try: + results = self._search_document(element) + 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) + datas = base64.b64encode(open(path, 'rb').read()) + if results: + for result in results: + self._assign_document(filename, datas, result) + 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, + 'data': datas, + }) + 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, record): + return { + 'name': filename, + 'datas': datas, + 'datas_fname': filename, + 'res_model': record._name, + 'res_id': record.id, + 'mimetype': mimetypes.guess_type(filename) + } + + def _assign_document(self, filename, datas, record): + self.env['ir.attachment'].create( + self._get_attachment_vals(filename, datas, record) + ) + + @api.model + def _search_document_pdf(self, path): + records = [] + try: + images = pdf2image.convert_from_bytes( + open(path, 'rb').read()) + 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 = [] + for result in results: + record = self.with_context( + no_raise_document_access=True).read_code(result) + if record: + records += record + return records + + @api.model + def _search_document(self, path): + filename, extension = os.path.splitext(path) + if extension == '.pdf': + return self._search_document_pdf(path) + return [] + + @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 0000000..1402bc6 --- /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 0000000..93ec993 --- /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 0000000..c930885 --- /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 0000000..3dd10e6 --- /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 0000000..696fe2c --- /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 0000000..0193f6e --- /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..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 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 0000000..dfc9b56 --- /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 0000000..9248511 --- /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 0000000..e0beb63 --- /dev/null +++ b/document_quick_access_folder_auto_classification/tests/test_document_quick_access_auto_classification.py @@ -0,0 +1,160 @@ +# 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 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(self): + partner = 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 = partner.get_quick_access_code() + with patch( + 'odoo.addons.document_quick_access_folder_auto_classification.' + 'models.document_quick_access_rule.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(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): + 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_no_ok_reject(self): + 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): + 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' + ): + 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..750d16c83fd9eb2d48dd9d0ade7dc63cd9d89e40 GIT binary patch literal 2186 zcmb_dTTB#J7)BAbL#-q#EsZu~2@1L}_t_Z(RCi{VE-L|dS1zK4VfFxv%g#D8YgkjS z6su92^n%ew>aE+vLKNFzz3_l;ftvP5j$A5_#j;{w#Cl@qW5+NWCvmN|5zEgfR)b)@*?=<;aT5-Izyvzs2q-3y zfvWjHVIgLiI#8Oh=A+C_#+!X_nU_qmd0d`?%#_r5QbB6#N$;6ZZfpLTm94gvOeV8{ zNiif(ftq3@r|s@fUv{`^`hpg=B?E6uuW4I?^N8<-Ua^p1{3XU ze$yxex}-IQv>*tYxG>^0hn&Wy@S2dcfTnvwiV6rBZ1JnWU1Y-uPl;k|!uj~E@qu~x z$&Fw#^=k6-zu#%!m@0>y#hbVM7*hv+Q+M`koW1z{G~4|nLl64(^Fx7#+38&~P81(( zzmt1!&gDMoihEGZz0!2@=bLGknv09lmv56|oo`h3o_ziJ!t@TNlDEDnz8~va zy|(+kzFE8NfeXLBSZFzt&h_o^^D|tx(<~r%wqcFrZ|)X7(_N>N&-r#JcQ)TW<$ZYe zuZo(g`t}z41GU|{s;Z*=QdZaA%bk0TuI%=#H_W?xb(SSMxAK?H(|ea3JYald4EC*<<%(?IX1RX!bnT;K zeeKs4_oP~)y^kx3Bhk`9XATbzM_Zx?gp3=t&v*YY|59B!Gv88^afOZsTDxyuA9_*i ztjT#wS_T$^m(Lb{d`dI6U?i-(-TE8pWvo$|G`jrkq&V%xE z_Bmaja=q?>pZ3|G`Ru+u18eJGJn!!*vD_ZqTzI7S?V+7ngXi8{=54!neQC<0TfKi^ zmfx-4_I;d_`Q@#K!kMX+Pp*|%5*Zm9N-5j^(V{7e50wQfIhN%|s}Cg|VX$NVuj;e@ z_v$Osl?Vna5p^V9jZ^UE2JEE@yh2S&N=mwV{Ap>^oVmy0HvU{77xyPko5Dq88@aI>!90Ef&3IhKRjoPcpG%=rjr>WV)AbTorJ{;dFpg>Y*cte|)mV1zSRr{Pq*Y~ewM zgCbv~;UhAPAjq`c&da=2hQXE{qDYa9Xk!bpe-}ZeDr>6L0Mt!JxCtjj2P?={t4P@$ kR)(YOGH0WCQkDgdf_!-)F_{$IjHeH#mcg>J?4=IuAFi#fY5)KL 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 0000000..e6c82da --- /dev/null +++ b/document_quick_access_folder_auto_classification/views/document_quick_access_missing.xml @@ -0,0 +1,77 @@ + + + + + + + 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} + + + + Documents to process + + + + +
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 0000000..4d34fe7 --- /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 0000000..fa5bb7e --- /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 0000000..3986eed --- /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/oca_dependencies.txt b/oca_dependencies.txt index 112a756..f6b5bab 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1 +1,2 @@ +queue server-ux-doc https://github.com/etobella/server-ux 11.0-add-document_quick_access diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e36c3f9 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pyzbar +pdf2image