Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][PORT] edi_oca: Port edi_oca from edi/14.0 #62

Closed
wants to merge 25 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
f318105
edi_oca: avoid duplicate field name
petrus-v Aug 31, 2023
fbe9be8
[FIX] edi_oca: Adapt tests to changes on error messages
etobella Sep 15, 2023
b5027f4
Revert "[FIX] edi_oca: Adapt tests to changes on error messages"
simahawk Oct 2, 2023
d825a65
edi: drop useless view form/tree strings
simahawk Oct 2, 2023
4ceb926
edi: exc type fix view archived rules
simahawk Sep 7, 2023
c1943a7
edi: exc type view allow archive rules
simahawk Sep 7, 2023
3c85040
edi: fix archive rules when type archived
simahawk Sep 7, 2023
c734545
edi_oca: fix edi_id label
simahawk May 15, 2023
594a9f6
edi_oca: tests.common ease ctx setup
simahawk Aug 7, 2023
7179735
[IMP] edi_oca: Show traceback on exception errors
HviorForgeFlow Nov 6, 2023
57b6618
edi: fix job return msg
simahawk Oct 19, 2023
026be89
edi: make send failed job retryable
simahawk Oct 19, 2023
8e49425
edi: test_same_code_same_backend mute sql logger
simahawk Nov 21, 2023
16ed8fc
edi: add generate_ok message
simahawk Jul 5, 2023
a47f195
edi: fix backend jobs test old api
simahawk Jul 6, 2023
774900a
edi: chain generate/send jobs
simahawk Jul 5, 2023
4581521
edi: improve send job retry
simahawk Nov 24, 2023
a8d0970
edi: add file checksum
simahawk Nov 24, 2023
829a9da
edi: use job identity_key
simahawk Nov 24, 2023
f258f7c
edi: raise send job prio to max
simahawk Nov 29, 2023
04e762f
edi: fix consumer mixin copy origin exc record
simahawk Nov 29, 2023
114b4cc
[FIX] edi_oca: fixs after porting from 14.0
Jan 22, 2024
73f4983
Update blacklist of edi_oca
Jan 22, 2024
fd63bae
[FIX] edi_sale_oca: fix test
Jan 22, 2024
732f2bc
[FIX] edi_sale_edifact_oca: fix test
Jan 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .oca/oca-port/blacklist/edi_oca.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@
"OCA/edi#774": "already ported",
"OCA/edi#778": "already ported",
"OCA/edi#797": "already ported",
"OCA/edi#790": "Nothing to port from PR #OCA/edi#790"
"OCA/edi#790": "Nothing to port from PR #OCA/edi#790",
"OCA/edi#808": "already ported",
"OCA/edi#819": "already ported",
"OCA/edi#822": "already ported",
"OCA/edi#824": "already ported",
"OCA/edi#830": "already ported",
"OCA/edi#852": "already ported",
"OCA/edi#855": "already ported",
"OCA/edi#796": "already ported",
"OCA/edi#880": "already ported",
"OCA/edi#897": "already ported"
}
}
83 changes: 55 additions & 28 deletions edi_oca/models/edi_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,25 @@

import base64
import logging
import traceback
from io import StringIO

from odoo import _, exceptions, fields, models

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

from ..exceptions import EDIValidationError

_logger = logging.getLogger(__name__)


def _get_exception_msg(exc):
if hasattr(exc, "args") and isinstance(exc.args[0], str):
return exc.args[0]
return repr(exc)
def _get_exception_msg():
buff = StringIO()
traceback.print_exc(file=buff)
traceback_txt = buff.getvalue()
buff.close()
return traceback_txt


