diff --git a/edi_core_oca/README.rst b/edi_core_oca/README.rst new file mode 100644 index 000000000..dd8b8f609 --- /dev/null +++ b/edi_core_oca/README.rst @@ -0,0 +1,240 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +=== +EDI +=== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d81318a2b32374412fddae78f470a1ed33828abb1b021ead2d4bbbd98213cab9 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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--framework-lightgray.png?logo=github + :target: https://github.com/OCA/edi-framework/tree/19.0/edi_core_oca + :alt: OCA/edi-framework +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/edi-framework-19-0/edi-framework-19-0-edi_core_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-framework&target_branch=19.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Base EDI backend. + +Provides following models: + +1. EDI Backend, to centralize configuration +2. EDI Backend Type, to classify EDI backends (eg: UBL, GS1, e-invoice, + pick-yours) +3. EDI Exchange Type, to define file types of exchange +4. EDI Exchange Record, to define a record exchanged between systems + +Also define a mixin to be inherited by records that will generate EDIs + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +This module aims to provide an infrastructure to simplify +interchangability of documents between systems providing a configuration +platform. It will be inherited by other modules in order to define the +proper implementations of components. + +In order to define a new Exchange Record, we need to configure: + +- Backend Type +- Exchange Type +- Backend +- Components + +Jobs +---- + +- **Internal User**: might be an EDI user without even knowing about it, + triggering EDI flows by some of his actions on business records; does + not need access to related queue jobs. + +- **EDI User**: more conscious EDI user that might sometimes need to + debug things a bit further and thus needs access to related queue + jobs. + +- **EDI Manager**: full configuration access. + +Code to execute +--------------- + +By default, EDI Framework uses fields on ``edi.backend`` to get the +right function to execute. Each function is related to a model where the +specific function is defined. This models needs to inherit the specific +handler of each case. + +- receive: model ``edi.oca.handler.receive`` with function receive. +- process: model ``edi.oca.handler.process`` with function process. +- generate: model ``edi.oca.handler.generate`` with function generate. +- send: model ``edi.oca.handler.send`` with function send. +- check: model ``edi.oca.handler.check`` with function check. +- validate on inputs: model ``edi.oca.handler.input.validate`` with + function input_validate. +- validate on outputs: model ``edi.oca.handler.output.validate`` with + function input_validate. + +You can see an example on the tests fake_models. + +For a more complex behaviour, you can use ``edi_component_oca`` module +to use components. + +User EDI generation +------------------- + +On the exchange type, it might be possible to define a set of models, a +domain and a snippet of code. After defining this fields, we will +automatically see buttons on the view to generate the exchange records. +This configuration is useful to define a way of generation managed by +user. + +Exchange type rules configuration +--------------------------------- + +Exchange types can be further configured with rules. You can use rules +to: + +1. make buttons automatically appear in forms +2. define your own custom logic + +Go to an exchange type and go to the tab "Model rules". There you can +add one or more rule, one per model. On each rule you can define a +domain or a snippet to activate it. In case of a "Form button" kind, if +the domain and/ the snippet is/are satisfied, a form btn will appear on +the top of the form. This button can be used by the end user to manually +generate an exchange. If there's more than a backend and the exchange +type has not a backend set, a wizard will appear asking to select a +backend to be used for the exchange. + +In case of "Custom" kind, you'll have to define your own logic to do +something. + +Usage +===== + +After certain operations or manual execution, Exchange records will be +generated. This Exchange records might be input records or outputs +records. + +The change of state can be manually executed by the system or be managed +through by ir.cron. + +Output Exchange records +----------------------- + +An output record is intended to be used for exchange information from +Odoo to another system. + +The flow of an output record should be: + +- Creation +- Generation of data +- Validation of data +- Sending data +- Validation of data processed properly by the other party + +Input Exchange records +---------------------- + +An input record is intended to be used for exchange information another +system to odoo. + +The flow of an input record should be: + +- Creation +- Reception of data +- Checking data +- Processing data + +Known issues / Roadmap +====================== + +14.0.1.0.0 +---------- + +The module name has been changed from edi to edi_oca. + +18.0.1.4.0 +---------- + +Components dependancy has been removed and set on a new dependant module +``edi_component_oca``. Module ``edi_oca`` has been_renamed to +``edi_core_oca``. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* ACSONE +* Dixmit +* Camptocamp + +Contributors +------------ + +- Simone Orsi +- Enric Tobella +- Manuel Regidor +- Thien Vo +- Jordi Masvidal + +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. + +.. |maintainer-simahawk| image:: https://github.com/simahawk.png?size=40px + :target: https://github.com/simahawk + :alt: simahawk +.. |maintainer-etobella| image:: https://github.com/etobella.png?size=40px + :target: https://github.com/etobella + :alt: etobella + +Current `maintainers `__: + +|maintainer-simahawk| |maintainer-etobella| + +This module is part of the `OCA/edi-framework `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/edi_core_oca/__init__.py b/edi_core_oca/__init__.py new file mode 100644 index 000000000..aee8895e7 --- /dev/null +++ b/edi_core_oca/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/edi_core_oca/__manifest__.py b/edi_core_oca/__manifest__.py new file mode 100644 index 000000000..efb8f0e26 --- /dev/null +++ b/edi_core_oca/__manifest__.py @@ -0,0 +1,55 @@ +# Copyright 2020 ACSONE +# Copyright 2021 Camptocamp +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +{ + "name": "EDI", + "summary": """ + Define backends, exchange types, exchange records, + basic automation and views for handling EDI exchanges. + """, + "version": "19.0.1.0.0", + "website": "https://github.com/OCA/edi-framework", + "development_status": "Beta", + "license": "LGPL-3", + "author": "ACSONE,Dixmit,Camptocamp,Odoo Community Association (OCA)", + "maintainers": ["simahawk", "etobella"], + "depends": [ + "base_edi", + "mail", + "base_sparse_field", + ], + "external_dependencies": {"python": ["PyYAML", "openupgradelib"]}, + "data": [ + "wizards/edi_exchange_record_create_wiz.xml", + "data/cron.xml", + "data/ir_actions_server.xml", + "data/sequence.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/edi_configuration_trigger_views.xml", + "views/res_partner.xml", + "views/menuitems.xml", + "templates/exchange_chatter_msg.xml", + "templates/exchange_mixin_buttons.xml", + ], + "assets": { + "web.assets_backend": [ + "edi_core_oca/static/src/js/widget_edi.esm.js", + "edi_core_oca/static/src/xml/widget_edi.xml", + ], + "web.assets_unit_tests": [ + "edi_core_oca/static/tests/**/*", + ], + }, + "demo": ["demo/edi_backend_demo.xml"], + "installable": True, +} diff --git a/edi_core_oca/data/cron.xml b/edi_core_oca/data/cron.xml new file mode 100644 index 000000000..691bd3340 --- /dev/null +++ b/edi_core_oca/data/cron.xml @@ -0,0 +1,32 @@ + + + + EDI exchange check output sync + + + 1 + hours + + code + model.search([])._cron_check_output_exchange_sync() + + + + EDI exchange check input sync + + + 1 + hours + + code + model.search([])._cron_check_input_exchange_sync() + + diff --git a/edi_core_oca/data/edi_configuration.xml b/edi_core_oca/data/edi_configuration.xml new file mode 100644 index 000000000..565919c74 --- /dev/null +++ b/edi_core_oca/data/edi_configuration.xml @@ -0,0 +1,42 @@ + + + + + + On record create + on_record_create + Trigger when a record is created + + + On record write + on_record_write + Trigger when a record is updated + + + + Send via email + on_send_via_email + Send record via email TBD + + + Send via EDI + on_send_via_edi + Send record via EDI TBD + + + + Send Via Email + False + + record._edi_send_via_email() + + + Send Via EDI + False + + record._edi_send_via_edi(conf.type_id) + + diff --git a/edi_core_oca/data/ir_actions_server.xml b/edi_core_oca/data/ir_actions_server.xml new file mode 100644 index 000000000..b1309cec9 --- /dev/null +++ b/edi_core_oca/data/ir_actions_server.xml @@ -0,0 +1,14 @@ + + + + Retry + + + + code + + if records: + action = records.action_retry() + + + diff --git a/edi_core_oca/data/sequence.xml b/edi_core_oca/data/sequence.xml new file mode 100644 index 000000000..37eb19893 --- /dev/null +++ b/edi_core_oca/data/sequence.xml @@ -0,0 +1,11 @@ + + + + + EDI Exchange Record + edi.exchange + EDI/%(year)s/ + 10 + + + diff --git a/edi_core_oca/demo/edi_backend_demo.xml b/edi_core_oca/demo/edi_backend_demo.xml new file mode 100644 index 000000000..4e69bf811 --- /dev/null +++ b/edi_core_oca/demo/edi_backend_demo.xml @@ -0,0 +1,11 @@ + + + + Demo EDI backend type + demo_backend + + + Demo EDI backend + + + diff --git a/edi_core_oca/exceptions.py b/edi_core_oca/exceptions.py new file mode 100644 index 000000000..4bd8d5cc9 --- /dev/null +++ b/edi_core_oca/exceptions.py @@ -0,0 +1,11 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +class EDIValidationError(Exception): + """Thrown when a document validation fails.""" + + +class EDINotImplementedError(NotImplementedError): + """Thrown when a method is not implemented for a specific backend.""" diff --git a/edi_core_oca/i18n/edi_core_oca.pot b/edi_core_oca/i18n/edi_core_oca.pot new file mode 100644 index 000000000..f7b5222ce --- /dev/null +++ b/edi_core_oca/i18n/edi_core_oca.pot @@ -0,0 +1,1621 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * edi_core_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "" +"\n" +" Advanced technical settings as YAML format.\n" +" The YAML structure should reproduce a dictionary.\n" +" The backend might use these settings for automated operations.\n" +"\n" +" Currently supported conf:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # set a value for component work context\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # set a value for the whole processing env\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In any case, you can use these settings\n" +" to provide your own configuration for whatever need you might have.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration_trigger +msgid "" +"\n" +" Describe what triggers a specific action for a configuration.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration +msgid "" +"\n" +" This model is used to configure EDI (Electronic Data Interchange) flows.\n" +" It allows users to create their own configurations, which can be tailored\n" +" to meet the specific needs of their business processes.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "" +"\n" +" Automatically set the record as processed after sending.\n" +" Usecase: the web service you send the file to processes it on the fly.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__kind +msgid "" +"\n" +"* Form button: show a button on the related model form\n" +" when conditions from domain and snippet are satisfied\n" +"\n" +"* Custom: let devs handle a custom behavior with specific developments\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_consumer_mixin_buttons +msgid " EDI actions" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "" +"\n" +" The related record is not available anymore.\n" +" Consider deleting this record too or fixing its relation.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "EDI exchange:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Message:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "State:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Type:" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_related_record_related_record_unique +msgid "A record can only be related once to a specific exchange record." +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "ACK" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file is required for this exchange but not found." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received but contains errors." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK generated for current exchange." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_received_on +msgid "ACK received on" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_consumer_mixin +msgid "Abstract record where exchange records can be assigned" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_expected +msgid "Ack Expected" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "Ack exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_for_type_ids +msgid "Ack for exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Action retry: state moved back to '%s'" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__active +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Active" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings +msgid "Advanced Settings" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "Advanced YAML settings" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Advanced settings" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_all +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "All" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__allow_empty_files_on_receive +msgid "Allow Empty Files" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "An error happened while sending. Please check exchange record info." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__model_id +msgid "Apply to this model" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_trigger_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Archived" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "" +"Auto generate output for records missing their payload. If active, a cron " +"will take care of generating the output when not set yet. " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Backend" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "Backend Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend must match with exchange type's backend!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_type.py:0 +msgid "Backend should respect backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_type_id +msgid "Backend type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_backend_type_uniq_code +msgid "Backend type code must be unique!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend type must match with exchange type's backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend_type +msgid "Backend types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend +msgid "Backends" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Cancel" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__check_model_id +msgid "Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__code +msgid "Code" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_configuration_trigger_code_uniq +msgid "Code must be unique" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__company_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__company_id +msgid "Company" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_config +msgid "Config" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_configuration +msgid "Configurations" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record_create_wiz +msgid "Create an Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Create date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_uid +msgid "Created by" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_date +msgid "Created on" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__custom +msgid "Custom" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "Decoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__description +msgid "Describe what the conf is for" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__description +msgid "Description" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__direction +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__direction +msgid "Direction" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "Disable auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Domain" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.view_partner_form +msgid "EDI" +msgstr "" + +#. module: edi_core_oca +#: model:res.groups,name:edi_core_oca.group_edi_advanced_settings_manager +msgid "EDI Advanced Settings Manager" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_view +#: model:ir.model,name:edi_core_oca.model_edi_backend +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "EDI Backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_type_view +#: model:ir.model,name:edi_core_oca.model_edi_backend_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_search +msgid "EDI Backend Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_id +msgid "EDI Backend type" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Before Do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_configuration_view +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Configuration" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "EDI Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_view +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "EDI Exchange Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_rule_view +msgid "EDI Exchange Type Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type_rule +msgid "EDI Exchange type rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_id_mixin__edi_id +msgid "EDI ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_id_mixin +msgid "EDI ID mixin" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_check +msgid "EDI OCA Handler Check" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_generate +msgid "EDI OCA Handler Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_input_validate +msgid "EDI OCA Handler Input Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_output_validate +msgid "EDI OCA Handler Output Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_process +msgid "EDI OCA Handler Process" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_receive +msgid "EDI OCA Handler Receive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_send +msgid "EDI OCA Handler Send" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record +msgid "EDI exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_related_record +msgid "EDI exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_input_exchange_ir_actions_server +msgid "EDI exchange check input sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_output_exchange_ir_actions_server +msgid "EDI exchange check output sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_type_id +msgid "EDI origin exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI origin record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI record that originated this document." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_config +msgid "Edi Config" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Edi Configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_has_form_config +msgid "Edi Has Form Config" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Empty files are not allowed for exchange type %(name)s (%(code)s)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Enable on domain" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "Enable on snippet" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "Enabled for partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Encoding" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "Encoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding +msgid "" +"Encoding to be applied to generate/process the exchanged file.\n" +"Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed_error +msgid "Error on process" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_receive_error +msgid "Error on reception" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__validate_error +msgid "Error on validation" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_file +msgid "Exchange File" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "Exchange File Auto Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_ext +msgid "Exchange File Ext" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filechecksum +msgid "Exchange Filechecksum" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filename +msgid "Exchange Filename" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "Exchange Filename Pattern" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "Exchange Filename Sequence" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_ids +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__exchange_record_id +msgid "Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_count +msgid "Exchange Record Count" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_related_record_ids +msgid "Exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__exchange_type_id +msgid "Exchange Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange data generated" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchange date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error +msgid "Exchange error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error_traceback +msgid "Exchange error traceback" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not received" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not valid" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed with errors" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange received successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d already has a file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d is not an outgoing record, cannot be generated" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "" +"Exchange record ID=%d is not in draft state and has already an output value." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__edi_exchange_state +msgid "Exchange state" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange state must respect direction!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type_rule +msgid "Exchange type rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchange types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Exchanged On" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_root +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_failed +msgid "Exchanges - failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_inbound +msgid "Exchanges - inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_outbound +msgid "Exchanges - outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_pending +msgid "Exchanges - pending" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Execution handlers" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__external_identifier +msgid "External Identifier" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_pending +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Filter domain to be checked on Models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "" +"For output exchange types this should be a formatting string with the following variables available (to be used between brackets, `{}`): `exchange_record`, `record_name`, `type`, `dt` and `seq`. For instance, a valid string would be {record_name}-{type.code}-{dt}-{seq}\n" +"For more information:\n" +"- `exchange_record` means exchange record\n" +"- `record_name` means name of the exchange record\n" +"- `type` means code of the exchange record type\n" +"- `dt` means datetime\n" +"- `seq` means sequence. You need a sequence to be defined in `Exchange Filename Sequence` to use `seq`\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Form" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__form_btn +msgid "Form button" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Form button label" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Form button tooltip" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.edi_exchange_record_create_act_window +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__generate_model_id +msgid "Generator" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Group By" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "" +"Handling of decoding errors on process (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "" +"Handling of encoding errors on generate (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__has_message +msgid "Has Message" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Help message visible as tooltip on button h-over" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__id +msgid "ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__identifier +msgid "Identifier" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "" +"Identify the type of the ack. If this field is valued it means an hack is " +"expected." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "" +"If the `Exchange Filename Pattern` has `{seq}`, you should define a sequence" +" in this field to show the sequence in your filename" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__ignore +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__ignore +msgid "Ignore" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Inactive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_inbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__input +msgid "Input" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__input_validate_model_id +msgid "Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_id_mixin__edi_id +msgid "Internal or external identifier for records." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_checker +msgid "Is Edi Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_generator +msgid "Is Edi Generator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_input_validator +msgid "Is Edi Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_output_validator +msgid "Is Edi Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_processor +msgid "Is Edi Processor" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_receiver +msgid "Is Edi Receiver" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_sender +msgid "Is Edi Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__kind +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Kind" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_date +msgid "Last Updated on" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_ids +msgid "Messages" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Model" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model +msgid "Model code" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Model rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_name +msgid "Model tech name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__model_id +msgid "Model the conf applies to. Leave blank to apply for all models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_ir_model +msgid "Models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__name +msgid "Name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__new +msgid "New" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "No handler for %(action)s" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Open Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Original exchange which originated this record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_outbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__output +msgid "Output" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "Output Sent Processed Auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__output_validate_model_id +msgid "Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Parent" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Parent exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "Partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_failed +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Pending" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed +msgid "Processed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__process_model_id +msgid "Processor" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "Quick execution" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__strict +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__strict +msgid "Raise Error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_received +msgid "Received" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__receive_model_id +msgid "Receiver" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_recent +msgid "Recent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_recent_view +msgid "Recent exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__res_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__res_id +msgid "Record" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to send!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be processed" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be sent!" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Regenerate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_name +msgid "Related Name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_exists +msgid "Related Record Exists" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related Records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_exchange_ids +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related exchanges" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_ids +msgid "Related records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__surrogateescape +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__surrogateescape +msgid "Replace Byte with Individual Surrogate Code" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__backslashreplace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__backslashreplace +msgid "Replace with Backslashed Escape Sequences" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__replace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__replace +msgid "Replace with Replacement Marker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__xmlcharrefreplace +msgid "Replace with XML/HTML Numeric Character Reference" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__res_id +msgid "Res" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.action_retry_edi_exchange_record +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Retry" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__retryable +msgid "Retryable" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rules to handle exchanges and UI automatically" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__send_model_id +msgid "Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent +msgid "Sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_error +msgid "Sent and error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_processed +msgid "Sent and processed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_received_on +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Sent or received on this date." +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created in the last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created today" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Snippet" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet Before Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_do +msgid "Snippet Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "" +"Snippet of code to be checked on Models,\n" +" You can use `record` and `exchange_type` here.\n" +" It will be executed if variable result has been defined as True\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet to validate the state and collect records to do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "State" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Status" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Successful" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_type_code_uniq +msgid "The code must be unique per backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_external_identifier_uniq +msgid "The external_identifier must be unique for a type and a backend." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_identifier_uniq +msgid "The identifier must be unique." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__retryable +msgid "The record state can be rolled back manually in case of failure." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger that activates this configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Type name used by default" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_do +msgid "" +"Used to do something specific here.\n" +" Receives: operation, edi_action, vals, old_vals." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_pending +msgid "Waiting to be received" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_pending +msgid "Waiting to be sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "" +"When active, records of this type will be processed immediately without " +"waiting for the cron to pass by." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "When marked, EDI automatic processing will be avoided" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "" +"You can use this field to limit generating/processing exchanges for specific" +" partners. Use it directly or within models rules (domain or snippet)." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_error_on_send +msgid "error on send" +msgstr "" diff --git a/edi_core_oca/i18n/edi_oca.pot b/edi_core_oca/i18n/edi_oca.pot new file mode 100644 index 000000000..6679a73df --- /dev/null +++ b/edi_core_oca/i18n/edi_oca.pot @@ -0,0 +1,1519 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * edi_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__advanced_settings_edit +msgid "" +"\n" +" Advanced technical settings as YAML format.\n" +" The YAML structure should reproduce a dictionary.\n" +" The backend might use these settings for automated operations.\n" +"\n" +" Currently supported conf:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # set a value for component work context\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # set a value for the whole processing env\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In any case, you can use these settings\n" +" to provide your own configuration for whatever need you might have.\n" +" " +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_configuration_trigger +msgid "" +"\n" +" Describe what triggers a specific action for a configuration.\n" +" " +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_configuration +msgid "" +"\n" +" This model is used to configure EDI (Electronic Data Interchange) flows.\n" +" It allows users to create their own configurations, which can be tailored\n" +" to meet the specific needs of their business processes.\n" +" " +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_backend__output_sent_processed_auto +msgid "" +"\n" +" Automatically set the record as processed after sending.\n" +" Usecase: the web service you send the file to processes it on the fly.\n" +" " +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__kind +msgid "" +"\n" +"* Form button: show a button on the related model form\n" +" when conditions from domain and snippet are satisfied\n" +"\n" +"* Custom: let devs handle a custom behavior with specific developments\n" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_consumer_mixin_buttons +msgid " EDI actions" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "" +"\n" +" The related record is not available anymore.\n" +" Consider deleting this record too or fixing its relation.\n" +" " +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.message_edi_exchange_link +msgid "EDI exchange:" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.message_edi_exchange_link +msgid "Message:" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.message_edi_exchange_link +msgid "State:" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.message_edi_exchange_link +msgid "Type:" +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_exchange_related_record_related_record_unique +msgid "A record can only be related once to a specific exchange record." +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "ACK" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK exchange" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "ACK file is required for this exchange but not found." +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "ACK file received but contains errors." +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "ACK file received." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK generated for current exchange." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__ack_received_on +msgid "ACK received on" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_exchange_consumer_mixin +msgid "Abstract record where exchange records can be assigned" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__ack_expected +msgid "Ack Expected" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__ack_type_id +msgid "Ack exchange type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__ack_for_type_ids +msgid "Ack for exchange type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Action retry: state moved back to '%s'" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__active +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__active +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__active +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__active +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__active +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Active" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__advanced_settings +msgid "Advanced Settings" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__advanced_settings_edit +msgid "Advanced YAML settings" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_form +msgid "Advanced settings" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_all +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "All" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__allow_empty_files_on_receive +msgid "Allow Empty Files" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "An error happened while sending. Please check exchange record info." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__model_id +msgid "Apply to this model" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_trigger_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Archived" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "" +"Auto generate output for records missing their payload. If active, a cron " +"will take care of generating the output when not set yet. " +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__backend_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__backend_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__backend_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__backend_id +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Backend" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_search +msgid "Backend Type" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_configuration.py:0 +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Backend must match with exchange type's backend!" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_type.py:0 +msgid "Backend should respect backend type!" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__backend_type_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__backend_type_id +msgid "Backend type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_backend_type_uniq_code +msgid "Backend type code must be unique!" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_configuration.py:0 +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Backend type must match with exchange type's backend type!" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_backend_type +msgid "Backend types" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_backend +msgid "Backends" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_create_form_view +msgid "Cancel" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__backend_type_code +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__code +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__trigger +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__code +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__code +msgid "Code" +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_configuration_trigger_code_uniq +msgid "Code must be unique" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__company_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__company_id +msgid "Company" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_config +msgid "Config" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_configuration +msgid "Configurations" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_exchange_record_create_wiz +msgid "Create an Exchange Record" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Create date" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__create_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__create_uid +msgid "Created by" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Created last 7 days" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__create_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__create_date +msgid "Created on" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Created today" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type_rule__kind__custom +msgid "Custom" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "Decoding Error Handler" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_configuration__description +#: model:ir.model.fields,help:edi_oca.field_edi_configuration_trigger__description +msgid "Describe what the conf is for" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__description +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__description +msgid "Description" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__direction +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__direction +msgid "Direction" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "Disable auto" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__display_name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_form +msgid "Domain" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.view_partner_form +msgid "EDI" +msgstr "" + +#. module: edi_oca +#: model:res.groups,name:edi_oca.group_edi_advanced_settings_manager +msgid "EDI Advanced Settings Manager" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_backend_view +#: model:ir.model,name:edi_oca.model_edi_backend +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_search +msgid "EDI Backend" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_backend_type_view +#: model:ir.model,name:edi_oca.model_edi_backend_type +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_type_view_search +msgid "EDI Backend Type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__backend_type_id +msgid "EDI Backend type" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_form +msgid "EDI Config Before Do" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_form +msgid "EDI Config Do" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_configuration_view +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_form +msgid "EDI Configuration" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "EDI Exchange Record" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_type_view +#: model:ir.model,name:edi_oca.model_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "EDI Exchange Type" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_type_rule_view +msgid "EDI Exchange Type Rule" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_exchange_type_rule +msgid "EDI Exchange type rule" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_id_mixin__edi_id +msgid "EDI ID" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_id_mixin +msgid "EDI ID mixin" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_exchange_record +msgid "EDI exchange Record" +msgstr "" + +#. module: edi_oca +#: model:ir.model,name:edi_oca.model_edi_exchange_related_record +msgid "EDI exchange Related Record" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.server,name:edi_oca.cron_edi_backend_check_input_exchange_ir_actions_server +msgid "EDI exchange check input sync" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.server,name:edi_oca.cron_edi_backend_check_output_exchange_ir_actions_server +msgid "EDI exchange check output sync" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__origin_exchange_type_id +msgid "EDI origin exchange type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI origin record" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI record that originated this document." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__edi_config +msgid "Edi Config" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_search +msgid "Edi Configuration" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__edi_has_form_config +msgid "Edi Has Form Config" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Empty files are not allowed for exchange type %(name)s (%(code)s)" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__enable_domain +msgid "Enable on domain" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__enable_snippet +msgid "Enable on snippet" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__partner_ids +msgid "Enabled for partners" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__encoding +msgid "Encoding" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "Encoding Error Handler" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__encoding +msgid "" +"Encoding to be applied to generate/process the exchanged file.\n" +"Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Error" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__input_processed_error +msgid "Error on process" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__input_receive_error +msgid "Error on reception" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__validate_error +msgid "Error on validation" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchange_file +msgid "Exchange File" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "Exchange File Auto Generate" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__exchange_file_ext +msgid "Exchange File Ext" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchange_filechecksum +msgid "Exchange Filechecksum" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchange_filename +msgid "Exchange Filename" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "Exchange Filename Pattern" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "Exchange Filename Sequence" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__exchange_record_ids +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__exchange_record_id +msgid "Exchange Record" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__exchange_record_count +msgid "Exchange Record Count" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_consumer_mixin__exchange_related_record_ids +msgid "Exchange Related Record" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__type_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__exchange_type_id +msgid "Exchange Type" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange data generated" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Exchange date" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchange_error +msgid "Exchange error" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchange_error_traceback +msgid "Exchange error traceback" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange not received" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange not valid" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed successfully" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed with errors" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange received successfully" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d already has a file to process!" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d is not an outgoing record, cannot be generated" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "" +"Exchange record ID=%d is not in draft state and has already an output value." +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange sent" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__edi_exchange_state +msgid "Exchange state" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_exchange_record.py:0 +msgid "Exchange state must respect direction!" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__type_id +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +msgid "Exchange type" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_type_rule +msgid "Exchange type rules" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_form +msgid "Exchange types" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__exchanged_on +msgid "Exchanged On" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Exchanged last 7 days" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Exchanged today" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_view +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_root +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_backend_view_form +msgid "Exchanges" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_view_failed +msgid "Exchanges - failed" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_view_inbound +msgid "Exchanges - inbound" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_view_outbound +msgid "Exchanges - outbound" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_view_pending +msgid "Exchanges - pending" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__external_identifier +msgid "External Identifier" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_pending +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Failed" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__enable_domain +msgid "Filter domain to be checked on Models" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "" +"For output exchange types this should be a formatting string with the following variables available (to be used between brackets, `{}`): `exchange_record`, `record_name`, `type`, `dt` and `seq`. For instance, a valid string would be {record_name}-{type.code}-{dt}-{seq}\n" +"For more information:\n" +"- `exchange_record` means exchange record\n" +"- `record_name` means name of the exchange record\n" +"- `type` means code of the exchange record type\n" +"- `dt` means datetime\n" +"- `seq` means sequence. You need a sequence to be defined in `Exchange Filename Sequence` to use `seq`\n" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_form +msgid "Form" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type_rule__kind__form_btn +msgid "Form button" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Form button label" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Form button tooltip" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_create_form_view +msgid "Generate" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.edi_exchange_record_create_act_window +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_create_form_view +msgid "Generate Exchange Record" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Group By" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "" +"Handling of decoding errors on process (default is always 'Raise Error')." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "" +"Handling of encoding errors on generate (default is always 'Raise Error')." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__has_message +msgid "Has Message" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Help message visible as tooltip on button h-over" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__id +msgid "ID" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__identifier +msgid "Identifier" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__ack_type_id +msgid "" +"Identify the type of the ack. If this field is valued it means an hack is " +"expected." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "" +"If the `Exchange Filename Pattern` has `{seq}`, you should define a sequence" +" in this field to show the sequence in your filename" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_in_error_handler__ignore +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__ignore +msgid "Ignore" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_search +msgid "Inactive" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_inbound +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Inbound" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__direction__input +msgid "Input" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_id_mixin__edi_id +msgid "Internal or external identifier for records." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__job_channel_id +msgid "Job Channel" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__job_priority +msgid "Job Priority" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Jobs" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__kind +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +msgid "Kind" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__write_uid +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__write_date +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__write_date +msgid "Last Updated on" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_ids +msgid "Messages" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__model_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__model_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__model +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__model +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__model +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__model_id +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_search +msgid "Model" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__model +msgid "Model code" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_form +msgid "Model rules" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__model_name +msgid "Model tech name" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_configuration__model_id +#: model:ir.model.fields,help:edi_oca.field_edi_configuration_trigger__model_id +msgid "Model the conf applies to. Leave blank to apply for all models" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__name +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend_type__name +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__name +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration_trigger__name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__name +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__name +msgid "Name" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__new +msgid "New" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Open Record" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__parent_id +msgid "Original exchange which originated this record" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_outbound +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_view_search +msgid "Outbound" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__direction__output +msgid "Output" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_backend__output_sent_processed_auto +msgid "Output Sent Processed Auto" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__parent_id +msgid "Parent" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Parent exchange" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_configuration.py:0 +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_configuration_view_form +msgid "Partners" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_failed +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Pending" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__input_processed +msgid "Processed" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__quick_exec +msgid "Quick execution" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_in_error_handler__strict +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__strict +msgid "Raise Error" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__input_received +msgid "Received" +msgstr "" + +#. module: edi_oca +#: model:ir.ui.menu,name:edi_oca.menu_edi_exchange_record_recent +msgid "Recent" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.act_window,name:edi_oca.act_open_edi_exchange_record_recent_view +msgid "Recent exchanges" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__res_id +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_related_record__res_id +msgid "Record" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to process!" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to send!" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be processed" +msgstr "" + +#. module: edi_oca +#. odoo-python +#: code:addons/edi_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be sent!" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Regenerate" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__related_name +msgid "Related Name" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__related_queue_jobs_count +msgid "Related Queue Jobs Count" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__related_record_exists +msgid "Related Record Exists" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Related Records" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__related_exchange_ids +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Related exchanges" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Related record" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__related_record_ids +msgid "Related records" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_in_error_handler__surrogateescape +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__surrogateescape +msgid "Replace Byte with Individual Surrogate Code" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_in_error_handler__backslashreplace +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__backslashreplace +msgid "Replace with Backslashed Escape Sequences" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_in_error_handler__replace +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__replace +msgid "Replace with Replacement Marker" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_type__encoding_out_error_handler__xmlcharrefreplace +msgid "Replace with XML/HTML Numeric Character Reference" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record_create_wiz__res_id +msgid "Res" +msgstr "" + +#. module: edi_oca +#: model:ir.actions.server,name:edi_oca.action_retry_edi_exchange_record +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Retry" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__retryable +msgid "Retryable" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type__rule_ids +msgid "Rule" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__rule_ids +msgid "Rules to handle exchanges and UI automatically" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__output_sent +msgid "Sent" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_error +msgid "Sent and error" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_processed +msgid "Sent and processed" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__ack_received_on +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__exchanged_on +msgid "Sent or received on this date." +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Show all records created in the last 7 days" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Show all records created today" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_type_rule_view_form +msgid "Snippet" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__snippet_before_do +msgid "Snippet Before Do" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__snippet_do +msgid "Snippet Do" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__enable_snippet +msgid "" +"Snippet of code to be checked on Models,\n" +" You can use `record` and `exchange_type` here.\n" +" It will be executed if variable result has been defined as True\n" +" " +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_configuration__snippet_before_do +msgid "Snippet to validate the state and collect records to do" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "State" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_form +msgid "Status" +msgstr "" + +#. module: edi_oca +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Successful" +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_exchange_type_code_uniq +msgid "The code must be unique per backend" +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_exchange_record_external_identifier_uniq +msgid "The external_identifier must be unique for a type and a backend." +msgstr "" + +#. module: edi_oca +#: model:ir.model.constraint,message:edi_oca.constraint_edi_exchange_record_identifier_uniq +msgid "The identifier must be unique." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__retryable +msgid "The record state can be rolled back manually in case of failure." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_configuration__trigger_id +msgid "Trigger" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_configuration__trigger_id +msgid "Trigger that activates this configuration" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_type_rule__type_id +#: model_terms:ir.ui.view,arch_db:edi_oca.edi_exchange_record_view_search +msgid "Type" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Type name used by default" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_configuration__snippet_do +msgid "" +"Used to do something specific here.\n" +" Receives: operation, edi_action, vals, old_vals." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__input_pending +msgid "Waiting to be received" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__output_pending +msgid "Waiting to be sent" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,field_description:edi_oca.field_edi_exchange_record__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_record__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__quick_exec +msgid "" +"When active, records of this type will be processed immediately without " +"waiting for the cron to pass by." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "When marked, EDI automatic processing will be avoided" +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields,help:edi_oca.field_edi_exchange_type__partner_ids +msgid "" +"You can use this field to limit generating/processing exchanges for specific" +" partners. Use it directly or within models rules (domain or snippet)." +msgstr "" + +#. module: edi_oca +#: model:ir.model.fields.selection,name:edi_oca.selection__edi_exchange_record__edi_exchange_state__output_error_on_send +msgid "error on send" +msgstr "" diff --git a/edi_core_oca/i18n/es.po b/edi_core_oca/i18n/es.po new file mode 100644 index 000000000..aa87a158c --- /dev/null +++ b/edi_core_oca/i18n/es.po @@ -0,0 +1,1631 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * edi_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: Automatically generated\n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "" +"\n" +" Advanced technical settings as YAML format.\n" +" The YAML structure should reproduce a dictionary.\n" +" The backend might use these settings for automated operations.\n" +"\n" +" Currently supported conf:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # set a value for component work context\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # set a value for the whole processing env\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In any case, you can use these settings\n" +" to provide your own configuration for whatever need you might " +"have.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration_trigger +msgid "" +"\n" +" Describe what triggers a specific action for a configuration.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration +msgid "" +"\n" +" This model is used to configure EDI (Electronic Data Interchange) " +"flows.\n" +" It allows users to create their own configurations, which can be " +"tailored\n" +" to meet the specific needs of their business processes.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "" +"\n" +" Automatically set the record as processed after sending.\n" +" Usecase: the web service you send the file to processes it on the fly.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__kind +msgid "" +"\n" +"* Form button: show a button on the related model form\n" +" when conditions from domain and snippet are satisfied\n" +"\n" +"* Custom: let devs handle a custom behavior with specific developments\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_consumer_mixin_buttons +msgid " EDI actions" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "" +"\n" +" The related record is not available " +"anymore.\n" +" Consider deleting this record too or fixing " +"its relation.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "EDI exchange:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Message:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "State:" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Type:" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_related_record_related_record_unique +msgid "A record can only be related once to a specific exchange record." +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "ACK" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file is required for this exchange but not found." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received but contains errors." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK generated for current exchange." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_received_on +msgid "ACK received on" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_consumer_mixin +msgid "Abstract record where exchange records can be assigned" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_expected +msgid "Ack Expected" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "Ack exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_for_type_ids +msgid "Ack for exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "Action Needed" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Action retry: state moved back to '%s'" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__active +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Active" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings +msgid "Advanced Settings" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "Advanced YAML settings" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Advanced settings" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_all +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "All" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__allow_empty_files_on_receive +msgid "Allow Empty Files" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "An error happened while sending. Please check exchange record info." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__model_id +msgid "Apply to this model" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_trigger_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Archived" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_attachment_count +msgid "Attachment Count" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "" +"Auto generate output for records missing their payload. If active, a cron " +"will take care of generating the output when not set yet. " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Backend" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "Backend Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend must match with exchange type's backend!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_type.py:0 +msgid "Backend should respect backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_type_id +msgid "Backend type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_backend_type_uniq_code +msgid "Backend type code must be unique!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend type must match with exchange type's backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend_type +msgid "Backend types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend +msgid "Backends" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Cancel" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__check_model_id +msgid "Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__code +msgid "Code" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_configuration_trigger_code_uniq +msgid "Code must be unique" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__company_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__company_id +msgid "Company" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_config +msgid "Config" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_configuration +msgid "Configurations" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record_create_wiz +msgid "Create an Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Create date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_uid +msgid "Created by" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_date +msgid "Created on" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__custom +msgid "Custom" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "Decoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__description +msgid "Describe what the conf is for" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__description +msgid "Description" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__direction +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__direction +msgid "Direction" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "Disable auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__display_name +msgid "Display Name" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Domain" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.view_partner_form +msgid "EDI" +msgstr "" + +#. module: edi_core_oca +#: model:res.groups,name:edi_core_oca.group_edi_advanced_settings_manager +msgid "EDI Advanced Settings Manager" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_view +#: model:ir.model,name:edi_core_oca.model_edi_backend +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "EDI Backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_type_view +#: model:ir.model,name:edi_core_oca.model_edi_backend_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_search +msgid "EDI Backend Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_id +msgid "EDI Backend type" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Before Do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_configuration_view +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Configuration" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "EDI Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_view +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "EDI Exchange Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_rule_view +msgid "EDI Exchange Type Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type_rule +msgid "EDI Exchange type rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_id_mixin__edi_id +msgid "EDI ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_id_mixin +msgid "EDI ID mixin" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_check +msgid "EDI OCA Handler Check" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_generate +msgid "EDI OCA Handler Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_input_validate +msgid "EDI OCA Handler Input Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_output_validate +msgid "EDI OCA Handler Output Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_process +msgid "EDI OCA Handler Process" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_receive +msgid "EDI OCA Handler Receive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_send +msgid "EDI OCA Handler Send" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record +msgid "EDI exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_related_record +msgid "EDI exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_input_exchange_ir_actions_server +msgid "EDI exchange check input sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_output_exchange_ir_actions_server +msgid "EDI exchange check output sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_type_id +msgid "EDI origin exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI origin record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI record that originated this document." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_config +msgid "Edi Config" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Edi Configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_has_form_config +msgid "Edi Has Form Config" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Empty files are not allowed for exchange type %(name)s (%(code)s)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Enable on domain" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "Enable on snippet" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "Enabled for partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Encoding" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "Encoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding +msgid "" +"Encoding to be applied to generate/process the exchanged file.\n" +"Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed_error +msgid "Error on process" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_receive_error +msgid "Error on reception" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__validate_error +msgid "Error on validation" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_file +msgid "Exchange File" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "Exchange File Auto Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_ext +msgid "Exchange File Ext" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filechecksum +msgid "Exchange Filechecksum" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filename +msgid "Exchange Filename" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "Exchange Filename Pattern" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "Exchange Filename Sequence" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_ids +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__exchange_record_id +msgid "Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_count +msgid "Exchange Record Count" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_related_record_ids +msgid "Exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__exchange_type_id +msgid "Exchange Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange data generated" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchange date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error +msgid "Exchange error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error_traceback +msgid "Exchange error traceback" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not received" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not valid" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed with errors" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange received successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d already has a file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d is not an outgoing record, cannot be generated" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "" +"Exchange record ID=%d is not in draft state and has already an output value." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__edi_exchange_state +msgid "Exchange state" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange state must respect direction!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type_rule +msgid "Exchange type rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchange types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Exchanged On" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_root +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_failed +msgid "Exchanges - failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_inbound +msgid "Exchanges - inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_outbound +msgid "Exchanges - outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_pending +msgid "Exchanges - pending" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Execution handlers" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__external_identifier +msgid "External Identifier" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_pending +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Filter domain to be checked on Models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_follower_ids +msgid "Followers" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "" +"For output exchange types this should be a formatting string with the " +"following variables available (to be used between brackets, `{}`): " +"`exchange_record`, `record_name`, `type`, `dt` and `seq`. For instance, a " +"valid string would be {record_name}-{type.code}-{dt}-{seq}\n" +"For more information:\n" +"- `exchange_record` means exchange record\n" +"- `record_name` means name of the exchange record\n" +"- `type` means code of the exchange record type\n" +"- `dt` means datetime\n" +"- `seq` means sequence. You need a sequence to be defined in `Exchange " +"Filename Sequence` to use `seq`\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Form" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__form_btn +msgid "Form button" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Form button label" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Form button tooltip" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.edi_exchange_record_create_act_window +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__generate_model_id +msgid "Generator" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Group By" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "" +"Handling of decoding errors on process (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "" +"Handling of encoding errors on generate (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__has_message +msgid "Has Message" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Help message visible as tooltip on button h-over" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__id +msgid "ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__identifier +msgid "Identifier" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "" +"Identify the type of the ack. If this field is valued it means an hack is " +"expected." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "" +"If the `Exchange Filename Pattern` has `{seq}`, you should define a sequence " +"in this field to show the sequence in your filename" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__ignore +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__ignore +msgid "Ignore" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Inactive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_inbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__input +msgid "Input" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__input_validate_model_id +msgid "Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_id_mixin__edi_id +msgid "Internal or external identifier for records." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_checker +msgid "Is Edi Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_generator +msgid "Is Edi Generator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_input_validator +msgid "Is Edi Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_output_validator +msgid "Is Edi Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_processor +msgid "Is Edi Processor" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_receiver +msgid "Is Edi Receiver" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_sender +msgid "Is Edi Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_is_follower +msgid "Is Follower" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__kind +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Kind" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_date +msgid "Last Updated on" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "Message Delivery error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_ids +msgid "Messages" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Model" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model +msgid "Model code" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Model rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_name +msgid "Model tech name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__model_id +msgid "Model the conf applies to. Leave blank to apply for all models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_ir_model +msgid "Models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__name +msgid "Name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__new +msgid "New" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "No handler for %(action)s" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of Actions" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of errors" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Open Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Original exchange which originated this record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_outbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__output +msgid "Output" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "Output Sent Processed Auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__output_validate_model_id +msgid "Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Parent" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Parent exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "Partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_failed +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Pending" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed +msgid "Processed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__process_model_id +msgid "Processor" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "Quick execution" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__strict +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__strict +msgid "Raise Error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_received +msgid "Received" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__receive_model_id +msgid "Receiver" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_recent +msgid "Recent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_recent_view +msgid "Recent exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__res_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__res_id +msgid "Record" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to send!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be processed" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be sent!" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Regenerate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_name +msgid "Related Name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_exists +msgid "Related Record Exists" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related Records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_exchange_ids +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related exchanges" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_ids +msgid "Related records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__surrogateescape +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__surrogateescape +msgid "Replace Byte with Individual Surrogate Code" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__backslashreplace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__backslashreplace +msgid "Replace with Backslashed Escape Sequences" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__replace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__replace +msgid "Replace with Replacement Marker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__xmlcharrefreplace +msgid "Replace with XML/HTML Numeric Character Reference" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__res_id +msgid "Res" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.action_retry_edi_exchange_record +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Retry" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__retryable +msgid "Retryable" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rules to handle exchanges and UI automatically" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__send_model_id +msgid "Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent +msgid "Sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_error +msgid "Sent and error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_processed +msgid "Sent and processed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_received_on +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Sent or received on this date." +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created in the last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created today" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Snippet" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet Before Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_do +msgid "Snippet Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "" +"Snippet of code to be checked on Models,\n" +" You can use `record` and `exchange_type` here.\n" +" It will be executed if variable result has been defined as True\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet to validate the state and collect records to do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "State" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Status" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Successful" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_type_code_uniq +msgid "The code must be unique per backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_external_identifier_uniq +msgid "The external_identifier must be unique for a type and a backend." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_identifier_uniq +msgid "The identifier must be unique." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__retryable +msgid "The record state can be rolled back manually in case of failure." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger that activates this configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Type name used by default" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_do +msgid "" +"Used to do something specific here.\n" +" Receives: operation, edi_action, vals, old_vals." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_pending +msgid "Waiting to be received" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_pending +msgid "Waiting to be sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "" +"When active, records of this type will be processed immediately without " +"waiting for the cron to pass by." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "When marked, EDI automatic processing will be avoided" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "" +"You can use this field to limit generating/processing exchanges for specific " +"partners. Use it directly or within models rules (domain or snippet)." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_error_on_send +msgid "error on send" +msgstr "" diff --git a/edi_core_oca/i18n/fr.po b/edi_core_oca/i18n/fr.po new file mode 100644 index 000000000..a34b528b8 --- /dev/null +++ b/edi_core_oca/i18n/fr.po @@ -0,0 +1,1697 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * edi_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2021-06-17 15:48+0000\n" +"Last-Translator: Yves Le Doeuff \n" +"Language-Team: none\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "" +"\n" +" Advanced technical settings as YAML format.\n" +" The YAML structure should reproduce a dictionary.\n" +" The backend might use these settings for automated operations.\n" +"\n" +" Currently supported conf:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # set a value for component work context\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # set a value for the whole processing env\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In any case, you can use these settings\n" +" to provide your own configuration for whatever need you might " +"have.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration_trigger +msgid "" +"\n" +" Describe what triggers a specific action for a configuration.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration +msgid "" +"\n" +" This model is used to configure EDI (Electronic Data Interchange) " +"flows.\n" +" It allows users to create their own configurations, which can be " +"tailored\n" +" to meet the specific needs of their business processes.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "" +"\n" +" Automatically set the record as processed after sending.\n" +" Usecase: the web service you send the file to processes it on the fly.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__kind +msgid "" +"\n" +"* Form button: show a button on the related model form\n" +" when conditions from domain and snippet are satisfied\n" +"\n" +"* Custom: let devs handle a custom behavior with specific developments\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_consumer_mixin_buttons +msgid " EDI actions" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "" +"\n" +" The related record is not available " +"anymore.\n" +" Consider deleting this record too or fixing " +"its relation.\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "EDI exchange:" +msgstr "Échange EDI:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Message:" +msgstr "Message:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "State:" +msgstr "État:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Type:" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_related_record_related_record_unique +msgid "A record can only be related once to a specific exchange record." +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "ACK" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file is required for this exchange but not found." +msgstr "" +"Le fichier ACK est nécessaire pour cet échange mais n'a pas été trouvé." + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received but contains errors." +msgstr "Fichier ACK reçu mais contenant des erreurs." + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received." +msgstr "Fichier ACK reçu." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK generated for current exchange." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_received_on +msgid "ACK received on" +msgstr "ACK bien reçu" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_consumer_mixin +msgid "Abstract record where exchange records can be assigned" +msgstr "" +"Enregistrement abstrait où les enregistrements d'échange peuvent être " +"assignés" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_expected +msgid "Ack Expected" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "Ack exchange type" +msgstr "Type d'échange Ack" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_for_type_ids +msgid "Ack for exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "Action Needed" +msgstr "Action nécessaire" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Action retry: state moved back to '%s'" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__active +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Active" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings +msgid "Advanced Settings" +msgstr "Paramètres avancés" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "Advanced YAML settings" +msgstr "Paramètres YAML avancés" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Advanced settings" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_all +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "All" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__allow_empty_files_on_receive +msgid "Allow Empty Files" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "An error happened while sending. Please check exchange record info." +msgstr "" +"Une erreur s'est produite lors de l'envoi. Veuillez vérifier les " +"informations sur l'enregistrement de l'échange." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__model_id +msgid "Apply to this model" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_trigger_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Archived" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_attachment_count +msgid "Attachment Count" +msgstr "Nombre de pièces jointes" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "" +"Auto generate output for records missing their payload. If active, a cron " +"will take care of generating the output when not set yet. " +msgstr "" +"Génère automatiquement la sortie pour les enregistrements dont la charge " +"utile est manquante. Si cette option est active, un cron se chargera de " +"générer la sortie lorsqu'elle n'est pas encore définie. " + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Backend" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "Backend Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend must match with exchange type's backend!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_type.py:0 +msgid "Backend should respect backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_type_id +msgid "Backend type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_backend_type_uniq_code +msgid "Backend type code must be unique!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend type must match with exchange type's backend type!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend_type +msgid "Backend types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend +msgid "Backends" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Cancel" +msgstr "Annuler" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__check_model_id +msgid "Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__code +msgid "Code" +msgstr "Code" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_configuration_trigger_code_uniq +msgid "Code must be unique" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__company_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__company_id +msgid "Company" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_config +msgid "Config" +msgstr "Paramètres de configuration" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_configuration +msgid "Configurations" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record_create_wiz +msgid "Create an Exchange Record" +msgstr "Créer un enregistrement d'échange" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Create date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_uid +msgid "Created by" +msgstr "Créé par" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_date +msgid "Created on" +msgstr "Créé le" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__custom +msgid "Custom" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "Decoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__description +msgid "Describe what the conf is for" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__description +msgid "Description" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__direction +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__direction +msgid "Direction" +msgstr "Direction" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "Disable auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__display_name +msgid "Display Name" +msgstr "Afficher Nom" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Domain" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.view_partner_form +msgid "EDI" +msgstr "" + +#. module: edi_core_oca +#: model:res.groups,name:edi_core_oca.group_edi_advanced_settings_manager +msgid "EDI Advanced Settings Manager" +msgstr "Gestionnaire des paramètres avancés EDI" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_view +#: model:ir.model,name:edi_core_oca.model_edi_backend +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "EDI Backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_type_view +#: model:ir.model,name:edi_core_oca.model_edi_backend_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_search +msgid "EDI Backend Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_id +msgid "EDI Backend type" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Before Do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_configuration_view +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Configuration" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "EDI Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_view +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "EDI Exchange Type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_rule_view +msgid "EDI Exchange Type Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type_rule +msgid "EDI Exchange type rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_id_mixin__edi_id +msgid "EDI ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_id_mixin +msgid "EDI ID mixin" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_check +msgid "EDI OCA Handler Check" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_generate +msgid "EDI OCA Handler Generate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_input_validate +msgid "EDI OCA Handler Input Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_output_validate +msgid "EDI OCA Handler Output Validate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_process +msgid "EDI OCA Handler Process" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_receive +msgid "EDI OCA Handler Receive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_send +msgid "EDI OCA Handler Send" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record +msgid "EDI exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_related_record +msgid "EDI exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_input_exchange_ir_actions_server +msgid "EDI exchange check input sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_output_exchange_ir_actions_server +msgid "EDI exchange check output sync" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_type_id +msgid "EDI origin exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI origin record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI record that originated this document." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_config +msgid "Edi Config" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Edi Configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_has_form_config +msgid "Edi Has Form Config" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Empty files are not allowed for exchange type %(name)s (%(code)s)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Enable on domain" +msgstr "Activation sur le domaine" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "Enable on snippet" +msgstr "Activation sur l'extrait" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "Enabled for partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Encoding" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "Encoding Error Handler" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding +msgid "" +"Encoding to be applied to generate/process the exchanged file.\n" +"Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Error" +msgstr "Erreur" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed_error +msgid "Error on process" +msgstr "Erreur de traitement" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_receive_error +msgid "Error on reception" +msgstr "Erreur de réception" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__validate_error +msgid "Error on validation" +msgstr "Erreur lors de la validation" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_file +msgid "Exchange File" +msgstr "Fichier Exchange" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "Exchange File Auto Generate" +msgstr "Génération automatique de fichiers Exchange" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_ext +msgid "Exchange File Ext" +msgstr "Extension de fichier Exchange" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filechecksum +msgid "Exchange Filechecksum" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filename +msgid "Exchange Filename" +msgstr "Nom du fichier Exchange" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "Exchange Filename Pattern" +msgstr "Modèle de nom de fichier Exchange" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "Exchange Filename Sequence" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_ids +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__exchange_record_id +msgid "Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_count +msgid "Exchange Record Count" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_related_record_ids +msgid "Exchange Related Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__exchange_type_id +msgid "Exchange Type" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange data generated" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchange date" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error +msgid "Exchange error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error_traceback +msgid "Exchange error traceback" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not received" +msgstr "Échange non reçu" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not valid" +msgstr "Échange non valide" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed with errors" +msgstr "Échange traité avec des erreurs" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange received successfully" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d already has a file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d is not an outgoing record, cannot be generated" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "" +"Exchange record ID=%d is not in draft state and has already an output value." +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange sent" +msgstr "Échange envoyé" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__edi_exchange_state +msgid "Exchange state" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange state must respect direction!" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Exchange type" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type_rule +msgid "Exchange type rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchange types" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchanged_on +#, fuzzy +msgid "Exchanged On" +msgstr "Échange envoyé" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged today" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_root +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_failed +msgid "Exchanges - failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_inbound +msgid "Exchanges - inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_outbound +msgid "Exchanges - outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_pending +msgid "Exchanges - pending" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Execution handlers" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__external_identifier +msgid "External Identifier" +msgstr "Lien externe" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_pending +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Failed" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Filter domain to be checked on Models" +msgstr "Domaine de filtrage à vérifier sur les modèles" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_follower_ids +msgid "Followers" +msgstr "Abonnés" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_partner_ids +msgid "Followers (Partners)" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "" +"For output exchange types this should be a formatting string with the " +"following variables available (to be used between brackets, `{}`): " +"`exchange_record`, `record_name`, `type`, `dt` and `seq`. For instance, a " +"valid string would be {record_name}-{type.code}-{dt}-{seq}\n" +"For more information:\n" +"- `exchange_record` means exchange record\n" +"- `record_name` means name of the exchange record\n" +"- `type` means code of the exchange record type\n" +"- `dt` means datetime\n" +"- `seq` means sequence. You need a sequence to be defined in `Exchange " +"Filename Sequence` to use `seq`\n" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Form" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__form_btn +msgid "Form button" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Form button label" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Form button tooltip" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate" +msgstr "Générer" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.edi_exchange_record_create_act_window +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate Exchange Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__generate_model_id +#, fuzzy +msgid "Generator" +msgstr "Générer" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Group By" +msgstr "Grouper par" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "" +"Handling of decoding errors on process (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "" +"Handling of encoding errors on generate (default is always 'Raise Error')." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__has_message +#, fuzzy +msgid "Has Message" +msgstr "Messages" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Help message visible as tooltip on button h-over" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__id +msgid "ID" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__identifier +msgid "Identifier" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "" +"Identify the type of the ack. If this field is valued it means an hack is " +"expected." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "If checked, new messages require your attention." +msgstr "" +"Si cette case est cochée, de nouveaux messages requièrent votre attention." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "" +"If the `Exchange Filename Pattern` has `{seq}`, you should define a sequence " +"in this field to show the sequence in your filename" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__ignore +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__ignore +msgid "Ignore" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Inactive" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_inbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Inbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__input +msgid "Input" +msgstr "Entrée" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__input_validate_model_id +msgid "Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_id_mixin__edi_id +msgid "Internal or external identifier for records." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_checker +msgid "Is Edi Checker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_generator +msgid "Is Edi Generator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_input_validator +msgid "Is Edi Input Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_output_validator +msgid "Is Edi Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_processor +msgid "Is Edi Processor" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_receiver +#, fuzzy +msgid "Is Edi Receiver" +msgstr "Reçu" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_sender +msgid "Is Edi Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_is_follower +msgid "Is Follower" +msgstr "est un abonné" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__kind +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Kind" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_uid +msgid "Last Updated by" +msgstr "Dernière mise à jour par" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_date +msgid "Last Updated on" +msgstr "Dernière mise à jour le" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "Message Delivery error" +msgstr "Erreur de livraison du message" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_ids +msgid "Messages" +msgstr "Messages" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Model" +msgstr "Modèle" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model +msgid "Model code" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Model rules" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_name +msgid "Model tech name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__model_id +msgid "Model the conf applies to. Leave blank to apply for all models" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_ir_model +#, fuzzy +msgid "Models" +msgstr "Modèle" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__name +msgid "Name" +msgstr "Nom" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__new +msgid "New" +msgstr "Nouveau" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "No handler for %(action)s" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of Actions" +msgstr "Nombre d'actions" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of errors" +msgstr "Nombre d'erreurs" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Nombre de messages avec erreur de destinataire" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Open Record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Original exchange which originated this record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_outbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Outbound" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__output +msgid "Output" +msgstr "Sortie" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "Output Sent Processed Auto" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__output_validate_model_id +msgid "Output Validator" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Parent" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Parent exchange" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "Partners" +msgstr "" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_failed +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Pending" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed +msgid "Processed" +msgstr "Traité" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__process_model_id +#, fuzzy +msgid "Processor" +msgstr "Traité" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "Quick execution" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__strict +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__strict +msgid "Raise Error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_received +msgid "Received" +msgstr "Reçu" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__receive_model_id +#, fuzzy +msgid "Receiver" +msgstr "Reçu" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_recent +msgid "Recent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_recent_view +msgid "Recent exchanges" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__res_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__res_id +msgid "Record" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to process!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to send!" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be processed" +msgstr "" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be sent!" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Regenerate" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_name +msgid "Related Name" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_exists +msgid "Related Record Exists" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related Records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_exchange_ids +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related exchanges" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related record" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_ids +msgid "Related records" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__surrogateescape +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__surrogateescape +msgid "Replace Byte with Individual Surrogate Code" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__backslashreplace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__backslashreplace +msgid "Replace with Backslashed Escape Sequences" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__replace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__replace +msgid "Replace with Replacement Marker" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__xmlcharrefreplace +msgid "Replace with XML/HTML Numeric Character Reference" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__res_id +msgid "Res" +msgstr "" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.action_retry_edi_exchange_record +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Retry" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__retryable +msgid "Retryable" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rule" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rules to handle exchanges and UI automatically" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__send_model_id +msgid "Sender" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent +msgid "Sent" +msgstr "Envoyé" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_error +msgid "Sent and error" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_processed +msgid "Sent and processed" +msgstr "Envoyé et traité" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_received_on +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Sent or received on this date." +msgstr "Envoyé ou reçu à cette date." + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created in the last 7 days" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created today" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Snippet" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet Before Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_do +msgid "Snippet Do" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "" +"Snippet of code to be checked on Models,\n" +" You can use `record` and `exchange_type` here.\n" +" It will be executed if variable result has been defined as True\n" +" " +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet to validate the state and collect records to do" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "State" +msgstr "Etat" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Status" +msgstr "" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Successful" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_type_code_uniq +msgid "The code must be unique per backend" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_external_identifier_uniq +msgid "The external_identifier must be unique for a type and a backend." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_identifier_uniq +msgid "The identifier must be unique." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__retryable +msgid "The record state can be rolled back manually in case of failure." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger that activates this configuration" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Type" +msgstr "Type" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Type name used by default" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_do +msgid "" +"Used to do something specific here.\n" +" Receives: operation, edi_action, vals, old_vals." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_pending +msgid "Waiting to be received" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_pending +msgid "Waiting to be sent" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website Messages" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website communication history" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "" +"When active, records of this type will be processed immediately without " +"waiting for the cron to pass by." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "When marked, EDI automatic processing will be avoided" +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "" +"You can use this field to limit generating/processing exchanges for specific " +"partners. Use it directly or within models rules (domain or snippet)." +msgstr "" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_error_on_send +msgid "error on send" +msgstr "" + +#, python-format +#~ msgid "Exchange received successfully " +#~ msgstr "Échange traité avec succès " + +#~ msgid "Last Modified on" +#~ msgstr "Dernière modification le" + +#~ msgid "Main Attachment" +#~ msgstr "Pièce jointe principale" + +#, fuzzy +#~ msgid "Exchange Error" +#~ msgstr "Fichier Exchange" + +#~ msgid "Number of messages which requires an action" +#~ msgstr "Nombre de messages nécessitant une action" + +#~ msgid "Number of unread messages" +#~ msgstr "Nombre de messages non lus" + +#~ msgid "Unread Messages" +#~ msgstr "Messages non lus" + +#~ msgid "Unread Messages Counter" +#~ msgstr "Compteur de messages non lus" + +#~ msgid "" +#~ "The record has some pending EDIs to be " +#~ "generated" +#~ msgstr "" +#~ "L'enregistrement a des EDIs en attente d'être " +#~ "générés" + +#~ msgid "Expected Edi Configuration" +#~ msgstr "Configuration Edi attendue" + +#~ msgid "File %s processed successfully " +#~ msgstr "Échange traité avec succès " + +#~ msgid "Ack Exchange" +#~ msgstr "Ack Exchange" + +#~ msgid "Ack for this exchange" +#~ msgstr "Ack pour cet échange" + +#~ msgid "Configuration" +#~ msgstr "Configuration" diff --git a/edi_core_oca/i18n/it.po b/edi_core_oca/i18n/it.po new file mode 100644 index 000000000..7e82ff17d --- /dev/null +++ b/edi_core_oca/i18n/it.po @@ -0,0 +1,1777 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * edi_oca +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 17.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-10-07 09:43+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "" +"\n" +" Advanced technical settings as YAML format.\n" +" The YAML structure should reproduce a dictionary.\n" +" The backend might use these settings for automated operations.\n" +"\n" +" Currently supported conf:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # set a value for component work context\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # set a value for the whole processing env\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In any case, you can use these settings\n" +" to provide your own configuration for whatever need you might " +"have.\n" +" " +msgstr "" +"\n" +" Impostazioni tecniche avanzate in formato YAML.\n" +" La struttura YAML deve riprodurre un dizionario.\n" +" Il backend può usare queste impostazioni per operazioni " +"automatiche.\n" +"\n" +" Configurazione attualmente supportata:\n" +"\n" +" components:\n" +" generate:\n" +" usage: $comp_usage\n" +" # imposta un valore per il context di lavoro del " +"componente\n" +" work_ctx:\n" +" opt1: True\n" +" validate:\n" +" usage: $comp_usage\n" +" env_ctx:\n" +" # imposta un valore per l'intero ambiente di " +"elaborazione\n" +" opt2: False\n" +" check:\n" +" usage: $comp_usage\n" +" send:\n" +" usage: $comp_usage\n" +" receive:\n" +" usage: $comp_usage\n" +" process:\n" +" usage: $comp_usage\n" +"\n" +" filename_pattern:\n" +" force_tz: Europe/Rome\n" +" date_pattern: %Y-%m-%d-%H-%M-%S\n" +"\n" +" In ogni caso si possono utilizzare queste impostazioni\n" +" per fornire la propria configurazione per qualsiasi esigenza si " +"abbia.\n" +" " + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration_trigger +msgid "" +"\n" +" Describe what triggers a specific action for a configuration.\n" +" " +msgstr "" +"\n" +" Descrivere cosa attiva una azione specifica per una configurazione.\n" +" " + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_configuration +msgid "" +"\n" +" This model is used to configure EDI (Electronic Data Interchange) " +"flows.\n" +" It allows users to create their own configurations, which can be " +"tailored\n" +" to meet the specific needs of their business processes.\n" +" " +msgstr "" +"\n" +" Questo modello è utilizzato per configurare i flussi EDI (Electronic " +"Data Interchange).\n" +" Consente agli utenti di creare la propria configurazione, che può " +"essere adattata\n" +" per soddisfare esigenze specifiche dei loro processi aziendali.\n" +" " + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "" +"\n" +" Automatically set the record as processed after sending.\n" +" Usecase: the web service you send the file to processes it on the fly.\n" +" " +msgstr "" +"\n" +" Imposta automaticamente il record come elaborato dopo la spedizione.\n" +" Caso d'uso: il servizio web a cui è stato inviato il file lo ha " +"elaborato al volo.\n" +" " + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__kind +msgid "" +"\n" +"* Form button: show a button on the related model form\n" +" when conditions from domain and snippet are satisfied\n" +"\n" +"* Custom: let devs handle a custom behavior with specific developments\n" +msgstr "" +"\n" +"* Da pulsante: visualizza un pulsante nella maschera relativa al modello\n" +" quando sono soddisfatte le condizioni da dominio e snippet\n" +"\n" +"* Personalizzato: consente agli sviluppatori di gestire un comportamento " +"personalizzato con sviluppo personalizzato\n" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_consumer_mixin_buttons +msgid " EDI actions" +msgstr " Azioni EDI" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "" +"\n" +" The related record is not available " +"anymore.\n" +" Consider deleting this record too or fixing " +"its relation.\n" +" " +msgstr "" +"\n" +" Il record relativo non è più disponibile.\n" +" Considerare l'eliminazione anche di questo " +"record o correggere la sua relazione.\n" +" " + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "EDI exchange:" +msgstr "Scambio EDI:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Message:" +msgstr "Messaggio:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "State:" +msgstr "Stato:" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.message_edi_exchange_link +msgid "Type:" +msgstr "Tipo:" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_related_record_related_record_unique +msgid "A record can only be related once to a specific exchange record." +msgstr "" +"Un record può essere correlato solo una volta a uno specifico record di " +"scambio." + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "ACK" +msgstr "ACK" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK exchange" +msgstr "Scambio ACK" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file is required for this exchange but not found." +msgstr "Il file ACK è richiesto per questo scambio ma non è stato trovato." + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received but contains errors." +msgstr "Ricevuto il file ACK ma contiene errori." + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "ACK file received." +msgstr "File ACK ricevuto." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_exchange_id +msgid "ACK generated for current exchange." +msgstr "Generato ACK per lo scambio attuale." + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_received_on +msgid "ACK received on" +msgstr "Ricevuto ACK su" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_consumer_mixin +msgid "Abstract record where exchange records can be assigned" +msgstr "Record astratto dove i record di scambio possono essere assegnati" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__ack_expected +msgid "Ack Expected" +msgstr "Atteso ACK" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "Ack exchange type" +msgstr "Tipo scambio ACK" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__ack_for_type_ids +msgid "Ack for exchange type" +msgstr "ACK per tipo scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "Action Needed" +msgstr "Azione richiesta" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Action retry: state moved back to '%s'" +msgstr "Riemesso ACK: stato riportato a '%s'" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__active +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__active +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Active" +msgstr "Attivo" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings +msgid "Advanced Settings" +msgstr "Impostazioni avanzate" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__advanced_settings_edit +msgid "Advanced YAML settings" +msgstr "Impostazioni YAML avanzate" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Advanced settings" +msgstr "Impostazioni avanzate" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_all +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "All" +msgstr "Tutti" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__allow_empty_files_on_receive +msgid "Allow Empty Files" +msgstr "Consentire file vuoti" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "An error happened while sending. Please check exchange record info." +msgstr "" +"Si è verificato un errore nella spedizione. Verificare le informazione del " +"record di scambio." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__model_id +msgid "Apply to this model" +msgstr "Applica a questo modello" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_trigger_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Archived" +msgstr "In archivio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_attachment_count +msgid "Attachment Count" +msgstr "Conteggio allegati" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "" +"Auto generate output for records missing their payload. If active, a cron " +"will take care of generating the output when not set yet. " +msgstr "" +"Genera automaticamente l'output per i record che hanno perso il loro " +"contenuto. Se attiva, un cron gestirà la generazione dell'output quando non " +"ancora impostato. " + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Backend" +msgstr "Backend" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "Backend Type" +msgstr "Tipo backend" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend must match with exchange type's backend!" +msgstr "Il backend deve corrispondere al tipo backend dello scambio!" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_type.py:0 +msgid "Backend should respect backend type!" +msgstr "Il backend dovrebbe rispettare il tipo backend!" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__backend_type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__backend_type_id +msgid "Backend type" +msgstr "Tipo backend" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_backend_type_uniq_code +msgid "Backend type code must be unique!" +msgstr "Il codice tipo backend deve essere univoco!" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Backend type must match with exchange type's backend type!" +msgstr "Il tipo backend deve corrispondere al tipo backend del tipo scambio!" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend_type +msgid "Backend types" +msgstr "Tipi backend" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_backend +msgid "Backends" +msgstr "Backend" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Cancel" +msgstr "Annulla" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__check_model_id +msgid "Checker" +msgstr "Controllore" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__code +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__code +msgid "Code" +msgstr "Codice" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_configuration_trigger_code_uniq +msgid "Code must be unique" +msgstr "Il codice deve essere univoco" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__company_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__company_id +msgid "Company" +msgstr "Azienda" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_config +msgid "Config" +msgstr "Configurazione" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_configuration +msgid "Configurations" +msgstr "Configurazioni" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record_create_wiz +msgid "Create an Exchange Record" +msgstr "Crea un record di scambio" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Create date" +msgstr "Data creazione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created last 7 days" +msgstr "Creati negli ultimi 7 giorni" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__create_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Created today" +msgstr "Creato oggi" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__custom +msgid "Custom" +msgstr "Personalizzato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "Decoding Error Handler" +msgstr "Gestore errore decodifica" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__description +msgid "Describe what the conf is for" +msgstr "Descrivere a cosa serve la configurazione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__description +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__description +msgid "Description" +msgstr "Descrizione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__direction +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__direction +msgid "Direction" +msgstr "Direzione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "Disable auto" +msgstr "Disabilita automatico" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__display_name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Domain" +msgstr "Dominio" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.view_partner_form +msgid "EDI" +msgstr "EDI" + +#. module: edi_core_oca +#: model:res.groups,name:edi_core_oca.group_edi_advanced_settings_manager +msgid "EDI Advanced Settings Manager" +msgstr "Gestore impostazioni avanzate EDI" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_view +#: model:ir.model,name:edi_core_oca.model_edi_backend +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_search +msgid "EDI Backend" +msgstr "Backend EDI" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_backend_type_view +#: model:ir.model,name:edi_core_oca.model_edi_backend_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_type_view_search +msgid "EDI Backend Type" +msgstr "Tipo backend EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__backend_type_id +msgid "EDI Backend type" +msgstr "Tipo backend EDI" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Before Do" +msgstr "Configurazione EDI prima di eseguire" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Config Do" +msgstr "Configurazione EDI esecuzione" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_configuration_view +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "EDI Configuration" +msgstr "Configurazione EDI" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "EDI Exchange Record" +msgstr "Record di scambio EDI" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_view +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "EDI Exchange Type" +msgstr "Tipo scambio EDI" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_type_rule_view +msgid "EDI Exchange Type Rule" +msgstr "Regola tipo scambio EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_type_rule +msgid "EDI Exchange type rule" +msgstr "Regola tipo scambio EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_id_mixin__edi_id +msgid "EDI ID" +msgstr "ID EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_id_mixin +msgid "EDI ID mixin" +msgstr "Mixin ID EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_check +msgid "EDI OCA Handler Check" +msgstr "Controllo handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_generate +msgid "EDI OCA Handler Generate" +msgstr "Genera handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_input_validate +msgid "EDI OCA Handler Input Validate" +msgstr "Validazione ingresso handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_output_validate +msgid "EDI OCA Handler Output Validate" +msgstr "Validazione uscita handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_process +msgid "EDI OCA Handler Process" +msgstr "Processo handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_receive +msgid "EDI OCA Handler Receive" +msgstr "Ricezione handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_oca_handler_send +msgid "EDI OCA Handler Send" +msgstr "Invio handler OCA EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_record +msgid "EDI exchange Record" +msgstr "Record di scambio EDI" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_edi_exchange_related_record +msgid "EDI exchange Related Record" +msgstr "Record correlato allo scambio EDI" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_input_exchange_ir_actions_server +msgid "EDI exchange check input sync" +msgstr "Controllo sincro ingresso scambio EDI" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.cron_edi_backend_check_output_exchange_ir_actions_server +msgid "EDI exchange check output sync" +msgstr "Controllo sinc output scambio EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_type_id +msgid "EDI origin exchange type" +msgstr "Tipo scambio origine EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI origin record" +msgstr "Record origine EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__origin_exchange_record_id +msgid "EDI record that originated this document." +msgstr "Record EDI che ha generato questo documento." + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_config +msgid "Edi Config" +msgstr "Configurazione EDI" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Edi Configuration" +msgstr "Configurazione EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__edi_has_form_config +msgid "Edi Has Form Config" +msgstr "EDI ha una maschera di configurazione" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Empty files are not allowed for exchange type %(name)s (%(code)s)" +msgstr "" +"I file vuoti non sono consentiti per il tipo scambio %(name)s (%(code)s)" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Enable on domain" +msgstr "Abilita sul dominio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "Enable on snippet" +msgstr "Abilita sull'esempio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "Enabled for partners" +msgstr "Abilita per i partner" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Encoding" +msgstr "Codifica" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "Encoding Error Handler" +msgstr "Gestore errore codifica" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding +msgid "" +"Encoding to be applied to generate/process the exchanged file.\n" +"Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')" +msgstr "" +"Codifica da applicare per generare/elaborare il file scambiato.\n" +"Esempio: UTF-8, Windows-1252, ASCII...(il valore predefinito è sempre " +"'UTF-8')" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Error" +msgstr "Errore" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed_error +msgid "Error on process" +msgstr "Errore nel processo" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_receive_error +msgid "Error on reception" +msgstr "Errore alla ricezione" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__validate_error +msgid "Error on validation" +msgstr "Errore in validazione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_file +msgid "Exchange File" +msgstr "File scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_auto_generate +msgid "Exchange File Auto Generate" +msgstr "Auto generazione file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_file_ext +msgid "Exchange File Ext" +msgstr "Ext file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filechecksum +msgid "Exchange Filechecksum" +msgstr "Checksum file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_filename +msgid "Exchange Filename" +msgstr "Nome file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "Exchange Filename Pattern" +msgstr "Schema nome file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "Exchange Filename Sequence" +msgstr "Sequenza nome file scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_ids +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__exchange_record_id +msgid "Exchange Record" +msgstr "Record di scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_record_count +msgid "Exchange Record Count" +msgstr "Conteggio record di scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_consumer_mixin__exchange_related_record_ids +msgid "Exchange Related Record" +msgstr "Record relativo allo scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__type_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__exchange_type_id +msgid "Exchange Type" +msgstr "Tipo scambio" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange data generated" +msgstr "Dati scambio generati" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchange date" +msgstr "Data scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error +msgid "Exchange error" +msgstr "Errore scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchange_error_traceback +msgid "Exchange error traceback" +msgstr "Tracciamento errore scambio" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not received" +msgstr "Scambio non ricevuto" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange not valid" +msgstr "Scambio non valido" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed successfully" +msgstr "Scambio elaborato con successo" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange processed with errors" +msgstr "Scambio elaborato con errori" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange received successfully" +msgstr "Scambio ricevuto con successo" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d already has a file to process!" +msgstr "Il record di scambio ID=%d ha già un file da elaborare!" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Exchange record ID=%d is not an outgoing record, cannot be generated" +msgstr "" +"Il record di scambio ID=%d non è un record in uscita, non può essere generato" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "" +"Exchange record ID=%d is not in draft state and has already an output value." +msgstr "" +"Il record di scambio ID=%d non è in stato bozza e ha già un valore di output." + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange sent" +msgstr "Scambio inviato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__edi_exchange_state +msgid "Exchange state" +msgstr "Stato scambio" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_exchange_record.py:0 +msgid "Exchange state must respect direction!" +msgstr "Lo stato dello scambio deve rispettare la direzione!" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Exchange type" +msgstr "Tipo scambio" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type_rule +msgid "Exchange type rules" +msgstr "Regole tipo scambio" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_type +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchange types" +msgstr "Tipi scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Exchanged On" +msgstr "Scambiato in" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged last 7 days" +msgstr "Scambiati negli ultimi 7 giorni" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Exchanged today" +msgstr "Scambiato oggi" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_root +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_backend_view_form +msgid "Exchanges" +msgstr "Scambi" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_failed +msgid "Exchanges - failed" +msgstr "Scambi - falliti" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_inbound +msgid "Exchanges - inbound" +msgstr "Scambi - in arrivo" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_outbound +msgid "Exchanges - outbound" +msgstr "Scambi - in uscita" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_view_pending +msgid "Exchanges - pending" +msgstr "Scambi - in attesa" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Execution handlers" +msgstr "Esecuzione handler" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__external_identifier +msgid "External Identifier" +msgstr "Identificatore esterno" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_pending +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Failed" +msgstr "Fallito" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_domain +msgid "Filter domain to be checked on Models" +msgstr "Filtra dominio da controllare nei modelli" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_follower_ids +msgid "Followers" +msgstr "Seguito da" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_partner_ids +msgid "Followers (Partners)" +msgstr "Seguito da (partner)" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_pattern +msgid "" +"For output exchange types this should be a formatting string with the " +"following variables available (to be used between brackets, `{}`): " +"`exchange_record`, `record_name`, `type`, `dt` and `seq`. For instance, a " +"valid string would be {record_name}-{type.code}-{dt}-{seq}\n" +"For more information:\n" +"- `exchange_record` means exchange record\n" +"- `record_name` means name of the exchange record\n" +"- `type` means code of the exchange record type\n" +"- `dt` means datetime\n" +"- `seq` means sequence. You need a sequence to be defined in `Exchange " +"Filename Sequence` to use `seq`\n" +msgstr "" +"Per i tipi di scambio di output, questa dovrebbe essere una stringa di " +"formattazione con le seguenti variabili disponibili (da utilizzare tra " +"parentesi, `{}`): `exchange_record`, `record_name`, `type`, `dt` e `seq`. Ad " +"esempio, una stringa valida sarebbe {record_name}-{type.code}-{dt}-{seq}\n" +"Per maggiori informazioni:\n" +"- `exchange_record` significa record di scambio\n" +"- `record_name` significa nome del record di scambio\n" +"- `type` significa codice del tipo di record di scambio\n" +"- `dt` significa data ora\n" +"- `seq` significa sequenza. È necessario che una sequenza sia definita in " +"`Sequenza nome file scambio` per utilizzare `seq`\n" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Form" +msgstr "Maschera" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type_rule__kind__form_btn +msgid "Form button" +msgstr "Pulsante maschera" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Form button label" +msgstr "Etichetta pulsante maschera" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Form button tooltip" +msgstr "Descrizione comando pulsante maschera" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate" +msgstr "Genera" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.edi_exchange_record_create_act_window +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_create_form_view +msgid "Generate Exchange Record" +msgstr "Genera record di scambio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__generate_model_id +msgid "Generator" +msgstr "Generatore" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Group By" +msgstr "Raggruppa per" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_in_error_handler +msgid "" +"Handling of decoding errors on process (default is always 'Raise Error')." +msgstr "" +"Gestione degli errori di decodifica nel processo (l'impostazione predefinita " +"è sempre 'Segnala errore')." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__encoding_out_error_handler +msgid "" +"Handling of encoding errors on generate (default is always 'Raise Error')." +msgstr "" +"Gestione degli errori di codifica durante la generazione (il valore " +"predefinito è sempre 'Segnala errore')." + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__has_message +msgid "Has Message" +msgstr "Ha un messaggio" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_tooltip +msgid "Help message visible as tooltip on button h-over" +msgstr "" +"Messaggio di aiuto visibile come descrizione comando se cursore sul pulsante" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__id +msgid "ID" +msgstr "ID" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__identifier +msgid "Identifier" +msgstr "Identificativo" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__ack_type_id +msgid "" +"Identify the type of the ack. If this field is valued it means an hack is " +"expected." +msgstr "" +"Identifica il tipo di ACK. Se questo campo è valorizzato significa che ci si " +"aspetta un ACK." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction +msgid "If checked, new messages require your attention." +msgstr "Se selezionata, nuovi messaggi richiedono attenzione." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "If checked, some messages have a delivery error." +msgstr "Se selezionata, alcuni messaggi hanno un errore di consegna." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__exchange_filename_sequence_id +msgid "" +"If the `Exchange Filename Pattern` has `{seq}`, you should define a sequence " +"in this field to show the sequence in your filename" +msgstr "" +"Se il `Modello del nome file di scambio` ha `{seq}`, si dovrebbe definire " +"una sequenza in questo campo per mostrare la sequenza nel nome file" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__ignore +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__ignore +msgid "Ignore" +msgstr "Ignora" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_search +msgid "Inactive" +msgstr "Inattivo" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_inbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Inbound" +msgstr "In entrata" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__input +msgid "Input" +msgstr "Input" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__input_validate_model_id +msgid "Input Validator" +msgstr "Validatore ingresso" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_id_mixin__edi_id +msgid "Internal or external identifier for records." +msgstr "Identificatore interno o esterno per i record." + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_checker +msgid "Is Edi Checker" +msgstr "È un controllore EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_generator +msgid "Is Edi Generator" +msgstr "È un generatore EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_input_validator +msgid "Is Edi Input Validator" +msgstr "È un validatore ingresso EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_output_validator +msgid "Is Edi Output Validator" +msgstr "È un validatore uscita EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_processor +msgid "Is Edi Processor" +msgstr "È un elaboratore EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_receiver +msgid "Is Edi Receiver" +msgstr "È un ricevitore EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_ir_model__is_edi_sender +msgid "Is Edi Sender" +msgstr "È un mittente EDI" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_is_follower +msgid "Is Follower" +msgstr "Segue" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__kind +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Kind" +msgstr "Genere" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_uid +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__write_date +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error +msgid "Message Delivery error" +msgstr "Errore di consegna messaggio" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_ids +msgid "Messages" +msgstr "Messaggi" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__model_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__model +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_search +msgid "Model" +msgstr "Modello" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__model +msgid "Model code" +msgstr "Codice modello" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_form +msgid "Model rules" +msgstr "Regole modello" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__model_name +msgid "Model tech name" +msgstr "Nome tecnico modulo" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__model_id +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration_trigger__model_id +msgid "Model the conf applies to. Leave blank to apply for all models" +msgstr "" +"Modello a cui si applica la configurazione. Lasciare vuoto per applicare a " +"tutti i modelli" + +#. module: edi_core_oca +#: model:ir.model,name:edi_core_oca.model_ir_model +msgid "Models" +msgstr "Modelli" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration_trigger__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__name +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__name +msgid "Name" +msgstr "Nome" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__new +msgid "New" +msgstr "Nuovo" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "No handler for %(action)s" +msgstr "Nessun handler per %(action)s" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of Actions" +msgstr "Numero di azioni" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of errors" +msgstr "Numero di errori" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_needaction_counter +msgid "Number of messages requiring action" +msgstr "Numero di messaggi che richiedono un'azione" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__message_has_error_counter +msgid "Number of messages with delivery error" +msgstr "Numero di messaggi con errore di consegna" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Open Record" +msgstr "Apri record" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Original exchange which originated this record" +msgstr "Scambio origine che ha generato questo record" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_outbound +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_view_search +msgid "Outbound" +msgstr "In uscita" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__direction__output +msgid "Output" +msgstr "Output" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_backend__output_sent_processed_auto +msgid "Output Sent Processed Auto" +msgstr "Output ha inviato auto elaborato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__output_validate_model_id +msgid "Output Validator" +msgstr "Validatore uscita" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__parent_id +msgid "Parent" +msgstr "Padre" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Parent exchange" +msgstr "Scambio padre" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_configuration.py:0 +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_configuration_view_form +msgid "Partners" +msgstr "Partner" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_failed +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Pending" +msgstr "In attesa" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_processed +msgid "Processed" +msgstr "Processato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__process_model_id +msgid "Processor" +msgstr "Processore" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "Quick execution" +msgstr "Esecuzione rapida" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__strict +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__strict +msgid "Raise Error" +msgstr "Segnala errore" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_received +msgid "Received" +msgstr "Ricevuto" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__receive_model_id +msgid "Receiver" +msgstr "Ricevitore" + +#. module: edi_core_oca +#: model:ir.ui.menu,name:edi_core_oca.menu_edi_exchange_record_recent +msgid "Recent" +msgstr "Recente" + +#. module: edi_core_oca +#: model:ir.actions.act_window,name:edi_core_oca.act_open_edi_exchange_record_recent_view +msgid "Recent exchanges" +msgstr "Scambi recenti" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__res_id +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_related_record__res_id +msgid "Record" +msgstr "Record" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to process!" +msgstr "Il record ID=%d non ha file da elaborare!" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d has no file to send!" +msgstr "Il record ID=%d non ha file da inviare!" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be processed" +msgstr "Record ID=%d non è previsto che sia elaborato" + +#. module: edi_core_oca +#. odoo-python +#: code:addons/edi_core_oca/models/edi_backend.py:0 +msgid "Record ID=%d is not meant to be sent!" +msgstr "Record ID=%d non è previsto che sia inviato!" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Regenerate" +msgstr "Rigenera" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_name +msgid "Related Name" +msgstr "Nome collegato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_exists +msgid "Related Record Exists" +msgstr "Esistono record collegati" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related Records" +msgstr "Record collegati" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_exchange_ids +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related exchanges" +msgstr "Scambi collegati" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Related record" +msgstr "Record collegato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__related_record_ids +msgid "Related records" +msgstr "Record collegati" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__surrogateescape +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__surrogateescape +msgid "Replace Byte with Individual Surrogate Code" +msgstr "Sostituisci byte con codice surrogato individuale" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__backslashreplace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__backslashreplace +msgid "Replace with Backslashed Escape Sequences" +msgstr "Sostituisci con sequenze di escape con barra rovesciata" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_in_error_handler__replace +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__replace +msgid "Replace with Replacement Marker" +msgstr "Sostituire con il marcatore sostitutivo" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_type__encoding_out_error_handler__xmlcharrefreplace +msgid "Replace with XML/HTML Numeric Character Reference" +msgstr "Sostituisci con riferimento ai caratteri numerici XML/HTML" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record_create_wiz__res_id +msgid "Res" +msgstr "Res" + +#. module: edi_core_oca +#: model:ir.actions.server,name:edi_core_oca.action_retry_edi_exchange_record +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Retry" +msgstr "Riprova" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__retryable +msgid "Retryable" +msgstr "Si può ritentare" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rule" +msgstr "Regola" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__rule_ids +msgid "Rules to handle exchanges and UI automatically" +msgstr "Regole per gestire scambi e UI automaticamente" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type__send_model_id +msgid "Sender" +msgstr "Mittente" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent +msgid "Sent" +msgstr "Inviato" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_error +msgid "Sent and error" +msgstr "Inviato ed errore" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_sent_and_processed +msgid "Sent and processed" +msgstr "Inviato ed elaborato" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__ack_received_on +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__exchanged_on +msgid "Sent or received on this date." +msgstr "Inviato o ricevuto in questa data." + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created in the last 7 days" +msgstr "Visualizza tutti i record creati negli ultimi 7 giorni" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Show all records created today" +msgstr "Visualizza tutti i record creati oggi" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_type_rule_view_form +msgid "Snippet" +msgstr "Esempio codice" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet Before Do" +msgstr "Esempio prima di eseguire" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__snippet_do +msgid "Snippet Do" +msgstr "Esempio esecuzione" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__enable_snippet +msgid "" +"Snippet of code to be checked on Models,\n" +" You can use `record` and `exchange_type` here.\n" +" It will be executed if variable result has been defined as True\n" +" " +msgstr "" +"Esempio di codice da controllare nei modelli,\n" +" Si può usare `record` e `exchange_type` qui.\n" +" Verrà eseguito se la variabile risultato è stata definita come True\n" +" " + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_before_do +msgid "Snippet to validate the state and collect records to do" +msgstr "Esempio per validare lo stato e raccogliere i record da elaborare" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "State" +msgstr "Stato" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_form +msgid "Status" +msgstr "Stato" + +#. module: edi_core_oca +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Successful" +msgstr "Riuscito" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_type_code_uniq +msgid "The code must be unique per backend" +msgstr "Il codice deve essere univoco per backend" + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_external_identifier_uniq +msgid "The external_identifier must be unique for a type and a backend." +msgstr "L'external_identifier deve essere univoco per un tipo e un backend." + +#. module: edi_core_oca +#: model:ir.model.constraint,message:edi_core_oca.constraint_edi_exchange_record_identifier_uniq +msgid "The identifier must be unique." +msgstr "L'identificativo deve essere univoco." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__retryable +msgid "The record state can be rolled back manually in case of failure." +msgstr "" +"Lo stato del record può essere riportato indietro manualmente nel caso di " +"fallimento." + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger" +msgstr "Attivazione" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__trigger_id +msgid "Trigger that activates this configuration" +msgstr "Attivazione che attiva questa configurazione" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_type_rule__type_id +#: model_terms:ir.ui.view,arch_db:edi_core_oca.edi_exchange_record_view_search +msgid "Type" +msgstr "Tipo" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type_rule__form_btn_label +msgid "Type name used by default" +msgstr "Nome tipo utilizzato in modo predefinito" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_configuration__snippet_do +msgid "" +"Used to do something specific here.\n" +" Receives: operation, edi_action, vals, old_vals." +msgstr "" +"Utilizzata per eseguire qualcosa di specifico qui.\n" +" Ricezioni: operation, edi_action, vals, old_vals." + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__input_pending +msgid "Waiting to be received" +msgstr "In attesa di essere ricevuto" + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_pending +msgid "Waiting to be sent" +msgstr "In attesa di essere inviato" + +#. module: edi_core_oca +#: model:ir.model.fields,field_description:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website Messages" +msgstr "Messaggi sito web" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_record__website_message_ids +msgid "Website communication history" +msgstr "Cronologia comunicazioni sito web" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__quick_exec +msgid "" +"When active, records of this type will be processed immediately without " +"waiting for the cron to pass by." +msgstr "" +"Quando attiva, i record di questo tipo verranno elaborati immediatamente " +"senza attendere il cron." + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_consumer_mixin__edi_disable_auto +msgid "When marked, EDI automatic processing will be avoided" +msgstr "Quando selezionata, l'elaborazione EDI automatica verrà evitata" + +#. module: edi_core_oca +#: model:ir.model.fields,help:edi_core_oca.field_edi_exchange_type__partner_ids +msgid "" +"You can use this field to limit generating/processing exchanges for specific " +"partners. Use it directly or within models rules (domain or snippet)." +msgstr "" +"Si può usare questo campo per limitare la generazione/elaborazione degli " +"scambi per partner specifici. Utilizzarlo direttamente o all'interno delle " +"regole dei modelli (dominio o esempio codice)." + +#. module: edi_core_oca +#: model:ir.model.fields.selection,name:edi_core_oca.selection__edi_exchange_record__edi_exchange_state__output_error_on_send +msgid "error on send" +msgstr "errore nell'invio" + +#~ msgid "Job Channel" +#~ msgstr "Canale lavoro" + +#~ msgid "Job Priority" +#~ msgstr "Priorità lavoro" + +#~ msgid "Jobs" +#~ msgstr "Lavori" + +#~ msgid "Related Queue Jobs Count" +#~ msgstr "Conteggio lavori in coda relativo" + +#~ msgid " EDI actions" +#~ msgstr " Azioni EDI" + +#~ msgid "SMS Delivery error" +#~ msgstr "Errore consegna SMS" + +#~ msgid "Ratings" +#~ msgstr "Valutazioni" + +#~ msgid "Edi" +#~ msgstr "EDI" + +#, python-format +#~ msgid "Exchange processed successfully " +#~ msgstr "Scambio elaborato con successo " + +#, python-format +#~ msgid "Exchange received successfully " +#~ msgstr "Scambio ricevuto con successo " diff --git a/edi_core_oca/models/__init__.py b/edi_core_oca/models/__init__.py new file mode 100644 index 000000000..d2a513750 --- /dev/null +++ b/edi_core_oca/models/__init__.py @@ -0,0 +1,12 @@ +from . import edi_backend +from . import edi_backend_type +from . import edi_exchange_record +from . import edi_exchange_related_record +from . import edi_exchange_consumer_mixin +from . import edi_exchange_type +from . import edi_exchange_type_rule +from . import edi_id_mixin +from . import edi_configuration_trigger +from . import edi_configuration +from . import edi_oca_handler +from . import ir_model diff --git a/edi_core_oca/models/edi_backend.py b/edi_core_oca/models/edi_backend.py new file mode 100644 index 000000000..d4af65ff6 --- /dev/null +++ b/edi_core_oca/models/edi_backend.py @@ -0,0 +1,656 @@ +# Copyright 2020 ACSONE SA +# Copyright 2020 Dixmit +# Copyright 2021 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +import base64 +import logging +import traceback +from io import StringIO + +from odoo import exceptions, fields, models +from odoo.exceptions import UserError + +from ..exceptions import EDINotImplementedError, EDIValidationError + +_logger = logging.getLogger(__name__) + + +def _get_exception_msg(exc): + if hasattr(exc, "args") and isinstance(exc.args[0], str): + return exc.args[0] + return repr(exc) + + +def _get_exception_traceback(): + buff = StringIO() + traceback.print_exc(file=buff) + traceback_txt = buff.getvalue() + buff.close() + return traceback_txt + + +class EDIBackend(models.Model): + """Generic backend to control EDI exchanges. + + Backends can be organized with types. + + The backend should be responsible for managing records. + For each record it can generate or parse their values + depending on their direction (incoming, outgoing) + and send or receive them automatically depending on their state. + """ + + _name = "edi.backend" + _description = "EDI Backend" + + name = fields.Char(required=True) + backend_type_id = fields.Many2one( + string="EDI Backend type", + comodel_name="edi.backend.type", + required=True, + ondelete="restrict", + ) + backend_type_code = fields.Char(related="backend_type_id.code") + output_sent_processed_auto = fields.Boolean( + help=""" + Automatically set the record as processed after sending. + Usecase: the web service you send the file to processes it on the fly. + """ + ) + active = fields.Boolean(default=True) + company_id = fields.Many2one("res.company", string="Company") + + @property + def exchange_record_model(self): + return self.env["edi.exchange.record"] + + def create_record(self, type_code, values): + """Create an exchange record for current backend. + + :param type_code: edi.exchange.type code + :param values: edi.exchange.record values + :return: edi.exchange.record record + """ + self.ensure_one() + _values = self._create_record_prepare_values(type_code, values) + return self.exchange_record_model.create(_values) + + def _create_record_prepare_values(self, type_code, values): + res = values.copy() # do not pollute original dict + exchange_type = self.env["edi.exchange.type"].search( + self._get_exchange_type_domain(type_code), limit=1 + ) + assert exchange_type, f"Exchange type not found: {type_code}" + res["type_id"] = exchange_type.id + res["backend_id"] = self.id + return res + + def _get_exchange_type_domain(self, code): + return [ + ("code", "=", code), + "|", + ("backend_id", "=", self.id), + "&", + ("backend_type_id", "=", self.backend_type_id.id), + ("backend_id", "=", False), + ] + + def exchange_generate(self, exchange_record, store=True, force=False, **kw): + """Generate output content for given exchange record. + + :param exchange_record: edi.exchange.record recordset + :param store: store output on the record itself + :param force: allow to re-generate the content + :param kw: keyword args to be propagated to output generate handler + """ + self.ensure_one() + if force and exchange_record.exchange_file: + # Remove file to regenerate + exchange_record.exchange_file = False + self._check_exchange_generate(exchange_record, force=force) + output = self._exchange_generate(exchange_record, **kw) + message = None + encoding = exchange_record.type_id.encoding or "UTF-8" + encoding_error_handler = ( + exchange_record.type_id.encoding_out_error_handler or "strict" + ) + if output and store: + if not isinstance(output, bytes): + output = output.encode(encoding, errors=encoding_error_handler) + exchange_record.update( + { + "exchange_file": base64.b64encode(output), + "edi_exchange_state": "output_pending", + } + ) + if output: + message = exchange_record._exchange_status_message("generate_ok") + try: + self._validate_data(exchange_record, output) + except EDIValidationError as err: + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + state = "validate_error" + message = exchange_record._exchange_status_message("validate_ko") + exchange_record.update( + { + "edi_exchange_state": state, + "exchange_error": error, + "exchange_error_traceback": traceback, + } + ) + exchange_record.notify_action_complete("generate", message=message) + return message + + # TODO: unify to all other checkes that return something + def _check_exchange_generate(self, exchange_record, force=False): + exchange_record.ensure_one() + if ( + exchange_record.edi_exchange_state != "new" + and exchange_record.exchange_file + and not force + ): + raise exceptions.UserError( + self.env._( + "Exchange record ID=%d is not in draft state " + "and has already an output value.", + exchange_record.id, + ) + ) + if exchange_record.direction != "output": + raise exceptions.UserError( + self.env._( + "Exchange record ID=%d is not an outgoing record, " + "cannot be generated", + exchange_record.id, + ) + ) + if exchange_record.exchange_file: + raise exceptions.UserError( + self.env._( + "Exchange record ID=%d already has a file to process!", + exchange_record.id, + ) + ) + + def _exchange_generate(self, exchange_record, **kw): + exchange_function = self._get_exec_handler(exchange_record, "generate") + ctx = self._get_record_env_ctx(exchange_record, "generate") + return exchange_function(exchange_record.with_context(**ctx), **kw) + + # TODO: add tests + def _validate_data(self, exchange_record, value=None, **kw): + if exchange_record.direction == "input" and not exchange_record.exchange_file: + if not exchange_record.type_id.allow_empty_files_on_receive: + raise ValueError( + self.env._( + "Empty files are not allowed for exchange " + "type %(name)s (%(code)s)", + name=exchange_record.type_id.name, + code=exchange_record.type_id.code, + ) + ) + try: + return self._exchange_validate_data(exchange_record, value=value, **kw) + except EDINotImplementedError: # pylint: disable=W8138 + # We use this exception for inheritance purpose only. + # If it fails, we assume that no validation is implemented + pass + + def _exchange_validate_data(self, exchange_record, value=None, **kw): + action = f"{exchange_record.type_id.direction}_validate" + exchange_function = self._get_exec_handler(exchange_record, action) + ctx = self._get_record_env_ctx(exchange_record, action) + return exchange_function(exchange_record.with_context(**ctx), value=value, **kw) + + def exchange_send(self, exchange_record): + """Send exchange file.""" + self.ensure_one() + exchange_record.ensure_one() + # In case already sent: skip sending and check the state + check = self._output_check_send(exchange_record) + if not check: + return self._failed_output_check_send_msg() + state = exchange_record.edi_exchange_state + error = traceback = False + message = None + res = "" + try: + self._exchange_send(exchange_record) + _logger.debug("%s sent", exchange_record.identifier) + except self._send_retryable_exceptions() as err: + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + _logger.debug("%s send failed. To be retried.", exchange_record.identifier) + raise self._retryable_exception()( + error, **exchange_record._job_retry_params() + ) from err + except self._swallable_exceptions() as err: + if self.env.context.get("_edi_send_break_on_error"): + raise + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + state = "output_error_on_send" + message = exchange_record._exchange_status_message("send_ko") + res = f"Error: {error}" + _logger.debug( + "%s send failed. Marked as errored.", exchange_record.identifier + ) + else: + # TODO: maybe the send handler should return desired message and state + message = exchange_record._exchange_status_message("send_ok") + error = traceback = None + state = ( + "output_sent_and_processed" + if self.output_sent_processed_auto + else "output_sent" + ) + res = message + finally: + exchange_record.write( + { + "edi_exchange_state": state, + "exchange_error": error, + "exchange_error_traceback": traceback, + # FIXME: this should come from _compute_exchanged_on + # but somehow it's failing in send tests (in record tests it works). + "exchanged_on": fields.Datetime.now(), + } + ) + exchange_record.notify_action_complete("send", message=message) + return res + + def _swallable_exceptions(self): + # TODO: improve this list + return ( + ValueError, + FileNotFoundError, + exceptions.UserError, + exceptions.ValidationError, + ) + + def _send_retryable_exceptions(self): + # To be modified in edi_queue_oca + return () + + def _retryable_exception(self): + return UserError + + def _output_check_send(self, exchange_record): + if exchange_record.direction != "output": + raise exceptions.UserError( + self.env._("Record ID=%d is not meant to be sent!", exchange_record.id) + ) + if not exchange_record.exchange_file: + raise exceptions.UserError( + self.env._("Record ID=%d has no file to send!", exchange_record.id) + ) + return exchange_record.edi_exchange_state in [ + "output_pending", + "output_error_on_send", + ] + + def _exchange_send(self, exchange_record): + exchange_function = self._get_exec_handler(exchange_record, "send") + ctx = self._get_record_env_ctx(exchange_record, "send") + return exchange_function(exchange_record.with_context(**ctx)) + + 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): + """Generate and send output files for given records. + + If both are False, the record will be generated and sent right away + with chained jobs. + + If both `skip_generate` and `skip_send` are True, nothing will be done. + :param recordset: edi.exchange.record recordset + :param skip_generate: only send records + :param skip_send: only generate missing output + """ + for rec in recordset: + if not skip_generate and not skip_send: + rec.action_exchange_generate_send_chained() + elif skip_send: + rec.action_exchange_generate() + elif not skip_send: + rec.action_exchange_send() + + def _check_output_exchange_sync( + self, skip_send=False, skip_sent=True, record_ids=None + ): + """Lookup for pending output records and take care of them. + + First work on records that need output generation. + Then work on records waiting for a state update. + + :param skip_send: only generate missing output. + :param skip_sent: ignore records that were already sent. + """ + # Generate output files + new_records = self._get_new_output_exchange_records(record_ids=record_ids) + _logger.info( + "EDI Exchange output sync: found %d new records to process.", + len(new_records), + ) + if new_records: + self.exchange_generate_send(new_records, skip_send=skip_send) + + if skip_send: + return + pending_records = self.exchange_record_model.search( + self._output_pending_records_domain( + skip_sent=skip_sent, record_ids=record_ids + ) + ) + _logger.info( + "EDI Exchange output sync: found %d pending records to process.", + len(pending_records), + ) + for rec in pending_records: + if rec.edi_exchange_state == "output_pending": + rec.action_exchange_send() + else: + # TODO: run in job as well? + self._exchange_output_check_state(rec) + + def _get_new_output_exchange_records(self, record_ids=None): + return self.exchange_record_model.search( + self._output_new_records_domain(record_ids=record_ids) + ) + + def _output_new_records_domain(self, record_ids=None): + """Domain for output records needing output content generation.""" + domain = [ + ("backend_id", "=", self.id), + ("type_id.exchange_file_auto_generate", "=", True), + ("type_id.direction", "=", "output"), + ("edi_exchange_state", "=", "new"), + ("exchange_file", "=", False), + ] + if record_ids: + domain.append(("id", "in", record_ids)) + return domain + + def _output_pending_records_domain(self, skip_sent=True, record_ids=None): + """Domain for pending output records. + + Records might be waiting to be sent or have errors or have ack to handle.""" + states = ("output_pending", "output_sent_and_error") + if not skip_sent: + # If you want to update sent records + # you'll have to provide a `check` component. + states += ("output_sent",) + domain = [ + ("type_id.direction", "=", "output"), + ("backend_id", "=", self.id), + ("edi_exchange_state", "in", states), + ] + if record_ids: + domain.append(("id", "in", record_ids)) + return domain + + def _exchange_output_check_state(self, exchange_record): + exchange_function = self._get_exec_handler(exchange_record, "check") + ctx = self._get_record_env_ctx(exchange_record, "check") + return exchange_function(exchange_record.with_context(**ctx)) + + def _exchange_process_check(self, exchange_record): + if not exchange_record.direction == "input": + raise exceptions.UserError( + self.env._( + "Record ID=%d is not meant to be processed", exchange_record.id + ) + ) + if ( + not exchange_record.exchange_file + and not exchange_record.type_id.allow_empty_files_on_receive + ): + raise exceptions.UserError( + self.env._("Record ID=%d has no file to process!", exchange_record.id) + ) + return exchange_record.edi_exchange_state in [ + "input_received", + "input_processed_error", + ] + + def exchange_process(self, exchange_record): + """Process an incoming document.""" + self.ensure_one() + exchange_record.ensure_one() + # In case already processed: skip processing and check the state + check = self._exchange_process_check(exchange_record) + if not check: + return "Nothing to do. Likely already processed." + old_state = state = exchange_record.edi_exchange_state + error = traceback = False + message = None + try: + res = self._exchange_process(exchange_record) + except self._swallable_exceptions() as err: + if self.env.context.get("_edi_process_break_on_error"): + raise + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + state = "input_processed_error" + res = f"Error: {error}" + else: + error = traceback = None + state = "input_processed" + finally: + exchange_record.write( + { + "edi_exchange_state": state, + "exchange_error": error, + "exchange_error_traceback": traceback, + # FIXME: this should come from _compute_exchanged_on + # but somehow it's failing in send tests (in record tests it works). + "exchanged_on": fields.Datetime.now(), + } + ) + if ( + state == "input_processed_error" + and old_state != "input_processed_error" + ): + exchange_record._notify_error("process_ko") + elif state == "input_processed": + exchange_record._notify_done() + exchange_record.notify_action_complete("process", message=message) + return res + + def _exchange_process(self, exchange_record): + exchange_function = self._get_exec_handler(exchange_record, "process") + ctx = self._get_record_env_ctx(exchange_record, "process") + return exchange_function(exchange_record.with_context(**ctx)) + + def exchange_receive(self, exchange_record): + """Retrieve an incoming document.""" + self.ensure_one() + exchange_record.ensure_one() + # In case already processed: skip processing and check the state + check = self._exchange_receive_check(exchange_record) + if not check: + return "Nothing to do. Likely already received." + state = exchange_record.edi_exchange_state + error = traceback = False + message = None + content = None + try: + content = self._exchange_receive(exchange_record) + # Ignore result of FileNotFoundError/OSError + if content is not None: + exchange_record._set_file_content(content) + self._validate_data(exchange_record) + except EDIValidationError as err: + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + state = "validate_error" + message = exchange_record._exchange_status_message("validate_ko") + res = f"Validation error: {error}" + except self._swallable_exceptions() as err: + if self.env.context.get("_edi_receive_break_on_error"): + raise + traceback = _get_exception_traceback() + error = _get_exception_msg(err) + state = "input_receive_error" + message = exchange_record._exchange_status_message("receive_ko") + res = f"Input error: {error}" + else: + message = exchange_record._exchange_status_message("receive_ok") + error = traceback = None + state = "input_received" + res = message + finally: + exchange_record.write( + { + "edi_exchange_state": state, + "exchange_error": error, + "exchange_error_traceback": traceback, + # FIXME: this should come from _compute_exchanged_on + # but somehow it's failing in send tests (in record tests it works). + "exchanged_on": fields.Datetime.now(), + } + ) + exchange_record.notify_action_complete("receive", message=message) + return res + + def _exchange_receive_check(self, exchange_record): + # TODO: use `filtered_domain` + _input_pending_records_domain + # and raise one single error + # do the same for all the other check cases. + if not exchange_record.direction == "input": + raise exceptions.UserError( + self.env._( + "Record ID=%d is not meant to be processed", exchange_record.id + ) + ) + return exchange_record.edi_exchange_state in [ + "input_pending", + "input_receive_error", + ] + + def _exchange_receive(self, exchange_record): + exchange_function = self._get_exec_handler(exchange_record, "receive") + ctx = self._get_record_env_ctx(exchange_record, "receive") + return exchange_function(exchange_record.with_context(**ctx)) + + def _cron_check_input_exchange_sync(self, **kw): + for backend in self: + backend._check_input_exchange_sync(**kw) + + # TODO: add tests + # TODO: consider splitting cron in 2 (1 for receiving, 1 for processing) + def _check_input_exchange_sync(self, record_ids=None, **kw): + """Lookup for pending input records and take care of them. + + First work on records that need to receive input. + Then work on records waiting to be processed. + """ + pending_records = self.exchange_record_model.search( + self._input_pending_records_domain(record_ids=record_ids) + ) + _logger.info( + "EDI Exchange input sync: found %d pending records to receive.", + len(pending_records), + ) + for rec in pending_records: + rec.action_exchange_receive() + + pending_process_records = self.exchange_record_model.search( + self._input_pending_process_records_domain(record_ids=record_ids) + ) + _logger.info( + "EDI Exchange input sync: found %d pending records to process.", + len(pending_process_records), + ) + for rec in pending_process_records: + rec.action_exchange_process() + + def _input_pending_records_domain(self, record_ids=None): + domain = [ + ("backend_id", "=", self.id), + ("type_id.direction", "=", "input"), + ("edi_exchange_state", "=", "input_pending"), + ("exchange_file", "=", False), + ] + if record_ids: + domain.append(("id", "in", record_ids)) + return domain + + def _input_pending_process_records_domain(self, record_ids=None): + states = ("input_received",) + domain = [ + ("backend_id", "=", self.id), + ("type_id.direction", "=", "input"), + ("edi_exchange_state", "in", states), + ] + if record_ids: + domain.append(("id", "in", record_ids)) + return domain + + def _find_existing_exchange_records( + self, exchange_type, extra_domain=None, count_only=False + ): + domain = [ + ("backend_id", "=", self.id), + ("type_id", "=", exchange_type.id), + ] + extra_domain or [] + if count_only: + return self.env["edi.exchange.record"].search_count(domain) + else: + return self.env["edi.exchange.record"].search(domain) + + def action_view_exchanges(self): + xmlid = "edi_core_oca.act_open_edi_exchange_record_view" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["context"] = { + "search_default_backend_id": self.id, + "default_backend_id": self.id, + "default_backend_type_id": self.backend_type_id.id, + } + return action + + def action_view_exchange_types(self): + xmlid = "edi_core_oca.act_open_edi_exchange_type_view" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["context"] = { + "search_default_backend_id": self.id, + "default_backend_id": self.id, + "default_backend_type_id": self.backend_type_id.id, + } + return action + + def _is_valid_edi_action(self, action, raise_if_not=False): + try: + assert action in ("generate", "send", "process", "receive", "check") + return True + except AssertionError: + if raise_if_not: + raise + return False + + def _failed_output_check_send_msg(self): + return "Nothing to do. Likely already sent." + + def _get_exec_handler(self, exchange_record, action): + model = exchange_record.type_id[f"{action}_model_id"].sudo() + if model: + ctx = self._get_record_env_ctx(exchange_record, action) + return getattr(self.env[model.model].with_context(**ctx), action) + raise EDINotImplementedError( + self.env._("No handler for %(action)s", action=action) + ) + + def _get_conf_for_record(self, exchange_record, key): + """Get the configuration for a given record and key.""" + settings = exchange_record.type_id.get_settings() + return settings.get("execution_model", {}).get(key, {}) + + def _get_record_env_ctx(self, exchange_record, action): + # TODO: Maybe adding some keys on the configuration + return self._get_conf_for_record(exchange_record, action).get("env_ctx", {}) diff --git a/edi_core_oca/models/edi_backend_type.py b/edi_core_oca/models/edi_backend_type.py new file mode 100644 index 000000000..bc2a9ae3e --- /dev/null +++ b/edi_core_oca/models/edi_backend_type.py @@ -0,0 +1,40 @@ +# Copyright 2020 ACSONE SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import api, fields, models + +from ..utils import normalize_string + + +class EDIBackendType(models.Model): + """Define a kind of backend.""" + + _name = "edi.backend.type" + _description = "EDI Backend Type" + + name = fields.Char(required=True) + code = fields.Char( + required=True, + inverse="_inverse_code", + ) + + _uniq_code = models.Constraint("unique(code)", "Backend type code must be unique!") + + @api.onchange("name", "code") + def _onchange_code(self): + for rec in self: + rec.code = rec.code or rec.name + + def _inverse_code(self): + for rec in self: + # Make sure it's always normalized + rec.code = normalize_string(self, rec.code) + + def copy_data(self, default=None): + # OVERRIDE: ``code`` cannot be copied as it must me unique. + # Yet, we want to be able to duplicate a record from the UI. + self.ensure_one() + default = dict(default or {}) + default.setdefault("code", f"{self.code}_COPY_FIXME") + return super().copy_data(default=default) diff --git a/edi_core_oca/models/edi_configuration.py b/edi_core_oca/models/edi_configuration.py new file mode 100644 index 000000000..81780196b --- /dev/null +++ b/edi_core_oca/models/edi_configuration.py @@ -0,0 +1,221 @@ +# Copyright 2024 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import datetime + +import pytz +from psycopg2.extensions import AsIs + +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(required=True) + active = fields.Boolean(default=True) + description = fields.Char(help="Describe what the conf is for") + backend_id = fields.Many2one(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", + bypass_search_access=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, string="Model tech name" + ) + trigger_id = fields.Many2one( + comodel_name="edi.configuration.trigger", + help="Trigger that activates this configuration", + domain="['|', ('model_id', '=', model_id), ('model_id', '=', False)]", + ) + trigger = fields.Char(related="trigger_id.code") + snippet_before_do = fields.Text( + help="Snippet to validate the state and collect records to do", + ) + snippet_do = fields.Text( + 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( + self.env._("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( + self.env._( + "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 "", + } + + 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") + 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), + "conf": self, + "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, + "conf": self, + "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) + + def action_view_partners(self): + # TODO: add tests + partner_model = self.env["res.partner"] + partner_ids = set() + # Find partners linked to this conf no matter which field + query = "SELECT DISTINCT(partner_id) FROM %(table)s WHERE conf_id=%(conf_id)s" + for field in partner_model._fields.values(): + if field.type == "many2many" and field.comodel_name == self._name: + self.env.cr.execute( + query, {"table": AsIs(field.relation), "conf_id": self.id} + ) + partner_ids.update([r[0] for r in self.env.cr.fetchall()]) + return { + "type": "ir.actions.act_window", + "name": self.env._("Partners"), + "res_model": "res.partner", + "view_mode": "list,form", + "domain": [("id", "in", list(partner_ids))], + } diff --git a/edi_core_oca/models/edi_configuration_trigger.py b/edi_core_oca/models/edi_configuration_trigger.py new file mode 100644 index 000000000..92ee11318 --- /dev/null +++ b/edi_core_oca/models/edi_configuration_trigger.py @@ -0,0 +1,23 @@ +# Copyright 2024 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + + +class EdiConfigurationTrigger(models.Model): + _name = "edi.configuration.trigger" + _description = """ + Describe what triggers a specific action for a configuration. + """ + + name = fields.Char(required=True) + code = fields.Char(required=True, copy=False) + active = fields.Boolean(default=True) + description = fields.Char(help="Describe what the conf is for") + model_id = fields.Many2one( + "ir.model", + help="Model the conf applies to. Leave blank to apply for all models", + ) + + _code_uniq = models.Constraint("unique(code)", "Code must be unique") diff --git a/edi_core_oca/models/edi_exchange_consumer_mixin.py b/edi_core_oca/models/edi_exchange_consumer_mixin.py new file mode 100644 index 000000000..a7638ee38 --- /dev/null +++ b/edi_core_oca/models/edi_exchange_consumer_mixin.py @@ -0,0 +1,454 @@ +# Copyright 2020 ACSONE SA +# Copyright 2020 Dixmit +# Copyright 2022 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + + +from lxml import etree + +from odoo import api, fields, models +from odoo.tools import safe_eval +from odoo.tools.misc import frozendict + +from odoo.addons.base_sparse_field.models.fields import Serialized + + +class EDIExchangeConsumerMixin(models.AbstractModel): + """Record that might have related EDI Exchange records""" + + _name = "edi.exchange.consumer.mixin" + _description = "Abstract record where exchange records can be assigned" + + origin_exchange_record_id = fields.Many2one( + string="EDI origin record", + comodel_name="edi.exchange.record", + ondelete="set null", + help="EDI record that originated this document.", + copy=False, + ) + origin_exchange_type_id = fields.Many2one( + string="EDI origin exchange type", + comodel_name="edi.exchange.type", + ondelete="set null", + related="origin_exchange_record_id.type_id", + # Store it to ease searching by type + store=True, + copy=False, + ) + exchange_related_record_ids = fields.One2many( + "edi.exchange.related.record", + inverse_name="res_id", + domain=lambda r: [("model", "=", r._name)], + ) + exchange_record_ids = fields.One2many( + "edi.exchange.record", + compute="_compute_exchange_record_ids", + ) + exchange_record_count = fields.Integer(compute="_compute_exchange_record_count") + edi_config = Serialized( + compute="_compute_edi_config", + default={}, + ) + edi_has_form_config = fields.Boolean(compute="_compute_edi_config") + edi_disable_auto = fields.Boolean( + string="Disable auto", + help="When marked, EDI automatic processing will be avoided", + # Each extending module should override `states` as/if needed. + ) + + def _compute_edi_config(self): + for record in self: + config = record._edi_get_exchange_type_config() + record.edi_config = config + record.edi_has_form_config = any([x.get("form") for x in config.values()]) + + @api.depends("exchange_related_record_ids") + def _compute_exchange_record_ids(self): + for rec in self: + rec.exchange_record_ids = rec.exchange_related_record_ids.exchange_record_id + + def _edi_get_exchange_type_config(self): + # TODO: move this machinery to the rule model + rules = ( + self.env["edi.exchange.type.rule"] + .sudo() + .search([("model_id.model", "=", self._name)]) + ) + result = {} + for rule in rules: + exchange_type = rule.type_id + eval_ctx = dict( + self._get_eval_context(), record=self, exchange_type=exchange_type + ) + domain = safe_eval.safe_eval(rule.enable_domain or "[]", eval_ctx) + if not self.filtered_domain(domain): + continue + if rule.enable_snippet: + safe_eval.safe_eval(rule.enable_snippet, eval_ctx, mode="exec") + if not eval_ctx.get("result", False): + continue + + result[rule.id] = self._edi_get_exchange_type_rule_conf(rule) + return result + + @api.model + def _edi_get_exchange_type_rule_conf(self, rule): + conf = { + "form": {}, + "type": { + "id": rule.type_id.id, + "name": rule.type_id.name, + }, + } + if rule.kind == "form_btn": + label = rule.form_btn_label or rule.type_id.name + conf.update( + {"form": {"btn": {"label": label, "tooltip": rule.form_btn_tooltip}}} + ) + return conf + + def _get_eval_context(self): + """Prepare context to evalue python code snippet. + + :returns: dict -- evaluation context given to safe_eval + """ + return { + "datetime": safe_eval.datetime, + "dateutil": safe_eval.dateutil, + "time": safe_eval.time, + "uid": self.env.uid, + "user": self.env.user, + } + + @api.model + def get_view(self, view_id=None, view_type="form", **options): + res = super().get_view(view_id, view_type, **options) + if view_type == "form": + View = self.env["ir.ui.view"] + if view_id and res.get("base_model", self._name) != self._name: + View = View.with_context(base_model_name=res["base_model"]) + doc = etree.XML(res["arch"]) + all_models = res["models"].copy() + # We need to check if the new view modifies or adds new models + # Theoretically, it should not be modified, but it is wise to keep it + # Basically we will add new ones later + + # Select main `sheet` only as they can be nested into fields custom forms. + # I'm looking at you `account.view_move_line_form` on v16 :S + for node in doc.xpath("//sheet[not(ancestor::field)]"): + # TODO: add a default group + group = False + if hasattr(self, "_edi_generate_group"): + group = self._edi_generate_group + str_element = self.env["ir.qweb"]._render( + "edi_core_oca.edi_exchange_consumer_mixin_buttons", + {"group": group}, + ) + new_node = etree.fromstring(str_element) + new_arch, new_models = View.postprocess_and_fields(new_node, self._name) + for model in new_models: + all_models[model] = tuple( + set(all_models.get(model, {})) | set(new_models[model]) + ) + node.addprevious(etree.fromstring(new_arch)) + res["arch"] = etree.tostring(doc) + res["models"] = frozendict(all_models) + return res + + def _edi_create_exchange_record_vals(self, exchange_type): + return { + "model": self._name, + "res_id": self.id, + } + + def _edi_create_exchange_record(self, exchange_type, backend=None, vals=None): + backend = exchange_type.backend_id or backend + assert backend + vals = vals or {} + vals.update(self._edi_create_exchange_record_vals(exchange_type)) + return backend.create_record(exchange_type.code, vals) + + def edi_create_exchange_record(self, exchange_type_id): + self.ensure_one() + exchange_type = self.env["edi.exchange.type"].browse(exchange_type_id) + backend = exchange_type.backend_id + if ( + not backend + and self.env["edi.backend"].search_count( + [("backend_type_id", "=", exchange_type.backend_type_id.id)] + ) + == 1 + ): + backend = self.env["edi.backend"].search( + [("backend_type_id", "=", exchange_type.backend_type_id.id)] + ) + # FIXME: here you can still have more than one backend per type. + # We should always get to the wizard w/ pre-populated values. + # Maybe this behavior can be controlled by exc type adv param. + if backend: + exchange_record = self._edi_create_exchange_record(exchange_type, backend) + self._manual_notify_edi_generation(exchange_record) + return exchange_record.get_formview_action() + return self._edi_get_create_record_wiz_action(exchange_type_id) + + def _manual_notify_edi_generation(self, exchange_record): + # Hook for adding events later on + pass + + def _edi_get_create_record_wiz_action(self, exchange_type_id): + xmlid = "edi_core_oca.edi_exchange_record_create_act_window" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["context"] = { + "default_res_id": self.id, + "default_model": self._name, + "default_exchange_type_id": exchange_type_id, + } + return action + + def _has_exchange_record(self, exchange_type, backend=False, extra_domain=False): + """Check presence of related exchange record with a specific exchange type""" + return bool( + self.env["edi.exchange.record"].search_count( + self._has_exchange_record_domain( + exchange_type, backend=backend, extra_domain=extra_domain + ) + ) + ) + + def _has_exchange_record_domain( + self, exchange_type, backend=False, extra_domain=False + ): + if isinstance(exchange_type, str): + # Backward compat: allow passing the code when method is called directly + type_leaf = [("type_id.code", "=", exchange_type)] + else: + type_leaf = [("type_id", "=", exchange_type.id)] + domain = [ + ("model", "=", self._name), + ("res_id", "=", self.id), + ] + type_leaf + if backend is None: + backend = exchange_type.backend_id + if backend: + domain.append(("backend_id", "=", backend.id)) + if extra_domain: + domain += extra_domain + return domain + + def _get_exchange_record(self, exchange_type, backend=False, extra_domain=False): + """Get all related exchange records matching give exchange type.""" + return self.env["edi.exchange.record"].search( + self._has_exchange_record_domain( + exchange_type, backend=backend, extra_domain=extra_domain + ) + ) + + @api.depends("exchange_record_ids") + def _compute_exchange_record_count(self): + data = self.env["edi.exchange.related.record"]._read_group( + domain=[("res_id", "in", self.ids), ("model", "=", self._name)], + groupby=["res_id"], + aggregates=["__count"], + ) + mapped_data = {x[0]: x[1] for x in data} + for rec in self: + rec.exchange_record_count = mapped_data.get(rec.id, 0) + + def action_view_edi_records(self): + self.ensure_one() + xmlid = "edi_core_oca.act_open_edi_exchange_record_view" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["domain"] = [("id", "in", self.exchange_record_ids.ids)] + # Purge default search filters from ctx to avoid hiding records + ctx = action.get("context", {}) + if isinstance(ctx, str): + ctx = safe_eval.safe_eval(ctx, dict(self.env.context)) + action["context"] = { + k: v for k, v in ctx.items() if not k.startswith("search_default_") + } + # Drop ID otherwise the context will be loaded from the action's record :S + action.pop("id") + return action + + @api.model + def get_edi_access(self, doc_ids, operation, model_name=False): + """Retrieve access policy. + + The behavior is similar to `mail.thread` and `mail.message` + and it relies on the access rules defines on the related record. + The behavior can be customized on the related model + by defining `_edi_exchange_record_access`. + + By default `write`, otherwise the custom permission is returned. + """ + DocModel = self.env[model_name] if model_name else self + create_allow = getattr(DocModel, "_edi_exchange_record_access", "write") + if operation in ["write", "unlink"]: + check_operation = "write" + elif operation == "create" and create_allow in [ + "create", + "read", + "write", + "unlink", + ]: + check_operation = create_allow + elif operation == "create": + check_operation = "write" + else: + check_operation = operation + return check_operation + + def _edi_set_origin(self, exc_record): + self.sudo().update({"origin_exchange_record_id": exc_record.id}) + + def _edi_get_origin(self): + self.ensure_one() + return self.origin_exchange_record_id + + # TODO: full unit test coverage + def _edi_send_via_edi(self, exchange_type, backend=None, force=False, **kw): + """Simply sending out a record via EDI. + + If the exchange type requires an ack, it will be generated + if not already present. + """ + exchange_record = None + # If we are sending an ack, we must check if we can generate it + if exchange_type.ack_for_type_ids: + # TODO: shall we raise an error if the ack is not possible? + if self._edi_can_generate_ack(exchange_type): + __, exchange_record = self._edi_get_or_create_ack_record( + exchange_type, force=force + ) + else: + exchange_record = self._edi_create_exchange_record( + exchange_type, backend=backend + ) + # If quick exec is on, `exchange_generate_send` already ran + if exchange_record and not exchange_type.quick_exec: + exchange_record.action_exchange_generate_send(**kw) + + # TODO: full unit test coverage + def _edi_can_generate_ack(self, exchange_type, force=False): + """Have to generate ack for this exchange type? + + :param exchange_type: The exchange type to check. + + It should be generated if: + - automation is not disabled and not forced + - origin exchange record is set (means it was originated by another record) + - origin exchange type is compatible with the configured ack types + """ + if (self.edi_disable_auto and not force) or not self.origin_exchange_record_id: + return False + return self.origin_exchange_type_id in exchange_type.ack_for_type_ids + + # TODO: full unit test coverage + def _edi_get_or_create_ack_record(self, exchange_type, backend=None, force=False): + """ + Get or create a child record for the given exchange type. + + If the record has not been sent out yet for whatever reason + (job delayed, job failed, send failed, etc) + we still want to generate a new up to date record to be sent. + + :param exchange_type: The exchange type to create the record for. + :param force: If True, force creation of the record in case of ack type. + """ + if not self._edi_can_generate_ack(exchange_type, force=force): + return False, False + parent = self._edi_get_origin() + # Filter acks that are not valued yet. + exchange_record = self._get_exchange_record(exchange_type).filtered( + lambda x: not x.exchange_file + ) + created = False + # If the record has not been sent out yet for whatever reason + # (job delayed, job failed, send failed, etc) + # we still want to generate a new up to date record to be sent. + still_pending = exchange_record.edi_exchange_state in ( + "output_pending", + "output_error_on_send", + ) + if not exchange_record or still_pending: + vals = exchange_record._exchange_child_record_values() + vals["parent_id"] = parent.id + # NOTE: to fully automatize this, + # is recommended to enable `quick_exec` on the type + # otherwise records will have to wait for the cron to pass by. + exchange_record = self._edi_create_exchange_record( + exchange_type, backend=backend, vals=vals + ) + created = True + return created, exchange_record + + # TODO: full unit test coverage + def _edi_send_via_email( + self, ir_action, subtype_ref=None, partner_method=None, partners=None + ): + """Send EDI file via email using the provided action.""" + # FIXME: missing generation of the record and adding it as an attachment + # In this case, the record should be generated immediately + # and attached to the email. + # An alternative is to generate the record + # and have a component to send via email. + + # Retrieve context and composer model + ctx = ir_action.get("context", {}) + composer_model = self.env[ir_action["res_model"]].with_context(**ctx) + + # Determine subtype and partner_ids dynamically based on model-specific logic + subtype = subtype_ref and self.env.ref(subtype_ref) or None + if not subtype: + return False + + # THIS IS the part that should be delegated to a specific send component + # It could be also moved to its own module. + composer = composer_model.create({"subtype_id": subtype.id}) + composer.onchange_template_id_wrapper() + + # Retrieve partners based on provided method or fallback to parameter + if partner_method and hasattr(self, partner_method): + composer.partner_ids = getattr(self, partner_method)().ids + elif partners: + composer.partner_ids = partners.ids + else: + return False + + # Send the email + composer.send_mail() + return True + + def _edi_config_field_relation(self): + # Link to edi.config fields to be used + # We can use a child fields like "self.partner_id.edi_config_ids" + return self.env["edi.configuration"] + + def _execute_edi_config(self, trigger): + edi_confs = self._edi_config_field_relation() + if not edi_confs: + return + confs = edi_confs.edi_get_conf(trigger) + for conf in confs: + conf.edi_exec_snippet_do(self) + + @api.model_create_multi + def create(self, mvals): + records = super().create(mvals) + for record in records: + record._execute_edi_config("on_record_create") + return records + + def write(self, vals): + state_change = "state" in vals and "state" in self._fields + if state_change: + for record in self: + record._execute_edi_config(f"on_edi_{self._table}_before_state_change") + result = super().write(vals) + if state_change: + for record in self: + record._execute_edi_config(f"on_edi_{self._table}_state_change") + for record in self: + record._execute_edi_config("on_record_write") + return result diff --git a/edi_core_oca/models/edi_exchange_record.py b/edi_core_oca/models/edi_exchange_record.py new file mode 100644 index 000000000..fff530ffa --- /dev/null +++ b/edi_core_oca/models/edi_exchange_record.py @@ -0,0 +1,686 @@ +# Copyright 2020 ACSONE SA +# Copyright 2021 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 +import logging +from collections import defaultdict + +from odoo import Command, api, exceptions, fields, models +from odoo.exceptions import AccessError +from odoo.tools import Query, split_every + +from ..utils import get_checksum + +_logger = logging.getLogger(__name__) + + +class EDIExchangeRecord(models.Model): + """ + Define an exchange record. + """ + + _name = "edi.exchange.record" + _inherit = "mail.thread" + _description = "EDI exchange Record" + _order = "exchanged_on desc, id desc" + _rec_name = "identifier" + + identifier = fields.Char(required=True, index=True, readonly=True, copy=False) + external_identifier = fields.Char(index=True, readonly=True, copy=False) + type_id = fields.Many2one( + string="Exchange type", + comodel_name="edi.exchange.type", + required=True, + ondelete="cascade", + bypass_search_access=True, + index=True, + ) + direction = fields.Selection(related="type_id.direction") + backend_id = fields.Many2one(comodel_name="edi.backend", required=True) + model = fields.Char( + index=True, + required=False, + readonly=False, + compute="_compute_record", + inverse="_inverse_record", + store=True, + copy=False, + ) + res_id = fields.Many2oneReference( + string="Record", + index=True, + required=False, + readonly=False, + model_field="model", + copy=False, + compute="_compute_record", + inverse="_inverse_record", + store=True, + ) + related_record_exists = fields.Boolean(compute="_compute_related_record_exists") + related_name = fields.Char(compute="_compute_related_name", compute_sudo=True) + exchange_file = fields.Binary(attachment=True, copy=False) + exchange_filename = fields.Char( + compute="_compute_exchange_filename", readonly=False, store=True + ) + exchange_filechecksum = fields.Char( + compute="_compute_exchange_filechecksum", + store=True, + tracking=True, + readonly=True, + ) + exchanged_on = fields.Datetime( + help="Sent or received on this date.", + compute="_compute_exchanged_on", + store=True, + readonly=True, + ) + exchange_file_frozen = fields.Boolean( + help="Not allowed to change the file anymore.", + compute="_compute_exchange_file_frozen", + compute_sudo=True, + ) + edi_exchange_state = fields.Selection( + string="Exchange state", + readonly=True, + copy=False, + default="new", + index=True, + selection=[ + # Common states + ("new", "New"), + ("validate_error", "Error on validation"), + # output exchange states + ("output_pending", "Waiting to be sent"), + ("output_error_on_send", "error on send"), + ("output_sent", "Sent"), + ("output_sent_and_processed", "Sent and processed"), + ("output_sent_and_error", "Sent and error"), + # input exchange states + ("input_pending", "Waiting to be received"), + ("input_received", "Received"), + ("input_receive_error", "Error on reception"), + ("input_processed", "Processed"), + ("input_processed_error", "Error on process"), + ], + ) + exchange_error = fields.Text(string="Exchange error", readonly=True, copy=False) + exchange_error_traceback = fields.Text( + string="Exchange error traceback", readonly=True, copy=False + ) + # Relations w/ other records + parent_id = fields.Many2one( + comodel_name="edi.exchange.record", + help="Original exchange which originated this record", + ) + related_exchange_ids = fields.One2many( + string="Related exchanges", + comodel_name="edi.exchange.record", + inverse_name="parent_id", + ) + related_record_ids = fields.One2many( + string="Related records", + comodel_name="edi.exchange.related.record", + inverse_name="exchange_record_id", + ) + ack_expected = fields.Boolean(compute="_compute_ack_expected") + # TODO: shall we add a constrain on the direction? + # In theory if the record is outgoing the ack should be incoming and vice versa. + ack_exchange_id = fields.Many2one( + string="ACK exchange", + comodel_name="edi.exchange.record", + help="ACK generated for current exchange.", + compute="_compute_ack_exchange_id", + store=True, + ) + ack_received_on = fields.Datetime( + string="ACK received on", related="ack_exchange_id.exchanged_on" + ) + retryable = fields.Boolean( + compute="_compute_retryable", + help="The record state can be rolled back manually in case of failure.", + ) + company_id = fields.Many2one("res.company", string="Company") + + _identifier_uniq = models.Constraint( + "unique(identifier)", "The identifier must be unique." + ) + _external_identifier_uniq = models.Constraint( + "unique(external_identifier, backend_id, type_id)", + "The external_identifier must be unique for a type and a backend.", + ) + + @api.depends("model", "res_id") + def _compute_related_name(self): + for rec in self: + related_record = rec.record + rec.related_name = related_record.display_name if related_record else "" + + @api.depends("model", "type_id") + def _compute_exchange_filename(self): + for rec in self: + if not rec.type_id: + continue + if not rec.exchange_filename: + rec.exchange_filename = rec.type_id._make_exchange_filename(rec) + + @api.depends("exchange_file") + def _compute_exchange_filechecksum(self): + for rec in self: + content = rec.exchange_file or "" + if not isinstance(content, bytes): + content = content.encode() + rec.exchange_filechecksum = get_checksum(content) + + @api.depends("edi_exchange_state") + def _compute_exchanged_on(self): + for rec in self: + if rec.edi_exchange_state in ("input_received", "output_sent"): + rec.exchanged_on = fields.Datetime.now() + + @api.depends("exchange_file", "edi_exchange_state") + def _compute_exchange_file_frozen(self): + for rec in self: + rec.exchange_file_frozen = bool( + rec.exchange_file + and rec.edi_exchange_state + in ("input_processed", "output_sent", "output_sent_and_processed") + ) and not self.env.user.has_group( + "edi_core_oca.group_edi_override_exchange_file_content" + ) + + @api.constrains("edi_exchange_state") + def _constrain_edi_exchange_state(self): + for rec in self: + if rec.edi_exchange_state in ("new", "validate_error"): + continue + if not rec.edi_exchange_state.startswith(rec.direction): + raise exceptions.ValidationError( + self.env._("Exchange state must respect direction!") + ) + + @api.depends("related_exchange_ids.type_id") + def _compute_ack_exchange_id(self): + for rec in self: + rec.ack_exchange_id = rec._get_ack_record() + + def _get_ack_record(self): + if not self.type_id.ack_type_id: + return None + return self.related_exchange_ids.filtered( + lambda x: x.type_id == self.type_id.ack_type_id + ).sorted("id", reverse=True)[:1] + + def _compute_ack_expected(self): + for rec in self: + rec.ack_expected = bool(self.type_id.ack_type_id) + + def _get_edi_first_related_record_query(self): + query = """ + WITH related_records AS ( + SELECT + exchange_record_id, + res_id, + model, + ROW_NUMBER() OVER + (PARTITION BY exchange_record_id ORDER BY id) + AS row_num + FROM + edi_exchange_related_record + WHERE + exchange_record_id in %s + ) + SELECT + exchange_record_id, + res_id, + model + FROM + related_records + WHERE + row_num = 1; + """ + params = (tuple(self.ids),) + return query, params + + def _get_edi_first_related_record_dict(self): + """ + Returns a dictionary with exchange_record_id as key and + the first related record res_id and model as value. + """ + self.env["edi.exchange.related.record"].flush_model() + if not self.ids: + return {} + query, params = self._get_edi_first_related_record_query() + self.env.cr.execute(query, params) + return { + rec[0]: {"res_id": rec[1], "model": rec[2]} + for rec in self.env.cr.fetchall() + } + + @api.depends("related_record_ids") + def _compute_record(self): + """ + Main related record is computed as the first related record created + for the exchange. + """ + data = self._get_edi_first_related_record_dict() + for rec in self: + rec.res_id = data.get(rec.id, {}).get("res_id", False) + rec.model = data.get(rec.id, {}).get("model", False) + + def _inverse_record(self): + """ + Create related record when writing the main record for the exchange. + """ + for rec in self: + if ( + not rec.res_id + or not rec.model + or any( + rel.res_id == rec.res_id and rel.model == rec.model + for rel in rec.related_record_ids + ) + ): + continue + rec.related_record_ids = [ + Command.create({"res_id": rec.res_id, "model": rec.model}) + ] + + @api.depends("res_id", "model") + def _compute_related_record_exists(self): + for rec in self: + rec.related_record_exists = bool(rec.record) + + def needs_ack(self): + return self.type_id.ack_type_id and not self.ack_exchange_id + + _rollback_state_mapping = { + # From: to + "output_error_on_send": "output_pending", + "output_sent_and_error": "output_pending", + "input_receive_error": "input_pending", + "input_processed_error": "input_received", + } + + @api.depends("edi_exchange_state") + def _compute_retryable(self): + for rec in self: + rec.retryable = rec.edi_exchange_state in self._rollback_state_mapping + + @property + def record(self): + # In some case the res_model (and res_id) could be empty so we have to load + # data from parent + if not self.model and not self.parent_id: + return None + if not self.model and self.parent_id: + return self.parent_id.record + return self.env[self.model].browse(self.res_id).exists() + + def _set_file_content( + self, output_string, encoding="utf-8", field_name="exchange_file" + ): + """Handy method to no have to convert b64 back and forth.""" + self.ensure_one() + if not isinstance(output_string, bytes): + output_string = bytes(output_string, encoding) + self[field_name] = base64.b64encode(output_string) + + def _get_file_content( + self, field_name="exchange_file", binary=True, as_bytes=False + ): + """Handy method to not have to convert b64 back and forth.""" + self.ensure_one() + encoding = self.type_id.encoding or "UTF-8" + decoding_error_handler = self.type_id.encoding_in_error_handler or "strict" + if not self[field_name]: + return "" + if binary: + res = base64.b64decode(self[field_name]) + return ( + res.decode(encoding, errors=decoding_error_handler) + if not as_bytes + else res + ) + return self[field_name] + + @api.depends("identifier", "res_id", "model", "type_id") + def _compute_display_name(self): + for rec in self: + rec_name = ( + rec.record.display_name if rec.res_id and rec.model else rec.identifier + ) + rec.display_name = f"[{rec.type_id.name}] {rec_name}" + + @api.model_create_multi + def create(self, vals_list): + for vals in vals_list: + vals["identifier"] = self._get_identifier() + records = super().create(vals_list) + for rec in records: + if rec._quick_exec_enabled(): + rec._execute_next_action() + return records + + @api.model + def _get_identifier(self): + return self.env["ir.sequence"].next_by_code("edi.exchange") + + def _quick_exec_enabled(self): + if self.env.context.get("edi__skip_quick_exec"): + return False + return self.type_id.quick_exec and self.backend_id.active + + def _execute_next_action(self): + # The backend already knows how to handle records + # according to their direction and status. + # Let it decide. + if self.type_id.direction == "output": + self.backend_id._check_output_exchange_sync(record_ids=self.ids) + else: + self.backend_id._check_input_exchange_sync(record_ids=self.ids) + + @api.constrains("backend_id", "type_id") + def _constrain_backend(self): + for rec in self: + if rec.type_id.backend_id: + if rec.type_id.backend_id != rec.backend_id: + raise exceptions.ValidationError( + self.env._("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( + self.env._( + "Backend type must match with exchange type's backend type!" + ) + ) + + @property + def _exchange_status_messages(self): + return { + # status: message + "generate_ok": self.env._("Exchange data generated"), + "send_ok": self.env._("Exchange sent"), + "send_ko": self.env._( + "An error happened while sending. Please check exchange record info." + ), + "process_ok": self.env._("Exchange processed successfully"), + "process_ko": self.env._("Exchange processed with errors"), + "receive_ok": self.env._("Exchange received successfully"), + "receive_ko": self.env._("Exchange not received"), + "ack_received": self.env._("ACK file received."), + "ack_missing": self.env._( + "ACK file is required for this exchange but not found." + ), + "ack_received_error": self.env._("ACK file received but contains errors."), + "validate_ko": self.env._("Exchange not valid"), + } + + def _exchange_status_message(self, key): + return self._exchange_status_messages[key] + + def action_exchange_generate(self, **kw): + self.ensure_one() + return self.backend_id.exchange_generate(self, **kw) + + def action_exchange_send(self): + self.ensure_one() + return self.backend_id.exchange_send(self) + + def action_exchange_generate_send(self, **kw): + return self.backend_id.exchange_generate_send(self, **kw) + + def action_exchange_process(self): + self.ensure_one() + return self.backend_id.exchange_process(self) + + def action_exchange_receive(self): + self.ensure_one() + return self.backend_id.exchange_receive(self) + + def exchange_create_ack_record(self, **kw): + return self.exchange_create_child_record( + exc_type=self.type_id.ack_type_id, **kw + ) + + def exchange_create_child_record(self, exc_type=None, **kw): + exc_type = exc_type or self.type_id + values = self._exchange_child_record_values() + values.update(**kw) + return self.backend_id.create_record(exc_type.code, values) + + def _exchange_child_record_values(self): + return { + "parent_id": self.id, + "model": self.model, + "res_id": self.res_id, + } + + def action_retry(self): + for rec in self: + rec._retry_exchange_action() + + def _retry_exchange_action(self): + """Move back to precedent state to retry exchange action if failed.""" + if not self.retryable: + return False + new_state = self._rollback_state_mapping[self.edi_exchange_state] + fname = "edi_exchange_state" + self[fname] = new_state + display_state = self._fields[fname].convert_to_export(self[fname], self) + self.message_post( + body=self.env._("Action retry: state moved back to '%s'", display_state) + ) + if self._quick_exec_enabled(): + self._execute_next_action() + return True + + def action_regenerate(self): + for rec in self: + rec.action_exchange_generate(force=True) + + def action_open_related_record(self): + self.ensure_one() + if not self.related_record_exists: + return {} + return self.record.get_formview_action() + + def _set_related_record(self, odoo_record): + commands = [] + for rec in odoo_record: + commands.append(Command.create({"model": rec._name, "res_id": rec.id})) + self.sudo().update({"related_record_ids": commands}) + + def action_open_related_exchanges(self): + self.ensure_one() + if not self.related_exchange_ids: + return {} + xmlid = "edi_core_oca.act_open_edi_exchange_record_view" + action = self.env["ir.actions.act_window"]._for_xml_id(xmlid) + action["domain"] = [("id", "in", self.related_exchange_ids.ids)] + return action + + def notify_action_complete(self, action, message=None): + """Notify current record that an edi action has been completed. + + Implementers should take care of calling this method + if they work on records w/o calling edi_backend methods (eg: action_send). + + Implementers can hook to this method to do something after any action ends. + """ + if message: + self._notify_related_record(message) + + # Trigger generic action complete event on exchange record + event_name = f"{action}_complete" + self._trigger_edi_event(event_name) + if self.related_record_exists: + # Trigger specific event on related record + self._trigger_edi_event(event_name, target=self.record) + + def _notify_related_record(self, message, level="info"): + """Post notification on the original records.""" + for rec in self.related_record_ids: + rec._notify_related_record(message, level) + + def _trigger_edi_event(self, name, suffix=None, target=None, **kw): + """Hook to be implemented in other modules""" + pass + + def _notify_done(self): + self._notify_related_record(self._exchange_status_message("process_ok")) + self._trigger_edi_event("done") + + def _notify_error(self, message_key): + self._notify_related_record( + self._exchange_status_message(message_key), + level="error", + ) + self._trigger_edi_event("error") + + def _notify_ack_received(self): + self._notify_related_record(self._exchange_status_message("ack_received")) + self._trigger_edi_event("done", suffix="ack_received") + + def _notify_ack_missing(self): + self._notify_related_record( + self._exchange_status_message("ack_missing"), + level="warning", + ) + self._trigger_edi_event("done", suffix="ack_missing") + + def _notify_ack_received_error(self): + self._notify_related_record( + self._exchange_status_message("ack_received_error"), + ) + self._trigger_edi_event("done", suffix="ack_received_error") + + def _search(self, domain, offset=0, limit=None, order=None, **kw) -> Query: + query = super()._search( + domain=domain, + offset=offset, + limit=limit, + order=order, + ) + if self.env.is_superuser(): + # restrictions do not apply for the superuser + return query + + # TODO highlight orphaned EDI records in UI: + # - self.model + self.res_id are set + # - self.record returns empty recordset + # Remark: self.record is @property, not field + + if query.is_empty(): + return query + orig_ids = list(query) + ids = set(orig_ids) + result = [] + model_data = defaultdict(lambda: defaultdict(set)) + sub_query = """ + SELECT id, res_id, model + FROM %(table)s + WHERE id = ANY (%%(ids)s) + """ + for sub_ids in split_every(self.env.cr.IN_MAX, ids): + self.env.cr.execute( + sub_query % {"table": self._table}, + dict(ids=list(sub_ids)), + ) + for eid, res_id, model in self.env.cr.fetchall(): + if not model: + result.append(eid) + continue + model_data[model][res_id].add(eid) + + for model, targets in model_data.items(): + try: + self.env[model].check_access("read") + except AccessError: # no read access rights + continue + recs = self.env[model].browse(list(targets)) + missing = recs - recs.exists() + if missing: + for res_id in missing.ids: + _logger.warning( + "Deleted record %s,%s is referenced by edi.exchange.record %s", + model, + res_id, + list(targets[res_id]), + ) + recs = recs - missing + allowed = list( + self.env[model] + .with_context(active_test=False) + ._search([("id", "in", recs.ids)]) + ) + if self.env.is_system(): + # Group "Settings" can list exchanges where record is deleted + allowed.extend(missing.ids) + for target_id in allowed: + result += list(targets[target_id]) + if len(orig_ids) == limit and len(result) < len(orig_ids): + extend_query = self._search( + domain, + offset=offset + len(orig_ids), + limit=limit, + order=order, + ) + extend_ids = list(extend_query) + result.extend(extend_ids[: limit - len(result)]) + + # Restore original ordering + result = [x for x in orig_ids if x in result] + if set(orig_ids) != set(result): + # Create a virgin query + query = self.browse(result)._as_query() + return query + + def read(self, fields=None, load="_classic_read"): + """Override to explicitely call check_access_rule, that is not called + by the ORM. It instead directly fetches ir.rules and apply them.""" + self.check_access("read") + return super().read(fields=fields, load=load) + + def check_access(self, operation): + """In order to check if we can access a record, we are checking if we can access + the related document""" + super().check_access(operation) + if self.env.is_superuser(): + return + default_checker = self.env["edi.exchange.consumer.mixin"].get_edi_access + by_model_rec_ids = defaultdict(set) + by_model_checker = {} + for exc_rec in self.sudo(): + if not exc_rec.related_record_exists: + continue + model = exc_rec.model + res_id = exc_rec.res_id + if not model and exc_rec.parent_id: + model = exc_rec.parent_id.model + res_id = exc_rec.parent_id.res_id + by_model_rec_ids[model].add(res_id) + if model not in by_model_checker: + by_model_checker[model] = getattr( + self.env[model], "get_edi_access", default_checker + ) + + for model, rec_ids in by_model_rec_ids.items(): + records = self.env[model].browse(rec_ids).with_user(self.env.uid) + checker = by_model_checker[model] + for record in records: + check_operation = checker( + [record.id], operation, model_name=record._name + ) + record.check_access(check_operation) + + def write(self, vals): + self.check_access("write") + return super().write(vals) + + def action_exchange_generate_send_chained(self): + self.action_exchange_generate() + self.action_exchange_send() diff --git a/edi_core_oca/models/edi_exchange_related_record.py b/edi_core_oca/models/edi_exchange_related_record.py new file mode 100644 index 000000000..11f78747c --- /dev/null +++ b/edi_core_oca/models/edi_exchange_related_record.py @@ -0,0 +1,60 @@ +# Copyright 2023 ForgeFlow S.L. +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + + +class EDIExchangeRelatedRecord(models.Model): + """ + Model linking an Odoo record with an exchange record. + """ + + _name = "edi.exchange.related.record" + _description = "EDI exchange Related Record" + _order = "id" + + exchange_record_id = fields.Many2one( + comodel_name="edi.exchange.record", + required=True, + ondelete="cascade", + index=True, + ) + model = fields.Char(index=True, required=True, readonly=True) + res_id = fields.Many2oneReference( + string="Record", + index=True, + required=True, + readonly=True, + model_field="model", + copy=False, + ) + + _related_record_unique = models.Constraint( + "unique(exchange_record_id, model, res_id)", + "A record can only be related once to a specific exchange record.", + ) + + @property + def record(self): + return self.env[self.model].browse(self.res_id).exists() + + def _notify_related_record(self, message, level="info"): + """Post notification on the original record.""" + if not self.record or not hasattr(self.record, "message_post_with_source"): + return + self.record.message_post_with_source( + "edi_core_oca.message_edi_exchange_link", + render_values={ + "backend": self.exchange_record_id.backend_id, + "exchange_record": self.exchange_record_id, + "message": message, + "level": level, + }, + subtype_id=self.env.ref("mail.mt_note").id, + ) + + def action_open_related_record(self): + self.ensure_one() + if not self.record: + return {} + return self.record.get_formview_action() diff --git a/edi_core_oca/models/edi_exchange_type.py b/edi_core_oca/models/edi_exchange_type.py new file mode 100644 index 000000000..d6a2cab96 --- /dev/null +++ b/edi_core_oca/models/edi_exchange_type.py @@ -0,0 +1,327 @@ +# Copyright 2020 ACSONE SA +# Copyright 2021 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import logging +from datetime import datetime + +from pytz import timezone, utc + +from odoo import api, exceptions, fields, models +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT +from odoo.tools import groupby + +from odoo.addons.base_sparse_field.models.fields import Serialized + +_logger = logging.getLogger(__name__) + + +try: + import yaml +except ImportError: + _logger.debug("`yaml` lib is missing") + + +class EDIExchangeType(models.Model): + """ + Define a kind of exchange. + """ + + _name = "edi.exchange.type" + _description = "EDI Exchange Type" + + active = fields.Boolean(default=True, inverse="_inverse_active") + backend_id = fields.Many2one( + string="Backend", + comodel_name="edi.backend", + ondelete="set null", + ) + backend_type_id = fields.Many2one( + string="Backend type", + comodel_name="edi.backend.type", + required=True, + ondelete="restrict", + ) + name = fields.Char(required=True) + code = fields.Char(required=True, copy=False) + direction = fields.Selection( + selection=[("input", "Input"), ("output", "Output")], required=True + ) + exchange_filename_pattern = fields.Char( + default="{record_name}-{type.code}-{dt}", + help="For output exchange types this should be a formatting string " + "with the following variables available (to be used between " + "brackets, `{}`): `exchange_record`, `record_name`, `type`, " + "`dt` and `seq`. For instance, a valid string would be " + "{record_name}-{type.code}-{dt}-{seq}\n" + "For more information:\n" + "- `exchange_record` means exchange record\n" + "- `record_name` means name of the exchange record\n" + "- `type` means code of the exchange record type\n" + "- `dt` means datetime\n" + "- `seq` means sequence. You need a sequence to be defined in " + "`Exchange Filename Sequence` to use `seq`\n", + ) + # TODO make required if exchange_filename_pattern is + exchange_file_ext = fields.Char() + # TODO: this flag should be probably deprecated + # because when an exchange w/o file is pending + # there's no reason not to generate it. + # Also this could be controlled more generally w/ edi auto settings. + exchange_file_auto_generate = fields.Boolean( + help="Auto generate output for records missing their payload. " + "If active, a cron will take care of generating the output when not set yet. " + ) + ack_type_id = fields.Many2one( + string="Ack exchange type", + comodel_name="edi.exchange.type", + ondelete="set null", + help="Identify the type of the ack. " + "If this field is valued it means an hack is expected.", + ) + ack_for_type_ids = fields.Many2many( + string="Ack for exchange type", + comodel_name="edi.exchange.type", + compute="_compute_ack_for_type_ids", + ) + receive_model_id = fields.Many2one( + "ir.model", domain=[("is_edi_receiver", "=", True)], string="Receiver" + ) + process_model_id = fields.Many2one( + "ir.model", domain=[("is_edi_processor", "=", True)], string="Processor" + ) + check_model_id = fields.Many2one( + "ir.model", domain=[("is_edi_checker", "=", True)], string="Checker" + ) + generate_model_id = fields.Many2one( + "ir.model", domain=[("is_edi_generator", "=", True)], string="Generator" + ) + send_model_id = fields.Many2one( + "ir.model", domain=[("is_edi_sender", "=", True)], string="Sender" + ) + input_validate_model_id = fields.Many2one( + "ir.model", + domain=[("is_edi_input_validator", "=", True)], + string="Input Validator", + ) + output_validate_model_id = fields.Many2one( + "ir.model", + domain=[("is_edi_output_validator", "=", True)], + string="Output Validator", + ) + advanced_settings_edit = fields.Text( + string="Advanced YAML settings", + help=""" + Advanced technical settings as YAML format. + The YAML structure should reproduce a dictionary. + The backend might use these settings for automated operations. + + Currently supported conf: + + components: + generate: + usage: $comp_usage + # set a value for component work context + work_ctx: + opt1: True + validate: + usage: $comp_usage + env_ctx: + # set a value for the whole processing env + opt2: False + check: + usage: $comp_usage + send: + usage: $comp_usage + receive: + usage: $comp_usage + process: + usage: $comp_usage + + filename_pattern: + force_tz: Europe/Rome + date_pattern: %Y-%m-%d-%H-%M-%S + + In any case, you can use these settings + to provide your own configuration for whatever need you might have. + """, + ) + advanced_settings = Serialized(default={}, compute="_compute_advanced_settings") + rule_ids = fields.One2many( + comodel_name="edi.exchange.type.rule", + inverse_name="type_id", + help="Rules to handle exchanges and UI automatically", + ) + quick_exec = fields.Boolean( + string="Quick execution", + help="When active, records of this type will be processed immediately " + "without waiting for the cron to pass by.", + ) + partner_ids = fields.Many2many( + string="Enabled for partners", + comodel_name="res.partner", + help=( + "You can use this field to limit generating/processing exchanges " + "for specific partners. " + "Use it directly or within models rules (domain or snippet)." + ), + ) + exchange_filename_sequence_id = fields.Many2one( + "ir.sequence", + "Exchange Filename Sequence", + help="If the `Exchange Filename Pattern` has `{seq}`, " + "you should define a sequence in this field to show " + "the sequence in your filename", + ) + # https://docs.python.org/3/library/codecs.html#standard-encodings + encoding = fields.Char( + help="Encoding to be applied to generate/process the exchanged file.\n" + "Example: UTF-8, Windows-1252, ASCII...(default is always 'UTF-8')", + ) + # https://docs.python.org/3/library/codecs.html#codec-base-classes + encoding_out_error_handler = fields.Selection( + string="Encoding Error Handler", + selection=[ + ("strict", "Raise Error"), + ("ignore", "Ignore"), + ("replace", "Replace with Replacement Marker"), + ("backslashreplace", "Replace with Backslashed Escape Sequences"), + ("surrogateescape", "Replace Byte with Individual Surrogate Code"), + ("xmlcharrefreplace", "Replace with XML/HTML Numeric Character Reference"), + ], + help="Handling of encoding errors on generate " + "(default is always 'Raise Error').", + ) + # https://docs.python.org/3/library/codecs.html#codec-base-classes + encoding_in_error_handler = fields.Selection( + string="Decoding Error Handler", + selection=[ + ("strict", "Raise Error"), + ("ignore", "Ignore"), + ("replace", "Replace with Replacement Marker"), + ("backslashreplace", "Replace with Backslashed Escape Sequences"), + ("surrogateescape", "Replace Byte with Individual Surrogate Code"), + ], + help="Handling of decoding errors on process " + "(default is always 'Raise Error').", + ) + allow_empty_files_on_receive = fields.Boolean(string="Allow Empty Files") + + _code_uniq = models.Constraint( + "unique(code, backend_id)", "The code must be unique per backend" + ) + + def _inverse_active(self): + for rec in self: + # Disable rules if type gets disabled + if not rec.active: + rec.rule_ids.active = False + + @api.depends("advanced_settings_edit") + def _compute_advanced_settings(self): + for rec in self: + rec.advanced_settings = rec._load_advanced_settings() + + def _load_advanced_settings(self): + # TODO: validate settings w/ a schema. + # Could be done w/ Cerberus or JSON-schema. + # This would help documenting core and custom keys. + return yaml.safe_load(self.advanced_settings_edit or "") or {} + + def _compute_ack_for_type_ids(self): + ack_for = self.search([("ack_type_id", "in", self.ids)]) + by_type_id = dict(groupby(ack_for, lambda x: x.ack_type_id.id)) + for rec in self: + rec.ack_for_type_ids = [x.id for x in by_type_id.get(rec.id, [])] + + def get_settings(self): + return self.advanced_settings + + def set_settings(self, val): + self.advanced_settings_edit = val + + @api.constrains("backend_id", "backend_type_id") + def _check_backend(self): + for rec in self: + if not rec.backend_id: + continue + if rec.backend_id.backend_type_id != rec.backend_type_id: + raise exceptions.UserError( + self.env._("Backend should respect backend type!") + ) + + def _make_exchange_filename_datetime(self): + """ + Returns current datetime (now) using filename pattern + which can be set using advanced settings. + + Example: + filename_pattern: + force_tz: Europe/Rome + date_pattern: %Y-%m-%d-%H-%M-%S + """ + self.ensure_one() + pattern_settings = self.advanced_settings.get("filename_pattern", {}) + force_tz = pattern_settings.get("force_tz", self.env.user.tz) + date_pattern = pattern_settings.get("date_pattern", DATETIME_FORMAT) + tz = timezone(force_tz) if force_tz else None + now = datetime.now(utc).astimezone(tz) + return self.env["ir.http"]._slugify(now.strftime(date_pattern)) + + def _make_exchange_filename_sequence(self): + self.ensure_one() + return ( + self.exchange_filename_sequence_id.next_by_id() + if self.exchange_filename_sequence_id + else "" + ) + + def _make_exchange_filename(self, exchange_record): + """Generate filename.""" + pattern = self.exchange_filename_pattern + ext = self.exchange_file_ext + if ext: + pattern += ".{ext}" + dt = self._make_exchange_filename_datetime() + seq = self._make_exchange_filename_sequence() + record_name = self._get_record_name(exchange_record) + record = exchange_record + if exchange_record.model and exchange_record.res_id: + record = exchange_record.record + return pattern.format( + exchange_record=exchange_record, + record=record, + record_name=record_name, + type=self, + dt=dt, + seq=seq, + ext=ext, + ) + + def _get_record_name(self, exchange_record): + if not exchange_record.res_id or not exchange_record.model: + return self.env["ir.http"]._slugify(exchange_record.display_name or "") + if hasattr(exchange_record.record, "_get_edi_exchange_record_name"): + return exchange_record.record._get_edi_exchange_record_name(exchange_record) + return self.env["ir.http"]._slugify(exchange_record.record.display_name or "") + + def is_partner_enabled(self, partner): + """Check if given partner record is allowed for the current type. + + You can leverage this in your own logic to trigger or not + certain exchanges for specific partners. + + For instance: a customer might require an ORDRSP while another does not. + """ + exc_type = self.sudo() + if exc_type.partner_ids: + return partner.id in exc_type.partner_ids.ids + return True + + def copy_data(self, default=None): + # OVERRIDE: ``code`` cannot be copied as it must me unique. + # Yet, we want to be able to duplicate a record from the UI. + self.ensure_one() + default = dict(default or {}) + default.setdefault("code", f"{self.code}_COPY_FIXME") + return super().copy_data(default=default) diff --git a/edi_core_oca/models/edi_exchange_type_rule.py b/edi_core_oca/models/edi_exchange_type_rule.py new file mode 100644 index 000000000..4dfe9710b --- /dev/null +++ b/edi_core_oca/models/edi_exchange_type_rule.py @@ -0,0 +1,62 @@ +# Copyright 2023 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + +KIND_HELP = """ +* Form button: show a button on the related model form + when conditions from domain and snippet are satisfied + +* Custom: let devs handle a custom behavior with specific developments +""" + + +class EDIExchangeTypeRule(models.Model): + """ + Define rules for exchange types. + """ + + _name = "edi.exchange.type.rule" + _description = "EDI Exchange type rule" + + active = fields.Boolean(default=True) + name = fields.Char(required=True) + type_id = fields.Many2one( + comodel_name="edi.exchange.type", + required=True, + ondelete="cascade", + ) + model_id = fields.Many2one( + comodel_name="ir.model", + help="Apply to this model", + ondelete="cascade", + ) + model = fields.Char("Model code", related="model_id.model") # Tech field + enable_domain = fields.Char( + string="Enable on domain", help="Filter domain to be checked on Models" + ) + enable_snippet = fields.Char( + string="Enable on snippet", + help="""Snippet of code to be checked on Models, + You can use `record` and `exchange_type` here. + It will be executed if variable result has been defined as True + """, + ) + kind = fields.Selection( + selection=[ + ("form_btn", "Form button"), + ("custom", "Custom"), + ], + required=True, + default="form_btn", + help=KIND_HELP, + ) + form_btn_label = fields.Char( + string="Form button label", translate=True, help="Type name used by default" + ) + form_btn_tooltip = fields.Text( + string="Form button tooltip", + translate=True, + help="Help message visible as tooltip on button h-over", + ) diff --git a/edi_core_oca/models/edi_id_mixin.py b/edi_core_oca/models/edi_id_mixin.py new file mode 100644 index 000000000..87e039739 --- /dev/null +++ b/edi_core_oca/models/edi_id_mixin.py @@ -0,0 +1,16 @@ +# Copyright 2022 Camptocamp SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import fields, models + + +class EDIIdMixin(models.AbstractModel): + """Mixin to expose identifier's features""" + + _name = "edi.id.mixin" + _description = "EDI ID mixin" + + edi_id = fields.Char( + string="EDI ID", help="Internal or external identifier for records." + ) diff --git a/edi_core_oca/models/edi_oca_handler.py b/edi_core_oca/models/edi_oca_handler.py new file mode 100644 index 000000000..2406d5154 --- /dev/null +++ b/edi_core_oca/models/edi_oca_handler.py @@ -0,0 +1,61 @@ +# Copyright 2025 Dixmit +# @author Enric Tobella +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo import models + + +class EdiOcaHandlerGenerate(models.AbstractModel): + _name = "edi.oca.handler.generate" + _description = "EDI OCA Handler Generate" + + def generate(self, exchange_record): + pass + + +class EdiOcaHandlerInputValidate(models.AbstractModel): + _name = "edi.oca.handler.input.validate" + _description = "EDI OCA Handler Input Validate" + + def input_validate(self, exchange_record, **kw): + pass + + +class EdiOcaHandlerOutputValidate(models.AbstractModel): + _name = "edi.oca.handler.output.validate" + _description = "EDI OCA Handler Output Validate" + + def output_validate(self, exchange_record, **kw): + pass + + +class EdiOcaHandlerSend(models.AbstractModel): + _name = "edi.oca.handler.send" + _description = "EDI OCA Handler Send" + + def send(self, exchange_record): + pass + + +class EdiOcaHandlerReceive(models.AbstractModel): + _name = "edi.oca.handler.receive" + _description = "EDI OCA Handler Receive" + + def receive(self, exchange_record): + pass + + +class EdiOcaHandlerProcess(models.AbstractModel): + _name = "edi.oca.handler.process" + _description = "EDI OCA Handler Process" + + def process(self, exchange_record): + pass + + +class EdiOcaHandlerCheck(models.AbstractModel): + _name = "edi.oca.handler.check" + _description = "EDI OCA Handler Check" + + def check(self, exchange_record): + pass diff --git a/edi_core_oca/models/ir_model.py b/edi_core_oca/models/ir_model.py new file mode 100644 index 000000000..20d5fe359 --- /dev/null +++ b/edi_core_oca/models/ir_model.py @@ -0,0 +1,60 @@ +# Copyright 2025 Dixmit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrModel(models.Model): + _inherit = "ir.model" + + is_edi_generator = fields.Boolean() + is_edi_input_validator = fields.Boolean() + is_edi_output_validator = fields.Boolean() + is_edi_sender = fields.Boolean() + is_edi_receiver = fields.Boolean() + is_edi_processor = fields.Boolean() + is_edi_checker = fields.Boolean() + + def _reflect_model_params(self, model): + """ + Add flags to identify EDI handler models. + We will use these flags in domains to select models for specific + EDI handler roles. + We will use several flags instead of a single selection field to + allow models to have multiple roles. + 1. is_edi_generator: Model can generate EDI documents. + 2. is_edi_input_validator: Model can validate incoming EDI documents. + 3. is_edi_output_validator: Model can validate outgoing EDI documents. + 4. is_edi_sender: Model can send EDI documents to external systems. + 5. is_edi_receiver: Model can receive EDI documents from external systems. + 6. is_edi_processor: Model can process EDI documents + 7. is_edi_checker: Model can check the status of EDI documents. + """ + vals = super()._reflect_model_params(model) + vals["is_edi_generator"] = ( + model._name != "edi.oca.handler.generate" + and isinstance(model, self.pool["edi.oca.handler.generate"]) + ) + vals["is_edi_input_validator"] = ( + model._name != "edi.oca.handler.input.validate" + and isinstance(model, self.pool["edi.oca.handler.input.validate"]) + ) + vals["is_edi_output_validator"] = ( + model._name != "edi.oca.handler.output.validate" + and isinstance(model, self.pool["edi.oca.handler.output.validate"]) + ) + vals["is_edi_sender"] = model._name != "edi.oca.handler.send" and isinstance( + model, self.pool["edi.oca.handler.send"] + ) + vals["is_edi_receiver"] = ( + model._name != "edi.oca.handler.receive" + and isinstance(model, self.pool["edi.oca.handler.receive"]) + ) + vals["is_edi_processor"] = ( + model._name != "edi.oca.handler.process" + and isinstance(model, self.pool["edi.oca.handler.process"]) + ) + vals["is_edi_checker"] = model._name != "edi.oca.handler.check" and isinstance( + model, self.pool["edi.oca.handler.check"] + ) + return vals diff --git a/edi_core_oca/pyproject.toml b/edi_core_oca/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/edi_core_oca/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/edi_core_oca/readme/CONFIGURE.md b/edi_core_oca/readme/CONFIGURE.md new file mode 100644 index 000000000..48045deba --- /dev/null +++ b/edi_core_oca/readme/CONFIGURE.md @@ -0,0 +1,65 @@ +This module aims to provide an infrastructure to simplify +interchangability of documents between systems providing a configuration +platform. It will be inherited by other modules in order to define the +proper implementations of components. + +In order to define a new Exchange Record, we need to configure: + +- Backend Type +- Exchange Type +- Backend +- Components + +## Jobs + +- **Internal User**: might be an EDI user without even knowing about it, triggering EDI flows by some of his actions on business records; does not need access to related queue jobs. + +- **EDI User**: more conscious EDI user that might sometimes need to debug things a bit further and thus needs access to related queue jobs. + +- **EDI Manager**: full configuration access. + +## Code to execute + +By default, EDI Framework uses fields on `edi.backend` to get the right function to execute. +Each function is related to a model where the specific function is defined. +This models needs to inherit the specific handler of each case. + +- receive: model `edi.oca.handler.receive` with function receive. +- process: model `edi.oca.handler.process` with function process. +- generate: model `edi.oca.handler.generate` with function generate. +- send: model `edi.oca.handler.send` with function send. +- check: model `edi.oca.handler.check` with function check. +- validate on inputs: model `edi.oca.handler.input.validate` with function input_validate. +- validate on outputs: model `edi.oca.handler.output.validate` with function input_validate. + +You can see an example on the tests fake_models. + +For a more complex behaviour, you can use `edi_component_oca` module to use components. + +## User EDI generation + +On the exchange type, it might be possible to define a set of models, a +domain and a snippet of code. After defining this fields, we will +automatically see buttons on the view to generate the exchange records. +This configuration is useful to define a way of generation managed by +user. + +## Exchange type rules configuration + +Exchange types can be further configured with rules. You can use rules +to: + +1. make buttons automatically appear in forms +2. define your own custom logic + +Go to an exchange type and go to the tab "Model rules". There you can +add one or more rule, one per model. On each rule you can define a +domain or a snippet to activate it. In case of a "Form button" kind, if +the domain and/ the snippet is/are satisfied, a form btn will appear on +the top of the form. This button can be used by the end user to manually +generate an exchange. If there's more than a backend and the exchange +type has not a backend set, a wizard will appear asking to select a +backend to be used for the exchange. + +In case of "Custom" kind, you'll have to define your own logic to do +something. diff --git a/edi_core_oca/readme/CONTRIBUTORS.md b/edi_core_oca/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..405f579f4 --- /dev/null +++ b/edi_core_oca/readme/CONTRIBUTORS.md @@ -0,0 +1,5 @@ +- Simone Orsi \<\> +- Enric Tobella \<\> +- Manuel Regidor \<\> +- Thien Vo \<\> +- Jordi Masvidal \<\> diff --git a/edi_core_oca/readme/DESCRIPTION.md b/edi_core_oca/readme/DESCRIPTION.md new file mode 100644 index 000000000..8cfcb472b --- /dev/null +++ b/edi_core_oca/readme/DESCRIPTION.md @@ -0,0 +1,11 @@ +Base EDI backend. + +Provides following models: + +1. EDI Backend, to centralize configuration +2. EDI Backend Type, to classify EDI backends (eg: UBL, GS1, e-invoice, + pick-yours) +3. EDI Exchange Type, to define file types of exchange +4. EDI Exchange Record, to define a record exchanged between systems + +Also define a mixin to be inherited by records that will generate EDIs diff --git a/edi_core_oca/readme/ROADMAP.md b/edi_core_oca/readme/ROADMAP.md new file mode 100644 index 000000000..094c42f96 --- /dev/null +++ b/edi_core_oca/readme/ROADMAP.md @@ -0,0 +1,8 @@ +## 14.0.1.0.0 + +The module name has been changed from edi to edi_oca. + +## 18.0.1.4.0 + +Components dependancy has been removed and set on a new dependant module `edi_component_oca`. +Module `edi_oca` has been_renamed to `edi_core_oca`. diff --git a/edi_core_oca/readme/USAGE.md b/edi_core_oca/readme/USAGE.md new file mode 100644 index 000000000..e07863d2c --- /dev/null +++ b/edi_core_oca/readme/USAGE.md @@ -0,0 +1,31 @@ +After certain operations or manual execution, Exchange records will be +generated. This Exchange records might be input records or outputs +records. + +The change of state can be manually executed by the system or be managed +through by ir.cron. + +## Output Exchange records + +An output record is intended to be used for exchange information from +Odoo to another system. + +The flow of an output record should be: + +- Creation +- Generation of data +- Validation of data +- Sending data +- Validation of data processed properly by the other party + +## Input Exchange records + +An input record is intended to be used for exchange information another +system to odoo. + +The flow of an input record should be: + +- Creation +- Reception of data +- Checking data +- Processing data diff --git a/edi_core_oca/security/ir_model_access.xml b/edi_core_oca/security/ir_model_access.xml new file mode 100644 index 000000000..8c85f038b --- /dev/null +++ b/edi_core_oca/security/ir_model_access.xml @@ -0,0 +1,184 @@ + + + + access_edi_backend_type manager + + + + + + + + + access_edi_backend manager + + + + + + + + + access_edi_exchange_type manager + + + + + + + + + access_edi_exchange_type_rule manager + + + + + + + + + access_edi_exchange_record manager + + + + + + + + + access_edi_exchange_related_record manager + + + + + + + + + access_edi_backend_type user + + + + + + + + + access_edi_backend user + + + + + + + + + access_edi_exchange_type user + + + + + + + + + access_edi_exchange_type_rule user + + + + + + + + + access_edi_exchange_record user + + + + + + + + + access_edi_exchange_related_record user + + + + + + + + + access_edi_exchange_record user + + + + + + + + + Assigned EDI exchange records + + ['|', ('model','!=', False), ('res_id', '=', False)] + + + + Manager EDI exchange records + + [(1, '=', 1)] + + + + edi_exchange_record multi-company + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + + edi_backend multi-company + + ['|',('company_id','=',False),('company_id', 'in', company_ids)] + + + access_edi_configuration manager + + + + + + + + + access_edi_configuration user + + + + + + + + + access_edi_configuration_trigger manager + + + + + + + + + access_edi_configuration_trigger user + + + + + + + + diff --git a/edi_core_oca/security/res_groups.xml b/edi_core_oca/security/res_groups.xml new file mode 100644 index 000000000..d65dc3878 --- /dev/null +++ b/edi_core_oca/security/res_groups.xml @@ -0,0 +1,19 @@ + + + + EDI Advanced Settings Manager + + + + + EDI Override Exchange File Content + + + + diff --git a/edi_core_oca/static/description/icon.png b/edi_core_oca/static/description/icon.png new file mode 100644 index 000000000..610c5380f Binary files /dev/null and b/edi_core_oca/static/description/icon.png differ diff --git a/edi_core_oca/static/description/icon.svg b/edi_core_oca/static/description/icon.svg new file mode 100644 index 000000000..5cd4189f7 --- /dev/null +++ b/edi_core_oca/static/description/icon.svg @@ -0,0 +1,112 @@ + + diff --git a/edi_core_oca/static/description/index.html b/edi_core_oca/static/description/index.html new file mode 100644 index 000000000..7fe0c9952 --- /dev/null +++ b/edi_core_oca/static/description/index.html @@ -0,0 +1,583 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

