Skip to content

Commit

Permalink
Merge PR #855 into 14.0
Browse files Browse the repository at this point in the history
Signed-off-by etobella
  • Loading branch information
OCA-git-bot committed Nov 23, 2023
2 parents b135599 + c2ff53f commit d3a8b93
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 28 deletions.
21 changes: 11 additions & 10 deletions edi_exchange_template_oca/tests/test_edi_backend_output.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright 2020 ACSONE SA/NV (<http://acsone.eu>)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
import base64

from lxml import etree

Expand Down Expand Up @@ -139,11 +138,12 @@ def test_get_template(self):
def test_generate_file(self):
output = self.backend.exchange_generate(self.record1)
expected = "{0.ref} - {0.name}".format(self.partner)
self.assertEqual(output.strip(), expected)
file_content = base64.b64decode(self.record1.exchange_file).decode()
self.assertEqual(output, "Exchange data generated")
file_content = self.record1._get_file_content()
self.assertEqual(file_content.strip(), expected)
output = self.backend.exchange_generate(self.record2)
doc = etree.fromstring(output)
file_content = self.record2._get_file_content()
doc = etree.fromstring(file_content)
self.assertEqual(doc.tag, "Record")
self.assertEqual(doc.attrib, {"ref": self.partner.ref})
self.assertEqual(doc.getchildren()[0].tag, "Name")
Expand All @@ -156,16 +156,17 @@ def test_prettify(self):
self.tmpl_out2.template_id.arch = (
'<t t-name="edi_exchange.test_output2"><root><a>1</a></root></t>'
)
result = self.tmpl_out2.exchange_generate(self.record2)
self.assertEqual(result, b"<root><a>1</a></root>")
output = self.tmpl_out2.exchange_generate(self.record2)
self.assertEqual(output, b"<root><a>1</a></root>")
self.tmpl_out2.prettify = True
result = self.tmpl_out2.exchange_generate(self.record2)
self.assertEqual(result, b"<root>\n <a>1</a>\n</root>\n")
output = self.tmpl_out2.exchange_generate(self.record2)
self.assertEqual(output, b"<root>\n <a>1</a>\n</root>\n")

def test_generate_file_report(self):
output = self.backend.exchange_generate(self.record3)
self.assertTrue(output)
self.assertEqual(output, "Exchange data generated")
file_content = self.record3._get_file_content()
self.assertEqual(
self.report._render([self.record3.res_id])[0].strip().decode("UTF-8"),
output.strip(),
file_content.strip(),
)
36 changes: 24 additions & 12 deletions edi_oca/models/edi_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
from odoo import _, exceptions, fields, models, tools

from odoo.addons.component.exception import NoComponentError
from odoo.addons.queue_job.exception import RetryableJobError

from ..exceptions import EDIValidationError

Expand Down Expand Up @@ -229,6 +230,7 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw):
except UnicodeDecodeError:
pass
if output:
message = exchange_record._exchange_status_message("generate_ok")
try:
self._validate_data(exchange_record, output)
except EDIValidationError:
Expand All @@ -239,8 +241,9 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw):
{"edi_exchange_state": state, "exchange_error": error}
)
exchange_record.notify_action_complete("generate", message=message)
return output
return message

