diff --git a/.copier-answers.yml b/.copier-answers.yml index bd1ae630c2..4aaa546ac3 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,8 +1,9 @@ # Do NOT update manually; changes here will be overwritten by Copier -_commit: v1.21.1 +_commit: v1.29 _src_path: gh:oca/oca-addons-repo-template ci: GitHub convert_readme_fragments_to_markdown: false +enable_checklog_odoo: false generate_requirements_txt: true github_check_license: true github_ci_extra_env: {} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 38b0ba110e..afd7524ef0 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -13,13 +13,13 @@ jobs: pre-commit: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v2 + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 with: python-version: "3.11" - name: Get python version run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV - - uses: actions/cache@v1 + - uses: actions/cache@v4 with: path: ~/.cache/pre-commit key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4c760bbff8..8b7736f505 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest name: Detect unreleased dependencies steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: | for reqfile in requirements.txt test-requirements.txt ; do if [ -f ${reqfile} ] ; then @@ -62,7 +62,7 @@ jobs: INCLUDE: "${{ matrix.include }}" EXCLUDE: "${{ matrix.exclude }}" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: persist-credentials: false - name: Install addons and dependencies diff --git a/.gitignore b/.gitignore index 0090721f5d..6ec07a054b 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,19 @@ var/ *.egg *.eggs +# Windows installers +*.msi + +# Debian packages +*.deb + +# Redhat packages +*.rpm + +# MacOS packages +*.dmg +*.pkg + # Installer logs pip-log.txt pip-delete-this-directory.txt diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 564d1d6a31..8647d7aa24 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ exclude: | # Files and folders generated by bots, to avoid loops ^setup/|/static/description/index\.html$| # We don't want to mess with tool-generated files - .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/| + .svg$|/tests/([^/]+/)?cassettes/|^.copier-answers.yml$|^.github/|^eslint.config.cjs|^prettier.config.cjs| # Maybe reactivate this when all README files include prettier ignore tags? ^README\.md$| # Library files can have extraneous formatting (even minimized) @@ -39,7 +39,7 @@ repos: language: fail files: '[a-zA-Z0-9_]*/i18n/en\.po$' - repo: https://github.com/oca/maintainer-tools - rev: 9a170331575a265c092ee6b24b845ec508e8ef75 + rev: d5fab7ee87fceee858a3d01048c78a548974d935 hooks: # update the NOT INSTALLABLE ADDONS section above - id: oca-update-pre-commit-excluded-addons @@ -58,6 +58,8 @@ repos: hooks: - id: oca-checks-odoo-module - id: oca-checks-po + args: + - --disable=po-pretty-format - repo: https://github.com/myint/autoflake rev: v1.6.1 hooks: @@ -73,25 +75,35 @@ repos: rev: 22.8.0 hooks: - id: black - - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 + - repo: local hooks: - id: prettier name: prettier (with plugin-xml) + entry: prettier + args: + - --write + - --list-different + - --ignore-unknown + types: [text] + files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ + language: node additional_dependencies: - "prettier@2.7.1" - "@prettier/plugin-xml@2.2.0" - args: - - --plugin=@prettier/plugin-xml - files: \.(css|htm|html|js|json|jsx|less|md|scss|toml|ts|xml|yaml|yml)$ - - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.24.0 + - repo: local hooks: - id: eslint - verbose: true + name: eslint + entry: eslint args: - --color - --fix + verbose: true + types: [javascript] + language: node + additional_dependencies: + - "eslint@8.24.0" + - "eslint-plugin-jsdoc@" - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.3.0 hooks: @@ -138,7 +150,7 @@ repos: - --header - "# generated from manifests external_dependencies" - repo: https://github.com/PyCQA/flake8 - rev: 3.9.2 + rev: 5.0.0 hooks: - id: flake8 name: flake8 diff --git a/README.md b/README.md index 76613d68d7..0f3468e2b1 100644 --- a/README.md +++ b/README.md @@ -31,25 +31,26 @@ addon | version | maintainers | summary [mail_activity_plan](mail_activity_plan/) | 16.0.1.0.1 | [![victoralmau](https://github.com/victoralmau.png?size=30px)](https://github.com/victoralmau) | Mail activity plan [mail_activity_reminder](mail_activity_reminder/) | 16.0.1.0.0 | | Reminder notifications about planned activities [mail_activity_reply_creator](mail_activity_reply_creator/) | 16.0.1.0.0 | | Assign new to its creator -[mail_activity_team](mail_activity_team/) | 16.0.1.0.1 | | Add Teams to Activities +[mail_activity_team](mail_activity_team/) | 16.0.1.0.2 | | Add Teams to Activities +[mail_activity_unlink_log](mail_activity_unlink_log/) | 16.0.1.0.0 | | Leave a message when an activity is unlinked [mail_attach_existing_attachment](mail_attach_existing_attachment/) | 16.0.1.1.0 | | Adding attachment on the object by sending this one [mail_attach_existing_attachment_account](mail_attach_existing_attachment_account/) | 16.0.1.0.1 | | Module to use attach existing attachment for account module [mail_autosubscribe](mail_autosubscribe/) | 16.0.1.0.1 | | Automatically subscribe partners to its company's business documents -[mail_composer_cc_bcc](mail_composer_cc_bcc/) | 16.0.2.0.3 | [![hailangvn2023](https://github.com/hailangvn2023.png?size=30px)](https://github.com/hailangvn2023) | This module enables sending mail to CC and BCC partners in mail composer form. +[mail_composer_cc_bcc](mail_composer_cc_bcc/) | 16.0.2.0.5 | [![trisdoan](https://github.com/trisdoan.png?size=30px)](https://github.com/trisdoan) | This module enables sending mail to CC and BCC partners in mail composer form. [mail_composer_cc_bcc_account](mail_composer_cc_bcc_account/) | 16.0.2.0.0 | [![hailangvn2023](https://github.com/hailangvn2023.png?size=30px)](https://github.com/hailangvn2023) | This module enables sending mail to CC and BCC partners for invoices. [mail_debrand](mail_debrand/) | 16.0.1.0.2 | [![pedrobaeza](https://github.com/pedrobaeza.png?size=30px)](https://github.com/pedrobaeza) [![joao-p-marques](https://github.com/joao-p-marques.png?size=30px)](https://github.com/joao-p-marques) | Remove Odoo branding in sent emails Removes anchor 20characters [mail_discuss_security](mail_discuss_security/) | 16.0.1.0.0 | [![legalsylvain](https://github.com/legalsylvain.png?size=30px)](https://github.com/legalsylvain) | Add a group to display 'Discuss' Application menu entry [mail_drop_target](mail_drop_target/) | 16.0.1.1.0 | | Attach emails to Odoo by dragging them from your desktop -[mail_gateway](mail_gateway/) | 16.0.1.1.0 | | Set a gateway -[mail_gateway_telegram](mail_gateway_telegram/) | 16.0.1.0.1 | | Set a gateway for telegram -[mail_gateway_whatsapp](mail_gateway_whatsapp/) | 16.0.1.0.0 | | Set a gateway for whatsapp +[mail_gateway](mail_gateway/) | 16.0.1.2.0 | | Set a gateway +[mail_gateway_telegram](mail_gateway_telegram/) | 16.0.1.1.0 | | Set a gateway for telegram +[mail_gateway_whatsapp](mail_gateway_whatsapp/) | 16.0.1.1.0 | | Set a gateway for whatsapp [mail_improved_tracking_value](mail_improved_tracking_value/) | 16.0.1.0.0 | | Improves tracking changed values for certain type of fields.Adds a user-friendly view to consult them. [mail_inline_css](mail_inline_css/) | 16.0.0.1.0 | | Convert style tags in inline style in your mails [mail_layout_force](mail_layout_force/) | 16.0.1.0.0 | [![ivantodorovich](https://github.com/ivantodorovich.png?size=30px)](https://github.com/ivantodorovich) | Force a mail layout on selected email templates [mail_layout_preview](mail_layout_preview/) | 16.0.1.0.0 | | Preview email templates in the browser [mail_notification_custom_subject](mail_notification_custom_subject/) | 16.0.1.0.0 | [![yajo](https://github.com/yajo.png?size=30px)](https://github.com/yajo) [![edlopen](https://github.com/edlopen.png?size=30px)](https://github.com/edlopen) [![rafaelbn](https://github.com/rafaelbn.png?size=30px)](https://github.com/rafaelbn) | Apply a custom subject to mail notifications [mail_optional_autofollow](mail_optional_autofollow/) | 16.0.1.0.0 | | Choose if you want to automatically add new recipients as followers on mail.compose.message -[mail_optional_follower_notification](mail_optional_follower_notification/) | 16.0.1.0.1 | | Choose to notify followers on mail.compose.message +[mail_optional_follower_notification](mail_optional_follower_notification/) | 16.0.1.1.0 | | Choose to notify followers on mail.compose.message [mail_outbound_static](mail_outbound_static/) | 16.0.1.0.2 | | Allows you to configure the from header for a mail server. [mail_partner_opt_out](mail_partner_opt_out/) | 16.0.1.0.0 | | Add the partner's email to the blackmailed list [mail_post_defer](mail_post_defer/) | 16.0.1.1.2 | [![Yajo](https://github.com/Yajo.png?size=30px)](https://github.com/Yajo) | Faster and cancellable outgoing messages @@ -63,12 +64,13 @@ addon | version | maintainers | summary [mail_tracking_mailgun](mail_tracking_mailgun/) | 16.0.1.0.1 | | Mail tracking and Mailgun webhooks integration [mail_tracking_mass_mailing](mail_tracking_mass_mailing/) | 16.0.1.0.0 | | Improve mass mailing email tracking [mass_mailing_contact_active](mass_mailing_contact_active/) | 16.0.1.0.0 | | Adds active feature on mailing list contact and subscriptions -[mass_mailing_custom_unsubscribe](mass_mailing_custom_unsubscribe/) | 16.0.1.0.1 | | Know and track (un)subscription reasons, GDPR compliant +[mass_mailing_custom_unsubscribe](mass_mailing_custom_unsubscribe/) | 16.0.1.0.2 | | Know and track (un)subscription reasons, GDPR compliant [mass_mailing_event_registration_exclude](mass_mailing_event_registration_exclude/) | 16.0.1.0.0 | | Link mass mailing with event for excluding recipients -[mass_mailing_list_dynamic](mass_mailing_list_dynamic/) | 16.0.1.0.0 | | Mass mailing lists that get autopopulated -[mass_mailing_partner](mass_mailing_partner/) | 16.0.1.0.0 | | Link partners with mass-mailing +[mass_mailing_list_dynamic](mass_mailing_list_dynamic/) | 16.0.2.0.0 | | Mass mailing lists that get autopopulated +[mass_mailing_partner](mass_mailing_partner/) | 16.0.2.0.0 | | Link partners with mass-mailing [mass_mailing_resend](mass_mailing_resend/) | 16.0.1.0.0 | [![pedrobaeza](https://github.com/pedrobaeza.png?size=30px)](https://github.com/pedrobaeza) | Resend mass mailings [mass_mailing_unique](mass_mailing_unique/) | 16.0.1.0.1 | | Avoids duplicate mailing lists and contacts +[outgoing_email_by_model](outgoing_email_by_model/) | 16.0.1.0.0 | [![mmequignon](https://github.com/mmequignon.png?size=30px)](https://github.com/mmequignon) | Outgoing Email by Model [//]: # (end addons) diff --git a/mail_activity_team/README.rst b/mail_activity_team/README.rst index 21f59fe201..88b854ec3d 100644 --- a/mail_activity_team/README.rst +++ b/mail_activity_team/README.rst @@ -7,12 +7,12 @@ Mail Activity Team !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:b1261c4f9f0b86d5baa910cd3d60d49619515bcf142af6214bafa4e861ff5141 + !! source digest: sha256:34104d83ec1f80bb0a4acd5971667cee041eb9122c070281a472d45ddc3957f2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status - :alt: Alpha + :alt: Beta .. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 @@ -30,11 +30,6 @@ Mail Activity Team This module adds the possibility to assign teams to activities. -.. IMPORTANT:: - This is an alpha version, the data model and design can change at any time without warning. - Only for development or testing purpose, do not use in production. - `More details on development status `_ - **Table of contents** .. contents:: diff --git a/mail_activity_team/__manifest__.py b/mail_activity_team/__manifest__.py index ba44bc2934..5379454bb9 100644 --- a/mail_activity_team/__manifest__.py +++ b/mail_activity_team/__manifest__.py @@ -4,8 +4,8 @@ { "name": "Mail Activity Team", "summary": "Add Teams to Activities", - "version": "16.0.1.0.1", - "development_status": "Alpha", + "version": "16.0.1.0.2", + "development_status": "Beta", "category": "Social Network", "website": "https://github.com/OCA/social", "author": "ForgeFlow, Sodexis, Odoo Community Association (OCA)", diff --git a/mail_activity_team/i18n/it.po b/mail_activity_team/i18n/it.po index 8d3accefda..010e7f35b9 100644 --- a/mail_activity_team/i18n/it.po +++ b/mail_activity_team/i18n/it.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 13.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-04-04 11:37+0000\n" +"PO-Revision-Date: 2025-01-16 15:06+0000\n" "Last-Translator: mymage \n" "Language-Team: none\n" "Language: it\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: mail_activity_team #: model:ir.model.fields,field_description:mail_activity_team.field_mail_activity_team__active @@ -148,7 +148,7 @@ msgstr "Team" #: code:addons/mail_activity_team/static/src/components/activity_menu_view/activity_menu_view.xml:0 #, python-format msgid "Team Activities" -msgstr "Attività del mio team" +msgstr "Attività team" #. module: mail_activity_team #: model:ir.model.fields,field_description:mail_activity_team.field_mail_activity_team__user_id diff --git a/mail_activity_team/static/description/index.html b/mail_activity_team/static/description/index.html index ee568cff00..75acf15216 100644 --- a/mail_activity_team/static/description/index.html +++ b/mail_activity_team/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +: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. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +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 } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -366,16 +367,10 @@

