Skip to content

Commit

Permalink
edi_oca: Add new model edi.configuration
Browse files Browse the repository at this point in the history
The aim of this model is to ease configuration for all kind of exchanges
in particular at partner level.
  • Loading branch information
thienvh332 authored and simahawk committed Nov 15, 2024
1 parent 0b6b2c5 commit 541bd96
Show file tree
Hide file tree
Showing 16 changed files with 648 additions and 15 deletions.
1 change: 1 addition & 0 deletions edi_oca/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ Contributors

* Simone Orsi <[email protected]>
* Enric Tobella <[email protected]>
* Thien Vo <[email protected]>

Maintainers
~~~~~~~~~~~
Expand Down
2 changes: 2 additions & 0 deletions edi_oca/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,15 @@
"data/sequence.xml",
"data/job_channel.xml",
"data/job_function.xml",
"data/edi_configuration.xml",
"security/res_groups.xml",
"security/ir_model_access.xml",
"views/edi_backend_views.xml",
"views/edi_backend_type_views.xml",
"views/edi_exchange_record_views.xml",
"views/edi_exchange_type_views.xml",
"views/edi_exchange_type_rule_views.xml",
"views/edi_configuration_views.xml",
"views/menuitems.xml",
"templates/exchange_chatter_msg.xml",
"templates/exchange_mixin_buttons.xml",
Expand Down
23 changes: 23 additions & 0 deletions edi_oca/data/edi_configuration.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- The `ir_action` parameter must be passed in order to use `send_via_email`
Examlple:
<field name="snippet_do">record._edi_send_via_email(ir_action)</field>
-->
<record id="edi_conf_send_via_email" model="edi.configuration">
<field name="name">Send Via Email</field>
<field name="active">False</field>
<field name="code">on_send_via_email</field>
<field name="trigger">on_send_via_email</field>
<field name="snippet_do">record._edi_send_via_email()</field>
</record>

<!-- Add type_id to use Send Via EDI -->
<record id="edi_conf_send_via_edi" model="edi.configuration">
<field name="name">Send Via EDI</field>
<field name="active">False</field>
<field name="code">on_send_via_edi</field>
<field name="trigger">on_send_via_edi</field>
<field name="snippet_do">record._edi_send_via_edi(conf.type_id)</field>
</record>
</odoo>
1 change: 1 addition & 0 deletions edi_oca/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
from . import edi_exchange_type
from . import edi_exchange_type_rule
from . import edi_id_mixin
from . import edi_configuration
22 changes: 15 additions & 7 deletions edi_oca/models/edi_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,19 @@ def _cron_check_output_exchange_sync(self, **kw):
for backend in self:
backend._check_output_exchange_sync(**kw)

def exchange_generate_send(self, recordset, skip_generate=False, skip_send=False):
for rec in recordset:
if skip_generate:
job1 = rec
else:
job1 = rec.delayable().action_exchange_generate()
if hasattr(job1, "on_done"):
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()

def _check_output_exchange_sync(
self, skip_send=False, skip_sent=True, record_ids=None
):
Expand All @@ -396,13 +409,8 @@ def _check_output_exchange_sync(
"EDI Exchange output sync: found %d new records to process.",
len(new_records),
)
for rec in new_records:
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 new_records:
self.exchange_generate_send(new_records, skip_send=skip_send)

if skip_send:
return
Expand Down
209 changes: 209 additions & 0 deletions edi_oca/models/edi_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
# Copyright 2024 Camptocamp SA
# @author Simone Orsi <[email protected]>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).

import datetime

import pytz

from odoo import _, api, exceptions, fields, models
from odoo.tools import DotDict, safe_eval


def date_to_datetime(dt):
"""Convert date to datetime."""
if isinstance(dt, datetime.date):
return datetime.datetime.combine(dt, datetime.datetime.min.time())
return dt


def to_utc(dt):
"""Convert date or datetime to UTC."""
# Gracefully convert to datetime if needed 1st
return date_to_datetime(dt).astimezone(pytz.UTC)


class EdiConfiguration(models.Model):
_name = "edi.configuration"
_description = """
This model is used to configure EDI (Electronic Data Interchange) flows.
It allows users to create their own configurations, which can be tailored
to meet the specific needs of their business processes.
"""