class EDIBackend(models.Model):
Expand Down Expand Up @@ -211,18 +216,20 @@ def exchange_generate(self, exchange_record, store=True, force=False, **kw):
}
)
if output:
message = exchange_record._exchange_status_message("generate_ok")
try:
self._validate_data(exchange_record, output)
except EDIValidationError as err:
error = _get_exception_msg(err)
except EDIValidationError:
error = _get_exception_msg()
state = "validate_error"
message = exchange_record._exchange_status_message("validate_ko")
exchange_record.update(
{"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 @@ -270,19 +277,30 @@ 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() as err:
_logger.debug("%s sent", exchange_record.identifier)
except self._send_retryable_exceptions() as err:
error = _get_exception_msg()
_logger.debug("%s send failed. To be retried.", exchange_record.identifier)
raise RetryableJobError(
error, **exchange_record._job_retry_params()
) from err
except self._swallable_exceptions():
if self.env.context.get("_edi_send_break_on_error"):
raise
error = _get_exception_msg(err)
error = _get_exception_msg()
state = "output_error_on_send"
message = exchange_record._exchange_status_message("send_ko")
res = False
res = f"Error: {error}"
_logger.debug(
"%s send failed. Marked as errored.", exchange_record.identifier
)
else:
# TODO: maybe the send handler should return desired message and state
message = exchange_record._exchange_status_message("send_ok")
Expand All @@ -292,7 +310,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 @@ -315,6 +333,12 @@ def _swallable_exceptions(self):
exceptions.ValidationError,
)

def _send_retryable_exceptions(self):
# IOError is a base class for all connection errors
# OSError is a base class for all errors
# when dealing w/ internal or external systems or filesystems
return (IOError, OSError)

def _output_check_send(self, exchange_record):
if exchange_record.direction != "output":
raise exceptions.UserError(
Expand All @@ -339,7 +363,6 @@ def _cron_check_output_exchange_sync(self, **kw):
for backend in self:
backend._check_output_exchange_sync(**kw)

# TODO: consider splitting cron in 2 (1 for receiving, 1 for processing)
def _check_output_exchange_sync(
self, skip_send=False, skip_sent=True, record_ids=None
):
Expand All @@ -360,7 +383,12 @@ def _check_output_exchange_sync(
len(new_records),
)
for rec in new_records:
rec.with_delay().action_exchange_generate()
job1 = rec.delayable().action_exchange_generate()
if not skip_send:
# Chain send job.
# Raise prio to max to send the record out as fast as possible.
job1.on_done(rec.delayable(priority=0).action_exchange_send())
job1.delay()

if skip_send:
return
Expand Down Expand Up @@ -438,22 +466,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)
except self._swallable_exceptions() as err:
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(err)
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 @@ -487,7 +514,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 @@ -497,23 +524,23 @@ def exchange_receive(self, exchange_record):
if content:
exchange_record._set_file_content(content)
self._validate_data(exchange_record)
except EDIValidationError as err:
error = _get_exception_msg(err)
except EDIValidationError:
error = _get_exception_msg()
state = "validate_error"
message = exchange_record._exchange_status_message("validate_ko")
res = False
except self._swallable_exceptions() as err:
res = f"Validation error: {error}"
except self._swallable_exceptions():
if self.env.context.get("_edi_receive_break_on_error"):
raise
error = _get_exception_msg(err)
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
2 changes: 2 additions & 0 deletions edi_oca/models/edi_exchange_consumer_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class EDIExchangeConsumerMixin(models.AbstractModel):
comodel_name="edi.exchange.record",
ondelete="set null",
help="EDI record that originated this document.",
copy=False,
)
origin_exchange_type_id = fields.Many2one(
string="EDI origin exchange type",
Expand All @@ -33,6 +34,7 @@ class EDIExchangeConsumerMixin(models.AbstractModel):
related="origin_exchange_record_id.type_id",
# Store it to ease searching by type
store=True,
copy=False,
)
exchange_record_ids = fields.One2many(
"edi.exchange.record",
Expand Down
28 changes: 26 additions & 2 deletions edi_oca/models/edi_exchange_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

from odoo import _, api, exceptions, fields, models

from ..utils import exchange_record_job_identity_exact, get_checksum

_logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -50,6 +52,9 @@ class EDIExchangeRecord(models.Model):
exchange_filename = fields.Char(
compute="_compute_exchange_filename", readonly=False, store=True
)
exchange_filechecksum = fields.Char(
compute="_compute_exchange_filechecksum", store=True
)
exchanged_on = fields.Datetime(
help="Sent or received on this date.",
compute="_compute_exchanged_on",
Expand Down Expand Up @@ -133,6 +138,14 @@ def _compute_exchange_filename(self):
if not rec.exchange_filename:
rec.exchange_filename = rec.type_id._make_exchange_filename(rec)

@api.depends("exchange_file")
def _compute_exchange_filechecksum(self):
for rec in self:
content = rec.exchange_file or ""
if not isinstance(content, bytes):
content = content.encode()
rec.exchange_filechecksum = get_checksum(content)

@api.depends("edi_exchange_state")
def _compute_exchanged_on(self):
for rec in self:
Expand Down Expand Up @@ -275,13 +288,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 @@ -567,9 +581,19 @@ def _job_delay_params(self):
channel = self.type_id.sudo().job_channel_id
if channel:
params["channel"] = channel.complete_name
# Avoid generating the same job for the same record if existing
params["identity_key"] = exchange_record_job_identity_exact
return params

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 {}
8 changes: 7 additions & 1 deletion edi_oca/models/edi_exchange_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class EDIExchangeType(models.Model):
_name = "edi.exchange.type"
_description = "EDI Exchange Type"

active = fields.Boolean(default=True)
active = fields.Boolean(default=True, inverse="_inverse_active")
backend_id = fields.Many2one(
string="Backend",
comodel_name="edi.backend",
Expand Down Expand Up @@ -139,6 +139,12 @@ class EDIExchangeType(models.Model):
)
]

def _inverse_active(self):
for rec in self:
# Disable rules if type gets disabled
if not rec.active:
rec.rule_ids.active = False

@api.depends("advanced_settings_edit")
def _compute_advanced_settings(self):
for rec in self:
Expand Down
2 changes: 1 addition & 1 deletion edi_oca/models/edi_exchange_type_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ class EDIExchangeTypeRule(models.Model):
help="Apply to this model",
ondelete="cascade",
)
model = fields.Char(related="model_id.model") # Tech field
model = fields.Char("Model code", related="model_id.model") # Tech field
enable_domain = fields.Char(
string="Enable on domain", help="Filter domain to be checked on Models"
)
Expand Down
4 changes: 3 additions & 1 deletion edi_oca/models/edi_id_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ class EDIIdMixin(models.AbstractModel):
_name = "edi.id.mixin"
_description = "EDI ID mixin"

edi_id = fields.Char(help="Internal or external identifier for records.")
edi_id = fields.Char(
string="EDI ID", help="Internal or external identifier for records."
)
11 changes: 7 additions & 4 deletions edi_oca/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@

class EDIBackendTestMixin(object):
@classmethod
def _setup_context(cls):
return dict(cls.env.context, tracking_disable=True, queue_job__no_delay=True)
def _setup_context(cls, **kw):
return dict(
cls.env.context, tracking_disable=True, test_queue_job_no_delay=True, **kw
)

@classmethod
def _setup_env(cls):
cls.env = cls.env(context=cls._setup_context())
def _setup_env(cls, ctx=None):
ctx = ctx or {}
cls.env = cls.env(context=cls._setup_context(**ctx))

@classmethod
def _setup_records(cls):
Expand Down
Loading
Loading