Initial commit: OCA Ai packages (4 packages)

This commit is contained in:
Ernad Husremovic 2025-08-29 15:43:05 +02:00
commit 0adb4b78b1
170 changed files with 12385 additions and 0 deletions

50
README.md Normal file
View file

@ -0,0 +1,50 @@
# OCA Ai
This repository contains **4** OCA packages for ai.
## Packages Included (4 packages)
- **odoo-bringout-oca-ai-ai_oca_bridge** - From ai: ai_oca_bridge
- **odoo-bringout-oca-ai-ai_oca_bridge_chatter** - From ai: ai_oca_bridge_chatter
- **odoo-bringout-oca-ai-ai_oca_bridge_document_page** - From ai: ai_oca_bridge_document_page
- **odoo-bringout-oca-ai-ai_oca_bridge_helpdesk_mgmt** - From ai: ai_oca_bridge_helpdesk_mgmt
## Installation
Install any package from this category:
```bash
# Install from local directory
pip install packages/oca-ai/PACKAGE_NAME/
# Install in development mode
pip install -e packages/oca-ai/PACKAGE_NAME/
# Using uv (recommended for speed)
uv add packages/oca-ai/PACKAGE_NAME/
```
## Repository Structure
Each package in this repository follows the standard Odoo addon structure:
```
oca-ai/
├── odoo-bringout-oca-PROJECT-ADDON/
│ ├── ADDON_NAME/ # Complete addon code
│ │ ├── __init__.py
│ │ ├── __manifest__.py
│ │ └── ... (models, views, etc.)
│ ├── pyproject.toml # Python package configuration
│ └── README.md # Package documentation
└── ...
```
## Contributing
These packages are maintained as part of the [OCA (Odoo Community Association)](https://github.com/OCA) ecosystem.
## License
Each package maintains its original license as specified in the OCA repositories.

View file

@ -0,0 +1,44 @@
# AI OCA Bridge
Odoo addon: ai_oca_bridge
## Installation
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge
```
## Dependencies
This addon depends on:
- mail
## Manifest Information
- **Name**: AI OCA Bridge
- **Version**: 16.0.2.0.1
- **Category**: AI
- **License**: AGPL-3
- **Installable**: False
## Source
Based on [OCA/ai](https://github.com/OCA/ai) branch 16.0, addon `ai_oca_bridge`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,205 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
=============
AI OCA Bridge
=============
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:95324eee7973f3fc8e883c34e3e72df6337726fe9eac34f04e68145eeaed22da
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github
:target: https://github.com/OCA/ai/tree/16.0/ai_oca_bridge
:alt: OCA/ai
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge
: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/ai&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module is used to create a bridge between Odoo and other AI systems
like n8n.
**Table of contents**
.. contents::
:local:
Use Cases / Context
===================
Right now, there are 2 different approaches for AI integration with
Odoo:
1. Make everything inside Odoo.
2. Make it using other tools and integrate Odoo with these tools.
IMO, it would be better to make use of option 2 for different reasons:
- Odoo server is intended as a transactional system. AI systems requires
other kind of characteristics
- Everything changes too fast. I am not confident that Odoo can keep the
pace in this topic
- There are OSS tools that fills the gap perfectly and are created just
for this topic.
Anyway, OCA is open to everyone and we don't intend to force an
opinionated way of doing. For this reason, we have this module, that can
be used as Bridge with AI systems.
Configuration
=============
As an administrator access ``AI Bridge\AI Bridge``.
Create a new bridge. Define the name, model, url and configuration.
In order to improve the view of the AI configuration, use groups and
domain to set better filters.
Payload Configuration
---------------------
On the external system, you will receive a POST payload. The data
included will be the following:
General
~~~~~~~
- \_odoo: Standard data to identify the Odoo Database
- \_model: Model of the related object
- \_id: Id of the related object
- \_response_url: Url to call with the response in case of async calls
Record Payload
~~~~~~~~~~~~~~
Adds a new item called record with all the fields.
Record Payload (v0)
~~~~~~~~~~~~~~~~~~~
Adds all the fields directly on the payload. It will be removed on 17.0.
Asynchronous and synchronous calls
----------------------------------
The new system allows asynchronous and synchronous calls. Asynchronous
calls makes sense when the task to be processed don't need to be
immediate. For example, reviewing an invoice and leave a comment with
the result. The same would happen with a chat message. We expect that
the system will leave time to the AI to answer and Odoo's user can do
other things.
Meanwhile, Synchronous calls will froze odoo system and wait for an
answer. This makes sense when we expect some feedback from odoo user. It
makes sense, when we open an action for example.
In the synchronous call, the result is processed when the AI system
answers on the webhook. On the other hand, it will be processed
automatically on the synchronous call.
Result processing
-----------------
With the answers of the system we expect to do something about it. We
have the following options:
No processing
~~~~~~~~~~~~~
In this case, the result will do nothing
Post a Message
~~~~~~~~~~~~~~
We will post a message on the original thread of the system. The thread
is computed by a function, so it can be overriden in future modules. It
expects the keyword arguments of the ``message_post`` function.
Action
~~~~~~
It expects to launch an action on the user interface. It only makes
sense on synchronous calls.
It expects an action item with the following parameters:
- action: xmlid of the action
- context: Context to pass to the action (not required)
- res_id: Id of the resource (not required)
Usage
=====
Use the bolt widget in the chatter to execute the different AI options.
The options will be filtered according to the configuration.
Known issues / Roadmap
======================
- Define examples to use and import
- Allow child fields. Right now, only first level fields are accepted.
- Information popover is not working properly when there is large data.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/ai/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Dixmit
Contributors
------------
- `Dixmit <https://www.dixmit.com>`__
- Enric Tobella
- `Sygel Technology <https://www.sygel.es>`__
- Valentín Vinagre
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/ai <https://github.com/OCA/ai/tree/16.0/ai_oca_bridge>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,2 @@
from . import controllers
from . import models

View file

@ -0,0 +1,35 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "AI OCA Bridge",
"summary": """Makes a basic configuration to be used as bridge with external AI systems""",
"version": "16.0.2.0.1",
"license": "AGPL-3",
"author": "Dixmit,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/ai",
"category": "AI",
"development_status": "Beta",
"depends": ["mail"],
"data": [
"data/ir_module_category.xml",
"security/ir.model.access.csv",
"security/security.xml",
"views/menu.xml",
"views/ai_bridge_execution.xml",
"views/ai_bridge.xml",
],
"assets": {
"web.assets_backend": [
"ai_oca_bridge/static/src/**/*.xml",
"ai_oca_bridge/static/src/**/*.esm.js",
],
"web.qunit_suite_tests": [
"ai_oca_bridge/static/tests/web/**/*.esm.js",
],
"web.tests_assets": [
"ai_oca_bridge/static/tests/helpers/**/*.esm.js",
],
},
"application": True,
}

View file

@ -0,0 +1 @@
from . import ai

View file

@ -0,0 +1,48 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import json
from odoo import fields, http
from odoo.http import request
from odoo.tools import consteq
from odoo.tools.translate import _
class AIController(http.Controller):
@http.route(
[
"/ai/response/<int:execution_id>/<string:token>",
],
type="http",
auth="public",
cors="*",
csrf=False,
)
def ai_process_response(self, execution_id, token):
execution = request.env["ai.bridge.execution"].sudo().browse(execution_id)
if not execution.exists():
return request.make_response(_("Execution not found."), status=404)
if not consteq(execution._generate_token(), token):
return request.make_response(
_("Token is not allowed for this execution."), status=404
)
if (
not execution.expiration_date
or execution.expiration_date < fields.Datetime.now()
):
return request.make_response(_("Execution is expired."), status=404)
return request.make_response(
json.dumps(
execution._process_response(
json.loads(
request.httprequest.get_data().decode(
request.httprequest.charset
)
)
)
),
headers=[
("Content-Type", "application/json"),
],
)

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.module.category" id="module_category_ai">
<field name="name">AI</field>
<field name="sequence">99</field>
</record>
</odoo>

View file

@ -0,0 +1,726 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""

View file

@ -0,0 +1,726 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr "%s uspešno izvršeno."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr "%s neuspešno."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr "%s nije aktivno."
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr "AI"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr "AI most"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr "AI most izvršen"
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr "AI most izvršavanje"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr "AI most neuspešan"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr "AI most neaktivan"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr "AI most mixin"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr "AI izvršavanje"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr "AI nit kreiranje"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr "AI nit ukloni"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr "AI nit piši"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr "Akcija"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr "Potrebna akcija"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr "Aktivan"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr "Aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr "Dekoracija iznimke aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr "Status aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr "Ikona tipa aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr "Ai most"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Konfiguracija AI mosta"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr "Ai most informacije"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ai izvršavanje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr "Ai upotreba"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr "Arhivirano"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr "Asinhroni timeout"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr "Asinhrono"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr "Broj priloga"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr "Auth lozinka"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr "Auth token"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr "Auth korisničko ime"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr "Tip autentifikacije"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr "Osnovna autentifikacija"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr "Preduzeće"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr "Kreirao"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr "Kreirano"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr "Definiše tip rezultata očekivanog od AI sistema."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr "Opis"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr "Prikazani naziv"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr "Gotovo"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr "U pripremi"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr "Nit e-pošte"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr "Greška"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr "Izvršavanje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr "Broj izvršavanja"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr "Detalji izvršavanja"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr "Izvršavanje je isteklo."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr "Izvršavanje nije pronađeno."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr "Datum isteka"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr "Datum isteka za token asinhrone operacije."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr "Polje"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr "Polja za uključivanje u AI most."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr "Filter"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr "Pratioci"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr "Pratioci (Partneri)"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr "Font awesome ikona npr. fa-tasks"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr "Grupa"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr "Ima poruku"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr "ID"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr "Ikona"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr "Ikona za prikaz iznimki."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr "Ako je zakačeno, nove poruke će zahtjevati vašu pažnju"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr "Ako je označeno neke poruke mogu imati grešku u dostavi."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr "Trenutno"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr "Je Ai most nit"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr "Pratilac"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr "Zadnje mijenjano"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr "Zadnji ažurirao"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr "Zadnje ažurirano"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr "Glavna zakačka"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr "Greška pri isporuci poruke"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr "Poruke"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr "Model"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr "Naziv modela"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr "Model obavezan"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr "Modeli"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr "Rok za moju aktivnost"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr "Naziv:"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr "Krajnji rok za sljedeću aktivnost"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr "Pregled sljedeće aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr "Tip sljedeće aktivnosti"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr "Nema korisnog tereta"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr "Nema obrade"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr "Broj akcija"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr "Broj grešaka"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr "Broj poruka koje zahtijevaju aktivnost"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "Broj poruka sa greškama pri isporuci"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr "Korisni teret"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr "Korisni teret tekst"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tip korisnog tereta"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr "Objavi poruku"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr "Zapis"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr "Korisni teret zapisa"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr "Zapis v0"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr "Res"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr "Odgovor"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr "Odgovorni korisnik"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr "Rezultat"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr "Vrsta rezultata"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr "Tip rezultata"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr "Uzorak"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr "Uzorak korisnog tereta"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr "Sekvenca"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr "Status"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr "URL spoljašnjeg AI sistema na koji se ovaj most povezuje."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr "Model sa kojim je ovaj most povezan."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr "Tip autentifikacije koji se koristi za povezivanje sa spoljašnjim AI sistemom."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr "Korisnik koji će biti prikazan pri izvršavanju ovog AI mosta."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr "Nit"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr "Token autentifikacija"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr "Token nije dozvoljen za ovo izvršavanje."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr "Vrsta aktivnosti iznimke na zapisu."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr "URL"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr "Nepodržan tip autentifikacije."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr "Upotreba"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr "Korisnik"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr "Korisničke grupe kojima je dozvoljeno korišćenje ovog AI mosta."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr "Poruke sa website-a"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr "Povijest komunikacije Web stranice"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""

View file

