Skip to content

Commit

Permalink
Add edi_exchange_template
Browse files Browse the repository at this point in the history
  • Loading branch information
simahawk authored and john-herholz-dt committed Jun 28, 2024
1 parent 91b3a7f commit bbaab17
Show file tree
Hide file tree
Showing 19 changed files with 1,180 additions and 0 deletions.
83 changes: 83 additions & 0 deletions edi_exchange_template_oca/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
=====================
EDI Exchange Template
=====================

.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fedi-lightgray.png?logo=github
:target: https://github.com/OCA/edi/tree/13.0/edi_exchange_template
:alt: OCA/edi
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/edi-13-0/edi-13-0-edi_exchange_template
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/226/13.0
:alt: Try me on Runbot

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

Provide EDI exchange templates to control input/output records contents.

Provides following models:

1. EDI exchange output template, generates output using QWeb templates
2. [TODO] EDI exchange input template

.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_

**Table of contents**

.. contents::
:local:

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 smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/edi/issues/new?body=module:%20edi_exchange_template%0Aversion:%2013.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]>

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/13.0/edi_exchange_template>`_ 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_exchange_template_oca/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import components
18 changes: 18 additions & 0 deletions edi_exchange_template_oca/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2020 ACSONE
# @author: Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

{
"name": "EDI Exchange Template",
"summary": """Allows definition of exchanges via templates.""",
"version": "13.0.1.0.0",
"development_status": "Alpha",
"license": "AGPL-3",
"author": "ACSONE,Odoo Community Association (OCA)",
"maintainers": ["simahawk"],
"depends": ["edi", "component"],
"data": [
"security/ir_model_access.xml",
"views/edi_exchange_template_output_views.xml",
],
}
2 changes: 2 additions & 0 deletions edi_exchange_template_oca/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import common
from . import output_mixin
20 changes: 20 additions & 0 deletions edi_exchange_template_oca/components/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Copyright 2020 ACSONE
# @author: Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo.addons.component.core import AbstractComponent


class EDIExchangeInfoMixin(AbstractComponent):
"""Abstract component mixin provide info for exchanges."""

_name = "edi.info.provider.mixin"
_collection = "edi.backend"
# Enable validation of work context attributes
_work_context_validate_attrs = []

def __init__(self, work_context):
super().__init__(work_context)
for key in self._work_context_validate_attrs:
if not hasattr(work_context, key):
raise AttributeError(f"`{key}` is required for this component!")
42 changes: 42 additions & 0 deletions edi_exchange_template_oca/components/output_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright 2020 ACSONE
# @author: Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

import datetime

import pytz

from odoo import fields

from odoo.addons.component.core import AbstractComponent


class EDIExchangeInfoOutputMixin(AbstractComponent):
"""Abstract component mixin to generate GS1 compliant XML files."""

_name = "edi.output.mixin"
_inherit = "edi.info.provider.mixin"
# Enable validation of work context attributes
_work_context_validate_attrs = ["exchange_record"]

@property
def record(self):
return self.work.exchange_record.record

def generate_info(self):
"""Generate and return data for output info.
:return: odoo.tools.DotDict
"""
raise NotImplementedError()

# helper methods
@staticmethod
def _utc_now():
return datetime.datetime.utcnow().isoformat()

@staticmethod
def date_to_string(dt, utc=True):
if utc:
dt = dt.astimezone(pytz.UTC)
return fields.Date.to_string(dt)
3 changes: 3 additions & 0 deletions edi_exchange_template_oca/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import edi_backend
from . import edi_exchange_template_mixin
from . import edi_exchange_template_output
35 changes: 35 additions & 0 deletions edi_exchange_template_oca/models/edi_backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Copyright 2020 ACSONE SA
# @author Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).

from odoo import models


class EDIBackend(models.Model):
_inherit = "edi.backend"

def _generate_output(self, exchange_record, **kw):
# Template take precedence over component lookup
tmpl = self._get_output_template(exchange_record)
if tmpl:
return tmpl.generate_output(exchange_record, **kw)
return super()._generate_output(exchange_record, **kw)

@property
def output_template_model(self):
return self.env["edi.exchange.template.output"]

def _get_output_template(self, exchange_record, code=None):
"""Retrieve output templates by convention.
Template's code must match the same component usage as per normal components.
"""
tmpl = None
candidates = self._generate_output_component_usage_candidates(exchange_record)
if code:
candidates.insert(code)
for usage in candidates:
tmpl = self.output_template_model.search([("code", "=", usage)], limit=1)
if tmpl:
break
return tmpl
119 changes: 119 additions & 0 deletions edi_exchange_template_oca/models/edi_exchange_template_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
# Copyright 2020 ACSONE SA
# @author Simone Orsi <[email protected]>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import datetime
import logging
import textwrap
import time

import dateutil
import pytz

from odoo import fields, models
from odoo.tools import DotDict
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)


class EDIExchangeTemplateMixin(models.AbstractModel):
"""Define a common ground for EDI exchange templates.
"""

_name = "edi.exchange.template.mixin"
_description = "EDI Exchange Output Mixin"

name = fields.Char(required=True)
# TODO: make unique, autocompute slugified name
code = fields.Char(required=True, index=True)
backend_type_id = fields.Many2one(
string="EDI Backend type",
comodel_name="edi.backend.type",
ondelete="restrict",
required=True,
)
type_id = fields.Many2one(
string="EDI Exchange type",
comodel_name="edi.exchange.type",
ondelete="cascade",
auto_join=True,
)
backend_id = fields.Many2one(
comodel_name="edi.backend",
ondelete="cascade",
# TODO: default to type_id if given, validate otherwise
)
# TODO: add default content w/ comment on what you can use
code_snippet = fields.Text()
code_snippet_docs = fields.Text(
compute="_compute_code_snippet_docs",
default=lambda self: self._default_code_snippet_docs(),
)

def _compute_code_snippet_docs(self):
for rec in self:
rec.code_snippet_docs = textwrap.dedent(rec._default_code_snippet_docs())

def _default_code_snippet_docs(self):
return """
Available vars:
* datetime
* dateutil
* time
* uid
* user
* DotDict
"""

def _code_snippet_valued(self):
snippet = self.code_snippet or ""
return bool(
[
not line.startswith("#")
for line in (snippet.splitlines())
if line.strip("")
]
)

@staticmethod
def _utc_now():
return datetime.datetime.utcnow().isoformat()

@staticmethod
def _date_to_string(dt, utc=True):
if utc:
dt = dt.astimezone(pytz.UTC)
return fields.Date.to_string(dt)

def _get_code_snippet_eval_context(self):
"""Prepare the context used when evaluating python code
:returns: dict -- evaluation context given to safe_eval
"""
return {
"datetime": datetime,
"dateutil": dateutil,
"time": time,
"uid": self.env.uid,
"user": self.env.user,
"DotDict": DotDict,
}

def _evaluate_code_snippet(self, **render_values):
if not self._code_snippet_valued():
return {}
eval_ctx = dict(render_values, **self._get_code_snippet_eval_context())
safe_eval(self.code_snippet, eval_ctx, mode="exec", nocopy=True)
result = eval_ctx.get("result", {})
if not isinstance(result, dict):
_logger.error("code_snippet should return a dict into `result`")
return {}
return result

def _get_validator(self, exchange_record):
# TODO: lookup for validator (
# can be to validate received file or generated file)
pass

def validate(self, exchange_record):
pass
Loading

0 comments on commit bbaab17

Please sign in to comment.