Mail Activity Team

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:b1261c4f9f0b86d5baa910cd3d60d49619515bcf142af6214bafa4e861ff5141 +!! source digest: sha256:34104d83ec1f80bb0a4acd5971667cee041eb9122c070281a472d45ddc3957f2 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Alpha License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

This module adds the possibility to assign teams to activities.

-
-

Important

-

This is an alpha version, the data model and design can change at any time without warning. -Only for development or testing purpose, do not use in production. -More details on development status

-

Table of contents

  • Usage
  • -
  • Bug Tracker
  • -
  • Credits @@ -412,6 +414,7 @@

    Configure the gateway

    • Use the Meta App authentication key as Token field
    • Use the Meta App Phone Number ID as Whatsapp from Phone field
    • +
    • Use the Meta Account Business ID as Whatsapp account field (only if you need sync templates)
    • Write your own Webhook key
    • Use the Application Secret Key on Whatsapp Security Key. It will be used in order to validate the data
    • Press the Integrate Webhook Key. In this case, it will not integrate it, we need to make it manually
    • @@ -434,8 +437,16 @@

      Usage

    • Now you will be able to respond and receive messages to this person.
  • +
    +

    Known issues / Roadmap

    +

    WhatsApp templates

    + +
    -

    Bug Tracker

    +

    Bug Tracker

    Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. If you spotted it first, help us to smash it by providing a detailed and welcomed @@ -443,29 +454,40 @@

    Bug Tracker

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

    -

    Credits

    +

    Credits

    -

    Authors

    +

    Authors

    • Creu Blanca
    • Dixmit
    -

    Contributors

    +

    Contributors

    +
    -

    Other credits

    +

    Other credits

    This work has been funded by AEOdoo (Asociación Española de Odoo - https://www.aeodoo.org)

    -

    Maintainers

    +

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/mail_gateway_whatsapp/tests/__init__.py b/mail_gateway_whatsapp/tests/__init__.py index 7fcaa6a1d7..de4e35a4f4 100644 --- a/mail_gateway_whatsapp/tests/__init__.py +++ b/mail_gateway_whatsapp/tests/__init__.py @@ -1 +1,2 @@ from . import test_mail_gateway_whatsapp +from . import test_mail_whatsapp_template diff --git a/mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py b/mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py index 23977c2f47..cf15c1d728 100644 --- a/mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py +++ b/mail_gateway_whatsapp/tests/test_mail_gateway_whatsapp.py @@ -6,7 +6,10 @@ import json from unittest.mock import MagicMock, patch +from markupsafe import Markup + from odoo.exceptions import UserError +from odoo.tests import Form, RecordCapturer from odoo.tests.common import tagged from odoo.tools import mute_logger @@ -14,7 +17,7 @@ @tagged("-at_install", "post_install") -class TestMailGatewayTelegram(MailGatewayTestCase): +class TestMailGatewayWhatsApp(MailGatewayTestCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -26,6 +29,18 @@ def setUpClass(cls): "token": "token", "whatsapp_security_key": "key", "webhook_secret": "MY-SECRET", + "member_ids": [(4, cls.env.user.id)], + } + ) + cls.ws_template = cls.env["mail.whatsapp.template"].create( + { + "name": "New template", + "category": "marketing", + "language": "es", + "body": "Demo template", + "state": "approved", + "is_supported": True, + "gateway_id": cls.gateway.id, } ) cls.partner = cls.env["res.partner"].create( @@ -279,6 +294,65 @@ def test_send_document_error(self): ) self.assertEqual(message.notification_ids.notification_status, "exception") + def test_send_message_text(self): + """ + Test that the message is sent correctly + - First message need a template + - Second message does not need a template + """ + ctx = { + "default_res_model": self.partner._name, + "default_res_id": self.partner.id, + "default_number_field_name": "mobile", + "default_composition_mode": "comment", + "default_gateway_id": self.gateway.id, + } + self.gateway.whatsapp_account_id = "123456" + form_composer = Form(self.env["whatsapp.composer"].with_context(**ctx)) + form_composer.body = "Body test" + self.assertTrue(form_composer.is_required_template) + self.assertTrue(form_composer._get_modifier("template_id", "required")) + form_composer.template_id = self.ws_template + composer = form_composer.save() + self.assertEqual(composer.body, "Demo template") + channel = self.partner._whatsapp_get_channel( + composer.number_field_name, composer.gateway_id + ) + message_domain = [ + ("gateway_type", "=", "whatsapp"), + ("model", "=", channel._name), + ("res_id", "=", channel.id), + ] + with RecordCapturer(self.env["mail.message"], message_domain) as capture, patch( + "requests.post" + ) as post_mock: + post_mock.return_value = MagicMock() + composer.action_send_whatsapp() + self.assertEqual(len(capture.records), 1) + self.assertEqual(capture.records.body, Markup("

    Demo template

    ")) + # second message + form_composer = Form(self.env["whatsapp.composer"].with_context(**ctx)) + form_composer.body = "Body test" + self.assertFalse(form_composer.is_required_template) + self.assertFalse(form_composer._get_modifier("template_id", "required")) + composer = form_composer.save() + self.assertEqual(composer.body, "Body test") + channel = self.partner._whatsapp_get_channel( + composer.number_field_name, composer.gateway_id + ) + message_domain = [ + ("gateway_type", "=", "whatsapp"), + ("model", "=", channel._name), + ("res_id", "=", channel.id), + ] + with RecordCapturer(self.env["mail.message"], message_domain) as capture, patch( + "requests.post" + ) as post_mock: + post_mock.return_value = MagicMock() + composer.action_send_whatsapp() + self.assertEqual(len(capture.records), 1) + self.assertEqual(capture.records.body, Markup("

    Body test

    ")) + def test_compose(self): self.gateway.webhook_key = self.webhook self.gateway.set_webhook() diff --git a/mail_gateway_whatsapp/tests/test_mail_whatsapp_template.py b/mail_gateway_whatsapp/tests/test_mail_whatsapp_template.py new file mode 100644 index 0000000000..b7d30045d7 --- /dev/null +++ b/mail_gateway_whatsapp/tests/test_mail_whatsapp_template.py @@ -0,0 +1,170 @@ +# Copyright 2024 Tecnativa - Carlos López +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import json +from unittest.mock import patch + +import requests + +from odoo.exceptions import UserError +from odoo.tests.common import tagged + +from odoo.addons.mail_gateway.tests.common import MailGatewayTestCase + + +@tagged("-at_install", "post_install") +class TestMailWhatsAppTemplate(MailGatewayTestCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.gateway = cls.env["mail.gateway"].create( + { + "name": "gateway", + "gateway_type": "whatsapp", + "token": "token", + "whatsapp_security_key": "key", + "webhook_secret": "MY-SECRET", + "member_ids": [(4, cls.env.user.id)], + } + ) + cls.new_template_response_data = { + "id": "018273645", + "status": "APPROVED", + } + cls.new_template_full_response_data = { + "name": "new_template", + "parameter_format": "POSITIONAL", + "components": [ + {"type": "HEADER", "format": "TEXT", "text": "Header 1"}, + {"type": "BODY", "text": "Body 1"}, + {"type": "FOOTER", "text": "Footer changed"}, + ], + "language": "es", + "status": "APPROVED", + "category": "MARKETING", + "id": "018273645", + } + cls.template_1_data = { + "name": "test_odoo_1", + "parameter_format": "POSITIONAL", + "components": [ + {"type": "HEADER", "format": "TEXT", "text": "Header 1"}, + {"type": "BODY", "text": "Body 1"}, + {"type": "FOOTER", "text": "Footer 1"}, + ], + "language": "es", + "status": "APPROVED", + "category": "MARKETING", + "id": "1234567890", + } + cls.template_2_data = { + "name": "test_with_buttons", + "parameter_format": "POSITIONAL", + "components": [ + {"type": "HEADER", "format": "TEXT", "text": "Header 2"}, + {"type": "BODY", "text": "Body 2"}, + { + "type": "BUTTONS", + "buttons": [{"type": "QUICK_REPLY", "text": "Button 1"}], + }, + ], + "language": "es", + "status": "APPROVED", + "category": "MARKETING", + "sub_category": "CUSTOM", + "id": "0987654321", + } + cls.templates_download = { + "data": [cls.template_1_data, cls.template_2_data], + } + + def _make_meta_requests(self, url, json_data, status_code=200): + """ + Simulate a fake request to the Meta API: + :param json_data: Dictionary with the json data to return + :param status_code: Status code expected + :returns requests.Response object + """ + response = requests.Response() + response.status_code = status_code + response._content = json.dumps(json_data).encode() + response.url = url + response.headers["Content-Type"] = "application/json" + return response + + def test_download_templates(self): + def _patch_request_post(url, *args, **kwargs): + if "message_templates" in url: + return self._make_meta_requests(url, self.templates_download) + return original_get(url, *args, **kwargs) + + with self.assertRaisesRegex( + UserError, "WhatsApp Account is required to import templates" + ): + self.gateway.button_import_whatsapp_template() + self.gateway.whatsapp_account_id = "123456" + original_get = requests.get + with patch.object(requests, "get", _patch_request_post): + self.gateway.button_import_whatsapp_template() + self.assertEqual(self.gateway.whatsapp_template_count, 2) + template_1 = self.gateway.whatsapp_template_ids.filtered( + lambda t: t.template_uid == "1234567890" + ) + self.assertTrue(template_1.is_supported) + self.assertEqual(template_1.template_name, "test_odoo_1") + self.assertEqual(template_1.category, "marketing") + self.assertEqual(template_1.language, "es") + self.assertEqual(template_1.state, "approved") + self.assertEqual(template_1.header, "Header 1") + self.assertEqual(template_1.body, "Body 1") + self.assertEqual(template_1.footer, "Footer 1") + template_2 = self.gateway.whatsapp_template_ids.filtered( + lambda t: t.template_uid == "0987654321" + ) + self.assertFalse(template_2.is_supported) + self.assertEqual(template_2.template_name, "test_with_buttons") + self.assertEqual(template_2.category, "marketing") + self.assertEqual(template_2.language, "es") + self.assertEqual(template_2.state, "approved") + self.assertEqual(template_2.header, "Header 2") + self.assertEqual(template_2.body, "Body 2") + self.assertFalse(template_2.footer) + self.assertFalse(template_2.is_supported) + + def test_export_template(self): + def _patch_request_post(url, *args, **kwargs): + if "message_templates" in url: + return self._make_meta_requests(url, self.new_template_response_data) + return original_post(url, *args, **kwargs) + + def _patch_request_get(url, *args, **kwargs): + if "018273645" in url: + return self._make_meta_requests( + url, self.new_template_full_response_data + ) + return original_get(url, *args, **kwargs) + + original_post = requests.post + original_get = requests.get + self.gateway.whatsapp_account_id = "123456" + new_template = self.env["mail.whatsapp.template"].create( + { + "name": "New template", + "category": "marketing", + "language": "es", + "header": "Header 1", + "body": "Body 1", + "gateway_id": self.gateway.id, + } + ) + self.assertEqual(new_template.template_name, "new_template") + with patch.object(requests, "post", _patch_request_post): + new_template.button_export_template() + self.assertTrue(new_template.template_uid) + self.assertTrue(new_template.is_supported) + self.assertFalse(new_template.footer) + self.assertEqual(new_template.state, "approved") + # sync templates, footer should be updated + with patch.object(requests, "get", _patch_request_get): + new_template.button_sync_template() + self.assertEqual(new_template.footer, "Footer changed") diff --git a/mail_gateway_whatsapp/tools/__init__.py b/mail_gateway_whatsapp/tools/__init__.py new file mode 100644 index 0000000000..4c73b5fa59 --- /dev/null +++ b/mail_gateway_whatsapp/tools/__init__.py @@ -0,0 +1 @@ +from . import const diff --git a/mail_gateway_whatsapp/tools/const.py b/mail_gateway_whatsapp/tools/const.py new file mode 100644 index 0000000000..a20b782418 --- /dev/null +++ b/mail_gateway_whatsapp/tools/const.py @@ -0,0 +1,75 @@ +# https://developers.facebook.com/docs/whatsapp/business-management-api/message-templates/supported-languages # noqa: B950 +# res.lang not matching with supported languages(iso codes) +supported_languages = [ + ("af", "Afrikaans"), + ("sq", "Albanian"), + ("ar", "Arabic"), + ("az", "Azerbaijani"), + ("bn", "Bengali"), + ("bg", "Bulgarian"), + ("ca", "Catalan"), + ("zh_CN", "Chinese (CHN)"), + ("zh_HK", "Chinese (HKG)"), + ("zh_TW", "Chinese (TAI)"), + ("hr", "Croatian"), + ("cs", "Czech"), + ("da", "Danish"), + ("nl", "Dutch"), + ("en", "English"), + ("en_GB", "English (UK)"), + ("en_US", "English (US)"), + ("et", "Estonian"), + ("fil", "Filipino"), + ("fi", "Finnish"), + ("fr", "French"), + ("ka", "Georgian"), + ("de", "German"), + ("el", "Greek"), + ("gu", "Gujarati"), + ("ha", "Hausa"), + ("he", "Hebrew"), + ("hi", "Hindi"), + ("hu", "Hungarian"), + ("id", "Indonesian"), + ("ga", "Irish"), + ("it", "Italian"), + ("ja", "Japanese"), + ("kn", "Kannada"), + ("kk", "Kazakh"), + ("rw_RW", "Kinyarwanda"), + ("ko", "Korean"), + ("ky_KG", "Kyrgyz (Kyrgyzstan)"), + ("lo", "Lao"), + ("lv", "Latvian"), + ("lt", "Lithuanian"), + ("mk", "Macedonian"), + ("ms", "Malay"), + ("ml", "Malayalam"), + ("mr", "Marathi"), + ("nb", "Norwegian"), + ("fa", "Persian"), + ("pl", "Polish"), + ("pt_BR", "Portuguese (BR)"), + ("pt_PT", "Portuguese (POR)"), + ("pa", "Punjabi"), + ("ro", "Romanian"), + ("ru", "Russian"), + ("sr", "Serbian"), + ("sk", "Slovak"), + ("sl", "Slovenian"), + ("es", "Spanish"), + ("es_AR", "Spanish (ARG)"), + ("es_ES", "Spanish (SPA)"), + ("es_MX", "Spanish (MEX)"), + ("sw", "Swahili"), + ("sv", "Swedish"), + ("ta", "Tamil"), + ("te", "Telugu"), + ("th", "Thai"), + ("tr", "Turkish"), + ("uk", "Ukrainian"), + ("ur", "Urdu"), + ("uz", "Uzbek"), + ("vi", "Vietnamese"), + ("zu", "Zulu"), +] diff --git a/mail_gateway_whatsapp/views/mail_gateway.xml b/mail_gateway_whatsapp/views/mail_gateway.xml index 159eb034f1..6d3a06ff6c 100644 --- a/mail_gateway_whatsapp/views/mail_gateway.xml +++ b/mail_gateway_whatsapp/views/mail_gateway.xml @@ -7,7 +7,35 @@ mail.gateway + + + + + + + + view.mail.whatsapp.template.tree + mail.whatsapp.template + + + + + + + + + + + + + + + + + view.mail.whatsapp.template.form + mail.whatsapp.template + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + + +
    +
    +
    +
    + + + view.mail.whatsapp.template.search + mail.whatsapp.template + + + + + + + + + + + WhatsApp Templates + ir.actions.act_window + mail.whatsapp.template + tree,form + [('gateway_id', '=', active_id)] + {'default_gateway_id': active_id} + + +
    diff --git a/mail_gateway_whatsapp/wizards/__init__.py b/mail_gateway_whatsapp/wizards/__init__.py index 734f1724ac..47359283f4 100644 --- a/mail_gateway_whatsapp/wizards/__init__.py +++ b/mail_gateway_whatsapp/wizards/__init__.py @@ -1 +1,2 @@ +from . import mail_compose_gateway_message from . import whatsapp_composer diff --git a/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.py b/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.py new file mode 100644 index 0000000000..43422e676b --- /dev/null +++ b/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.py @@ -0,0 +1,30 @@ +# Copyright 2024 Tecnativa - Carlos López +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import markupsafe + +from odoo import api, fields, models + + +class MailComposeGatewayMessage(models.TransientModel): + _inherit = "mail.compose.gateway.message" + + whatsapp_template_id = fields.Many2one( + "mail.whatsapp.template", + domain="""[ + ('state', '=', 'approved'), + ('is_supported', '=', True) + ]""", + ) + + @api.onchange("whatsapp_template_id") + def onchange_whatsapp_template_id(self): + if self.whatsapp_template_id: + self.body = markupsafe.Markup(self.whatsapp_template_id.body) + + def _action_send_mail(self, auto_commit=False): + if self.whatsapp_template_id: + self = self.with_context(whatsapp_template_id=self.whatsapp_template_id.id) + return super(MailComposeGatewayMessage, self)._action_send_mail( + auto_commit=auto_commit + ) diff --git a/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.xml b/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.xml new file mode 100644 index 0000000000..b0c3aafe97 --- /dev/null +++ b/mail_gateway_whatsapp/wizards/mail_compose_gateway_message.xml @@ -0,0 +1,31 @@ + + + + + + mail.compose.gateway.message + + + + + + + {'invisible': [('whatsapp_template_id', '!=', False)]} + + + {'invisible': [('whatsapp_template_id', '!=', False)]} + + + + diff --git a/mail_gateway_whatsapp/wizards/whatsapp_composer.py b/mail_gateway_whatsapp/wizards/whatsapp_composer.py index 4c3a7f8695..5414bc2026 100644 --- a/mail_gateway_whatsapp/wizards/whatsapp_composer.py +++ b/mail_gateway_whatsapp/wizards/whatsapp_composer.py @@ -1,5 +1,7 @@ # Copyright 2022 CreuBlanca +# Copyright 2024 Tecnativa - Carlos López # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from datetime import datetime from odoo import _, api, fields, models from odoo.exceptions import UserError @@ -17,7 +19,57 @@ class WhatsappComposer(models.TransientModel): gateway_id = fields.Many2one( "mail.gateway", domain=[("gateway_type", "=", "whatsapp")], required=True ) + template_id = fields.Many2one( + "mail.whatsapp.template", + domain="""[ + ('gateway_id', '=', gateway_id), + ('state', '=', 'approved'), + ('is_supported', '=', True) + ]""", + ) body = fields.Text("Message") + is_required_template = fields.Boolean(compute="_compute_is_required_template") + + @api.depends("res_model", "res_id", "number_field_name", "gateway_id") + def _compute_is_required_template(self): + MailMessage = self.env["mail.message"] + for wizard in self: + if ( + not wizard.res_model + or not wizard.gateway_id + or not wizard.number_field_name + ): + wizard.is_required_template = False + continue + record = self.env[wizard.res_model].browse(wizard.res_id) + is_required_template = True + channel = record._whatsapp_get_channel( + wizard.number_field_name, wizard.gateway_id + ) + if channel: + last_message = MailMessage.search( + [ + ("gateway_type", "=", "whatsapp"), + ("model", "=", channel._name), + ("res_id", "=", channel.id), + ], + order="date desc", + limit=1, + ) + if last_message: + delta = (datetime.now() - last_message.date).total_seconds() / 3600 + if delta < 24.0: + is_required_template = False + wizard.is_required_template = is_required_template + + @api.onchange("gateway_id") + def onchange_gateway_id(self): + self.template_id = False + + @api.onchange("template_id") + def onchange_template_id(self): + if self.template_id: + self.body = self.template_id.body @api.model def default_get(self, fields): @@ -33,7 +85,7 @@ def _action_send_whatsapp(self): if not record: return channel = record._whatsapp_get_channel(self.number_field_name, self.gateway_id) - channel.message_post( + channel.with_context(whatsapp_template_id=self.template_id.id).message_post( body=self.body, subtype_xmlid="mail.mt_comment", message_type="comment" ) diff --git a/mail_gateway_whatsapp/wizards/whatsapp_composer.xml b/mail_gateway_whatsapp/wizards/whatsapp_composer.xml index 8bc4851fcd..efd5f34adc 100644 --- a/mail_gateway_whatsapp/wizards/whatsapp_composer.xml +++ b/mail_gateway_whatsapp/wizards/whatsapp_composer.xml @@ -13,11 +13,21 @@ name="gateway_id" attrs="{'invisible': [('find_gateway', '=', False)]}" /> + - + +

    Bug Tracker

    @@ -421,12 +424,15 @@

    Contributors

  • Laurent Mignon <laurent.mignon@acsone.eu>
  • Andrea Stirpe <a.stirpe@onestein.nl>
  • Robin Goots <robin.goots@dynapps.be>
  • +
  • Quan Nguyen <quan.nhm@komit-consulting.com>
  • Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/mail_optional_follower_notification/static/description/optional_follower_003.png b/mail_optional_follower_notification/static/description/optional_follower_003.png new file mode 100644 index 0000000000..c300f27897 Binary files /dev/null and b/mail_optional_follower_notification/static/description/optional_follower_003.png differ diff --git a/mail_optional_follower_notification/views/res_config_settings_view.xml b/mail_optional_follower_notification/views/res_config_settings_view.xml new file mode 100644 index 0000000000..dba75107a0 --- /dev/null +++ b/mail_optional_follower_notification/views/res_config_settings_view.xml @@ -0,0 +1,23 @@ + + + res.config.settings.view.form.inherit.mail + res.config.settings + + + + +
    +
    + +
    +
    +
    +
    +
    +
    +
    +
    diff --git a/mail_optional_follower_notification/wizard/mail_compose_message.py b/mail_optional_follower_notification/wizard/mail_compose_message.py index 0cd83ec37e..6ee12acd8f 100644 --- a/mail_optional_follower_notification/wizard/mail_compose_message.py +++ b/mail_optional_follower_notification/wizard/mail_compose_message.py @@ -7,7 +7,9 @@ class MailComposeMessage(models.TransientModel): _inherit = "mail.compose.message" - notify_followers = fields.Boolean(default=True) + notify_followers = fields.Boolean( + default=lambda self: self.env.company.notify_followers + ) def _action_send_mail(self, auto_commit=False): result_mails_su, result_messages = ( diff --git a/mail_restrict_follower_selection/i18n/it.po b/mail_restrict_follower_selection/i18n/it.po index 3dc0fcbfef..a6adc3cf6c 100644 --- a/mail_restrict_follower_selection/i18n/it.po +++ b/mail_restrict_follower_selection/i18n/it.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-05-17 01:02+0000\n" -"PO-Revision-Date: 2024-06-13 13:36+0000\n" +"PO-Revision-Date: 2025-02-20 11:06+0000\n" "Last-Translator: mymage \n" "Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" "Language: it\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: mail_restrict_follower_selection #: model:ir.actions.act_window,name:mail_restrict_follower_selection.action_setup @@ -37,7 +37,7 @@ msgstr "Discussione e-mail" #. module: mail_restrict_follower_selection #: model:ir.model,name:mail_restrict_follower_selection.model_mail_wizard_invite msgid "Invite wizard" -msgstr "Wizard Creazione Invito" +msgstr "Procedura guidata invito" #~ msgid "Display Name" #~ msgstr "Nome da visualizzare" diff --git a/mail_send_confirmation/i18n/fr.po b/mail_send_confirmation/i18n/fr.po index 9aa2441f8e..d733d267d9 100644 --- a/mail_send_confirmation/i18n/fr.po +++ b/mail_send_confirmation/i18n/fr.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-03-13 10:36+0000\n" +"PO-Revision-Date: 2025-02-14 16:06+0000\n" "Last-Translator: Pierre-François Teyssier \n" "Language-Team: none\n" "Language: fr\n" @@ -14,7 +14,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n > 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: mail_send_confirmation #. odoo-javascript @@ -28,7 +28,7 @@ msgstr "Confirmer" #: code:addons/mail_send_confirmation/static/src/models/composer_view.esm.js:0 #, python-format msgid "Confirmation" -msgstr "" +msgstr "Confirmation" #. module: mail_send_confirmation #. odoo-javascript @@ -46,4 +46,4 @@ msgid "" "would like to send this message?" msgstr "" "Ce message sera également envoyé aux partenaires externes. Êtes vous sûr de " -"vouloir envoyer ce message?" +"vouloir envoyer ce message ?" diff --git a/mail_show_follower/i18n/pt_BR.po b/mail_show_follower/i18n/pt_BR.po index c6618427f4..83d093d174 100644 --- a/mail_show_follower/i18n/pt_BR.po +++ b/mail_show_follower/i18n/pt_BR.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"PO-Revision-Date: 2024-07-10 18:58+0000\n" +"PO-Revision-Date: 2024-12-19 19:06+0000\n" "Last-Translator: Rodrigo Sottomaior Macedo " "\n" "Language-Team: none\n" @@ -45,7 +45,7 @@ msgstr "Visualização da Mensagem" #. module: mail_show_follower #: model_terms:ir.ui.view,arch_db:mail_show_follower.res_config_settings_view_form msgid "Models to exclude" -msgstr "" +msgstr "Modelos para exclusão" #. module: mail_show_follower #: model_terms:ir.ui.view,arch_db:mail_show_follower.res_config_settings_view_form @@ -67,7 +67,7 @@ msgstr "Formato do Parceiro" #. module: mail_show_follower #: model:ir.model.fields,field_description:mail_show_follower.field_res_config_settings__show_followers_models_to_exclude msgid "Show Followers Models To Exclude" -msgstr "" +msgstr "Mostrar modelos de seguidores para excluir" #. module: mail_show_follower #: model_terms:ir.ui.view,arch_db:mail_show_follower.res_config_settings_view_form @@ -107,7 +107,7 @@ msgstr "" #. module: mail_show_follower #: model:ir.model.fields,help:mail_show_follower.field_res_config_settings__show_followers_models_to_exclude msgid "Tecnichal model names separated by coma" -msgstr "" +msgstr "Nomes de modelos técnicos separados por vírgula" #. module: mail_show_follower #: model:ir.model.fields,field_description:mail_show_follower.field_res_company__show_followers_message_response_warning @@ -134,4 +134,4 @@ msgstr "Usuário" #. module: mail_show_follower #: model_terms:ir.ui.view,arch_db:mail_show_follower.res_config_settings_view_form msgid "blog.blog,blog.post" -msgstr "" +msgstr "blog.blog,blog.post" diff --git a/mail_tracking/i18n/tr.po b/mail_tracking/i18n/tr.po index 2c0a11280c..74e1d25138 100644 --- a/mail_tracking/i18n/tr.po +++ b/mail_tracking/i18n/tr.po @@ -9,14 +9,15 @@ msgstr "" "Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-22 00:51+0000\n" -"PO-Revision-Date: 2017-07-22 00:51+0000\n" -"Last-Translator: OCA Transbot , 2016\n" +"PO-Revision-Date: 2025-02-12 14:06+0000\n" +"Last-Translator: Ahmet Yiğit Budak \n" "Language-Team: Turkish (https://www.transifex.com/oca/teams/23907/tr/)\n" "Language: tr\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.6.2\n" #. module: mail_tracking #: model:ir.model.fields,help:mail_tracking.field_mail_tracking_email__state @@ -41,11 +42,31 @@ msgid "" " * The 'Soft bounced' status indicates that message was soft bounced by " "recipient Mail Exchange (MX) server.\n" msgstr "" +" * 'Hata' durumu, e-posta gönderilmeye çalışılırken bir hata oluştuğunu " +"belirtir, örneğin, 'Geçerli alıcı yok'.\n" +" * 'Gönderildi' durumu, mesajın giden e-posta sunucusu (SMTP) üzerinden " +"başarıyla gönderildiğini belirtir.\n" +" * 'Teslim Edildi' durumu, mesajın alıcının Mail Exchange (MX) sunucusuna " +"başarıyla iletildiğini belirtir.\n" +" * 'Açıldı' durumu, mesajın alıcı tarafından açıldığını veya tıklandığını " +"belirtir.\n" +" * 'Reddedildi' durumu, alıcı e-posta adresinin giden e-posta sunucusu (SMTP)" +" tarafından kara listeye alındığını belirtir. Bu e-posta adresini silmeniz " +"önerilir.\n" +" * 'Spam' durumu, giden e-posta sunucusunun (SMTP) bu mesajı spam olarak " +"değerlendirdiğini belirtir.\n" +" * 'Abonelikten Çıkıldı' durumu, alıcının bu mesajdan abonelikten çıkmak " +"istediğini belirtir.\n" +" * 'Bounced' durumu, mesajın alıcının Mail Exchange (MX) sunucusu tarafından " +"geri döndürüldüğünü belirtir.\n" +" * 'Soft bounced' durumu, mesajın alıcının Mail Exchange (MX) sunucusu " +"tarafından yumuşak bir şekilde geri döndürüldüğünü belirtir.\n" #. module: mail_tracking #: model:ir.model.fields,help:mail_tracking.field_mail_message__email_cc msgid "Additional recipients that receive a \"Carbon Copy\" of the e-mail" msgstr "" +"Ek alıcılar, e-postanın bir \"Karbon Kopyasını\" (Carbon Copy - CC) alır" #. module: mail_tracking #. odoo-python @@ -59,7 +80,7 @@ msgstr "" #: code:addons/mail_tracking/static/src/components/discuss/discuss.xml:0 #, python-format msgid "Anonymous Recipient" -msgstr "" +msgstr "Anonim Alıcı" #. module: mail_tracking #: model_terms:ir.ui.view,arch_db:mail_tracking.view_mail_tracking_event_search @@ -69,12 +90,12 @@ msgstr "" #. module: mail_tracking #: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__bounce_description msgid "Bounce Description" -msgstr "" +msgstr "Bounce Açıklaması" #. module: mail_tracking #: model:ir.model.fields,field_description:mail_tracking.field_mail_tracking_email__bounce_type msgid "Bounce Type" -msgstr "" +msgstr "Bounce Tipi" #. module: mail_tracking #: model:ir.model.fields.selection,name:mail_tracking.selection__mail_tracking_email__state__bounced diff --git a/mass_mailing_custom_unsubscribe/README.rst b/mass_mailing_custom_unsubscribe/README.rst index ef685211d8..ab9cca3e16 100644 --- a/mass_mailing_custom_unsubscribe/README.rst +++ b/mass_mailing_custom_unsubscribe/README.rst @@ -7,7 +7,7 @@ Customizable unsubscription process on mass mailing emails !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:3cd60bd009ca6770a962ab0284115db4c24a67db1644f355bd289190843a520d + !! source digest: sha256:13d8151e265fcc908d241a316fdd3838390cff084e07df446bcbcb523ceb8904 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/mass_mailing_custom_unsubscribe/__manifest__.py b/mass_mailing_custom_unsubscribe/__manifest__.py index 6075d1ef5f..ccab564c1f 100644 --- a/mass_mailing_custom_unsubscribe/__manifest__.py +++ b/mass_mailing_custom_unsubscribe/__manifest__.py @@ -7,7 +7,7 @@ "name": "Customizable unsubscription process on mass mailing emails", "summary": "Know and track (un)subscription reasons, GDPR compliant", "category": "Marketing", - "version": "16.0.1.0.1", + "version": "16.0.1.0.2", "depends": ["mass_mailing"], "data": [ "security/ir.model.access.csv", diff --git a/mass_mailing_custom_unsubscribe/security/ir.model.access.csv b/mass_mailing_custom_unsubscribe/security/ir.model.access.csv index b8241abbbf..47a6a483b5 100644 --- a/mass_mailing_custom_unsubscribe/security/ir.model.access.csv +++ b/mass_mailing_custom_unsubscribe/security/ir.model.access.csv @@ -1,5 +1,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink read_unsubscription_reason_public,Public users can read unsubscription reasons,model_mail_unsubscription_reason,base.group_public,1,0,0,0 +read_unsubscription_reason_portal,Portal users can read unsubscription reasons,model_mail_unsubscription_reason,base.group_portal,1,0,0,0 read_unsubscription_reason_employee,Employee users can read unsubscription reasons,model_mail_unsubscription_reason,base.group_user,1,0,0,0 write_unsubscription_reason,Mass mailing managers can manage unsubscription reasons,model_mail_unsubscription_reason,mass_mailing.group_mass_mailing_user,1,1,1,1 read_unsubscription,Marketing users can read unsubscriptions,model_mail_unsubscription,mass_mailing.group_mass_mailing_user,1,0,0,0 diff --git a/mass_mailing_custom_unsubscribe/static/description/index.html b/mass_mailing_custom_unsubscribe/static/description/index.html index 0a24fd8625..b5df11a102 100644 --- a/mass_mailing_custom_unsubscribe/static/description/index.html +++ b/mass_mailing_custom_unsubscribe/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +: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. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +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 } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -366,7 +367,7 @@

    Customizable unsubscription process on mass mailing emails

    Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

    This addon extends the unsubscription form to let you:

    @@ -471,7 +472,9 @@

    Contributors

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.esm.js b/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.esm.js index 50c27ebee8..e099db02ab 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.esm.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/contact.tour.esm.js @@ -16,6 +16,10 @@ tour.register( test: true, }, [ + { + content: "Confirm unsubscribe", + trigger: "button:contains('Unsubscribe')", + }, { content: "Choose other reason", trigger: ".radio:contains('Other reason') :radio:not(:checked)", diff --git a/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.esm.js b/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.esm.js index cbd0f3c344..2578cf49f2 100644 --- a/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.esm.js +++ b/mass_mailing_custom_unsubscribe/static/src/js/partner.tour.esm.js @@ -16,6 +16,10 @@ tour.register( test: true, }, [ + { + content: "Confirm unsubscribe", + trigger: "button:contains('Unsubscribe')", + }, { content: "Choose other reason", trigger: ".radio:contains('Other reason') :radio:not(:checked)", diff --git a/mass_mailing_event_registration_exclude/i18n/it.po b/mass_mailing_event_registration_exclude/i18n/it.po index 837ca23735..360adebf2d 100644 --- a/mass_mailing_event_registration_exclude/i18n/it.po +++ b/mass_mailing_event_registration_exclude/i18n/it.po @@ -9,7 +9,7 @@ msgstr "" "Project-Id-Version: Odoo Server 10.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-01 02:19+0000\n" -"PO-Revision-Date: 2024-01-05 15:33+0000\n" +"PO-Revision-Date: 2025-01-16 15:06+0000\n" "Last-Translator: mymage \n" "Language-Team: Italian (https://www.transifex.com/oca/teams/23907/it/)\n" "Language: it\n" @@ -17,7 +17,7 @@ msgstr "" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: \n" "Plural-Forms: nplurals=2; plural=n != 1;\n" -"X-Generator: Weblate 4.17\n" +"X-Generator: Weblate 5.6.2\n" #. module: mass_mailing_event_registration_exclude #: model:ir.model.fields,field_description:mass_mailing_event_registration_exclude.field_event_registration_state__code @@ -87,7 +87,7 @@ msgstr "Ultimo aggiornamento il" #. module: mass_mailing_event_registration_exclude #: model:ir.model,name:mass_mailing_event_registration_exclude.model_mailing_contact msgid "Mailing Contact" -msgstr "Contatto Mass Mailing" +msgstr "Contatto spedizione" #. module: mass_mailing_event_registration_exclude #: model:ir.model,name:mass_mailing_event_registration_exclude.model_mailing_mailing diff --git a/mass_mailing_list_dynamic/README.rst b/mass_mailing_list_dynamic/README.rst index 2519a462b4..baf6b87f19 100644 --- a/mass_mailing_list_dynamic/README.rst +++ b/mass_mailing_list_dynamic/README.rst @@ -7,7 +7,7 @@ Dynamic Mass Mailing Lists !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:d54687657712efee61f900722a799a865953c5668e08c46496633b6a7e75f0ff + !! source digest: sha256:381db0b3262500d95e221ecfc6c4c586d596d033cb56dde2eb82dbed00b4a731 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -126,6 +126,10 @@ Contributors * Nguyễn Minh Chiến +* `Moduon `_: + + * Jairo Llopis + Other credits ~~~~~~~~~~~~~ diff --git a/mass_mailing_list_dynamic/__manifest__.py b/mass_mailing_list_dynamic/__manifest__.py index ff6ddeba67..aa66566164 100644 --- a/mass_mailing_list_dynamic/__manifest__.py +++ b/mass_mailing_list_dynamic/__manifest__.py @@ -5,7 +5,7 @@ { "name": "Dynamic Mass Mailing Lists", "summary": "Mass mailing lists that get autopopulated", - "version": "16.0.1.0.0", + "version": "16.0.2.0.0", "category": "Marketing", "website": "https://github.com/OCA/social", "author": "Tecnativa, Odoo Community Association (OCA)", diff --git a/mass_mailing_list_dynamic/readme/CONTRIBUTORS.rst b/mass_mailing_list_dynamic/readme/CONTRIBUTORS.rst index 6d302e4cc8..23a9f49c0a 100644 --- a/mass_mailing_list_dynamic/readme/CONTRIBUTORS.rst +++ b/mass_mailing_list_dynamic/readme/CONTRIBUTORS.rst @@ -17,3 +17,7 @@ * `Trobz `_: * Nguyễn Minh Chiến + +* `Moduon `_: + + * Jairo Llopis diff --git a/mass_mailing_list_dynamic/static/description/index.html b/mass_mailing_list_dynamic/static/description/index.html index 5b0e026267..a77a80ffbe 100644 --- a/mass_mailing_list_dynamic/static/description/index.html +++ b/mass_mailing_list_dynamic/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +: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. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +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 } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -367,7 +367,7 @@

    Dynamic Mass Mailing Lists

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:d54687657712efee61f900722a799a865953c5668e08c46496633b6a7e75f0ff +!! source digest: sha256:381db0b3262500d95e221ecfc6c4c586d596d033cb56dde2eb82dbed00b4a731 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

    Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

    Without this addon you have to choose between providing a dynamic domain and @@ -479,6 +479,13 @@

    Contributors

    +
  • Moduon:

    +
    +
      +
    • Jairo Llopis
    • +
    +
    +
  • @@ -488,7 +495,9 @@

    Other credits

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/mass_mailing_list_dynamic/tests/test_dynamic_lists.py b/mass_mailing_list_dynamic/tests/test_dynamic_lists.py index 112dbeca00..530cadd759 100644 --- a/mass_mailing_list_dynamic/tests/test_dynamic_lists.py +++ b/mass_mailing_list_dynamic/tests/test_dynamic_lists.py @@ -14,15 +14,16 @@ class DynamicListCase(common.TransactionCase): def setUpClass(cls): super().setUpClass() cls.tag = cls.env["res.partner.category"].create({"name": "testing tag"}) - cls.partners = cls.env["res.partner"] - for number in range(5): - cls.partners |= cls.partners.create( + cls.partners = cls.env["res.partner"].create( + [ { "name": "partner %d" % number, "category_id": [(4, cls.tag.id, False)], "email": "%d@example.com" % number, } - ) + for number in range(5) + ] + ) cls.list = cls.env["mailing.list"].create( { "name": "test list", @@ -180,3 +181,17 @@ def test_partners_merge(self): wizard.action_merge() self.assertTrue(partner_1.id in self.list.contact_ids.mapped("partner_id").ids) self.assertTrue(partner_1.id in list2.contact_ids.mapped("partner_id").ids) + + def test_synced_contacts_can_be_bounced(self): + self.list.sync_method = "full" + self.list.action_sync() + contact = self.env["mailing.contact"].search( + [ + ("list_ids", "in", self.list.ids), + ("partner_id", "=", self.partners[0].id), + ] + ) + # A bounce arrives through fetchmail + contact._message_receive_bounce(contact.email, contact.partner_id) + # The contact is marked as bounced + self.assertEqual(contact.message_bounce, 1) diff --git a/mass_mailing_partner/README.rst b/mass_mailing_partner/README.rst index b6549c5ecb..9cec9c75eb 100644 --- a/mass_mailing_partner/README.rst +++ b/mass_mailing_partner/README.rst @@ -7,7 +7,7 @@ Link partners with mass-mailing !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:a712294673e6879264c33b711c2fa62256e598736aa9b6c95f3aef1e5ae0e352 + !! source digest: sha256:12f10b00f27a13c7938cacf9d5a525fa6e9dc2b0dea6f06ee26223f67bbaec49 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png @@ -98,6 +98,10 @@ Contributors * Nguyễn Minh Chiến +* `Moduon `_: + + * Jairo Llopis + Other credits ~~~~~~~~~~~~~ diff --git a/mass_mailing_partner/__manifest__.py b/mass_mailing_partner/__manifest__.py index 1be6c59a04..626e5d20c1 100644 --- a/mass_mailing_partner/__manifest__.py +++ b/mass_mailing_partner/__manifest__.py @@ -6,7 +6,7 @@ { "name": "Link partners with mass-mailing", - "version": "16.0.1.0.0", + "version": "16.0.2.0.0", "author": "Tecnativa, " "Odoo Community Association (OCA)", "website": "https://github.com/OCA/social", "license": "AGPL-3", diff --git a/mass_mailing_partner/models/mailing_contact.py b/mass_mailing_partner/models/mailing_contact.py index 5316cc1bc3..0e4752a20a 100644 --- a/mass_mailing_partner/models/mailing_contact.py +++ b/mass_mailing_partner/models/mailing_contact.py @@ -53,37 +53,30 @@ def _onchange_partner_mass_mailing_partner(self): ) self.country_id = self.partner_id.country_id - @api.model - def _get_contact_vals(self, origin_vals): - record = self.new(origin_vals) - if not record.partner_id: - record._set_partner() - record._onchange_partner_mass_mailing_partner() - new_vals = record._convert_to_write(record._cache) - new_vals.update( - subscription_list_ids=origin_vals.get("subscription_list_ids", []), - list_ids=origin_vals.get("list_ids", []), - ) - if new_vals.get("partner_id") and "tag_ids" in new_vals: - # When there is a partner, tag_ids must get value from the compute function - # otherwise, its values will be different from partner - del new_vals["tag_ids"] - return new_vals + def _overwrite_partner(self, vals, creating=False): + """Overwrite partner and update contact data if needed.""" + self.ensure_one() + if self.env.context.get("mass_mailing_partner_writing"): + return + _self = self.with_context(mass_mailing_partner_writing=True) + prev_partner = _self.partner_id + if "partner_id" not in vals: + _self._set_partner() + if creating or prev_partner != _self.partner_id: + _self._onchange_partner_mass_mailing_partner() @api.model_create_multi def create(self, vals_list): - new_vals_list = [] - for vals in vals_list: - new_vals = self._get_contact_vals(vals) - new_vals_list.append(new_vals) - return super().create(new_vals_list) + result = super().create(vals_list) + for contact, vals in zip(result, vals_list): + contact._overwrite_partner(vals, True) + return result def write(self, vals): + result = super().write(vals) for contact in self: - origin_vals = contact.copy_data(vals)[0] - new_vals = self._get_contact_vals(origin_vals) - super(MailingContact, contact).write(new_vals) - return True + contact._overwrite_partner(vals) + return result def _get_categories(self): ca_ids = ( @@ -113,6 +106,8 @@ def _set_partner(self): email = self.email.strip() partner = m_partner.search([("email", "=ilike", email)], limit=1) if partner: + if partner == self.partner_id: + return # Partner found self.partner_id = partner else: diff --git a/mass_mailing_partner/readme/CONTRIBUTORS.rst b/mass_mailing_partner/readme/CONTRIBUTORS.rst index 72bda72d0e..b8a68a83c1 100644 --- a/mass_mailing_partner/readme/CONTRIBUTORS.rst +++ b/mass_mailing_partner/readme/CONTRIBUTORS.rst @@ -16,3 +16,7 @@ * `Trobz `_: * Nguyễn Minh Chiến + +* `Moduon `_: + + * Jairo Llopis diff --git a/mass_mailing_partner/static/description/index.html b/mass_mailing_partner/static/description/index.html index 20b3beea1d..9c95eaed2b 100644 --- a/mass_mailing_partner/static/description/index.html +++ b/mass_mailing_partner/static/description/index.html @@ -1,4 +1,3 @@ - @@ -9,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +: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. @@ -275,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +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 } @@ -301,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -367,7 +367,7 @@

    Link partners with mass-mailing

    !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:a712294673e6879264c33b711c2fa62256e598736aa9b6c95f3aef1e5ae0e352 +!! source digest: sha256:12f10b00f27a13c7938cacf9d5a525fa6e9dc2b0dea6f06ee26223f67bbaec49 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

    Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

    This module links mass-mailing contacts with partners.

    @@ -447,6 +447,13 @@

    Contributors

    +
  • Moduon:

    +
    +
      +
    • Jairo Llopis
    • +
    +
    +
  • @@ -456,7 +463,9 @@

    Other credits

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use.

    diff --git a/mass_mailing_partner/tests/test_partner_mail_list_wizard.py b/mass_mailing_partner/tests/test_partner_mail_list_wizard.py index 20beef65b0..dc7dd79f76 100644 --- a/mass_mailing_partner/tests/test_partner_mail_list_wizard.py +++ b/mass_mailing_partner/tests/test_partner_mail_list_wizard.py @@ -14,7 +14,7 @@ def test_add_to_mail_list(self): wizard = self.env["partner.mail.list.wizard"].create( {"mail_list_id": self.mailing_list.id} ) - wizard.partner_ids = [self.partner.id] + wizard.partner_ids = self.partner wizard.add_to_mail_list() contacts = self.env["mailing.contact"].search( [("partner_id", "=", self.partner.id)] diff --git a/mass_mailing_partner/wizard/partner_mail_list_wizard.py b/mass_mailing_partner/wizard/partner_mail_list_wizard.py index 0c4949d14e..a75363a721 100644 --- a/mass_mailing_partner/wizard/partner_mail_list_wizard.py +++ b/mass_mailing_partner/wizard/partner_mail_list_wizard.py @@ -36,8 +36,8 @@ def add_to_mail_list(self): contact_vals = { "partner_id": partner.id, "list_ids": [(4, self.mail_list_id.id)], - "title_id": partner.title or False, + "title_id": partner.title.id or False, "company_name": partner.company_id.name or False, - "country_id": partner.country_id or False, + "country_id": partner.country_id.id or False, } contact_obj.create(contact_vals) diff --git a/outgoing_email_by_model/README.rst b/outgoing_email_by_model/README.rst new file mode 100644 index 0000000000..901396484b --- /dev/null +++ b/outgoing_email_by_model/README.rst @@ -0,0 +1,95 @@ +======================= +Outgoing Email by Model +======================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:d0f8f708d1f070c36f8177a3aed9963a44d051a537394cd2e87bfbfb59782b83 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsocial-lightgray.png?logo=github + :target: https://github.com/OCA/social/tree/16.0/outgoing_email_by_model + :alt: OCA/social +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/social-16-0/social-16-0-outgoing_email_by_model + :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/social&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Allow to select an outgoing mail parameters per model, such as email_from, or mail_server_id. + +To make it work, the model must inherit `mail.thread`. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +If you want to use a specific outgoing mail for a model, populate the following fields in the model record (Settings > Technical > Models) accordingly: + +* Outgoing Mailserver: Set an outgoing mail server. This will be used as the forced outgoing mail server for the model. +* Outgoing Email: Set an email address. This will be used as the outgoing email for the model. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp + +Contributors +~~~~~~~~~~~~ + +* Matthieu Méquignon +* Emilie SOUTIRAS + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-mmequignon| image:: https://github.com/mmequignon.png?size=40px + :target: https://github.com/mmequignon + :alt: mmequignon + +Current `maintainer `__: + +|maintainer-mmequignon| + +This module is part of the `OCA/social `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/outgoing_email_by_model/__init__.py b/outgoing_email_by_model/__init__.py new file mode 100644 index 0000000000..0650744f6b --- /dev/null +++ b/outgoing_email_by_model/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/outgoing_email_by_model/__manifest__.py b/outgoing_email_by_model/__manifest__.py new file mode 100644 index 0000000000..54cda4b29c --- /dev/null +++ b/outgoing_email_by_model/__manifest__.py @@ -0,0 +1,23 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "Outgoing Email by Model", + "version": "16.0.1.0.0", + "category": "Social", + "website": "https://github.com/OCA/social", + "author": "Camptocamp, Odoo Community Association (OCA)", + "maintainers": ["mmequignon"], + "license": "AGPL-3", + "installable": True, + "auto_install": False, + "external_dependencies": { + "python": ["odoo_test_helper"], + }, + "depends": [ + "mail", + ], + "data": [ + "views/ir_model.xml", + ], +} diff --git a/outgoing_email_by_model/i18n/it.po b/outgoing_email_by_model/i18n/it.po new file mode 100644 index 0000000000..2024e57667 --- /dev/null +++ b/outgoing_email_by_model/i18n/it.po @@ -0,0 +1,63 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * outgoing_email_by_model +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2024-12-19 10:06+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.6.2\n" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,help:outgoing_email_by_model.field_ir_model__outgoing_email +msgid "" +"\n" +" Allows to force the usage of a given email address if this setting is set.\n" +" However, this setting will be active only if the model extends `mail.thread`.\n" +msgstr "" +"\n" +" Consente di forzare l'utilizzo di un dato indirizzo email se questa " +"impostazione è impostata.\n" +" Tuttavia, questa impostazione sarà attiva solo se il modello estende " +"`mail.thread`.\n" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,help:outgoing_email_by_model.field_ir_model__outgoing_mailserver_id +msgid "" +"\n" +" Allows to force the usage of a given outgoing mail server if this setting is set.\n" +" However, this setting will be active only if the model extends `mail.thread`.\n" +msgstr "" +"\n" +" Consente di forzare l'utilizzo di un dato server di posta in uscita se " +"questa impostazione è impostata. \n" +" Tuttavia, questa impostazione sarà attiva solo se il modello estende " +"`mail.thread`.\n" + +#. module: outgoing_email_by_model +#: model:ir.model,name:outgoing_email_by_model.model_mail_thread +msgid "Email Thread" +msgstr "Discussione e-mail" + +#. module: outgoing_email_by_model +#: model:ir.model,name:outgoing_email_by_model.model_ir_model +msgid "Models" +msgstr "Modelli" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,field_description:outgoing_email_by_model.field_ir_model__outgoing_email +msgid "Outgoing Email" +msgstr "E-mail in uscita" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,field_description:outgoing_email_by_model.field_ir_model__outgoing_mailserver_id +msgid "Outgoing Mailserver" +msgstr "Server e-mail in uscita" diff --git a/outgoing_email_by_model/i18n/outgoing_email_by_model.pot b/outgoing_email_by_model/i18n/outgoing_email_by_model.pot new file mode 100644 index 0000000000..b97a0c4c28 --- /dev/null +++ b/outgoing_email_by_model/i18n/outgoing_email_by_model.pot @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * outgoing_email_by_model +# +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: outgoing_email_by_model +#: model:ir.model.fields,help:outgoing_email_by_model.field_ir_model__outgoing_email +msgid "" +"\n" +" Allows to force the usage of a given email address if this setting is set.\n" +" However, this setting will be active only if the model extends `mail.thread`.\n" +msgstr "" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,help:outgoing_email_by_model.field_ir_model__outgoing_mailserver_id +msgid "" +"\n" +" Allows to force the usage of a given outgoing mail server if this setting is set.\n" +" However, this setting will be active only if the model extends `mail.thread`.\n" +msgstr "" + +#. module: outgoing_email_by_model +#: model:ir.model,name:outgoing_email_by_model.model_mail_thread +msgid "Email Thread" +msgstr "" + +#. module: outgoing_email_by_model +#: model:ir.model,name:outgoing_email_by_model.model_ir_model +msgid "Models" +msgstr "" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,field_description:outgoing_email_by_model.field_ir_model__outgoing_email +msgid "Outgoing Email" +msgstr "" + +#. module: outgoing_email_by_model +#: model:ir.model.fields,field_description:outgoing_email_by_model.field_ir_model__outgoing_mailserver_id +msgid "Outgoing Mailserver" +msgstr "" diff --git a/outgoing_email_by_model/models/__init__.py b/outgoing_email_by_model/models/__init__.py new file mode 100644 index 0000000000..6856c57a21 --- /dev/null +++ b/outgoing_email_by_model/models/__init__.py @@ -0,0 +1,2 @@ +from . import ir_model +from . import mail_thread diff --git a/outgoing_email_by_model/models/ir_model.py b/outgoing_email_by_model/models/ir_model.py new file mode 100644 index 0000000000..f9445894b1 --- /dev/null +++ b/outgoing_email_by_model/models/ir_model.py @@ -0,0 +1,22 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + +OUTGOING_MAILSERVER_DESCRIPTION = """ + Allows to force the usage of a given outgoing mail server if this setting is set. + However, this setting will be active only if the model extends `mail.thread`. +""" +OUTGOING_EMAIL_DESCRIPTION = """ + Allows to force the usage of a given email address if this setting is set. + However, this setting will be active only if the model extends `mail.thread`. +""" + + +class IrModel(models.Model): + _inherit = "ir.model" + + outgoing_mailserver_id = fields.Many2one( + "ir.mail_server", help=OUTGOING_MAILSERVER_DESCRIPTION + ) + outgoing_email = fields.Char(help=OUTGOING_EMAIL_DESCRIPTION) diff --git a/outgoing_email_by_model/models/mail_thread.py b/outgoing_email_by_model/models/mail_thread.py new file mode 100644 index 0000000000..4521a39f1f --- /dev/null +++ b/outgoing_email_by_model/models/mail_thread.py @@ -0,0 +1,23 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models + + +class MailThread(models.AbstractModel): + _inherit = "mail.thread" + + def _notify_by_email_get_final_mail_values( + self, recipient_ids, base_mail_values, additional_values=None + ): + res = super()._notify_by_email_get_final_mail_values( + recipient_ids, base_mail_values, additional_values=additional_values + ) + model = self.env["ir.model"].sudo().search([("model", "=", self._name)]) + custom_mailserver = model.outgoing_mailserver_id + if custom_mailserver: + res.update({"mail_server_id": custom_mailserver.id}) + custom_email = model.outgoing_email + if custom_email: + res.update({"email_from": custom_email}) + return res diff --git a/outgoing_email_by_model/readme/CONFIGURE.rst b/outgoing_email_by_model/readme/CONFIGURE.rst new file mode 100644 index 0000000000..1626e169d1 --- /dev/null +++ b/outgoing_email_by_model/readme/CONFIGURE.rst @@ -0,0 +1,4 @@ +If you want to use a specific outgoing mail for a model, populate the following fields in the model record (Settings > Technical > Models) accordingly: + +* Outgoing Mailserver: Set an outgoing mail server. This will be used as the forced outgoing mail server for the model. +* Outgoing Email: Set an email address. This will be used as the outgoing email for the model. diff --git a/outgoing_email_by_model/readme/CONTRIBUTORS.rst b/outgoing_email_by_model/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..09f0dff572 --- /dev/null +++ b/outgoing_email_by_model/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Matthieu Méquignon +* Emilie SOUTIRAS diff --git a/outgoing_email_by_model/readme/DESCRIPTION.rst b/outgoing_email_by_model/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..cd105d0ff5 --- /dev/null +++ b/outgoing_email_by_model/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +Allow to select an outgoing mail parameters per model, such as email_from, or mail_server_id. + +To make it work, the model must inherit `mail.thread`. diff --git a/outgoing_email_by_model/static/description/icon.png b/outgoing_email_by_model/static/description/icon.png new file mode 100644 index 0000000000..3a0328b516 Binary files /dev/null and b/outgoing_email_by_model/static/description/icon.png differ diff --git a/outgoing_email_by_model/static/description/index.html b/outgoing_email_by_model/static/description/index.html new file mode 100644 index 0000000000..e7afc6599b --- /dev/null +++ b/outgoing_email_by_model/static/description/index.html @@ -0,0 +1,436 @@ + + + + + +Outgoing Email by Model + + + +
    +

    Outgoing Email by Model

    + + +

    Beta License: AGPL-3 OCA/social Translate me on Weblate Try me on Runboat

    +

    Allow to select an outgoing mail parameters per model, such as email_from, or mail_server_id.

    +

    To make it work, the model must inherit mail.thread.

    +

    Table of contents

    + +
    +

    Configuration

    +

    If you want to use a specific outgoing mail for a model, populate the following fields in the model record (Settings > Technical > Models) accordingly:

    +
      +
    • Outgoing Mailserver: Set an outgoing mail server. This will be used as the forced outgoing mail server for the model.
    • +
    • Outgoing Email: Set an email address. This will be used as the outgoing email for the model.
    • +
    +
    +
    +

    Bug Tracker

    +

    Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

    +

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

    +
    +
    +

    Credits

    +
    +

    Authors

    +
      +
    • Camptocamp
    • +
    +
    + +
    +

    Maintainers

    +

    This module is maintained by the OCA.

    + +Odoo Community Association + +

    OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

    +

    Current maintainer:

    +

    mmequignon

    +

    This module is part of the OCA/social project on GitHub.

    +

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

    +
    +
    +
    + + diff --git a/outgoing_email_by_model/tests/__init__.py b/outgoing_email_by_model/tests/__init__.py new file mode 100644 index 0000000000..92c998b19b --- /dev/null +++ b/outgoing_email_by_model/tests/__init__.py @@ -0,0 +1 @@ +from . import test_mailserver_by_model diff --git a/outgoing_email_by_model/tests/models.py b/outgoing_email_by_model/tests/models.py new file mode 100644 index 0000000000..203f804d52 --- /dev/null +++ b/outgoing_email_by_model/tests/models.py @@ -0,0 +1,11 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class ModelWithMail(models.Model): + _name = "model.with.mail" + _inherit = ["mail.thread"] + + partner_id = fields.Many2one("res.partner") diff --git a/outgoing_email_by_model/tests/test_mailserver_by_model.py b/outgoing_email_by_model/tests/test_mailserver_by_model.py new file mode 100644 index 0000000000..72e6c01400 --- /dev/null +++ b/outgoing_email_by_model/tests/test_mailserver_by_model.py @@ -0,0 +1,98 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo_test_helper import FakeModelLoader + +from odoo.tests.common import Form, TransactionCase + + +class TestMailserverByModel(TransactionCase): + at_install = False + post_install = True + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.setUpClassModels() + cls.setUpClassMailserver() + cls.setUpClassMail() + + @classmethod + def tearDownClass(cls): + cls.loader.restore_registry() + return super().tearDownClass() + + @classmethod + def setUpClassModels(cls): + cls.loader = FakeModelLoader(cls.env, cls.__module__) + cls.loader.backup_registry() + from .models import ModelWithMail + + cls.loader.update_registry((ModelWithMail,)) + dest_partner = cls.env["res.partner"].create( + {"name": "René Coty", "email": "rene.coty@gouv.fr"} + ) + cls.record_with_mail = cls.env[ModelWithMail._name].create( + {"partner_id": dest_partner.id} + ) + cls.model_with_mail_model = cls.env["ir.model"].search( + [("model", "=", ModelWithMail._name)] + ) + + @classmethod + def setUpClassMailserver(cls): + mailserver_model = cls.env["ir.mail_server"] + cls.secondary_mailserver = mailserver_model.create( + { + "smtp_host": "localhost", + "smtp_port": "25", + "smtp_pass": "1 4m v3ry 53cur3", + "smtp_authentication": "login", + "smtp_encryption": "none", + "name": "secondary", + "smtp_user": "secondary@localhost", + "sequence": 42, + } + ) + + @classmethod + def setUpClassMail(cls): + cls.mail_template = cls.env["mail.template"].create( + { + "model_id": cls.model_with_mail_model.id, + "name": "Model with Mail: Send by Mail", + "subject": "Model with Mail: {{object.partner_id.name}}", + "partner_to": "{{object.partner_id.id}}", + "body_html": "Hello, this is a mail", + } + ) + + def _write_message_on_record(self, record): + composer = Form( + self.env["mail.compose.message"].with_context( + default_model=record._name, + default_res_id=record.id, + default_use_template=True, + default_template_id=self.mail_template.id, + default_composition_mode="comment", + ) + ) + composer.save().action_send_mail() + return record.message_ids[0] + + def test_mail_server(self): + # By default, message.mail_server_id is False + message = self._write_message_on_record(self.record_with_mail) + self.assertFalse(message.mail_server_id) + # But if we set outgoing_mailserver_id on the model, secondary_mailserver + # is forced. + self.model_with_mail_model.write( + { + "outgoing_mailserver_id": self.secondary_mailserver.id, + "outgoing_email": self.secondary_mailserver.smtp_user, + } + ) + message = self._write_message_on_record(self.record_with_mail) + self.assertEqual(message.mail_server_id, self.secondary_mailserver) + self.assertEqual(message.email_from, self.secondary_mailserver.smtp_user) diff --git a/outgoing_email_by_model/views/ir_model.xml b/outgoing_email_by_model/views/ir_model.xml new file mode 100644 index 0000000000..9e4abe16df --- /dev/null +++ b/outgoing_email_by_model/views/ir_model.xml @@ -0,0 +1,26 @@ + + + + + + ir.model.form.inherit + ir.model + + + + + + + + + + diff --git a/requirements.txt b/requirements.txt index d03e71a2f0..ddc9e7d1a5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ cairosvg cryptography<37 extract_msg lottie +odoo_test_helper premailer python-telegram-bot requests_toolbelt diff --git a/setup/_metapackage/VERSION.txt b/setup/_metapackage/VERSION.txt index 08f4324410..954f5ab29b 100644 --- a/setup/_metapackage/VERSION.txt +++ b/setup/_metapackage/VERSION.txt @@ -1 +1 @@ -16.0.20241217.0 \ No newline at end of file +16.0.20250221.0 \ No newline at end of file diff --git a/setup/_metapackage/setup.py b/setup/_metapackage/setup.py index 4e31377b8a..e2b64de3d2 100644 --- a/setup/_metapackage/setup.py +++ b/setup/_metapackage/setup.py @@ -19,6 +19,7 @@ 'odoo-addon-mail_activity_reminder>=16.0dev,<16.1dev', 'odoo-addon-mail_activity_reply_creator>=16.0dev,<16.1dev', 'odoo-addon-mail_activity_team>=16.0dev,<16.1dev', + 'odoo-addon-mail_activity_unlink_log>=16.0dev,<16.1dev', 'odoo-addon-mail_attach_existing_attachment>=16.0dev,<16.1dev', 'odoo-addon-mail_attach_existing_attachment_account>=16.0dev,<16.1dev', 'odoo-addon-mail_autosubscribe>=16.0dev,<16.1dev', @@ -56,6 +57,7 @@ 'odoo-addon-mass_mailing_partner>=16.0dev,<16.1dev', 'odoo-addon-mass_mailing_resend>=16.0dev,<16.1dev', 'odoo-addon-mass_mailing_unique>=16.0dev,<16.1dev', + 'odoo-addon-outgoing_email_by_model>=16.0dev,<16.1dev', ], classifiers=[ 'Programming Language :: Python', diff --git a/setup/mail_activity_unlink_log/odoo/addons/mail_activity_unlink_log b/setup/mail_activity_unlink_log/odoo/addons/mail_activity_unlink_log new file mode 120000 index 0000000000..0591943519 --- /dev/null +++ b/setup/mail_activity_unlink_log/odoo/addons/mail_activity_unlink_log @@ -0,0 +1 @@ +../../../../mail_activity_unlink_log \ No newline at end of file diff --git a/setup/mail_activity_unlink_log/setup.py b/setup/mail_activity_unlink_log/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/mail_activity_unlink_log/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/setup/outgoing_email_by_model/odoo/addons/outgoing_email_by_model b/setup/outgoing_email_by_model/odoo/addons/outgoing_email_by_model new file mode 120000 index 0000000000..44f6a2eb4f --- /dev/null +++ b/setup/outgoing_email_by_model/odoo/addons/outgoing_email_by_model @@ -0,0 +1 @@ +../../../../outgoing_email_by_model \ No newline at end of file diff --git a/setup/outgoing_email_by_model/setup.py b/setup/outgoing_email_by_model/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/outgoing_email_by_model/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)