Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 9 additions & 1 deletion edi_core_oca/models/edi_backend.py

@Ricardoalso Ricardoalso Jul 15, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some time ago I added the line except (OperationalError, IntegrityError): https://github.com/OCA/edi-framework/blob/19.0/edi_core_oca/models/edi_backend.py#L266 PR #240 in order to fix failed transactions where the cursor enters an aborted state.

With this new commit we will never reach the conditional structure if res != "__sql_error__": https://github.com/OCA/edi-framework/blob/19.0/edi_core_oca/models/edi_backend.py#L282

Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,20 @@ def exchange_send(self, exchange_record):
return res

def _swallable_exceptions(self):
# TODO: improve this list
# These errors are permanent because retrying the same data will fail again.
# They should be swallowed so the exchange can move to an error state.
#
# OperationalError is excluded because it may be transient and should be
# re-raised to allow retries.
return (
ValueError,
FileNotFoundError,
exceptions.UserError,
exceptions.ValidationError,
IntegrityError,
TypeError,
LookupError, # covers KeyError / IndexError
AttributeError,
)

def _send_retryable_exceptions(self):
Expand Down
28 changes: 22 additions & 6 deletions edi_core_oca/tests/test_backend_process.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,28 @@ def test_process_outbound_record(self):
record.action_exchange_process()

def test_process_record_with_integrity_error(self):
# IntegrityError is permanent (e.g. a DB unique constraint
# violation): retrying the same data would fail again, so it must
# be swallowed and move the record to an error state instead of
# being re-raised and leaving it stuck in "input_received".
self.record.write({"edi_exchange_state": "input_received"})
with self.assertRaises(IntegrityError):
self.backend.with_context(
test_break_process=IntegrityError("SQL error")
).exchange_process(self.record)
self.assertRecordValues(self.record, [{"edi_exchange_state": "input_received"}])
self.assertFalse(self.record.exchange_error)
with self.assertRaisesRegex(IntegrityError, "SQL error"):
self.record.with_context(
test_break_process=IntegrityError("SQL error"),
_edi_process_break_on_error=True,
).action_exchange_process()

self.record.with_context(
test_break_process=IntegrityError("SQL error")
).action_exchange_process()
self.assertRecordValues(
self.record,
[
{
"edi_exchange_state": "input_processed_error",
"exchange_error": "SQL error",
}
],
)

# TODO: test ack file are processed
Loading