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 0000000..3a0328b 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/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 0000000..750d16c 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/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