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

[12.0][BPRT] edi_storage_oca: Backport from 14.0 #970

Open
wants to merge 69 commits into
base: 12.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
790d68d
Add edi_storage
simahawk Nov 27, 2020
59e050c
edi_storage: add base tests for component
simahawk Nov 27, 2020
776bde0
edi_storage: fix messages test isolation
simahawk Nov 29, 2020
8b87e33
[UPD] Update edi_storage.pot
oca-travis Nov 30, 2020
8fd2948
edi_storage 13.0.1.1.0
OCA-git-bot Nov 30, 2020
9b61749
edi_storage: adapt to edi api changes
simahawk Dec 2, 2020
4010b36
edi_storage 13.0.1.2.0
OCA-git-bot Dec 2, 2020
9eb8589
edi_storage: put ack handling on hold
simahawk Dec 2, 2020
d1349ff
edi_storage 13.0.1.3.0
OCA-git-bot Dec 2, 2020
123bb07
edi_storage: adapt to new components lookup
simahawk Feb 8, 2021
3a1eec3
edi_storage 13.0.1.4.0
OCA-git-bot Mar 10, 2021
4a2b22e
[CHG] edi: Use more permissive licence: AGPL-> LGPL
etobella Mar 10, 2021
91c753f
[UPD] README.rst
OCA-git-bot Mar 15, 2021
f654543
edi_storage 13.0.1.4.1
OCA-git-bot Mar 15, 2021
79fafc6
[FIX] edi_storage: sort key was not working properly
etobella Apr 7, 2021
176ad65
edi_storage 13.0.1.5.0
OCA-git-bot Apr 9, 2021
2a69f8e
[IMP] edi_storage_oca: isort, black, prettier.
fshah-initos May 5, 2021
8faf61a
[MIG] Migrate module edi_storage_oca to v14.
fshah-initos May 5, 2021
fe8e173
[UPD] Update edi_storage_oca.pot
oca-travis May 27, 2021
d2dc684
[UPD] README.rst
OCA-git-bot May 27, 2021
5d912f4
[IMP] edi_storage_oca: implement basis of the input process.
LoisRForgeFlow Jun 22, 2021
89cfa92
[FIX] edi_oca: show the exchange file name while there is no file yet.
LoisRForgeFlow Jun 22, 2021
165d24d
edi_storage: improve input process
simahawk Jun 23, 2021
58b8b49
[FIX] edi_storage_oca: support the use or not of filename patern + sm…
LoisRForgeFlow Jun 23, 2021
0d877ca
[IMP] edi_storage_oca: add specific channel and job function definitions
LoisRForgeFlow Jul 1, 2021
fc4ef99
[FIX] edi_storage_oca: support regex pattern searching when fetching …
LoisRForgeFlow Jul 1, 2021
cb7e369
[FIX] edi_storage: consider if the exchange type has a specific backe…
LoisRForgeFlow Jul 2, 2021
0ae2785
[IMP] : black, isort, prettier
LoisRForgeFlow Jul 5, 2021
410d2a7
[UPD] Update edi_storage_oca.pot
oca-travis Jul 6, 2021
3c7a47c
[UPD] README.rst
OCA-git-bot Jul 6, 2021
83d1961
edi_storage_oca 14.0.1.1.0
OCA-git-bot Jul 6, 2021
4f89da5
[IMP] edi_storage: add listener to move files to done/error dirs
LoisRForgeFlow Jul 5, 2021
6c7597a
[FIX] edi_storage: `list_files` expects a string.
LoisRForgeFlow Jul 9, 2021
8596037
edi_storage_oca 14.0.1.2.0
OCA-git-bot Jul 14, 2021
f60d5da
[14.0][FIX] edi_storage_oca: remove duplicate backend_type_id field i…
JordiMForgeFlow Aug 4, 2021
beb54a1
edi_storage_oca 14.0.1.2.1
OCA-git-bot Aug 4, 2021
e7464c8
[FIX] edi_storage: incorrect call to output checker in receive compon…
LoisRForgeFlow Sep 6, 2021
3eeee9f
edi_storage_oca 14.0.1.2.2
OCA-git-bot Oct 1, 2021
d01b27c
edi: improve backend view extension
simahawk Sep 17, 2021
56be5e4
[UPD] Update edi_storage_oca.pot
oca-travis Oct 13, 2021
8bc4990
edi_storage_oca 14.0.1.3.0
OCA-git-bot Oct 13, 2021
9e77f67
edi: load main vars at init
simahawk Sep 22, 2021
e2d6e86
edi_storage: fix find files test
simahawk Oct 30, 2021
4959f62
edi_storage_oca 14.0.1.4.0
OCA-git-bot Nov 2, 2021
c1935b5
edi: get rid of name, use identifier
simahawk Mar 10, 2022
e1e7dfd
edi_storage: fail gracefully w/o error report
simahawk Mar 10, 2022
75024b0
edi_storage_oca 14.0.1.5.0
OCA-git-bot Mar 19, 2022
73870d5
edi_storage_oca: improve error handling
jcoux May 17, 2022
4badf88
edi_storage_oca 14.0.1.5.1
OCA-git-bot May 19, 2022
ed65d40
edi_storage: path configurable by type and param
simahawk May 6, 2022
3747393
[IMP] edi_storage_oca: Update development status
JasminSForgeFlow Apr 12, 2022
fbf5313
[UPD] README.rst
OCA-git-bot Jul 13, 2022
159d4af
edi_storage_oca 14.0.1.5.2
OCA-git-bot Jul 13, 2022
e0e8832
edi_storage_oca 14.0.1.6.0
OCA-git-bot Jul 28, 2022
9087ec7
edi: fix _cron_check_output_exchange_sync
simahawk Aug 28, 2022
2f4d172
edi_storage_oca 14.0.1.7.0
OCA-git-bot Aug 29, 2022
e32ade2
edi_storage: adapt _check_output_exchange_sync override
simahawk Sep 6, 2022
fb99c66
edi_storage_oca 14.0.1.8.0
OCA-git-bot Sep 6, 2022
3fa35ad
[IMP] edi_storage_oca: Delete processed files if you want it
etobella Feb 7, 2023
80a6203
[UPD] Update edi_storage_oca.pot
Feb 15, 2023
4334ac5
edi_storage_oca 14.0.1.8.1
OCA-git-bot Feb 15, 2023
908f9e2
[UPD] README.rst
OCA-git-bot Sep 3, 2023
97891f5
Added translation using Weblate (Spanish)
Ivorra78 Nov 20, 2023
4da7b23
Translated using Weblate (Spanish)
Ivorra78 Nov 20, 2023
d78255b
[IMP] edi_storage_oca: black, isort, prettier
thienvh332 Apr 15, 2024
edfbabb
Add edi_storage
simahawk Nov 27, 2020
c33bb12
edi_storage: add base tests for component
simahawk Nov 27, 2020
2827f5d
[BPRT] edi_storage_oca: Backport from 14.0
thienvh332 Apr 17, 2024
378b050
[DON'T MERGE] Add test-requirements.txt
thienvh332 Apr 17, 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
114 changes: 114 additions & 0 deletions edi_storage_oca/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
===========================
EDI Storage backend support
===========================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:58511c29ba3a21a1216a155c53ffb1da90ba5561d407af23213e4207b9a0bcaf
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github
:target: https://github.com/OCA/edi/tree/12.0/edi_storage_oca
:alt: OCA/edi
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/edi-12-0/edi-12-0-edi_storage_oca
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/edi&target_branch=12.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

Allow exchange files using storage backends from `OCA/storage`.

This module adds a storage backend relation on the EDI backend.
There you can configure the backend to be used (most often and SFTP)
and the paths where to read or put files.

Often the convention when exchanging files via SFTP
is to have one input forder (to receive files)
and an output folder (to send files).

Inside this folder you have this hierarchy::

input/output folder
|- pending
|- done
|- error

* `pending` folder contains files that have been just sent
* `done` folder contains files that have been processes successfully
* `error` folder contains files with errors and cannot be processed

The storage handlers take care of reading files and putting files
in/from the right place and update exchange records data accordingly.

**Table of contents**

.. contents::
:local:

Usage
=====

Go to "EDI -> EDI backend" then configure your backend to use a storage backend.

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/edi/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/edi/issues/new?body=module:%20edi_storage_oca%0Aversion:%2012.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Do not contact contributors directly about support or help with technical issues.

Credits
=======

Authors
~~~~~~~

* ACSONE

Contributors
~~~~~~~~~~~~

* Simone Orsi <[email protected]>
* Foram Shah <[email protected]>
* Lois Rilo <[email protected]>
* `Trobz <https://trobz.com>`_:

* Thien <[email protected]>


Other credits
~~~~~~~~~~~~~

The backport of this module from 14.0 to 12.0 was financially supported by Camptocamp

Maintainers
~~~~~~~~~~~

This module is maintained by the OCA.

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

This module is part of the `OCA/edi <https://github.com/OCA/edi/tree/12.0/edi_storage_oca>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions edi_storage_oca/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import components
from . import models
24 changes: 24 additions & 0 deletions edi_storage_oca/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2020 ACSONE
# @author: Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

{
"name": "EDI Storage backend support",
"summary": """
Base module to allow exchanging files via storage backend (eg: SFTP).
""",
"version": "12.0.1.0.1",
"development_status": "Beta",
"license": "LGPL-3",
"website": "https://github.com/OCA/edi",
"author": "ACSONE,Odoo Community Association (OCA)",
"depends": ["edi_oca", "storage_backend", "component"],
"data": [
"data/cron.xml",
"data/job_channel_data.xml",
"data/queue_job_function_data.xml",
"security/ir_model_access.xml",
"views/edi_backend_views.xml",
],
"demo": ["demo/edi_backend_demo.xml"],
}
5 changes: 5 additions & 0 deletions edi_storage_oca/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from . import base
from . import check
from . import send
from . import receive
from . import listener
99 changes: 99 additions & 0 deletions edi_storage_oca/components/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Copyright 2020 ACSONE
# Copyright 2022 Camptocamp
# @author: Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import logging
from pathlib import PurePath

from odoo.addons.component.core import AbstractComponent

_logger = logging.getLogger(__file__)


class EDIStorageComponentMixin(AbstractComponent):

_name = "edi.storage.component.mixin"
_inherit = "edi.component.mixin"
# Components having `_storage_backend_type` will have precedence.
# If the value is not set, generic components will be used.
_storage_backend_type = None

@classmethod
def _component_match(cls, work, usage=None, model_name=None, **kw):
res = super()._component_match(work, usage=usage, model_name=model_name, **kw)
storage_type = kw.get("storage_backend_type")
if storage_type and cls._storage_backend_type:
return cls._storage_backend_type == storage_type
return res

@property
def storage(self):
return self.backend.storage_id

def _dir_by_state(self, direction, state):
"""Return remote directory path by direction and state.

:param direction: string stating direction of the exchange
:param state: string stating state of the exchange
:return: PurePath object
"""
assert direction in ("input", "output")
assert state in ("pending", "done", "error")
return PurePath(
(self.backend[direction + "_dir_" + state] or "").strip().rstrip("/")
)

def _remote_file_path(self, direction, state, filename):
"""Return remote file path by direction and state for give filename.

:param direction: string stating direction of the exchange
:param state: string stating state of the exchange
:param filename: string for file name
:return: PurePath object
"""
_logger.warning(
"Call of deprecated function `_remote_file_path`. "
"Please use `_get_remote_file_path` instead.",
)
return self._dir_by_state(direction, state) / filename.strip("/ ")

def _get_remote_file_path(self, state, filename=None):
"""Retrieve remote path for current exchange record."""
filename = filename or self.exchange_record.exchange_filename
direction = self.exchange_record.direction
directory = self._dir_by_state(direction, state).as_posix()
path = self.exchange_record.type_id._storage_fullpath(
directory=directory, filename=filename
)
return path

def _get_remote_file(self, state, filename=None, binary=False):
"""Get file for current exchange_record in the given destination state.

:param state: string ("pending", "done", "error")
:param filename: custom file name, exchange_record filename used by default
:return: remote file content as string
"""
path = self._get_remote_file_path(state, filename=filename)
try:
# TODO: support match via pattern (eg: filename-prefix-*)
# otherwise is impossible to retrieve input files and acks
# (the date will never match)
return self.storage._get_b64_data(path.as_posix(), binary=binary)
except FileNotFoundError:
_logger.info(
"Ignored FileNotFoundError when trying "
"to get file %s into path %s for state %s",
filename,
path,
state,
)
return None
except OSError:
_logger.info(
"Ignored OSError when trying to get file %s into path %s for state %s",
filename,
path,
state,
)
return None
92 changes: 92 additions & 0 deletions edi_storage_oca/components/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Copyright 2020 ACSONE
# @author: Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import logging

from odoo.tools import pycompat

from odoo.addons.component.core import Component

_logger = logging.getLogger(__name__)


class EDIStorageCheckComponentMixin(Component):

_name = "edi.storage.component.check"
_inherit = [
"edi.component.check.mixin",
"edi.storage.component.mixin",
]
_usage = "storage.check"

def check(self):
return self._exchange_output_check()

def _exchange_output_check(self):
"""Check status output exchange and update record.

1. check if the file has been processed already (done)
2. if yes, post message and exit
3. if not, check for errors
4. if no errors, return

:return: boolean
* False if there's nothing else to be done
* True if file still need action
"""
if self._get_remote_file("done"):
_logger.info(
"%s done",
self.exchange_record.identifier,
)
if (
not self.exchange_record.edi_exchange_state
== "output_sent_and_processed"
):
self.exchange_record.edi_exchange_state = "output_sent_and_processed"
self.exchange_record._notify_done()
return False

error = self._get_remote_file("error")
if error:
_logger.info(
"%s error",
self.exchange_record.identifier,
)
# Assume a text file will be placed there w/ the same name and error suffix
err_filename = self.exchange_record.exchange_filename + ".error"
error_report = (
self._get_remote_file("error", filename=err_filename) or "no-report"
)
if self.exchange_record.edi_exchange_state == "output_sent":
self.exchange_record.update(
{
"edi_exchange_state": "output_sent_and_error",
"exchange_error": pycompat.to_text(error_report),
}
)
self.exchange_record._notify_error("process_ko")
return False
return True

# FIXME: this is not used ATM -> should be refactored
# into an incoming exchange.
# The backend will look for records needing an ack
# and generate and ack record.
def _exchange_output_handle_ack(self):
ack_type = self.exchange_record.type_id.ack_type_id
filename = ack_type._make_exchange_filename(self.exchange_record)
ack_file = self._get_remote_file("done", filename=filename)
if ack_file:
self.backend.create_record(
ack_type.code,
{
"parent_id": self.exchange_record.id,
"exchange_file": ack_file,
"edi_exchange_state": "input_received",
},
)
self.exchange_record._notify_ack_received()
else:
self.exchange_record._notify_ack_missing()
Loading
Loading