name = fields.Char(string="Name", required=True)
active = fields.Boolean(default=True)
code = fields.Char(required=True, copy=False, index=True, unique=True)
description = fields.Char(help="Describe what the conf is for")
backend_id = fields.Many2one(string="Backend", comodel_name="edi.backend")
# Field `type_id` is not a mandatory field because we will create 2 common confs
# for EDI (`send_via_email` and `send_via_edi`). So type_id is
# a mandatory field will create unwanted data for users when installing this module.
type_id = fields.Many2one(
string="Exchange Type",
comodel_name="edi.exchange.type",
ondelete="cascade",
auto_join=True,
index=True,
)
model_id = fields.Many2one(
"ir.model",
string="Model",
help="Model the conf applies to. Leave blank to apply for all models",
)
model_name = fields.Char(related="model_id.model", store=True)
trigger = fields.Selection(
# The selections below are intended to assist with basic operations
# and are used to setup common configuration.
[
("on_record_write", "Update Record"),
("on_record_create", "Create Record"),
("on_send_via_email", "Send Via Email"),
("on_send_via_edi", "Send Via EDI"),
("disabled", "Disabled"),
],
string="Trigger",
# The default selection will be disabled.
# which would allow to keep the conf visible but disabled.
required=True,
default="disabled",
ondelete="on default",
)
snippet_before_do = fields.Text(
string="Snippet Before Do",
help="Snippet to validate the state and collect records to do",
)
snippet_do = fields.Text(
string="Snippet Do",
help="""Used to do something specific here.
Receives: operation, edi_action, vals, old_vals.""",
)

@api.constrains("backend_id", "type_id")
def _constrains_backend(self):
for rec in self:
if rec.type_id.backend_id:
if rec.type_id.backend_id != rec.backend_id:
raise exceptions.ValidationError(
_("Backend must match with exchange type's backend!")
)
else:
if rec.type_id.backend_type_id != rec.backend_id.backend_type_id:
raise exceptions.ValidationError(
_("Backend type must match with exchange type's backend type!")
)

# TODO: This function is also available in `edi_exchange_template`.
# Consider adding this to util or mixin
def _code_snippet_valued(self, snippet):
snippet = snippet or ""
return bool(
[
not line.startswith("#")
for line in (snippet.splitlines())
if line.strip("")
]
)

@staticmethod
def _date_to_string(dt, utc=True):
if not dt:
return ""
if utc:
dt = to_utc(dt)
return fields.Date.to_string(dt)

@staticmethod
def _datetime_to_string(dt, utc=True):
if not dt:
return ""
if utc:
dt = to_utc(dt)
return fields.Datetime.to_string(dt)

def _time_utils(self):
return {
"datetime": safe_eval.datetime,
"dateutil": safe_eval.dateutil,
"time": safe_eval.time,
"utc_now": fields.Datetime.now(),
"date_to_string": self._date_to_string,
"datetime_to_string": self._datetime_to_string,
"time_to_string": lambda dt: dt.strftime("%H:%M:%S") if dt else "",
"first_of": fields.first,
}

def _get_code_snippet_eval_context(self):
"""Prepare the context used when evaluating python code
:returns: dict -- evaluation context given to safe_eval
"""
ctx = {
"uid": self.env.uid,
"user": self.env.user,
"DotDict": DotDict,
"conf": self,
}
ctx.update(self._time_utils())
return ctx

def _evaluate_code_snippet(self, snippet, **render_values):
if not self._code_snippet_valued(snippet):
return {}
eval_ctx = dict(render_values, **self._get_code_snippet_eval_context())
safe_eval.safe_eval(snippet, eval_ctx, mode="exec", nocopy=True)
result = eval_ctx.get("result", {})
if not isinstance(result, dict):
return {}
return result

def edi_exec_snippet_before_do(self, record, **kwargs):
self.ensure_one()
# Execute snippet before do
vals_before_do = self._evaluate_code_snippet(
self.snippet_before_do, record=record, **kwargs
)

# Prepare data
vals = {
"todo": vals_before_do.get("todo", True),
"snippet_do_vars": vals_before_do.get("snippet_do_vars", False),
"event_only": vals_before_do.get("event_only", False),
"tracked_fields": vals_before_do.get("tracked_fields", False),
"edi_action": vals_before_do.get("edi_action", False),
}
return vals

