Skip to content

[18.0][IMP] edi_core_oca: add listener action#232

Open
ArnauCForgeFlow wants to merge 3 commits intoOCA:18.0from
ForgeFlow:18.0-imp-edi_core_oca-move_listener
Open

[18.0][IMP] edi_core_oca: add listener action#232
ArnauCForgeFlow wants to merge 3 commits intoOCA:18.0from
ForgeFlow:18.0-imp-edi_core_oca-move_listener

Conversation

@ArnauCForgeFlow
Copy link

@ArnauCForgeFlow ArnauCForgeFlow commented Feb 12, 2026

Move _trigger_edi_event_make_name method to edi_core_oca module in order to make the listener usable without requiring the edi_component_oca module

@OCA-git-bot
Copy link
Contributor

Hi @simahawk, @etobella,
some modules you are maintaining are being modified, check this out!

@ArnauCForgeFlow ArnauCForgeFlow force-pushed the 18.0-imp-edi_core_oca-move_listener branch from 0bc4d2e to 7a14077 Compare February 13, 2026 10:32
@ArnauCForgeFlow
Copy link
Author

@simahawk Thanks for the review!

So, basically, your suggestion is moving the helper method _trigger_edi_event_make_name from the component module to the core, and then simply calling the _trigger_edi_event hook from the other modules?

@simahawk
Copy link
Contributor

@simahawk Thanks for the review!

So, basically, your suggestion is moving the helper method _trigger_edi_event_make_name from the component module to the core, and then simply calling the _trigger_edi_event hook from the other modules?

yes. And we should move the hook to the consumer mixin so that the logic won't be put into generic models like exc record.

@ArnauCForgeFlow
Copy link
Author

ArnauCForgeFlow commented Feb 19, 2026

@simahawk Thanks for the review!
So, basically, your suggestion is moving the helper method _trigger_edi_event_make_name from the component module to the core, and then simply calling the _trigger_edi_event hook from the other modules?

yes. And we should move the hook to the consumer mixin so that the logic won't be put into generic models like exc record.

The problem with moving the hook to the consumer mixin is that for input exchanges that fail early, there is no record_id yet. For example, in edi_storage_oca, even if the process fails, we still need to execute the hook to move the file from 'pending' directory to 'error' directory. Any idea on how to handle this?

Thanks!

@simahawk
Copy link
Contributor

simahawk commented Feb 26, 2026

@ArnauCForgeFlow good point. Well, that's why components have been used so far 😄

A possible approach is the one I took on endpoint_route_handler to check cross models that we wouldn't have any routing conflict. See https://github.com/OCA/web-api/blob/18.0/endpoint_route_handler/models/endpoint_route_handler.py#L90

We could collect all models inheriting from consumer mixin and trigger their handlers if any.
Something like that:

class EDIExchangeConsumerMixin(models.AbstractModel):
    _name = "edi.exchange.consumer.mixin"
    [...]

    @api.model
    def _get_edi_consumer_models(self):
        global EDI_CONSUMER_MODELS
        if EDI_CONSUMER_MODELS.get(self.env.cr.dbname):
            return EDI_CONSUMER_MODELS.get(self.env.cr.dbname)
        models = []
        for model in self.env.values():
            if (
                model._name != self._name
                and not model._abstract
                and self._name in model._inherit
            ):
                models.append(model._name)
        EDI_CONSUMER_MODELS[self.env.cr.dbname] = models
        return models

    def edi_trigger_consumer_models(self, trigger, records=None):
        """Trigger EDI config execution on all related models."""
        for model_name in self._get_edi_consumer_models():
            model = self.env[model_name]
            if hasattr(model, trigger):
                getattr(model, trigger)(records)

WDYT?

@simahawk
Copy link
Contributor

simahawk commented Feb 26, 2026

Answering myself: this can lead to overlapping handlers as you could have the same hook for different exchanges.