@ -0,0 +1,754 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-15 22:35+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\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"
"X-Generator: Weblate 5.10.4\n"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
"\n"
" Define cómo se procesa el resultado del sistema de IA.\n"
" - 'Inmediato': el resultado se procesa inmediatamente después de que "
"el sistema de IA responda.\n"
" - 'Asíncrono': El resultado se procesa en segundo plano.\n"
" Permite operaciones más largas.\n"
" Odoo proporcionará una URL al sistema de IA donde se enviará la "
"respuesta.\n"
" Los usuarios recibirán una notificación cuando se inicie la "
"operación.\n"
" No se enviará ninguna notificación cuando finalice.\n"
" "
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr "%s ejecutado correctamente."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr "%s falló."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr "%s no está activo."
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr "IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr "Puente de IA ejecutado"
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr "Ejecución de Puente de IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr "Puente de IA fallo"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr "Puente de IA inactivo"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr "Mezcla de puente de IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr "Crear hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr "Eliminar hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr "Escribir hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr "Acción"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr "Acción necesaria"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr "Activo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr "Actividades"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr "Decoración de excepción de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr "Estado de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr "Icono de tipo de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr "Información de puente de IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr "Uso de IA"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr "Archivado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr "Tiempo de espera asíncrono"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr "Asincrónica"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr "Conteo de anexos"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr "Contraseña de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr "Token de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr "Nombre de usuario de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr "Tipo de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr "Autenticación básica"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr "Compañía"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr "Creado el"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr "Define el tipo de resultado esperado del sistema de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr "Descripción"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr "Nombre a mostrar"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr "Hecho"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr "Borrador"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr "Hilo de correo electrónico"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr "Error"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr "Ejecución"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr "Recuento de ejecuciones"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr "Detalles de ejecución"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr "La ejecución ha expirado."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr "No se ha encontrado la ejecución."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr "Fecha de expiración"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr "Fecha de expiración del token de operación asincrónica."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr "Campo"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr "Campos que se incluirán en el puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr "Filtrar"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr "Seguidores"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr "Seguidores (Socios)"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr "Icono Font awesome, por ejemplo fa-tasks"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr "Grupo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr "Tiene mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr "ID"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr "Icono"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr "Icono para indicar una actividad de excepción."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr "Si está marcada, nuevos mensajes requieren su atención."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr "Si se activa, algunos mensajes tienen un error de envío."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr "Inmediato"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr "Es un hilo del puente IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr "Es seguidor"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr "Última modificación el"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr "Adjunto principal"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr "Error de entrega del mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr "Mensajes"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr "Modelo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr "Nombre del modelo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr "Modelo requerido"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr "Modelos"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr "Mi plazo de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr "Nombre"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr "Fecha límite para la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr "Resumen de la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr "Tipo de la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr "Sin carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr "Sin procesamiento"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr "Ninguno"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr "Número de acciones"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr "Número de errores"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr "Número de mensajes que requieren una acción"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "Número de mensajes con error de entrega"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr "Carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr "Carga útil de texto"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr "Deja un mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr "Registro"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr "Carga útil de registro"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr "Registro v0"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr "Respuesta"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr "Respuesta"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr "Usuario responsable"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr "Resultado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr "Tipo de resultado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr "Tipo de resultado"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr "Ejemplo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr "Carga útil de muestra"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
"Carga útil de muestra que se enviará al sistema de IA. Esto se utiliza con "
"fines de prueba y depuración."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr "Secuencia"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr "Estado"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
"Estado basado en actividades\n"
"Vencida: La fecha de vencimiento ya ha pasado\n"
"Hoy: La fecha de la actividad es hoy\n"
"Planificada: Actividades futuras."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr "La URL del sistema de IA externo al que se conecta este puente."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr "El modelo al que se asocia este puente."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr ""
"El tipo de autenticación utilizado para conectarse al sistema de IA externo."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr "El usuario que se mostrará al ejecutar este puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr "Hilo"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
"Tiempo de espera en segundos para operaciones asincrónicas. Si la operación "
"no se completa dentro de este tiempo, se considerará fallida."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr "Autenticación de token"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr "Token no está permitido para esta ejecución."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr "Tipo de la actividad de excepción registrada."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr "Dirección URL"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr "Tipo de autenticación no admitido."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr "Usuario"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr "Grupos de usuarios autorizados a utilizar este puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr "Mensajes del sitio web"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr "Historia de la comunicación en la web"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""
"Cuando el uso es \"Hilo Puente IA\", el tipo de carga útil debe ser "
"\"Sin carga útil\"."

View file

@ -0,0 +1,754 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-16 01:25+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\n"
"Language-Team: none\n"
"Language: es_VE\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: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
"\n"
" Define cómo se procesa el resultado del sistema de IA.\n"
" - 'Inmediato': el resultado se procesa inmediatamente después de que "
"el sistema de IA responda.\n"
" - 'Asíncrono': El resultado se procesa en segundo plano.\n"
" Permite operaciones más largas.\n"
" Odoo proporcionará una URL al sistema de IA donde se enviará la "
"respuesta.\n"
" Los usuarios recibirán una notificación cuando se inicie la "
"operación.\n"
" No se enviará ninguna notificación cuando finalice.\n"
" "
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr "%s ejecutado correctamente."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr "%s falló."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr "%s no está activo."
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr "IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr "Puente de IA ejecutado"
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr "Ejecución de Puente de IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr "Puente de IA fallo"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr "Puente de IA inactivo"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr "Mezcla de puente de IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr "Crear hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr "Eliminar hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr "Escribir hilo de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr "Acción"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr "Acción necesaria"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr "Activo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr "Actividades"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr "Decoración de excepción de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr "Estado de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr "Icono de tipo de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr "Información de puente de IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr "Uso de IA"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr "Archivado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr "Tiempo de espera asíncrono"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr "Asincrónica"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr "Conteo de anexos"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr "Contraseña de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr "Token de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr "Nombre de usuario de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr "Tipo de autenticación"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr "Autenticación básica"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr "Compañía"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr "Creado el"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr "Define el tipo de resultado esperado del sistema de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr "Descripción"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr "Nombre a mostrar"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr "Hecho"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr "Borrador"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr "Hilo de correo electrónico"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr "Error"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr "Ejecución"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr "Recuento de ejecuciones"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr "Detalles de ejecución"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr "La ejecución ha expirado."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr "No se ha encontrado la ejecución."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr "Fecha de expiración"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr "Fecha de expiración del token de operación asincrónica."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr "Campo"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr "Campos que se incluirán en el puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr "Filtrar"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr "Seguidores"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr "Seguidores (Socios)"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr "Icono Font awesome, por ejemplo fa-tasks"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr "Grupo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr "Tiene mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr "ID"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr "Icono"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr "Icono para indicar una actividad de excepción."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr "Si está marcada, nuevos mensajes requieren su atención."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr "Si se activa, algunos mensajes tienen un error de envío."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr "Inmediato"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr "Es un hilo del puente IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr "Es seguidor"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr "Última modificación el"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr "Adjunto principal"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr "Error de entrega del mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr "Mensajes"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr "Modelo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr "Nombre del modelo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr "Modelo requerido"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr "Modelos"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr "Mi plazo de actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr "Nombre"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr "Fecha límite para la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr "Resumen de la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr "Tipo de la próxima actividad"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr "Sin carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr "Sin procesamiento"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr "Ninguno"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr "Número de acciones"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr "Número de errores"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr "Número de mensajes que requieren una acción"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "Número de mensajes con error de entrega"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr "Carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr "Carga útil de texto"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr "Deja un mensaje"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr "Registro"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr "Carga útil de registro"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr "Registro v0"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr "Respuesta"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr "Respuesta"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr "Usuario responsable"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr "Resultado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr "Tipo de resultado"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr "Tipo de resultado"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr "Ejemplo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr "Carga útil de muestra"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
"Carga útil de muestra que se enviará al sistema de IA. Esto se utiliza con "
"fines de prueba y depuración."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr "Secuencia"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr "Estado"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
"Estado basado en actividades\n"
"Vencida: La fecha de vencimiento ya ha pasado\n"
"Hoy: La fecha de la actividad es hoy\n"
"Planificada: Actividades futuras."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr "La URL del sistema de IA externo al que se conecta este puente."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr "El modelo al que se asocia este puente."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr ""
"El tipo de autenticación utilizado para conectarse al sistema de IA externo."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr "El usuario que se mostrará al ejecutar este puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr "Hilo"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
"Tiempo de espera en segundos para operaciones asincrónicas. Si la operación "
"no se completa dentro de este tiempo, se considerará fallida."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr "Autenticación de token"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr "Token no está permitido para esta ejecución."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr "Tipo de la actividad de excepción registrada."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr "Dirección URL"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr "Tipo de autenticación no admitido."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr "Usuario"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr "Grupos de usuarios autorizados a utilizar este puente de IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr "Mensajes del sitio web"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr "Historia de la comunicación en la web"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""
"Cuando el uso es \"Hilo Puente IA\", el tipo de carga útil debe ser "
"\"Sin carga útil\"."

View file

@ -0,0 +1,754 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-07-31 10:59+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\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: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI "
"system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will "
"be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
"\n"
" Definisce come il risultato dal sistema IA è elaborato.\n"
" - 'Immediato': il risultato è elaborato immediatamente dopo la "
"risposta della IA.\n"
" - 'Asincrono': il risultato è elaborato in background.\n"
" Consente operazioni più lunghe.\n"
" Odoo fornirà un URL al sistema IA dove la risposta verrà inviata.\n"
" Gli utenti riceveranno una notifica quando si avvia l'operazione.\n"
" Non verrà inviata una notifica alla conclusione.\n"
" "
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr "%s eseguito con successo."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr "%s fallito."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr "%s non è attivo."
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr "IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr "Collegamento IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr "Collegamento IA eseguito"
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr "Esecuzione collegamento IA"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr "Collegamento IA fallito"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr "Collegamento IA inattivo"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr "Mixin bridge IA"
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr "Esecuzione IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr "Creazione thread IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr "Rilascio thread IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr "Scrittura thread IA"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr "Azione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr "Azione richiesta"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr "Attivo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr "Attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr "Decorazione eccezione attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr "Stato attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr "Icona tipo attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr "Collegamento IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configurazione collegamento IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr "Informazioni collegamento IA"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Esecuzione IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr "Utilizzo IA"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr "In archivio"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr "Timeout asincrono"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr "Asincrono"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr "Conteggio allegati"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr "Password autenticazione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr "Token autenticazione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr "Nome utente autenticazione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr "Tipo autenticazione"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr "Autenticazione base"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr "Azienda"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr "Creato da"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr "Creato il"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Definisce come viene usato questo collegamento. Se \"Discussione\", verrà "
"usato nel contesto della discussione e-mail."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr "Definisce il tipo di risultato che ci si aspetta dal sistema IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr "Descrizione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr "Nome visualizzato"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr "Eseguito"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr "Bozza"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr "Discussione e-mail"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr "Errore"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr "Esecuzione"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr "Conteggio esecuzioni"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr "Dettagli esecuzione"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr "L'esecuzione è scaduta."
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr "Esecuzione non trovata."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr "Data di scadenza"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr "Data scadenza per il token operazione asincrono."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr "Campo"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr "Campi da includere nel collegamento IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr "Filtro"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr "Seguito da"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr "Seguito da (partner)"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr "Icona Font Awesome es. fa-tasks"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr "Gruppo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr "Ha un messaggio"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr "ID"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr "Icona"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr "Icona per indicare un'attività eccezione."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr "Se selezionata, nuovi messaggi richiedono attenzione."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr "Se selezionata, alcuni messaggi hanno un errore di consegna."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr "Immediato"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr "È un thread IA"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr "Segue"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr "Ultima modifica il"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr "Ultimo aggiornamento di"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr "Ultimo aggiornamento il"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr "Allegato principale"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr "Errore di consegna messaggio"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr "Messaggi"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr "Modello"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr "Nome modello"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr "Modello richiesto"
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr "Modelli"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr "Scadenza mia attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr "Nome"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr "Scadenza prossima attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr "Riepilogo prossima attività"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr "Tipo prossima attività"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr "Nessun carico"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr "Nessuna elaborazione"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr "Nessuno"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr "Numero di azioni"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr "Numero di errori"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr "Numero di messaggi che richiedono un'azione"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr "Numero di messaggi con errore di consegna"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr "Carico"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr "Testo del carico"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo carico"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr "Invia un messaggio"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr "Record"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr "Record carico"
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr "Record v0"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr "Res"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr "Risposta"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr "Utente responsabile"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr "Risultato"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr "Genere risultato"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr "Tipo risultato"
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr "Esempio"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr "Carico esempio"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
"Carico esempio da inviare al sistema IA. Questo è usato per attività di test "
"e debug."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr "Sequenza"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr "Stato"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
"Stato in base alle attività\n"
"Scaduto: la data richiesta è trascorsa\n"
"Oggi: la data attività è oggi\n"
"Pianificato: attività future."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr "L'URL del sistema IA esterno a cui questo collegamento fa riferimento."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr "Modello a cui è associato questo collegamento."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr ""
"Il tipo di autenticazione utilizzato per connettere il sistema di IA esterno."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr "Utente da visualizzare quando si esegue questo collegamento IA."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr "Discussione"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
"Timeout in secondi per operazioni asincrone. Se l'operazione non si completa "
"entro questo tempo, verrà considerata fallita."
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr "Autenticazione token"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr "Il token non è autorizzato per questa esecuzione."
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr "Tipo di attività eccezione sul record."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr "URL"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr "Tipo autenticazione non supportato."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr "Uilizzo"
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr "Utente"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr "Gruppi utente autorizzati ad usare questo collegamento IA."
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr "Messaggi sito web"
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr "Cronologia comunicazioni sito web"
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""
"Quando l'utilizzo è 'Rilascio thread IA', il tipo di carico deve essere "
"'Nessun carico'."

View file

@ -0,0 +1,727 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\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: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_kind
msgid ""
"\n"
" Defines how the result from the AI system is processed.\n"
" - 'Immediate': The result is processed immediately after the AI system responds.\n"
" - 'Asynchronous': The result is processed in the background.\n"
" It allows longer operations.\n"
" Odoo will provide a URL to the AI system where the response will be sent.\n"
" Users will receive a notification when the operation is started.\n"
" No notification will be sent when it is finished.\n"
" "
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s executed successfully."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s failed."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "%s is not active."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.module.category,name:ai_oca_bridge.module_category_ai
msgid "AI"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_act_window
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_menu
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_root_menu
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Executed"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.ui.menu,name:ai_oca_bridge.ai_bridge_execution_menu
msgid "AI Bridge Execution"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Failed"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid "AI Bridge Inactive"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_thread
msgid "AI Bridge Mixin"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.actions.act_window,name:ai_oca_bridge.ai_bridge_execution_act_window
msgid "AI Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_create
msgid "AI Thread Create"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_unlink
msgid "AI Thread Unlink"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__ai_thread_write
msgid "AI Thread Write"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__action
msgid "Action"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "Action Needed"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__active
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Active"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_ids
msgid "Activities"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Activity Exception Decoration"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_state
msgid "Activity State"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Activity Type Icon"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__ai_bridge_id
msgid "Ai Bridge"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_document_page__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_helpdesk_ticket_team__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_channel__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_cc__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_mail_thread_phone__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_phone_blacklist__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_partner__ai_bridge_info
#: model:ir.model.fields,field_description:ai_oca_bridge.field_res_users__ai_bridge_info
msgid "Ai Bridge Info"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__ai_usage
msgid "Ai Usage"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_search_view
msgid "Archived"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__async_timeout
msgid "Async Timeout"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__async
msgid "Asynchronous"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_attachment_count
msgid "Attachment Count"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_password
msgid "Auth Password"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_token
msgid "Auth Token"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_username
msgid "Auth Username"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__auth_type
msgid "Authentication Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__basic
msgid "Basic Authentication"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__company_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__company_id
msgid "Company"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_uid
msgid "Created by"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__create_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__create_date
msgid "Created on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__result_type
msgid "Defines the type of result expected from the AI system."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__description
msgid "Description"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__display_name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__display_name
msgid "Display Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__done
msgid "Done"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__draft
msgid "Draft"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_mail_thread
msgid "Email Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__error
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge_execution__state__error
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_ids
msgid "Execution"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__execution_count
msgid "Execution Count"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Execution Details"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution is expired."
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Execution not found."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration Date"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge_execution__expiration_date
msgid "Expiration date for the async operation token."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Field"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__field_ids
msgid "Fields to include in the AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__domain
msgid "Filter"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_follower_ids
msgid "Followers"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_partner_ids
msgid "Followers (Partners)"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_type_icon
msgid "Font awesome icon e.g. fa-tasks"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__group_ids
msgid "Group"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__has_message
msgid "Has Message"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__id
msgid "ID"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_icon
msgid "Icon to indicate an exception activity."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction
msgid "If checked, new messages require your attention."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "If checked, some messages have a delivery error."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_kind__immediate
msgid "Immediate"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ir_model__is_ai_bridge_thread
msgid "Is Ai Bridge Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_is_follower
msgid "Is Follower"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge____last_update
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution____last_update
msgid "Last Modified on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_uid
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_uid
msgid "Last Updated by"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__write_date
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__write_date
msgid "Last Updated on"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_main_attachment_id
msgid "Main Attachment"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error
msgid "Message Delivery error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_ids
msgid "Messages"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_id
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__model_id
msgid "Model"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model
msgid "Model Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__model_required
msgid "Model Required"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model,name:ai_oca_bridge.model_ir_model
msgid "Models"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__my_activity_date_deadline
msgid "My Activity Deadline"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__name
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__name
msgid "Name"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_date_deadline
msgid "Next Activity Deadline"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_summary
msgid "Next Activity Summary"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_type_id
msgid "Next Activity Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__none
msgid "No payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__none
msgid "No processing"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__none
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__none
msgid "None"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of Actions"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of errors"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_needaction_counter
msgid "Number of messages requiring action"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__message_has_error_counter
msgid "Number of messages with delivery error"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__payload_txt
msgid "Payload Txt"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__result_type__message
msgid "Post a Message"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record
msgid "Record"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Record Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__payload_type__record_v0
msgid "Record v0"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__res_id
msgid "Res"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_execution_form_view
msgid "Response"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__activity_user_id
msgid "Responsible User"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__result
msgid "Result"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_kind
msgid "Result Kind"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__result_type
msgid "Result Type"
msgstr ""
#. module: ai_oca_bridge
#: model_terms:ir.ui.view,arch_db:ai_oca_bridge.ai_bridge_form_view
msgid "Sample"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sample_payload
msgid "Sample Payload"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__sample_payload
msgid ""
"Sample payload to be sent to the AI system. This is used for testing and "
"debugging purposes."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__sequence
msgid "Sequence"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge_execution__state
msgid "State"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_state
msgid ""
"Status based on activities\n"
"Overdue: Due date is already passed\n"
"Today: Activity date is today\n"
"Planned: Future activities."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__url
msgid "The URL of the external AI system to which this bridge connects."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__model_id
msgid "The model to which this bridge is associated."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__auth_type
msgid "The type of authentication used to connect to the external AI system."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__user_id
msgid "The user that will be shown when executing this AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__usage__thread
msgid "Thread"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__async_timeout
msgid ""
"Timeout in seconds for asynchronous operations. If the operation does not "
"complete within this time, it will be considered failed."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields.selection,name:ai_oca_bridge.selection__ai_bridge__auth_type__token
msgid "Token Authentication"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/controllers/ai.py:0
#, python-format
msgid "Token is not allowed for this execution."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__activity_exception_decoration
msgid "Type of the exception activity on record."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__url
msgid "URL"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge_execution.py:0
#, python-format
msgid "Unsupported authentication type."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__user_id
msgid "User"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__group_ids
msgid "User groups allowed to use this AI bridge."
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,field_description:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website Messages"
msgstr ""
#. module: ai_oca_bridge
#: model:ir.model.fields,help:ai_oca_bridge.field_ai_bridge__website_message_ids
msgid "Website communication history"
msgstr ""
#. module: ai_oca_bridge
#. odoo-python
#: code:addons/ai_oca_bridge/models/ai_bridge.py:0
#, python-format
msgid ""
"When usage is 'AI Thread Unlink', the Payload Type must be 'No payload'."
msgstr ""

View file

@ -0,0 +1,5 @@
from . import ai_bridge_thread
from . import ai_bridge
from . import ai_bridge_execution
from . import mail_thread
from . import ir_model

View file

@ -0,0 +1,321 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import base64
import json
import logging
from datetime import date, datetime
from odoo import _, api, fields, models
from odoo.tools.safe_eval import safe_eval
_logger = logging.getLogger(__name__)
class AiBridge(models.Model):
_name = "ai.bridge"
_inherit = ["mail.thread", "mail.activity.mixin"]
_description = "Ai Bridge Configuration"
_order = "sequence, id"
sequence = fields.Integer(
default=10,
)
company_id = fields.Many2one(
"res.company",
# We leave it empty to allow multiple companies to use the same bridge.
)
usage = fields.Selection(
[
("none", "None"),
("thread", "Thread"),
("ai_thread_create", "AI Thread Create"),
("ai_thread_write", "AI Thread Write"),
("ai_thread_unlink", "AI Thread Unlink"),
],
default="none",
help="Defines how this bridge is used. "
"If 'Thread', it will be used in the mail thread context.",
)
name = fields.Char(required=True, translate=True)
active = fields.Boolean(default=True)
description = fields.Html(translate=True)
user_id = fields.Many2one(
"res.users",
default=lambda self: self.env.user,
help="The user that will be shown when executing this AI bridge.",
)
payload_type = fields.Selection(
[
("none", "No payload"),
("record", "Record"),
("record_v0", "Record v0"), # Deprecated, use 'record' instead
],
required=True,
store=True,
readonly=False,
compute="_compute_payload_type",
default="record",
)
result_type = fields.Selection(
[
("none", "No processing"),
("message", "Post a Message"),
("action", "Action"),
],
required=True,
default="none",
help="Defines the type of result expected from the AI system.",
)
result_kind = fields.Selection(
[("immediate", "Immediate"), ("async", "Asynchronous")],
default="immediate",
help="""
Defines how the result from the AI system is processed.
- 'Immediate': The result is processed immediately after the AI system responds.
- 'Asynchronous': The result is processed in the background.
It allows longer operations.
Odoo will provide a URL to the AI system where the response will be sent.
Users will receive a notification when the operation is started.
No notification will be sent when it is finished.
""",
)
async_timeout = fields.Integer(
default=300,
help="Timeout in seconds for asynchronous operations. "
"If the operation does not complete within this time, it will be considered failed.",
)
execution_ids = fields.One2many("ai.bridge.execution", "ai_bridge_id")
execution_count = fields.Integer(
compute="_compute_execution_count",
)
url = fields.Char(
string="URL",
help="The URL of the external AI system to which this bridge connects.",
)
auth_type = fields.Selection(
selection=[
("none", "None"),
("basic", "Basic Authentication"),
("token", "Token Authentication"),
],
default="none",
string="Authentication Type",
help="The type of authentication used to connect to the external AI system.",
)
auth_username = fields.Char(groups="base.group_system")
auth_password = fields.Char(groups="base.group_system")
auth_token = fields.Char(groups="base.group_system")
group_ids = fields.Many2many(
"res.groups",
help="User groups allowed to use this AI bridge.",
)
sample_payload = fields.Text(
help="Sample payload to be sent to the AI system. "
"This is used for testing and debugging purposes.",
compute="_compute_sample_payload",
)
model_id = fields.Many2one(
"ir.model",
string="Model",
required=False,
ondelete="cascade",
help="The model to which this bridge is associated.",
)
model_required = fields.Boolean(compute="_compute_model_fields")
#######################################
# Payload type 'record' specific fields
#######################################
field_ids = fields.Many2many(
"ir.model.fields",
help="Fields to include in the AI bridge.",
compute="_compute_field_ids",
store=True,
readonly=False,
)
model = fields.Char(
related="model_id.model",
string="Model Name",
)
domain = fields.Char(
string="Filter", compute="_compute_domain", readonly=False, store=True
)
@api.onchange("usage")
def _compute_payload_type(self):
for record in self:
if record.usage == "ai_thread_unlink":
record.payload_type = "none"
@api.constrains("usage", "payload_type")
def _check_payload_type_usage_compatibility(self):
for record in self:
if record.usage == "ai_thread_unlink" and record.payload_type != "none":
raise models.ValidationError(
_(
"When usage is 'AI Thread Unlink', "
"the Payload Type must be 'No payload'."
)
)
@api.depends("usage")
def _compute_model_fields(self):
for record in self:
record.update(record._get_model_fields())
def _get_model_fields(self):
if self.usage == "thread":
return {
"model_required": True,
}
if self.usage in ["ai_thread_create", "ai_thread_write", "ai_thread_unlink"]:
return {
"model_required": True,
}
return {
"model_required": False,
}
@api.depends("model_id")
def _compute_domain(self):
for record in self:
record.domain = "[]"
@api.depends("model_id")
def _compute_field_ids(self):
for record in self:
record.field_ids = False
@api.depends("field_ids", "model_id", "payload_type")
def _compute_sample_payload(self):
for record in self:
record.sample_payload = json.dumps(
record.with_context(sample_payload=True)._prepare_payload(), indent=4
)
@api.depends("execution_ids")
def _compute_execution_count(self):
for record in self:
record.execution_count = len(record.execution_ids)
def _get_info(self):
return {"id": self.id, "name": self.name, "description": self.description}
def execute_ai_bridge(self, res_model, res_id):
self.ensure_one()
if not self.active or (
self.group_ids and not self.env.user.groups_id & self.group_ids
):
return {
"body": _("%s is not active.", self.name),
"args": {"type": "warning", "title": _("AI Bridge Inactive")},
}
record = self.env[res_model].browse(res_id).exists()
if record:
execution = self.env["ai.bridge.execution"].create(
{
"ai_bridge_id": self.id,
"model_id": self.sudo().env["ir.model"]._get_id(res_model),
"res_id": res_id,
}
)
result = execution._execute()
if result:
return result
if execution.state == "done":
return {
"notification": {
"body": _("%s executed successfully.", self.name),
"args": {"type": "success", "title": _("AI Bridge Executed")},
}
}
return {
"notification": {
"body": _("%s failed.", self.name),
"args": {"type": "danger", "title": _("AI Bridge Failed")},
}
}
def _enabled_for(self, record):
"""Check if the bridge is enabled for the given record."""
self.ensure_one()
domain = safe_eval(self.domain)
if self.group_ids and not self.env.user.groups_id & self.group_ids:
return False
if domain:
return bool(record.filtered_domain(domain))
return True
def _prepare_payload(self, **kwargs):
method = getattr(self, f"_prepare_payload_{self.payload_type}", None)
if not method:
raise ValueError(
f"Unsupported payload type: {self.payload_type}. "
"Please implement a method for this payload type."
)
return method(**kwargs)
def _prepare_payload_none(self, res_model=False, res_id=False, **kwargs):
return {
"_model": res_model,
"_id": res_id,
}
def _prepare_payload_record(self, record=None, **kwargs):
"""Prepare the payload to be sent to the AI system."""
self.ensure_one()
if not self.model_id:
return {}
if record is None and self.env.context.get("sample_payload"):
record = self.env[self.model_id.model].search([], limit=1)
if not record:
return {}
vals = {}
if self.sudo().field_ids:
vals = record.read(self.sudo().field_ids.mapped("name"))[0]
return json.loads(
json.dumps(
{
"record": vals,
"_model": record._name,
"_id": record.id,
},
default=self.custom_serializer,
)
)
def _prepare_payload_record_v0(self, record=None, **kwargs):
"""Prepare the payload to be sent to the AI system."""
_logger.warning(
"The 'record_v0' payload type is deprecated. " "Use 'record' instead."
)
self.ensure_one()
if not self.model_id:
return {}
if record is None and self.env.context.get("sample_payload"):
record = self.env[self.model_id.model].search([], limit=1)
if not record:
return {}
vals = {}
if self.sudo().field_ids:
vals = record.read(self.sudo().field_ids.mapped("name"))[0]
return json.loads(
json.dumps(
{
**vals,
"_model": record._name,
"_id": record.id,
},
default=self.custom_serializer,
)
)
def custom_serializer(self, obj):
if isinstance(obj, datetime) or isinstance(obj, date):
return obj.isoformat()
if isinstance(obj, bytes):
return base64.b64encode(obj).decode("utf-8")
raise TypeError(f"Type {type(obj)} not serializable")

View file

@ -0,0 +1,218 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import json
import traceback
from datetime import timedelta
from io import StringIO
import requests
from werkzeug import urls
from odoo import _, api, fields, models, tools
class AiBridgeExecution(models.Model):
_name = "ai.bridge.execution"
_description = "Ai Execution"
_order = "id desc"
name = fields.Char(
store=True,
compute="_compute_name",
)
ai_bridge_id = fields.Many2one(
"ai.bridge",
required=True,
ondelete="cascade",
)
res_id = fields.Integer(required=False)
state = fields.Selection(
[
("draft", "Draft"),
("done", "Done"),
("error", "Error"),
],
default="draft",
required=True,
)
model_id = fields.Many2one(
"ir.model",
required=False,
ondelete="cascade",
)
payload = fields.Json(readonly=True)
payload_txt = fields.Text(
compute="_compute_payload_txt",
)
result = fields.Text(readonly=True)
error = fields.Text(readonly=True)
company_id = fields.Many2one(
"res.company",
compute="_compute_company_id",
store=True,
readonly=True,
)
expiration_date = fields.Datetime(
readonly=True,
help="Expiration date for the async operation token.",
)
@api.depends("model_id", "res_id", "ai_bridge_id")
def _compute_name(self):
for record in self:
model = record.sudo().model_id.name or "Unknown Model"
related = self.env[record.sudo().model_id.model].browse(record.res_id)
record.name = (
f"{model} - {related.display_name} - {record.ai_bridge_id.name}"
)
@api.depends("payload")
def _compute_payload_txt(self):
for record in self:
if record.payload:
try:
record.payload_txt = json.dumps(record.payload, indent=4)
except (TypeError, ValueError):
record.payload_txt = str(record.payload)
else:
record.payload_txt = ""
@api.depends("ai_bridge_id")
def _compute_company_id(self):
for record in self:
record.company_id = record.ai_bridge_id.company_id
def _add_extra_payload_fields(self, payload):
"""Add extra fields to the payload if needed."""
self.ensure_one()
if self.ai_bridge_id.result_kind == "async":
self.expiration_date = fields.Datetime.now() + timedelta(
seconds=self.ai_bridge_id.async_timeout
)
token = self._generate_token()
payload["_response_url"] = urls.url_join(
self.get_base_url(), f"/ai/response/{self.id}/{token}"
)
IrParamSudo = self.env["ir.config_parameter"].sudo()
dbuuid = IrParamSudo.get_param("database.uuid")
db_create_date = IrParamSudo.get_param("database.create_date")
payload["_odoo"] = {
"db": dbuuid,
"db_name": self.env.cr.dbname,
"db_hash": tools.hmac(
self.env(su=True),
"database-hash",
(dbuuid, db_create_date, self.env.cr.dbname),
),
"user_id": self.env.user.id,
}
return payload
def _execute(self, **kwargs):
self.ensure_one()
record = None
if self.res_id and self.model_id:
record = self.env[self.sudo().model_id.model].browse(self.res_id)
payload = self.ai_bridge_id._prepare_payload(
record=record,
res_id=self.res_id,
model=self.sudo().model_id.model,
**kwargs,
)
payload = self._add_extra_payload_fields(payload)
try:
response = requests.post(
self.ai_bridge_id.url,
json=payload,
auth=self._get_auth(),
headers=self._get_headers(),
timeout=30, # Default timeout, can be overridden by _execute_kwargs
**self._execute_kwargs(**kwargs),
)
self.result = response.content
response.raise_for_status()
self.state = "done"
self.payload = payload
if self.ai_bridge_id.result_kind == "immediate":
return self._process_response(response.json())
except Exception:
self.state = "error"
self.payload = payload
buff = StringIO()
traceback.print_exc(file=buff)
self.error = buff.getvalue()
buff.close()
def _execute_kwargs(self, timeout=False, **kwargs):
self.ensure_one()
result = {}
if timeout:
result["timeout"] = timeout
return result
def _get_auth(self):
"""Return authentication for the request."""
if self.ai_bridge_id.auth_type == "none":
return None
elif self.ai_bridge_id.auth_type == "basic":
return (
self.ai_bridge_id.sudo().auth_username,
self.ai_bridge_id.sudo().auth_password,
)
elif self.ai_bridge_id.auth_type == "token":
return {"Authorization": f"Bearer {self.ai_bridge_id.sudo().auth_token}"}
else:
raise ValueError(_("Unsupported authentication type."))
def _get_headers(self):
"""Return headers for the request."""
return {
"Content-Type": "application/json",
"Accept": "application/json",
}
def _generate_token(self):
"""Generate a token for async operations."""
self.ensure_one()
return tools.hmac(
self.env(su=True),
"ai_bridge-access_token",
(
self.id,
self.expiration_date and self.expiration_date.isoformat() or "expired",
),
)
def _process_response(self, response):
"""Process the response from the AI bridge."""
self.ensure_one()
self.expiration_date = None
return getattr(
self.with_user(self.ai_bridge_id.user_id.id),
f"_process_response_{self.ai_bridge_id.result_type}",
self._process_response_none,
)(response)
def _process_response_none(self, response):
return {}
def _process_response_message(self, response):
return {"id": self._get_channel().message_post(**response).id}
def _process_response_action(self, response):
if response.get("action"):
action = self.env["ir.actions.actions"]._for_xml_id(response["action"])
if response.get("context"):
action["context"] = response["context"]
if response.get("res_id"):
action["res_id"] = response["res_id"]
return {"action": action}
return {}
def _get_channel(self):
if self.model_id and self.res_id:
return self.env[self.model_id.model].browse(self.res_id)
return None

View file

@ -0,0 +1,77 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class AiBridgeThread(models.AbstractModel):
_name = "ai.bridge.thread"
_description = "AI Bridge Mixin"
@api.model_create_multi
def create(self, vals_list):
records = super().create(vals_list)
model_id = self.sudo().env["ir.model"]._get_id(self._name)
for bridge in self.env["ai.bridge"].search(
[("model_id", "=", model_id), ("usage", "=", "ai_thread_create")]
):
for record in records:
if bridge._enabled_for(record):
try:
bridge.execute_ai_bridge(record._name, record.id)
except Exception as e:
_logger.error(
"Error creating AI thread for creation on %s: %s",
record,
e,
)
return records
def write(self, values):
result = super().write(values)
model_id = self.sudo().env["ir.model"]._get_id(self._name)
for bridge in self.env["ai.bridge"].search(
[("model_id", "=", model_id), ("usage", "=", "ai_thread_write")]
):
for record in self:
if bridge._enabled_for(record):
try:
bridge.execute_ai_bridge(record._name, record.id)
except Exception as e:
_logger.error(
"Error writing AI thread for writing on %s: %s",
record,
e,
)
return result
def unlink(self):
model_id = self.sudo().env["ir.model"]._get_id(self._name)
executions = self.env["ai.bridge.execution"]
for bridge in self.env["ai.bridge"].search(
[("model_id", "=", model_id), ("usage", "=", "ai_thread_unlink")]
):
for record in self:
if bridge._enabled_for(record):
executions |= self.env["ai.bridge.execution"].create(
{
"ai_bridge_id": bridge.id,
"model_id": model_id,
"res_id": record.id,
}
)
result = super().unlink()
for execution in executions:
try:
execution._execute()
except Exception as e:
_logger.error(
"Error executing AI thread unlink for %s: %s",
self,
e,
)
return result

View file

@ -0,0 +1,28 @@
# 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_ai_bridge_thread = fields.Boolean()
ai_usage = fields.Char(store=False, search="_search_ai_usage")
def _reflect_model_params(self, model):
vals = super(IrModel, self)._reflect_model_params(model)
vals["is_ai_bridge_thread"] = (
isinstance(model, self.pool["ai.bridge.thread"]) and not model._abstract
)
return vals
def _search_ai_usage(self, operator, value):
if operator not in ("="):
return []
if value == "thread":
return [("is_mail_thread", "=", True), ("transient", "=", False)]
if value in ["ai_thread_create", "ai_thread_write", "ai_thread_unlink"]:
return [("is_ai_bridge_thread", "=", True), ("transient", "=", False)]
return []

View file

@ -0,0 +1,68 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from lxml import etree
from odoo import api, fields, models
from odoo.tools.misc import frozendict
class MailThread(models.AbstractModel):
_inherit = "mail.thread"
ai_bridge_info = fields.Json(compute="_compute_ai_bridge_info", store=False)
@api.depends()
def _compute_ai_bridge_info(self):
for record in self:
record.ai_bridge_info = [
bridge._get_info() for bridge in record._get_ai_bridge_info()
]
def _get_ai_bridge_info(self):
self.ensure_one()
model_id = self.env["ir.model"].sudo().search([("model", "=", self._name)]).id
return (
self.env["ai.bridge"]
.search([("model_id", "=", model_id), ("usage", "=", "thread")])
.filtered(lambda r: r._enabled_for(self))
)
@api.model
def get_view(self, view_id=None, view_type="form", **options):
res = super().get_view(view_id=view_id, view_type=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"])
# We need to copy, because it is a frozen dict
all_models = res["models"].copy()
for node in doc.xpath("/form/div[hasclass('oe_chatter')]"):
# _add_tier_validation_label process
new_node = etree.fromstring(
"<field name='ai_bridge_info' invisible='1'/>"
)
new_arch, new_models = View.postprocess_and_fields(new_node, self._name)
new_node = etree.fromstring(new_arch)
for model in list(filter(lambda x: x not in all_models, new_models)):
if model not in res["models"]:
all_models[model] = new_models[model]
else:
all_models[model] = res["models"][model]
node.addprevious(new_node)
res["arch"] = etree.tostring(doc)
res["models"] = frozendict(all_models)
return res
@api.model
def _get_view_fields(self, view_type, models):
"""
We need to add this in order to fix the usage of form opening from
trees inside a form
"""
result = super()._get_view_fields(view_type, models)
if view_type == "form":
result[self._name].add("ai_bridge_info")
return result

View file

@ -0,0 +1,68 @@
As an administrator access `AI Bridge\AI Bridge`.
Create a new bridge.
Define the name, model, url and configuration.
In order to improve the view of the AI configuration, use groups and domain to set better filters.
## Payload Configuration
On the external system, you will receive a POST payload. The data included will be the following:
### General
- _odoo: Standard data to identify the Odoo Database
- _model: Model of the related object
- _id: Id of the related object
- _response_url: Url to call with the response in case of async calls
### Record Payload
Adds a new item called record with all the fields.
### Record Payload (v0)
Adds all the fields directly on the payload.
It will be removed on 17.0.
## Asynchronous and synchronous calls
The new system allows asynchronous and synchronous calls.
Asynchronous calls makes sense when the task to be processed don't need to be immediate.
For example, reviewing an invoice and leave a comment with the result.
The same would happen with a chat message.
We expect that the system will leave time to the AI to answer and Odoo's user can do other things.
Meanwhile, Synchronous calls will froze odoo system and wait for an answer.
This makes sense when we expect some feedback from odoo user.
It makes sense, when we open an action for example.
In the synchronous call, the result is processed when the AI system answers on the webhook.
On the other hand, it will be processed automatically on the synchronous call.
## Result processing
With the answers of the system we expect to do something about it.
We have the following options:
### No processing
In this case, the result will do nothing
### Post a Message
We will post a message on the original thread of the system.
The thread is computed by a function, so it can be overriden in future modules.
It expects the keyword arguments of the `message_post` function.
### Action
It expects to launch an action on the user interface.
It only makes sense on synchronous calls.
It expects an action item with the following parameters:
- action: xmlid of the action
- context: Context to pass to the action (not required)
- res_id: Id of the resource (not required)

View file

@ -0,0 +1,12 @@
Right now, there are 2 different approaches for AI integration with Odoo:
1. Make everything inside Odoo.
2. Make it using other tools and integrate Odoo with these tools.
IMO, it would be better to make use of option 2 for different reasons:
- Odoo server is intended as a transactional system. AI systems requires other kind of characteristics
- Everything changes too fast. I am not confident that Odoo can keep the pace in this topic
- There are OSS tools that fills the gap perfectly and are created just for this topic.
Anyway, OCA is open to everyone and we don't intend to force an opinionated way of doing. For this reason, we have this module, that can be used as Bridge with AI systems.

View file

@ -0,0 +1,8 @@
- [Dixmit](https://www.dixmit.com)
- Enric Tobella
- [Sygel Technology](https://www.sygel.es)
- Valentín Vinagre

View file

@ -0,0 +1 @@
This module is used to create a bridge between Odoo and other AI systems like n8n.

View file

@ -0,0 +1,3 @@
- Define examples to use and import
- Allow child fields. Right now, only first level fields are accepted.
- Information popover is not working properly when there is large data.

View file

@ -0,0 +1,3 @@
Use the bolt widget in the chatter to execute the different AI options.
The options will be filtered according to the configuration.

View file

@ -0,0 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_ai_bridge_user,ai_bridge.user,model_ai_bridge,base.group_user,1,0,0,0
access_ai_bridge_manager,ai_bridge.manager,model_ai_bridge,base.group_system,1,1,1,0
access_ai_bridge_execution_user,ai_bridge.user,model_ai_bridge_execution,base.group_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_ai_bridge_user ai_bridge.user model_ai_bridge base.group_user 1 0 0 0
3 access_ai_bridge_manager ai_bridge.manager model_ai_bridge base.group_system 1 1 1 0
4 access_ai_bridge_execution_user ai_bridge.user model_ai_bridge_execution base.group_user 1 1 1 0

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="ai_bridge_multi_company_rule" model="ir.rule">
<field name="name">AI Bridge Multi Company Rule</field>
<field name="model_id" ref="model_ai_bridge" />
<field
name="domain_force"
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
<field name="perm_read" eval="1" />
</record>
<record id="ai_bridge_execution_multi_company_rule" model="ir.rule">
<field name="name">AI Bridge Multi Company Rule</field>
<field name="model_id" ref="model_ai_bridge_execution" />
<field
name="domain_force"
>['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
<field name="perm_read" eval="1" />
</record>
</odoo>

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 9.9 KiB

View file

@ -0,0 +1,560 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="ai-oca-bridge">
<h1>AI OCA Bridge</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:95324eee7973f3fc8e883c34e3e72df6337726fe9eac34f04e68145eeaed22da
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge"><img alt="OCA/ai" src="https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/ai&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module is used to create a bridge between Odoo and other AI systems
like n8n.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#use-cases-context" id="toc-entry-1">Use Cases / Context</a></li>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a><ul>
<li><a class="reference internal" href="#payload-configuration" id="toc-entry-3">Payload Configuration</a><ul>
<li><a class="reference internal" href="#general" id="toc-entry-4">General</a></li>
<li><a class="reference internal" href="#record-payload" id="toc-entry-5">Record Payload</a></li>
<li><a class="reference internal" href="#record-payload-v0" id="toc-entry-6">Record Payload (v0)</a></li>
</ul>
</li>
<li><a class="reference internal" href="#asynchronous-and-synchronous-calls" id="toc-entry-7">Asynchronous and synchronous calls</a></li>
<li><a class="reference internal" href="#result-processing" id="toc-entry-8">Result processing</a><ul>
<li><a class="reference internal" href="#no-processing" id="toc-entry-9">No processing</a></li>
<li><a class="reference internal" href="#post-a-message" id="toc-entry-10">Post a Message</a></li>
<li><a class="reference internal" href="#action" id="toc-entry-11">Action</a></li>
</ul>
</li>
</ul>
</li>
<li><a class="reference internal" href="#usage" id="toc-entry-12">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-13">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-14">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-15">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-16">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-17">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-18">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="use-cases-context">
<h2><a class="toc-backref" href="#toc-entry-1">Use Cases / Context</a></h2>
<p>Right now, there are 2 different approaches for AI integration with
Odoo:</p>
<ol class="arabic simple">
<li>Make everything inside Odoo.</li>
<li>Make it using other tools and integrate Odoo with these tools.</li>
</ol>
<p>IMO, it would be better to make use of option 2 for different reasons:</p>
<ul class="simple">
<li>Odoo server is intended as a transactional system. AI systems requires
other kind of characteristics</li>
<li>Everything changes too fast. I am not confident that Odoo can keep the
pace in this topic</li>
<li>There are OSS tools that fills the gap perfectly and are created just
for this topic.</li>
</ul>
<p>Anyway, OCA is open to everyone and we dont intend to force an
opinionated way of doing. For this reason, we have this module, that can
be used as Bridge with AI systems.</p>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<p>As an administrator access <tt class="docutils literal">AI Bridge\AI Bridge</tt>.</p>
<p>Create a new bridge. Define the name, model, url and configuration.</p>
<p>In order to improve the view of the AI configuration, use groups and
domain to set better filters.</p>
<div class="section" id="payload-configuration">
<h3><a class="toc-backref" href="#toc-entry-3">Payload Configuration</a></h3>
<p>On the external system, you will receive a POST payload. The data
included will be the following:</p>
<div class="section" id="general">
<h4><a class="toc-backref" href="#toc-entry-4">General</a></h4>
<ul class="simple">
<li>_odoo: Standard data to identify the Odoo Database</li>
<li>_model: Model of the related object</li>
<li>_id: Id of the related object</li>
<li>_response_url: Url to call with the response in case of async calls</li>
</ul>
</div>
<div class="section" id="record-payload">
<h4><a class="toc-backref" href="#toc-entry-5">Record Payload</a></h4>
<p>Adds a new item called record with all the fields.</p>
</div>
<div class="section" id="record-payload-v0">
<h4><a class="toc-backref" href="#toc-entry-6">Record Payload (v0)</a></h4>
<p>Adds all the fields directly on the payload. It will be removed on 17.0.</p>
</div>
</div>
<div class="section" id="asynchronous-and-synchronous-calls">
<h3><a class="toc-backref" href="#toc-entry-7">Asynchronous and synchronous calls</a></h3>
<p>The new system allows asynchronous and synchronous calls. Asynchronous
calls makes sense when the task to be processed dont need to be
immediate. For example, reviewing an invoice and leave a comment with
the result. The same would happen with a chat message. We expect that
the system will leave time to the AI to answer and Odoos user can do
other things.</p>
<p>Meanwhile, Synchronous calls will froze odoo system and wait for an
answer. This makes sense when we expect some feedback from odoo user. It
makes sense, when we open an action for example.</p>
<p>In the synchronous call, the result is processed when the AI system
answers on the webhook. On the other hand, it will be processed
automatically on the synchronous call.</p>
</div>
<div class="section" id="result-processing">
<h3><a class="toc-backref" href="#toc-entry-8">Result processing</a></h3>
<p>With the answers of the system we expect to do something about it. We
have the following options:</p>
<div class="section" id="no-processing">
<h4><a class="toc-backref" href="#toc-entry-9">No processing</a></h4>
<p>In this case, the result will do nothing</p>
</div>
<div class="section" id="post-a-message">
<h4><a class="toc-backref" href="#toc-entry-10">Post a Message</a></h4>
<p>We will post a message on the original thread of the system. The thread
is computed by a function, so it can be overriden in future modules. It
expects the keyword arguments of the <tt class="docutils literal">message_post</tt> function.</p>
</div>
<div class="section" id="action">
<h4><a class="toc-backref" href="#toc-entry-11">Action</a></h4>
<p>It expects to launch an action on the user interface. It only makes
sense on synchronous calls.</p>
<p>It expects an action item with the following parameters:</p>
<ul class="simple">
<li>action: xmlid of the action</li>
<li>context: Context to pass to the action (not required)</li>
<li>res_id: Id of the resource (not required)</li>
</ul>
</div>
</div>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-12">Usage</a></h2>
<p>Use the bolt widget in the chatter to execute the different AI options.</p>
<p>The options will be filtered according to the configuration.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h2><a class="toc-backref" href="#toc-entry-13">Known issues / Roadmap</a></h2>
<ul class="simple">
<li>Define examples to use and import</li>
<li>Allow child fields. Right now, only first level fields are accepted.</li>
<li>Information popover is not working properly when there is large data.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-14">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/ai/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-15">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-16">Authors</a></h3>
<ul class="simple">
<li>Dixmit</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-17">Contributors</a></h3>
<ul class="simple">
<li><a class="reference external" href="https://www.dixmit.com">Dixmit</a><ul>
<li>Enric Tobella</li>
</ul>
</li>
<li><a class="reference external" href="https://www.sygel.es">Sygel Technology</a><ul>
<li>Valentín Vinagre</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-18">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge">OCA/ai</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1,32 @@
/** @odoo-module **/
import {registerPatch} from "@mail/model/model_core";
registerPatch({
name: "Chatter",
recordMethods: {
async onClickAiBridge(aiBridge) {
const saved = await this.doSaveRecord();
if (!saved) {
return;
}
const result = await this.env.services.orm.call(
"ai.bridge",
"execute_ai_bridge",
[[aiBridge.id], this.thread.model, this.thread.id]
);
if (result.action && this.env.services && this.env.services.action) {
this.env.services.action.doAction(result.action);
} else if (
result.notification &&
this.env.services &&
this.env.services.notification
) {
this.env.services.notification.add(
result.notification.body,
result.notification.args
);
}
},
},
});

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t
t-name="ai_oca_bridge.ChatterTopbar"
t-inherit="mail.ChatterTopbar"
t-inherit-mode="extension"
owl="1"
>
<xpath
expr="//div[hasclass('o_ChatterTopbar_rightSection')]/button[1]"
position="before"
>
<ChatterAITopbar
record="chatterTopbar"
t-if="chatterTopbar.chatter.webRecord.data.ai_bridge_info !== undefined and chatterTopbar.chatter.webRecord.data.ai_bridge_info.length > 0"
/>
</xpath>
</t>
</templates>

View file

@ -0,0 +1,24 @@
/** @odoo-module **/
import {ChatterAIItem} from "../chatter_topbar_ai_item/chatter_topbar_ai_item.esm";
const {Component} = owl;
import {Dropdown} from "@web/core/dropdown/dropdown";
import {DropdownItem} from "@web/core/dropdown/dropdown_item";
import {registerMessagingComponent} from "@mail/utils/messaging_component";
export class ChatterAITopbar extends Component {
/**
* @returns {ChatterAITopbar}
*/
get chatterTopbar() {
return this.props.record;
}
}
Object.assign(ChatterAITopbar, {
props: {record: Object},
components: {Dropdown, DropdownItem, ChatterAIItem},
template: "ai_oca_bridge.ChatterAITopbar",
});
registerMessagingComponent(ChatterAITopbar);

View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="ai_oca_bridge.ChatterAITopbar" owl="1">
<Dropdown
class="'btn-group o_ChatterTopbar_AIButton'"
togglerClass="'btn btn-secondary ai_button_selection text-uppercase'"
hotkey="'i'"
showCaret="true"
>
<t t-set-slot="toggler">
<i class="fa fa-bolt" />
</t>
<t
t-foreach="props.record.chatter.webRecord.data.ai_bridge_info"
t-as="aiBridge"
t-key="aiBridge.id"
>
<DropdownItem
class="'dropdown-item'"
onSelected="() => this.props.record.chatter.onClickAiBridge(aiBridge)"
>
<ChatterAIItem bridge="aiBridge" />
</DropdownItem>
</t>
</Dropdown>
</t>
</templates>

View file

@ -0,0 +1,46 @@
/** @odoo-module **/
const {Component, markup} = owl;
import {usePopover} from "@web/core/popover/popover_hook";
export class ChatterAIItemPopover extends Component {}
ChatterAIItemPopover.template = "ai_oca_bridge.ChatterAIItemPopover";
export class ChatterAIItem extends Component {
setup() {
super.setup();
this.popover = usePopover();
this.tooltipPopover = null;
}
get tooltipInfo() {
return {
help: markup(this.props.bridge.description || ""),
};
}
onMouseEnter(ev) {
this.closeTooltip();
this.tooltipPopover = this.popover.add(
ev.currentTarget,
ChatterAIItemPopover,
this.tooltipInfo,
{
closeOnClickAway: true,
position: "top",
}
);
}
onMouseLeave() {
this.closeTooltip();
}
closeTooltip() {
if (this.tooltipPopover) {
this.tooltipPopover();
this.tooltipPopover = null;
}
}
}
ChatterAIItem.template = "ai_oca_bridge.ChatterAIItem";
ChatterAIItem.props = {bridge: Object};

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<t t-name="ai_oca_bridge.ChatterAIItem" owl="1">
<span class="o_ChatterTopbar_AIItem">
<t t-esc="props.bridge.name" />
<i
class="fa fa-info-circle ms-1"
role="img"
t-on-mouseenter="(ev) => this.onMouseEnter(ev)"
t-on-mouseleave="(ev) => this.onMouseLeave(ev)"
t-if="props.bridge.description"
/>
</span>
</t>
<t t-name="ai_oca_bridge.ChatterAIItemPopover" owl="1">
<div class="popup-div">
<p class="popover-content p-2">
<t t-out="props.help or ''" />
</p>
</div>
</t>
</templates>

View file

@ -0,0 +1,36 @@
/** @odoo-module **/
// ensure mail mock server is loaded first.
import "@mail/../tests/helpers/mock_server";
import {MockServer} from "@web/../tests/helpers/mock_server";
import {patch} from "@web/core/utils/patch";
patch(MockServer.prototype, "ai_oca_bridge", {
async _performRPC(route, args) {
if (args.model === "ai.bridge" && args.method === "execute_ai_bridge") {
const record = this.models["ai.bridge"].records.filter(
(record) => record.id === args.args[0][0]
);
if (record && record[0].result_type === "action") {
return {
action: {
type: "ir.actions.act_window",
res_model: "res.partner",
views: [[false, "tree"]],
},
};
}
return {
notification: {
body: "Mocked AI Bridge Response",
args: {
type: "info",
title: "AI Bridge Notification",
},
},
};
}
return this._super(...arguments);
},
});

View file

@ -0,0 +1,29 @@
/** @odoo-module **/
import {
addModelNamesToFetch,
insertModelFields,
insertRecords,
} from "@bus/../tests/helpers/model_definitions_helpers";
addModelNamesToFetch(["ai.bridge"]);
insertModelFields("res.partner", {
ai_bridge_info: {default: [], type: "json"},
});
insertModelFields("ai.bridge", {
result_type: {
default: "none",
type: "selection",
selection: [
["none", "None"],
["action", "Action"],
["notification", "Notification"],
],
},
name: {string: "Name", type: "char"},
});
insertRecords("ai.bridge", [
{id: 1, name: "Test AI Bridge", result_type: "none"},
{id: 2, name: "Test AI Bridge Action", result_type: "action"},
]);

View file

@ -0,0 +1,98 @@
/** @odoo-module */
/* global QUnit */
/*
Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
*/
import {start, startServer} from "@mail/../tests/helpers/test_utils";
QUnit.module("ai_oca_bridge");
QUnit.test("AI Notification", async function (assert) {
const pyEnv = await startServer();
const resPartnerId1 = pyEnv["res.partner"].create({
ai_bridge_info: [
{name: "AI 1", id: 1, description: "test1 description"},
{name: "AI 2", id: 2},
],
});
const views = {
"res.partner,false,form": `<form>
<field name="ai_bridge_info" />
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>`,
};
const {click, openView} = await start({serverData: {views}});
await openView({
res_id: resPartnerId1,
res_model: "res.partner",
views: [[false, "form"]],
});
await assert.strictEqual(
document.querySelectorAll(`.o_ChatterTopbar_AIButton .ai_button_selection`)
.length,
1,
"should have an AI button"
);
await click(".o_ChatterTopbar_AIButton .ai_button_selection");
assert.strictEqual(
document.querySelectorAll(`.o_ChatterTopbar_AIItem`).length,
2,
"should have 2 AI Items"
);
await click(document.querySelectorAll(".o_ChatterTopbar_AIItem")[0]);
assert.strictEqual(
document.querySelectorAll(`.o_notification_manager .o_notification`).length,
1,
"should have 1 Notification after clicking on AI Item"
);
});
QUnit.test("AI Action", async function (assert) {
const pyEnv = await startServer();
const resPartnerId1 = pyEnv["res.partner"].create({
ai_bridge_info: [
{name: "AI 1", id: 1, description: "test1 description"},
{name: "AI 2", id: 2},
],
});
const views = {
"res.partner,false,form": `<form>
<field name="ai_bridge_info" />
<div class="oe_chatter">
<field name="message_follower_ids"/>
<field name="message_ids"/>
</div>
</form>`,
"res.partner,false,tree": `<tree>
<field name="name"/>
<field name="active"/>
</tree>`,
};
const {click, openView} = await start({serverData: {views}});
await openView({
res_id: resPartnerId1,
res_model: "res.partner",
views: [[false, "form"]],
});
await assert.strictEqual(
document.querySelectorAll(`.o_ChatterTopbar_AIButton .ai_button_selection`)
.length,
1,
"should have an AI button"
);
await click(".o_ChatterTopbar_AIButton .ai_button_selection");
assert.strictEqual(
document.querySelectorAll(`.o_ChatterTopbar_AIItem`).length,
2,
"should have 2 AI Items"
);
await click(document.querySelectorAll(".o_ChatterTopbar_AIItem")[1]);
assert.strictEqual(
document.querySelectorAll(`.o_list_view`).length,
1,
"should have 1 List View after clicking on AI Item with action"
);
});

View file

@ -0,0 +1,4 @@
from . import test_bridge
from . import test_frontend
from . import test_connection
from . import test_mixin

View file

@ -0,0 +1,9 @@
from odoo import fields, models
class BridgeTest(models.Model):
_name = "bridge.test"
_inherit = "ai.bridge.thread"
_description = "Test Model for AI Bridge"
name = fields.Char()

View file

@ -0,0 +1,367 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import json
from unittest import mock
from odoo.tests.common import TransactionCase
class TestBridge(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.bridge = cls.env["ai.bridge"].create(
{
"name": "Test Bridge",
"model_id": cls.env.ref("base.model_res_partner").id,
"url": "https://example.com/api",
"auth_type": "none",
"usage": "thread",
}
)
# We add this in order to simplify tests, as jsons will be filled.
cls.bridge_extra = cls.env["ai.bridge"].create(
{
"name": "Test Bridge Extra",
"model_id": cls.env.ref("base.model_res_partner").id,
"url": "https://example.com/api",
"auth_type": "none",
"usage": "thread",
}
)
cls.partner = cls.env["res.partner"].create(
{
"name": "Test Partner",
"email": "test@example.com",
}
)
cls.group = cls.env["res.groups"].create(
{
"name": "Test Group",
}
)
def test_bridge_none_auth(self):
self.assertEqual(self.bridge.auth_type, "none")
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertTrue(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertEqual(execution.res_id, self.partner.id)
self.assertNotIn("name", execution.payload)
def test_bridge_none_auth_fields_record_v0(self):
self.bridge.write(
{
"payload_type": "record_v0",
"auth_type": "none",
"field_ids": [
(4, self.env.ref("base.field_res_partner__name").id),
(4, self.env.ref("base.field_res_partner__create_date").id),
(4, self.env.ref("base.field_res_partner__image_1920").id),
],
}
)
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertTrue(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertEqual(execution.res_id, self.partner.id)
self.assertIn("name", execution.payload)
self.assertEqual(execution.payload["name"], self.partner.name)
self.assertEqual(1, self.bridge.execution_count)
def test_bridge_none_auth_fields_record(self):
self.bridge.write(
{
"payload_type": "record",
"auth_type": "none",
"field_ids": [
(4, self.env.ref("base.field_res_partner__name").id),
(4, self.env.ref("base.field_res_partner__create_date").id),
(4, self.env.ref("base.field_res_partner__image_1920").id),
],
}
)
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertTrue(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertEqual(execution.res_id, self.partner.id)
self.assertIn("name", execution.payload["record"])
self.assertEqual(execution.payload["record"]["name"], self.partner.name)
self.assertEqual(1, self.bridge.execution_count)
def test_bridge_basic_auth(self):
self.bridge.write(
{
"auth_type": "basic",
"auth_username": "test_user",
"auth_password": "test_pass",
}
)
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertTrue(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
def test_bridge_token_auth(self):
self.bridge.write(
{
"auth_type": "token",
"auth_token": "test_token",
}
)
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertTrue(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
def test_bridge_error(self):
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertTrue(execution)
self.assertTrue(execution.error)
def test_bridge_unactive(self):
self.bridge.toggle_active()
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertFalse(execution)
def test_bridge_check_group(self):
self.bridge.group_ids = [(4, self.group.id)]
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertFalse(execution)
def test_bridge_domain_filtering(self):
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.bridge.write({"domain": f"[('id', '!=', {self.partner.id})]"})
self.partner.invalidate_recordset()
self.assertNotIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
def test_bridge_group_filtering(self):
self.assertTrue(self.partner.ai_bridge_info)
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.bridge.write({"group_ids": [(4, self.group.id)]})
self.partner.invalidate_recordset()
self.assertNotIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
self.env.user.groups_id |= self.group
self.partner.invalidate_recordset()
self.assertIn(
self.bridge.id, [bridge["id"] for bridge in self.partner.ai_bridge_info]
)
def test_view_fields(self):
view = self.partner.get_view(view_type="form")
self.assertIn("ai_bridge_info", view["models"][self.partner._name])
self.assertIn(b'name="ai_bridge_info"', view["arch"])
def test_sample(self):
self.assertTrue(self.bridge.sample_payload)
self.assertIn("_id", self.bridge.sample_payload)
def test_bridge_result_message(self):
self.bridge.write({"result_type": "message"})
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
message_count = self.env["mail.message"].search_count(
[("model", "=", self.partner._name), ("res_id", "=", self.partner.id)]
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertEqual(
self.env["mail.message"].search_count(
[("model", "=", self.partner._name), ("res_id", "=", self.partner.id)]
),
message_count + 1,
)
def test_bridge_result_message_async(self):
self.bridge.write({"result_type": "message", "result_kind": "async"})
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
message_count = self.env["mail.message"].search_count(
[("model", "=", self.partner._name), ("res_id", "=", self.partner.id)]
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertEqual(
self.env["mail.message"].search_count(
[("model", "=", self.partner._name), ("res_id", "=", self.partner.id)]
),
message_count,
)
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertTrue(execution.expiration_date)
execution._process_response({"body": "My message"})
self.assertEqual(
self.env["mail.message"].search_count(
[("model", "=", self.partner._name), ("res_id", "=", self.partner.id)]
),
message_count + 1,
)
self.assertFalse(execution.expiration_date)
def test_bridge_result_action_immediate(self):
self.bridge.write({"result_type": "action", "result_kind": "immediate"})
self.assertFalse(
self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200,
json=lambda: {
"action": "ai_oca_bridge.ai_bridge_act_window",
"context": {"key": "value"},
},
)
result = self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
self.assertIn("action", result)
self.assertEqual(
result["action"]["id"],
self.env.ref("ai_oca_bridge.ai_bridge_act_window").id,
)
def test_bridge_execute_computed_fields(self):
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
mock_post.assert_called_once()
execution = self.env["ai.bridge.execution"].search(
[("ai_bridge_id", "=", self.bridge.id)]
)
self.assertEqual(
execution.payload["_id"], json.loads(execution.payload_txt)["_id"]
)

View file

@ -0,0 +1,116 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from unittest import mock
from werkzeug import urls
from odoo.tests.common import HttpCase, tagged
@tagged("post_install", "-at_install")
class TestAsyncConnection(HttpCase):
def setUp(self):
super().setUp()
self.bridge = self.env["ai.bridge"].create(
{
"name": "Test Bridge",
"model_id": self.env.ref("base.model_res_partner").id,
"url": "https://example.com/api",
"auth_type": "none",
"result_type": "message",
"result_kind": "async",
"usage": "thread",
}
)
self.partner = self.env["res.partner"].create(
{
"name": "Test Partner",
"email": "test@example.com",
}
)
with mock.patch("requests.post") as mock_post:
self.bridge.execute_ai_bridge(self.partner._name, self.partner.id)
self.url = mock_post.call_args[1]["json"]["_response_url"]
self.message_count = self.env["mail.message"].search_count(
[
("model", "=", self.partner._name),
("res_id", "=", self.partner.id),
]
)
def test_wrong_key(self):
result = self.opener.post(f"{self.url}1234", json={"body": "Test response"})
self.assertEqual(
result.status_code, 404, "Should return 404 for wrong key in URL."
)
def test_wrong_id(self):
result = self.opener.post(
f"{self.base_url()}/ai/response/-1/TOKEN", json={"body": "Test response"}
)
self.assertEqual(
result.status_code, 404, "Should return 404 for wrong key in URL."
)
def test_connection(self):
self.assertTrue(
self.env["ai.bridge.execution"].search(
[
("ai_bridge_id", "=", self.bridge.id),
("expiration_date", "!=", False),
]
)
)
self.opener.post(self.url, json={"body": "Test response"})
self.assertEqual(
self.env["mail.message"].search_count(
[
("model", "=", self.partner._name),
("res_id", "=", self.partner.id),
]
),
self.message_count + 1,
"A new message should be created in the thread.",
)
self.assertFalse(
self.env["ai.bridge.execution"].search(
[
("ai_bridge_id", "=", self.bridge.id),
("expiration_date", "!=", False),
]
)
)
# Key is wrong, so no message should be created
result = self.opener.post(self.url, json={"body": "Test response"})
self.assertEqual(
result.status_code, 404, "Should return 404 for wrong key in URL."
)
def test_connection_expired(self):
self.assertTrue(
self.env["ai.bridge.execution"].search(
[
("ai_bridge_id", "=", self.bridge.id),
("expiration_date", "!=", False),
]
)
)
execution = self.env["ai.bridge.execution"].search(
[
("ai_bridge_id", "=", self.bridge.id),
("expiration_date", "!=", False),
]
)
execution.expiration_date = "2020-01-01 00:00:00"
token = execution._generate_token()
result = self.opener.post(
urls.url_join(
execution.get_base_url(), f"/ai/response/{execution.id}/{token}"
),
json={"body": "Test response"},
)
self.assertEqual(
result.status_code, 404, "Should return 404 for expired execution."
)

View file

@ -0,0 +1,12 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo.tests import common
@common.tagged("post_install", "-at_install")
class TestFrontend(common.HttpCase):
def test_javascript(self):
self.browser_js(
"/web/tests?module=ai_oca_bridge", "", login="admin", timeout=1800
)

View file

@ -0,0 +1,174 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from unittest import mock
from odoo_test_helper import FakeModelLoader
from odoo.exceptions import ValidationError
from odoo.tests.common import Form, TransactionCase
class TestBridge(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
# Load fake models ->/
cls.loader = FakeModelLoader(cls.env, cls.__module__)
cls.loader.backup_registry()
from .fake_models import BridgeTest
cls.loader.update_registry((BridgeTest,))
cls.bridge = cls.env["ai.bridge"].create(
{
"name": "Test Bridge",
"model_id": cls.env["ir.model"]._get_id("bridge.test"),
"url": "https://example.com/api",
"auth_type": "none",
"usage": "none",
}
)
@classmethod
def tearDownClass(cls):
cls.loader.restore_registry()
super().tearDownClass()
def test_bridge_thread_creation(self):
self.bridge.write({"usage": "ai_thread_create"})
with mock.patch("requests.post") as mock_post:
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {"result": "success"}
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
# Create a test record
record = self.env["bridge.test"].create({"name": "Test Record"})
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
mock_post.assert_called_once()
record.write({"name": "Updated Record"})
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
record.unlink()
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
mock_post.assert_called_once()
def test_bridge_thread_write(self):
self.bridge.write({"usage": "ai_thread_write"})
with mock.patch("requests.post") as mock_post:
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {"result": "success"}
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
# Create a test record
record = self.env["bridge.test"].create({"name": "Test Record"})
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
record.write({"name": "Updated Record"})
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
record.unlink()
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
mock_post.assert_called_once()
def test_bridge_thread_unlink(self):
self.assertNotEqual(self.bridge.payload_type, "none")
with Form(self.bridge) as bridge_form:
bridge_form.usage = "ai_thread_unlink"
self.assertEqual(self.bridge.payload_type, "none")
with mock.patch("requests.post") as mock_post:
mock_post.return_value.status_code = 200
mock_post.return_value.json.return_value = {"result": "success"}
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
# Create a test record
record = self.env["bridge.test"].create({"name": "Test Record"})
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
record.write({"name": "Updated Record"})
self.assertEqual(
0,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
record.unlink()
self.assertEqual(
1,
self.env["ai.bridge.execution"].search_count(
[("ai_bridge_id", "=", self.bridge.id)]
),
)
mock_post.assert_called_once()
def test_bridge_thread_unlink_constrains(self):
self.assertNotEqual(self.bridge.payload_type, "none")
with Form(self.bridge) as bridge_form:
bridge_form.usage = "ai_thread_unlink"
self.assertEqual(self.bridge.payload_type, "none")
with self.assertRaises(ValidationError):
self.bridge.payload_type = "record"
def test_bridge_model_search(self):
models = self.env["ir.model"].search([("ai_usage", "=", "thread")])
model = self.env["ir.model"]._get_id("bridge.test")
self.assertTrue(models)
self.assertIn(self.env.ref("base.model_res_partner"), models)
self.assertNotIn(model, models.ids)
models = self.env["ir.model"].search([("ai_usage", "=", "ai_thread_create")])
self.assertTrue(models)
self.assertNotIn(self.env.ref("base.model_res_partner"), models)
self.assertIn(model, models.ids)
models = self.env["ir.model"].search([("ai_usage", "=", "none")])
self.assertTrue(models)
self.assertIn(self.env.ref("base.model_res_partner"), models)
self.assertIn(model, models.ids)
def test_bridge_model_required(self):
self.assertFalse(self.bridge.model_required)
self.bridge.usage = "ai_thread_create"
self.assertTrue(self.bridge.model_required)
self.bridge.usage = "thread"
self.assertTrue(self.bridge.model_required)

View file

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="ai_bridge_form_view">
<field name="model">ai.bridge</field>
<field name="arch" type="xml">
<form>
<header />
<sheet>
<widget
name="web_ribbon"
title="Archived"
bg_color="bg-danger"
attrs="{'invisible': [('active', '=', True)]}"
/>
<h1 class="oe_title">
<field name="name" />
</h1>
<group>
<group>
<field name="sequence" />
<field name="usage" />
<field name="payload_type" />
<field name="result_kind" />
<field
name="async_timeout"
attrs="{'invisible': [('result_kind', '!=', 'async')]}"
/>
<field name="result_type" />
<field
name="model_id"
options="{'no_create': True, 'no_create_edit': True}"
domain="[('ai_usage', '=', usage)]"
attrs="{'required': [('model_required', '=', 'True')]}"
/>
<field name="model_required" invisible="1" />
<field name="description" />
</group>
<group>
<field name="url" />
<field name="auth_type" />
<field
name="auth_token"
password="1"
attrs="{'invisible': [('auth_type', '!=', 'token')], 'required': [('auth_type', '=', 'token')]}"
/>
<field
name="auth_username"
attrs="{'invisible': [('auth_type', '!=', 'basic')], 'required': [('auth_type', '=', 'basic')]}"
/>
<field
name="auth_password"
password="1"
attrs="{'invisible': [('auth_type', '!=', 'basic')], 'required': [('auth_type', '=', 'basic')]}"
/>
<field name="active" invisible="1" />
</group>
</group>
<notebook>
<page
string="Record Payload"
name="record_configuration"
attrs="{'invisible': [('payload_type', 'not in', ['record', 'record_v0'])]}"
>
<group>
<field name="group_ids" widget="many2many_tags" />
<field name="model" invisible="1" />
<field
name="field_ids"
widget="many2many_tags"
options="{'no_create': True, 'no_create_edit': True}"
domain="[('model_id', '=', model_id)]"
/>
<field
name="domain"
widget="domain"
options="{'model': 'model'}"
/>
</group>
</page>
<page string="Sample" name="sample">
<field name="sample_payload" />
</page>
</notebook>
</sheet>
<div class="oe_chatter">
<field name="message_follower_ids" groups="base.group_user" />
<field name="activity_ids" />
<field name="message_ids" />
</div>
</form>
</field>
</record>
<record model="ir.ui.view" id="ai_bridge_search_view">
<field name="model">ai.bridge</field>
<field name="arch" type="xml">
<search>
<field name="name" />
<field name="model_id" />
<field name="usage" />
<field name="company_id" groups="base.group_multi_company" />
<separator />
<filter
name="active"
string="Active"
domain="[('active', '=', True)]"
/>
<filter
name="inactive"
string="Archived"
domain="[('active', '=', False)]"
/>
</search>
</field>
</record>
<record model="ir.ui.view" id="ai_bridge_tree_view">
<field name="model">ai.bridge</field>
<field name="arch" type="xml">
<tree decoration-muted="active == False">
<field name="active" invisible="1" />
<field name="sequence" widget="handle" />
<field name="name" />
<field name="model_id" />
<field name="company_id" groups="base.group_multi_company" />
<field name="usage" />
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="ai_bridge_act_window">
<field name="name">AI Bridge</field>
<field name="res_model">ai.bridge</field>
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
</record>
<record model="ir.ui.menu" id="ai_bridge_menu">
<field name="name">AI Bridge</field>
<field name="parent_id" ref="ai_bridge_root_menu" />
<field name="action" ref="ai_bridge_act_window" />
<field name="sequence" eval="10" />
</record>
</odoo>

View file

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="ai_bridge_execution_form_view">
<field name="model">ai.bridge.execution</field>
<field name="arch" type="xml">
<form create="false" edit="false">
<header>
<field name="state" widget="statusbar" />
</header>
<sheet>
<group>
<field name="ai_bridge_id" />
<field name="res_id" />
<field name="model_id" />
<field name="create_uid" />
<field name="create_date" />
<field name="company_id" groups="base.group_multi_company" />
</group>
<notebook>
<page string="Execution Details" name="execution_details">
<group string="Payload">
<field name="payload_txt" nolabel="1" cols="2" />
</group>
<group string="Response">
<field name="result" nolabel="1" cols="2" />
</group>
</page>
<page
string="Error"
name="error"
attrs="{'invisible': [('error', '=', False)]}"
>
<field name="error" />
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record model="ir.ui.view" id="ai_bridge_execution_search_view">
<field name="model">ai.bridge.execution</field>
<field name="arch" type="xml">
<search>
<field name="name" />
<field name="ai_bridge_id" />
<field name="model_id" />
<field name="create_uid" />
<field name="create_date" />
<field name="company_id" groups="base.group_multi_company" />
</search>
</field>
</record>
<record model="ir.ui.view" id="ai_bridge_execution_tree_view">
<field name="model">ai.bridge.execution</field>
<field name="arch" type="xml">
<tree create="false" edit="false">
<field name="ai_bridge_id" />
<field name="res_id" />
<field name="model_id" />
<field name="company_id" groups="base.group_multi_company" />
<field name="create_uid" />
<field name="create_date" />
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="ai_bridge_execution_act_window">
<field name="name">AI Execution</field>
<field name="res_model">ai.bridge.execution</field>
<field name="view_mode">tree,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
</record>
<record model="ir.ui.menu" id="ai_bridge_execution_menu">
<field name="name">AI Bridge Execution</field>
<field name="parent_id" ref="ai_bridge_root_menu" />
<field name="action" ref="ai_bridge_execution_act_window" />
<field name="sequence" eval="20" />
</record>
</odoo>

View file

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
XML which can edit the lateral bar menu of the view.
-->
<odoo>
<menuitem
name="AI Bridge"
id="ai_bridge_root_menu"
groups="base.group_system"
web_icon="ai_oca_bridge,static/description/icon.png"
sequence="40"
/>
</odoo>

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Ai_oca_bridge Module - ai_oca_bridge
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for ai_oca_bridge. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,17 @@
# Controllers
HTTP routes provided by this module.
```mermaid
sequenceDiagram
participant U as User/Client
participant C as Module Controllers
participant O as ORM/Views
U->>C: HTTP GET/POST (routes)
C->>O: ORM operations, render templates
O-->>U: HTML/JSON/PDF
```
Notes
- See files in controllers/ for route definitions.

View file

@ -0,0 +1,5 @@
# Dependencies
This addon depends on:
- [mail](../../odoo-bringout-oca-ocb-mail)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon ai_oca_bridge or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge"
# or
uv pip install odoo-bringout-oca-ai-ai_oca_bridge"
```

View file

@ -0,0 +1,16 @@
# Models
Detected core models and extensions in ai_oca_bridge.
```mermaid
classDiagram
class ai_bridge
class ai_bridge_execution
class ai_bridge_thread
class ir_model
class mail_thread
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: ai_oca_bridge. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon ai_oca_bridge
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,41 @@
# Security
Access control and security definitions in ai_oca_bridge.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[ir.model.access.csv](../ai_oca_bridge/security/ir.model.access.csv)**
- 3 model access rules
## Record Rules
Row-level security rules defined in:
## Security Groups & Configuration
Security groups and permissions defined in:
- **[security.xml](../ai_oca_bridge/security/security.xml)**
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[ir.model.access.csv](../ai_oca_bridge/security/ir.model.access.csv)**
- Model access permissions (CRUD rights)
- **[security.xml](../ai_oca_bridge/security/security.xml)**
- Security groups, categories, and XML-based rules
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon ai_oca_bridge
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -0,0 +1,42 @@
[project]
name = "odoo-bringout-oca-ai-ai_oca_bridge"
version = "16.0.0"
description = "AI OCA Bridge - Makes a basic configuration to be used as bridge with external AI systems"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-mail>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["ai_oca_bridge"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]

View file

@ -0,0 +1,44 @@
# Ai Oca Bridge Chatter
Odoo addon: ai_oca_bridge_chatter
## Installation
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter
```
## Dependencies
This addon depends on:
- ai_oca_bridge
## Manifest Information
- **Name**: Ai Oca Bridge Chatter
- **Version**: 16.0.1.0.0
- **Category**: N/A
- **License**: AGPL-3
- **Installable**: False
## Source
Based on [OCA/ai](https://github.com/OCA/ai) branch 16.0, addon `ai_oca_bridge_chatter`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,103 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
=====================
Ai Oca Bridge Chatter
=====================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb5b0cdbee135ee2cd5731de3c50a697c02fb931d21800fcc5fa39ca670edad6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github
:target: https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter
:alt: OCA/ai
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge_chatter
: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/ai&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows usage of LLM chatbots inside Odoo.
The logic of the chatbot should be defined in an external system like
n8n.
**Table of contents**
.. contents::
:local:
Configuration
=============
On your external AI system create a workflow that will receive messages
and will return the call directly.
Here you can see an `example <./static/description/Chat.json>`__ of
configuration in n8n.
After that, create a bridge with usage type chatter and payload type
chatter. Then, create a user and assign the bridge to it.
With this configuration, the user will answer automatically using the
external systema and will be online permanently. It can be used on
livechat without any issues.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/ai/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge_chatter%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
-------
* Dixmit
Contributors
------------
- `Dixmit <https://www.dixmit.com>`__
- Enric Tobella
- `Binhex <https://www.binhex.cloud/>`__
Maintainers
-----------
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/ai <https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1 @@
from . import models

View file

@ -0,0 +1,19 @@
# Copyright 202 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Ai Oca Bridge Chatter",
"summary": """Integrate a Bridge with a user that will use it on chatter""",
"version": "16.0.1.0.0",
"license": "AGPL-3",
"author": "Dixmit,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/ai",
"depends": [
"ai_oca_bridge",
],
"data": [
"views/res_users.xml",
"views/ai_bridge.xml",
],
"demo": [],
}

View file

@ -0,0 +1,86 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr ""

View file

@ -0,0 +1,86 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.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: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "AI most"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Konfiguracija Ai mosta"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ai izvršavanje"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Razgovor"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Korisnik razgovora"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Kontakt"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Kanal rasprave"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tip korisnog tereta"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "Poruka ne pripada nijednom kanalu."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "Zapis mora biti instanca mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Upotreba"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Korisnik"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-16 01:25+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\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"
"X-Generator: Weblate 5.10.4\n"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Usuario de Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canal de discusión"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "El mensaje no pertenece a ningún canal."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "El registro debe ser una instancia de mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Usuario"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-08-16 01:25+0000\n"
"Last-Translator: \"Leonardo J. Caballero G.\" <leonardocaballero@gmail.com>\n"
"Language-Team: none\n"
"Language: es_VE\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: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configuración de puente de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Ejecución de IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Usuario de Chatter"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contacto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Define cómo se utiliza este puente. Si es 'Hilo', se usará en el contexto "
"del hilo de correo."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canal de discusión"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo de carga útil"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "El mensaje no pertenece a ningún canal."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "El registro debe ser una instancia de mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uso"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Usuario"

View file

@ -0,0 +1,91 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-07-29 09:25+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\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: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr "Collegamento IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr "Configurazione collegamento IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr "Esecuzione IA"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr "Elenco messaggi"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr "Utente elenco messaggi"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr "Contatto"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
"Definisce come viene usato questo collegamento. Se \"Discussione\", verrà "
"usato nel contesto della discussione e-mail."
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr "Canale discussione"
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr "Tipo carico"
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr "Questo messaggi non appartiene a nessun canale."
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr "Il record deve essere una istanza mail.message."
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr "Uilizzo"
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr "Utente"

View file

@ -0,0 +1,87 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * ai_oca_bridge_chatter
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: Automatically generated\n"
"Language-Team: none\n"
"Language: pt_BR\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: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_res_users__ai_bridge_id
msgid "AI Bridge"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge
msgid "Ai Bridge Configuration"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_ai_bridge_execution
msgid "Ai Execution"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__payload_type__chatter
#: model:ir.model.fields.selection,name:ai_oca_bridge_chatter.selection__ai_bridge__usage__chatter
msgid "Chatter"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge_execution__chatter_user_id
msgid "Chatter User"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_partner
msgid "Contact"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,help:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid ""
"Defines how this bridge is used. If 'Thread', it will be used in the mail "
"thread context."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_mail_channel
msgid "Discussion Channel"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__payload_type
msgid "Payload Type"
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge_execution.py:0
#, python-format
msgid "The message does not belong to any channel."
msgstr ""
#. module: ai_oca_bridge_chatter
#. odoo-python
#: code:addons/ai_oca_bridge_chatter/models/ai_bridge.py:0
#, python-format
msgid "The record must be a mail.message instance."
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model.fields,field_description:ai_oca_bridge_chatter.field_ai_bridge__usage
msgid "Usage"
msgstr ""
#. module: ai_oca_bridge_chatter
#: model:ir.model,name:ai_oca_bridge_chatter.model_res_users
msgid "User"
msgstr ""

View file

@ -0,0 +1,5 @@
from . import ai_bridge
from . import ai_bridge_execution
from . import res_partner
from . import res_users
from . import mail_channel

View file

@ -0,0 +1,32 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, fields, models
class AiBridge(models.Model):
_inherit = "ai.bridge"
usage = fields.Selection(
selection_add=[("chatter", "Chatter")], ondelete={"chatter": "set default"}
)
payload_type = fields.Selection(
selection_add=[("chatter", "Chatter")], ondelete={"chatter": "set default"}
)
def _prepare_payload_chatter(self, record=None, **kwargs):
if not record:
record = self.env["mail.message"].search([], limit=1)
if record._name != "mail.message":
raise ValueError(_("The record must be a mail.message instance."))
return {
"message": {
"res_id": record.res_id,
"model": record.model,
"body": record.body,
"author_id": record.author_id.id,
"subject": record.subject,
"date": record.date.isoformat(),
"author_name": record.author_id.name,
}
}

View file

@ -0,0 +1,41 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import _, fields, models
class AiBridgeExecution(models.Model):
_inherit = "ai.bridge.execution"
chatter_user_id = fields.Many2one("res.users", readonly=True)
def _get_channel(self):
if self.ai_bridge_id.usage == "chatter":
# For chatter usage, we need to get the channel from the message
message = self.env["mail.message"].browse(self.res_id)
if message.model != "mail.channel":
raise ValueError(_("The message does not belong to any channel."))
return (
self.env["mail.channel"]
.browse(message.res_id)
.with_user(self.chatter_user_id.id)
)
return super()._get_channel()
def _process_response_message(self, response):
if self.ai_bridge_id.usage == "chatter":
recipient = (
self.env["mail.channel.member"]
.sudo()
.search(
[
("partner_id", "=", self.chatter_user_id.partner_id.id),
("channel_id", "=", self._get_channel().id),
],
limit=1,
)
)
recipient._notify_typing(is_typing=False)
response["author_id"] = self.chatter_user_id.partner_id.id
response["message_type"] = "comment"
return super()._process_response_message(response)

View file

@ -0,0 +1,50 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class MailChannel(models.Model):
_inherit = "mail.channel"
def message_post(self, **kwargs):
message = super().message_post(**kwargs)
if not message.body:
return message
if message.author_id.user_ids.ai_bridge_id:
# Don't answer AI agents
return message
channel_recipient_ids = self.sudo().channel_member_ids.filtered(
lambda recipient: recipient.partner_id != message.author_id
and recipient.partner_id.user_ids
and recipient.partner_id.user_ids.ai_bridge_id
and self._eligibile_for_ai(message, recipient)
)
for recipient in channel_recipient_ids:
recipient._notify_typing(is_typing=True)
for user in recipient.partner_id.user_ids:
for bridge in user.ai_bridge_id:
execution = self.env["ai.bridge.execution"].create(
{
"ai_bridge_id": bridge.id,
"model_id": self.sudo()
.env.ref("mail.model_mail_message")
.id,
"res_id": message.id,
"chatter_user_id": user.id,
}
)
execution._execute()
return message
def _eligibile_for_ai(self, message, recipient):
if len(self.sudo().channel_member_ids) <= 2:
return True
if recipient.partner_id in message.partner_ids:
# If the recipient is already in the message partners,
# it was invoked by the user.
# This will make it work on general channels
return True
# TODO: add more checks to determine if the message is eligible
# for AI processing, like livechat messages.
return False

View file

@ -0,0 +1,21 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import models
class ResPartner(models.Model):
_inherit = "res.partner"
def _compute_im_status(self):
"""
Override to set im_status to 'online' for partners
that have an associated user with an AI bridge.
It will be shown in general chatter as online.
"""
for record in self.filtered(lambda r: r.user_ids.ai_bridge_id):
record.im_status = "online"
to_process = self.filtered(lambda r: not r.user_ids.ai_bridge_id)
if not to_process:
return
return super(ResPartner, to_process)._compute_im_status()

View file

@ -0,0 +1,25 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models
class ResUsers(models.Model):
_inherit = "res.users"
ai_bridge_id = fields.Many2one(
"ai.bridge", string="AI Bridge", domain=[("usage", "=", "chatter")]
)
def _compute_im_status(self):
"""
Override to set im_status to 'online' for users
that have an associated user with an AI bridge.
Useful for Live chat
"""
for record in self.filtered(lambda r: r.ai_bridge_id):
record.im_status = "online"
to_process = self.filtered(lambda r: not r.ai_bridge_id)
if not to_process:
return
return super(ResUsers, to_process)._compute_im_status()

View file

@ -0,0 +1,9 @@
On your external AI system create a workflow that will receive messages and will return the call directly.
Here you can see an [example](./static/description/Chat.json) of configuration in n8n.
After that, create a bridge with usage type chatter and payload type chatter.
Then, create a user and assign the bridge to it.
With this configuration, the user will answer automatically using the external systema and will be online permanently.
It can be used on livechat without any issues.

View file

@ -0,0 +1,5 @@
- [Dixmit](https://www.dixmit.com)
- Enric Tobella
- [Binhex](https://www.binhex.cloud/)

View file

@ -0,0 +1,3 @@
This module allows usage of LLM chatbots inside Odoo.
The logic of the chatbot should be defined in an external system like n8n.

View file

@ -0,0 +1,185 @@
{
"name": "Chat",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "af33e75e-2cb4-4953-903c-4d7ce88aba7d",
"options": {}
},
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [-540, -180],
"id": "604ad5d2-f45f-4f27-8040-392c5cde67de",
"name": "Webhook",
"webhookId": "af33e75e-2cb4-4953-903c-4d7ce88aba7d"
},
{
"parameters": {
"method": "POST",
"url": "={{ $('Webhook').item.json.body._response_url }}",
"sendBody": true,
"bodyParameters": {
"parameters": [
{
"name": "body",
"value": "={{ $json.data }}"
}
]
},
"options": {}
},
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [320, -180],
"id": "196baea5-927e-49d7-b888-41a032255eb5",
"name": "HTTP Request",
"alwaysOutputData": false
},
{
"parameters": {
"promptType": "define",
"text": "={{ $json.data }}",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 2,
"position": [-200, -180],
"id": "92dff2e3-eede-4ba3-b494-ddbd3b33d009",
"name": "AI Agent"
},
{
"parameters": {
"model": "gemma3:1b",
"options": {}
},
"type": "@n8n/n8n-nodes-langchain.lmChatOllama",
"typeVersion": 1,
"position": [-200, -20],
"id": "4f3c84b0-217b-44dd-9798-f9f3bde0437c",
"name": "Ollama Chat Model",
"credentials": {
"ollamaApi": {
"id": "hjdwBXqKYq7uKQBz",
"name": "Ollama account"
}
}
},
{
"parameters": {
"html": "={{ $json.body.message.body }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [-380, -180],
"id": "870d0777-4943-4f9d-a283-c31bb079f8b6",
"name": "Markdown"
},
{
"parameters": {
"mode": "markdownToHtml",
"markdown": "={{ $json.output }}",
"options": {}
},
"type": "n8n-nodes-base.markdown",
"typeVersion": 1,
"position": [160, -180],
"id": "3217e297-6508-47b2-bc67-52810c6512bd",
"name": "Markdown1"
},
{
"parameters": {
"sessionIdType": "customKey",
"sessionKey": "={{ $json.body._odoo.db }}-{{ $json.body._odoo.db_hash }}-{{ $json.body.message.model }}-{{ $json.body.message.res_id }}"
},
"type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
"typeVersion": 1.3,
"position": [-100, -20],
"id": "35174f6e-a121-4db5-be62-86db987bc294",
"name": "Simple Memory"
}
],
"pinData": {
"Webhook": []
},
"connections": {
"Webhook": {
"main": [
[
{
"node": "Markdown",
"type": "main",
"index": 0
}
]
]
},
"Ollama Chat Model": {
"ai_languageModel": [
[
{
"node": "AI Agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"AI Agent": {
"main": [
[
{
"node": "Markdown1",
"type": "main",
"index": 0
}
]
]
},
"Markdown": {
"main": [
[
{
"node": "AI Agent",
"type": "main",
"index": 0
}
]
]
},
"Markdown1": {
"main": [
[
{
"node": "HTTP Request",
"type": "main",
"index": 0
}
]
]
},
"Simple Memory": {
"ai_memory": [
[
{
"node": "AI Agent",
"type": "ai_memory",
"index": 0
}
]
]
}
},
"active": true,
"settings": {
"executionOrder": "v1"
},
"versionId": "539a3987-4bb4-41e2-b37e-26c1d8896779",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "853b8a3aa64a88c45a4d2dd4197c75abdf32fa4cca1ff05538d7e4592b457dcb"
},
"id": "YP8qXDrWacI1QVyu",
"tags": []
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View file

@ -0,0 +1,448 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="ai-oca-bridge-chatter">
<h1>Ai Oca Bridge Chatter</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:eb5b0cdbee135ee2cd5731de3c50a697c02fb931d21800fcc5fa39ca670edad6
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter"><img alt="OCA/ai" src="https://img.shields.io/badge/github-OCA%2Fai-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/ai-16-0/ai-16-0-ai_oca_bridge_chatter"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/ai&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module allows usage of LLM chatbots inside Odoo.</p>
<p>The logic of the chatbot should be defined in an external system like
n8n.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-2">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-3">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-4">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-5">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-6">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>On your external AI system create a workflow that will receive messages
and will return the call directly.</p>
<p>Here you can see an <a class="reference external" href="./static/description/Chat.json">example</a> of
configuration in n8n.</p>
<p>After that, create a bridge with usage type chatter and payload type
chatter. Then, create a user and assign the bridge to it.</p>
<p>With this configuration, the user will answer automatically using the
external systema and will be online permanently. It can be used on
livechat without any issues.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-2">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/ai/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/ai/issues/new?body=module:%20ai_oca_bridge_chatter%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-3">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-4">Authors</a></h3>
<ul class="simple">
<li>Dixmit</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-5">Contributors</a></h3>
<ul class="simple">
<li><a class="reference external" href="https://www.dixmit.com">Dixmit</a><ul>
<li>Enric Tobella</li>
</ul>
</li>
<li><a class="reference external" href="https://www.binhex.cloud/">Binhex</a></li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/ai/tree/16.0/ai_oca_bridge_chatter">OCA/ai</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -0,0 +1 @@
from . import test_chatter

View file

@ -0,0 +1,183 @@
# Copyright 2025 Dixmit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from unittest import mock
from odoo.tests import common, new_test_user
class TestChatter(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, mail_create_nosubscribe=True))
cls.bridge = cls.env["ai.bridge"].create(
{
"name": "Test Bridge",
"model_id": cls.env.ref("base.model_res_partner").id,
"url": "https://example.com/api",
"auth_type": "none",
"usage": "chatter",
"payload_type": "chatter",
"result_kind": "immediate",
# We will use the immediate result_kind to simplify the test
"result_type": "message",
}
)
cls.ai_user = new_test_user(
cls.env,
login="test-chatter-user",
groups="base.group_user",
)
cls.ai_user.write({"ai_bridge_id": cls.bridge.id})
cls.user = new_test_user(
cls.env,
login="test-chatter-user-2",
groups="base.group_user",
)
cls.chat = (
cls.env["mail.channel"]
.with_user(cls.user.id)
.create(
{
"name": "Test Channel",
"channel_type": "chat",
"channel_member_ids": [
(0, 0, {"partner_id": cls.ai_user.partner_id.id}),
(0, 0, {"partner_id": cls.user.partner_id.id}),
],
}
)
)
cls.channel = cls.env["mail.channel"].create(
{
"name": "Main Channel",
"channel_type": "channel",
"channel_member_ids": [
(0, 0, {"partner_id": cls.ai_user.partner_id.id}),
(0, 0, {"partner_id": cls.user.partner_id.id}),
(0, 0, {"partner_id": cls.env.user.partner_id.id}),
],
}
)
def test_user_status(self):
self.assertEqual("online", self.ai_user.partner_id.im_status)
self.assertEqual("offline", self.user.partner_id.im_status)
self.assertEqual("online", self.ai_user.im_status)
self.assertEqual("offline", self.user.im_status)
def test_chat(self):
"""Answer is direct in this case"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.chat.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.chat.with_user(self.user.id).message_post(
body="Test message",
)
mock_post.assert_called_once()
self.assertEqual(
2,
self.env["mail.message"].search_count(
[("res_id", "=", self.chat.id), ("model", "=", "mail.channel")]
),
)
def test_channel_not_called(self):
"""No AI bridge should be called when the user is not callend in the channel"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
)
mock_post.assert_not_called()
self.assertEqual(
1,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_channel_called(self):
"""Test that AI answers only if they are called in channels"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id],
)
mock_post.assert_called_once()
self.assertEqual(
2,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_channel_multiple_calls(self):
"""Test that AI answers might be from multiple users in the channel at the same time"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
self.user.ai_bridge_id = self.bridge
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id, self.user.partner_id.id],
)
mock_post.assert_called()
self.assertEqual(
3,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
def test_chat_ai_no_answer(self):
"""Test that AI does not answer to AI messages"""
self.assertFalse(
self.env["mail.message"].search(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)
self.user.ai_bridge_id = self.bridge
with mock.patch("requests.post") as mock_post:
mock_post.return_value = mock.Mock(
status_code=200, json=lambda: {"body": "My message"}
)
self.channel.with_user(self.user.id).message_post(
body="Test message",
partner_ids=[self.ai_user.partner_id.id],
)
mock_post.assert_not_called()
self.assertEqual(
1,
self.env["mail.message"].search_count(
[("res_id", "=", self.channel.id), ("model", "=", "mail.channel")]
),
)

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<!--
<record model="ir.ui.view" id="ai_bridge_form_view">
<field name="model">ai.bridge</field>
<field name="inherit_id" ref="TODO othermodule.form_view"/>
<field name="arch" type="xml">
</field>
</record>
-->
</odoo>

View file

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2025 Dixmit
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). -->
<odoo>
<record model="ir.ui.view" id="res_users_form_view">
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<field name="signature" position="before">
<field name="ai_bridge_id" />
</field>
</field>
</record>
</odoo>

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Ai_oca_bridge_chatter Module - ai_oca_bridge_chatter
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for ai_oca_bridge_chatter. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,5 @@
# Dependencies
This addon depends on:
- [ai_oca_bridge](../../odoo-bringout-oca-ai-ai_oca_bridge)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon ai_oca_bridge_chatter or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter"
# or
uv pip install odoo-bringout-oca-ai-ai_oca_bridge_chatter"
```

View file

@ -0,0 +1,16 @@
# Models
Detected core models and extensions in ai_oca_bridge_chatter.
```mermaid
classDiagram
class ai_bridge
class ai_bridge_execution
class mail_channel
class res_partner
class res_users
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: ai_oca_bridge_chatter. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon ai_oca_bridge_chatter
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,8 @@
# Security
This module does not define custom security rules or access controls beyond Odoo defaults.
Default Odoo security applies:
- Base user access through standard groups
- Model access inherited from dependencies
- No custom row-level security rules

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

Some files were not shown because too many files have changed in this diff Show more