# TODO: unify to all other checkes that return something
def _check_exchange_generate(self, exchange_record, force=False):
exchange_record.ensure_one()
if (
Expand Down Expand Up @@ -288,10 +291,11 @@ def exchange_send(self, exchange_record):
# In case already sent: skip sending and check the state
check = self._output_check_send(exchange_record)
if not check:
return False
return "Nothing to do. Likely already sent."
state = exchange_record.edi_exchange_state
error = False
message = None
res = ""
try:
self._exchange_send(exchange_record)
except self._swallable_exceptions():
Expand All @@ -300,7 +304,12 @@ def exchange_send(self, exchange_record):
error = _get_exception_msg()
state = "output_error_on_send"
message = exchange_record._exchange_status_message("send_ko")
res = False
res = f"Error: {error}"
except self._send_retryable_exceptions() as err:
error = _get_exception_msg()
raise RetryableJobError(
error, **exchange_record._job_retry_params()
) from err
else:
# TODO: maybe the send handler should return desired message and state
message = exchange_record._exchange_status_message("send_ok")
Expand All @@ -310,7 +319,7 @@ def exchange_send(self, exchange_record):
if self.output_sent_processed_auto
else "output_sent"
)
res = True
res = message
finally:
exchange_record.write(
{
Expand All @@ -333,6 +342,10 @@ def _swallable_exceptions(self):
exceptions.ValidationError,
)

def _send_retryable_exceptions(self):
# IOError is a base class for all connection errors
return (IOError,)

def _output_check_send(self, exchange_record):
if exchange_record.direction != "output":
raise exceptions.UserError(
Expand Down Expand Up @@ -456,22 +469,21 @@ def exchange_process(self, exchange_record):
# In case already processed: skip processing and check the state
check = self._exchange_process_check(exchange_record)
if not check:
return False
return "Nothing to do. Likely already processed."
old_state = state = exchange_record.edi_exchange_state
error = False
message = None
try:
self._exchange_process(exchange_record)
res = self._exchange_process(exchange_record)
except self._swallable_exceptions():
if self.env.context.get("_edi_process_break_on_error"):
raise
error = _get_exception_msg()
state = "input_processed_error"
res = False
res = f"Error: {error}"
else:
error = None
state = "input_processed"
res = True
finally:
exchange_record.write(
{
Expand Down Expand Up @@ -505,7 +517,7 @@ def exchange_receive(self, exchange_record):
# In case already processed: skip processing and check the state
check = self._exchange_receive_check(exchange_record)
if not check:
return False
return "Nothing to do. Likely already received."
state = exchange_record.edi_exchange_state
error = False
message = None
Expand All @@ -519,19 +531,19 @@ def exchange_receive(self, exchange_record):
error = _get_exception_msg()
state = "validate_error"
message = exchange_record._exchange_status_message("validate_ko")
res = False
res = f"Validation error: {error}"
except self._swallable_exceptions():
if self.env.context.get("_edi_receive_break_on_error"):
raise
error = _get_exception_msg()
state = "input_receive_error"
message = exchange_record._exchange_status_message("receive_ko")
res = False
res = f"Input error: {error}"
else:
message = exchange_record._exchange_status_message("receive_ok")
error = None
state = "input_received"
res = True
res = message
finally:
exchange_record.write(
{
Expand Down
13 changes: 11 additions & 2 deletions edi_oca/models/edi_exchange_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,14 @@ def _constrain_backend(self):
def _exchange_status_messages(self):
return {
# status: message
"generate_ok": _("Exchange data generated"),
"send_ok": _("Exchange sent"),
"send_ko": _(
"An error happened while sending. Please check exchange record info."
),
"process_ok": _("Exchange processed successfully "),
"process_ok": _("Exchange processed successfully"),
"process_ko": _("Exchange processed with errors"),
"receive_ok": _("Exchange received successfully "),
"receive_ok": _("Exchange received successfully"),
"receive_ko": _("Exchange not received"),
"ack_received": _("ACK file received."),
"ack_missing": _("ACK file is required for this exchange but not found."),
Expand Down Expand Up @@ -571,3 +572,11 @@ def with_delay(self, **kw):
params = self._job_delay_params()
params.update(kw)
return super().with_delay(**params)

def delayable(self, **kw):
params = self._job_delay_params()
params.update(kw)
return super().delayable(**params)

def _job_retry_params(self):
return {}
59 changes: 55 additions & 4 deletions edi_oca/tests/test_backend_jobs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
# @author: Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import mock
from requests.exceptions import ConnectionError as ReqConnectionError

from odoo.addons.queue_job.exception import RetryableJobError
from odoo.addons.queue_job.tests.common import JobMixin

from .common import EDIBackendCommonTestCase
Expand All @@ -19,27 +23,74 @@ def test_output(self):
"res_id": self.partner.id,
}
record = self.backend.create_record("test_csv_output", vals)
self.backend.with_delay().exchange_generate(record)
self.assertEqual(record.edi_exchange_state, "new")
job = self.backend.with_delay().exchange_generate(record)
created = job_counter.search_created()
self.assertEqual(len(created), 1)
self.assertEqual(
created.name, "Generate output content for given exchange record."
)
self.backend.with_delay().exchange_send(record)
with mock.patch.object(
type(self.backend), "_exchange_generate"
) as mocked_generate, mock.patch.object(
type(self.backend), "_validate_data"
) as mocked_validate:
mocked_generate.return_value = "filecontent"
mocked_validate.return_value = None
res = job.perform()
self.assertEqual(res, "Exchange data generated")
self.assertEqual(record.edi_exchange_state, "output_pending")
job = self.backend.with_delay().exchange_send(record)
created = job_counter.search_created()
with mock.patch.object(type(self.backend), "_exchange_send") as mocked:
mocked.return_value = "ok"
res = job.perform()
self.assertEqual(res, "Exchange sent")
self.assertEqual(record.edi_exchange_state, "output_sent")
self.assertEqual(created[0].name, "Send exchange file.")

def test_output_fail_retry(self):
job_counter = self.job_counter()
vals = {
"model": self.partner._name,
"res_id": self.partner.id,
"edi_exchange_state": "output_pending",
}
record = self.backend.create_record("test_csv_output", vals)
record._set_file_content("ABC")
job = self.backend.with_delay().exchange_send(record)
job_counter.search_created()
with mock.patch.object(type(self.backend), "_exchange_send") as mocked:
mocked.side_effect = ReqConnectionError("Connection broken")
with self.assertRaises(RetryableJobError):
job.perform()

def test_input(self):
job_counter = self.job_counter()
vals = {
"model": self.partner._name,
"res_id": self.partner.id,
}
record = self.backend.create_record("test_csv_input", vals)
self.backend.with_delay().exchange_receive(record)
job = self.backend.with_delay().exchange_receive(record)
created = job_counter.search_created()
self.assertEqual(len(created), 1)
self.assertEqual(created.name, "Retrieve an incoming document.")
self.backend.with_delay().exchange_process(record)
with mock.patch.object(
type(self.backend), "_exchange_receive"
) as mocked_receive, mock.patch.object(
type(self.backend), "_validate_data"
) as mocked_validate:
mocked_receive.return_value = "filecontent"
mocked_validate.return_value = None
res = job.perform()
# the state is not input_pending hence there's nothing to do
self.assertEqual(res, "Nothing to do. Likely already received.")
record.edi_exchange_state = "input_pending"
res = job.perform()
# the state is not input_pending hence there's nothing to do
self.assertEqual(res, "Exchange received successfully")
self.assertEqual(record.edi_exchange_state, "input_received")
job = self.backend.with_delay().exchange_process(record)
created = job_counter.search_created()
self.assertEqual(created[0].name, "Process an incoming document.")
3 changes: 3 additions & 0 deletions edi_oca/tests/test_exchange_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

from freezegun import freeze_time

from odoo.tools import mute_logger

from .common import EDIBackendCommonTestCase


Expand All @@ -23,6 +25,7 @@ def test_ack_for(self):
self.exchange_type_out_ack.ack_for_type_ids.ids,
)

@mute_logger("odoo.sql_db")
def test_same_code_same_backend(self):
with self.assertRaises(Exception) as err:
self.exchange_type_in.copy({"code": "test_csv_input"})
Expand Down

0 comments on commit d3a8b93

Please sign in to comment.