EDI

+ +

Beta License: LGPL-3 OCA/edi-framework Translate me on Weblate Try me on Runboat

+

Base EDI backend.

+

Provides following models:

+
    +
  1. EDI Backend, to centralize configuration
  2. +
  3. EDI Backend Type, to classify EDI backends (eg: UBL, GS1, e-invoice, +pick-yours)
  4. +
  5. EDI Exchange Type, to define file types of exchange
  6. +
  7. EDI Exchange Record, to define a record exchanged between systems
  8. +
+

Also define a mixin to be inherited by records that will generate EDIs

+

Table of contents

+ +
+

Configuration

+

This module aims to provide an infrastructure to simplify +interchangability of documents between systems providing a configuration +platform. It will be inherited by other modules in order to define the +proper implementations of components.

+

In order to define a new Exchange Record, we need to configure:

+
    +
  • Backend Type
  • +
  • Exchange Type
  • +
  • Backend
  • +
  • Components
  • +
+
+

Jobs

+
    +
  • Internal User: might be an EDI user without even knowing about it, +triggering EDI flows by some of his actions on business records; does +not need access to related queue jobs.
  • +
  • EDI User: more conscious EDI user that might sometimes need to +debug things a bit further and thus needs access to related queue +jobs.
  • +
  • EDI Manager: full configuration access.
  • +
+
+
+

Code to execute

+

By default, EDI Framework uses fields on edi.backend to get the +right function to execute. Each function is related to a model where the +specific function is defined. This models needs to inherit the specific +handler of each case.

+
    +
  • receive: model edi.oca.handler.receive with function receive.
  • +
  • process: model edi.oca.handler.process with function process.
  • +
  • generate: model edi.oca.handler.generate with function generate.
  • +
  • send: model edi.oca.handler.send with function send.
  • +
  • check: model edi.oca.handler.check with function check.
  • +
  • validate on inputs: model edi.oca.handler.input.validate with +function input_validate.
  • +
  • validate on outputs: model edi.oca.handler.output.validate with +function input_validate.
  • +
+

You can see an example on the tests fake_models.

+

For a more complex behaviour, you can use edi_component_oca module +to use components.

+
+
+

User EDI generation

+

On the exchange type, it might be possible to define a set of models, a +domain and a snippet of code. After defining this fields, we will +automatically see buttons on the view to generate the exchange records. +This configuration is useful to define a way of generation managed by +user.

+
+
+

Exchange type rules configuration

+

Exchange types can be further configured with rules. You can use rules +to:

+
    +
  1. make buttons automatically appear in forms
  2. +
  3. define your own custom logic
  4. +
+

Go to an exchange type and go to the tab “Model rules”. There you can +add one or more rule, one per model. On each rule you can define a +domain or a snippet to activate it. In case of a “Form button” kind, if +the domain and/ the snippet is/are satisfied, a form btn will appear on +the top of the form. This button can be used by the end user to manually +generate an exchange. If there’s more than a backend and the exchange +type has not a backend set, a wizard will appear asking to select a +backend to be used for the exchange.

+

In case of “Custom” kind, you’ll have to define your own logic to do +something.

+
+
+
+

Usage

+

After certain operations or manual execution, Exchange records will be +generated. This Exchange records might be input records or outputs +records.

+

The change of state can be manually executed by the system or be managed +through by ir.cron.

+
+

Output Exchange records

+

An output record is intended to be used for exchange information from +Odoo to another system.

+

The flow of an output record should be:

+
    +
  • Creation
  • +
  • Generation of data
  • +
  • Validation of data
  • +
  • Sending data
  • +
  • Validation of data processed properly by the other party
  • +
+
+
+

Input Exchange records

+

An input record is intended to be used for exchange information another +system to odoo.

+

The flow of an input record should be:

+
    +
  • Creation
  • +
  • Reception of data
  • +
  • Checking data
  • +
  • Processing data
  • +
+
+
+
+

