diff options
| author | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
|---|---|---|
| committer | stephanchrst <stephanchrst@gmail.com> | 2022-05-10 21:51:50 +0700 |
| commit | 3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch) | |
| tree | a44932296ef4a9b71d5f010906253d8c53727726 /addons/account_edi_extended | |
| parent | 0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff) | |
initial commit 2
Diffstat (limited to 'addons/account_edi_extended')
| -rw-r--r-- | addons/account_edi_extended/__init__.py | 13 | ||||
| -rw-r--r-- | addons/account_edi_extended/__manifest__.py | 20 | ||||
| -rw-r--r-- | addons/account_edi_extended/i18n/account_edi_extended.pot | 151 | ||||
| -rw-r--r-- | addons/account_edi_extended/models/__init__.py | 3 | ||||
| -rw-r--r-- | addons/account_edi_extended/models/account_edi_document.py | 18 | ||||
| -rw-r--r-- | addons/account_edi_extended/models/account_move.py | 81 | ||||
| -rw-r--r-- | addons/account_edi_extended/models/account_payment.py | 12 | ||||
| -rw-r--r-- | addons/account_edi_extended/tests/__init__.py | 5 | ||||
| -rw-r--r-- | addons/account_edi_extended/tests/common.py | 114 | ||||
| -rw-r--r-- | addons/account_edi_extended/tests/test_edi.py | 157 | ||||
| -rw-r--r-- | addons/account_edi_extended/views/account_edi_document_views.xml | 21 | ||||
| -rw-r--r-- | addons/account_edi_extended/views/account_move_views.xml | 52 | ||||
| -rw-r--r-- | addons/account_edi_extended/views/account_payment_views.xml | 15 |
13 files changed, 662 insertions, 0 deletions
diff --git a/addons/account_edi_extended/__init__.py b/addons/account_edi_extended/__init__.py new file mode 100644 index 00000000..de362ffd --- /dev/null +++ b/addons/account_edi_extended/__init__.py @@ -0,0 +1,13 @@ +# -*- encoding: utf-8 -*- +from . import models + + +def account_edi_block_level(cr, registery): + ''' The default value for blocking_level is 'error', but without this module, + the behavior is the same as a blocking_level of 'warning' so we need to set + all documents in error. + ''' + from odoo import api, SUPERUSER_ID + + env = api.Environment(cr, SUPERUSER_ID, {}) + env['account.edi.document'].search([('error', '!=', False)]).write({'blocking_level': 'warning'}) diff --git a/addons/account_edi_extended/__manifest__.py b/addons/account_edi_extended/__manifest__.py new file mode 100644 index 00000000..a3b85641 --- /dev/null +++ b/addons/account_edi_extended/__manifest__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +{ + 'name' : 'Additionnal features for account_edi', + 'description':""" + This module add features to account_edi to support new Edi formats. + """, + 'version' : '1.0', + 'category': 'Accounting/Accounting', + 'depends' : ['account_edi'], + 'data': [ + 'views/account_edi_document_views.xml', + 'views/account_move_views.xml', + 'views/account_payment_views.xml', + ], + 'installable': True, + 'application': False, + 'auto_install': False, + 'post_init_hook': 'account_edi_block_level', + 'license': 'LGPL-3', +} diff --git a/addons/account_edi_extended/i18n/account_edi_extended.pot b/addons/account_edi_extended/i18n/account_edi_extended.pot new file mode 100644 index 00000000..3a1c1597 --- /dev/null +++ b/addons/account_edi_extended/i18n/account_edi_extended.pot @@ -0,0 +1,151 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * account_edi_extended +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2021-01-05 11:07+0000\n" +"PO-Revision-Date: 2021-01-05 11:07+0000\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: account_edi_extended +#: code:addons/account_edi_extended/models/account_move.py:0 +#, python-format +msgid " Electronic invoicing error(s)" +msgstr "" + +#. module: account_edi_extended +#: code:addons/account_edi_extended/models/account_move.py:0 +#, python-format +msgid " Electronic invoicing info(s)" +msgstr "" + +#. module: account_edi_extended +#: code:addons/account_edi_extended/models/account_move.py:0 +#, python-format +msgid " Electronic invoicing warning(s)" +msgstr "" + +#. module: account_edi_extended +#: code:addons/account_edi_extended/models/account_move.py:0 +#, python-format +msgid "A request for cancellation of the EDI has been called off." +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_edi_document__blocking_level +msgid "Blocking Level" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,help:account_edi_extended.field_account_edi_document__blocking_level +msgid "" +"Blocks the document current operation depending on the error severity :\n" +" * Info: the document is not blocked and everything is working as it should.\n" +" * Warning : there is an error that doesn't prevent the current Electronic Invoicing operation to succeed.\n" +" * Error : there is an error that blocks the current Electronic Invoicing operation." +msgstr "" + +#. module: account_edi_extended +#: model_terms:ir.ui.view,arch_db:account_edi_extended.view_move_form_inherit +msgid "Call off EDI Cancellation" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_edi_document__display_name +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move__display_name +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment__display_name +msgid "Display Name" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_bank_statement_line__edi_blocking_level +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move__edi_blocking_level +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment__edi_blocking_level +msgid "Edi Blocking Level" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_bank_statement_line__edi_error_message +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move__edi_error_message +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment__edi_error_message +msgid "Edi Error Message" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_bank_statement_line__edi_show_abandon_cancel_button +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move__edi_show_abandon_cancel_button +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment__edi_show_abandon_cancel_button +msgid "Edi Show Abandon Cancel Button" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model,name:account_edi_extended.model_account_edi_document +msgid "Electronic Document for an account.move" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_edi_document__blocking_level__error +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_move__edi_blocking_level__error +msgid "Error" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_edi_document__id +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move__id +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment__id +msgid "ID" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_edi_document__blocking_level__info +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_move__edi_blocking_level__info +msgid "Info" +msgstr "" + +#. module: account_edi_extended +#: code:addons/account_edi_extended/models/account_edi_document.py:0 +#, python-format +msgid "Invalid configuration:" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model,name:account_edi_extended.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields,field_description:account_edi_extended.field_account_edi_document____last_update +#: model:ir.model.fields,field_description:account_edi_extended.field_account_move____last_update +#: model:ir.model.fields,field_description:account_edi_extended.field_account_payment____last_update +msgid "Last Modified on" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model,name:account_edi_extended.model_account_payment +msgid "Payments" +msgstr "" + +#. module: account_edi_extended +#: model_terms:ir.ui.view,arch_db:account_edi_extended.view_move_form_inherit +#: model_terms:ir.ui.view,arch_db:account_edi_extended.view_payment_form_inherit +msgid "Retry" +msgstr "" + +#. module: account_edi_extended +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_edi_document__blocking_level__warning +#: model:ir.model.fields.selection,name:account_edi_extended.selection__account_move__edi_blocking_level__warning +msgid "Warning" +msgstr "" + +#. module: account_edi_extended +#: model_terms:ir.ui.view,arch_db:account_edi_extended.view_move_form_inherit +msgid "⇒ See errors" +msgstr "" diff --git a/addons/account_edi_extended/models/__init__.py b/addons/account_edi_extended/models/__init__.py new file mode 100644 index 00000000..e8ed2ffb --- /dev/null +++ b/addons/account_edi_extended/models/__init__.py @@ -0,0 +1,3 @@ +from . import account_edi_document +from . import account_move +from . import account_payment diff --git a/addons/account_edi_extended/models/account_edi_document.py b/addons/account_edi_extended/models/account_edi_document.py new file mode 100644 index 00000000..0894b13e --- /dev/null +++ b/addons/account_edi_extended/models/account_edi_document.py @@ -0,0 +1,18 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import models, fields, _ +import logging + +_logger = logging.getLogger(__name__) +DEFAULT_BLOCKING_LEVEL = 'warning' # Keep previous behavior. TODO : when account_edi_extended is merged with account_edi, should be 'error' (document will not be processed again until forced retry or reset to draft) + + +class AccountEdiDocument(models.Model): + _inherit = 'account.edi.document' + + blocking_level = fields.Selection(selection=[('info', 'Info'), ('warning', 'Warning'), ('error', 'Error')], + help="Blocks the document current operation depending on the error severity :\n" + " * Info: the document is not blocked and everything is working as it should.\n" + " * Warning : there is an error that doesn't prevent the current Electronic Invoicing operation to succeed.\n" + " * Error : there is an error that blocks the current Electronic Invoicing operation.") + diff --git a/addons/account_edi_extended/models/account_move.py b/addons/account_edi_extended/models/account_move.py new file mode 100644 index 00000000..4f81822a --- /dev/null +++ b/addons/account_edi_extended/models/account_move.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, models, fields, _ + + +class AccountMove(models.Model): + _inherit = 'account.move' + + edi_show_abandon_cancel_button = fields.Boolean( + compute='_compute_edi_show_abandon_cancel_button') + edi_error_message = fields.Html(compute='_compute_edi_error_message') + edi_blocking_level = fields.Selection(selection=[('info', 'Info'), ('warning', 'Warning'), ('error', 'Error')], compute='_compute_edi_error_message') + + @api.depends( + 'edi_document_ids', + 'edi_document_ids.state', + 'edi_document_ids.blocking_level', + 'edi_document_ids.edi_format_id', + 'edi_document_ids.edi_format_id.name') + def _compute_edi_web_services_to_process(self): + # OVERRIDE to take blocking_level into account + for move in self: + to_process = move.edi_document_ids.filtered(lambda d: d.state in ['to_send', 'to_cancel'] and d.blocking_level != 'error') + format_web_services = to_process.edi_format_id.filtered(lambda f: f._needs_web_services()) + move.edi_web_services_to_process = ', '.join(f.name for f in format_web_services) + + @api.depends( + 'state', + 'edi_document_ids.state', + 'edi_document_ids.attachment_id') + def _compute_edi_show_abandon_cancel_button(self): + for move in self: + move.edi_show_abandon_cancel_button = any(doc.edi_format_id._needs_web_services() + and doc.state == 'to_cancel' + and move.is_invoice(include_receipts=True) + and doc.edi_format_id._is_required_for_invoice(move) + for doc in move.edi_document_ids) + + @api.depends('edi_error_count', 'edi_document_ids.error', 'edi_document_ids.blocking_level') + def _compute_edi_error_message(self): + for move in self: + if move.edi_error_count == 0: + move.edi_error_message = None + move.edi_blocking_level = None + elif move.edi_error_count == 1: + error_doc = move.edi_document_ids.filtered(lambda d: d.error) + move.edi_error_message = error_doc.error + move.edi_blocking_level = error_doc.blocking_level + else: + error_levels = set([doc.blocking_level for doc in move.edi_document_ids]) + if 'error' in error_levels: + move.edi_error_message = str(move.edi_error_count) + _(" Electronic invoicing error(s)") + move.edi_blocking_level = 'error' + elif 'warning' in error_levels: + move.edi_error_message = str(move.edi_error_count) + _(" Electronic invoicing warning(s)") + move.edi_blocking_level = 'warning' + else: + move.edi_error_message = str(move.edi_error_count) + _(" Electronic invoicing info(s)") + move.edi_blocking_level = 'info' + + def action_retry_edi_documents_error(self): + self.edi_document_ids.write({'error': False, 'blocking_level': False}) + self.action_process_edi_web_services() + + def button_abandon_cancel_posted_posted_moves(self): + '''Cancel the request for cancellation of the EDI. + ''' + documents = self.env['account.edi.document'] + for move in self: + is_move_marked = False + for doc in move.edi_document_ids: + if doc.state == 'to_cancel' \ + and move.is_invoice(include_receipts=True) \ + and doc.edi_format_id._is_required_for_invoice(move): + documents |= doc + is_move_marked = True + if is_move_marked: + move.message_post(body=_("A request for cancellation of the EDI has been called off.")) + + documents.write({'state': 'sent'}) diff --git a/addons/account_edi_extended/models/account_payment.py b/addons/account_edi_extended/models/account_payment.py new file mode 100644 index 00000000..558c4aaa --- /dev/null +++ b/addons/account_edi_extended/models/account_payment.py @@ -0,0 +1,12 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import models + + +class AccountPayment(models.Model): + _inherit = 'account.payment' + + def action_retry_edi_documents_error(self): + self.ensure_one() + return self.move_id.action_retry_edi_documents_error() diff --git a/addons/account_edi_extended/tests/__init__.py b/addons/account_edi_extended/tests/__init__.py new file mode 100644 index 00000000..eea5d7ed --- /dev/null +++ b/addons/account_edi_extended/tests/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from . import common +from . import test_edi diff --git a/addons/account_edi_extended/tests/common.py b/addons/account_edi_extended/tests/common.py new file mode 100644 index 00000000..3bb2ff6f --- /dev/null +++ b/addons/account_edi_extended/tests/common.py @@ -0,0 +1,114 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.account_edi.tests.common import AccountEdiTestCommon +from contextlib import contextmanager +from unittest.mock import patch +import base64 + +# TODO for the test to work, we need a chart template (COA) but we don't have any and don't want to add dependency (create empty coa ?) + + +def _generate_mocked_needs_web_services(needs_web_services): + return lambda edi_format: needs_web_services + + +def _generate_mocked_support_batching(support_batching): + return lambda edi_format, move, state, company: support_batching + + +def _mocked_get_batch_key(edi_format, move, state): + return () + + +def _mocked_check_move_configuration_success(edi_format, move): + return [] + + +def _mocked_check_move_configuration_fail(edi_format, move): + return ['Fake error (mocked)'] + + +def _mocked_post(edi_format, invoices, test_mode): + res = {} + for invoice in invoices: + attachment = edi_format.env['ir.attachment'].create({ + 'name': 'mock_simple.xml', + 'datas': base64.encodebytes(b"<?xml version='1.0' encoding='UTF-8'?><Invoice/>"), + 'mimetype': 'application/xml' + }) + res[invoice] = {'attachment': attachment} + return res + + +def _mocked_post_two_steps(edi_format, invoices, test_mode): + # For this test, we use the field ref to know if the first step is already done or not. + # Typically, a technical field for the reference of the upload to the web-service will + # be saved on the invoice. + invoices_no_ref = invoices.filtered(lambda i: not i.ref) + if len(invoices_no_ref) == len(invoices): # first step + invoices_no_ref.ref = 'test_ref' + return {invoice: {} for invoice in invoices} + elif len(invoices_no_ref) == 0: # second step + res = {} + for invoice in invoices: + attachment = edi_format.env['ir.attachment'].create({ + 'name': 'mock_simple.xml', + 'datas': base64.encodebytes(b"<?xml version='1.0' encoding='UTF-8'?><Invoice/>"), + 'mimetype': 'application/xml' + }) + res[invoice] = {'attachment': attachment} + return res + else: + raise ValueError('wrong use of "_mocked_post_two_steps"') + + +def _mocked_cancel_success(edi_format, invoices, test_mode): + return {invoice: {'success': True} for invoice in invoices} + + +def _mocked_cancel_failed(edi_format, invoices, test_mode): + return {invoice: {'error': 'Faked error (mocked)'} for invoice in invoices} + + +class AccountEdiExtendedTestCommon(AccountEdiTestCommon): + + @contextmanager + def mock_edi(self, + _is_required_for_invoice_method=lambda edi_format, invoice: True, + _is_required_for_payment_method=lambda edi_format, invoice: True, + _support_batching_method=_generate_mocked_support_batching(False), + _get_batch_key_method=_mocked_get_batch_key, + _needs_web_services_method=_generate_mocked_needs_web_services(False), + _check_move_configuration_method=_mocked_check_move_configuration_success, + _post_invoice_edi_method=_mocked_post, + _cancel_invoice_edi_method=_mocked_cancel_success, + _post_payment_edi_method=_mocked_post, + _cancel_payment_edi_method=_mocked_cancel_success, + ): + + try: + with patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._is_required_for_invoice', + new=_is_required_for_invoice_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._is_required_for_payment', + new=_is_required_for_payment_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._needs_web_services', + new=_needs_web_services_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._support_batching', + new=_support_batching_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._get_batch_key', + new=_get_batch_key_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._check_move_configuration', + new=_check_move_configuration_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._post_invoice_edi', + new=_post_invoice_edi_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._cancel_invoice_edi', + new=_cancel_invoice_edi_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._post_payment_edi', + new=_post_payment_edi_method), \ + patch('odoo.addons.account_edi.models.account_edi_format.AccountEdiFormat._cancel_payment_edi', + new=_cancel_payment_edi_method): + + yield + finally: + pass diff --git a/addons/account_edi_extended/tests/test_edi.py b/addons/account_edi_extended/tests/test_edi.py new file mode 100644 index 00000000..164a6c88 --- /dev/null +++ b/addons/account_edi_extended/tests/test_edi.py @@ -0,0 +1,157 @@ + +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo.addons.account_edi_extended.tests.common import AccountEdiExtendedTestCommon, _mocked_post, _mocked_post_two_steps, _generate_mocked_needs_web_services, _mocked_cancel_failed, _generate_mocked_support_batching + + +class TestAccountEdi(AccountEdiExtendedTestCommon): + + @classmethod + def setUpClass(cls, chart_template_ref=None, edi_format_ref=None): + super().setUpClass(chart_template_ref=chart_template_ref, edi_format_ref=edi_format_ref) + + cls.invoice = cls.init_invoice('out_invoice', products=cls.product_a + cls.product_b) + + def test_edi_flow(self): + with self.mock_edi(): + doc = self.invoice._get_edi_document(self.edi_format) + self.assertFalse(doc) + self.invoice.action_post() + doc = self.invoice._get_edi_document(self.edi_format) + self.assertEqual(len(doc), 1) + self.assertEqual(doc.state, 'sent') + self.invoice.button_draft() + self.invoice.button_cancel() + self.assertEqual(doc.state, 'cancelled') + + def test_edi_flow_two_steps(self): + with self.mock_edi(_post_invoice_edi_method=_mocked_post_two_steps, + _needs_web_services_method=_generate_mocked_needs_web_services(True)): + doc = self.invoice._get_edi_document(self.edi_format) + self.assertFalse(doc) + self.invoice.action_post() + doc = self.invoice._get_edi_document(self.edi_format) + self.assertEqual(len(doc), 1) + self.assertEqual(doc.state, 'to_send') + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'to_send') + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'sent') + + def test_edi_flow_request_cancel_success(self): + with self.mock_edi(_needs_web_services_method=_generate_mocked_needs_web_services(True)): + self.assertEqual(self.invoice.state, 'draft') + self.invoice.action_post() + doc = self.invoice._get_edi_document(self.edi_format) + self.assertEqual(doc.state, 'to_send') + self.assertEqual(self.invoice.state, 'posted') + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'sent') + self.assertEqual(self.invoice.state, 'posted') + self.invoice.button_cancel_posted_moves() + self.assertEqual(doc.state, 'to_cancel') + self.assertEqual(self.invoice.state, 'posted') + doc._process_documents_web_services() + self.assertEqual(doc.state, 'cancelled') + self.assertEqual(self.invoice.state, 'cancel') + + def test_edi_flow_request_cancel_failed(self): + with self.mock_edi(_needs_web_services_method=_generate_mocked_needs_web_services(True), + _cancel_invoice_edi_method=_mocked_cancel_failed): + self.assertEqual(self.invoice.state, 'draft') + self.invoice.action_post() + doc = self.invoice._get_edi_document(self.edi_format) + self.assertEqual(doc.state, 'to_send') + self.assertEqual(self.invoice.state, 'posted') + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'sent') + self.assertEqual(self.invoice.state, 'posted') + self.invoice.button_cancel_posted_moves() + self.assertEqual(doc.state, 'to_cancel') + self.assertEqual(self.invoice.state, 'posted') + # Call off edi Cancellation + self.invoice.button_abandon_cancel_posted_posted_moves() + self.assertEqual(doc.state, 'sent') + self.assertFalse(doc.error) + + # Failed cancel + self.invoice.button_cancel_posted_moves() + self.assertEqual(doc.state, 'to_cancel') + self.assertEqual(self.invoice.state, 'posted') + doc._process_documents_web_services() + self.assertEqual(doc.state, 'to_cancel') + self.assertEqual(self.invoice.state, 'posted') + + # Call off edi Cancellation + self.invoice.button_abandon_cancel_posted_posted_moves() + self.assertEqual(doc.state, 'sent') + self.assertIsNotNone(doc.error) + + def test_edi_flow_two_step_cancel_with_call_off_request(self): + def _mock_cancel(edi_format, invoices, test_mode): + invoices_no_ref = invoices.filtered(lambda i: not i.ref) + if len(invoices_no_ref) == len(invoices): # first step + invoices_no_ref.ref = 'test_ref_cancel' + return {invoice: {} for invoice in invoices} + elif len(invoices_no_ref) == 0: # second step + for invoice in invoices: + invoice.ref = None + return {invoice: {'success': True} for invoice in invoices} + else: + raise ValueError('wrong use of "_mocked_post_two_steps"') + + def _is_needed_for_invoice(edi_format, invoice): + return not bool(invoice.ref) + + with self.mock_edi(_needs_web_services_method=_generate_mocked_needs_web_services(True), + _is_required_for_invoice_method=_is_needed_for_invoice, + _cancel_invoice_edi_method=_mock_cancel): + self.invoice.action_post() + doc = self.invoice._get_edi_document(self.edi_format) + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'sent') + + # Request Cancellation + self.invoice.button_cancel_posted_moves() + doc._process_documents_web_services(with_commit=False) # first step of cancel + self.assertEqual(doc.state, 'to_cancel') + + # Call off edi Cancellation + self.invoice.button_abandon_cancel_posted_posted_moves() + self.assertEqual(doc.state, 'to_cancel') + + # If we cannot call off edi cancellation, only solution is to post again + doc._process_documents_web_services(with_commit=False) # second step of cancel + self.assertEqual(doc.state, 'cancelled') + self.invoice.action_post() + doc._process_documents_web_services(with_commit=False) + self.assertEqual(doc.state, 'sent') + + def test_batches(self): + def _get_batch_key_method(edi_format, move, state): + return (move.ref) + + with self.mock_edi(_get_batch_key_method=_get_batch_key_method, + _support_batching_method=_generate_mocked_support_batching(True)): + edi_docs = self.env['account.edi.document'] + doc1 = self.create_edi_document(self.edi_format, 'to_send') + edi_docs |= doc1 + doc2 = self.create_edi_document(self.edi_format, 'to_send') + edi_docs |= doc2 + doc3 = self.create_edi_document(self.edi_format, 'to_send') + edi_docs |= doc3 + + to_process = edi_docs._prepare_jobs() + self.assertEqual(len(to_process), 1) + + doc1.move_id.ref = 'batch1' + doc2.move_id.ref = 'batch2' + doc3.move_id.ref = 'batch3' + + to_process = edi_docs._prepare_jobs() + self.assertEqual(len(to_process), 3) + + doc2.move_id.ref = 'batch1' + to_process = edi_docs._prepare_jobs() + self.assertEqual(len(to_process), 2) diff --git a/addons/account_edi_extended/views/account_edi_document_views.xml b/addons/account_edi_extended/views/account_edi_document_views.xml new file mode 100644 index 00000000..965de176 --- /dev/null +++ b/addons/account_edi_extended/views/account_edi_document_views.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <record id="view_tree_account_edi_document_inherit" model="ir.ui.view"> + <field name="name">Account.edi.document.tree.inherit</field> + <field name="model">account.edi.document</field> + <field name="inherit_id" ref="account_edi.view_tree_account_edi_document"/> + <field name="arch" type="xml"> + <xpath expr="//tree" position="inside"> + <field name="blocking_level" invisible="1" /> + </xpath> + <xpath expr="//tree" position="attributes"> + <attribute name="decoration-info">blocking_level == 'info'</attribute> + <attribute name="decoration-warning">blocking_level == 'warning'</attribute> + <attribute name="decoration-danger">blocking_level == 'error'</attribute> + </xpath> + + </field> + </record> + </data> +</odoo> diff --git a/addons/account_edi_extended/views/account_move_views.xml b/addons/account_edi_extended/views/account_move_views.xml new file mode 100644 index 00000000..820fa1ea --- /dev/null +++ b/addons/account_edi_extended/views/account_move_views.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + + <record id="view_move_form_inherit" model="ir.ui.view"> + <field name="name">account.move.form.inherit</field> + <field name="model">account.move</field> + <field name="inherit_id" ref="account_edi.view_move_form_inherit" /> + <field name="arch" type="xml"> + <xpath expr="//button[@name='%(account_edi.action_open_edi_documents)d']" position="after"> + <button name="action_retry_edi_documents_error" type="object" class="oe_link oe_inline" string="Retry" /> + </xpath> + <xpath expr="//button[@name='button_cancel_posted_moves']" position="after"> + <field name="edi_show_abandon_cancel_button" invisible="1"/> + <button name="button_abandon_cancel_posted_posted_moves" + string="Call off EDI Cancellation" + type="object" + groups="account.group_account_invoice" + attrs="{'invisible' : [('edi_show_abandon_cancel_button', '=', False)]}"/> + </xpath> + <!-- Nasty xpath to replace the error count warning banner. In master, it will be merged. --> + <xpath expr="//div[hasclass('alert-warning')]" position="replace"> + <field name="edi_blocking_level" invisible="1" /> + <field name="edi_error_count" invisible="1" /> + <div class="alert alert-danger" role="alert" style="margin-bottom:0px;" + attrs="{'invisible': ['|', ('edi_error_count', '=', 0), ('edi_blocking_level', '!=', 'error')]}"> + <div class="o_row"> + <field name="edi_error_message" /> + <button name="%(account_edi.action_open_edi_documents)d" string="⇒ See errors" type="action" class="oe_link" attrs="{'invisible': [('edi_error_count', '=', 1)]}" /> + <button name="action_retry_edi_documents_error" type="object" class="oe_link oe_inline" string="Retry" /> + </div> + </div> + <div class="alert alert-warning" role="alert" style="margin-bottom:0px;" + attrs="{'invisible': ['|', ('edi_error_count', '=', 0), ('edi_blocking_level', '!=', 'warning')]}"> + <div class="o_row"> + <field name="edi_error_message" /> + <button name="%(account_edi.action_open_edi_documents)d" string="⇒ See errors" type="action" class="oe_link" attrs="{'invisible': [('edi_error_count', '=', 1)]}" /> + </div> + </div> + <div class="alert alert-info" role="alert" style="margin-bottom:0px;" + attrs="{'invisible': ['|', ('edi_error_count', '=', 0), ('edi_blocking_level', '!=', 'info')]}"> + <div class="o_row"> + <field name="edi_error_message" /> + <button name="%(account_edi.action_open_edi_documents)d" string="⇒ See errors" type="action" class="oe_link" attrs="{'invisible': [('edi_error_count', '=', 1)]}" /> + </div> + </div> + </xpath> + + </field> + </record> + </data> +</odoo> diff --git a/addons/account_edi_extended/views/account_payment_views.xml b/addons/account_edi_extended/views/account_payment_views.xml new file mode 100644 index 00000000..8a4af21e --- /dev/null +++ b/addons/account_edi_extended/views/account_payment_views.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <data> + <record id="view_payment_form_inherit" model="ir.ui.view"> + <field name="name">account.payment.form.inherit</field> + <field name="model">account.payment</field> + <field name="inherit_id" ref="account_edi.view_payment_form_inherit" /> + <field name="arch" type="xml"> + <xpath expr="//button[@name='%(account_edi.action_open_payment_edi_documents)d']" position="after"> + <button name="action_retry_edi_documents_error" type="object" class="oe_link oe_inline" string="Retry" /> + </xpath> + </field> + </record> + </data> +</odoo> |