Could we leverage edi.configuration then?
By default edi config are meant to work w/ the partners linked to a record/model and a trigger.
We could have a generic configuration that does not need a partner but only a trigger and a model.
This will leave the door opened for manual configuration and to place the final handler where you prefer.

@ArnauCForgeFlow
Copy link
Author

Hi @simahawk Thanks for the suggestion! I've been looking into the edi.configuration approach, but to be honest, I'm a bit lost, so I'd love a bit more context on the implementation details.

  • Which method should be the trigger? on_edi_exchange_done / on_edi_exchange_error, or _trigger_edi_event?
  • In which model should I place the hooks? A custom one, edi.exchange.record, or edi.exchange.consumer.mixin?

Any quick example or guidance would be appreciated. Thanks!

@simahawk
Copy link
Contributor

simahawk commented Mar 5, 2026

All starts with notify_action_complete. This must be called all the times an "official" action is done.
In turns, it calls _trigger_edi_event, which should take care of what we are discussing now.

There we should trigger the event an all related records and all consumer models.
The trigger on related records partly implemented is since the split https://github.com/OCA/edi-framework/blob/18.0/edi_core_oca/models/edi_exchange_record.py#L525 but it's incomplete as _trigger_edi_event does nothing ATM.

Hence, for related records, we should do what is done already for state change, create and write https://github.com/OCA/edi-framework/blob/18.0/edi_core_oca/models/edi_exchange_consumer_mixin.py#L449
There's it's easier because the lookup of edi.config is done already via _edi_config_field_relation (eg:

return self.partner_id.edi_sale_conf_ids
).

When we don't have a record, we have to decide how to lookup for edi.confg. Today we always rely on a m2m relation via partner (we have a m2m relation to be able to reuse configs). We should probably state which configs are meant to run globally for a given model (eg: w/ a flag global or non_record_related, whatever).

I hope it's clear. Otherwise we can organize a call 😉

@ArnauCForgeFlow ArnauCForgeFlow force-pushed the 18.0-imp-edi_core_oca-move_listener branch from 7a14077 to 08547d9 Compare March 12, 2026 10:31
@ArnauCForgeFlow
Copy link
Author

ArnauCForgeFlow commented Mar 12, 2026

Hi, @simahawk I've applied the suggested changes. Please let me know if this aligns with what you had in mind. I’ve also included in an extra commit how edi_storage_oca will look with this change to show the full integration (if we merge, this commit will be deleted and added to the other PR).

Please let me know if I missed anything or misunderstood something. Thanks!

is_global = fields.Boolean(
string="Global Configuration",
help="If checked, this configuration will be executed for all records, "
"regardless of the partner.",
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
"regardless of the partner.",
"regardless of any related odoo record.",

@@ -0,0 +1,24 @@
<odoo noupdate="1">
<record id="edi_config_trigger_storage_done" model="edi.configuration.trigger">
<field name="name">On record exchange done</field>
Copy link
Contributor

Choose a reason for hiding this comment

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

aren't these triggers generic enough to stay in the core module? 🤔

help="Record created from a file found in this FS storage",
)

def _move_file(self, storage, from_dir_str, to_dir_str, filename):
Copy link
Contributor

Choose a reason for hiding this comment

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

I would move this to fs_storage as we are dragging this from project to project, module to module

functools.partial(move_func, storage, sftp_filepath, sftp_destination_path)
)

def on_edi_exchange_done(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def on_edi_exchange_done(self):
def _storage_on_edi_exchange_done(self):

res = self._move_file(storage, error_dir, done_dir, file)
return res

def on_edi_exchange_error(self):
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
def on_edi_exchange_error(self):
def _storage_on_edi_exchange_error(self):

I'm not a fan of adding this custom logic to the record, having it on the component was better.
However, TBH, I have no better place to suggest 🤷

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I agree with you, I think it's much cleaner to keep using components and move that logic into a glue module, as I did in my first approach.

Copy link
Contributor

Choose a reason for hiding this comment

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

we can, but at least we should prefix w/ proper naming (eg: _storage_*).

@etobella your POV?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants