+ As you have opted for recurring donations, fixed payments will be debited by on the anniversary
+ of the first donation.You can submit your cancellation request by emailing to the address: (processing would be done within 3 business days).
+
+
+
+
+
+
+
diff --git a/website_payment_recurring_donations/i18n/nl.po b/website_payment_recurring_donations/i18n/nl.po
new file mode 100755
index 0000000..0c899f6
--- /dev/null
+++ b/website_payment_recurring_donations/i18n/nl.po
@@ -0,0 +1,86 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * website_payment_recurring_donations
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 16.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-22 13:35+0000\n"
+"PO-Revision-Date: 2023-09-22 13:35+0000\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: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.s_donation_button
+msgid "Give Monthly"
+msgstr "Geef maandelijks"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.s_donation_button
+msgid "Just Once"
+msgstr "Slechts één keer"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.donation_mail_body
+msgid "As you have opted for recurring donations, fixed"
+msgstr "Omdat u heeft gekozen voor terugkerende donaties, vast"
+
+#. module: website_payment_recurring_donations
+#: model:ir.model.fields,field_description:website_payment_recurring_donations.field_payment_transaction__donation_frequency
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.payment_checkout
+msgid "Donation Frequency"
+msgstr "Donatie Frequentie"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.payment_checkout
+msgid "Give Monthly"
+msgstr "Geef maandelijks"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.payment_checkout
+msgid "Just Once"
+msgstr "Slechts één keer"
+
+#. module: website_payment_recurring_donations
+#: model:ir.model.fields.selection,name:website_payment_recurring_donations.selection__payment_transaction__donation_frequency__monthly
+msgid "Monthly"
+msgstr "Maandelijks"
+
+#. module: website_payment_recurring_donations
+#: model:ir.model.fields.selection,name:website_payment_recurring_donations.selection__payment_transaction__donation_frequency__onetime
+msgid "One Time"
+msgstr "Eenmaal"
+
+#. module: website_payment_recurring_donations
+#: model:ir.model,name:website_payment_recurring_donations.model_payment_transaction
+msgid "Payment Transaction"
+msgstr "Betalingstransactie"
+
+#. module: website_payment_recurring_donations
+#: model:ir.model.fields,field_description:website_payment_recurring_donations.field_payment_transaction__recurring_donation_provider_reference
+msgid "Provider Reference For Recurring Donation"
+msgstr "Providerreferentie voor terugkerende donaties"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.donation_mail_body
+msgid ""
+"anniversary\n"
+" of the first donation.You can submit your cancellation request by emailing to the address: (processing would be done within 3 business days)."
+msgstr ""
+"verjaardag\n"
+" van de eerste donatie.Je kunt je annuleringsverzoek indienen door een e-mail te sturen naar het volgende adres: (verwerking zal binnen 3 werkdagen plaatsvinden)."
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.donation_mail_body
+msgid "on the"
+msgstr "op de"
+
+#. module: website_payment_recurring_donations
+#: model_terms:ir.ui.view,arch_db:website_payment_recurring_donations.donation_mail_body
+msgid "payments will be debited by"
+msgstr "betalingen worden gedebiteerd door"
diff --git a/website_payment_recurring_donations/models/__init__.py b/website_payment_recurring_donations/models/__init__.py
new file mode 100755
index 0000000..ea7d6a3
--- /dev/null
+++ b/website_payment_recurring_donations/models/__init__.py
@@ -0,0 +1 @@
+from . import payment_transaction
\ No newline at end of file
diff --git a/website_payment_recurring_donations/models/payment_transaction.py b/website_payment_recurring_donations/models/payment_transaction.py
new file mode 100755
index 0000000..e970a35
--- /dev/null
+++ b/website_payment_recurring_donations/models/payment_transaction.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Onestein - Anjeel Haria
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from odoo import fields, models
+
+
+class PaymentTransaction(models.Model):
+ _inherit = 'payment.transaction'
+
+ donation_frequency = fields.Selection(
+ [
+ ('onetime', 'One Time'),
+ ('monthly', 'Monthly')
+ ],
+ string='Donation Frequency'
+ )
+ recurring_donation_provider_reference = fields.Char('Provider Reference For Recurring Donation')
+ is_recurring_donation_terminated = fields.Boolean('Is Recurring Donation Terminated')
+
+ def _send_donation_email(self, is_internal_notification=False, comment=None, recipient_email=None):
+ self = self.with_context(lang=self.partner_lang)
+ return super()._send_donation_email(is_internal_notification, comment, recipient_email)
+
+ def action_terminate_recurring_donation(self):
+ # This method needs to be extended in each provider module.
+ # This method cancels/terminates the recurring donation on the provider end
+ return True
+
+
diff --git a/website_payment_recurring_donations/static/src/js/website_payment_form.js b/website_payment_recurring_donations/static/src/js/website_payment_form.js
new file mode 100755
index 0000000..6f6c20a
--- /dev/null
+++ b/website_payment_recurring_donations/static/src/js/website_payment_form.js
@@ -0,0 +1,15 @@
+/** @odoo-module **/
+/* Copyright 2023 Onestein - Anjeel Haria
+ * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
+
+import checkoutForm from 'payment.checkout_form';
+
+checkoutForm.include({
+ _prepareTransactionRouteParams: function (code, paymentOptionId, flow) {
+ const transactionRouteParams = this._super(...arguments);
+ return $('.o_donation_payment_form').length ? {
+ ...transactionRouteParams,
+ 'donation_frequency':this.$('input[name="donation_frequency"]:checked').val(),
+ } : transactionRouteParams;
+ },
+});
diff --git a/website_payment_recurring_donations/static/src/snippets/s_donation/000.js b/website_payment_recurring_donations/static/src/snippets/s_donation/000.js
new file mode 100755
index 0000000..806801d
--- /dev/null
+++ b/website_payment_recurring_donations/static/src/snippets/s_donation/000.js
@@ -0,0 +1,18 @@
+/** @odoo-module **/
+/* Copyright 2023 Onestein - Anjeel Haria
+ * License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
+
+import publicWidget from 'web.public.widget';
+const DonationSnippet = publicWidget.registry.DonationSnippet;
+DonationSnippet.include({
+ events: _.extend({}, DonationSnippet.prototype.events, {
+ 'click .donation_frequency': '_onClickDonationFrequency',
+ }),
+
+ _onClickDonationFrequency(ev){
+ const $button = $(ev.currentTarget);
+ this.$('.donation_frequency').parent('label').removeClass('active');
+ $button.parent('label').addClass('active');
+ },
+
+});
diff --git a/website_payment_recurring_donations/static/src/snippets/s_donation/000.scss b/website_payment_recurring_donations/static/src/snippets/s_donation/000.scss
new file mode 100755
index 0000000..933506b
--- /dev/null
+++ b/website_payment_recurring_donations/static/src/snippets/s_donation/000.scss
@@ -0,0 +1,16 @@
+.s_donation:not([data-vcss]) {
+ .donation_frequency{
+ position: absolute;
+ width: 1px;
+ height: 1px;
+ margin: -1px;
+ padding: 0;
+ overflow: hidden;
+ border: 0;
+ clip: rect(0 0 0 0);
+ }
+}
+
+.o_donation_payment_form{
+ @include o-input-number-no-arrows();
+}
diff --git a/website_payment_recurring_donations/views/donation_templates.xml b/website_payment_recurring_donations/views/donation_templates.xml
new file mode 100755
index 0000000..2d73721
--- /dev/null
+++ b/website_payment_recurring_donations/views/donation_templates.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/website_payment_recurring_donations/views/payment_transaction_view.xml b/website_payment_recurring_donations/views/payment_transaction_view.xml
new file mode 100755
index 0000000..70f0c05
--- /dev/null
+++ b/website_payment_recurring_donations/views/payment_transaction_view.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+ Recurring Donations Form
+ payment.transaction
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Payment Transactions
+ payment.transaction
+ tree,form
+ [('partner_id','=', active_id)]
+ {'create': False}
+
+
\ No newline at end of file
diff --git a/website_payment_recurring_donations/views/res_partner_view.xml b/website_payment_recurring_donations/views/res_partner_view.xml
new file mode 100755
index 0000000..0757ed6
--- /dev/null
+++ b/website_payment_recurring_donations/views/res_partner_view.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ res.partner
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/website_payment_recurring_donations/views/snippets/s_donation.xml b/website_payment_recurring_donations/views/snippets/s_donation.xml
new file mode 100755
index 0000000..8e91fee
--- /dev/null
+++ b/website_payment_recurring_donations/views/snippets/s_donation.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+ Indicate the Server URL and the Nextcloud admin
+ credentials then click Save. Do not put a trailing forward slash
+ “/” at the end of the Server URL
+
+ 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 smashing it by
+ providing a detailed and welcomed
+ feedback.
+
+
+ Do not contact contributors directly about support or help with
+ technical issues.
+
+ 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.
+
+ Events created in Odoo is Public by default. Check Privacy in Options tab to change to Private otherwise.
+ The following reminders are not supported in Nextcloud and will not be synced: Email - 3 Hours, Email - 6 Hours
+
+
+ You cannot create/update nextcloud events for calendars other than default one
+
+
+ You cannot update nextcloud events for Birthday calendars
+
+ Default Limits Applied To Recurring Events(With End Date As 'Forever') Being Created From/To be Synced to Nextcloud.
+ Define In Years The Limit For Each Of Frequency For Which The Events Should Be Created.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ days
+
+
+
+
+
+
+
+
+
+
+
+ Settings
+ ir.actions.act_window
+ res.config.settings
+ form
+ inline
+ {'module' : 'nextcloud_odoo_sync', 'bin_size': False}
+
+
+
+
diff --git a/nextcloud_odoo_sync/views/res_users_views.xml b/nextcloud_odoo_sync/views/res_users_views.xml
new file mode 100644
index 0000000..931a818
--- /dev/null
+++ b/nextcloud_odoo_sync/views/res_users_views.xml
@@ -0,0 +1,17 @@
+
+
+ view.users.form.simple.modif.inherit
+ res.users
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nextcloud_odoo_sync/wizards/__init__.py b/nextcloud_odoo_sync/wizards/__init__.py
new file mode 100644
index 0000000..803fa75
--- /dev/null
+++ b/nextcloud_odoo_sync/wizards/__init__.py
@@ -0,0 +1 @@
+from . import run_sync_wizard
diff --git a/nextcloud_odoo_sync/wizards/run_sync_wizard.py b/nextcloud_odoo_sync/wizards/run_sync_wizard.py
new file mode 100644
index 0000000..324917d
--- /dev/null
+++ b/nextcloud_odoo_sync/wizards/run_sync_wizard.py
@@ -0,0 +1,11 @@
+# Copyright (c) 2023 iScale Solutions Inc.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
+
+from odoo import models
+
+
+class RunSyncWizard(models.TransientModel):
+ _name = "run.sync.wizard"
+
+ def run_sync_cron(self):
+ self.env["nextcloud.caldav"].sync_cron()
diff --git a/nextcloud_odoo_sync/wizards/run_sync_wizard_views.xml b/nextcloud_odoo_sync/wizards/run_sync_wizard_views.xml
new file mode 100644
index 0000000..4d8bde4
--- /dev/null
+++ b/nextcloud_odoo_sync/wizards/run_sync_wizard_views.xml
@@ -0,0 +1,38 @@
+
+
+ run.sync.confirm.form
+ run.sync.wizard
+
+
+
+
+
+
+ Run Sync
+ ir.actions.act_window
+ run.sync.wizard
+ form
+
+ {}
+ new
+
+
+
+
From 90868d21ff596c6ac623ba43084bbb43fca5fe6b Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Wed, 17 Apr 2024 17:47:58 +0800
Subject: [PATCH 15/22] [FIX] Fixed get_domain_yaml_path
The get_domain_yaml_path did not return a value in all execution paths.
---
argocd_deployer/models/application_tag.py | 11 ++--
argocd_deployer/tests/__init__.py | 1 +
argocd_deployer/tests/test_application_tag.py | 54 +++++++++++++++++++
3 files changed, 60 insertions(+), 6 deletions(-)
create mode 100644 argocd_deployer/tests/test_application_tag.py
diff --git a/argocd_deployer/models/application_tag.py b/argocd_deployer/models/application_tag.py
index ef75fce..0571392 100644
--- a/argocd_deployer/models/application_tag.py
+++ b/argocd_deployer/models/application_tag.py
@@ -19,9 +19,8 @@ class ApplicationTag(models.Model):
def get_domain_yaml_path(self, application_set=False):
self.ensure_one()
- if not application_set:
- return self.domain_yaml_path
- if application_set.id in self.domain_override_ids.application_set_id.ids:
- return self.domain_override_ids.filtered(
- lambda do: do.application_set_id == application_set
- ).domain_yaml_path
+ if not application_set or application_set.id not in self.domain_override_ids.application_set_id.ids:
+ return self.domain_yaml_path or ""
+ return self.domain_override_ids.filtered(
+ lambda do: do.application_set_id == application_set
+ ).domain_yaml_path or ""
diff --git a/argocd_deployer/tests/__init__.py b/argocd_deployer/tests/__init__.py
index ec4f841..5b5206c 100644
--- a/argocd_deployer/tests/__init__.py
+++ b/argocd_deployer/tests/__init__.py
@@ -1,2 +1,3 @@
from . import test_application
from . import test_application_set
+from . import test_application_tag
diff --git a/argocd_deployer/tests/test_application_tag.py b/argocd_deployer/tests/test_application_tag.py
new file mode 100644
index 0000000..7f6c844
--- /dev/null
+++ b/argocd_deployer/tests/test_application_tag.py
@@ -0,0 +1,54 @@
+from unittest import skip
+from unittest.mock import MagicMock, patch
+
+from odoo import Command
+from odoo.tests.common import TransactionCase
+
+
+class TestApplicationTag(TransactionCase):
+
+ def test_get_domain_yaml_path(self):
+ """get_domain_yaml_path returns a reasonable value in all circumstances."""
+
+ template = self.env["argocd.application.set.template"].create({
+ "name": "test-set-template",
+ "yaml": """test:
+ me: domain"""
+ })
+ application_set_1 = self.env["argocd.application.set"].create({
+ "name": "test-set",
+ "template_id": template.id,
+ "repository_url": "http://hello.com",
+ "branch": "hello",
+ "repository_directory": "hello",
+ "deployment_directory": "bye",
+ })
+ application_set_2 = self.env["argocd.application.set"].create({
+ "name": "test-set-also",
+ "template_id": template.id,
+ "repository_url": "http://hello.com",
+ "branch": "hello",
+ "repository_directory": "hello",
+ "deployment_directory": "byeagain",
+ })
+ tag = self.env["argocd.application.tag"].create({
+ "name": "test-tag",
+ "domain_yaml_path": False,
+ "key": "thegoldentowerkey"
+ })
+
+ self.assertFalse(tag.get_domain_yaml_path())
+ self.assertFalse(tag.get_domain_yaml_path(application_set_1))
+
+ tag.domain_yaml_path = "test.me"
+ self.assertEqual("test.me", tag.get_domain_yaml_path())
+ self.assertEqual("test.me", tag.get_domain_yaml_path(application_set_1))
+
+ tag.domain_override_ids = [Command.create({
+ "application_set_id": application_set_1.id,
+ "domain_yaml_path": "test.me.now",
+ })]
+
+ self.assertEqual("test.me", tag.get_domain_yaml_path())
+ self.assertEqual("test.me.now", tag.get_domain_yaml_path(application_set_1))
+ self.assertEqual("test.me", tag.get_domain_yaml_path(application_set_2))
From 4578b06e5751b5f7565d421a3687b38bfbbbc0e5 Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Wed, 17 Apr 2024 18:11:16 +0800
Subject: [PATCH 16/22] [FIX] Pre-commit stuff
---
argocd_deployer/models/application_tag.py | 14 +-
argocd_deployer/tests/test_application_tag.py | 72 +-
nextcloud_odoo_sync/__manifest__.py | 2 +-
nextcloud_odoo_sync/hooks.py | 5 +-
nextcloud_odoo_sync/models/calendar_event.py | 188 +-
.../models/calendar_recurrence.py | 189 +-
nextcloud_odoo_sync/models/jicson.py | 2 +-
nextcloud_odoo_sync/models/nc_calendar.py | 2 +-
nextcloud_odoo_sync/models/nc_event_status.py | 2 +-
nextcloud_odoo_sync/models/nc_sync_error.py | 2 +-
nextcloud_odoo_sync/models/nc_sync_log.py | 4 +-
nextcloud_odoo_sync/models/nc_sync_user.py | 199 +-
nextcloud_odoo_sync/models/nextcloud_base.py | 12 +-
.../models/nextcloud_caldav.py | 1944 ++++++++++++-----
.../models/res_config_settings.py | 3 +-
nextcloud_odoo_sync/models/res_partner.py | 2 +-
nextcloud_odoo_sync/models/res_users.py | 6 +-
nextcloud_odoo_sync/security/ir_rule.xml | 2 +-
nextcloud_odoo_sync/tests/common.py | 116 +-
.../tests/test_nextcloud_config.py | 6 +-
nextcloud_odoo_sync/tests/test_sync_common.py | 5 +-
.../tests/test_sync_odoo2nextcloud.py | 9 +-
.../__init__.py | 2 +-
.../__manifest__.py | 22 +-
.../controllers/portal.py | 93 +-
.../models/__init__.py | 2 +-
.../models/payment_transaction.py | 26 +-
.../static/src/snippets/s_donation/000.js | 2 +-
.../views/payment_transaction_view.xml | 2 +-
.../views/res_partner_view.xml | 2 +-
.../__init__.py | 2 +-
.../__manifest__.py | 20 +-
.../models/payment_provider.py | 30 +-
.../models/payment_transaction.py | 155 +-
.../views/res_partner_view.xml | 1 -
35 files changed, 2203 insertions(+), 942 deletions(-)
diff --git a/argocd_deployer/models/application_tag.py b/argocd_deployer/models/application_tag.py
index 0571392..b817855 100644
--- a/argocd_deployer/models/application_tag.py
+++ b/argocd_deployer/models/application_tag.py
@@ -19,8 +19,14 @@ class ApplicationTag(models.Model):
def get_domain_yaml_path(self, application_set=False):
self.ensure_one()
- if not application_set or application_set.id not in self.domain_override_ids.application_set_id.ids:
+ if (
+ not application_set
+ or application_set.id not in self.domain_override_ids.application_set_id.ids
+ ):
return self.domain_yaml_path or ""
- return self.domain_override_ids.filtered(
- lambda do: do.application_set_id == application_set
- ).domain_yaml_path or ""
+ return (
+ self.domain_override_ids.filtered(
+ lambda do: do.application_set_id == application_set
+ ).domain_yaml_path
+ or ""
+ )
diff --git a/argocd_deployer/tests/test_application_tag.py b/argocd_deployer/tests/test_application_tag.py
index 7f6c844..a6f1b86 100644
--- a/argocd_deployer/tests/test_application_tag.py
+++ b/argocd_deployer/tests/test_application_tag.py
@@ -1,41 +1,41 @@
-from unittest import skip
-from unittest.mock import MagicMock, patch
-
from odoo import Command
from odoo.tests.common import TransactionCase
class TestApplicationTag(TransactionCase):
-
def test_get_domain_yaml_path(self):
"""get_domain_yaml_path returns a reasonable value in all circumstances."""
- template = self.env["argocd.application.set.template"].create({
- "name": "test-set-template",
- "yaml": """test:
- me: domain"""
- })
- application_set_1 = self.env["argocd.application.set"].create({
- "name": "test-set",
- "template_id": template.id,
- "repository_url": "http://hello.com",
- "branch": "hello",
- "repository_directory": "hello",
- "deployment_directory": "bye",
- })
- application_set_2 = self.env["argocd.application.set"].create({
- "name": "test-set-also",
- "template_id": template.id,
- "repository_url": "http://hello.com",
- "branch": "hello",
- "repository_directory": "hello",
- "deployment_directory": "byeagain",
- })
- tag = self.env["argocd.application.tag"].create({
- "name": "test-tag",
- "domain_yaml_path": False,
- "key": "thegoldentowerkey"
- })
+ template = self.env["argocd.application.set.template"].create(
+ {
+ "name": "test-set-template",
+ "yaml": """test:
+ me: domain""",
+ }
+ )
+ application_set_1 = self.env["argocd.application.set"].create(
+ {
+ "name": "test-set",
+ "template_id": template.id,
+ "repository_url": "http://hello.com",
+ "branch": "hello",
+ "repository_directory": "hello",
+ "deployment_directory": "bye",
+ }
+ )
+ application_set_2 = self.env["argocd.application.set"].create(
+ {
+ "name": "test-set-also",
+ "template_id": template.id,
+ "repository_url": "http://hello.com",
+ "branch": "hello",
+ "repository_directory": "hello",
+ "deployment_directory": "byeagain",
+ }
+ )
+ tag = self.env["argocd.application.tag"].create(
+ {"name": "test-tag", "domain_yaml_path": False, "key": "thegoldentowerkey"}
+ )
self.assertFalse(tag.get_domain_yaml_path())
self.assertFalse(tag.get_domain_yaml_path(application_set_1))
@@ -44,10 +44,14 @@ def test_get_domain_yaml_path(self):
self.assertEqual("test.me", tag.get_domain_yaml_path())
self.assertEqual("test.me", tag.get_domain_yaml_path(application_set_1))
- tag.domain_override_ids = [Command.create({
- "application_set_id": application_set_1.id,
- "domain_yaml_path": "test.me.now",
- })]
+ tag.domain_override_ids = [
+ Command.create(
+ {
+ "application_set_id": application_set_1.id,
+ "domain_yaml_path": "test.me.now",
+ }
+ )
+ ]
self.assertEqual("test.me", tag.get_domain_yaml_path())
self.assertEqual("test.me.now", tag.get_domain_yaml_path(application_set_1))
diff --git a/nextcloud_odoo_sync/__manifest__.py b/nextcloud_odoo_sync/__manifest__.py
index bf34e76..4921a34 100644
--- a/nextcloud_odoo_sync/__manifest__.py
+++ b/nextcloud_odoo_sync/__manifest__.py
@@ -6,7 +6,7 @@
"category": "Others",
"description": """Sync Nextcloud apps into Odoo""",
"author": "iScale Solutions Inc.",
- "website": "http://iscale-solutions.com",
+ "website": "https://www.onestein.nl",
"external_dependencies": {"python": ["caldav"]},
"depends": ["base", "calendar", "resource", "contacts"],
"maintainers": ["iscale-solutions"],
diff --git a/nextcloud_odoo_sync/hooks.py b/nextcloud_odoo_sync/hooks.py
index 25f88c2..ba6377f 100644
--- a/nextcloud_odoo_sync/hooks.py
+++ b/nextcloud_odoo_sync/hooks.py
@@ -4,9 +4,8 @@
from odoo import SUPERUSER_ID, api
-ACTIONS = (
- "calendar.action_calendar_event",
-)
+ACTIONS = ("calendar.action_calendar_event",)
+
def uninstall_hook(cr, registry):
"""Restore calendar action"""
diff --git a/nextcloud_odoo_sync/models/calendar_event.py b/nextcloud_odoo_sync/models/calendar_event.py
index 2c4c8ef..c5f73fa 100644
--- a/nextcloud_odoo_sync/models/calendar_event.py
+++ b/nextcloud_odoo_sync/models/calendar_event.py
@@ -2,9 +2,10 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import ast
+
import pytz
-from odoo import api, models, fields, _
+from odoo import _, api, fields, models
from odoo.exceptions import UserError
@@ -42,10 +43,12 @@ def _get_nc_calendar_selection(self):
nc_to_delete = fields.Boolean("To Delete")
nc_allday = fields.Boolean("Nextcloud All day")
nc_detach = fields.Boolean("Detach from recurring event")
- nc_event_updateable = fields.Boolean("Event Updateable In Nextcloud", compute="_compute_nc_event_updateable")
- nextcloud_event_timezone = fields.Char('Nextcloud Event Timezone')
- nextcloud_calendar_type = fields.Char('Nextcloud Calendar Type')
- nextcloud_rrule = fields.Char('Nextcloud Rrule')
+ nc_event_updateable = fields.Boolean(
+ "Event Updateable In Nextcloud", compute="_compute_nc_event_updateable"
+ )
+ nextcloud_event_timezone = fields.Char("Nextcloud Event Timezone")
+ nextcloud_calendar_type = fields.Char("Nextcloud Calendar Type")
+ nextcloud_rrule = fields.Char("Nextcloud Rrule")
@api.model
def default_get(self, fields):
@@ -61,7 +64,9 @@ def default_get(self, fields):
).id
return res
- @api.depends("recurrence_id", "allday", "start", "event_tz", "nextcloud_event_timezone")
+ @api.depends(
+ "recurrence_id", "allday", "start", "event_tz", "nextcloud_event_timezone"
+ )
def _compute_nc_rid(self):
"""
This method generates a value for RECURRENCE-ID
@@ -74,8 +79,7 @@ def _compute_nc_rid(self):
tz = event.nextcloud_event_timezone
if tz and not event.nc_rid:
dt_tz = start.replace(tzinfo=pytz.utc)
- start = dt_tz.astimezone(
- pytz.timezone(tz))
+ start = dt_tz.astimezone(pytz.timezone(tz))
event.nc_rid = start.strftime("%Y%m%dT%H%M%S")
else:
event.nc_rid = event.nc_rid or False
@@ -101,7 +105,7 @@ def _compute_nc_calendar(self):
event.nc_calendar_id = calendar
event.nc_calendar_select = str(calendar) if calendar else False
- @api.depends('nc_calendar_id', 'nextcloud_calendar_type')
+ @api.depends("nc_calendar_id", "nextcloud_calendar_type")
def _compute_nc_event_updateable(self):
for event in self:
nc_event_updateable = True
@@ -110,7 +114,13 @@ def _compute_nc_event_updateable(self):
elif event.nc_calendar_id:
default_calendar_id = (
self.env["nc.sync.user"]
- .search([("user_id", "=", self.env.user.id), ("sync_calendar", "=", True)], limit=1)
+ .search(
+ [
+ ("user_id", "=", self.env.user.id),
+ ("sync_calendar", "=", True),
+ ],
+ limit=1,
+ )
.mapped("nc_calendar_id")
)
if event.nc_calendar_id != default_calendar_id:
@@ -140,7 +150,13 @@ def onchange_nc_user_id(self):
if self.nc_require_calendar:
default_calendar_id = (
self.env["nc.sync.user"]
- .search([("user_id", "=", self.user_id.id), ("sync_calendar", "=", True)], limit=1)
+ .search(
+ [
+ ("user_id", "=", self.user_id.id),
+ ("sync_calendar", "=", True),
+ ],
+ limit=1,
+ )
.mapped("nc_calendar_id")
)
if default_calendar_id and self.user_id == self.env.user:
@@ -167,7 +183,13 @@ def onchange_nc_calendar_select(self):
elif not self.nc_calendar_select and self.user_id:
calendar_id = (
self.env["nc.sync.user"]
- .search([("user_id", "=", self.user_id.id), ("sync_calendar", "=", True)], limit=1)
+ .search(
+ [
+ ("user_id", "=", self.user_id.id),
+ ("sync_calendar", "=", True),
+ ],
+ limit=1,
+ )
.mapped("nc_calendar_id")
)
else:
@@ -206,18 +228,28 @@ def create(self, vals):
"nextcloud_odoo_sync.nc_event_status_confirmed"
).id
res = super(CalendarEvent, self).create(vals)
- if vals.get('user_id'):
+ if vals.get("user_id"):
# Check if a value for calendar exist for the user:
nc_sync_user_id = self.env["nc.sync.user"].search(
- [("user_id", "=", vals["user_id"]), ("sync_calendar", "=", True)], limit=1
+ [("user_id", "=", vals["user_id"]), ("sync_calendar", "=", True)],
+ limit=1,
)
- if "nc_calendar_ids" not in vals or vals["nc_calendar_ids"] == [[6, False, []]]:
+ if "nc_calendar_ids" not in vals or vals["nc_calendar_ids"] == [
+ [6, False, []]
+ ]:
if nc_sync_user_id and nc_sync_user_id.nc_calendar_id:
res.nc_calendar_ids = [(4, nc_sync_user_id.nc_calendar_id.id)]
- if not self._context.get("sync_from_nextcloud",
- False) and res.nc_calendar_id and res.nc_calendar_id != nc_sync_user_id.nc_calendar_id:
- raise UserError(_('You cannot create nextcloud events for calendars other than default one(%s)',
- nc_sync_user_id.nc_calendar_id.name))
+ if (
+ not self._context.get("sync_from_nextcloud", False)
+ and res.nc_calendar_id
+ and res.nc_calendar_id != nc_sync_user_id.nc_calendar_id
+ ):
+ raise UserError(
+ _(
+ "You cannot create nextcloud events for calendars other than default one(%s)",
+ nc_sync_user_id.nc_calendar_id.name,
+ )
+ )
return res
def write(self, vals):
@@ -241,67 +273,98 @@ def write(self, vals):
fields_to_update = list(vals.keys())
calendar_recurrence_obj = self.env["calendar.recurrence"].sudo()
detach = False
- if not self._context.get('update_recurring', False):
- if not vals.get('recurrence_update', '') in ['future_events', 'all_events']:
+ if not self._context.get("update_recurring", False):
+ if not vals.get("recurrence_update", "") in ["future_events", "all_events"]:
for f in fields_to_update:
if f not in ex_fields:
detach = True
break
else:
self = self.with_context(update_recurring=True)
- ex_fields.extend(["nc_allday", "event_tz", "write_date", "nextcloud_calendar_type"])
- ex_fields.remove('nc_to_delete')
+ ex_fields.extend(
+ ["nc_allday", "event_tz", "write_date", "nextcloud_calendar_type"]
+ )
+ ex_fields.remove("nc_to_delete")
record_updated = False
for f in fields_to_update:
if f not in ex_fields:
record_updated = True
break
- if not self._context.get("sync",
- False) and "nc_synced" not in vals and record_updated and not self._context.get(
- 'update_recurring', False):
+ if (
+ not self._context.get("sync", False)
+ and "nc_synced" not in vals
+ and record_updated
+ and not self._context.get("update_recurring", False)
+ ):
vals["nc_synced"] = False
- if self._context.get('update_recurring', False) and len(self.ids) == 1 and self.ids == [
- self.recurrence_id.base_event_id.id]:
+ if (
+ self._context.get("update_recurring", False)
+ and len(self.ids) == 1
+ and self.ids == [self.recurrence_id.base_event_id.id]
+ ):
vals["nc_synced"] = False
- if not self._context.get('update_nc_rid', False):
+ if not self._context.get("update_nc_rid", False):
if not self.allday:
start = self.start
tz = self.nextcloud_event_timezone
if tz:
dt_tz = start.replace(tzinfo=pytz.utc)
- start = dt_tz.astimezone(
- pytz.timezone(tz))
+ start = dt_tz.astimezone(pytz.timezone(tz))
nc_rid = start.strftime("%Y%m%dT%H%M%S")
else:
nc_rid = self.nc_rid
else:
nc_rid = self.start.strftime("%Y%m%d")
vals["nc_rid"] = nc_rid
- if vals.get('recurrence_update') == 'future_events':
+ if vals.get("recurrence_update") == "future_events":
for record in self:
record.recurrence_id.base_event_id.sudo().write(
- {'nc_synced': False, 'nc_uid': False, 'nc_hash_ids': [(6, 0, [])]})
+ {"nc_synced": False, "nc_uid": False, "nc_hash_ids": [(6, 0, [])]}
+ )
for record in self:
# Detach the record from recurring event whenever an edit was made
# to make it compatible when synced to Nextcloud calendar
- if not self._context.get("sync_from_nextcloud",
- False) and detach and record.nc_uid and record.user_id and record.user_id != self.env.user:
- raise UserError(_('You cannot update nextcloud events if you are not the organizer'))
- if not self._context.get("sync_from_nextcloud",
- False) and not record.nc_event_updateable and detach:
+ if (
+ not self._context.get("sync_from_nextcloud", False)
+ and detach
+ and record.nc_uid
+ and record.user_id
+ and record.user_id != self.env.user
+ ):
+ raise UserError(
+ _("You cannot update nextcloud events if you are not the organizer")
+ )
+ if (
+ not self._context.get("sync_from_nextcloud", False)
+ and not record.nc_event_updateable
+ and detach
+ ):
if record.nextcloud_calendar_type:
- raise UserError(_('You cannot update nextcloud events for Birthday calendars'))
+ raise UserError(
+ _("You cannot update nextcloud events for Birthday calendars")
+ )
default_calendar_id = (
self.env["nc.sync.user"]
- .search([("user_id", "=", self.env.user.id), ("sync_calendar", "=", True)], limit=1)
+ .search(
+ [
+ ("user_id", "=", self.env.user.id),
+ ("sync_calendar", "=", True),
+ ],
+ limit=1,
+ )
.mapped("nc_calendar_id")
)
- raise UserError(_('You cannot update nextcloud events for calendars other than default one(%s)',
- default_calendar_id.name))
- if not self._context.get('sync_from_nextcloud'):
- if 'active' in vals and not vals.get('active'):
- new_recurrence = calendar_recurrence_obj.search([('base_event_id', '=', record.id)],
- limit=1)
+ raise UserError(
+ _(
+ "You cannot update nextcloud events for calendars other than default one(%s)",
+ default_calendar_id.name,
+ )
+ )
+ if not self._context.get("sync_from_nextcloud"):
+ if "active" in vals and not vals.get("active"):
+ new_recurrence = calendar_recurrence_obj.search(
+ [("base_event_id", "=", record.id)], limit=1
+ )
if new_recurrence:
new_recurring_events = new_recurrence.calendar_event_ids.sorted(
key=lambda r: r.start
@@ -310,9 +373,19 @@ def write(self, vals):
new_recurrence.base_event_id = new_recurring_events[0].id
hash_vals_list = []
for rec in record.nc_hash_ids:
- hash_vals_list.append((0, 0, {"nc_sync_user_id": rec.nc_sync_user_id.id,
- "nc_event_hash": rec.nc_event_hash, }))
- new_recurring_events.write({'nc_hash_ids': hash_vals_list, 'nc_synced': True})
+ hash_vals_list.append(
+ (
+ 0,
+ 0,
+ {
+ "nc_sync_user_id": rec.nc_sync_user_id.id,
+ "nc_event_hash": rec.nc_event_hash,
+ },
+ )
+ )
+ new_recurring_events.write(
+ {"nc_hash_ids": hash_vals_list, "nc_synced": True}
+ )
new_recurring_events[0].write({"nc_synced": False})
if record.recurrence_id:
if detach:
@@ -326,17 +399,26 @@ def unlink(self):
from Nextcloud Calendar at the next sync. We just mark the event as to
delete (nc_to_delete=True) before we sync.
"""
- has_nc_uids = self.env['calendar.event']
+ has_nc_uids = self.env["calendar.event"]
if not self._context.get("force_delete", False):
for record in self:
if record.nc_uid and record.user_id and record.user_id != self.env.user:
- raise UserError(_('You cannot delete nextcloud events if you are not the organizer'))
+ raise UserError(
+ _(
+ "You cannot delete nextcloud events if you are not the organizer"
+ )
+ )
default_calendar_id = (
self.env["nc.sync.user"]
- .search([("user_id", "=", self.env.user.id), ("sync_calendar", "=", True)], limit=1)
+ .search(
+ [("user_id", "=", self.env.user.id), ("sync_calendar", "=", True)],
+ limit=1,
+ )
.mapped("nc_calendar_id")
)
- has_nc_uids = self.filtered(lambda r: r.nc_uid and r.nc_calendar_id == default_calendar_id)
+ has_nc_uids = self.filtered(
+ lambda r: r.nc_uid and r.nc_calendar_id == default_calendar_id
+ )
if has_nc_uids:
has_nc_uids.write({"nc_to_delete": True})
for record in self:
diff --git a/nextcloud_odoo_sync/models/calendar_recurrence.py b/nextcloud_odoo_sync/models/calendar_recurrence.py
index a0492f1..70f1a44 100644
--- a/nextcloud_odoo_sync/models/calendar_recurrence.py
+++ b/nextcloud_odoo_sync/models/calendar_recurrence.py
@@ -1,36 +1,50 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields, _
+from datetime import datetime, timedelta
+
from dateutil import rrule
+
+from odoo import _, fields, models
from odoo.exceptions import UserError
-from datetime import timedelta,datetime
SELECT_FREQ_TO_RRULE = {
- 'daily': rrule.DAILY,
- 'weekly': rrule.WEEKLY,
- 'monthly': rrule.MONTHLY,
- 'yearly': rrule.YEARLY,
+ "daily": rrule.DAILY,
+ "weekly": rrule.WEEKLY,
+ "monthly": rrule.MONTHLY,
+ "yearly": rrule.YEARLY,
}
-RRULE_WEEKDAYS = {'SUN': 'SU', 'MON': 'MO', 'TUE': 'TU', 'WED': 'WE', 'THU': 'TH', 'FRI': 'FR', 'SAT': 'SA'}
+RRULE_WEEKDAYS = {
+ "SUN": "SU",
+ "MON": "MO",
+ "TUE": "TU",
+ "WED": "WE",
+ "THU": "TH",
+ "FRI": "FR",
+ "SAT": "SA",
+}
def freq_to_rrule(freq):
return SELECT_FREQ_TO_RRULE[freq]
+
def weeks_between(start_date, end_date):
weeks = rrule.rrule(rrule.WEEKLY, dtstart=start_date, until=end_date)
return weeks.count() - 1
+
def months_between(start_date, end_date):
months = rrule.rrule(rrule.MONTHLY, dtstart=start_date, until=end_date)
return months.count() - 1
+
def years_between(start_date, end_date):
years = rrule.rrule(rrule.YEARLY, dtstart=start_date, until=end_date)
return years.count() - 1
+
def days_between(start_date, end_date):
days = rrule.rrule(rrule.DAILY, dtstart=start_date, until=end_date)
return days.count() - 1
@@ -41,15 +55,13 @@ class CalendarRecurrence(models.Model):
nc_exdate = fields.Char("Nextcloud Exdate")
-
-
def _get_rrule(self, dtstart=None):
self.ensure_one()
- if self._context.get('update_until', False):
+ if self._context.get("update_until", False):
if self.until:
self.until = self.until - timedelta(days=1)
- if not self.base_event_id.nc_calendar_id or self.end_type != 'forever':
+ if not self.base_event_id.nc_calendar_id or self.end_type != "forever":
return super()._get_rrule(dtstart)
freq = self.rrule_type
rrule_params = dict(
@@ -60,70 +72,111 @@ def _get_rrule(self, dtstart=None):
dtstart_date = (self.dtstart or dtstart).date()
past_event = True if (today - dtstart_date).days > 0 else False
config = self.env["ir.config_parameter"].sudo()
- if freq == 'monthly' and self.month_by == 'date': # e.g. every 15th of the month
- rrule_params['bymonthday'] = self.day
- elif freq == 'monthly' and self.month_by == 'day': # e.g. every 2nd Monday in the month
- rrule_params['byweekday'] = getattr(rrule, RRULE_WEEKDAYS[self.weekday])(
- int(self.byday)) # e.g. MO(+2) for the second Monday of the month
- elif freq == 'weekly':
+ if (
+ freq == "monthly" and self.month_by == "date"
+ ): # e.g. every 15th of the month
+ rrule_params["bymonthday"] = self.day
+ elif (
+ freq == "monthly" and self.month_by == "day"
+ ): # e.g. every 2nd Monday in the month
+ rrule_params["byweekday"] = getattr(rrule, RRULE_WEEKDAYS[self.weekday])(
+ int(self.byday)
+ ) # e.g. MO(+2) for the second Monday of the month
+ elif freq == "weekly":
weekdays = self._get_week_days()
if not weekdays:
raise UserError(_("You have to choose at least one day in the week"))
- rrule_params['byweekday'] = weekdays
- rrule_params['wkst'] = self._get_lang_week_start()
- weekly_recurring_events_limit_value = (2
- if not config.get_param(
- "nextcloud_odoo_sync.weekly_recurring_events_limit")
- else int(config.get_param(
- "nextcloud_odoo_sync.weekly_recurring_events_limit"))
- ) * 52
+ rrule_params["byweekday"] = weekdays
+ rrule_params["wkst"] = self._get_lang_week_start()
+ weekly_recurring_events_limit_value = (
+ 2
+ if not config.get_param(
+ "nextcloud_odoo_sync.weekly_recurring_events_limit"
+ )
+ else int(
+ config.get_param(
+ "nextcloud_odoo_sync.weekly_recurring_events_limit"
+ )
+ )
+ ) * 52
if past_event:
- weekly_recurring_events_limit_value += weeks_between(dtstart_date,today)
- rrule_params['count'] = ((
- weekly_recurring_events_limit_value // self.interval) if self.interval < weekly_recurring_events_limit_value else 1) * len(
- weekdays) # maximum recurring events for 2 years
-
- elif freq == 'daily':
- daily_recurring_events_limit_value = (2
- if not config.get_param(
- "nextcloud_odoo_sync.daily_recurring_events_limit")
- else int(config.get_param(
- "nextcloud_odoo_sync.daily_recurring_events_limit")
- )) * 365
+ weekly_recurring_events_limit_value += weeks_between(
+ dtstart_date, today
+ )
+ rrule_params["count"] = (
+ (weekly_recurring_events_limit_value // self.interval)
+ if self.interval < weekly_recurring_events_limit_value
+ else 1
+ ) * len(
+ weekdays
+ ) # maximum recurring events for 2 years
+
+ elif freq == "daily":
+ daily_recurring_events_limit_value = (
+ 2
+ if not config.get_param(
+ "nextcloud_odoo_sync.daily_recurring_events_limit"
+ )
+ else int(
+ config.get_param("nextcloud_odoo_sync.daily_recurring_events_limit")
+ )
+ ) * 365
if past_event:
- daily_recurring_events_limit_value += days_between(dtstart_date,today)
- rrule_params['count'] = (
- daily_recurring_events_limit_value // self.interval) if self.interval < daily_recurring_events_limit_value else 1 # maximum recurring events for 2 years
- if freq in ('yearly', 'monthly'):
- yearly_recurring_events_limit_value = (10
- if not config.get_param(
- "nextcloud_odoo_sync.yearly_recurring_events_limit")
- else int(config.get_param(
- "nextcloud_odoo_sync.yearly_recurring_events_limit")
- ))
- monthly_recurring_events_limit_value = (2
- if not config.get_param(
- "nextcloud_odoo_sync.monthly_recurring_events_limit")
- else int(config.get_param(
- "nextcloud_odoo_sync.monthly_recurring_events_limit")
- )) * 12
- if freq == 'yearly':
+ daily_recurring_events_limit_value += days_between(dtstart_date, today)
+ rrule_params["count"] = (
+ (daily_recurring_events_limit_value // self.interval)
+ if self.interval < daily_recurring_events_limit_value
+ else 1
+ ) # maximum recurring events for 2 years
+ if freq in ("yearly", "monthly"):
+ yearly_recurring_events_limit_value = (
+ 10
+ if not config.get_param(
+ "nextcloud_odoo_sync.yearly_recurring_events_limit"
+ )
+ else int(
+ config.get_param(
+ "nextcloud_odoo_sync.yearly_recurring_events_limit"
+ )
+ )
+ )
+ monthly_recurring_events_limit_value = (
+ 2
+ if not config.get_param(
+ "nextcloud_odoo_sync.monthly_recurring_events_limit"
+ )
+ else int(
+ config.get_param(
+ "nextcloud_odoo_sync.monthly_recurring_events_limit"
+ )
+ )
+ ) * 12
+ if freq == "yearly":
if past_event:
- yearly_recurring_events_limit_value += years_between(dtstart_date, today)
- rrule_params['count'] = (
- yearly_recurring_events_limit_value // self.interval) if self.interval < yearly_recurring_events_limit_value else 1 # maximum recurring events for 10 years
- elif freq == 'monthly':
+ yearly_recurring_events_limit_value += years_between(
+ dtstart_date, today
+ )
+ rrule_params["count"] = (
+ (yearly_recurring_events_limit_value // self.interval)
+ if self.interval < yearly_recurring_events_limit_value
+ else 1
+ ) # maximum recurring events for 10 years
+ elif freq == "monthly":
if self.interval >= 12:
if past_event:
- yearly_recurring_events_limit_value += years_between(dtstart_date, today)
- rrule_params['count'] = (yearly_recurring_events_limit_value // (
- self.interval // 12)) # maximum recurring events for years defined
+ yearly_recurring_events_limit_value += years_between(
+ dtstart_date, today
+ )
+ rrule_params["count"] = yearly_recurring_events_limit_value // (
+ self.interval // 12
+ ) # maximum recurring events for years defined
else:
if past_event:
- monthly_recurring_events_limit_value += months_between(dtstart_date, today)
- rrule_params['count'] = (
- monthly_recurring_events_limit_value // self.interval) # maximum recurring events for months defined
-
- return rrule.rrule(
- freq_to_rrule(freq), **rrule_params
- )
+ monthly_recurring_events_limit_value += months_between(
+ dtstart_date, today
+ )
+ rrule_params["count"] = (
+ monthly_recurring_events_limit_value // self.interval
+ ) # maximum recurring events for months defined
+
+ return rrule.rrule(freq_to_rrule(freq), **rrule_params)
diff --git a/nextcloud_odoo_sync/models/jicson.py b/nextcloud_odoo_sync/models/jicson.py
index 8bc3649..00a1944 100644
--- a/nextcloud_odoo_sync/models/jicson.py
+++ b/nextcloud_odoo_sync/models/jicson.py
@@ -1,5 +1,5 @@
-from urllib.request import Request, urlopen
import io
+from urllib.request import Request, urlopen
class StreamObject:
diff --git a/nextcloud_odoo_sync/models/nc_calendar.py b/nextcloud_odoo_sync/models/nc_calendar.py
index dde87f5..f0e6c64 100644
--- a/nextcloud_odoo_sync/models/nc_calendar.py
+++ b/nextcloud_odoo_sync/models/nc_calendar.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields, api
+from odoo import api, fields, models
class NcCalendar(models.Model):
diff --git a/nextcloud_odoo_sync/models/nc_event_status.py b/nextcloud_odoo_sync/models/nc_event_status.py
index f622216..39e88eb 100644
--- a/nextcloud_odoo_sync/models/nc_event_status.py
+++ b/nextcloud_odoo_sync/models/nc_event_status.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields
+from odoo import fields, models
class NcEventStatus(models.Model):
diff --git a/nextcloud_odoo_sync/models/nc_sync_error.py b/nextcloud_odoo_sync/models/nc_sync_error.py
index 87791be..30be50c 100644
--- a/nextcloud_odoo_sync/models/nc_sync_error.py
+++ b/nextcloud_odoo_sync/models/nc_sync_error.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields
+from odoo import fields, models
class NcSyncError(models.Model):
diff --git a/nextcloud_odoo_sync/models/nc_sync_log.py b/nextcloud_odoo_sync/models/nc_sync_log.py
index c08bcec..52156fa 100644
--- a/nextcloud_odoo_sync/models/nc_sync_log.py
+++ b/nextcloud_odoo_sync/models/nc_sync_log.py
@@ -1,10 +1,10 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
+import logging
from datetime import datetime, timedelta
-from odoo import models, fields
-import logging
+from odoo import fields, models
_logger = logging.getLogger(__name__)
diff --git a/nextcloud_odoo_sync/models/nc_sync_user.py b/nextcloud_odoo_sync/models/nc_sync_user.py
index 73e5fb6..3a84042 100644
--- a/nextcloud_odoo_sync/models/nc_sync_user.py
+++ b/nextcloud_odoo_sync/models/nc_sync_user.py
@@ -4,10 +4,11 @@
import hashlib
import json
+from datetime import date, datetime
-from datetime import datetime, date
-from odoo import models, fields, api, _
+from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
+
from odoo.addons.nextcloud_odoo_sync.models import jicson
@@ -22,8 +23,8 @@ class NcSyncUser(models.Model):
nc_password = fields.Char("Password", required=True)
user_message = fields.Char(
default="'Default Calendar' field will be used"
- "as your default Nextcloud calendar when "
- "creating new events in Odoo"
+ "as your default Nextcloud calendar when "
+ "creating new events in Odoo"
)
user_id = fields.Many2one(
@@ -32,9 +33,18 @@ class NcSyncUser(models.Model):
partner_id = fields.Many2one(
"res.partner", "Partner", related="user_id.partner_id", store=True
)
- nc_calendar_id = fields.Many2one("nc.calendar", "Default Calendar", help="Allows 2 way syncing with this calendar")
- nc_calendar_ids = fields.Many2many("nc.calendar", "nc_sync_user_nc_calendar_rel", "sync_user_id", "nc_calendar_id",
- string="Show Other Calendars")
+ nc_calendar_id = fields.Many2one(
+ "nc.calendar",
+ "Default Calendar",
+ help="Allows 2 way syncing with this calendar",
+ )
+ nc_calendar_ids = fields.Many2many(
+ "nc.calendar",
+ "nc_sync_user_nc_calendar_rel",
+ "sync_user_id",
+ "nc_calendar_id",
+ string="Show Other Calendars",
+ )
nc_hash_ids = fields.One2many(
"calendar.event.nchash", "nc_sync_user_id", "Hash Values"
)
@@ -91,9 +101,9 @@ def onchange_nc_calendar_id(self):
"""
if self.nc_calendar_id:
self.user_message = (
- "%s will be used as your default Odoo "
- "calendar when creating new events"
- ) % self.nc_calendar_id.name
+ "%s will be used as your default Odoo "
+ "calendar when creating new events"
+ ) % self.nc_calendar_id.name
if self.nc_email:
self.sync_calendar = True
if self.nc_calendar_id.id in self.nc_calendar_ids.ids:
@@ -113,7 +123,7 @@ def onchange_nc_calendar_ids(self):
@api.model
def create(self, vals):
- if vals.get('nextcloud_url', False):
+ if vals.get("nextcloud_url", False):
vals["nextcloud_url"] = vals["nextcloud_url"].strip("/")
return super(NcSyncUser, self).create(vals)
@@ -125,14 +135,21 @@ def write(self, vals):
"""
nc_calendar_ids = []
calendar_event_obj = self.env["calendar.event"]
- if vals.get('user_name') or vals.get('nc_password') or vals.get('nextcloud_url', False) or vals.get(
- 'sync_calendar'):
+ if (
+ vals.get("user_name")
+ or vals.get("nc_password")
+ or vals.get("nextcloud_url", False)
+ or vals.get("sync_calendar")
+ ):
nextcloud_caldav_obj = self.env["nextcloud.caldav"]
for record in self:
- nc_url = ((vals.get("nextcloud_url", "").strip("/") if vals.get(
- "nextcloud_url") else record.nextcloud_url) + "/remote.php/dav")
- username = vals.get('user_name', False) or record.user_name
- nc_password = vals.get('nc_password', False) or record.nc_password
+ nc_url = (
+ vals.get("nextcloud_url", "").strip("/")
+ if vals.get("nextcloud_url")
+ else record.nextcloud_url
+ ) + "/remote.php/dav"
+ username = vals.get("user_name", False) or record.user_name
+ nc_password = vals.get("nc_password", False) or record.nc_password
connection, principal = nextcloud_caldav_obj.check_nextcloud_connection(
url=nc_url, username=username, password=nc_password
)
@@ -141,19 +158,18 @@ def write(self, vals):
response = principal["response_description"]
raise ValidationError(f"{sync_error}: {response}")
if "nc_calendar_id" in vals:
- calendar_event_ids = (
- calendar_event_obj
- .search(
- [
- "|",
- ("user_id", "=", self.user_id.id),
- ("partner_ids", "in", self.user_id.partner_id.id),
- ("nc_synced", "=", False),
- ]
- )
- .filtered(
- lambda x: (not x.nc_calendar_ids) and x.start >= datetime.combine(self.start_date or date.today(),
- datetime.min.time())
+ calendar_event_ids = calendar_event_obj.search(
+ [
+ "|",
+ ("user_id", "=", self.user_id.id),
+ ("partner_ids", "in", self.user_id.partner_id.id),
+ ("nc_synced", "=", False),
+ ]
+ ).filtered(
+ lambda x: (not x.nc_calendar_ids)
+ and x.start
+ >= datetime.combine(
+ self.start_date or date.today(), datetime.min.time()
)
)
# calendar_ids = calendar_event_ids.nc_calendar_ids.filtered(
@@ -164,19 +180,29 @@ def write(self, vals):
calendar_event_ids.with_context(sync=True).write(
{"nc_calendar_ids": [(4, vals["nc_calendar_id"])]}
)
- if vals.get('nextcloud_url', False):
+ if vals.get("nextcloud_url", False):
vals["nextcloud_url"] = vals["nextcloud_url"].strip("/")
- if vals.get('nc_calendar_ids'):
+ if vals.get("nc_calendar_ids"):
nc_calendar_ids = self.nc_calendar_ids
res = super(NcSyncUser, self).write(vals)
- if vals.get('nc_calendar_ids'):
+ if vals.get("nc_calendar_ids"):
for record in self:
for rec in nc_calendar_ids:
- if rec not in record.nc_calendar_ids and rec != record.nc_calendar_id:
+ if (
+ rec not in record.nc_calendar_ids
+ and rec != record.nc_calendar_id
+ ):
calendar_event_obj.search(
- [("partner_ids", "in", record.user_id.partner_id.id), ('nc_uid', '!=', False)]).filtered(
- lambda x: len(x.nc_calendar_ids) == 1 and rec in x.nc_calendar_ids).with_context(
- force_delete=True).unlink()
+ [
+ ("partner_ids", "in", record.user_id.partner_id.id),
+ ("nc_uid", "!=", False),
+ ]
+ ).filtered(
+ lambda x: len(x.nc_calendar_ids) == 1
+ and rec in x.nc_calendar_ids
+ ).with_context(
+ force_delete=True
+ ).unlink()
return res
def unlink(self):
@@ -187,11 +213,15 @@ def unlink(self):
calendar_event_obj = self.env["calendar.event"]
calendar_event_hash_obj = self.env["calendar.event.nchash"]
for record in self.filtered(lambda x: x.user_id):
- calendar_event_hash_obj.search([('nc_sync_user_id','=',record.id)]).unlink()
+ calendar_event_hash_obj.search(
+ [("nc_sync_user_id", "=", record.id)]
+ ).unlink()
calendar_ids = calendar_event_obj.search(
[("user_id", "=", record.user_id.id)]
)
- calendar_ids.filtered(lambda x:not x.nc_hash_ids).write({"nc_uid": False, "nc_synced": False})
+ calendar_ids.filtered(lambda x: not x.nc_hash_ids).write(
+ {"nc_uid": False, "nc_synced": False}
+ )
# Remove all Nextcloud calendar records
record.user_id.nc_calendar_ids.unlink()
return super(NcSyncUser, self).unlink()
@@ -205,7 +235,7 @@ def save_user_config(self):
return self.env.ref("calendar.action_calendar_event").sudo().read()[0]
def get_user_connection(self):
- nc_url = (self.nextcloud_url + "/remote.php/dav")
+ nc_url = self.nextcloud_url + "/remote.php/dav"
connection, principal = self.env["nextcloud.caldav"].check_nextcloud_connection(
url=nc_url, username=self.user_name, password=self.nc_password
)
@@ -213,8 +243,12 @@ def get_user_connection(self):
sync_error = principal["sync_error_id"].name
response = principal["response_description"]
raise ValidationError(f"{sync_error}: {response}")
- user_data = self.env["nextcloud.base"].get_user(principal.client.username, self.nextcloud_url, self.user_name,
- self.nc_password)
+ user_data = self.env["nextcloud.base"].get_user(
+ principal.client.username,
+ self.nextcloud_url,
+ self.user_name,
+ self.nc_password,
+ )
self.nc_email = user_data.get("email", False) if user_data else False
return {"connection": connection, "principal": principal}
@@ -248,7 +282,7 @@ def get_user_calendars(self, principal):
for record in nc_calendars:
nc_calendar_id = nc_calendar_ids.filtered(
lambda x: x.name == record.name
- and x.calendar_url == record.canonical_url
+ and x.calendar_url == record.canonical_url
)
if not nc_calendar_id:
result.append(
@@ -344,9 +378,15 @@ def get_all_user_events(self, **params):
"principal": False,
}
for user in self:
- start_date = datetime.combine(self.start_date or date.today(), datetime.min.time())
+ start_date = datetime.combine(
+ self.start_date or date.today(), datetime.min.time()
+ )
if not events:
- events = self.env["calendar.event"].sudo().search([('start', '>=', start_date)], order="start")
+ events = (
+ self.env["calendar.event"]
+ .sudo()
+ .search([("start", ">=", start_date)], order="start")
+ )
try:
connection_dict = self.get_user_connection()
principal = connection_dict.get("principal", False)
@@ -370,7 +410,7 @@ def get_all_user_events(self, **params):
# Get all Odoo events where user is organizer or attendee
od_event_ids = events.filtered(
lambda x: x.user_id == user.user_id
- or (x.partner_ids and user.partner_id in x.partner_ids)
+ or (x.partner_ids and user.partner_id in x.partner_ids)
)
for event in od_event_ids:
# if event is not yet syned into nextcloud but the current
@@ -378,9 +418,9 @@ def get_all_user_events(self, **params):
# should not be created in nextcloud since it will
# be automatically created by the event organizer
if (
- event.user_id in params["all_sync_user_ids"].mapped("user_id")
- and not event.nc_uid
- and event.user_id != user.user_id
+ event.user_id in params["all_sync_user_ids"].mapped("user_id")
+ and not event.nc_uid
+ and event.user_id != user.user_id
):
continue
event_hash = False
@@ -395,20 +435,23 @@ def get_all_user_events(self, **params):
"event_hash": event_hash[0] if event_hash else False,
}
)
- if event.recurrence_id and event.recurrence_id.base_event_id not in od_event_ids:
- event_hash = False
- base_event = event.recurrence_id.base_event_id
- if base_event.nc_hash_ids:
- event_hash = base_event.nc_hash_ids.filtered(
- lambda x: x.nc_sync_user_id == user
- ).mapped("nc_event_hash")
- result["od_events"].append(
- {
- "nc_uid": base_event.nc_uid,
- "od_event": base_event,
- "event_hash": event_hash[0] if event_hash else False,
- }
- )
+ if (
+ event.recurrence_id
+ and event.recurrence_id.base_event_id not in od_event_ids
+ ):
+ event_hash = False
+ base_event = event.recurrence_id.base_event_id
+ if base_event.nc_hash_ids:
+ event_hash = base_event.nc_hash_ids.filtered(
+ lambda x: x.nc_sync_user_id == user
+ ).mapped("nc_event_hash")
+ result["od_events"].append(
+ {
+ "nc_uid": base_event.nc_uid,
+ "od_event": base_event,
+ "event_hash": event_hash[0] if event_hash else False,
+ }
+ )
# Get all Nextcloud events of the user
nc_calendar_obj = self.env["nc.calendar"]
@@ -438,8 +481,12 @@ def get_all_user_events(self, **params):
}
)
continue
- if calendar.canonical_url not in self.nc_calendar_ids.mapped(
- 'calendar_url') and not calendar.canonical_url == self.nc_calendar_id.calendar_url:
+ if (
+ calendar.canonical_url
+ not in self.nc_calendar_ids.mapped("calendar_url")
+ and not calendar.canonical_url
+ == self.nc_calendar_id.calendar_url
+ ):
continue
events_fetched = calendar.search(
start=start_date,
@@ -508,10 +555,15 @@ def get_nc_event_hash_by_uid(self, nc_uid):
nc_calendar_id.name = calendar.name
else:
return False
- if calendar.canonical_url not in self.nc_calendar_ids.mapped(
- 'calendar_url') and not calendar.canonical_url == self.nc_calendar_id.calendar_url:
+ if (
+ calendar.canonical_url
+ not in self.nc_calendar_ids.mapped("calendar_url")
+ and not calendar.canonical_url == self.nc_calendar_id.calendar_url
+ ):
continue
- start_date = datetime.combine(self.start_date or date.today(), datetime.min.time())
+ start_date = datetime.combine(
+ self.start_date or date.today(), datetime.min.time()
+ )
events_fetched = calendar.search(
start=start_date,
event=True,
@@ -551,10 +603,15 @@ def get_nc_event_hash_by_uid_for_other_user(self, nc_uid):
nc_calendar_id.name = calendar.name
else:
return False, False
- if calendar.canonical_url not in self.nc_calendar_ids.mapped(
- 'calendar_url') and not calendar.canonical_url == self.nc_calendar_id.calendar_url:
+ if (
+ calendar.canonical_url
+ not in self.nc_calendar_ids.mapped("calendar_url")
+ and not calendar.canonical_url == self.nc_calendar_id.calendar_url
+ ):
continue
- start_date = datetime.combine(self.start_date or date.today(), datetime.min.time())
+ start_date = datetime.combine(
+ self.start_date or date.today(), datetime.min.time()
+ )
events_fetched = calendar.search(
start=start_date,
event=True,
diff --git a/nextcloud_odoo_sync/models/nextcloud_base.py b/nextcloud_odoo_sync/models/nextcloud_base.py
index 3fcd4d5..dd80e67 100644
--- a/nextcloud_odoo_sync/models/nextcloud_base.py
+++ b/nextcloud_odoo_sync/models/nextcloud_base.py
@@ -2,8 +2,8 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import requests
+
from odoo import models
-from odoo.http import request
class NextcloudBase(models.AbstractModel):
@@ -62,6 +62,7 @@ def rtn(self, resp):
:return json api response
"""
return resp.json()
+
#
# def get(self, url="", params=None):
# url = self.get_full_url(url, "/ocs/v1.php/cloud/users")
@@ -107,13 +108,8 @@ def get_user(self, uid, url, username, password):
"OCS-APIRequest": "true",
"Content-Type": "application/x-www-form-urlencoded",
},
- "auth_pk": (
- username,
- password
- ),
+ "auth_pk": (username, password),
}
- request = requests.get(
- res, auth=data["auth_pk"], headers=data["h_get"]
- )
+ request = requests.get(res, auth=data["auth_pk"], headers=data["h_get"])
result = self.rtn(request)
return result["ocs"]["data"]
diff --git a/nextcloud_odoo_sync/models/nextcloud_caldav.py b/nextcloud_odoo_sync/models/nextcloud_caldav.py
index 64c5867..0f5c20f 100644
--- a/nextcloud_odoo_sync/models/nextcloud_caldav.py
+++ b/nextcloud_odoo_sync/models/nextcloud_caldav.py
@@ -1,16 +1,17 @@
# Copyright (c) 2022 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
+import ast
import logging
-import requests
+from datetime import date as dtdate, datetime, timedelta
+
import pytz
-import ast
-from odoo.tools import html2plaintext
+import requests
from dateutil.parser import parse
-from datetime import datetime, timedelta, date as dtdate
-from odoo import models, _
-from odoo.addons.nextcloud_odoo_sync.models import jicson
+
+from odoo import _, models
from odoo.exceptions import ValidationError
+from odoo.tools import html2plaintext
_logger = logging.getLogger(__name__)
@@ -68,8 +69,8 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
od_events_dict = {"create": [], "write": [], "delete": []}
nc_events_dict = {"create": [], "write": [], "delete": []}
nc_events_create = []
- all_odoo_events = self.env['calendar.event'].search([])
- recurrence_id_key = 'RECURRENCE-ID'
+ all_odoo_events = self.env["calendar.event"].search([])
+ recurrence_id_key = "RECURRENCE-ID"
# Compare Odoo events to sync
if od_events and nc_events:
# Odoo -> Nextcloud
@@ -94,31 +95,43 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
if not od_event.nc_status_id:
od_event.nc_status_id = nc_event_status_confirmed_id.id
if (
- od_event.nc_status_id
- and od_event.nc_status_id.name.lower() != "canceled"
+ od_event.nc_status_id
+ and od_event.nc_status_id.name.lower() != "canceled"
):
duplicate = self.check_duplicate(nc_events, ode)
if not duplicate:
if od_event.recurrence_id:
base_event = od_event.recurrence_id.base_event_id
- if not base_event.nc_uid and base_event not in nc_events_create:
+ if (
+ not base_event.nc_uid
+ and base_event not in nc_events_create
+ ):
base_event_vals = {
"nc_uid": base_event.nc_uid,
"od_event": base_event,
"event_hash": False,
}
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
nc_events_dict["create"].append(base_event_vals)
nc_events_create.append(base_event)
continue
else:
if od_event not in nc_events_create:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
nc_events_dict["create"].append(ode)
nc_events_create.append(od_event)
continue
else:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
od_event.nc_uid = duplicate["nc_uid"]
ode["nc_uid"] = duplicate["nc_uid"]
duplicate["od_event"] = od_event
@@ -146,183 +159,419 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
vevent = ode["nc_caldav"].vobject_instance.vevent
if not od_event.nextcloud_calendar_type:
if (
- "status" not in vevent.contents
- or vevent.status.value.lower() == "cancelled"
+ "status" not in vevent.contents
+ or vevent.status.value.lower()
+ == "cancelled"
):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
if nce not in od_events_dict["delete"]:
od_events_dict["delete"].append(nce)
- if (
- od_event.nc_to_delete
- ):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if od_event.nc_to_delete:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if ode not in nc_events_dict["delete"]:
nc_events_dict["delete"].append(ode)
else:
if not od_event.nextcloud_calendar_type:
if (
- od_event.nc_status_id
- and od_event.nc_status_id.name.lower()
- != "canceled"
+ od_event.nc_status_id
+ and od_event.nc_status_id.name.lower()
+ != "canceled"
):
# Case 2.b: If there are changes to
# sync (nc_synced=False) and to delete
# (nc_to_delete=True), delete Nextcloud
# event
- if (
- od_event.nc_to_delete
- ):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
- if ode not in nc_events_dict["delete"]:
- nc_events_dict["delete"].append(ode)
+ if od_event.nc_to_delete:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
+ if (
+ ode
+ not in nc_events_dict["delete"]
+ ):
+ nc_events_dict["delete"].append(
+ ode
+ )
# Case 2.c: If there are changes to
# sync (nc_sycned=False) but not to
# delete (nc_to_delete=False), update
# Nextcloud event
else:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
if not od_event.recurrence_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
- nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
- "%Y%m%dT%H%M%SZ",
+ nc_last_modified = (
+ datetime.strptime(
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
+ "%Y%m%dT%H%M%SZ",
+ )
+ )
+ od_last_modified = (
+ od_event.write_date
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in nce["nc_event"][
- 0] and od_event.nc_rid in nce["nc_event"][0][
- 'exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce[
+ "nc_event"
+ ][0]
+ and od_event.nc_rid
+ in nce[
+ "nc_event"
+ ][0]["exdates"]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(
+ ode
+ )
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(nce)
else:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
if od_event.nc_rid:
- od_event_nc_rid = od_event.nc_rid
+ od_event_nc_rid = (
+ od_event.nc_rid
+ )
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
- if recurrence_id_key in key
+ value
+ for key, value in nce_events_dict.items()
+ if recurrence_id_key
+ in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[
+ 0
+ ]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(
+ ode
+ )
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],
- 'detach': True})
- od_events_dict["write"].append(
- recurring_nce)
- nc_modified = True
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
+ nc_modified = (
+ True
+ )
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if od_event == od_event.recurrence_id.base_event_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ od_event
+ == od_event.recurrence_id.base_event_id
+ ):
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- if nc_last_modified > od_event.recurrence_id.write_date:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in \
- nce["nc_event"][
- 0] and od_event.nc_rid in \
- nce["nc_event"][0]['exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nc_last_modified
+ > od_event.recurrence_id.write_date
+ ):
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce[
+ "nc_event"
+ ][0]
+ and od_event.nc_rid
+ in nce[
+ "nc_event"
+ ][0][
+ "exdates"
+ ]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(
+ ode
+ )
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(
+ nce
+ )
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
if od_event.nc_rid:
- od_event_nc_rid = od_event.nc_rid
+ od_event_nc_rid = (
+ od_event.nc_rid
+ )
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
- if recurrence_id_key in key
+ value
+ for key, value in nce_events_dict.items()
+ if recurrence_id_key
+ in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[
+ 0
+ ]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- nc_modified = True
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ nc_modified = (
+ True
+ )
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(
+ ode
+ )
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],'detach':True})
- od_events_dict["write"].append(
- recurring_nce)
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
if ode not in nc_events_dict["delete"]:
nc_events_dict["delete"].append(ode)
else:
- if (
- od_event.nc_to_delete
- ):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if od_event.nc_to_delete:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
if ode not in nc_events_dict["delete"]:
nc_events_dict["delete"].append(ode)
# Case 3: If both hash differs
else:
if od_event.nextcloud_calendar_type:
- if (
- od_event.nc_to_delete
- ):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if od_event.nc_to_delete:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if ode not in nc_events_dict["delete"]:
nc_events_dict["delete"].append(ode)
elif nce not in od_events_dict["write"]:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
od_events_dict["write"].append(nce)
continue
# Case 3.a: If Odoo event has no change
@@ -331,10 +580,13 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
# delete if cancelled
vevent = ode["nc_caldav"].vobject_instance.vevent
if (
- "status" not in vevent.contents
- or vevent.status.value.lower() == "cancelled"
+ "status" not in vevent.contents
+ or vevent.status.value.lower() == "cancelled"
):
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if nce not in od_events_dict["delete"]:
od_events_dict["delete"].append(nce)
else:
@@ -345,121 +597,332 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
# hence we retrict modification to
# odoo event by the attendee as
# well
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id
+ == od_event.user_id
+ ):
if not od_event.recurrence_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
- nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
- "%Y%m%dT%H%M%SZ",
+ nc_last_modified = (
+ datetime.strptime(
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
+ "%Y%m%dT%H%M%SZ",
+ )
+ )
+ od_last_modified = (
+ od_event.write_date
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in nce["nc_event"][0] and od_event.nc_rid in nce["nc_event"][0]['exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce["nc_event"][
+ 0
+ ]
+ and od_event.nc_rid
+ in nce["nc_event"][
+ 0
+ ]["exdates"]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(ode)
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(nce)
else:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
if od_event.nc_rid:
- od_event_nc_rid = od_event.nc_rid
+ od_event_nc_rid = (
+ od_event.nc_rid
+ )
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
- if recurrence_id_key in key
+ value
+ for key, value in nce_events_dict.items()
+ if recurrence_id_key
+ in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[
+ 0
+ ]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(
+ ode
+ )
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],'detach':True})
- od_events_dict["write"].append(
- recurring_nce)
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
nc_modified = True
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if od_event == od_event.recurrence_id.base_event_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ od_event
+ == od_event.recurrence_id.base_event_id
+ ):
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
- nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
- "%Y%m%dT%H%M%SZ",
+ nc_last_modified = (
+ datetime.strptime(
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
+ "%Y%m%dT%H%M%SZ",
+ )
)
- if nc_last_modified > od_event.recurrence_id.write_date:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in nce["nc_event"][
- 0] and od_event.nc_rid in nce["nc_event"][0][
- 'exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nc_last_modified
+ > od_event.recurrence_id.write_date
+ ):
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce[
+ "nc_event"
+ ][0]
+ and od_event.nc_rid
+ in nce[
+ "nc_event"
+ ][0]["exdates"]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(
+ ode
+ )
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(nce)
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
if od_event.nc_rid:
- od_event_nc_rid = od_event.nc_rid
+ od_event_nc_rid = (
+ od_event.nc_rid
+ )
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
- if recurrence_id_key in key
+ value
+ for key, value in nce_events_dict.items()
+ if recurrence_id_key
+ in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[
+ 0
+ ]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(
+ ode
+ )
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],'detach':True})
- od_events_dict["write"].append(
- recurring_nce)
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
nc_modified = True
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
# revert the event of attendee
# in nextcloud to event of
@@ -472,13 +935,13 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
# attendee in nextcloud
log_obj.log_event(
message="A Nextcloud event"
- " has been modified by one"
- " of its attendee in Nextcloud"
- " but does not get"
- " reflected in the organizer"
- " event. This changes will be"
- " ignored in Odoo. Event details:"
- "\n%s" % nce["nc_event"][0]
+ " has been modified by one"
+ " of its attendee in Nextcloud"
+ " but does not get"
+ " reflected in the organizer"
+ " event. This changes will be"
+ " ignored in Odoo. Event details:"
+ "\n%s" % nce["nc_event"][0]
)
else:
# Case 3.b: If Odoo has changes
@@ -486,7 +949,10 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
# (nc_to_delete=True), delete Nextcloud
# event
if not od_event.nc_synced and od_event.nc_to_delete:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if ode not in nc_events_dict["delete"]:
nc_events_dict["delete"].append(ode)
# Case 3.c: If Odoo has changes
@@ -495,127 +961,318 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
else:
# Check LAST-MODIFIED date value in
# Nextcloud event
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if not od_event.recurrence_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
- nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
- "%Y%m%dT%H%M%SZ",
+ nc_last_modified = (
+ datetime.strptime(
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
+ "%Y%m%dT%H%M%SZ",
+ )
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in nce["nc_event"][
- 0] and od_event.nc_rid in nce["nc_event"][0]['exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce["nc_event"][0]
+ and od_event.nc_rid
+ in nce["nc_event"][0][
+ "exdates"
+ ]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(ode)
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(nce)
else:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict["write"]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict["write"].append(
+ ode
+ )
if od_event.nc_rid:
od_event_nc_rid = od_event.nc_rid
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
+ value
+ for key, value in nce_events_dict.items()
if recurrence_id_key in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[0]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],
- 'detach': True})
- od_events_dict["write"].append(
- recurring_nce)
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
nc_modified = True
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if od_event == od_event.recurrence_id.base_event_id:
- if "LAST-MODIFIED" in nce["nc_event"][0]:
+ if (
+ od_event
+ == od_event.recurrence_id.base_event_id
+ ):
+ if (
+ "LAST-MODIFIED"
+ in nce["nc_event"][0]
+ ):
# The "Z" stands for Zulu time
# (zero hours ahead of GMT) which
# is another name for UTC
- nc_last_modified = datetime.strptime(
- nce["nc_event"][0]["LAST-MODIFIED"],
- "%Y%m%dT%H%M%SZ",
+ nc_last_modified = (
+ datetime.strptime(
+ nce["nc_event"][0][
+ "LAST-MODIFIED"
+ ],
+ "%Y%m%dT%H%M%SZ",
+ )
)
- if nc_last_modified > od_event.recurrence_id.write_date:
- if nce not in od_events_dict["write"]:
- if od_event.nc_rid and "exdates" in nce["nc_event"][
- 0] and od_event.nc_rid in nce["nc_event"][0][
- 'exdates']:
- if ode not in od_events_dict["delete"]:
- od_events_dict["delete"].append(ode)
+ if (
+ nc_last_modified
+ > od_event.recurrence_id.write_date
+ ):
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ if (
+ od_event.nc_rid
+ and "exdates"
+ in nce["nc_event"][
+ 0
+ ]
+ and od_event.nc_rid
+ in nce["nc_event"][
+ 0
+ ]["exdates"]
+ ):
+ if (
+ ode
+ not in od_events_dict[
+ "delete"
+ ]
+ ):
+ od_events_dict[
+ "delete"
+ ].append(ode)
else:
- od_events_dict["write"].append(nce)
+ od_events_dict[
+ "write"
+ ].append(nce)
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
if od_event.nc_rid:
od_event_nc_rid = od_event.nc_rid
nc_modified = False
- for nce_events_dict in nce["nc_event"]:
+ for nce_events_dict in nce[
+ "nc_event"
+ ]:
matching_values = [
- value for key, value in nce_events_dict.items()
+ value
+ for key, value in nce_events_dict.items()
if recurrence_id_key in key
]
if matching_values:
- if od_event_nc_rid == matching_values[
- 0] and "LAST-MODIFIED" in nce_events_dict:
+ if (
+ od_event_nc_rid
+ == matching_values[0]
+ and "LAST-MODIFIED"
+ in nce_events_dict
+ ):
nc_last_modified = datetime.strptime(
- nce_events_dict["LAST-MODIFIED"],
+ nce_events_dict[
+ "LAST-MODIFIED"
+ ],
"%Y%m%dT%H%M%SZ",
)
- od_last_modified = od_event.write_date
- if od_last_modified > nc_last_modified:
- if ode not in nc_events_dict["write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ od_last_modified = (
+ od_event.write_date
+ )
+ if (
+ od_last_modified
+ > nc_last_modified
+ ):
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
else:
- if nce not in od_events_dict["write"]:
- recurring_nce = nce.copy()
+ if (
+ nce
+ not in od_events_dict[
+ "write"
+ ]
+ ):
+ recurring_nce = (
+ nce.copy()
+ )
recurring_nce.update(
- {'nc_event': [nce_events_dict],'detach':True})
- od_events_dict["write"].append(recurring_nce)
- nc_modified=True
+ {
+ "nc_event": [
+ nce_events_dict
+ ],
+ "detach": True,
+ }
+ )
+ od_events_dict[
+ "write"
+ ].append(
+ recurring_nce
+ )
+ nc_modified = True
break
if not nc_modified:
- if ode not in nc_events_dict[
- "write"] and not od_event.nc_synced:
- nc_events_dict["write"].append(ode)
+ if (
+ ode
+ not in nc_events_dict[
+ "write"
+ ]
+ and not od_event.nc_synced
+ ):
+ nc_events_dict[
+ "write"
+ ].append(ode)
# Case 4: If the value of Odoo nc_uid is not found in all
# of Nextcloud events, then it was deleted in Nextcloud
if not valid_nc_uid:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if ode not in od_events_dict["delete"]:
od_events_dict["delete"].append(ode)
# Nextcloud -> Odoo
@@ -623,10 +1280,12 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
vevent = nce["nc_caldav"].vobject_instance.vevent
# ignore if cancelled
if (
- "status" not in vevent.contents
- or vevent.status.value.lower() != "cancelled"
+ "status" not in vevent.contents
+ or vevent.status.value.lower() != "cancelled"
):
- valid_nc_uid = all_odoo_events.filtered(lambda ev: ev.nc_uid == nce["nc_uid"])
+ valid_nc_uid = all_odoo_events.filtered(
+ lambda ev: ev.nc_uid == nce["nc_uid"]
+ )
# for ode in od_events:
# if nce["nc_uid"] == ode["nc_uid"]:
# valid_nc_uid = True
@@ -642,10 +1301,12 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
vevent = nce["nc_caldav"].vobject_instance.vevent
# ignore if cancelled
if (
- "status" not in vevent.contents
- or vevent.status.value.lower() != "cancelled"
+ "status" not in vevent.contents
+ or vevent.status.value.lower() != "cancelled"
):
- valid_nc_uid = all_odoo_events.filtered(lambda ev: ev.nc_uid == nce["nc_uid"])
+ valid_nc_uid = all_odoo_events.filtered(
+ lambda ev: ev.nc_uid == nce["nc_uid"]
+ )
if not valid_nc_uid:
od_events_dict["create"].append(nce)
# Case 7: If there is not a single event in Nextcloud, check if Odoo
@@ -655,22 +1316,31 @@ def compare_events(self, od_events, nc_events, sync_user_id, log_obj):
# ignore if cancelled
od_event = ode["od_event"]
if (
- od_event.nc_status_id
- and od_event.nc_status_id.name.lower() != "canceled"
+ od_event.nc_status_id
+ and od_event.nc_status_id.name.lower() != "canceled"
):
# Case 7.a: If the event has an existing nc_uid value, then
# its a previous event in Nextcloud that might have been
# deleted
if od_event.nc_uid:
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
od_events_dict["delete"].append(ode)
else:
# Case 7.b: If the event has no nc_uid value then its a
# new event in Odoo to be created in Nextcloud
- if od_event.user_id and sync_user_id.user_id == od_event.user_id:
+ if (
+ od_event.user_id
+ and sync_user_id.user_id == od_event.user_id
+ ):
if od_event.recurrence_id:
base_event = od_event.recurrence_id.base_event_id
- if not base_event.nc_uid and base_event not in nc_events_create:
+ if (
+ not base_event.nc_uid
+ and base_event not in nc_events_create
+ ):
base_event_vals = {
"nc_uid": base_event.nc_uid,
"od_event": base_event,
@@ -695,7 +1365,7 @@ def check_duplicate(self, nc_events, ode):
result = {}
fields = {"name": "SUMMARY", "start": "DTSTART", "stop": "DTEND"}
d = 0
- date_fields = ['dtstart', 'dtend', 'rrule', 'recurrence-id', 'last-modified']
+ date_fields = ["dtstart", "dtend", "rrule", "recurrence-id", "last-modified"]
for f in fields:
for nce in nc_events:
for nc_event in nce["nc_event"]:
@@ -707,13 +1377,17 @@ def check_duplicate(self, nc_events, ode):
data = value
if key_field[0] in date_fields:
data = self.get_event_datetime(
- key_field, value, nc_event, ode["od_event"], nce["nc_caldav"]
+ key_field,
+ value,
+ nc_event,
+ ode["od_event"],
+ nce["nc_caldav"],
)
allday = ode["od_event"].allday
if isinstance(data, datetime) or isinstance(data, dtdate):
if (data == ode["od_event"][f] and not allday) or (
- data == ode["od_event"][f].date() and allday
+ data == ode["od_event"][f].date() and allday
):
d += 1
elif data == ode["od_event"][f]:
@@ -755,18 +1429,19 @@ def get_event_attendees(self, calendar_event, user_id, **params):
organizer = calendar_event.instance.vevent.organizer.value
attendees.append(organizer)
except Exception:
- organizer = ''
+ organizer = ""
for att in attendees:
email = att.split(":")[-1].lower()
if email != "false":
# Check if an Odoo user has the same email address
att_user_id = nc_sync_user_obj.search(
- [("nc_email", "=", email), ("sync_calendar", "=", True)], limit=1
+ [("nc_email", "=", email), ("sync_calendar", "=", True)],
+ limit=1,
).user_id
if not att_user_id:
att_user_id = all_user_ids.filtered(
lambda x: x.partner_id.email
- and x.partner_id.email.lower() == email
+ and x.partner_id.email.lower() == email
)
# In case more than 1 user has the same email address,
# check which user is in nc.sync.user model
@@ -790,7 +1465,9 @@ def get_event_attendees(self, calendar_event, user_id, **params):
att_user_id.partner_id.email = email
attendee_partner_ids.append(att_user_id.partner_id.id)
else:
- contact_id = res_partner_obj.search([('email', '=', email)], limit=1)
+ contact_id = res_partner_obj.search(
+ [("email", "=", email)], limit=1
+ )
if not contact_id:
contact_id = res_partner_obj.create(
{"name": email, "email": email, "nc_sync": True}
@@ -798,33 +1475,32 @@ def get_event_attendees(self, calendar_event, user_id, **params):
all_partner_ids |= contact_id
attendee_partner_ids.append(contact_id.id)
if organizer:
- organizer_email = organizer.replace(
- "mailto:", ""
- )
+ organizer_email = organizer.replace("mailto:", "")
org_user_id = nc_sync_user_obj.search(
- [("nc_email", "=", organizer_email), ("sync_calendar", "=", True)], limit=1
+ [("nc_email", "=", organizer_email), ("sync_calendar", "=", True)],
+ limit=1,
).user_id
if not org_user_id:
org_user_id = all_user_ids.filtered(
lambda x: x.partner_id.email
- and x.partner_id.email.lower() == email
+ and x.partner_id.email.lower() == email
)
if not attendees:
attendee_partner_ids = [user_id.partner_id.id]
org_user_id = user_id
# Get attendees for Nextcloud event
elif (
- isinstance(calendar_event, models.Model)
- and calendar_event.partner_ids
- and all_sync_user_ids
+ isinstance(calendar_event, models.Model)
+ and calendar_event.partner_ids
+ and all_sync_user_ids
):
nc_user_ids = self.env["nc.sync.user"]
for partner in calendar_event.partner_ids:
# In Nextcloud, we don"t populate the attendee if there is only
# the organizer involve
if (
- partner != user_id.partner_id
- and len(calendar_event.partner_ids) > 1
+ partner != user_id.partner_id
+ and len(calendar_event.partner_ids) > 1
):
nc_user_id = all_sync_user_ids.filtered(
lambda x: x.partner_id.id == partner.id and x.sync_calendar
@@ -847,7 +1523,7 @@ def get_event_attendees(self, calendar_event, user_id, **params):
return list(set(attendee_partner_ids)), org_user_id, params
def get_event_datetime(
- self, nc_field, nc_value, vals, od_event=False, nc_event=False
+ self, nc_field, nc_value, vals, od_event=False, nc_event=False
):
"""
This method will parse the Nextcloud event date,
@@ -865,18 +1541,22 @@ def get_event_datetime(
# Cannot get the calendar event for single recurring instance
# hence we revent to string manipulation of date
event_date = nc_event.icalendar_component.get(key).dt
- tz = 'UTC'
+ tz = "UTC"
if isinstance(event_date, datetime):
tz = event_date.tzinfo.zone
date = parse(nc_value)
- if od_event and od_event.nextcloud_event_timezone and od_event.nextcloud_event_timezone == tz:
+ if (
+ od_event
+ and od_event.nextcloud_event_timezone
+ and od_event.nextcloud_event_timezone == tz
+ ):
dt_tz = pytz.timezone(tz).localize(date, is_dst=None)
date = dt_tz.astimezone(pytz.utc)
else:
if tz != "UTC":
date = date.astimezone(pytz.utc)
value = nc_field[-1].split("=")[-1]
- if value == 'date':
+ if value == "date":
if nc_field[0].upper() == "DTEND":
date = date - timedelta(days=1)
return date.date()
@@ -914,14 +1594,14 @@ def get_event_datetime(
if key == "DTEND":
date = date - timedelta(days=1)
value = nc_field[-1].split("=")[-1]
- if value == 'date' and isinstance(date, datetime):
+ if value == "date" and isinstance(date, datetime):
date = date.date()
if isinstance(date, datetime):
return date.replace(tzinfo=None)
else:
return date
return nc_value
- except Exception as e:
+ except Exception:
return nc_value
def get_recurrence_id_date(self, nc_field, nc_value, od_event_id):
@@ -978,20 +1658,26 @@ def manage_recurring_instance(self, event_dict, operation, vals):
recurring_event_ids = event_id.recurrence_id.calendar_event_ids
if exdates and operation == "create":
# Check for detached events in Odoo
- detach_ids = recurring_event_ids.filtered(lambda x: x.nc_detach and x.nc_rid)
+ detach_ids = recurring_event_ids.filtered(
+ lambda x: x.nc_detach and x.nc_rid
+ )
if detach_ids:
detach_exdates = [parse(x.nc_rid) for x in detach_ids]
[exdates.append(d) for d in detach_exdates if d not in exdates]
vals["exdate"] = exdates
if operation == "delete":
# Check if all instance of recurring events are for deletion
- to_delete_ids = recurring_event_ids.filtered(lambda x: x.nc_to_delete and x.nc_rid)
+ to_delete_ids = recurring_event_ids.filtered(
+ lambda x: x.nc_to_delete and x.nc_rid
+ )
if not to_delete_ids or len(to_delete_ids.ids) == len(
- event_id.recurrence_id.calendar_event_ids.ids
+ event_id.recurrence_id.calendar_event_ids.ids
):
return event_id, operation, vals
else:
- exdates.extend([parse(x.nc_rid) for x in to_delete_ids if x.nc_rid not in exdates])
+ exdates.extend(
+ [parse(x.nc_rid) for x in to_delete_ids if x.nc_rid not in exdates]
+ )
# Handle write operation by marking the existing caldav_event with exdate
# then create a new caldav_event that is detached from recurring rule
@@ -1004,22 +1690,38 @@ def manage_recurring_instance(self, event_dict, operation, vals):
x.strftime(date_format) for x in exdates
]
operation = "create"
- if operation == "write" and not event_id.nc_detach and event_id.recurrence_id.base_event_id == event_id:
+ if (
+ operation == "write"
+ and not event_id.nc_detach
+ and event_id.recurrence_id.base_event_id == event_id
+ ):
operation = "create"
# Set the exdates value in the caldav_event
if exdates and caldav_event:
for index, value in enumerate(exdates):
- if isinstance(value,datetime):
+ if isinstance(value, datetime):
if not value.tzinfo:
- dt_tz = pytz.timezone(event_id.nextcloud_event_timezone or event_id.event_tz or 'UTC').localize(value, is_dst=None)
+ dt_tz = pytz.timezone(
+ event_id.nextcloud_event_timezone
+ or event_id.event_tz
+ or "UTC"
+ ).localize(value, is_dst=None)
else:
dt_tz = value
exdates[index] = dt_tz.date() if event_id.allday else dt_tz
exdates = list(set(exdates))
parameters = {"VALUE": "DATE" if event_id.allday else "DATE-TIME"}
if not event_id.allday:
- parameters.update({"TZID": event_id.nextcloud_event_timezone or event_id.event_tz or 'UTC'})
- caldav_event.icalendar_component.add("exdate", exdates,parameters=parameters)
+ parameters.update(
+ {
+ "TZID": event_id.nextcloud_event_timezone
+ or event_id.event_tz
+ or "UTC"
+ }
+ )
+ caldav_event.icalendar_component.add(
+ "exdate", exdates, parameters=parameters
+ )
caldav_event.save()
return event_id, operation, vals
@@ -1065,14 +1767,20 @@ def update_event_hash(self, hash_vals, event_ids=False):
elif not event_ids and "principal" in hash_vals:
events_hash = hash_vals["events"]
principal = hash_vals["principal"]
- sync_user_id = self.env['nc.sync.user'].browse(hash_vals["nc_sync_user_id"])
+ sync_user_id = self.env["nc.sync.user"].browse(hash_vals["nc_sync_user_id"])
calendars = principal.calendars()
all_user_events = []
for calendar in calendars:
- if calendar.canonical_url not in sync_user_id.nc_calendar_ids.mapped(
- 'calendar_url') and not calendar.canonical_url == sync_user_id.nc_calendar_id.calendar_url:
+ if (
+ calendar.canonical_url
+ not in sync_user_id.nc_calendar_ids.mapped("calendar_url")
+ and not calendar.canonical_url
+ == sync_user_id.nc_calendar_id.calendar_url
+ ):
continue
- start_date = datetime.combine(sync_user_id.start_date or dtdate.today(), datetime.min.time())
+ start_date = datetime.combine(
+ sync_user_id.start_date or dtdate.today(), datetime.min.time()
+ )
events = calendar.search(
start=start_date,
event=True,
@@ -1144,7 +1852,7 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
calendar_event = self.env["calendar.event"].sudo()
calendar_recurrence_obj = self.env["calendar.recurrence"].sudo()
field_mapping = self.get_caldav_fields()
- date_fields = ['dtstart', 'dtend', 'rrule', 'recurrence-id', 'last-modified']
+ date_fields = ["dtstart", "dtend", "rrule", "recurrence-id", "last-modified"]
log_obj = params["log_obj"]
user_id = sync_user_id.user_id
user_name = sync_user_id.user_id.name
@@ -1155,19 +1863,25 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
for event in od_events_dict[operation]:
od_event_id = event["od_event"] if "od_event" in event else False
new_event_id = False
- detach = event.get('detach',False)
+ detach = event.get("detach", False)
if od_event_id and not od_event_id.exists():
continue
# Perform delete operation
try:
- if operation == "delete" and "od_event" in event and event["od_event"]:
+ if (
+ operation == "delete"
+ and "od_event" in event
+ and event["od_event"]
+ ):
all_odoo_event_ids = all_odoo_event_ids - event["od_event"]
- event["od_event"].sudo().with_context(force_delete=True).unlink()
+ event["od_event"].sudo().with_context(
+ force_delete=True
+ ).unlink()
params["delete_count"] += len(event["od_event"])
except Exception as e:
- message = (
- "Error deleting Odoo event '%s' for user '%s':\n"
- % (event["od_event"], user_name)
+ message = "Error deleting Odoo event '%s' for user '%s':\n" % (
+ event["od_event"],
+ user_name,
)
log_obj.log_event(
mode="error",
@@ -1185,7 +1899,7 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
for vevent in nc_event:
if od_event_id and not od_event_id.exists():
continue
- if operation == 'create' and new_event_id:
+ if operation == "create" and new_event_id:
od_event_id = new_event_id
vals = {"nc_uid": event["nc_uid"]}
nc_uid = event["nc_uid"]
@@ -1198,10 +1912,14 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
data = vevent[e]
if field[0] in date_fields:
data = self.get_event_datetime(
- field, vevent[e], vevent, od_event_id, caldav_event
+ field,
+ vevent[e],
+ vevent,
+ od_event_id,
+ caldav_event,
)
if field[0] == "dtstart" and not isinstance(
- data, datetime
+ data, datetime
):
all_day = True
if field[0] == "transp":
@@ -1250,26 +1968,34 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
data = data.strftime("%Y%m%d")
if data:
vals[field_mapping[field[0]]] = data
- if caldav_event.icalendar_component.get('DTSTART'):
- event_start_date = caldav_event.icalendar_component.get('DTSTART').dt
+ if caldav_event.icalendar_component.get("DTSTART"):
+ event_start_date = caldav_event.icalendar_component.get(
+ "DTSTART"
+ ).dt
tz = False
if isinstance(event_start_date, datetime):
tz = event_start_date.tzinfo.zone
if tz:
- vals['nextcloud_event_timezone'] = tz
- if caldav_event.icalendar_component.get('X-NEXTCLOUD-BC-FIELD-TYPE'):
- vals['nextcloud_calendar_type'] = caldav_event.icalendar_component.get('X-NEXTCLOUD-BC-FIELD-TYPE')
+ vals["nextcloud_event_timezone"] = tz
+ if caldav_event.icalendar_component.get(
+ "X-NEXTCLOUD-BC-FIELD-TYPE"
+ ):
+ vals[
+ "nextcloud_calendar_type"
+ ] = caldav_event.icalendar_component.get(
+ "X-NEXTCLOUD-BC-FIELD-TYPE"
+ )
if all_day:
vals["start_date"] = vals.pop("start")
vals["stop_date"] = vals.pop("stop")
if detach:
- vals['recurrence_id'] = False
- vals['recurrency'] = False
+ vals["recurrence_id"] = False
+ vals["recurrency"] = False
# Populate the nc_calendar_ids field in Odoo
nc_calendar_id = all_nc_calendar_ids.filtered(
lambda x: x.calendar_url
- == caldav_event.parent.canonical_url
- and x.user_id == user_id
+ == caldav_event.parent.canonical_url
+ and x.user_id == user_id
)
if all_odoo_event_ids:
event_nc_calendar_ids = all_odoo_event_ids.filtered(
@@ -1286,8 +2012,8 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
new_nc_calendar_ids = []
if nc_calendar_id:
new_nc_calendar_ids.append(nc_calendar_id.id)
- if vals.get('rrule',False):
- vals['nextcloud_rrule'] = vals.get('rrule')
+ if vals.get("rrule", False):
+ vals["nextcloud_rrule"] = vals.get("rrule")
# clear categ_ids when not present
if "categ_ids" not in vals:
vals["categ_ids"] = [(6, 0, [])]
@@ -1301,30 +2027,42 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
event_name = vals.get("name", "Untitled event")
vals.pop("write_date", False)
(
- attendee_partner_ids, organizer,
- params
+ attendee_partner_ids,
+ organizer,
+ params,
) = self.get_event_attendees(caldav_event, user_id, **params)
organizer_user_id = organizer[0].id if organizer else False
- hash_vals_list = [{
- "nc_sync_user_id": sync_user_id.id,
- "nc_event_hash": event_hash,
- }]
+ hash_vals_list = [
+ {
+ "nc_sync_user_id": sync_user_id.id,
+ "nc_event_hash": event_hash,
+ }
+ ]
if organizer_user_id:
nc_sync_user_id = self.env["nc.sync.user"].search(
- [("user_id", "=", organizer_user_id), ("sync_calendar", "=", True)], limit=1
+ [
+ ("user_id", "=", organizer_user_id),
+ ("sync_calendar", "=", True),
+ ],
+ limit=1,
)
if nc_sync_user_id != sync_user_id:
- nc_user_event_hash, nc_sync_user_calendar_id = (
- nc_sync_user_id.get_nc_event_hash_by_uid_for_other_user(
- nc_uid
- )
+ (
+ nc_user_event_hash,
+ nc_sync_user_calendar_id,
+ ) = nc_sync_user_id.get_nc_event_hash_by_uid_for_other_user(
+ nc_uid
+ )
+ hash_vals_list.append(
+ {
+ "nc_sync_user_id": nc_sync_user_id.id,
+ "nc_event_hash": nc_user_event_hash,
+ }
)
- hash_vals_list.append({
- "nc_sync_user_id": nc_sync_user_id.id,
- "nc_event_hash": nc_user_event_hash,
- })
if nc_sync_user_calendar_id:
- new_nc_calendar_ids.append(nc_sync_user_calendar_id.id)
+ new_nc_calendar_ids.append(
+ nc_sync_user_calendar_id.id
+ )
vals["nc_calendar_ids"] = [(6, 0, new_nc_calendar_ids)]
vals.update(
{
@@ -1332,7 +2070,7 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
"allday": all_day,
"nc_allday": all_day,
"nc_synced": True,
- "user_id": organizer_user_id
+ "user_id": organizer_user_id,
}
)
# Perform create operation
@@ -1343,10 +2081,12 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
if "nc_rid" in vals and nc_uid:
recurring_event_id = all_odoo_event_ids.filtered(
lambda x: x.nc_uid == nc_uid
- and x.nc_rid == vals["nc_rid"]
+ and x.nc_rid == vals["nc_rid"]
)
if recurring_event_id:
- recurring_event_id.with_context(sync_from_nextcloud=True).write(vals)
+ recurring_event_id.with_context(
+ sync_from_nextcloud=True
+ ).write(vals)
self.update_attendee_invite(recurring_event_id)
for hash_vals in hash_vals_list:
self.update_event_hash(
@@ -1362,14 +2102,21 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
for hash_vals in hash_vals_list:
nc_hash_ids.append((0, 0, hash_vals))
vals["nc_hash_ids"] = nc_hash_ids
- context_dict = {'sync_from_nextcloud':True}
- if caldav_event.icalendar_component.get('RELATED-TO'):
- if 'until' in vals.get('nextcloud_rrule','').lower():
- context_dict.update({'update_until': True})
- new_event_id = calendar_event.with_context(context_dict).create(vals)
+ context_dict = {"sync_from_nextcloud": True}
+ if caldav_event.icalendar_component.get(
+ "RELATED-TO"
+ ):
+ if (
+ "until"
+ in vals.get("nextcloud_rrule", "").lower()
+ ):
+ context_dict.update({"update_until": True})
+ new_event_id = calendar_event.with_context(
+ context_dict
+ ).create(vals)
if (
- new_event_id.recurrence_id
- and new_event_id.recurrence_id.calendar_event_ids
+ new_event_id.recurrence_id
+ and new_event_id.recurrence_id.calendar_event_ids
):
recurring_event_ids = (
new_event_id.recurrence_id.calendar_event_ids
@@ -1397,8 +2144,8 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
params["create_count"] += 1
except Exception as e:
message = (
- "Error creating Odoo event '%s' for user '%s':\n"
- % (event_name, user_name)
+ "Error creating Odoo event '%s' for user '%s':\n"
+ % (event_name, user_name)
)
log_obj.log_event(
mode="error",
@@ -1415,76 +2162,202 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
# rrule but no nc_rid
if "rrule" in vals and "nc_rid" not in vals:
if od_event_id.exists():
- if od_event_id.recurrence_id.base_event_id == od_event_id:
- update = self.check_recurrent_event_vals(od_event_id,vals)
+ if (
+ od_event_id.recurrence_id.base_event_id
+ == od_event_id
+ ):
+ update = self.check_recurrent_event_vals(
+ od_event_id, vals
+ )
if update:
recurrence_vals = vals
- (od_event_id.recurrence_id.calendar_event_ids - od_event_id.recurrence_id.base_event_id).write(
- {'nc_uid': False})
- recurrence_vals.update({'recurrence_update': 'all_events'})
- recurring_events = od_event_id.recurrence_id.calendar_event_ids
- context_dict = {'sync_from_nextcloud':True}
- if (vals.get('nextcloud_rrule',
- False) and od_event_id.nextcloud_rrule != vals.get(
- 'nextcloud_rrule')) or (od_event_id.allday and ((
- vals.get('start_date', False) and vals.get(
- 'start_date') == od_event_id.start_date) or (vals.get('stop_date',
- False) and vals.get(
- 'stop_date') == od_event_id.stop_date))) or (
- not od_event_id.allday and ((
- vals.get('start', False) and vals.get(
- 'start') == od_event_id.start) or (
- vals.get('stop', False) and vals.get(
- 'stop') == od_event_id.stop))):
- recurrence_vals.update(
- {'nextcloud_rrule': vals['nextcloud_rrule']})
- recurrence_vals.update(
- {'rrule': vals['nextcloud_rrule']})
- recurrence_vals.update(calendar_recurrence_obj._rrule_parse(
- vals['nextcloud_rrule'], vals.get('start', od_event_id.start)))
- if (vals.get('nextcloud_rrule',
- False) and od_event_id.nextcloud_rrule != vals.get(
- 'nextcloud_rrule')) and 'until' in vals['nextcloud_rrule'].lower():
- context_dict.update({'update_until':True})
- context_dict.update({'update_nc_rid':True})
+ (
+ od_event_id.recurrence_id.calendar_event_ids
+ - od_event_id.recurrence_id.base_event_id
+ ).write({"nc_uid": False})
+ recurrence_vals.update(
+ {"recurrence_update": "all_events"}
+ )
+ recurring_events = (
+ od_event_id.recurrence_id.calendar_event_ids
+ )
+ context_dict = {
+ "sync_from_nextcloud": True
+ }
+ if (
+ (
+ vals.get(
+ "nextcloud_rrule", False
+ )
+ and od_event_id.nextcloud_rrule
+ != vals.get("nextcloud_rrule")
+ )
+ or (
+ od_event_id.allday
+ and (
+ (
+ vals.get(
+ "start_date", False
+ )
+ and vals.get(
+ "start_date"
+ )
+ == od_event_id.start_date
+ )
+ or (
+ vals.get(
+ "stop_date", False
+ )
+ and vals.get(
+ "stop_date"
+ )
+ == od_event_id.stop_date
+ )
+ )
+ )
+ or (
+ not od_event_id.allday
+ and (
+ (
+ vals.get("start", False)
+ and vals.get("start")
+ == od_event_id.start
+ )
+ or (
+ vals.get("stop", False)
+ and vals.get("stop")
+ == od_event_id.stop
+ )
+ )
+ )
+ ):
+ recurrence_vals.update(
+ {
+ "nextcloud_rrule": vals[
+ "nextcloud_rrule"
+ ]
+ }
+ )
+ recurrence_vals.update(
+ {
+ "rrule": vals[
+ "nextcloud_rrule"
+ ]
+ }
+ )
+ recurrence_vals.update(
+ calendar_recurrence_obj._rrule_parse(
+ vals["nextcloud_rrule"],
+ vals.get(
+ "start",
+ od_event_id.start,
+ ),
+ )
+ )
+ if (
+ vals.get(
+ "nextcloud_rrule", False
+ )
+ and od_event_id.nextcloud_rrule
+ != vals.get("nextcloud_rrule")
+ ) and "until" in vals[
+ "nextcloud_rrule"
+ ].lower():
+ context_dict.update(
+ {"update_until": True}
+ )
+ context_dict.update(
+ {"update_nc_rid": True}
+ )
else:
- recurrence_vals.pop('rrule',None)
- recurrence_vals.pop('nextcloud_rrule',None)
- od_event_id.recurrence_id.base_event_id.with_context(context_dict).write(
- recurrence_vals)
- new_recurring_events = od_event_id.recurrence_id.calendar_event_ids
+ recurrence_vals.pop("rrule", None)
+ recurrence_vals.pop(
+ "nextcloud_rrule", None
+ )
+ od_event_id.recurrence_id.base_event_id.with_context(
+ context_dict
+ ).write(
+ recurrence_vals
+ )
+ new_recurring_events = (
+ od_event_id.recurrence_id.calendar_event_ids
+ )
if not od_event_id.active:
- new_recurrence = calendar_recurrence_obj.search([('base_event_id','=',od_event_id.id)],limit=1)
+ new_recurrence = (
+ calendar_recurrence_obj.search(
+ [
+ (
+ "base_event_id",
+ "=",
+ od_event_id.id,
+ )
+ ],
+ limit=1,
+ )
+ )
if new_recurrence:
new_recurring_events = new_recurrence.calendar_event_ids.sorted(
key=lambda r: r.start
)
if new_recurring_events:
- all_odoo_event_ids = all_odoo_event_ids - od_event_id
- recurring_events = recurring_events - od_event_id
- od_event_id.with_context(force_delete=True).unlink()
- new_recurrence.base_event_id = new_recurring_events[0].id
- od_event_id = new_recurring_events[0]
- all_odoo_event_ids = self.update_recurring_events_in_all_events(new_recurring_events,recurring_events,all_odoo_event_ids)
- if context_dict.get('update_nc_rid'):
+ all_odoo_event_ids = (
+ all_odoo_event_ids
+ - od_event_id
+ )
+ recurring_events = (
+ recurring_events
+ - od_event_id
+ )
+ od_event_id.with_context(
+ force_delete=True
+ ).unlink()
+ new_recurrence.base_event_id = new_recurring_events[
+ 0
+ ].id
+ od_event_id = (
+ new_recurring_events[0]
+ )
+ all_odoo_event_ids = self.update_recurring_events_in_all_events(
+ new_recurring_events,
+ recurring_events,
+ all_odoo_event_ids,
+ )
+ if context_dict.get("update_nc_rid"):
if not od_event_id.allday:
start = od_event_id.start
- tz = od_event_id.nextcloud_event_timezone
+ tz = (
+ od_event_id.nextcloud_event_timezone
+ )
if tz:
- dt_tz = start.replace(tzinfo=pytz.utc)
+ dt_tz = start.replace(
+ tzinfo=pytz.utc
+ )
start = dt_tz.astimezone(
- pytz.timezone(tz))
- od_event_id.nc_rid = start.strftime("%Y%m%dT%H%M%S")
+ pytz.timezone(tz)
+ )
+ od_event_id.nc_rid = (
+ start.strftime(
+ "%Y%m%dT%H%M%S"
+ )
+ )
else:
- od_event_id.nc_rid = od_event_id.nc_rid
+ od_event_id.nc_rid = (
+ od_event_id.nc_rid
+ )
else:
- od_event_id.nc_rid = od_event_id.start.strftime("%Y%m%d")
+ od_event_id.nc_rid = (
+ od_event_id.start.strftime(
+ "%Y%m%d"
+ )
+ )
for hash_vals in hash_vals_list:
self.update_event_hash(
hash_vals, new_recurring_events
)
for hash_vals in hash_vals_list:
- self.update_event_hash(hash_vals, od_event_id)
+ self.update_event_hash(
+ hash_vals, od_event_id
+ )
all_odoo_event_ids = self.delete_exempted_event(
od_event_id, exdates, all_odoo_event_ids
)
@@ -1493,10 +2366,10 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
continue
# Check if the event is part of recurring event
elif (
- "rrule" not in vals
- and "nc_rid" in vals
- and od_event_id
- and od_event_id.recurrence_id
+ "rrule" not in vals
+ and "nc_rid" in vals
+ and od_event_id
+ and od_event_id.recurrence_id
):
recurring_event_ids = (
od_event_id.recurrence_id.calendar_event_ids
@@ -1508,8 +2381,10 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
continue
else:
od_event_id = recurring_event_id
- vals.pop('nextcloud_event_timezone',None)
- od_event_id.with_context(sync_from_nextcloud=True).write(vals)
+ vals.pop("nextcloud_event_timezone", None)
+ od_event_id.with_context(
+ sync_from_nextcloud=True
+ ).write(vals)
# # Update the hash value of the Odoo event that
# # corresponds to the current user_id
# if od_event_id.recurrence_id:
@@ -1528,8 +2403,8 @@ def update_odoo_events(self, sync_user_id, od_events_dict, **params):
params["write_count"] += 1
except Exception as e:
message = (
- "Error updating Odoo event '%s' for user '%s':\n"
- % (event_name, user_name)
+ "Error updating Odoo event '%s' for user '%s':\n"
+ % (event_name, user_name)
)
log_obj.log_event(
mode="error",
@@ -1576,8 +2451,8 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
vevent = caldav_event.vobject_instance.vevent
if event_id.recurrence_id:
if (
- event_id.recurrence_id in recurrent_rule_ids[operation]
- and not event_id.nc_detach
+ event_id.recurrence_id in recurrent_rule_ids[operation]
+ and not event_id.nc_detach
):
continue
else:
@@ -1596,9 +2471,9 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
# to Odoo fields with values
for field in field_mapping:
if (
- field not in fields
- or not event_id[field]
- or field in ["id", "write_date", "nc_rid"]
+ field not in fields
+ or not event_id[field]
+ or field in ["id", "write_date", "nc_rid"]
):
continue
value = event_id[field]
@@ -1610,7 +2485,10 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
}
vals[field_mapping[field]] = start_stop[field]
else:
- user_tz = event_id.nextcloud_event_timezone or sync_user_id.user_id.tz
+ user_tz = (
+ event_id.nextcloud_event_timezone
+ or sync_user_id.user_id.tz
+ )
vals[field_mapping[field]] = self.convert_date(
value, user_tz, "local"
)
@@ -1641,8 +2519,8 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
if value not in recurrent_rule_ids[operation]:
recurrent_rule_ids[operation].append(value)
rrule = self.get_rrule_dict(value._rrule_serialize())
- if rrule.get('UNTIL'):
- rrule.update({'UNTIL':parse(rrule.get('UNTIL'))})
+ if rrule.get("UNTIL"):
+ rrule.update({"UNTIL": parse(rrule.get("UNTIL"))})
vals[field_mapping[field]] = rrule
else:
vals[field_mapping[field]] = value
@@ -1664,9 +2542,12 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
event_id, operation, vals = self.manage_recurring_instance(
event, operation, vals
)
- if operation == 'null':
+ if operation == "null":
params["create_count"] += len(
- event_id.recurrence_id.calendar_event_ids.filtered(lambda x: x.nc_to_delete))
+ event_id.recurrence_id.calendar_event_ids.filtered(
+ lambda x: x.nc_to_delete
+ )
+ )
operation = current_operation
continue
elif not event_id.recurrence_id and operation != prev_operation:
@@ -1706,8 +2587,8 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
)
except Exception as e:
message = (
- "Error creating Nextcloud event '%s' for user '%s':\n"
- % (event_name, user_name)
+ "Error creating Nextcloud event '%s' for user '%s':\n"
+ % (event_name, user_name)
)
log_obj.log_event(
mode="error",
@@ -1785,8 +2666,8 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
params["write_count"] += 1
except Exception as e:
message = (
- "Error updating Nextcloud event '%s' for user '%s':\n"
- % (event_name, user_name)
+ "Error updating Nextcloud event '%s' for user '%s':\n"
+ % (event_name, user_name)
)
log_obj.log_event(
mode="error",
@@ -1804,7 +2685,7 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
vevent.uid.value
)
# Update the Odoo event record
- res = {'nc_synced': True}
+ res = {"nc_synced": True}
if "nc_hash_ids" not in res:
res["nc_hash_ids"] = []
event_nchash_id = event_id.nc_hash_ids.filtered(
@@ -1847,59 +2728,105 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
if event_id.recurrence_id:
hash_updated = False
- if not event_id.nc_detach and event_id.recurrence_id.base_event_id == event_id:
+ if (
+ not event_id.nc_detach
+ and event_id.recurrence_id.base_event_id == event_id
+ ):
if caldav_event.icalendar_component.get(
- 'DTSTART') and caldav_event.icalendar_component.get('RRULE'):
- event_start_date = caldav_event.icalendar_component.get('DTSTART').dt
+ "DTSTART"
+ ) and caldav_event.icalendar_component.get("RRULE"):
+ event_start_date = (
+ caldav_event.icalendar_component.get(
+ "DTSTART"
+ ).dt
+ )
tz = False
if isinstance(event_start_date, datetime):
tz = event_start_date.tzinfo.zone
if tz:
if event_id.nextcloud_event_timezone != tz:
- res['nextcloud_event_timezone'] = tz
- res['nextcloud_rrule'] = vevent.contents.get('rrule',False) and vevent.rrule.value or event_id.nextcloud_rrule or event_id.rrule
- event_id.recurrence_id.calendar_event_ids.with_context(sync_from_nextcloud=True).write(res)
- event_id.recurrence_id.calendar_event_ids.filtered(lambda x:not x.nc_hash_ids).write({'nc_hash_ids':[(
- 0 ,
- 0 ,
+ res["nextcloud_event_timezone"] = tz
+ res["nextcloud_rrule"] = (
+ vevent.contents.get("rrule", False)
+ and vevent.rrule.value
+ or event_id.nextcloud_rrule
+ or event_id.rrule
+ )
+ event_id.recurrence_id.calendar_event_ids.with_context(
+ sync_from_nextcloud=True
+ ).write(res)
+ event_id.recurrence_id.calendar_event_ids.filtered(
+ lambda x: not x.nc_hash_ids
+ ).write(
{
- "nc_sync_user_id": sync_user_id.id,
- "nc_event_hash": event_hash,
- },
- )]})
- event_id.recurrence_id.calendar_event_ids.filtered(lambda x:not x.nc_uid).write({"nc_uid": vevent.uid.value})
+ "nc_hash_ids": [
+ (
+ 0,
+ 0,
+ {
+ "nc_sync_user_id": sync_user_id.id,
+ "nc_event_hash": event_hash,
+ },
+ )
+ ]
+ }
+ )
+ event_id.recurrence_id.calendar_event_ids.filtered(
+ lambda x: not x.nc_uid
+ ).write({"nc_uid": vevent.uid.value})
hash_updated = True
if not hash_updated:
- event_vals = {"nc_uid": vevent.uid.value,"nc_hash_ids":res['nc_hash_ids']}
+ event_vals = {
+ "nc_uid": vevent.uid.value,
+ "nc_hash_ids": res["nc_hash_ids"],
+ }
else:
event_vals = {}
if event_id.nc_detach:
- if event_id.recurrence_id and event_id.recurrence_id.base_event_id == event_id:
- new_base_event = (event_id.recurrence_id.calendar_event_ids - event_id).sorted(
- key=lambda r: r.start
- )
+ if (
+ event_id.recurrence_id
+ and event_id.recurrence_id.base_event_id == event_id
+ ):
+ new_base_event = (
+ event_id.recurrence_id.calendar_event_ids
+ - event_id
+ ).sorted(key=lambda r: r.start)
if new_base_event:
- event_id.recurrence_id.base_event_id = new_base_event[0].id
- event_vals.update({
+ event_id.recurrence_id.base_event_id = (
+ new_base_event[0].id
+ )
+ event_vals.update(
+ {
"recurrence_id": False,
"recurrency": False,
"nc_detach": False,
- })
- event_id.with_context(sync_from_nextcloud=True).write(event_vals)
+ }
+ )
+ event_id.with_context(sync_from_nextcloud=True).write(
+ event_vals
+ )
else:
res.update({"nc_uid": vevent.uid.value})
if event_id.nc_detach:
- if event_id.recurrence_id and event_id.recurrence_id.base_event_id == event_id:
- new_base_event = (event_id.recurrence_id.calendar_event_ids - event_id).sorted(
- key=lambda r: r.start
- )
+ if (
+ event_id.recurrence_id
+ and event_id.recurrence_id.base_event_id == event_id
+ ):
+ new_base_event = (
+ event_id.recurrence_id.calendar_event_ids
+ - event_id
+ ).sorted(key=lambda r: r.start)
if new_base_event:
- event_id.recurrence_id.base_event_id = new_base_event[0].id
- res.update({
+ event_id.recurrence_id.base_event_id = (
+ new_base_event[0].id
+ )
+ res.update(
+ {
"recurrence_id": False,
"recurrency": False,
"nc_detach": False,
- })
+ }
+ )
event_id.with_context(sync_from_nextcloud=True).write(res)
# Commit the changes to the database since it is
# already been updated in Nextcloud
@@ -1935,7 +2862,9 @@ def update_nextcloud_events(self, sync_user_id, nc_events_dict, **params):
].filtered(lambda x: x not in to_delete_event_ids)
if not (len(all_events_with_nc_uid) - len(to_delete_event_ids)):
caldav_event.delete()
- to_delete_event_ids.sudo().with_context(force_delete=True).unlink()
+ to_delete_event_ids.sudo().with_context(
+ force_delete=True
+ ).unlink()
# Commit the changes to the database since it is
# already been deleted in Nextcloud
self.env.cr.commit()
@@ -1973,7 +2902,9 @@ def sync_cron(self):
[("user_id", "!=", False)]
),
"all_user_ids": self.env["res.users"].search([]),
- "all_sync_user_ids": self.env["nc.sync.user"].search([("sync_calendar", "=", True)]),
+ "all_sync_user_ids": self.env["nc.sync.user"].search(
+ [("sync_calendar", "=", True)]
+ ),
"all_partner_ids": self.env["res.partner"].search([("email", "!=", False)]),
"all_odoo_event_type_ids": self.env["calendar.event.type"].search([]),
"status_vals": {
@@ -2003,9 +2934,12 @@ def sync_cron(self):
# Get all events from Odoo and Nextcloud
# log_obj.log_event(message="Getting events for '%s'" % user.user_id.name)
if per_user_id:
- start_date = datetime.combine(user.start_date or dtdate.today(), datetime.min.time())
- params["all_odoo_event_ids"] = calendar_event_obj.search([('start', '>=', start_date)],
- order="start")
+ start_date = datetime.combine(
+ user.start_date or dtdate.today(), datetime.min.time()
+ )
+ params["all_odoo_event_ids"] = calendar_event_obj.search(
+ [("start", ">=", start_date)], order="start"
+ )
events_dict = user.get_all_user_events(**params)
od_events = events_dict["od_events"]
nc_events = events_dict["nc_events"]
@@ -2115,8 +3049,8 @@ def check_nextcloud_connection(self, url, username, password):
"response_description": str(e),
}
except (
- caldav.lib.error.PropfindError,
- requests.exceptions.ConnectionError,
+ caldav.lib.error.PropfindError,
+ requests.exceptions.ConnectionError,
) as e:
_logger.warning("Error: %s" % e)
return client, {
@@ -2232,7 +3166,9 @@ def convert_date(self, dt, tz, mode):
dt_conv = dt_tz.astimezone(pytz.timezone(tz))
return dt_conv
- def update_recurring_events_in_all_events(self,new_recurring_events,recurring_events,all_odoo_event_ids):
+ def update_recurring_events_in_all_events(
+ self, new_recurring_events, recurring_events, all_odoo_event_ids
+ ):
for event in recurring_events:
if not event.exists():
all_odoo_event_ids = all_odoo_event_ids - event
@@ -2243,23 +3179,35 @@ def update_recurring_events_in_all_events(self,new_recurring_events,recurring_ev
def check_recurrent_event_vals(self, od_event_id, vals):
update = False
- if vals.get('nextcloud_rrule', '') != od_event_id.nextcloud_rrule or vals.get('name',
- '') != od_event_id.name or vals.get(
- 'show_as', '') != od_event_id.show_as:
+ if (
+ vals.get("nextcloud_rrule", "") != od_event_id.nextcloud_rrule
+ or vals.get("name", "") != od_event_id.name
+ or vals.get("show_as", "") != od_event_id.show_as
+ ):
update = True
- elif (vals.get('description', False) and vals.get('description') != od_event_id.description) or (
- vals.get('location', False) and vals.get('location') != od_event_id.location):
+ elif (
+ vals.get("description", False)
+ and vals.get("description") != od_event_id.description
+ ) or (
+ vals.get("location", False) and vals.get("location") != od_event_id.location
+ ):
update = True
if od_event_id.allday:
- if (vals.get('start_date', False) and vals.get('start_date') != od_event_id.start_date) or (
- vals.get('stop_date', False) and vals.get('stop_date') != od_event_id.stop_date):
+ if (
+ vals.get("start_date", False)
+ and vals.get("start_date") != od_event_id.start_date
+ ) or (
+ vals.get("stop_date", False)
+ and vals.get("stop_date") != od_event_id.stop_date
+ ):
update = True
else:
- if (vals.get('start', False) and vals.get('start') != od_event_id.start) or (
- vals.get('stop', False) and vals.get('stop') != od_event_id.stop):
+ if (
+ vals.get("start", False) and vals.get("start") != od_event_id.start
+ ) or (vals.get("stop", False) and vals.get("stop") != od_event_id.stop):
update = True
- if vals.get('alarm_ids', []):
- new_vals = vals.get('alarm_ids')[0][2]
+ if vals.get("alarm_ids", []):
+ new_vals = vals.get("alarm_ids")[0][2]
alarm_ids = od_event_id.alarm_ids.ids
if len(alarm_ids) != len(new_vals):
update = True
@@ -2267,8 +3215,8 @@ def check_recurrent_event_vals(self, od_event_id, vals):
if rec not in alarm_ids:
update = True
break
- if vals.get('categ_ids', []):
- new_vals = vals.get('categ_ids')[0][2]
+ if vals.get("categ_ids", []):
+ new_vals = vals.get("categ_ids")[0][2]
categ_ids = od_event_id.categ_ids.ids
if len(categ_ids) != len(new_vals):
update = True
@@ -2276,8 +3224,8 @@ def check_recurrent_event_vals(self, od_event_id, vals):
if rec not in categ_ids:
update = True
break
- if vals.get('partner_ids', []):
- new_vals = vals.get('partner_ids')[0][2]
+ if vals.get("partner_ids", []):
+ new_vals = vals.get("partner_ids")[0][2]
partner_ids = od_event_id.partner_ids.ids
if len(partner_ids) != len(new_vals):
update = True
diff --git a/nextcloud_odoo_sync/models/res_config_settings.py b/nextcloud_odoo_sync/models/res_config_settings.py
index 6cf15d2..ce3dc6c 100644
--- a/nextcloud_odoo_sync/models/res_config_settings.py
+++ b/nextcloud_odoo_sync/models/res_config_settings.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import fields, models, api
+from odoo import fields, models
class ResConfigSettings(models.TransientModel):
@@ -32,4 +32,3 @@ class ResConfigSettings(models.TransientModel):
default=10,
config_parameter="nextcloud_odoo_sync.yearly_recurring_events_limit",
)
-
diff --git a/nextcloud_odoo_sync/models/res_partner.py b/nextcloud_odoo_sync/models/res_partner.py
index 35113e3..05a0cb1 100644
--- a/nextcloud_odoo_sync/models/res_partner.py
+++ b/nextcloud_odoo_sync/models/res_partner.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields
+from odoo import fields, models
class ResPartner(models.Model):
diff --git a/nextcloud_odoo_sync/models/res_users.py b/nextcloud_odoo_sync/models/res_users.py
index 858b675..5db119f 100644
--- a/nextcloud_odoo_sync/models/res_users.py
+++ b/nextcloud_odoo_sync/models/res_users.py
@@ -1,7 +1,7 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo import models, fields
+from odoo import fields, models
from odoo.exceptions import UserError
@@ -27,7 +27,9 @@ def setup_nc_sync_user(self):
return action
def sync_user_events(self):
- sync_users = self.env["nc.sync.user"].search([("user_id", "=", self.id)],limit=1)
+ sync_users = self.env["nc.sync.user"].search(
+ [("user_id", "=", self.id)], limit=1
+ )
if not sync_users:
raise UserError("Sync User not found")
elif sync_users and not sync_users.sync_calendar:
diff --git a/nextcloud_odoo_sync/security/ir_rule.xml b/nextcloud_odoo_sync/security/ir_rule.xml
index 775bb9c..20d6f78 100644
--- a/nextcloud_odoo_sync/security/ir_rule.xml
+++ b/nextcloud_odoo_sync/security/ir_rule.xml
@@ -8,4 +8,4 @@
-
\ No newline at end of file
+
diff --git a/nextcloud_odoo_sync/tests/common.py b/nextcloud_odoo_sync/tests/common.py
index 45f3437..e389727 100644
--- a/nextcloud_odoo_sync/tests/common.py
+++ b/nextcloud_odoo_sync/tests/common.py
@@ -1,11 +1,13 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
-from odoo.tests import common
-from vcr import VCR
-from datetime import datetime, timedelta
-from os.path import dirname, join
import logging
+from datetime import datetime
+from os.path import dirname, join
+
+from vcr import VCR
+
+from odoo.tests import common
logging.getLogger("vcr").setLevel(logging.WARNING)
@@ -24,21 +26,31 @@ def setUp(self):
# prepare users
self.organizer_user = self.env["res.users"].search([("name", "=", "test")])
if not self.organizer_user:
- partner = self.env['res.partner'].create({'name': 'test', 'email': 'test@example.com'})
- self.organizer_user = self.env['res.users'].create({
- 'name': 'test',
- 'login': 'test@example.com',
- 'partner_id': partner.id,
- })
+ partner = self.env["res.partner"].create(
+ {"name": "test", "email": "test@example.com"}
+ )
+ self.organizer_user = self.env["res.users"].create(
+ {
+ "name": "test",
+ "login": "test@example.com",
+ "partner_id": partner.id,
+ }
+ )
#
- self.attendee_user = self.env["res.users"].search([("name", "=", "John Attendee")])
+ self.attendee_user = self.env["res.users"].search(
+ [("name", "=", "John Attendee")]
+ )
if not self.attendee_user:
- partner = self.env['res.partner'].create({'name': 'John Attendee', 'email': 'john@attendee.com'})
- self.attendee_user = self.env['res.users'].create({
- 'name': 'John Attendee',
- 'login': 'john@attendee.com',
- 'partner_id': partner.id,
- })
+ partner = self.env["res.partner"].create(
+ {"name": "John Attendee", "email": "john@attendee.com"}
+ )
+ self.attendee_user = self.env["res.users"].create(
+ {
+ "name": "John Attendee",
+ "login": "john@attendee.com",
+ "partner_id": partner.id,
+ }
+ )
# -----------------------------------------------------------------------------------------
# To create Odoo events
@@ -53,21 +65,24 @@ def setUp(self):
"active": True,
"start": self.start_date,
"stop": self.end_date,
- "partner_ids": [(4, self.organizer_user.partner_id.id), (4, self.attendee_user.partner_id.id)],
+ "partner_ids": [
+ (4, self.organizer_user.partner_id.id),
+ (4, self.attendee_user.partner_id.id),
+ ],
}
self.recurrent_event_values = {
- 'name': 'recurring_event',
- 'description': 'a recurring event',
+ "name": "recurring_event",
+ "description": "a recurring event",
"partner_ids": [(4, self.attendee_user.partner_id.id)],
- 'recurrency': True,
- 'follow_recurrence': True,
- 'start': self.start_date.strftime("%Y-%m-%d %H:%M:%S"),
- 'stop': self.end_date.strftime("%Y-%m-%d %H:%M:%S"),
- 'event_tz': 'Europe/Amsterdam',
- 'recurrence_update': 'self_only',
- 'rrule_type': 'daily',
- 'end_type': 'forever',
- 'duration': 1,
+ "recurrency": True,
+ "follow_recurrence": True,
+ "start": self.start_date.strftime("%Y-%m-%d %H:%M:%S"),
+ "stop": self.end_date.strftime("%Y-%m-%d %H:%M:%S"),
+ "event_tz": "Europe/Amsterdam",
+ "recurrence_update": "self_only",
+ "rrule_type": "daily",
+ "end_type": "forever",
+ "duration": 1,
}
def create_events_for_tests(self):
@@ -78,11 +93,17 @@ def create_events_for_tests(self):
# ---- create some events that will be updated during tests -----
# a simple event
- self.simple_event = self.env["calendar.event"].search([("name", "=", "simple_event")])
+ self.simple_event = self.env["calendar.event"].search(
+ [("name", "=", "simple_event")]
+ )
if not self.simple_event:
- self.simple_event = self.env["calendar.event"].with_user(self.organizer_user).create(
- dict(
- self.simple_event_values,
+ self.simple_event = (
+ self.env["calendar.event"]
+ .with_user(self.organizer_user)
+ .create(
+ dict(
+ self.simple_event_values,
+ )
)
)
@@ -95,11 +116,17 @@ def create_events_for_tests(self):
already_created = self.recurrent_base_event
if not already_created:
- self.recurrent_base_event = self.env["calendar.event"].with_user(self.organizer_user).create(
- self.recurrent_event_values
+ self.recurrent_base_event = (
+ self.env["calendar.event"]
+ .with_user(self.organizer_user)
+ .create(self.recurrent_event_values)
)
- self.recurrence = self.env["calendar.recurrence"].search([("base_event_id", "=", self.recurrent_base_event.id)])
- self.recurrent_events = self.recurrence.calendar_event_ids.sorted(key=lambda r: r.start)
+ self.recurrence = self.env["calendar.recurrence"].search(
+ [("base_event_id", "=", self.recurrent_base_event.id)]
+ )
+ self.recurrent_events = self.recurrence.calendar_event_ids.sorted(
+ key=lambda r: r.start
+ )
self.recurrent_events_count = len(self.recurrent_events)
def assert_odoo_event(self, odoo_event, expected_values):
@@ -115,7 +142,9 @@ def assert_odoo_event(self, odoo_event, expected_values):
v = (v.id, v.name) if v else False
if isinstance(v, list):
- self.assertListEqual(sorted(v), sorted(odoo_event_values.get(k)), msg=f"'{k}' mismatch")
+ self.assertListEqual(
+ sorted(v), sorted(odoo_event_values.get(k)), msg=f"'{k}' mismatch"
+ )
else:
self.assertEqual(v, odoo_event_values.get(k), msg=f"'{k}' mismatch")
@@ -130,10 +159,11 @@ def assert_odoo_recurrence(self, odoo_recurrence, expected_values):
self.assertEqual(v, odoo_recurrence_values.get(k), msg=f"'{k}' mismatch")
def assert_dict_equal(self, dict1, dict2):
-
# check missing keys
keys = set(dict1.keys()) ^ set(dict2.keys())
- self.assertFalse(keys, msg="Following keys are not in both dicts: %s" % ", ".join(keys))
+ self.assertFalse(
+ keys, msg="Following keys are not in both dicts: %s" % ", ".join(keys)
+ )
# compare key by key
for k, v in dict1.items():
@@ -148,7 +178,7 @@ def test_successful_connection(self):
client, principal = nextcloud_caldav_obj.check_nextcloud_connection(
url + "/remote.php/dav", username, password
)
- user_data = self.env["nextcloud.base"].get_user(principal.client.username, url,
- username,
- password)
+ user_data = self.env["nextcloud.base"].get_user(
+ principal.client.username, url, username, password
+ )
self.get_user_calendars(principal)
diff --git a/nextcloud_odoo_sync/tests/test_nextcloud_config.py b/nextcloud_odoo_sync/tests/test_nextcloud_config.py
index 158ad3b..014c47a 100644
--- a/nextcloud_odoo_sync/tests/test_nextcloud_config.py
+++ b/nextcloud_odoo_sync/tests/test_nextcloud_config.py
@@ -1,9 +1,11 @@
# Copyright (c) 2023 iScale Solutions Inc.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
+from unittest.mock import MagicMock, patch
+
+from caldav.objects import Calendar, Event
+
from odoo.tests import common
-from unittest.mock import patch, MagicMock
-from caldav.objects import Event, Calendar
class TestNextCloudConfig(common.TransactionCase):
diff --git a/nextcloud_odoo_sync/tests/test_sync_common.py b/nextcloud_odoo_sync/tests/test_sync_common.py
index 515a709..2bd7bbc 100644
--- a/nextcloud_odoo_sync/tests/test_sync_common.py
+++ b/nextcloud_odoo_sync/tests/test_sync_common.py
@@ -1,9 +1,10 @@
from datetime import datetime
from unittest.mock import MagicMock, patch
+
+from caldav.objects import Calendar, Event
+
from odoo.tests.common import TransactionCase
from odoo.tools import html2plaintext
-from caldav.objects import Event, Calendar
-from odoo.addons.nextcloud_odoo_sync.models.nextcloud_caldav import Nextcloudcaldav
class TestSyncNextcloud(TransactionCase):
diff --git a/nextcloud_odoo_sync/tests/test_sync_odoo2nextcloud.py b/nextcloud_odoo_sync/tests/test_sync_odoo2nextcloud.py
index 8526de8..c2d24b3 100644
--- a/nextcloud_odoo_sync/tests/test_sync_odoo2nextcloud.py
+++ b/nextcloud_odoo_sync/tests/test_sync_odoo2nextcloud.py
@@ -1,9 +1,11 @@
from unittest.mock import MagicMock, patch
-from odoo.addons.nextcloud_odoo_sync.tests.test_sync_common import TestSyncNextcloud
-from odoo.addons.nextcloud_odoo_sync.models.nextcloud_caldav import Nextcloudcaldav
-from odoo.addons.nextcloud_odoo_sync.models.nc_sync_user import NcSyncUser
+
from caldav.objects import Calendar, Event
+from odoo.addons.nextcloud_odoo_sync.models.nc_sync_user import NcSyncUser
+from odoo.addons.nextcloud_odoo_sync.models.nextcloud_caldav import Nextcloudcaldav
+from odoo.addons.nextcloud_odoo_sync.tests.test_sync_common import TestSyncNextcloud
+
class TestSyncOdoo2Nextcloud(TestSyncNextcloud):
def setUp(self):
@@ -25,7 +27,6 @@ def mock_update_nextcloud_events(self, cal_event):
) as mock_get_nc_event_hash_by_uid, patch.object(
self.env.cr, "commit"
) as mock_cr_commit:
-
mock_get_user_calendar.return_value = cal_event.parent
mock_save_event.return_value = cal_event
mock_save.return_value = True
diff --git a/website_payment_recurring_donations/__init__.py b/website_payment_recurring_donations/__init__.py
index 19240f4..91c5580 100755
--- a/website_payment_recurring_donations/__init__.py
+++ b/website_payment_recurring_donations/__init__.py
@@ -1,2 +1,2 @@
from . import controllers
-from . import models
\ No newline at end of file
+from . import models
diff --git a/website_payment_recurring_donations/__manifest__.py b/website_payment_recurring_donations/__manifest__.py
index f24f21d..ea14704 100755
--- a/website_payment_recurring_donations/__manifest__.py
+++ b/website_payment_recurring_donations/__manifest__.py
@@ -1,23 +1,23 @@
# Copyright 2023 Onestein- Anjeel Haria
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
- 'name': 'Recurring donations',
- 'version': '16.0.1.0.0',
- 'category': 'Website',
- 'license': 'LGPL-3',
- 'summary': 'Recurring donations',
- 'description': """Recurring donations""",
- 'depends': ["website_payment"],
- 'data': [
+ "name": "Recurring donations",
+ "version": "16.0.1.0.0",
+ "category": "Website",
+ "license": "LGPL-3",
+ "summary": "Recurring donations",
+ "description": """Recurring donations""",
+ "depends": ["website_payment"],
+ "data": [
"data/donation_data.xml",
"views/snippets/s_donation.xml",
"views/donation_templates.xml",
"views/payment_transaction_view.xml",
- "views/res_partner_view.xml"
+ "views/res_partner_view.xml",
],
"assets": {
- 'web.assets_frontend': [
- 'website_payment_recurring_donations/static/src/js/website_payment_form.js',
+ "web.assets_frontend": [
+ "website_payment_recurring_donations/static/src/js/website_payment_form.js",
],
},
}
diff --git a/website_payment_recurring_donations/controllers/portal.py b/website_payment_recurring_donations/controllers/portal.py
index 5019d63..6156bdf 100755
--- a/website_payment_recurring_donations/controllers/portal.py
+++ b/website_payment_recurring_donations/controllers/portal.py
@@ -1,25 +1,35 @@
# Copyright 2023 Onestein - Anjeel Haria
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from odoo.addons.payment.controllers import portal as payment_portal
from odoo import http
from odoo.http import request
+from odoo.addons.payment.controllers import portal as payment_portal
-class PaymentPortal(payment_portal.PaymentPortal):
- @http.route('/donation/transaction/', type='json', auth='public', website=True, sitemap=False)
- def donation_transaction(self, amount, currency_id, partner_id, access_token, minimum_amount=0, **kwargs):
+class PaymentPortal(payment_portal.PaymentPortal):
+ @http.route(
+ "/donation/transaction/",
+ type="json",
+ auth="public",
+ website=True,
+ sitemap=False,
+ )
+ def donation_transaction(
+ self, amount, currency_id, partner_id, access_token, minimum_amount=0, **kwargs
+ ):
if request.env.user._is_public():
- kwargs['donation_partner_details'] = kwargs['partner_details']
- return super().donation_transaction(amount, currency_id, partner_id, access_token, minimum_amount, **kwargs)
+ kwargs["donation_partner_details"] = kwargs["partner_details"]
+ return super().donation_transaction(
+ amount, currency_id, partner_id, access_token, minimum_amount, **kwargs
+ )
def _get_custom_rendering_context_values(
- self,
- donation_options=None,
- donation_descriptions=None,
- is_donation=False,
- **kwargs
+ self,
+ donation_options=None,
+ donation_descriptions=None,
+ is_donation=False,
+ **kwargs
):
rendering_context = super()._get_custom_rendering_context_values(
donation_options=donation_options,
@@ -28,32 +38,53 @@ def _get_custom_rendering_context_values(
**kwargs
)
if is_donation:
- if kwargs.get('donation_frequency') and kwargs.get('donation_frequency') == 'monthly':
+ if (
+ kwargs.get("donation_frequency")
+ and kwargs.get("donation_frequency") == "monthly"
+ ):
is_monthly = True
else:
is_monthly = False
- rendering_context.update({
- 'is_onetime': not is_monthly,
- 'is_monthly': is_monthly,
- })
+ rendering_context.update(
+ {
+ "is_onetime": not is_monthly,
+ "is_monthly": is_monthly,
+ }
+ )
return rendering_context
- def _create_transaction(self, partner_id, *args, custom_create_values=None, **kwargs):
- if kwargs.get('donation_frequency'):
+ def _create_transaction(
+ self, partner_id, *args, custom_create_values=None, **kwargs
+ ):
+ if kwargs.get("donation_frequency"):
if not custom_create_values:
custom_create_values = {}
- if 'donation_frequency' not in custom_create_values: # We are in the payment module's flow
- custom_create_values['donation_frequency'] = kwargs.pop('donation_frequency')
- if kwargs.get('donation_partner_details'):
- res_partner_obj = request.env['res.partner'].sudo()
- details = kwargs.pop('donation_partner_details')
- country_id = int(details.get('country_id'))
- email = details.get('email')
+ if (
+ "donation_frequency" not in custom_create_values
+ ): # We are in the payment module's flow
+ custom_create_values["donation_frequency"] = kwargs.pop(
+ "donation_frequency"
+ )
+ if kwargs.get("donation_partner_details"):
+ res_partner_obj = request.env["res.partner"].sudo()
+ details = kwargs.pop("donation_partner_details")
+ country_id = int(details.get("country_id"))
+ email = details.get("email")
partner_id = res_partner_obj.search(
- [('email', '=ilike', email), ('country_id', '=', country_id)],
- limit=1).id
+ [("email", "=ilike", email), ("country_id", "=", country_id)],
+ limit=1,
+ ).id
if not partner_id:
- partner_id = res_partner_obj.create({'name': details.get('name'), 'email': email,
- 'country_id': country_id}).id
- return super()._create_transaction(partner_id=partner_id, *args, custom_create_values=custom_create_values,
- **kwargs)
+ partner_id = res_partner_obj.create(
+ {
+ "name": details.get("name"),
+ "email": email,
+ "country_id": country_id,
+ }
+ ).id
+ return super()._create_transaction(
+ partner_id=partner_id,
+ *args,
+ custom_create_values=custom_create_values,
+ **kwargs
+ )
diff --git a/website_payment_recurring_donations/models/__init__.py b/website_payment_recurring_donations/models/__init__.py
index ea7d6a3..cb741e9 100755
--- a/website_payment_recurring_donations/models/__init__.py
+++ b/website_payment_recurring_donations/models/__init__.py
@@ -1 +1 @@
-from . import payment_transaction
\ No newline at end of file
+from . import payment_transaction
diff --git a/website_payment_recurring_donations/models/payment_transaction.py b/website_payment_recurring_donations/models/payment_transaction.py
index e970a35..b960698 100755
--- a/website_payment_recurring_donations/models/payment_transaction.py
+++ b/website_payment_recurring_donations/models/payment_transaction.py
@@ -5,25 +5,27 @@
class PaymentTransaction(models.Model):
- _inherit = 'payment.transaction'
+ _inherit = "payment.transaction"
donation_frequency = fields.Selection(
- [
- ('onetime', 'One Time'),
- ('monthly', 'Monthly')
- ],
- string='Donation Frequency'
+ [("onetime", "One Time"), ("monthly", "Monthly")], string="Donation Frequency"
+ )
+ recurring_donation_provider_reference = fields.Char(
+ "Provider Reference For Recurring Donation"
+ )
+ is_recurring_donation_terminated = fields.Boolean(
+ "Is Recurring Donation Terminated"
)
- recurring_donation_provider_reference = fields.Char('Provider Reference For Recurring Donation')
- is_recurring_donation_terminated = fields.Boolean('Is Recurring Donation Terminated')
- def _send_donation_email(self, is_internal_notification=False, comment=None, recipient_email=None):
+ def _send_donation_email(
+ self, is_internal_notification=False, comment=None, recipient_email=None
+ ):
self = self.with_context(lang=self.partner_lang)
- return super()._send_donation_email(is_internal_notification, comment, recipient_email)
+ return super()._send_donation_email(
+ is_internal_notification, comment, recipient_email
+ )
def action_terminate_recurring_donation(self):
# This method needs to be extended in each provider module.
# This method cancels/terminates the recurring donation on the provider end
return True
-
-
diff --git a/website_payment_recurring_donations/static/src/snippets/s_donation/000.js b/website_payment_recurring_donations/static/src/snippets/s_donation/000.js
index 806801d..24008ad 100755
--- a/website_payment_recurring_donations/static/src/snippets/s_donation/000.js
+++ b/website_payment_recurring_donations/static/src/snippets/s_donation/000.js
@@ -1,7 +1,7 @@
/** @odoo-module **/
/* Copyright 2023 Onestein - Anjeel Haria
* License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). */
-
+
import publicWidget from 'web.public.widget';
const DonationSnippet = publicWidget.registry.DonationSnippet;
DonationSnippet.include({
diff --git a/website_payment_recurring_donations/views/payment_transaction_view.xml b/website_payment_recurring_donations/views/payment_transaction_view.xml
index 70f0c05..ccdf41e 100755
--- a/website_payment_recurring_donations/views/payment_transaction_view.xml
+++ b/website_payment_recurring_donations/views/payment_transaction_view.xml
@@ -34,4 +34,4 @@
[('partner_id','=', active_id)]{'create': False}
-
\ No newline at end of file
+
diff --git a/website_payment_recurring_donations/views/res_partner_view.xml b/website_payment_recurring_donations/views/res_partner_view.xml
index 0757ed6..d524c92 100755
--- a/website_payment_recurring_donations/views/res_partner_view.xml
+++ b/website_payment_recurring_donations/views/res_partner_view.xml
@@ -14,4 +14,4 @@
-
\ No newline at end of file
+
diff --git a/website_payment_recurring_donations_mollie/__init__.py b/website_payment_recurring_donations_mollie/__init__.py
index 9a7e03e..0650744 100755
--- a/website_payment_recurring_donations_mollie/__init__.py
+++ b/website_payment_recurring_donations_mollie/__init__.py
@@ -1 +1 @@
-from . import models
\ No newline at end of file
+from . import models
diff --git a/website_payment_recurring_donations_mollie/__manifest__.py b/website_payment_recurring_donations_mollie/__manifest__.py
index 5e43ada..fed0ddd 100755
--- a/website_payment_recurring_donations_mollie/__manifest__.py
+++ b/website_payment_recurring_donations_mollie/__manifest__.py
@@ -1,17 +1,15 @@
# Copyright 2023 Onestein- Anjeel Haria
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
{
- 'name': 'Recurring donations using Mollie Subscriptions',
- 'version': '16.0.1.0.0',
- 'category': 'Website',
- 'license': 'LGPL-3',
- 'summary': 'Recurring donations using Mollie Subscriptions',
- 'description': """Recurring donations using Mollie Subscriptions""",
- 'depends': ["website_payment_recurring_donations", "payment_mollie_official"],
- 'data': [
+ "name": "Recurring donations using Mollie Subscriptions",
+ "version": "16.0.1.0.0",
+ "category": "Website",
+ "license": "LGPL-3",
+ "summary": "Recurring donations using Mollie Subscriptions",
+ "description": """Recurring donations using Mollie Subscriptions""",
+ "depends": ["website_payment_recurring_donations", "payment_mollie_official"],
+ "data": [
"views/res_partner_view.xml",
],
- 'external_dependencies': {
- 'python': ['mollie-api-python']
- },
+ "external_dependencies": {"python": ["mollie-api-python"]},
}
diff --git a/website_payment_recurring_donations_mollie/models/payment_provider.py b/website_payment_recurring_donations_mollie/models/payment_provider.py
index 6c0c62f..77526a7 100755
--- a/website_payment_recurring_donations_mollie/models/payment_provider.py
+++ b/website_payment_recurring_donations_mollie/models/payment_provider.py
@@ -2,28 +2,38 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from mollie.api.client import Client as MollieClient
+
from odoo import models, service
class PaymentAcquirerMollie(models.Model):
- _inherit = 'payment.provider'
+ _inherit = "payment.provider"
def _api_mollie_create_customer_id(self):
- if self._context.get('partner', False):
- partner = self.env['res.partner'].browse(self._context.get('partner'))
- customer_data = {'name': partner.name, 'metadata': {'odoo_partner_id': partner.id}}
+ if self._context.get("partner", False):
+ partner = self.env["res.partner"].browse(self._context.get("partner"))
+ customer_data = {
+ "name": partner.name,
+ "metadata": {"odoo_partner_id": partner.id},
+ }
if partner.email:
- customer_data['email'] = partner.email
- return self._mollie_make_request('/customers', data=customer_data, method="POST")
+ customer_data["email"] = partner.email
+ return self._mollie_make_request(
+ "/customers", data=customer_data, method="POST"
+ )
return super(PaymentAcquirerMollie, self)._api_mollie_create_customer_id()
def _api_mollie_get_client(self):
mollie_client = MollieClient(timeout=10)
- if self.state == 'enabled':
+ if self.state == "enabled":
mollie_client.set_api_key(self.mollie_api_key)
- elif self.state == 'test':
+ elif self.state == "test":
mollie_client.set_api_key(self.mollie_api_key_test)
- mollie_client.set_user_agent_component('Odoo', service.common.exp_version()['server_version'])
- mollie_client.set_user_agent_component('MollieOdoo', self.env.ref('base.module_payment_mollie').installed_version)
+ mollie_client.set_user_agent_component(
+ "Odoo", service.common.exp_version()["server_version"]
+ )
+ mollie_client.set_user_agent_component(
+ "MollieOdoo", self.env.ref("base.module_payment_mollie").installed_version
+ )
return mollie_client
diff --git a/website_payment_recurring_donations_mollie/models/payment_transaction.py b/website_payment_recurring_donations_mollie/models/payment_transaction.py
index c061794..6fbbb99 100755
--- a/website_payment_recurring_donations_mollie/models/payment_transaction.py
+++ b/website_payment_recurring_donations_mollie/models/payment_transaction.py
@@ -1,20 +1,22 @@
# Copyright 2023 Onestein - Anjeel Haria
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-from werkzeug import urls
-from datetime import date,datetime
-from dateutil.relativedelta import relativedelta
import logging
+from datetime import date, datetime
+
+from dateutil.relativedelta import relativedelta
+from werkzeug import urls
+
+from odoo import _, models
-from odoo import _, models, fields
from odoo.addons.payment_mollie.controllers.main import MollieController
_logger = logging.getLogger(__name__)
-DONATION_FREQUENCY_MAP = {'monthly': 'months'}
+DONATION_FREQUENCY_MAP = {"monthly": "months"}
class PaymentTransaction(models.Model):
- _inherit = 'payment.transaction'
+ _inherit = "payment.transaction"
def _get_transaction_customer_id(self):
mollie_customer_id = False
@@ -23,76 +25,89 @@ def _get_transaction_customer_id(self):
if partner_obj.mollie_customer_id:
mollie_customer_id = partner_obj.mollie_customer_id
else:
- customer_id_data = self.provider_id.with_context(partner=partner_obj.id)._api_mollie_create_customer_id()
- if customer_id_data and customer_id_data.get('id'):
- mollie_customer_id = customer_id_data.get('id')
- partner_obj.write({'mollie_customer_id': mollie_customer_id})
+ customer_id_data = self.provider_id.with_context(
+ partner=partner_obj.id
+ )._api_mollie_create_customer_id()
+ if customer_id_data and customer_id_data.get("id"):
+ mollie_customer_id = customer_id_data.get("id")
+ partner_obj.write({"mollie_customer_id": mollie_customer_id})
return mollie_customer_id
def _process_notification_data(self, data):
"""
Overriden method for handling donation transactions
"""
- if self.provider_code != 'mollie' or self.sale_order_ids:
+ if self.provider_code != "mollie" or self.sale_order_ids:
return super()._process_notification_data(data)
self._process_refund_transactions_status()
provider_reference = self.provider_reference
- mollie_payment = self.provider_id._api_mollie_get_payment_data(provider_reference)
- payment_status = mollie_payment.get('status')
+ mollie_payment = self.provider_id._api_mollie_get_payment_data(
+ provider_reference
+ )
+ payment_status = mollie_payment.get("status")
if (
- (payment_status in ['paid', 'done'] and self.state == 'done') or
- (payment_status in ['pending', 'open'] and self.state == 'pending') or
- (payment_status == 'authorized' and self.state == 'authorized')
+ (payment_status in ["paid", "done"] and self.state == "done")
+ or (payment_status in ["pending", "open"] and self.state == "pending")
+ or (payment_status == "authorized" and self.state == "authorized")
):
return
- if payment_status in ['paid', 'done', 'pending', 'authorized']:
- if self.is_donation and self.donation_frequency != 'onetime':
+ if payment_status in ["paid", "done", "pending", "authorized"]:
+ if self.is_donation and self.donation_frequency != "onetime":
self._mollie_create_donation_subscription(mollie_payment)
- if payment_status in ['paid', 'done']:
+ if payment_status in ["paid", "done"]:
self._set_done()
- elif payment_status in ['pending', 'open']:
+ elif payment_status in ["pending", "open"]:
self._set_pending()
- elif payment_status == 'authorized':
+ elif payment_status == "authorized":
self._set_authorized()
- elif payment_status in ['expired', 'canceled', 'failed']:
- self._set_canceled("Mollie: " + _("Mollie: canceled due to status: %s", payment_status))
+ elif payment_status in ["expired", "canceled", "failed"]:
+ self._set_canceled(
+ "Mollie: " + _("Mollie: canceled due to status: %s", payment_status)
+ )
else:
- self._set_error("Mollie: " + _("Received data with invalid payment status: %s", payment_status))
+ self._set_error(
+ "Mollie: "
+ + _("Received data with invalid payment status: %s", payment_status)
+ )
def _mollie_create_donation_subscription(self, mollie_payment):
self.ensure_one()
mollie = self.env.ref("payment.payment_provider_mollie")
mollie_client = mollie._api_mollie_get_client()
amount = {
- 'currency': self.currency_id.name,
- 'value': "%.2f" % (self.amount + self.fees)
+ "currency": self.currency_id.name,
+ "value": "%.2f" % (self.amount + self.fees),
}
- interval = '{} {}'.format(1, DONATION_FREQUENCY_MAP[self.donation_frequency])
- description = '{} - {}'.format(self.partner_id.name + 'Donation', self.reference)
- webhook_url = ''
- webhook_urls = urls.url_join(mollie.get_base_url(), MollieController._webhook_url)
+ interval = "{} {}".format(1, DONATION_FREQUENCY_MAP[self.donation_frequency])
+ description = "{} - {}".format(
+ self.partner_id.name + "Donation", self.reference
+ )
+ webhook_url = ""
+ webhook_urls = urls.url_join(
+ mollie.get_base_url(), MollieController._webhook_url
+ )
if "://localhost" not in webhook_urls and "://192.168." not in webhook_urls:
webhook_url = webhook_urls
mollie_customer_id = self._get_transaction_customer_id()
customer = mollie_client.customers.get(mollie_customer_id)
data = {
- 'amount': amount or '',
- 'interval': interval or '',
- 'description': description or '',
- 'webhookUrl': webhook_url,
- 'startDate': self._get_start_date(),
- 'mandateId': mollie_payment and mollie_payment['mandateId'] or ''
+ "amount": amount or "",
+ "interval": interval or "",
+ "description": description or "",
+ "webhookUrl": webhook_url,
+ "startDate": self._get_start_date(),
+ "mandateId": mollie_payment and mollie_payment["mandateId"] or "",
}
subscription = customer.subscriptions.create(data)
- if subscription and subscription['resource'] == 'subscription':
- self.recurring_donation_provider_reference = subscription['id'] or ''
+ if subscription and subscription["resource"] == "subscription":
+ self.recurring_donation_provider_reference = subscription["id"] or ""
def _get_start_date(self):
start_date = date.today()
if self.donation_frequency:
- if DONATION_FREQUENCY_MAP[self.donation_frequency] == 'months':
+ if DONATION_FREQUENCY_MAP[self.donation_frequency] == "months":
start_date = date.today() + relativedelta(months=1)
return start_date.strftime("%Y-%m-%d")
@@ -102,47 +117,73 @@ def _create_mollie_order_or_payment(self):
"""
self.ensure_one()
method_record = self.provider_id.mollie_methods_ids.filtered(
- lambda m: m.method_code == self.mollie_payment_method)
+ lambda m: m.method_code == self.mollie_payment_method
+ )
- if (self.is_donation and self.donation_frequency != 'onetime' and method_record.supports_payment_api):
- result = self.with_context(first_mollie_donation_payment=True)._mollie_create_payment_record('payment')
+ if (
+ self.is_donation
+ and self.donation_frequency != "onetime"
+ and method_record.supports_payment_api
+ ):
+ result = self.with_context(
+ first_mollie_donation_payment=True
+ )._mollie_create_payment_record("payment")
if result:
return result
return super()._create_mollie_order_or_payment()
def _mollie_prepare_payment_payload(self, api_type):
- payment_data, params = super(PaymentTransaction, self)._mollie_prepare_payment_payload(api_type)
+ payment_data, params = super(
+ PaymentTransaction, self
+ )._mollie_prepare_payment_payload(api_type)
if self._context.get("first_mollie_donation_payment"):
- payment_data.update({
- 'description': 'First Payment for {} - {}'.format(self.partner_id.name + 'Donation', self.reference),
- 'sequenceType': 'first'
- })
+ payment_data.update(
+ {
+ "description": "First Payment for {} - {}".format(
+ self.partner_id.name + "Donation", self.reference
+ ),
+ "sequenceType": "first",
+ }
+ )
mollie_customer_id = self._get_transaction_customer_id()
- if api_type == 'order':
- payment_data['payment']["customerId"] = mollie_customer_id
+ if api_type == "order":
+ payment_data["payment"]["customerId"] = mollie_customer_id
else:
payment_data["customerId"] = mollie_customer_id
return payment_data, params
def action_terminate_recurring_donation(self):
- if self.provider_id.code != 'mollie':
+ if self.provider_id.code != "mollie":
return super().action_terminate_recurring_donation()
mollie = self.env.ref("payment.payment_provider_mollie")
mollie_client = mollie._api_mollie_get_client()
try:
customer = mollie_client.customers.get(self.partner_id.mollie_customer_id)
- subscription = customer.subscriptions.delete(self.recurring_donation_provider_reference)
+ subscription = customer.subscriptions.delete(
+ self.recurring_donation_provider_reference
+ )
if subscription:
canceled_date = False
- if 'canceledAt' in subscription.keys():
- canceled_date = datetime.strptime(subscription.get('canceledAt')[0:19], "%Y-%m-%dT%H:%M:%S")
- msg = _("The recurring donation on mollie has been terminated on %s" % (canceled_date))
+ if "canceledAt" in subscription.keys():
+ canceled_date = datetime.strptime(
+ subscription.get("canceledAt")[0:19], "%Y-%m-%dT%H:%M:%S"
+ )
+ msg = _(
+ "The recurring donation on mollie has been terminated on %s"
+ % (canceled_date)
+ )
self.sudo().message_post(body=msg)
except Exception:
- _logger.info(_('Mollie customer or subscription not found'))
+ _logger.info(_("Mollie customer or subscription not found"))
# marking all related payment transactions for recurring donations having same provider reference as terminated.
self.search(
- [('recurring_donation_provider_reference', '=', self.recurring_donation_provider_reference)]).write(
- {'is_recurring_donation_terminated': True})
+ [
+ (
+ "recurring_donation_provider_reference",
+ "=",
+ self.recurring_donation_provider_reference,
+ )
+ ]
+ ).write({"is_recurring_donation_terminated": True})
return True
diff --git a/website_payment_recurring_donations_mollie/views/res_partner_view.xml b/website_payment_recurring_donations_mollie/views/res_partner_view.xml
index a59a12b..c165ae0 100755
--- a/website_payment_recurring_donations_mollie/views/res_partner_view.xml
+++ b/website_payment_recurring_donations_mollie/views/res_partner_view.xml
@@ -14,4 +14,3 @@
-
From 8486fe873cfc9d1ce7db413fcc52cad57fe433a4 Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Thu, 18 Apr 2024 20:39:03 +0800
Subject: [PATCH 17/22] [ADD] base_list_owned_databases - Allow the list_db
selector to see databases with an owner role of which the current user is a
member.
This module needs to be added as a server-wide module.
---
base_list_owned_databases/__init__.py | 1 +
base_list_owned_databases/__manifest__.py | 20 ++++++
base_list_owned_databases/patches/__init__.py | 1 +
base_list_owned_databases/patches/db.py | 69 +++++++++++++++++++
4 files changed, 91 insertions(+)
create mode 100644 base_list_owned_databases/__init__.py
create mode 100644 base_list_owned_databases/__manifest__.py
create mode 100644 base_list_owned_databases/patches/__init__.py
create mode 100644 base_list_owned_databases/patches/db.py
diff --git a/base_list_owned_databases/__init__.py b/base_list_owned_databases/__init__.py
new file mode 100644
index 0000000..56e9651
--- /dev/null
+++ b/base_list_owned_databases/__init__.py
@@ -0,0 +1 @@
+from . import patches
diff --git a/base_list_owned_databases/__manifest__.py b/base_list_owned_databases/__manifest__.py
new file mode 100644
index 0000000..c3f0a7d
--- /dev/null
+++ b/base_list_owned_databases/__manifest__.py
@@ -0,0 +1,20 @@
+# Copyright 2017-2023 Onestein ()
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+{
+ "name": "List Owned Databases",
+ "summary": "When using the database selector, the list of databases "
+ "now consists of any databases the current user has "
+ "ownership of through role membership, rather than only "
+ "those specifically owned by the user. "
+ "Must be declared as a server-wide module.",
+ "author": "Onestein",
+ "website": "https://www.onestein.nl",
+ "category": "Tools",
+ "version": "16.0.1.0.0",
+ "license": "AGPL-3",
+ "depends": [
+ "base",
+ ],
+ "installable": True,
+}
diff --git a/base_list_owned_databases/patches/__init__.py b/base_list_owned_databases/patches/__init__.py
new file mode 100644
index 0000000..5dadfec
--- /dev/null
+++ b/base_list_owned_databases/patches/__init__.py
@@ -0,0 +1 @@
+from . import db
diff --git a/base_list_owned_databases/patches/db.py b/base_list_owned_databases/patches/db.py
new file mode 100644
index 0000000..3987397
--- /dev/null
+++ b/base_list_owned_databases/patches/db.py
@@ -0,0 +1,69 @@
+# Copyright 2017-2023 Onestein ()
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+# This monkey patch allows list databases to find databases of which the current
+# user is the owner, but also those that have an owner role that the current
+# user is a member of.
+import logging
+from contextlib import closing
+import odoo
+from odoo.exceptions import AccessDenied
+import odoo.release
+import odoo.sql_db
+import odoo.tools
+
+_logger = logging.getLogger(__name__)
+
+
+def list_dbs(force=False):
+ if not odoo.tools.config['list_db'] and not force:
+ raise odoo.exceptions.AccessDenied()
+
+ if not odoo.tools.config['dbfilter'] and odoo.tools.config['db_name']:
+ # In case --db-filter is not provided and --database is passed, Odoo will not
+ # fetch the list of databases available on the postgres server and instead will
+ # use the value of --database as comma seperated list of exposed databases.
+ res = sorted(db.strip() for db in odoo.tools.config['db_name'].split(','))
+ return res
+
+ chosen_template = odoo.tools.config['db_template']
+ templates_list = tuple(set(['postgres', chosen_template]))
+ db = odoo.sql_db.db_connect('postgres')
+ with closing(db.cursor()) as cr:
+ try:
+ # ### START OF PATCH ####
+ # cr.execute(
+ # "select datname from pg_database where datdba=(select usesysid from pg_user where usename=current_user) and not datistemplate and datallowconn and datname not in %s order by datname",
+ # (templates_list,))
+ cr.execute(
+ """SELECT datname
+ FROM pg_database
+ WHERE NOT datistemplate AND datallowconn AND datname NOT IN %s
+ AND datdba IN
+ (
+ WITH RECURSIVE membership_tree(grpid, userid) AS (
+ -- Get all roles and list them as their own group as well
+ SELECT pg_roles.oid, pg_roles.oid
+ FROM pg_roles
+ UNION ALL
+ -- Now add all group membership
+ SELECT m_1.roleid, t_1.userid
+ FROM pg_auth_members m_1, membership_tree t_1
+ WHERE m_1.member = t_1.grpid
+ )
+ SELECT DISTINCT t.grpid
+ FROM membership_tree t, pg_roles r, pg_roles m
+ WHERE t.grpid = m.oid AND t.userid = r.oid
+ AND t.userid IN (SELECT usesysid FROM pg_user WHERE usename=current_user)
+ )
+ ORDER BY datname;""",
+ (templates_list,)
+ )
+ # ### END OF PATCH ###
+ res = [odoo.tools.ustr(name) for (name,) in cr.fetchall()]
+ except Exception:
+ _logger.exception('Listing databases failed:')
+ res = []
+ return res
+
+
+odoo.service.db.list_dbs = list_dbs
From 6c8b22c3f0d39b7b4c9d4488067aef992e724367 Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Thu, 18 Apr 2024 21:00:49 +0800
Subject: [PATCH 18/22] [FIX] base_list_owned_databases - pre-commit
---
base_list_owned_databases/__manifest__.py | 8 ++++----
base_list_owned_databases/patches/db.py | 24 +++++++++++------------
2 files changed, 16 insertions(+), 16 deletions(-)
diff --git a/base_list_owned_databases/__manifest__.py b/base_list_owned_databases/__manifest__.py
index c3f0a7d..e82c25b 100644
--- a/base_list_owned_databases/__manifest__.py
+++ b/base_list_owned_databases/__manifest__.py
@@ -4,10 +4,10 @@
{
"name": "List Owned Databases",
"summary": "When using the database selector, the list of databases "
- "now consists of any databases the current user has "
- "ownership of through role membership, rather than only "
- "those specifically owned by the user. "
- "Must be declared as a server-wide module.",
+ "now consists of any databases the current user has "
+ "ownership of through role membership, rather than only "
+ "those specifically owned by the user. "
+ "Must be declared as a server-wide module.",
"author": "Onestein",
"website": "https://www.onestein.nl",
"category": "Tools",
diff --git a/base_list_owned_databases/patches/db.py b/base_list_owned_databases/patches/db.py
index 3987397..d5cf5cb 100644
--- a/base_list_owned_databases/patches/db.py
+++ b/base_list_owned_databases/patches/db.py
@@ -5,8 +5,8 @@
# user is a member of.
import logging
from contextlib import closing
+
import odoo
-from odoo.exceptions import AccessDenied
import odoo.release
import odoo.sql_db
import odoo.tools
@@ -15,19 +15,19 @@
def list_dbs(force=False):
- if not odoo.tools.config['list_db'] and not force:
+ if not odoo.tools.config["list_db"] and not force:
raise odoo.exceptions.AccessDenied()
- if not odoo.tools.config['dbfilter'] and odoo.tools.config['db_name']:
+ if not odoo.tools.config["dbfilter"] and odoo.tools.config["db_name"]:
# In case --db-filter is not provided and --database is passed, Odoo will not
# fetch the list of databases available on the postgres server and instead will
# use the value of --database as comma seperated list of exposed databases.
- res = sorted(db.strip() for db in odoo.tools.config['db_name'].split(','))
+ res = sorted(db.strip() for db in odoo.tools.config["db_name"].split(","))
return res
- chosen_template = odoo.tools.config['db_template']
- templates_list = tuple(set(['postgres', chosen_template]))
- db = odoo.sql_db.db_connect('postgres')
+ chosen_template = odoo.tools.config["db_template"]
+ templates_list = tuple({"postgres", chosen_template})
+ db = odoo.sql_db.db_connect("postgres")
with closing(db.cursor()) as cr:
try:
# ### START OF PATCH ####
@@ -36,9 +36,9 @@ def list_dbs(force=False):
# (templates_list,))
cr.execute(
"""SELECT datname
- FROM pg_database
- WHERE NOT datistemplate AND datallowconn AND datname NOT IN %s
- AND datdba IN
+ FROM pg_database
+ WHERE NOT datistemplate AND datallowconn AND datname NOT IN %s
+ AND datdba IN
(
WITH RECURSIVE membership_tree(grpid, userid) AS (
-- Get all roles and list them as their own group as well
@@ -56,12 +56,12 @@ def list_dbs(force=False):
AND t.userid IN (SELECT usesysid FROM pg_user WHERE usename=current_user)
)
ORDER BY datname;""",
- (templates_list,)
+ (templates_list,),
)
# ### END OF PATCH ###
res = [odoo.tools.ustr(name) for (name,) in cr.fetchall()]
except Exception:
- _logger.exception('Listing databases failed:')
+ _logger.exception("Listing databases failed:")
res = []
return res
From c2770de7bce0ad110983aca8e0f5a445d6b56f14 Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Mon, 22 Apr 2024 15:21:51 +0800
Subject: [PATCH 19/22] [FIX] Pre-commit - Ignore nextcloud_odoo_sync
---
.pre-commit-config.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e127082..c061545 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -18,8 +18,8 @@ exclude: |
readme/.*\.(rst|md)$|
# You don't usually want a bot to modify your legal texts
(LICENSE.*|COPYING.*)|
- # Emesa: prevent "not used from manifest" for indirectly loaded test data
- custom/addons/([^/]+)/test_data/
+ # Temporary modules
+ nextcloud_odoo_sync/
default_language_version:
python: python3
node: "16.17.0"
From da38f8979c11c1d1470f806726133a34765b92af Mon Sep 17 00:00:00 2001
From: Mark Schuit
Date: Mon, 22 Apr 2024 15:22:34 +0800
Subject: [PATCH 20/22] [FIX] Fixed pre-commit warnings and updated
website_payment_recurring_donations translations in NL.
---
.../__init__.py | 0
.../__manifest__.py | 2 +
.../controllers/__init__.py | 0
.../controllers/portal.py | 0
.../data/donation_data.xml | 0
.../i18n/nl.po | 0
.../models/__init__.py | 0
.../models/payment_transaction.py | 6 +--
...nt_form.js => website_payment_form.esm.js} | 1 +
.../s_donation/{000.js => 000.esm.js} | 0
.../static/src/snippets/s_donation/000.scss | 0
.../views/donation_templates.xml | 0
.../views/payment_transaction_view.xml | 0
.../views/res_partner_view.xml | 0
.../views/snippets/s_donation.xml | 6 +--
.../__init__.py | 0
.../__manifest__.py | 2 +
.../i18n/nl.po | 41 +++++++++++++++++--
.../models/__init__.py | 0
.../models/payment_provider.py | 0
.../models/payment_transaction.py | 6 +--
.../models/res_partner.py | 0
.../views/res_partner_view.xml | 0
23 files changed, 51 insertions(+), 13 deletions(-)
mode change 100755 => 100644 website_payment_recurring_donations/__init__.py
mode change 100755 => 100644 website_payment_recurring_donations/__manifest__.py
mode change 100755 => 100644 website_payment_recurring_donations/controllers/__init__.py
mode change 100755 => 100644 website_payment_recurring_donations/controllers/portal.py
mode change 100755 => 100644 website_payment_recurring_donations/data/donation_data.xml
mode change 100755 => 100644 website_payment_recurring_donations/i18n/nl.po
mode change 100755 => 100644 website_payment_recurring_donations/models/__init__.py
mode change 100755 => 100644 website_payment_recurring_donations/models/payment_transaction.py
rename website_payment_recurring_donations/static/src/js/{website_payment_form.js => website_payment_form.esm.js} (92%)
mode change 100755 => 100644
rename website_payment_recurring_donations/static/src/snippets/s_donation/{000.js => 000.esm.js} (100%)
mode change 100755 => 100644
mode change 100755 => 100644 website_payment_recurring_donations/static/src/snippets/s_donation/000.scss
mode change 100755 => 100644 website_payment_recurring_donations/views/donation_templates.xml
mode change 100755 => 100644 website_payment_recurring_donations/views/payment_transaction_view.xml
mode change 100755 => 100644 website_payment_recurring_donations/views/res_partner_view.xml
mode change 100755 => 100644 website_payment_recurring_donations/views/snippets/s_donation.xml
mode change 100755 => 100644 website_payment_recurring_donations_mollie/__init__.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/__manifest__.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/i18n/nl.po
mode change 100755 => 100644 website_payment_recurring_donations_mollie/models/__init__.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/models/payment_provider.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/models/payment_transaction.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/models/res_partner.py
mode change 100755 => 100644 website_payment_recurring_donations_mollie/views/res_partner_view.xml
diff --git a/website_payment_recurring_donations/__init__.py b/website_payment_recurring_donations/__init__.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/__manifest__.py b/website_payment_recurring_donations/__manifest__.py
old mode 100755
new mode 100644
index ea14704..c64b9df
--- a/website_payment_recurring_donations/__manifest__.py
+++ b/website_payment_recurring_donations/__manifest__.py
@@ -3,6 +3,8 @@
{
"name": "Recurring donations",
"version": "16.0.1.0.0",
+ "author": "Onestein",
+ "website": "https://www.onestein.nl",
"category": "Website",
"license": "LGPL-3",
"summary": "Recurring donations",
diff --git a/website_payment_recurring_donations/controllers/__init__.py b/website_payment_recurring_donations/controllers/__init__.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/controllers/portal.py b/website_payment_recurring_donations/controllers/portal.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/data/donation_data.xml b/website_payment_recurring_donations/data/donation_data.xml
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/i18n/nl.po b/website_payment_recurring_donations/i18n/nl.po
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/models/__init__.py b/website_payment_recurring_donations/models/__init__.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/models/payment_transaction.py b/website_payment_recurring_donations/models/payment_transaction.py
old mode 100755
new mode 100644
index b960698..e003c4c
--- a/website_payment_recurring_donations/models/payment_transaction.py
+++ b/website_payment_recurring_donations/models/payment_transaction.py
@@ -8,14 +8,12 @@ class PaymentTransaction(models.Model):
_inherit = "payment.transaction"
donation_frequency = fields.Selection(
- [("onetime", "One Time"), ("monthly", "Monthly")], string="Donation Frequency"
+ [("onetime", "One Time"), ("monthly", "Monthly")]
)
recurring_donation_provider_reference = fields.Char(
"Provider Reference For Recurring Donation"
)
- is_recurring_donation_terminated = fields.Boolean(
- "Is Recurring Donation Terminated"
- )
+ is_recurring_donation_terminated = fields.Boolean()
def _send_donation_email(
self, is_internal_notification=False, comment=None, recipient_email=None
diff --git a/website_payment_recurring_donations/static/src/js/website_payment_form.js b/website_payment_recurring_donations/static/src/js/website_payment_form.esm.js
old mode 100755
new mode 100644
similarity index 92%
rename from website_payment_recurring_donations/static/src/js/website_payment_form.js
rename to website_payment_recurring_donations/static/src/js/website_payment_form.esm.js
index 6f6c20a..771b909
--- a/website_payment_recurring_donations/static/src/js/website_payment_form.js
+++ b/website_payment_recurring_donations/static/src/js/website_payment_form.esm.js
@@ -5,6 +5,7 @@
import checkoutForm from 'payment.checkout_form';
checkoutForm.include({
+ // eslint-disable-next-line no-unused-vars
_prepareTransactionRouteParams: function (code, paymentOptionId, flow) {
const transactionRouteParams = this._super(...arguments);
return $('.o_donation_payment_form').length ? {
diff --git a/website_payment_recurring_donations/static/src/snippets/s_donation/000.js b/website_payment_recurring_donations/static/src/snippets/s_donation/000.esm.js
old mode 100755
new mode 100644
similarity index 100%
rename from website_payment_recurring_donations/static/src/snippets/s_donation/000.js
rename to website_payment_recurring_donations/static/src/snippets/s_donation/000.esm.js
diff --git a/website_payment_recurring_donations/static/src/snippets/s_donation/000.scss b/website_payment_recurring_donations/static/src/snippets/s_donation/000.scss
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/views/donation_templates.xml b/website_payment_recurring_donations/views/donation_templates.xml
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/views/payment_transaction_view.xml b/website_payment_recurring_donations/views/payment_transaction_view.xml
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/views/res_partner_view.xml b/website_payment_recurring_donations/views/res_partner_view.xml
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations/views/snippets/s_donation.xml b/website_payment_recurring_donations/views/snippets/s_donation.xml
old mode 100755
new mode 100644
index 8e91fee..b2bcb8d
--- a/website_payment_recurring_donations/views/snippets/s_donation.xml
+++ b/website_payment_recurring_donations/views/snippets/s_donation.xml
@@ -26,13 +26,13 @@
-
+ Donation 000 JSweb.assets_frontend
- website_payment_recurring_donations/static/src/snippets/s_donation/000.js
+ website_payment_recurring_donations/static/src/snippets/s_donation/000.esm.js
-
+ Donation 000 SCSSweb.assets_frontendwebsite_payment_recurring_donations/static/src/snippets/s_donation/000.scss
diff --git a/website_payment_recurring_donations_mollie/__init__.py b/website_payment_recurring_donations_mollie/__init__.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations_mollie/__manifest__.py b/website_payment_recurring_donations_mollie/__manifest__.py
old mode 100755
new mode 100644
index fed0ddd..9857082
--- a/website_payment_recurring_donations_mollie/__manifest__.py
+++ b/website_payment_recurring_donations_mollie/__manifest__.py
@@ -3,6 +3,8 @@
{
"name": "Recurring donations using Mollie Subscriptions",
"version": "16.0.1.0.0",
+ "author": "Onestein",
+ "website": "https://www.onestein.nl",
"category": "Website",
"license": "LGPL-3",
"summary": "Recurring donations using Mollie Subscriptions",
diff --git a/website_payment_recurring_donations_mollie/i18n/nl.po b/website_payment_recurring_donations_mollie/i18n/nl.po
old mode 100755
new mode 100644
index 5d899a5..dd4e23d
--- a/website_payment_recurring_donations_mollie/i18n/nl.po
+++ b/website_payment_recurring_donations_mollie/i18n/nl.po
@@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
-"Project-Id-Version: Odoo Server 15.0\n"
+"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-09-22 13:53+0000\n"
-"PO-Revision-Date: 2023-09-22 13:53+0000\n"
+"POT-Creation-Date: 2024-04-22 07:13+0000\n"
+"PO-Revision-Date: 2024-04-22 07:13+0000\n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
@@ -16,18 +16,53 @@ msgstr ""
"Plural-Forms: \n"
#. module: website_payment_recurring_donations_mollie
+#: model:ir.model,name:website_payment_recurring_donations_mollie.model_res_partner
+msgid "Contact"
+msgstr ""
+
+#. module: website_payment_recurring_donations_mollie
+#: model:ir.model.fields,field_description:website_payment_recurring_donations_mollie.field_res_partner__mollie_customer_id
+msgid "Mollie Customer ID"
+msgstr "Mollie Klant ID"
+
+#. module: website_payment_recurring_donations_mollie
+#. odoo-python
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
+#, python-format
+msgid "Mollie customer or subscription not found"
+msgstr "Mollie klant of abonnement niet gevonden"
+
+#. module: website_payment_recurring_donations_mollie
+#. odoo-python
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
#, python-format
msgid "Mollie: canceled due to status: %s"
msgstr "Mollie: geannuleerd vanwege status: %s"
+#. module: website_payment_recurring_donations_mollie
+#: model:ir.model,name:website_payment_recurring_donations_mollie.model_payment_provider
+msgid "Payment Provider"
+msgstr "Betaaldienstaanbieder"
+
#. module: website_payment_recurring_donations_mollie
#: model:ir.model,name:website_payment_recurring_donations_mollie.model_payment_transaction
msgid "Payment Transaction"
msgstr "Betalingstransactie"
#. module: website_payment_recurring_donations_mollie
+#. odoo-python
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
#, python-format
msgid "Received data with invalid payment status: %s"
msgstr "Gegevens ontvangen met ongeldige betalingsstatus: %s"
+
+#. module: website_payment_recurring_donations_mollie
+#. odoo-python
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
+#: code:addons/website_payment_recurring_donations_mollie/models/payment_transaction.py:0
+#, python-format
+msgid "The recurring donation on mollie has been terminated on %s."
+msgstr "The herhalende donatie via Mollie is gestopt op %s."
diff --git a/website_payment_recurring_donations_mollie/models/__init__.py b/website_payment_recurring_donations_mollie/models/__init__.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations_mollie/models/payment_provider.py b/website_payment_recurring_donations_mollie/models/payment_provider.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations_mollie/models/payment_transaction.py b/website_payment_recurring_donations_mollie/models/payment_transaction.py
old mode 100755
new mode 100644
index 6fbbb99..d2fa001
--- a/website_payment_recurring_donations_mollie/models/payment_transaction.py
+++ b/website_payment_recurring_donations_mollie/models/payment_transaction.py
@@ -170,10 +170,10 @@ def action_terminate_recurring_donation(self):
subscription.get("canceledAt")[0:19], "%Y-%m-%dT%H:%M:%S"
)
msg = _(
- "The recurring donation on mollie has been terminated on %s"
- % (canceled_date)
+ "The recurring donation on mollie has been terminated on %s.",
+ canceled_date,
)
- self.sudo().message_post(body=msg)
+ self.sudo().message_post(body=f"{msg}")
except Exception:
_logger.info(_("Mollie customer or subscription not found"))
# marking all related payment transactions for recurring donations having same provider reference as terminated.
diff --git a/website_payment_recurring_donations_mollie/models/res_partner.py b/website_payment_recurring_donations_mollie/models/res_partner.py
old mode 100755
new mode 100644
diff --git a/website_payment_recurring_donations_mollie/views/res_partner_view.xml b/website_payment_recurring_donations_mollie/views/res_partner_view.xml
old mode 100755
new mode 100644
From dd18d12ac5dcc9ed3cc4fd8a93906a9944e9d0a5 Mon Sep 17 00:00:00 2001
From: Anjeel Haria
Date: Tue, 23 Apr 2024 11:57:11 +0530
Subject: [PATCH 21/22] [FIX] membership_prorate_variable_period: Fix test
scripts
---
.../tests/test_membership_prorate_variable_period.py | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/membership_prorate_variable_period/tests/test_membership_prorate_variable_period.py b/membership_prorate_variable_period/tests/test_membership_prorate_variable_period.py
index 53ec9b2..0c164b0 100644
--- a/membership_prorate_variable_period/tests/test_membership_prorate_variable_period.py
+++ b/membership_prorate_variable_period/tests/test_membership_prorate_variable_period.py
@@ -63,7 +63,7 @@ def test_create_invoice_membership_product_prorate_fixed(self):
def test_create_invoice_membership_product_prorate_week(self):
invoice = self.create_invoice("2015-01-01") # It's thursday
- self.assertAlmostEqual(invoice.invoice_line_ids[0].quantity, 0.43, 2)
+ self.assertAlmostEqual(invoice.invoice_line_ids[0].quantity, 0.57, 2)
self.assertTrue(self.partner.member_lines)
self.assertEqual(self.partner.member_lines[0].state, "waiting")
self.assertEqual(
@@ -77,7 +77,7 @@ def test_create_invoice_membership_product_prorate_week(self):
def test_create_invoice_membership_product_prorate_month(self):
self.product.membership_interval_unit = "months"
invoice = self.create_invoice("2015-04-15")
- self.assertAlmostEqual(invoice.invoice_line_ids[0].quantity, 0.5, 2)
+ self.assertAlmostEqual(invoice.invoice_line_ids[0].quantity, 0.53, 2)
self.assertTrue(self.partner.member_lines)
self.assertEqual(self.partner.member_lines[0].state, "waiting")
self.assertEqual(
From aec67e57264a995cbcfae5517b9321d786bc7f60 Mon Sep 17 00:00:00 2001
From: Anjeel Haria
Date: Wed, 24 Apr 2024 16:35:11 +0530
Subject: [PATCH 22/22] Exclude nextcloud_odoo_sync from tests
---
.github/workflows/tests.yml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index d223c5c..5e18d72 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -41,13 +41,13 @@ jobs:
run: oca_init_test_database
env:
ADDONS_DIR: .,oca,third-party
- EXCLUDE: mail_tracking,website_sale_subscription_restrict_cart,l10n_nl_rgs,l10n_nl_rgs_account_financial_report,l10n_nl_rgs_asset,l10n_nl_rgs_mis_report,connector_jira_servicedesk,connector_jira,account_statement_import_file_reconcile_oca,membership_subscription_prorate_variable_period,membership_subscription,membership_prorate,web_leaflet_technical,website_cache_control,account_reconcile_oca,membership_variable_period,base_user_role,account_invoice_constraint_chronology,queue_job,auth_signup_verify_email,base_municipality,argocd_capacity,membership_prorate_variable_period,argocd_website,l10n_nl_account_tax_unece,auditlog,product_contract,subscription_oca,account_banking_mandate_contact,account_banking_sepa_credit_transfer,hr_expense_remove_mobile_link,website_sale_analytics_matomo,mrp_multi_level,mrp_planned_order_matrix,mrp_warehouse_calendar,partner_country_default_nl,partner_external_map,base_menu_visibility_restriction,partner_firstname,password_security,contract,l10n_nl_tax_statement,l10n_nl_xaf_auditfile_export,mass_mailing_partner,mis_builder,website_local_font,helpdesk_mgmt,base_vat_optional_vies,mis_builder_budget
+ EXCLUDE: mail_tracking,website_sale_subscription_restrict_cart,l10n_nl_rgs,l10n_nl_rgs_account_financial_report,l10n_nl_rgs_asset,l10n_nl_rgs_mis_report,connector_jira_servicedesk,connector_jira,account_statement_import_file_reconcile_oca,membership_subscription_prorate_variable_period,membership_subscription,membership_prorate,web_leaflet_technical,website_cache_control,account_reconcile_oca,membership_variable_period,base_user_role,account_invoice_constraint_chronology,queue_job,auth_signup_verify_email,base_municipality,argocd_capacity,membership_prorate_variable_period,argocd_website,l10n_nl_account_tax_unece,auditlog,product_contract,subscription_oca,account_banking_mandate_contact,account_banking_sepa_credit_transfer,hr_expense_remove_mobile_link,website_sale_analytics_matomo,mrp_multi_level,mrp_planned_order_matrix,mrp_warehouse_calendar,partner_country_default_nl,partner_external_map,base_menu_visibility_restriction,partner_firstname,password_security,contract,l10n_nl_tax_statement,l10n_nl_xaf_auditfile_export,mass_mailing_partner,mis_builder,website_local_font,helpdesk_mgmt,base_vat_optional_vies,mis_builder_budget,nextcloud_odoo_sync
ODOO_VERSION: "16.0"
- name: Run tests
run: oca_run_tests
env:
ADDONS_DIR: .,oca,third-party
- EXCLUDE: mail_tracking,website_sale_subscription_restrict_cart,l10n_nl_rgs,l10n_nl_rgs_account_financial_report,l10n_nl_rgs_asset,l10n_nl_rgs_mis_report,connector_jira_servicedesk,connector_jira,account_statement_import_file_reconcile_oca,membership_subscription_prorate_variable_period,membership_subscription,membership_prorate,web_leaflet_technical,website_cache_control,account_reconcile_oca,membership_variable_period,base_user_role,account_invoice_constraint_chronology,queue_job,auth_signup_verify_email,base_municipality,argocd_capacity,membership_prorate_variable_period,argocd_website,l10n_nl_account_tax_unece,auditlog,product_contract,subscription_oca,account_banking_mandate_contact,account_banking_sepa_credit_transfer,hr_expense_remove_mobile_link,website_sale_analytics_matomo,mrp_multi_level,mrp_planned_order_matrix,mrp_warehouse_calendar,partner_country_default_nl,partner_external_map,base_menu_visibility_restriction,partner_firstname,password_security,contract,l10n_nl_tax_statement,l10n_nl_xaf_auditfile_export,mass_mailing_partner,mis_builder,website_local_font,helpdesk_mgmt,base_vat_optional_vies,mis_builder_budget
+ EXCLUDE: mail_tracking,website_sale_subscription_restrict_cart,l10n_nl_rgs,l10n_nl_rgs_account_financial_report,l10n_nl_rgs_asset,l10n_nl_rgs_mis_report,connector_jira_servicedesk,connector_jira,account_statement_import_file_reconcile_oca,membership_subscription_prorate_variable_period,membership_subscription,membership_prorate,web_leaflet_technical,website_cache_control,account_reconcile_oca,membership_variable_period,base_user_role,account_invoice_constraint_chronology,queue_job,auth_signup_verify_email,base_municipality,argocd_capacity,membership_prorate_variable_period,argocd_website,l10n_nl_account_tax_unece,auditlog,product_contract,subscription_oca,account_banking_mandate_contact,account_banking_sepa_credit_transfer,hr_expense_remove_mobile_link,website_sale_analytics_matomo,mrp_multi_level,mrp_planned_order_matrix,mrp_warehouse_calendar,partner_country_default_nl,partner_external_map,base_menu_visibility_restriction,partner_firstname,password_security,contract,l10n_nl_tax_statement,l10n_nl_xaf_auditfile_export,mass_mailing_partner,mis_builder,website_local_font,helpdesk_mgmt,base_vat_optional_vies,mis_builder_budget,nextcloud_odoo_sync
ODOO_VERSION: "16.0"
- name: Prepare report
run: coverage xml -o coverage.xml --data-file .coverage