def edi_exec_snippet_do(self, record, **kwargs):
self.ensure_one()
if self.trigger == "disabled":
return False

old_value = kwargs.get("old_vals", {}).get(record.id, {})
new_value = kwargs.get("vals", {}).get(record.id, {})
vals = {
"todo": True,
"record": record,
"operation": kwargs.get("operation", False),
"edi_action": kwargs.get("edi_action", False),
"old_value": old_value,
"vals": new_value,
}
if self.snippet_before_do:
before_do_vals = self.edi_exec_snippet_before_do(record, **kwargs)
vals.update(before_do_vals)
if vals["todo"]:
return self._evaluate_code_snippet(self.snippet_do, **vals)
return True

def edi_get_conf(self, trigger, backend=None):
domain = [("trigger", "=", trigger)]
if backend:
domain.append(("backend_id", "=", backend.id))
else:
# We will only get confs that have backend_id = False
# or are equal to self.type_id.backend_id.id
backend_ids = self.mapped("type_id.backend_id.id")
backend_ids.append(False)
domain.append(("backend_id", "in", backend_ids))
return self.filtered_domain(domain)
3 changes: 3 additions & 0 deletions edi_oca/models/edi_exchange_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,9 @@ def action_exchange_generate(self, **kw):
self.ensure_one()
return self.backend_id.exchange_generate(self, **kw)

def action_exchange_generate_send(self, **kw):
return self.backend_id.exchange_generate_send(self, **kw)

def action_exchange_send(self):
self.ensure_one()
return self.backend_id.exchange_send(self)
Expand Down
1 change: 1 addition & 0 deletions edi_oca/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
* Simone Orsi <[email protected]>
* Enric Tobella <[email protected]>
* Thien Vo <[email protected]>
18 changes: 18 additions & 0 deletions edi_oca/security/ir_model_access.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,22 @@
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base_edi.group_edi_manager'))]" />
</record>
<record model="ir.model.access" id="access_edi_configuration_manager">
<field name="name">access_edi_configuration manager</field>
<field name="model_id" ref="model_edi_configuration" />
<field name="group_id" ref="base_edi.group_edi_manager" />
<field name="perm_read" eval="1" />
<field name="perm_create" eval="1" />
<field name="perm_write" eval="1" />
<field name="perm_unlink" eval="1" />
</record>
<record model="ir.model.access" id="access_edi_configuration_user">
<field name="name">access_edi_configuration user</field>
<field name="model_id" ref="model_edi_configuration" />
<field name="group_id" ref="base.group_user" />
<field name="perm_read" eval="1" />
<field name="perm_create" eval="0" />
<field name="perm_write" eval="0" />
<field name="perm_unlink" eval="0" />
</record>
</odoo>
12 changes: 5 additions & 7 deletions edi_oca/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@

/*
:Author: David Goodger ([email protected])
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
Expand Down Expand Up @@ -275,7 +274,7 @@
margin-left: 2em ;
margin-right: 2em }

pre.code .ln { color: gray; } /* line numbers */
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
Expand All @@ -301,7 +300,7 @@
span.pre {
white-space: pre }

span.problematic, pre.problematic {
span.problematic {
color: red }

span.section-subtitle {
Expand Down Expand Up @@ -516,14 +515,13 @@ <h2><a class="toc-backref" href="#toc-entry-13">Contributors</a></h2>
<ul class="simple">
<li>Simone Orsi &lt;<a class="reference external" href="mailto:simahawk&#64;gmail.com">simahawk&#64;gmail.com</a>&gt;</li>
<li>Enric Tobella &lt;<a class="reference external" href="mailto:etobella&#64;creublanca.es">etobella&#64;creublanca.es</a>&gt;</li>
<li>Thien Vo &lt;<a class="reference external" href="mailto:thienvh&#64;trobz.com">thienvh&#64;trobz.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-14">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>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.</p>
Expand Down
1 change: 1 addition & 0 deletions edi_oca/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@
from . import test_security
from . import test_quick_exec
from . import test_exchange_type_deprecated_fields
from . import test_edi_configuration
Loading

0 comments on commit 541bd96

Please sign in to comment.