summaryrefslogtreecommitdiff
path: root/addons/account_edi_extended
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/account_edi_extended
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/account_edi_extended')
-rw-r--r--addons/account_edi_extended/__init__.py13
-rw-r--r--addons/account_edi_extended/__manifest__.py20
-rw-r--r--addons/account_edi_extended/i18n/account_edi_extended.pot151
-rw-r--r--addons/account_edi_extended/models/__init__.py3
-rw-r--r--addons/account_edi_extended/models/account_edi_document.py18
-rw-r--r--addons/account_edi_extended/models/account_move.py81
-rw-r--r--addons/account_edi_extended/models/account_payment.py12
-rw-r--r--addons/account_edi_extended/tests/__init__.py5
-rw-r--r--addons/account_edi_extended/tests/common.py114
-rw-r--r--addons/account_edi_extended/tests/test_edi.py157
-rw-r--r--addons/account_edi_extended/views/account_edi_document_views.xml21
-rw-r--r--addons/account_edi_extended/views/account_move_views.xml52
-rw-r--r--addons/account_edi_extended/views/account_payment_views.xml15
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>