Known issues / Roadmap

+
+

14.0.1.0.0

+

The module name has been changed from edi to edi_oca.

+
+
+

18.0.1.4.0

+

Components dependancy has been removed and set on a new dependant module +edi_component_oca. Module edi_oca has been_renamed to +edi_core_oca.

+
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE
  • +
  • Dixmit
  • +
  • Camptocamp
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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.

+

Current maintainers:

+

simahawk etobella

+

This module is part of the OCA/edi-framework project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/edi_core_oca/static/src/js/widget_edi.esm.js b/edi_core_oca/static/src/js/widget_edi.esm.js new file mode 100644 index 000000000..4b9cddf9b --- /dev/null +++ b/edi_core_oca/static/src/js/widget_edi.esm.js @@ -0,0 +1,35 @@ +import {registry} from "@web/core/registry"; +import {standardFieldProps} from "@web/views/fields/standard_field_props"; +import {useService} from "@web/core/utils/hooks"; + +const {Component} = owl; + +export class EdiConfigurationWidget extends Component { + static props = {...standardFieldProps}; + async setup() { + super.setup(); + this.orm = useService("orm"); + this.action = useService("action"); + } + async onClick(ev, rule) { + ev.preventDefault(); + ev.stopPropagation(); + var typeId = rule.type.id; + const action = await this.orm.call( + this.props.record.resModel, + "edi_create_exchange_record", + [[this.props.record.resId], typeId], + {context: this.props.record.context} + ); + this.action.doAction(action); + } + get info() { + return this.props.record.data[this.props.name]; + } +} + +EdiConfigurationWidget.template = "edi_core_oca.EdiConfigurationWidget"; + +registry.category("fields").add("edi_configuration", { + component: EdiConfigurationWidget, +}); diff --git a/edi_core_oca/static/src/xml/widget_edi.xml b/edi_core_oca/static/src/xml/widget_edi.xml new file mode 100644 index 000000000..cf22249fe --- /dev/null +++ b/edi_core_oca/static/src/xml/widget_edi.xml @@ -0,0 +1,19 @@ + + +
+ + + + + + + +
+
diff --git a/edi_core_oca/static/tests/web/edi_core_oca.test.js b/edi_core_oca/static/tests/web/edi_core_oca.test.js new file mode 100644 index 000000000..0f3b8842e --- /dev/null +++ b/edi_core_oca/static/tests/web/edi_core_oca.test.js @@ -0,0 +1,76 @@ +/* + Copyright 2025 Dixmit + License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +*/ +import {defineMailModels, startServer} from "@mail/../tests/mail_test_helpers"; +import {defineModels, fields, models, mountView} from "@web/../tests/web_test_helpers"; +import {expect, test} from "@odoo/hoot"; +import {click} from "@odoo/hoot-dom"; +class Partner extends models.Model { + _name = "partner"; + name = fields.Char({}); + edi_config = fields.Json({default: {}}); + + edi_create_exchange_record(exchange_type_id) { + expect.step("EDI Launched for " + exchange_type_id); + return {type: "ir.actions.act_window_close"}; + } +} + +defineMailModels(); +defineModels([Partner]); + +test("EDI OCA Test widget", async () => { + const pyEnv = await startServer(); + const partner = pyEnv.partner.create({ + name: "Awesome partner", + edi_config: { + 1: { + form: {btn: {label: "EDI Task 01", tooltip: "EDI Task Tooltip"}}, + type: { + id: 1, + name: "EDI Type 01", + }, + }, + 2: { + form: {btn: {label: "EDI Task 02", tooltip: "EDI Task Tooltip"}}, + type: { + id: 2, + name: "EDI Type 02", + }, + }, + 3: { + form: {}, + type: { + id: 1, + name: "EDI Type 03", + }, + }, + }, + }); + await mountView({ + type: "form", + resId: partner, + resIds: [partner], + resModel: "partner", + arch: `
+ + + `, + }); + expect(".o_field_edi_configuration").toHaveCount(1); + expect(".o_field_edi_configuration .o_edi_action").toHaveCount(2); + // The one without form, shouldn't be shown + await click(".o_field_edi_configuration .o_edi_action"); + await expect.waitForSteps(["EDI Launched for 1"]); + expect.assertions(3); +}); diff --git a/edi_core_oca/templates/exchange_chatter_msg.xml b/edi_core_oca/templates/exchange_chatter_msg.xml new file mode 100644 index 000000000..d76a94078 --- /dev/null +++ b/edi_core_oca/templates/exchange_chatter_msg.xml @@ -0,0 +1,43 @@ + + + + diff --git a/edi_core_oca/templates/exchange_mixin_buttons.xml b/edi_core_oca/templates/exchange_mixin_buttons.xml new file mode 100644 index 000000000..d57c84b64 --- /dev/null +++ b/edi_core_oca/templates/exchange_mixin_buttons.xml @@ -0,0 +1,18 @@ + + + + diff --git a/edi_core_oca/tests/__init__.py b/edi_core_oca/tests/__init__.py new file mode 100644 index 000000000..9fec7d330 --- /dev/null +++ b/edi_core_oca/tests/__init__.py @@ -0,0 +1,16 @@ +from . import test_exchange_type_configuration +from . import test_backend_type +from . import test_exchange_type +from . import test_record +from . import test_backend_base +from . import test_backend_output +from . import test_backend_input +from . import test_backend_process +from . import test_backend_validate +from . import test_consumer_mixin +from . import test_edi_backend_cron +from . import test_security +from . import test_quick_exec +from . import test_exchange_type_encoding +from . import test_edi_configuration +from . import test_frontend diff --git a/edi_core_oca/tests/common.py b/edi_core_oca/tests/common.py new file mode 100644 index 000000000..fe9bdb9d1 --- /dev/null +++ b/edi_core_oca/tests/common.py @@ -0,0 +1,102 @@ +# Copyright 2020 ACSONE +# Copyright 2020 Dixmit +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import os + +from odoo.tests.common import TransactionCase + + +class EDIBackendTestMixin: + @classmethod + def _setup_context(cls, **kw): + return dict( + cls.env.context, tracking_disable=True, queue_job__no_delay=True, **kw + ) + + @classmethod + def _setup_env(cls, ctx=None): + ctx = ctx or {} + cls.env = cls.env(context=cls._setup_context(**ctx)) + + @classmethod + def _setup_records(cls): + cls.backend = cls._get_backend() + cls.backend_type_code = cls.backend.backend_type_id.code + cls.backend_model = cls.env["edi.backend"] + cls.backend_type_model = cls.env["edi.backend.type"] + cls.exchange_type_in = cls._create_exchange_type( + name="Test CSV input", + code="test_csv_input", + direction="input", + exchange_file_ext="csv", + exchange_filename_pattern="{record.ref}-{type.code}-{dt}", + ) + cls.exchange_type_out = cls._create_exchange_type( + name="Test CSV output", + code="test_csv_output", + direction="output", + exchange_file_ext="csv", + exchange_filename_pattern="{record.ref}-{type.code}-{dt}", + ) + cls.exchange_type_out_ack = cls._create_exchange_type( + name="Test CSV output ACK", + code="test_csv_output_ack", + direction="output", + exchange_file_ext="txt", + exchange_filename_pattern="{record.ref}-{type.code}-{dt}", + ) + cls.exchange_type_out.ack_type_id = cls.exchange_type_out_ack + cls.partner = cls.env["res.partner"].create({"name": "EDI EXC TEST"}) + cls.partner.ref = "EDI_EXC_TEST" + cls.sequence = cls.env["ir.sequence"].create( + { + "code": "test_sequence", + "name": "Test sequence", + "implementation": "no_gap", + "padding": 7, + } + ) + + def read_test_file(self, filename): + path = os.path.join(os.path.dirname(__file__), "examples", filename) + with open(path) as thefile: + return thefile.read() + + @classmethod + def _get_backend_type(cls): + return cls.env["edi.backend.type"].create( + { + "name": "Demo backend type", + "code": "demo_backend", + } + ) + + @classmethod + def _get_backend(cls): + return cls.env["edi.backend"].create( + { + "name": "Demo backend", + "backend_type_id": cls._get_backend_type().id, + } + ) + + @classmethod + def _create_exchange_type(cls, **kw): + model = cls.env["edi.exchange.type"] + vals = { + "name": "Test CSV exchange", + "backend_id": cls.backend.id, + "backend_type_id": cls.backend.backend_type_id.id, + } + vals.update(kw) + return model.create(vals) + + +class EDIBackendCommonTestCase(TransactionCase, EDIBackendTestMixin): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls._setup_env() + cls._setup_records() diff --git a/edi_core_oca/tests/fake_models.py b/edi_core_oca/tests/fake_models.py new file mode 100644 index 000000000..df7d5e25c --- /dev/null +++ b/edi_core_oca/tests/fake_models.py @@ -0,0 +1,100 @@ +# Copyright 2020 Dixmit +# @author: Enric Tobella +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from collections import defaultdict + +from odoo import fields, models + + +class EdiExchangeConsumerTest(models.Model): + _name = "edi.exchange.consumer.test" + _inherit = ["edi.exchange.consumer.mixin"] + + _description = "Model used only for test" + + name = fields.Char() + edi_config_ids = fields.Many2many( + string="EDI Test Config Ids", + comodel_name="edi.configuration", + relation="test_edi_configuration_rel", + column1="record_id", + column2="conf_id", + domain="[('model_name', '=', 'edi.exchange.consumer.test')]", + ) + + def _edi_config_field_relation(self): + return self.edi_config_ids + + def _get_edi_exchange_record_name(self, exchange_record): + return self.id + + +class EdiTestExecution(models.AbstractModel): + _name = "edi.framework.test.execution" + _inherit = [ + "edi.oca.handler.process", + "edi.oca.handler.check", + "edi.oca.handler.send", + "edi.oca.handler.receive", + "edi.oca.handler.output.validate", + "edi.oca.handler.input.validate", + ] + _description = "Fake EDI execution model for testing purposes" + + FAKED_COLLECTOR = defaultdict(list) + + def _fake_it(self, exchange_record, kind): + self.FAKED_COLLECTOR[kind].append(self._call_key(exchange_record)) + if self.env.context.get("test_break_" + kind): + exception = self.env.context.get("test_break_" + kind, "YOU BROKE IT!") + if not isinstance(exception, Exception): + exception = ValueError(exception) + raise exception + update_values = self.env.context.get("fake_update_values") + if update_values: + exchange_record.write(update_values) + return self.env.context.get("fake_output", self._call_key(exchange_record)) + + @classmethod + def _call_key(cls, rec): + return f"{cls._name}: {rec.id}" + + @classmethod + def reset_faked(cls, kind): + cls.FAKED_COLLECTOR[kind] = [] + + @classmethod + def check_called_for(cls, rec, kind): + return cls._call_key(rec) in cls.FAKED_COLLECTOR[kind] + + @classmethod + def check_not_called_for(cls, rec, kind): + return cls._call_key(rec) not in cls.FAKED_COLLECTOR[kind] + + def send(self, exchange_record): + return self._fake_it(exchange_record, "send") + + def generate(self, exchange_record): + return self._fake_it(exchange_record, "generate") + + def output_validate(self, exchange_record, **kw): + return self._fake_it(exchange_record, "output_validate") + + def input_validate(self, exchange_record, **kw): + return self._fake_it(exchange_record, "input_validate") + + def receive(self, exchange_record): + return self._fake_it(exchange_record, "receive") + + def process(self, exchange_record): + return self._fake_it(exchange_record, "process") + + def check(self, exchange_record): + return self._fake_it(exchange_record, "check") + + +class EdiTestExecutionExtra(models.AbstractModel): + _name = "edi.framework.test.execution.extra" + _inherit = "edi.framework.test.execution" + _description = "Fake EDI execution model for testing purposes with extra methods" diff --git a/edi_core_oca/tests/result.pdf b/edi_core_oca/tests/result.pdf new file mode 100644 index 000000000..9f185a804 Binary files /dev/null and b/edi_core_oca/tests/result.pdf differ diff --git a/edi_core_oca/tests/test_backend_base.py b/edi_core_oca/tests/test_backend_base.py new file mode 100644 index 000000000..87219bea0 --- /dev/null +++ b/edi_core_oca/tests/test_backend_base.py @@ -0,0 +1,98 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from freezegun import freeze_time + +from odoo.orm.model_classes import add_to_registry + +from odoo.addons.edi_core_oca.exceptions import EDINotImplementedError + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestCaseBase(EDIBackendCommonTestCase): + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, + ["edi.framework.test.execution"], + {"models_to_check": True}, + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_in.receive_model_id = cls.model + cls.exchange_type_in.process_model_id = cls.model + cls.exchange_type_in.input_validate_model_id = cls.model + + @freeze_time("2020-10-21 10:00:00") + def test_create_record(self): + self.env.user.tz = None # Have no timezone used in generated filename + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_input", vals) + expected = { + "type_id": self.exchange_type_in.id, + "edi_exchange_state": "new", + "exchange_filename": "EDI_EXC_TEST-test_csv_input-2020-10-21-100000.csv", + } + self.assertRecordValues(record, [expected]) + self.assertEqual(record.record, self.partner) + self.assertEqual(record.edi_exchange_state, "new") + + def test_action_view_exchanges(self): + # Just testing is not broken + self.assertTrue(self.backend.action_view_exchanges()) + + def test_action_view_exchange_types(self): + # Just testing is not broken + self.assertTrue(self.backend.action_view_exchange_types()) + + def _test_get_handler(self, user=None): + backend = self.backend + if user: + backend = self.backend.with_user(user) + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = backend.create_record("test_csv_input", vals) + for action in ["receive", "input_validate", "process"]: + self.assertEqual( + str(backend._get_exec_handler(record, action)), + str(getattr(self.ExecutionAbstractModel, action)), + ) + + def test_get_handler_admin(self): + self._test_get_handler() + + def test_get_handler_avg_user(self): + user = ( + self.env["res.users"] + .with_context(no_reset_password=True) + .create({"name": "Test User EDI", "login": "test_edi_perm_user"}) + ) + user.group_ids += self.env.ref("base.group_partner_manager") + self._test_get_handler(user=user) + + def test_get_handler_no_handler(self): + self.exchange_type_in.process_model_id = False + self.exchange_type_in.input_validate_model_id = False + self.exchange_type_in.receive_model_id = False + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_input", vals) + for action in ["receive", "input_validate", "process"]: + with self.assertRaises(EDINotImplementedError): + self.backend._get_exec_handler(record, action) diff --git a/edi_core_oca/tests/test_backend_input.py b/edi_core_oca/tests/test_backend_input.py new file mode 100644 index 000000000..4b2b92dde --- /dev/null +++ b/edi_core_oca/tests/test_backend_input.py @@ -0,0 +1,86 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.orm.model_classes import add_to_registry + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestInputCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_input", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_in.receive_model_id = cls.model + cls.exchange_type_in.process_model_id = cls.model + cls.exchange_type_in.input_validate_model_id = cls.model + + @classmethod + def _setup_context(cls): + return dict( + super()._setup_context(), + _edi_receive_break_on_error=True, + _edi_process_break_on_error=True, + ) + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("receive") + + def test_receive_record_nothing_todo(self): + self.backend.with_context(fake_output="yeah!").exchange_receive(self.record) + self.assertEqual(self.record._get_file_content(), "") + self.assertRecordValues(self.record, [{"edi_exchange_state": "new"}]) + + def test_receive_record(self): + self.record.edi_exchange_state = "input_pending" + self.backend.with_context(fake_output="yeah!").exchange_receive(self.record) + self.assertEqual(self.record._get_file_content(), "yeah!") + self.assertRecordValues(self.record, [{"edi_exchange_state": "input_received"}]) + + def test_receive_no_allow_empty_file_record(self): + self.record.edi_exchange_state = "input_pending" + self.backend.with_context( + fake_output="", _edi_receive_break_on_error=False + ).exchange_receive(self.record) + # Check the record + msg = ( + "Empty files are not allowed for exchange type " + f"{self.exchange_type_in.name} ({self.exchange_type_in.code})" + ) + self.assertEqual(msg, self.record.exchange_error) + self.assertIn(msg, self.record.exchange_error_traceback) + self.assertEqual(self.record._get_file_content(), "") + self.assertRecordValues( + self.record, [{"edi_exchange_state": "input_receive_error"}] + ) + + def test_receive_allow_empty_file_record(self): + self.record.edi_exchange_state = "input_pending" + self.record.type_id.allow_empty_files_on_receive = True + self.backend.with_context( + fake_output="", _edi_receive_break_on_error=False + ).exchange_receive(self.record) + # Check the record + self.assertEqual(self.record._get_file_content(), "") + self.assertRecordValues(self.record, [{"edi_exchange_state": "input_received"}]) diff --git a/edi_core_oca/tests/test_backend_output.py b/edi_core_oca/tests/test_backend_output.py new file mode 100644 index 000000000..a884003ee --- /dev/null +++ b/edi_core_oca/tests/test_backend_output.py @@ -0,0 +1,131 @@ +# Copyright 2020 ACSONE +# Copyright 2021 Camptocamp +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from unittest import mock + +from freezegun import freeze_time + +from odoo import fields, tools +from odoo.exceptions import UserError +from odoo.orm.model_classes import add_to_registry + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestOutputCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_output", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + cls.exchange_type_out.output_validate_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModel.reset_faked("send") + self.ExecutionAbstractModel.reset_faked("check") + + def test_generate_record_output(self): + self.record.with_context(fake_output="yeah!").action_exchange_generate() + self.assertEqual(self.record._get_file_content(), "yeah!") + + def test_generate_record_output_pdf(self): + pdf_content = tools.file_open( + "addons/edi_core_oca/tests/result.pdf", mode="rb" + ).read() + self.record.with_context(fake_output=pdf_content).action_exchange_generate() + + def test_send_record(self): + self.record.write({"edi_exchange_state": "output_pending"}) + self.record._set_file_content(f"TEST {self.record.id}") + self.assertFalse(self.record.exchanged_on) + with freeze_time("2020-10-21 10:00:00"): + self.record.action_exchange_send() + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(self.record, "send") + ) + self.assertRecordValues( + self.record, [{"edi_exchange_state": "output_sent"}] + ) + self.assertEqual( + fields.Datetime.to_string(self.record.exchanged_on), + "2020-10-21 10:00:00", + ) + + def test_send_record_with_error(self): + self.record.write({"edi_exchange_state": "output_pending"}) + self.record._set_file_content(f"TEST {self.record.id}") + self.assertFalse(self.record.exchanged_on) + self.record.with_context( + test_break_send="OOPS! Something went wrong :(" + ).action_exchange_send() + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(self.record, "send") + ) + self.assertRecordValues( + self.record, + [ + { + "edi_exchange_state": "output_error_on_send", + "exchange_error": "OOPS! Something went wrong :(", + } + ], + ) + self.assertIn( + "OOPS! Something went wrong :(", self.record.exchange_error_traceback + ) + + def test_send_invalid_direction(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_input", vals) + with mock.patch.object(type(self.backend), "_exchange_send") as mocked: + mocked.return_value = "AAA" + with self.assertRaises(UserError) as err: + record.action_exchange_send() + self.assertEqual( + err.exception.args[0], + f"Record ID={record.id} is not meant to be sent!", + ) + mocked.assert_not_called() + + def test_send_not_generated_record(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + with mock.patch.object(type(self.backend), "_exchange_send") as mocked: + mocked.return_value = "AAA" + with self.assertRaises(UserError) as err: + record.action_exchange_send() + self.assertEqual( + err.exception.args[0], f"Record ID={record.id} has no file to send!" + ) + mocked.assert_not_called() diff --git a/edi_core_oca/tests/test_backend_process.py b/edi_core_oca/tests/test_backend_process.py new file mode 100644 index 000000000..4e2bc59a2 --- /dev/null +++ b/edi_core_oca/tests/test_backend_process.py @@ -0,0 +1,112 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 + +from freezegun import freeze_time + +from odoo import fields +from odoo.exceptions import UserError +from odoo.orm.model_classes import add_to_registry +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestProcessCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + "exchange_file": base64.b64encode(b"1234"), + } + cls.record = cls.backend.create_record("test_csv_input", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_in.generate_model_id = cls.model + cls.exchange_type_in.process_model_id = cls.model + cls.exchange_type_in.input_validate_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("process") + + def test_process_record(self): + self.record.write({"edi_exchange_state": "input_received"}) + with freeze_time("2020-10-22 10:00:00"): + self.record.action_exchange_process() + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(self.record, "process") + ) + self.assertRecordValues( + self.record, [{"edi_exchange_state": "input_processed"}] + ) + self.assertEqual( + fields.Datetime.to_string(self.record.exchanged_on), "2020-10-22 10:00:00" + ) + + def test_process_record_with_error(self): + self.record.write({"edi_exchange_state": "input_received"}) + self.record._set_file_content(f"TEST {self.record.id}") + self.record.with_context( + test_break_process="OOPS! Something went wrong :(" + ).action_exchange_process() + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(self.record, "process") + ) + self.assertRecordValues( + self.record, + [ + { + "edi_exchange_state": "input_processed_error", + "exchange_error": "OOPS! Something went wrong :(", + } + ], + ) + self.assertIn( + "OOPS! Something went wrong :(", self.record.exchange_error_traceback + ) + + @mute_logger("odoo.models.unlink") + def test_process_no_file_record(self): + self.record.write({"edi_exchange_state": "input_received"}) + self.record.exchange_file = False + self.exchange_type_in.allow_empty_files_on_receive = False + with self.assertRaises(UserError): + self.record.action_exchange_process() + + @mute_logger("odoo.models.unlink") + def test_process_allow_no_file_record(self): + self.record.write({"edi_exchange_state": "input_received"}) + self.record.exchange_file = False + self.exchange_type_in.allow_empty_files_on_receive = True + self.record.action_exchange_process() + self.assertEqual(self.record.edi_exchange_state, "input_processed") + + def test_process_outbound_record(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + record._set_file_content(f"TEST {record.id}") + with self.assertRaises(UserError): + record.action_exchange_process() + + # TODO: test ack file are processed diff --git a/edi_core_oca/tests/test_backend_type.py b/edi_core_oca/tests/test_backend_type.py new file mode 100644 index 000000000..13d751094 --- /dev/null +++ b/edi_core_oca/tests/test_backend_type.py @@ -0,0 +1,31 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import psycopg2 + +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTypeTestCase(EDIBackendCommonTestCase): + def test_type_code(self): + btype = self.backend_type_model.create( + {"name": "Test new type", "code": "Test new type"} + ) + self.assertEqual(btype.code, "test_new_type") + + def test_type_code_uniq(self): + existing_code = self.backend.backend_type_id.code + with mute_logger("odoo.sql_db"): + with self.assertRaises(psycopg2.IntegrityError): + self.backend_type_model.create( + {"name": "Test new type", "code": existing_code} + ) + + def test_copy(self): + new_type = self.backend.backend_type_id.copy() + self.assertEqual( + new_type.code, f"{self.backend.backend_type_id.code}_copy_fixme" + ) diff --git a/edi_core_oca/tests/test_backend_validate.py b/edi_core_oca/tests/test_backend_validate.py new file mode 100644 index 000000000..d3c585f8b --- /dev/null +++ b/edi_core_oca/tests/test_backend_validate.py @@ -0,0 +1,145 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 + +from odoo.orm.model_classes import add_to_registry + +from ..exceptions import EDIValidationError +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestValidateCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + "exchange_file": base64.b64encode(b"1234"), + } + cls.record_in = cls.backend.create_record("test_csv_input", vals) + vals.pop("exchange_file") + cls.record_out = cls.backend.create_record("test_csv_output", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + cls.exchange_type_out.output_validate_model_id = cls.model + cls.exchange_type_in.receive_model_id = cls.model + cls.exchange_type_in.process_model_id = cls.model + cls.exchange_type_in.input_validate_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("input_validate") + self.ExecutionAbstractModel.reset_faked("receive") + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModel.reset_faked("output_validate") + + def test_receive_validate_record(self): + self.record_in.write({"edi_exchange_state": "input_pending"}) + self.backend.exchange_receive(self.record_in) + self.assertTrue( + self.ExecutionAbstractModel.check_called_for( + self.record_in, "input_validate" + ) + ) + self.assertRecordValues( + self.record_in, [{"edi_exchange_state": "input_received"}] + ) + + def test_receive_validate_record_error(self): + self.record_in.write({"edi_exchange_state": "input_pending"}) + exc = EDIValidationError("Data seems wrong!") + self.backend.with_context(test_break_input_validate=exc).exchange_receive( + self.record_in + ) + self.assertTrue( + self.ExecutionAbstractModel.check_called_for( + self.record_in, "input_validate" + ) + ) + self.assertRecordValues( + self.record_in, + [ + { + "edi_exchange_state": "validate_error", + "exchange_error": "Data seems wrong!", + } + ], + ) + self.assertIn("Data seems wrong!", self.record_in.exchange_error_traceback) + + def test_generate_validate_record(self): + self.record_out.write({"edi_exchange_state": "new"}) + self.backend.exchange_generate(self.record_out) + self.assertTrue( + self.ExecutionAbstractModel.check_called_for( + self.record_out, "output_validate" + ) + ) + self.assertRecordValues( + self.record_out, [{"edi_exchange_state": "output_pending"}] + ) + + def test_generate_validate_record_error(self): + self.record_out.write({"edi_exchange_state": "new"}) + exc = EDIValidationError("Data seems wrong!") + self.backend.with_context(test_break_output_validate=exc).exchange_generate( + self.record_out + ) + self.assertTrue( + self.ExecutionAbstractModel.check_called_for( + self.record_out, "output_validate" + ) + ) + self.assertRecordValues( + self.record_out, + [ + { + "edi_exchange_state": "validate_error", + "exchange_error": "Data seems wrong!", + } + ], + ) + self.assertIn("Data seems wrong!", self.record_out.exchange_error_traceback) + + def test_validate_record_error_regenerate(self): + self.record_out.write({"edi_exchange_state": "new"}) + exc = EDIValidationError("Data seems wrong!") + self.backend.with_context(test_break_output_validate=exc).exchange_generate( + self.record_out + ) + self.assertRecordValues( + self.record_out, + [ + { + "edi_exchange_state": "validate_error", + } + ], + ) + self.record_out.with_context(fake_output="yeah!").action_regenerate() + self.assertEqual(self.record_out._get_file_content(), "yeah!") + self.assertRecordValues( + self.record_out, + [ + { + "edi_exchange_state": "output_pending", + } + ], + ) diff --git a/edi_core_oca/tests/test_consumer_mixin.py b/edi_core_oca/tests/test_consumer_mixin.py new file mode 100644 index 000000000..f3adc208c --- /dev/null +++ b/edi_core_oca/tests/test_consumer_mixin.py @@ -0,0 +1,280 @@ +# Copyright 2020 Dixmit +# @author: Enric Tobella +# Copyright 2020 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import os +from unittest import mock, skipIf + +from lxml import etree + +from odoo.orm.model_classes import add_to_registry +from odoo.tests import Form + +from .common import EDIBackendCommonTestCase + + +# This clashes w/ some setup (eg: run tests w/ pytest when edi_storage is installed) +# If you still want to run `edi` tests w/ pytest when this happens, set this env var. +@skipIf(os.getenv("SKIP_EDI_CONSUMER_CASE"), "Consumer test case disabled.") +class TestConsumerMixinCase(EDIBackendCommonTestCase): + @classmethod + def _setup_env(cls): + super()._setup_env() + # Load fake models ->/ + from .fake_models import EdiExchangeConsumerTest, EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + add_to_registry(cls.registry, EdiExchangeConsumerTest) + cls.registry._setup_models__( + cls.env.cr, ["edi.framework.test.execution", "edi.exchange.consumer.test"] + ) + cls.registry.init_models( + cls.env.cr, + ["edi.framework.test.execution", "edi.exchange.consumer.test"], + {"models_to_check": True}, + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.exchange.consumer.test") + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + return super()._setup_env() + + # pylint: disable=W8110 + @classmethod + def _setup_records(cls): + super()._setup_records() + cls.consumer_record = cls.env["edi.exchange.consumer.test"].create( + {"name": "Test Consumer"} + ) + cls.exchange_type_out.exchange_filename_pattern = "{record.id}" + + rule_vals = { + "name": "Test", + "model_id": cls.env["ir.model"]._get_id(cls.consumer_record._name), + "kind": "custom", + "enable_domain": "[]", + "enable_snippet": """ +result = not record._has_exchange_record(exchange_type) +""", + } + cls.exchange_type_new = cls._create_exchange_type( + name="Test CSV output", + code="test_csv_new_output", + direction="output", + exchange_file_ext="csv", + backend_id=False, + exchange_filename_pattern="{record.ref}-{type.code}-{dt}", + rule_ids=[(0, 0, rule_vals)], + ) + rule_vals = { + "name": "Test", + "model_id": cls.env["ir.model"]._get_id(cls.consumer_record._name), + "kind": "custom", + "enable_domain": "[]", + "enable_snippet": """ +result = not record._has_exchange_record(exchange_type, exchange_type.backend_id) +""", + } + cls.exchange_type_out.write({"rule_ids": [(0, 0, rule_vals)]}) + cls.backend_02 = cls.backend.copy() + + def test_mixin(self): + self.assertEqual(self.consumer_record.exchange_record_count, 0) + vals = { + "model": self.consumer_record._name, + "res_id": self.consumer_record.id, + } + exchange_record = self.backend.create_record("test_csv_output", vals) + self.assertEqual(self.consumer_record.exchange_record_count, 1) + self.env["edi.exchange.record"].create( + { + "backend_id": self.backend.id, + "type_id": self.exchange_type_new.id, + "model": "an.other.model.with.same.id", + "res_id": self.consumer_record.id, + } + ) + self.consumer_record.invalidate_model() + self.assertEqual(self.consumer_record.exchange_record_count, 1) + action = self.consumer_record.action_view_edi_records() + self.consumer_record.invalidate_model() + self.assertEqual( + exchange_record, self.env["edi.exchange.record"].search(action["domain"]) + ) + self.assertTrue( + self.consumer_record._has_exchange_record( + exchange_record.type_id, self.backend + ) + ) + + def test_origin(self): + vals = { + "model": self.consumer_record._name, + "res_id": self.consumer_record.id, + } + exchange_record = self.backend.create_record("test_csv_output", vals) + self.consumer_record._edi_set_origin(exchange_record) + self.assertEqual( + self.consumer_record.origin_exchange_record_id, exchange_record + ) + self.assertEqual(self.consumer_record._edi_get_origin(), exchange_record) + + def test_expected_configuration(self): + # no btn enabled + + def make_config_data(**kw): + data = { + "form": {}, + "type": { + "id": self.exchange_type_out.id, + "name": self.exchange_type_out.name, + }, + } + data.update(kw) + return data + + rule = self.exchange_type_out.rule_ids[0] + self.assertFalse(self.consumer_record.edi_has_form_config) + self.assertEqual( + self.consumer_record.edi_config[str(rule.id)], + make_config_data(), + ) + # enable it + self.exchange_type_out.rule_ids[0].kind = "form_btn" + self.consumer_record.invalidate_model(["edi_has_form_config", "edi_config"]) + self.assertEqual( + self.consumer_record.edi_config[str(rule.id)], + make_config_data( + form={"btn": {"label": self.exchange_type_out.name, "tooltip": False}} + ), + ) + action = self.consumer_record.edi_create_exchange_record( + self.exchange_type_out.id + ) + self.assertEqual(action["res_model"], "edi.exchange.record") + self.consumer_record.invalidate_model() + self.assertNotIn( + str(rule.id), + self.consumer_record.edi_config, + ) + self.assertTrue(self.consumer_record.exchange_record_ids) + self.assertEqual( + self.consumer_record.exchange_record_ids.type_id, self.exchange_type_out + ) + + def test_multiple_backend(self): + rule = self.exchange_type_new.rule_ids[0] + self.assertIn( + str(rule.id), + self.consumer_record.edi_config, + ) + action = self.consumer_record.edi_create_exchange_record( + self.exchange_type_new.id + ) + self.assertNotEqual(action["res_model"], "edi.exchange.record") + self.assertEqual(action["res_model"], "edi.exchange.record.create.wiz") + wizard = ( + self.env[action["res_model"]] + .with_context(**action["context"]) + .create({"backend_id": self.backend_02.id}) + ) + wizard.create_edi() + self.consumer_record.invalidate_model() + self.assertNotIn( + str(rule.id), + self.consumer_record.edi_config, + ) + self.assertTrue(self.consumer_record.exchange_record_ids) + self.assertEqual( + self.consumer_record.exchange_record_ids.type_id, self.exchange_type_new + ) + + def test_form(self): + """Testing that the form has inherited the fields and inserted them. + + Unfortunately we are unable to test the buttons here + """ + view = self.env[self.consumer_record._name].get_view(False, "form") + with Form(self.consumer_record) as f: + self.assertIn("edi_has_form_config", f._values) + self.assertIn("edi_config", f._values) + form = etree.fromstring(view["arch"]) + self.assertTrue(form.xpath("//field[@name='edi_has_form_config']")) + self.assertTrue(form.xpath("//field[@name='edi_config']")) + + # Don't care about real data processing here + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._validate_data") + @mock.patch( + "odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_generate" + ) + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_send") + def test_edi_send_via_edi(self, mocked_send, mocked_generate, mocked_validate): + mocked_generate.return_value = "result" + self.assertEqual(self.consumer_record.exchange_record_count, 0) + self.consumer_record._edi_send_via_edi( + self.exchange_type_new, backend=self.backend + ) + self.assertEqual( + self.consumer_record.exchange_record_ids[0].type_id, self.exchange_type_new + ) + self.assertEqual( + self.consumer_record.exchange_record_ids[0]._get_file_content(), "result" + ) + + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._validate_data") + @mock.patch( + "odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_generate" + ) + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_send") + def test_edi_send_via_edi_ack(self, mocked_send, mocked_generate, mocked_validate): + mocked_generate.return_value = "result" + vals = { + "model": self.consumer_record._name, + "res_id": self.consumer_record.id, + } + origin_exchange_record = self.backend.create_record( + self.exchange_type_in.code, vals + ) + origin_exchange_record._set_file_content("original file") + self.consumer_record._edi_set_origin(origin_exchange_record) + self.assertEqual(self.consumer_record.exchange_record_count, 1) + # Type out is an hack for the original record, they will be linked + self.exchange_type_in.ack_type_id = self.exchange_type_out + self.consumer_record._edi_send_via_edi( + self.exchange_type_out, backend=self.backend + ) + self.assertEqual(self.consumer_record.exchange_record_count, 2) + ack_record = self.consumer_record.exchange_record_ids[1] + self.assertEqual(ack_record.parent_id, origin_exchange_record) + self.assertEqual(ack_record.type_id, self.exchange_type_out) + self.assertEqual(ack_record._get_file_content(), "result") + + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._validate_data") + @mock.patch( + "odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_generate" + ) + @mock.patch("odoo.addons.edi_core_oca.models.edi_backend.EDIBackend._exchange_send") + def test_edi_send_via_edi_invalid_ack( + self, mocked_send, mocked_generate, mocked_validate + ): + mocked_generate.return_value = "result" + vals = { + "model": self.consumer_record._name, + "res_id": self.consumer_record.id, + } + origin_exchange_record = self.backend.create_record( + self.exchange_type_in.code, vals + ) + origin_exchange_record._set_file_content("original file") + self.consumer_record._edi_set_origin(origin_exchange_record) + self.assertEqual(self.consumer_record.exchange_record_count, 1) + # Type out is an hack for another type, they will not be linked + self.exchange_type_in.ack_type_id = self.exchange_type_out_ack + self.consumer_record._edi_send_via_edi( + self.exchange_type_out, backend=self.backend + ) + self.assertEqual(self.consumer_record.exchange_record_count, 2) + ack_record = self.consumer_record.exchange_record_ids[1] + self.assertFalse(ack_record.parent_id) + self.assertEqual(ack_record.type_id, self.exchange_type_out) + self.assertEqual(ack_record._get_file_content(), "result") diff --git a/edi_core_oca/tests/test_edi_backend_cron.py b/edi_core_oca/tests/test_edi_backend_cron.py new file mode 100644 index 000000000..3e8f49147 --- /dev/null +++ b/edi_core_oca/tests/test_edi_backend_cron.py @@ -0,0 +1,124 @@ +# Copyright 2020 ACSONE +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.orm.model_classes import add_to_registry +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + +LOGGERS = ( + "odoo.addons.edi_core_oca.models.edi_backend", + "odoo.addons.queue_job.delay", + "odoo.addons.edi_exchange_template_oca.models.edi_backend", +) + + +class EDIBackendTestCronCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner2 = cls.env["res.partner"].create({"name": "EDI EXC TEST 2"}) + cls.partner3 = cls.env["res.partner"].create({"name": "EDI EXC TEST 3"}) + cls.record1 = cls.backend.create_record( + "test_csv_output", {"model": cls.partner._name, "res_id": cls.partner.id} + ) + cls.record2 = cls.backend.create_record( + "test_csv_output", {"model": cls.partner._name, "res_id": cls.partner2.id} + ) + cls.record3 = cls.backend.create_record( + "test_csv_output", {"model": cls.partner._name, "res_id": cls.partner3.id} + ) + cls.records = cls.record1 + cls.record1 + cls.record3 + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + cls.exchange_type_out.output_validate_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModel.reset_faked("send") + self.ExecutionAbstractModel.reset_faked("check") + + @mute_logger(*LOGGERS) + def test_exchange_generate_new_no_auto(self): + # No content ready to be sent, no auto-generate, nothing happens + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "new") + self.backend._cron_check_output_exchange_sync() + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "new") + + @mute_logger(*LOGGERS) + def test_exchange_generate_new_auto_skip_send(self): + self.exchange_type_out.exchange_file_auto_generate = True + # No content ready to be sent, will get the content but not send it + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "new") + self.backend._cron_check_output_exchange_sync(skip_send=True) + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "output_pending") + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(rec, "generate") + ) + self.assertEqual( + rec._get_file_content(), self.ExecutionAbstractModel._call_key(rec) + ) + # TODO: test better? + self.assertFalse(rec.ack_exchange_id) + + @mute_logger(*LOGGERS) + def test_exchange_generate_new_auto_send(self): + self.exchange_type_out.exchange_file_auto_generate = True + # No content ready to be sent, will get the content and send it + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "new") + self.backend._cron_check_output_exchange_sync() + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "output_sent") + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(rec, "generate") + ) + self.assertEqual( + rec._get_file_content(), self.ExecutionAbstractModel._call_key(rec) + ) + self.assertTrue(self.ExecutionAbstractModel.check_called_for(rec, "send")) + + @mute_logger(*LOGGERS) + def test_exchange_generate_output_ready_auto_send(self): + # No content ready to be sent, will get the content and send it + self.exchange_type_out.check_model_id = self.model + for rec in self.records: + self.assertEqual(rec.edi_exchange_state, "new") + self.record1._set_file_content("READY") + self.record1.edi_exchange_state = "output_sent" + self.backend.with_context( + fake_update_values={"edi_exchange_state": "output_sent_and_processed"} + )._cron_check_output_exchange_sync(skip_sent=False) + for rec in self.records - self.record1: + self.assertEqual(rec.edi_exchange_state, "new") + self.assertEqual(self.record1.edi_exchange_state, "output_sent_and_processed") + self.assertTrue( + self.ExecutionAbstractModel.check_not_called_for(self.record1, "generate") + ) + self.assertTrue( + self.ExecutionAbstractModel.check_not_called_for(self.record1, "send") + ) + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(self.record1, "check") + ) diff --git a/edi_core_oca/tests/test_edi_configuration.py b/edi_core_oca/tests/test_edi_configuration.py new file mode 100644 index 000000000..562f5b540 --- /dev/null +++ b/edi_core_oca/tests/test_edi_configuration.py @@ -0,0 +1,160 @@ +# Copyright 2024 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import os +import unittest + +from odoo.orm.model_classes import add_to_registry + +from .common import EDIBackendCommonTestCase + + +# This clashes w/ some setup (eg: run tests w/ pytest when edi_storage is installed) +# If you still want to run `edi` tests w/ pytest when this happens, set this env var. +@unittest.skipIf(os.getenv("SKIP_EDI_CONSUMER_CASE"), "Consumer test case disabled.") +class TestEDIConfigurations(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_output", vals) + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModel.reset_faked("send") + self.ExecutionAbstractModel.reset_faked("check") + self.consumer_record = self.env["edi.exchange.consumer.test"].create( + { + "name": "Test Consumer", + "edi_config_ids": [ + (4, self.create_config.id), + (4, self.write_config.id), + ], + } + ) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiExchangeConsumerTest, EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + add_to_registry(cls.registry, EdiExchangeConsumerTest) + cls.registry._setup_models__(cls.env.cr, ["edi.exchange.consumer.test"]) + cls.registry.init_models( + cls.env.cr, ["edi.exchange.consumer.test"], {"models_to_check": True} + ) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.addClassCleanup(cls.registry.__delitem__, "edi.exchange.consumer.test") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + cls.exchange_type_out.exchange_filename_pattern = "{record.id}" + cls.edi_configuration = cls.env["edi.configuration"] + cls.create_trigger = cls.env.ref("edi_core_oca.edi_conf_trigger_record_create") + cls.write_trigger = cls.env.ref("edi_core_oca.edi_conf_trigger_record_write") + cls.create_config = cls.edi_configuration.create( + { + "name": "Create Config", + "active": True, + "backend_id": cls.backend.id, + "type_id": cls.exchange_type_out.id, + "trigger_id": cls.create_trigger.id, + "model_id": cls.env["ir.model"]._get_id("edi.exchange.consumer.test"), + "snippet_do": "record._edi_send_via_edi(conf.type_id)", + } + ) + cls.write_config = cls.edi_configuration.create( + { + "name": "Write Config 1", + "active": True, + "backend_id": cls.backend.id, + "type_id": cls.exchange_type_out.id, + "trigger_id": cls.write_trigger.id, + "model_id": cls.env["ir.model"]._get_id("edi.exchange.consumer.test"), + "snippet_do": "record._edi_send_via_edi(conf.type_id)", + } + ) + + def test_edi_send_via_edi_config(self): + # Check configuration on create + self.consumer_record.invalidate_recordset() + exchange_record = self.consumer_record.exchange_record_ids + self.assertEqual(len(exchange_record), 1) + self.assertEqual(exchange_record.type_id, self.exchange_type_out) + self.assertEqual(exchange_record.edi_exchange_state, "output_sent") + # Write the existed consumer record + self.consumer_record.name = "Fixed Consumer" + # check Configuration on write + self.consumer_record.invalidate_recordset() + exchange_record = self.consumer_record.exchange_record_ids - exchange_record + self.assertEqual(len(exchange_record), 1) + self.assertEqual(exchange_record.type_id, self.exchange_type_out) + self.assertEqual(exchange_record.edi_exchange_state, "output_sent") + + def test_edi_code_snippet(self): + expected_value = { + "todo": True, + "snippet_do_vars": { + "a": 1, + "b": 2, + }, + "event_only": True, + "tracked_fields": ["state"], + "edi_action": "new_action", + } + # Simulate the snippet_before_do + self.write_config.snippet_before_do = "result = " + str(expected_value) + # Execute with the raw data + vals = self.write_config.edi_exec_snippet_before_do( + self.consumer_record, + tracked_fields=[], + edi_action="generate", + ) + # Check the new vals after execution + expected_value["conf"] = self.write_config + self.assertEqual(vals, expected_value) + + # Check the snippet_do + expected_value = { + "change_state": True, + "snippet_do_vars": { + "a": 1, + "b": 2, + }, + "record": self.consumer_record, + "tracked_fields": ["state"], + } + snippet_do = """\n +old_state = old_value.get("state", False)\n +new_state = vals.get("state", False)\n +change_state = True if old_state and new_state and old_state != new_state else False +result = {\n + "change_state": change_state,\n + "snippet_do_vars": snippet_do_vars,\n + "record": record,\n + "tracked_fields": tracked_fields,\n +} + """ + self.write_config.snippet_do = snippet_do + # Execute with the raw data + record_id = self.consumer_record.id + vals = self.write_config.edi_exec_snippet_do( + self.consumer_record, + tracked_fields=[], + edi_action="generate", + old_vals={record_id: dict(state="draft")}, + vals={record_id: dict(state="confirmed")}, + ) + # Check the new vals after execution + self.assertEqual(vals, expected_value) diff --git a/edi_core_oca/tests/test_exchange_type.py b/edi_core_oca/tests/test_exchange_type.py new file mode 100644 index 000000000..34b8bc958 --- /dev/null +++ b/edi_core_oca/tests/test_exchange_type.py @@ -0,0 +1,169 @@ +# Copyright 2020 ACSONE +# Copyright 2022 Camptocamp SA (https://www.camptocamp.com). +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from freezegun import freeze_time + +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + + +class EDIExchangeTypeTestCase(EDIBackendCommonTestCase): + def test_copy(self): + new_type = self.exchange_type_out.copy() + self.assertEqual(new_type.code, f"{self.exchange_type_out.code}_COPY_FIXME") + self.assertEqual( + new_type.backend_type_id, self.exchange_type_out.backend_type_id + ) + self.assertEqual(new_type.backend_id, self.exchange_type_out.backend_id) + + def test_ack_for(self): + self.assertEqual(self.exchange_type_out.ack_type_id, self.exchange_type_out_ack) + new_type = self.exchange_type_out.copy({"code": "just_a_test"}) + self.assertEqual(new_type.ack_type_id, self.exchange_type_out_ack) + self.exchange_type_out_ack.invalidate_recordset() + self.assertIn( + self.exchange_type_out.id, + self.exchange_type_out_ack.ack_for_type_ids.ids, + ) + self.assertIn( + new_type.id, + self.exchange_type_out_ack.ack_for_type_ids.ids, + ) + + @mute_logger("odoo.sql_db") + def test_same_code_same_backend(self): + with mute_logger("odoo.sql_db"): + with self.assertRaises(Exception) as err: + self.exchange_type_in.copy({"code": "test_csv_input"}) + err_msg = err.exception.args[0] + self.assertTrue( + err_msg.startswith("duplicate key value violates unique constraint") + ) + + def test_same_code_different_backend(self): + new_backend = self.backend.copy() + new_type = self.exchange_type_in.copy( + {"backend_id": new_backend.id, "code": "test_csv_input"} + ) + self.assertEqual(new_type.code, self.exchange_type_in.code) + self.assertEqual( + new_type.backend_type_id, self.exchange_type_in.backend_type_id + ) + self.assertNotEqual(new_type.backend_id, self.exchange_type_in.backend_id) + + def test_advanced_settings(self): + settings = """ + components: + foo: this + boo: that + whatever: + ok: True + """ + self.exchange_type_out.advanced_settings_edit = settings + # fmt:off + self.assertEqual(self.exchange_type_out.advanced_settings, { + "components": { + "foo": "this", + "boo": "that", + }, + "whatever": { + "ok": True, + } + }) + # fmt:on + + def _test_exchange_filename(self, wanted_filename): + filename = self.exchange_type_out._make_exchange_filename( + exchange_record=self.env["edi.exchange.record"] + ) + self.assertEqual(filename, wanted_filename) + + @freeze_time("2022-04-28 08:37:24") + def test_filename_pattern_settings(self): + """ + Test filename pattern defined into advanced settings. + + Example of pattern: + filename_pattern: + force_tz: Europe/Rome + date_pattern: %Y-%m-%d-%H-%M-%S + """ + + self.env.user.tz = None + self.exchange_type_out.write( + { + "exchange_filename_pattern": "Test-File", + "exchange_file_ext": "csv", + "advanced_settings_edit": None, + } + ) + + # Test without any settings and minimal filename pattern + self._test_exchange_filename("Test-File.csv") + + # Test without extension for filename pattern + self.exchange_type_out.exchange_file_ext = False + self._test_exchange_filename("Test-File") + + # Test with datetime in filename pattern + self.exchange_type_out.exchange_file_ext = "csv" + self.exchange_type_out.exchange_filename_pattern = "Test-File-{dt}" + self._test_exchange_filename("Test-File-2022-04-28-083724.csv") + + # Add timezone on current user + self.env.user.tz = "America/New_York" # New_York time is -4h + self._test_exchange_filename("Test-File-2022-04-28-043724.csv") + + # Force date pattern on advanced settings + self.exchange_type_out.advanced_settings_edit = """ + filename_pattern: + date_pattern: '%Y-%m-%d-%H' + """ + self._test_exchange_filename("Test-File-2022-04-28-04.csv") + + # Force timezone on advanced settings + self.exchange_type_out.advanced_settings_edit = """ + filename_pattern: + # Rome time is +2h + force_tz: Europe/Rome + """ + self._test_exchange_filename("Test-File-2022-04-28-103724.csv") + + # Force date pattern and timezone on advanced settings + self.exchange_type_out.advanced_settings_edit = """ + filename_pattern: + # Rome time is +2h + force_tz: Europe/Rome + date_pattern: '%Y-%m-%d-%H-%M' + """ + self._test_exchange_filename("Test-File-2022-04-28-10-37.csv") + + # Test with sequence in filename pattern + self.exchange_type_out.exchange_filename_pattern = "Test-File-{seq}" + self.exchange_type_out.exchange_filename_sequence_id = self.sequence + self._test_exchange_filename("Test-File-0000001.csv") + + def test_archive_rules(self): + exc_type = self.exchange_type_out + rule1 = exc_type.rule_ids.create( + { + "type_id": exc_type.id, + "name": "Fake partner rule", + "model_id": self.env["ir.model"]._get("res.partner").id, + } + ) + rule2 = exc_type.rule_ids.create( + { + "type_id": exc_type.id, + "name": "Fake user rule", + "model_id": self.env["ir.model"]._get("res.users").id, + } + ) + exc_type.active = False + rule1.invalidate_recordset() + rule2.invalidate_recordset() + self.assertFalse(rule1.active) + self.assertFalse(rule2.active) diff --git a/edi_core_oca/tests/test_exchange_type_configuration.py b/edi_core_oca/tests/test_exchange_type_configuration.py new file mode 100644 index 000000000..cda251c34 --- /dev/null +++ b/edi_core_oca/tests/test_exchange_type_configuration.py @@ -0,0 +1,67 @@ +# Copyright 2024 ForgeFlow S.L. (https://www.forgeflow.com) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.orm.model_classes import add_to_registry + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestOutputCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_output", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution, EdiTestExecutionExtra + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + add_to_registry(cls.registry, EdiTestExecutionExtra) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution.extra"]) + cls.registry.init_models( + cls.env.cr, + ["edi.framework.test.execution.extra"], + {"models_to_check": True}, + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.addClassCleanup( + cls.registry.__delitem__, "edi.framework.test.execution.extra" + ) + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.ExecutionAbstractModelExtra = cls.env["edi.framework.test.execution.extra"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModelExtra.reset_faked("validate") + + def test_multiple_configuration(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + record.type_id.generate_model_id = self.env["ir.model"].search( + [("model", "=", "edi.framework.test.execution.extra")] + ) + record.action_exchange_generate() + self.assertFalse( + self.ExecutionAbstractModel.check_called_for(record, "generate") + ) + self.assertTrue( + self.ExecutionAbstractModelExtra.check_called_for(record, "generate") + ) diff --git a/edi_core_oca/tests/test_exchange_type_encoding.py b/edi_core_oca/tests/test_exchange_type_encoding.py new file mode 100644 index 000000000..d87a2b8a9 --- /dev/null +++ b/edi_core_oca/tests/test_exchange_type_encoding.py @@ -0,0 +1,98 @@ +# Copyright 2024 ForgeFlow S.L. (https://www.forgeflow.com) +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). +import base64 + +import chardet + +from odoo.orm.model_classes import add_to_registry + +from .common import EDIBackendCommonTestCase + + +class EDIBackendTestOutputCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + vals = { + "model": cls.partner._name, + "res_id": cls.partner.id, + } + cls.record = cls.backend.create_record("test_csv_output", vals) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + + def test_encoding_default(self): + """ + Test default output/input encoding (UTF-8). Use string with special + character to test the encoding applied. + """ + self.backend.with_context(fake_output="Palmotićeva").exchange_generate( + self.record + ) + # Test decoding is applied correctly + self.assertEqual(self.record._get_file_content(), "Palmotićeva") + # Test encoding used + content = base64.b64decode(self.record.exchange_file) + encoding = chardet.detect(content)["encoding"].lower() + self.assertEqual(encoding, "utf-8") + + def test_encoding(self): + """ + Test specific output/input encoding. Use string with special + character to test the encoding applied. + """ + self.exchange_type_out.write({"encoding": "UTF-16"}) + self.backend.with_context(fake_output="Palmotićeva").exchange_generate( + self.record + ) + # Test decoding is applied correctly + self.assertEqual(self.record._get_file_content(), "Palmotićeva") + # Test encoding used + content = base64.b64decode(self.record.exchange_file) + encoding = chardet.detect(content)["encoding"].lower() + self.assertEqual(encoding, "utf-16") + + def test_encoding_error_handler(self): + self.exchange_type_out.write({"encoding": "ascii"}) + # By default, error handling raises error + with self.assertRaises(UnicodeEncodeError): + self.backend.with_context(fake_output="Palmotićeva").exchange_generate( + self.record + ) + self.exchange_type_out.write({"encoding_out_error_handler": "ignore"}) + self.backend.with_context(fake_output="Palmotićeva").exchange_generate( + self.record + ) + self.assertEqual(self.record._get_file_content(), "Palmotieva") + + def test_decoding_error_handler(self): + self.backend.with_context(fake_output="Palmotićeva").exchange_generate( + self.record + ) + # Change encoding to ascii to check the decoding + self.exchange_type_out.write({"encoding": "ascii"}) + # By default, error handling raises error + with self.assertRaises(UnicodeDecodeError): + content = self.record._get_file_content() + self.exchange_type_out.write({"encoding_in_error_handler": "ignore"}) + content = self.record._get_file_content() + self.assertEqual(content, "Palmotieva") diff --git a/edi_core_oca/tests/test_frontend.py b/edi_core_oca/tests/test_frontend.py new file mode 100644 index 000000000..5acaa4389 --- /dev/null +++ b/edi_core_oca/tests/test_frontend.py @@ -0,0 +1,17 @@ +# Copyright 2025 Dixmit +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +import odoo + +from odoo.addons.web.tests.test_js import WebSuite + + +@odoo.tests.tagged("post_install", "-at_install") +class TestEdiCoreOCAFrontend(WebSuite): + """Test EDI Core OCA Frontend""" + + def get_hoot_filters(self): + self._test_params = [("+", "@edi_core_oca")] + return super().get_hoot_filters() + + def test_edi_core_oca(self): + self.test_unit_desktop() diff --git a/edi_core_oca/tests/test_quick_exec.py b/edi_core_oca/tests/test_quick_exec.py new file mode 100644 index 000000000..796dca002 --- /dev/null +++ b/edi_core_oca/tests/test_quick_exec.py @@ -0,0 +1,132 @@ +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 +from unittest import mock + +from odoo.orm.model_classes import add_to_registry +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + +LOGGERS = ( + "odoo.addons.edi_core_oca.models.edi_backend", + "odoo.addons.queue_job.delay", + "odoo.addons.edi_exchange_template_oca.models.edi_backend", +) + + +class EDIQuickExecTestCase(EDIBackendCommonTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.partner2 = cls.env["res.partner"].create({"name": "EDI EXC TEST 2"}) + cls.partner3 = cls.env["res.partner"].create({"name": "EDI EXC TEST 3"}) + + @classmethod + def _setup_records(cls): # pylint:disable=missing-return + super()._setup_records() + # Load fake models ->/ + from .fake_models import EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + cls.ExecutionAbstractModel = cls.env["edi.framework.test.execution"] + cls.registry._setup_models__(cls.env.cr, ["edi.framework.test.execution"]) + cls.registry.init_models( + cls.env.cr, ["edi.framework.test.execution"], {"models_to_check": True} + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.model = cls.env["ir.model"]._get("edi.framework.test.execution") + cls.exchange_type_out.generate_model_id = cls.model + cls.exchange_type_out.send_model_id = cls.model + cls.exchange_type_out.output_validate_model_id = cls.model + cls.exchange_type_in.generate_model_id = cls.model + cls.exchange_type_in.process_model_id = cls.model + cls.exchange_type_in.input_validate_model_id = cls.model + + def setUp(self): + super().setUp() + self.ExecutionAbstractModel.reset_faked("generate") + self.ExecutionAbstractModel.reset_faked("send") + self.ExecutionAbstractModel.reset_faked("check") + self.ExecutionAbstractModel.reset_faked("process") + + @mute_logger(*LOGGERS) + def test_quick_exec_on_create_no_call(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + model = self.env["edi.exchange.record"] + # quick exec is off, we should not get any call + with mock.patch.object(type(model), "_execute_next_action") as mocked: + record0 = self.backend.create_record("test_csv_output", vals) + mocked.assert_not_called() + self.assertEqual(record0.edi_exchange_state, "new") + # enabled but bypassed + self.exchange_type_out.exchange_file_auto_generate = True + self.exchange_type_out.quick_exec = True + with mock.patch.object(type(model), "_execute_next_action") as mocked: + record0 = self.backend.with_context( + edi__skip_quick_exec=True + ).create_record("test_csv_output", vals) + # quick exec is off, we should not get any call + mocked.assert_not_called() + self.assertEqual(record0.edi_exchange_state, "new") + + @mute_logger(*LOGGERS) + def test_quick_exec_on_create_out(self): + self.exchange_type_out.exchange_file_auto_generate = True + self.exchange_type_out.quick_exec = True + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record0 = self.backend.create_record("test_csv_output", vals) + # File generated and sent! + self.assertEqual(record0.edi_exchange_state, "output_sent") + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(record0, "generate") + ) + self.assertEqual( + record0._get_file_content(), self.ExecutionAbstractModel._call_key(record0) + ) + + @mute_logger(*LOGGERS) + def test_quick_exec_on_create_in(self): + self.exchange_type_in.quick_exec = True + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "exchange_file": base64.b64encode(b"1234"), + "edi_exchange_state": "input_received", + } + record0 = self.backend.create_record("test_csv_input", vals) + self.assertEqual(record0.edi_exchange_state, "input_processed") + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(record0, "process") + ) + + @mute_logger(*LOGGERS) + def test_quick_exec_on_retry(self): + self.exchange_type_in.quick_exec = True + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "edi_exchange_state": "input_processed_error", + "exchange_file": base64.b64encode(b"1234"), + } + record0 = self.backend.with_context(edi__skip_quick_exec=True).create_record( + "test_csv_input", vals + ) + self.assertEqual(record0.edi_exchange_state, "input_processed_error") + self.assertTrue(record0.retryable) + # get record w/ a clean context + record0 = self.backend.exchange_record_model.browse(record0.id) + record0.action_retry() + # The file has been rolled back and processed right away + self.assertEqual(record0.edi_exchange_state, "input_processed") + self.assertTrue( + self.ExecutionAbstractModel.check_called_for(record0, "process") + ) diff --git a/edi_core_oca/tests/test_record.py b/edi_core_oca/tests/test_record.py new file mode 100644 index 000000000..f423211f3 --- /dev/null +++ b/edi_core_oca/tests/test_record.py @@ -0,0 +1,246 @@ +# Copyright 2020 ACSONE +# Copyright 2022 Camptocamp SA +# @author: Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import base64 +from unittest import mock + +from freezegun import freeze_time + +from odoo import exceptions, fields +from odoo.tools import mute_logger + +from odoo.addons.edi_core_oca.utils import get_checksum + +from .common import EDIBackendCommonTestCase + + +class EDIRecordTestCase(EDIBackendCommonTestCase): + # TODO: test more + + def test_record_identifier(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + self.assertTrue(record.identifier.startswith(f"EDI/{fields.Date.today().year}")) + new_record = self.backend.create_record( + "test_csv_output", dict(vals, identifier=record.identifier) + ) + self.assertTrue( + new_record.identifier.startswith(f"EDI/{fields.Date.today().year}") + ) + self.assertNotEqual(new_record.identifier, record.identifier) + + def test_record_validate_state(self): + expected_err = "Exchange state must respect direction!" + with self.assertRaises(exceptions.ValidationError, msg=expected_err): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "edi_exchange_state": "output_pending", + } + self.backend.create_record("test_csv_input", vals) + + def test_record_same_type_code(self): + # Two record.exchange.type sharing same code "test_csv_input" + # Record should be created with the right backend + new_backend = self.backend.copy() + self.exchange_type_in.copy( + {"backend_id": new_backend.id, "code": "test_csv_input"} + ) + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + rec1 = self.backend.create_record("test_csv_input", vals) + rec2 = new_backend.create_record("test_csv_input", vals) + self.assertEqual(rec1.backend_id, self.backend) + self.assertEqual(rec2.backend_id, new_backend) + + def test_record_exchange_date(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "edi_exchange_state": "output_pending", + } + record = self.backend.create_record("test_csv_output", vals) + self.assertFalse(record.exchanged_on) + with freeze_time("2020-10-21 10:00:00"): + record.edi_exchange_state = "output_sent" + self.assertEqual( + fields.Datetime.to_string(record.exchanged_on), "2020-10-21 10:00:00" + ) + + @mute_logger("odoo.models.unlink") + def test_record_relation(self): + # create new one to delete it later + partner = self.partner.copy({"name": "Test EDI record rel"}) + vals = { + "model": partner._name, + "res_id": partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + self.assertEqual(record.related_name, partner.name) + record1 = self.backend.create_record( + "test_csv_output", dict(vals, parent_id=record.id) + ) + self.assertEqual(record1.related_name, partner.name) + record2 = self.backend.create_record( + "test_csv_output_ack", dict(vals, parent_id=record.id) + ) + self.assertEqual(record2.related_name, partner.name) + self.assertIn(record1, record.related_exchange_ids) + self.assertIn(record2, record.related_exchange_ids) + self.assertEqual(record.ack_exchange_id, record2) + # Check deletion + partner.unlink() + self.assertFalse(record1.record) + self.assertFalse(record1.related_name) + + def test_record_empty_with_parent(self): + """Simulate child record doesn't have a model and res_id. + + In this case the .record should return the record of the parent. + """ + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + record1 = self.backend.create_record( + "test_csv_output", dict(vals, parent_id=record.id) + ) + self.assertTrue(record1.model) + self.assertEqual(record.record, record1.record) + # Don't use the vals to lets empty model and res_id + record2 = self.backend.create_record( + "test_csv_output_ack", dict(parent_id=record.id) + ) + self.assertFalse(record2.model) + self.assertEqual(record.record, record2.record) + + def test_create_child(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record0 = self.backend.create_record("test_csv_output", vals) + record1 = record0.exchange_create_child_record() + record2 = record0.exchange_create_child_record() + record3 = record2.exchange_create_child_record(model="res.users", res_id=1) + record0.invalidate_recordset() + record2.invalidate_recordset() + self.assertIn(record1, record0.related_exchange_ids) + self.assertIn(record2, record0.related_exchange_ids) + self.assertIn(record3, record2.related_exchange_ids) + self.assertRecordValues( + record1 + record2 + record3, + [ + { + "parent_id": record0.id, + "model": "res.partner", + "res_id": self.partner.id, + }, + { + "parent_id": record0.id, + "model": "res.partner", + "res_id": self.partner.id, + }, + {"parent_id": record2.id, "model": "res.users", "res_id": 1}, + ], + ) + + def test_create_ack(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record0 = self.backend.create_record("test_csv_output", vals) + ack = record0.exchange_create_ack_record() + record0.invalidate_recordset() + self.assertIn(ack, record0.related_exchange_ids) + self.assertRecordValues( + ack, + [ + { + "parent_id": record0.id, + "model": "res.partner", + "res_id": self.partner.id, + "type_id": self.exchange_type_out_ack.id, + }, + ], + ) + ack2 = record0.exchange_create_ack_record() + self.assertEqual(record0.ack_exchange_id, ack2) + + def test_retry(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record0 = self.backend.create_record("test_csv_output", vals) + self.assertFalse(record0.retryable) + record0.edi_exchange_state = "output_error_on_send" + self.assertTrue(record0.retryable) + with mock.patch.object(type(record0), "_execute_next_action") as mocked: + record0.action_retry() + # quick exec is off, we should not get any call + mocked.assert_not_called() + self.assertEqual(record0.edi_exchange_state, "output_pending") + self.assertFalse(record0.retryable) + + def test_checksum(self): + filecontent = base64.b64encode(b"ABC") + checksum1 = get_checksum(filecontent) + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "exchange_file": filecontent, + } + record0 = self.backend.create_record("test_csv_output", vals) + self.assertEqual(record0.exchange_filechecksum, checksum1) + filecontent = base64.b64encode(b"DEF") + checksum2 = get_checksum(filecontent) + record0.exchange_file = filecontent + self.assertEqual(record0.exchange_filechecksum, checksum2) + self.assertNotEqual(record0.exchange_filechecksum, checksum1) + + def test_file_frozen(self): + filecontent = base64.b64encode(b"ABC") + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + "exchange_file": filecontent, + } + record0 = self.backend.create_record("test_csv_output", vals) + bypass_group = "edi_core_oca.group_edi_override_exchange_file_content" + self.assertTrue(self.env.user.has_group(bypass_group)) + self.assertFalse(record0.exchange_file_frozen) + record0.edi_exchange_state = "output_sent" + self.assertFalse(record0.exchange_file_frozen) + self.env.user.group_ids -= self.env.ref(bypass_group) + record0.invalidate_recordset() + self.assertTrue(record0.exchange_file_frozen) + + def test_related_records(self): + vals = { + "model": self.partner._name, + "res_id": self.partner.id, + } + record = self.backend.create_record("test_csv_output", vals) + self.assertEqual(record.record, self.partner) + self.assertTrue(record.related_record_ids) + self.assertEqual(record.related_record_ids.record, self.partner) + # We will link exchange record to another model record + spain = self.env.ref("base.es") + # Link consumer model to exchange record + record._set_related_record(spain) + # Main record is still the same as before + self.assertEqual(record.record, self.partner) + # Check related records + self.assertEqual(len(record.related_record_ids), 2) + self.assertEqual(record.related_record_ids[0].record, self.partner) + self.assertEqual(record.related_record_ids[1].record, spain) diff --git a/edi_core_oca/tests/test_security.py b/edi_core_oca/tests/test_security.py new file mode 100644 index 000000000..e7669f586 --- /dev/null +++ b/edi_core_oca/tests/test_security.py @@ -0,0 +1,240 @@ +# Copyright 2020 Dixmit +# @author: Enric Tobella +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +from odoo.exceptions import AccessError +from odoo.orm.model_classes import add_to_registry +from odoo.tools import mute_logger + +from .common import EDIBackendCommonTestCase + + +class TestEDIExchangeRecordSecurity(EDIBackendCommonTestCase): + @classmethod + def _setup_env(cls): + # Load fake models ->/ + from .fake_models import EdiExchangeConsumerTest, EdiTestExecution + + add_to_registry(cls.registry, EdiTestExecution) + add_to_registry(cls.registry, EdiExchangeConsumerTest) + cls.registry._setup_models__( + cls.env.cr, ["edi.framework.test.execution", "edi.exchange.consumer.test"] + ) + cls.registry.init_models( + cls.env.cr, + ["edi.framework.test.execution", "edi.exchange.consumer.test"], + {"models_to_check": True}, + ) + cls.addClassCleanup(cls.registry.__delitem__, "edi.framework.test.execution") + cls.addClassCleanup(cls.registry.__delitem__, "edi.exchange.consumer.test") + return super()._setup_env() + + # pylint: disable=W8110 + @classmethod + def _setup_records(cls): + super()._setup_records() + cls.group = cls.env["res.groups"].create({"name": "Demo Group"}) + model = cls.env["ir.model"]._get("edi.exchange.consumer.test") + cls.ir_access = cls.env["ir.model.access"].create( + { + "name": "model access", + "model_id": model.id, + "group_id": cls.group.id, + "perm_read": True, + "perm_write": True, + "perm_create": True, + "perm_unlink": True, + } + ) + cls.rule = cls.env["ir.rule"].create( + { + "name": "Exchange Record rule demo", + "model_id": model.id, + "domain_force": "[('name', '=', 'test')]", + "groups": [(4, cls.group.id)], + } + ) + cls.user = ( + cls.env["res.users"] + .with_context(no_reset_password=True, mail_notrack=True) + .create( + { + "name": "Poor Partner (not integrating one)", + "email": "poor.partner@ododo.com", + "login": "poorpartner", + "group_ids": [(6, 0, [cls.env.ref("base_edi.group_edi_user").id])], + } + ) + ) + cls.consumer_record = cls.env["edi.exchange.consumer.test"].create( + {"name": "test"} + ) + cls.exchange_type_out.exchange_filename_pattern = "{record.id}" + + def create_record(self, user=False): + vals = { + "model": self.consumer_record._name, + "res_id": self.consumer_record.id, + } + backend = self.backend + if user: + backend = backend.with_user(user) + return backend.create_record("test_csv_output", vals) + + def test_superuser_create(self): + exchange_record = self.create_record() + self.assertTrue(exchange_record) + + def test_group_create(self): + self.user.write({"group_ids": [(4, self.group.id)]}) + exchange_record = self.create_record() + self.assertTrue(exchange_record) + + @mute_logger("odoo.addons.base.models.ir_rule") + def test_rule_no_create(self): + self.user.write({"group_ids": [(4, self.group.id)]}) + self.consumer_record.name = "no_rule" + with self.assertRaisesRegex(AccessError, "doesn't have 'write' access to"): + self.create_record(self.user) + + @mute_logger("odoo.addons.base.models.ir_model") + def test_no_group_no_create(self): + with self.assertRaises(AccessError): + self.create_record(self.user) + + @mute_logger("odoo.addons.base.models.ir_model") + def test_no_group_no_read(self): + exchange_record = self.create_record() + with self.assertRaisesRegex(AccessError, "You are not allowed to access"): + exchange_record.with_user(self.user).read() + + @mute_logger("odoo.addons.base.models.ir_rule") + def test_rule_no_read(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.assertTrue(exchange_record.with_user(self.user).read()) + self.consumer_record.name = "no_rule" + with self.assertRaisesRegex( + AccessError, "Looks like you have stumbled upon some top-secret records" + ): + exchange_record.with_user(self.user).read() + + @mute_logger("odoo.addons.base.models.ir_model") + def test_no_group_no_unlink(self): + exchange_record = self.create_record() + with self.assertRaises(AccessError): + exchange_record.with_user(self.user).unlink() + + @mute_logger("odoo.models.unlink") + def test_group_unlink(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.assertTrue(exchange_record.with_user(self.user).unlink()) + + @mute_logger("odoo.addons.base.models.ir_rule") + def test_rule_no_unlink(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.consumer_record.name = "no_rule" + with self.assertRaisesRegex(AccessError, "doesn't have 'write' access to"): + exchange_record.with_user(self.user).unlink() + + def test_no_group_no_search(self): + exchange_record = self.create_record() + self.assertEqual( + 0, + self.env["edi.exchange.record"] + .with_user(self.user) + .search_count([("id", "=", exchange_record.id)]), + ) + + def test_group_search(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.assertEqual( + 1, + self.env["edi.exchange.record"] + .with_user(self.user) + .search_count([("id", "=", exchange_record.id)]), + ) + + def test_rule_no_search(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.consumer_record.name = "no_rule" + self.assertEqual( + 0, + self.env["edi.exchange.record"] + .with_user(self.user) + .search_count([("id", "=", exchange_record.id)]), + ) + + def test_search_no_record(self): + # Consumer record no longer exists: + # exchange_record is hidden in search + exchange_record = self.create_record() + exchange_record.res_id = -1 + self.user.write({"group_ids": [(4, self.group.id)]}) + logger_name = "odoo.addons.edi_core_oca.models.edi_exchange_record" + expected_msg = ( + f"WARNING:{logger_name}:" + f"Deleted record {exchange_record.model},{exchange_record.res_id} " + f"is referenced by edi.exchange.record [{exchange_record.id}]" + ) + with self.assertLogs(logger_name, "WARNING") as watcher: + self.assertEqual( + 0, + self.env["edi.exchange.record"] + .with_user(self.user) + .search_count([("id", "=", exchange_record.id)]), + ) + self.assertEqual(watcher.output, [expected_msg]) + + def test_search_no_record_admin(self): + # Consumer record no longer exists: + # user with group "Settings" has access + exchange_record = self.create_record() + exchange_record.res_id = -1 + admin_group = self.env.ref("base.group_system") + self.user.write({"group_ids": [(4, self.group.id), (4, admin_group.id)]}) + logger_name = "odoo.addons.edi_core_oca.models.edi_exchange_record" + with self.assertLogs(logger_name, "WARNING"): + self.assertEqual( + 1, + self.env["edi.exchange.record"] + .with_user(self.user) + .search_count([("id", "=", exchange_record.id)]), + ) + + @mute_logger("odoo.addons.base.models.ir_model") + def test_no_group_no_write(self): + exchange_record = self.create_record() + with self.assertRaises(AccessError): + exchange_record.with_user(self.user).write({"external_identifier": "1234"}) + + def test_group_write(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + exchange_record.with_user(self.user).write({"external_identifier": "1234"}) + self.assertEqual(exchange_record.external_identifier, "1234") + + @mute_logger("odoo.addons.base.models.ir_rule") + def test_rule_no_write(self): + exchange_record = self.create_record() + self.user.write({"group_ids": [(4, self.group.id)]}) + self.consumer_record.name = "no_rule" + with self.assertRaisesRegex(AccessError, "doesn't have 'write' access"): + exchange_record.with_user(self.user).write({"external_identifier": "1234"}) + + @mute_logger("odoo.addons.base.models.ir_model") + def test_no_group_no_read_child(self): + exchange_record = self.create_record() + model = self.consumer_record + # Create child record without specific model and res_id + # It should follow the access rights of the parent + child_exchange_record = self.backend.create_record( + "test_csv_output", {"parent_id": exchange_record.id} + ) + msg = rf"not allowed to access '{model._description}' \({model._name}\)" + with self.assertRaisesRegex(AccessError, msg): + child_exchange_record.with_user(self.user).read() diff --git a/edi_core_oca/utils.py b/edi_core_oca/utils.py new file mode 100644 index 000000000..e9fe9a8c1 --- /dev/null +++ b/edi_core_oca/utils.py @@ -0,0 +1,14 @@ +# Copyright 2020 ACSONE SA +# @author Simone Orsi +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). + +import hashlib + + +def normalize_string(cls, a_string, sep="_"): + """Normalize given string, replace dashes with given separator.""" + return cls.env["ir.http"]._slugify(a_string).replace("-", sep) + + +def get_checksum(filecontent): + return hashlib.md5(filecontent).hexdigest() diff --git a/edi_core_oca/views/edi_backend_type_views.xml b/edi_core_oca/views/edi_backend_type_views.xml new file mode 100644 index 000000000..775839594 --- /dev/null +++ b/edi_core_oca/views/edi_backend_type_views.xml @@ -0,0 +1,55 @@ + + + + edi.backend.type + + + + + + + + + edi.backend.type + +
+ + + + + + +
+
+
+ + edi.backend.type + + + + + + + + + EDI Backend Type + ir.actions.act_window + edi.backend.type + list,form + + [] + {} + + + + + form + + + + + + list + + +
diff --git a/edi_core_oca/views/edi_backend_views.xml b/edi_core_oca/views/edi_backend_views.xml new file mode 100644 index 000000000..2f557e0d3 --- /dev/null +++ b/edi_core_oca/views/edi_backend_views.xml @@ -0,0 +1,115 @@ + + + + edi.backend + + + + + + + + + + edi.backend + +
+ + +
+
+
+
+ + + + + + + + + +
+
+
+
+ + edi.backend + + + + + + + + + + + + + EDI Backend + ir.actions.act_window + edi.backend + list,form + + [] + {'search_default_filter_all': 1} + + + + + form + + + + + + list + + +
diff --git a/edi_core_oca/views/edi_configuration_trigger_views.xml b/edi_core_oca/views/edi_configuration_trigger_views.xml new file mode 100644 index 000000000..2198d47c1 --- /dev/null +++ b/edi_core_oca/views/edi_configuration_trigger_views.xml @@ -0,0 +1,44 @@ + + + + edi.configuration.trigger + + + + + + + + + + + + edi.configuration.trigger + +
+ + + + + + + + + + + + + + +
+
+
+
diff --git a/edi_core_oca/views/edi_configuration_views.xml b/edi_core_oca/views/edi_configuration_views.xml new file mode 100644 index 000000000..053db7764 --- /dev/null +++ b/edi_core_oca/views/edi_configuration_views.xml @@ -0,0 +1,116 @@ + + + + edi.configuration.view.search + edi.configuration + + + + + + + + + + + + + + edi.configuration + + + + + + + + + + + + + + edi.configuration + +
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + EDI Configuration + ir.actions.act_window + edi.configuration + list,form + [] + + + + + form + + + + + + list + + +
diff --git a/edi_core_oca/views/edi_exchange_record_views.xml b/edi_core_oca/views/edi_exchange_record_views.xml new file mode 100644 index 000000000..be4e9e4eb --- /dev/null +++ b/edi_core_oca/views/edi_exchange_record_views.xml @@ -0,0 +1,419 @@ + + + + edi.exchange.record + + + + + + + + + + + + + + + + + + + edi.exchange.record + +
+
+
+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + The related record is not available anymore. + Consider deleting this record too or fixing its relation. + + + + + + + + + + + + + + + + + + + + + + + + + + + +