Skip to content

Commit 6ecf880

Browse files
[FIX] edi_storage_oca: add event listener
1 parent 26aeaf0 commit 6ecf880

7 files changed

Lines changed: 177 additions & 24 deletions

File tree

edi_storage_oca/README.rst

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
.. image:: https://odoo-community.org/readme-banner-image
2-
:target: https://odoo-community.org/get-involved?utm_source=readme
3-
:alt: Odoo Community Association
4-
51
===========================
62
EDI Storage backend support
73
===========================
@@ -17,7 +13,7 @@ EDI Storage backend support
1713
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
1814
:target: https://odoo-community.org/page/development-status
1915
:alt: Beta
20-
.. |badge2| image:: https://img.shields.io/badge/license-LGPL--3-blue.png
16+
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
2117
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
2218
:alt: License: LGPL-3
2319
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi--framework-lightgray.png?logo=github

edi_storage_oca/__manifest__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
"license": "LGPL-3",
1313
"website": "https://github.com/OCA/edi-framework",
1414
"author": "ACSONE,Odoo Community Association (OCA)",
15-
"depends": ["edi_core_oca", "fs_storage"],
15+
"depends": ["edi_core_oca", "fs_storage", "edi_component_oca"],
1616
"data": [
1717
"data/cron.xml",
1818
"security/ir_model_access.xml",

edi_storage_oca/models/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from . import edi_exchange_type
33
from . import edi_exchange_record
44
from . import edi_oca_storage_handler
5+
from . import edi_event_listener
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Copyright 2021 ForgeFlow S.L. (https://www.forgeflow.com)
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
import functools
5+
import os
6+
from pathlib import PurePath
7+
8+
from odoo.addons.component.core import Component
9+
10+
from .. import utils
11+
12+
13+
class EdiStorageListener(Component):
14+
_name = "edi.storage.component.listener"
15+
_inherit = "base.event.listener"
16+
17+
def _move_file(self, storage, from_dir_str, to_dir_str, filename):
18+
from_dir = PurePath(from_dir_str)
19+
to_dir = PurePath(to_dir_str)
20+
# - storage.list_files now includes path in fs_storage, breaking change
21+
# - we remove path
22+
files = utils.list_files(storage, from_dir.as_posix())
23+
files = [os.path.basename(f) for f in files]
24+
if filename not in files:
25+
return False
26+
self._add_post_commit_hook(
27+
utils.move_files,
28+
storage,
29+
[(from_dir / filename).as_posix()],
30+
to_dir.as_posix(),
31+
)
32+
return True
33+
34+
def _add_post_commit_hook(
35+
self, move_func, storage, sftp_filepath, sftp_destination_path
36+
):
37+
"""Add hook after commit to move the file when transaction is over."""
38+
self.env.cr.postcommit.add(
39+
functools.partial(move_func, storage, sftp_filepath, sftp_destination_path)
40+
)
41+
42+
def on_edi_exchange_done(self, record):
43+
storage = record.storage_id
44+
res = False
45+
if record.direction == "input" and storage:
46+
file = record.exchange_filename
47+
pending_dir = record.type_id._storage_fullpath(
48+
record.backend_id.input_dir_pending
49+
).as_posix()
50+
done_dir = record.type_id._storage_fullpath(
51+
record.backend_id.input_dir_done
52+
).as_posix()
53+
error_dir = record.type_id._storage_fullpath(
54+
record.backend_id.input_dir_error
55+
).as_posix()
56+
if not done_dir:
57+
return res
58+
res = self._move_file(storage, pending_dir, done_dir, file)
59+
if not res:
60+
# If a file previously failed it should have been previously
61+
# moved to the error dir, therefore it is not present in the
62+
# pending dir and we need to retry from error dir.
63+
res = self._move_file(storage, error_dir, done_dir, file)
64+
return res
65+
66+
def on_edi_exchange_error(self, record):
67+
storage = record.storage_id
68+
res = False
69+
if record.direction == "input" and storage:
70+
file = record.exchange_filename
71+
pending_dir = record.type_id._storage_fullpath(
72+
record.backend_id.input_dir_pending
73+
).as_posix()
74+
error_dir = record.type_id._storage_fullpath(
75+
record.backend_id.input_dir_error
76+
).as_posix()
77+
if error_dir:
78+
res = self._move_file(storage, pending_dir, error_dir, file)
79+
return res

edi_storage_oca/static/description/index.html

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
55
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
6-
<title>README.rst</title>
6+
<title>EDI Storage backend support</title>
77
<style type="text/css">
88

99
/*
@@ -360,21 +360,16 @@
360360
</style>
361361
</head>
362362
<body>
363-
<div class="document">
363+
<div class="document" id="edi-storage-backend-support">
364+
<h1 class="title">EDI Storage backend support</h1>
364365

365-
366-
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
367-
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
368-
</a>
369-
<div class="section" id="edi-storage-backend-support">
370-
<h1>EDI Storage backend support</h1>
371366
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
372367
!! This file is generated by oca-gen-addon-readme !!
373368
!! changes will be overwritten. !!
374369
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
375370
!! source digest: sha256:ae52c0238c8bab278e4e2d6a48d86d4222672b893df1f171be046f3b7a3c58ae
376371
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
377-
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/edi-framework/tree/18.0/edi_storage_oca"><img alt="OCA/edi-framework" src="https://img.shields.io/badge/github-OCA%2Fedi--framework-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/edi-framework-18-0/edi-framework-18-0-edi_storage_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/edi-framework&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
372+
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/edi-framework/tree/18.0/edi_storage_oca"><img alt="OCA/edi-framework" src="https://img.shields.io/badge/github-OCA%2Fedi--framework-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/edi-framework-18-0/edi-framework-18-0-edi_storage_oca"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/edi-framework&amp;target_branch=18.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
378373
<p>Allow exchange files using storage backends from OCA/storage.</p>
379374
<p>This module adds a storage backend relation on the EDI backend. There
380375
you can configure the backend to be used (most often and SFTP) and the
@@ -411,34 +406,34 @@ <h1>EDI Storage backend support</h1>
411406
</ul>
412407
</div>
413408
<div class="section" id="usage">
414-
<h2><a class="toc-backref" href="#toc-entry-1">Usage</a></h2>
409+
<h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
415410
<p>Go to “EDI -&gt; EDI backend” then configure your backend to use a storage
416411
backend.</p>
417412
</div>
418413
<div class="section" id="known-issues-roadmap">
419-
<h2><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h2>
414+
<h1><a class="toc-backref" href="#toc-entry-2">Known issues / Roadmap</a></h1>
420415
<ul class="simple">
421416
<li>clean deprecated methods in the storage</li>
422417
</ul>
423418
</div>
424419
<div class="section" id="bug-tracker">
425-
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
420+
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
426421
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/edi-framework/issues">GitHub Issues</a>.
427422
In case of trouble, please check there if your issue has already been reported.
428423
If you spotted it first, help us to smash it by providing a detailed and welcomed
429424
<a class="reference external" href="https://github.com/OCA/edi-framework/issues/new?body=module:%20edi_storage_oca%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
430425
<p>Do not contact contributors directly about support or help with technical issues.</p>
431426
</div>
432427
<div class="section" id="credits">
433-
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
428+
<h1><a class="toc-backref" href="#toc-entry-4">Credits</a></h1>
434429
<div class="section" id="authors">
435-
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
430+
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
436431
<ul class="simple">
437432
<li>ACSONE</li>
438433
</ul>
439434
</div>
440435
<div class="section" id="contributors">
441-
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
436+
<h2><a class="toc-backref" href="#toc-entry-6">Contributors</a></h2>
442437
<ul class="simple">
443438
<li>Simone Orsi &lt;<a class="reference external" href="mailto:simahawk&#64;gmail.com">simahawk&#64;gmail.com</a>&gt;</li>
444439
<li>Foram Shah &lt;<a class="reference external" href="mailto:foram.shah&#64;initos.com">foram.shah&#64;initos.com</a>&gt;</li>
@@ -451,12 +446,12 @@ <h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
451446
</ul>
452447
</div>
453448
<div class="section" id="other-credits">
454-
<h3><a class="toc-backref" href="#toc-entry-7">Other credits</a></h3>
449+
<h2><a class="toc-backref" href="#toc-entry-7">Other credits</a></h2>
455450
<p>The migration of this module from 15.0 to 16.0 was financially supported
456451
by Camptocamp.</p>
457452
</div>
458453
<div class="section" id="maintainers">
459-
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
454+
<h2><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h2>
460455
<p>This module is maintained by the OCA.</p>
461456
<a class="reference external image-reference" href="https://odoo-community.org">
462457
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
@@ -469,6 +464,5 @@ <h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
469464
</div>
470465
</div>
471466
</div>
472-
</div>
473467
</body>
474468
</html>

edi_storage_oca/tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from . import test_edi_backend_storage
22
from . import test_exchange_type
3+
from . import test_edi_event_listenner
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# Copyright 2026 ForgeFlow S.L. (https://www.forgeflow.com)
2+
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
3+
4+
import base64
5+
from unittest import mock
6+
7+
from odoo.addons.edi_component_oca.tests.common import (
8+
EDIBackendCommonComponentRegistryTestCase,
9+
)
10+
from odoo.addons.edi_component_oca.tests.fake_components import FakeInputProcess
11+
12+
LISTENER_MOCK_PATH = (
13+
"odoo.addons.edi_storage_oca.models.edi_event_listener.EdiStorageListener"
14+
)
15+
16+
17+
class EDIBackendTestCase(EDIBackendCommonComponentRegistryTestCase):
18+
@classmethod
19+
def setUpClass(cls):
20+
super().setUpClass()
21+
cls._load_module_components(cls, "edi_storage_oca")
22+
cls._build_components(
23+
cls,
24+
FakeInputProcess,
25+
)
26+
vals = {
27+
"model": cls.partner._name,
28+
"res_id": cls.partner.id,
29+
"exchange_file": base64.b64encode(b"1234"),
30+
}
31+
cls.record = cls.backend.create_record("test_csv_input", vals)
32+
cls.fake_move_args = None
33+
34+
@classmethod
35+
def _get_backend(cls):
36+
return cls.env.ref("edi_storage_oca.demo_edi_backend_storage")
37+
38+
def setUp(self):
39+
super().setUp()
40+
FakeInputProcess.reset_faked()
41+
42+
def _move_file_mocked(self, *args):
43+
self.fake_move_args = [*args]
44+
if not all([*args]):
45+
return False
46+
return True
47+
48+
def _mock_listener_move_file(self):
49+
return mock.patch(LISTENER_MOCK_PATH + "._move_file", self._move_file_mocked)
50+
51+
def test_01_process_record_success(self):
52+
with self._mock_listener_move_file():
53+
self.record.write(
54+
{
55+
"edi_exchange_state": "input_received",
56+
"storage_id": self.backend.storage_id.id,
57+
}
58+
)
59+
self.record.action_exchange_process()
60+
storage, from_dir_str, to_dir_str, filename = self.fake_move_args
61+
self.assertEqual(storage, self.backend.storage_id)
62+
self.assertEqual(from_dir_str, self.backend.input_dir_pending)
63+
self.assertEqual(to_dir_str, self.backend.input_dir_done)
64+
self.assertEqual(filename, self.record.exchange_filename)
65+
66+
def test_02_process_record_with_error(self):
67+
with self._mock_listener_move_file():
68+
self.record.write(
69+
{
70+
"edi_exchange_state": "input_received",
71+
"storage_id": self.backend.storage_id.id,
72+
}
73+
)
74+
self.record._set_file_content("TEST %d" % self.record.id)
75+
self.record.with_context(
76+
test_break_process="OOPS! Something went wrong :("
77+
).action_exchange_process()
78+
storage, from_dir_str, to_dir_str, filename = self.fake_move_args
79+
self.assertEqual(storage, self.backend.storage_id)
80+
self.assertEqual(from_dir_str, self.backend.input_dir_pending)
81+
self.assertEqual(to_dir_str, self.backend.input_dir_error)
82+
self.assertEqual(filename, self.record.exchange_filename)

0 commit comments

Comments
 (0)