From b8f1ca6f280e195d178cdd2a8992cdf1ad906acc Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Fri, 3 Oct 2025 18:58:19 +0000
Subject: [PATCH 1/2] [BOT] post-merge updates
---
calendar_import_ics/README.rst | 104 ++++
calendar_import_ics/__init__.py | 2 +
calendar_import_ics/__manifest__.py | 17 +
.../i18n/calendar_import_ics.pot | 115 +++++
calendar_import_ics/i18n/it.po | 120 +++++
calendar_import_ics/models/__init__.py | 1 +
calendar_import_ics/models/calendar_event.py | 9 +
calendar_import_ics/pyproject.toml | 3 +
calendar_import_ics/readme/CONTRIBUTORS.md | 4 +
calendar_import_ics/readme/DESCRIPTION.md | 7 +
calendar_import_ics/readme/USAGE.md | 11 +
.../security/calendar_import_security.xml | 7 +
.../security/ir.model.access.csv | 2 +
.../static/description/icon.png | Bin 0 -> 9455 bytes
.../static/description/index.html | 454 ++++++++++++++++++
calendar_import_ics/tests/__init__.py | 1 +
.../tests/sample_files/test_calendar.ics | 45 ++
.../tests/sample_files/test_calendar_2.ics | 45 ++
.../tests/sample_files/test_calendar_3.ics | 36 ++
.../tests/test_calendar_import_ics.py | 80 +++
calendar_import_ics/wizards/__init__.py | 1 +
.../wizards/wizard_import_ics.py | 136 ++++++
.../wizards/wizard_import_ics.xml | 39 ++
23 files changed, 1239 insertions(+)
create mode 100644 calendar_import_ics/README.rst
create mode 100644 calendar_import_ics/__init__.py
create mode 100644 calendar_import_ics/__manifest__.py
create mode 100644 calendar_import_ics/i18n/calendar_import_ics.pot
create mode 100644 calendar_import_ics/i18n/it.po
create mode 100644 calendar_import_ics/models/__init__.py
create mode 100644 calendar_import_ics/models/calendar_event.py
create mode 100644 calendar_import_ics/pyproject.toml
create mode 100644 calendar_import_ics/readme/CONTRIBUTORS.md
create mode 100644 calendar_import_ics/readme/DESCRIPTION.md
create mode 100644 calendar_import_ics/readme/USAGE.md
create mode 100644 calendar_import_ics/security/calendar_import_security.xml
create mode 100644 calendar_import_ics/security/ir.model.access.csv
create mode 100644 calendar_import_ics/static/description/icon.png
create mode 100644 calendar_import_ics/static/description/index.html
create mode 100644 calendar_import_ics/tests/__init__.py
create mode 100644 calendar_import_ics/tests/sample_files/test_calendar.ics
create mode 100644 calendar_import_ics/tests/sample_files/test_calendar_2.ics
create mode 100644 calendar_import_ics/tests/sample_files/test_calendar_3.ics
create mode 100644 calendar_import_ics/tests/test_calendar_import_ics.py
create mode 100644 calendar_import_ics/wizards/__init__.py
create mode 100644 calendar_import_ics/wizards/wizard_import_ics.py
create mode 100644 calendar_import_ics/wizards/wizard_import_ics.xml
diff --git a/calendar_import_ics/README.rst b/calendar_import_ics/README.rst
new file mode 100644
index 00000000..e35b2baa
--- /dev/null
+++ b/calendar_import_ics/README.rst
@@ -0,0 +1,104 @@
+.. image:: https://odoo-community.org/readme-banner-image
+ :target: https://odoo-community.org/get-involved?utm_source=readme
+ :alt: Odoo Community Association
+
+=====================
+Calendar - Import ics
+=====================
+
+..
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! source digest: sha256:0b76e943a6186b958123d04eef313d6d66e7b9361daf4122b045fbca02b88b23
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+ :target: https://odoo-community.org/page/development-status
+ :alt: Beta
+.. |badge2| image:: https://img.shields.io/badge/license-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcalendar-lightgray.png?logo=github
+ :target: https://github.com/OCA/calendar/tree/18.0/calendar_import_ics
+ :alt: OCA/calendar
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/calendar-18-0/calendar-18-0-calendar_import_ics
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/calendar&target_branch=18.0
+ :alt: Try me on Runboat
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module adds a new wizard that allows you to import .ics files into
+odoo calendar, importing events and the following attributes:
+
+- Summary
+- Start Date
+- End Date
+- UID
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Usage
+=====
+
+To use this module, follow these steps:
+
+1. Navigate to the Calendar App.
+2. Go to Configuration.
+3. Select Import ICS File.
+
+When importing, you have two options:
+
+- Specify start and end dates to import events occurring within that
+ range.
+- Leave the date fields empty to import all events from the entire file.
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+-------
+
+* ForgeFlow S.L.
+
+Contributors
+------------
+
+- Arnau Cruz
+
+Do not contact contributors directly about support or help with
+technical issues.
+
+Maintainers
+-----------
+
+This module is maintained by the OCA.
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+This module is part of the `OCA/calendar `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/calendar_import_ics/__init__.py b/calendar_import_ics/__init__.py
new file mode 100644
index 00000000..976591c9
--- /dev/null
+++ b/calendar_import_ics/__init__.py
@@ -0,0 +1,2 @@
+from . import wizards
+from . import models
diff --git a/calendar_import_ics/__manifest__.py b/calendar_import_ics/__manifest__.py
new file mode 100644
index 00000000..cc71fbf1
--- /dev/null
+++ b/calendar_import_ics/__manifest__.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2024 - ForgeFlow S.L.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+{
+ "name": "Calendar - Import ics",
+ "summary": "Allow importing an ics file to our calendar",
+ "version": "18.0.1.0.1",
+ "author": "ForgeFlow S.L.,Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/calendar",
+ "license": "AGPL-3",
+ "depends": ["calendar"],
+ "data": [
+ "security/calendar_import_security.xml",
+ "security/ir.model.access.csv",
+ "wizards/wizard_import_ics.xml",
+ ],
+}
diff --git a/calendar_import_ics/i18n/calendar_import_ics.pot b/calendar_import_ics/i18n/calendar_import_ics.pot
new file mode 100644
index 00000000..7b23c6bd
--- /dev/null
+++ b/calendar_import_ics/i18n/calendar_import_ics.pot
@@ -0,0 +1,115 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * calendar_import_ics
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 18.0\n"
+"Report-Msgid-Bugs-To: \n"
+"Last-Translator: \n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: \n"
+
+#. module: calendar_import_ics
+#: model:ir.model,name:calendar_import_ics.model_calendar_event
+msgid "Calendar Event"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model,name:calendar_import_ics.model_calendar_import_ics
+#: model:res.groups,name:calendar_import_ics.group_calendar_import
+msgid "Calendar Import Ics"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model_terms:ir.ui.view,arch_db:calendar_import_ics.calendar_import_ics_form
+msgid "Cancel"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__create_uid
+msgid "Created by"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__create_date
+msgid "Created on"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_end_date
+msgid "End Import Date"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_event__event_identifier
+msgid "Event Id"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__id
+msgid "ID"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,help:calendar_import_ics.field_calendar_import_ics__do_remove_old_event
+msgid ""
+"If checked, the previously imported events that are not in this import will "
+"be deleted"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model_terms:ir.ui.view,arch_db:calendar_import_ics.calendar_import_ics_form
+msgid "Import"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.actions.act_window,name:calendar_import_ics.action_import_ics_wizard
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_ics_file
+#: model:ir.ui.menu,name:calendar_import_ics.menu_calendar_import_ics
+msgid "Import Ics File"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_ics_filename
+msgid "Import Ics Filename"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__write_uid
+msgid "Last Updated by"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__write_date
+msgid "Last Updated on"
+msgstr ""
+
+#. module: calendar_import_ics
+#. odoo-python
+#: code:addons/calendar_import_ics/wizards/wizard_import_ics.py:0
+msgid "Only ics files are supported"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__partner_id
+msgid "Partner"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__do_remove_old_event
+msgid "Remove old events?"
+msgstr ""
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_start_date
+msgid "Start Import Date"
+msgstr ""
diff --git a/calendar_import_ics/i18n/it.po b/calendar_import_ics/i18n/it.po
new file mode 100644
index 00000000..aea1ef12
--- /dev/null
+++ b/calendar_import_ics/i18n/it.po
@@ -0,0 +1,120 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * calendar_import_ics
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 18.0\n"
+"Report-Msgid-Bugs-To: \n"
+"PO-Revision-Date: 2025-08-08 14:26+0000\n"
+"Last-Translator: mymage \n"
+"Language-Team: none\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: \n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 5.10.4\n"
+
+#. module: calendar_import_ics
+#: model:ir.model,name:calendar_import_ics.model_calendar_event
+msgid "Calendar Event"
+msgstr "Evento calendario"
+
+#. module: calendar_import_ics
+#: model:ir.model,name:calendar_import_ics.model_calendar_import_ics
+#: model:res.groups,name:calendar_import_ics.group_calendar_import
+msgid "Calendar Import Ics"
+msgstr "Importa calendario ics"
+
+#. module: calendar_import_ics
+#: model_terms:ir.ui.view,arch_db:calendar_import_ics.calendar_import_ics_form
+msgid "Cancel"
+msgstr "Annulla"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__create_uid
+msgid "Created by"
+msgstr "Creato da"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__create_date
+msgid "Created on"
+msgstr "Creato il"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__display_name
+msgid "Display Name"
+msgstr "Nome visualizzato"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_end_date
+msgid "End Import Date"
+msgstr "Data fine importazione"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_event__event_identifier
+msgid "Event Id"
+msgstr "ID evento"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__id
+msgid "ID"
+msgstr "ID"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,help:calendar_import_ics.field_calendar_import_ics__do_remove_old_event
+msgid ""
+"If checked, the previously imported events that are not in this import will "
+"be deleted"
+msgstr ""
+"Se selezionata, gli eventi precedentemente importati che non sono in questa "
+"importazione saranno cancellati"
+
+#. module: calendar_import_ics
+#: model_terms:ir.ui.view,arch_db:calendar_import_ics.calendar_import_ics_form
+msgid "Import"
+msgstr "Importa"
+
+#. module: calendar_import_ics
+#: model:ir.actions.act_window,name:calendar_import_ics.action_import_ics_wizard
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_ics_file
+#: model:ir.ui.menu,name:calendar_import_ics.menu_calendar_import_ics
+msgid "Import Ics File"
+msgstr "Importa file ics"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_ics_filename
+msgid "Import Ics Filename"
+msgstr "Nome file importazione ics"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__write_uid
+msgid "Last Updated by"
+msgstr "Ultimo aggiornamento di"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__write_date
+msgid "Last Updated on"
+msgstr "Ultimo aggiornamento il"
+
+#. module: calendar_import_ics
+#. odoo-python
+#: code:addons/calendar_import_ics/wizards/wizard_import_ics.py:0
+msgid "Only ics files are supported"
+msgstr "Sono supportati solo i file ics"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__partner_id
+msgid "Partner"
+msgstr "Partner"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__do_remove_old_event
+msgid "Remove old events?"
+msgstr "Rimuovere i vecchi eventi?"
+
+#. module: calendar_import_ics
+#: model:ir.model.fields,field_description:calendar_import_ics.field_calendar_import_ics__import_start_date
+msgid "Start Import Date"
+msgstr "Data inizio importazione"
diff --git a/calendar_import_ics/models/__init__.py b/calendar_import_ics/models/__init__.py
new file mode 100644
index 00000000..ba757cbe
--- /dev/null
+++ b/calendar_import_ics/models/__init__.py
@@ -0,0 +1 @@
+from . import calendar_event
diff --git a/calendar_import_ics/models/calendar_event.py b/calendar_import_ics/models/calendar_event.py
new file mode 100644
index 00000000..5202f110
--- /dev/null
+++ b/calendar_import_ics/models/calendar_event.py
@@ -0,0 +1,9 @@
+# Copyright (C) 2024 - ForgeFlow S.L.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+from odoo import fields, models
+
+
+class CalendarEvent(models.Model):
+ _inherit = "calendar.event"
+ event_identifier = fields.Char("Event Id")
diff --git a/calendar_import_ics/pyproject.toml b/calendar_import_ics/pyproject.toml
new file mode 100644
index 00000000..4231d0cc
--- /dev/null
+++ b/calendar_import_ics/pyproject.toml
@@ -0,0 +1,3 @@
+[build-system]
+requires = ["whool"]
+build-backend = "whool.buildapi"
diff --git a/calendar_import_ics/readme/CONTRIBUTORS.md b/calendar_import_ics/readme/CONTRIBUTORS.md
new file mode 100644
index 00000000..7bd3452d
--- /dev/null
+++ b/calendar_import_ics/readme/CONTRIBUTORS.md
@@ -0,0 +1,4 @@
+- Arnau Cruz \
+
+Do not contact contributors directly about support or help with
+technical issues.
diff --git a/calendar_import_ics/readme/DESCRIPTION.md b/calendar_import_ics/readme/DESCRIPTION.md
new file mode 100644
index 00000000..b20ce9c3
--- /dev/null
+++ b/calendar_import_ics/readme/DESCRIPTION.md
@@ -0,0 +1,7 @@
+This module adds a new wizard that allows you to import .ics files into
+odoo calendar, importing events and the following attributes:
+
+- Summary
+- Start Date
+- End Date
+- UID
diff --git a/calendar_import_ics/readme/USAGE.md b/calendar_import_ics/readme/USAGE.md
new file mode 100644
index 00000000..2285010d
--- /dev/null
+++ b/calendar_import_ics/readme/USAGE.md
@@ -0,0 +1,11 @@
+To use this module, follow these steps:
+
+1. Navigate to the Calendar App.
+2. Go to Configuration.
+3. Select Import ICS File.
+
+When importing, you have two options:
+
+- Specify start and end dates to import events occurring within that
+ range.
+- Leave the date fields empty to import all events from the entire file.
diff --git a/calendar_import_ics/security/calendar_import_security.xml b/calendar_import_ics/security/calendar_import_security.xml
new file mode 100644
index 00000000..1ad29a88
--- /dev/null
+++ b/calendar_import_ics/security/calendar_import_security.xml
@@ -0,0 +1,7 @@
+
+
+
+ Calendar Import Ics
+
+
+
diff --git a/calendar_import_ics/security/ir.model.access.csv b/calendar_import_ics/security/ir.model.access.csv
new file mode 100644
index 00000000..cadc4160
--- /dev/null
+++ b/calendar_import_ics/security/ir.model.access.csv
@@ -0,0 +1,2 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_wizard_calendar_import_ics,access_wizard_calendar_import_ics,model_calendar_import_ics,calendar_import_ics.group_calendar_import,1,1,1,1
diff --git a/calendar_import_ics/static/description/icon.png b/calendar_import_ics/static/description/icon.png
new file mode 100644
index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d
GIT binary patch
literal 9455
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
z
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
Xviia!U7SGha1wx#SCgwmn*{w2TRX*I
literal 0
HcmV?d00001
diff --git a/calendar_import_ics/static/description/index.html b/calendar_import_ics/static/description/index.html
new file mode 100644
index 00000000..2d45f67d
--- /dev/null
+++ b/calendar_import_ics/static/description/index.html
@@ -0,0 +1,454 @@
+
+
+
+
+
+README.rst
+
+
+
+
+
+
+
+
+
+
+
Calendar - Import ics
+
+

+
This module adds a new wizard that allows you to import .ics files into
+odoo calendar, importing events and the following attributes:
+
+- Summary
+- Start Date
+- End Date
+- UID
+
+
Table of contents
+
+
+
+
To use this module, follow these steps:
+
+- Navigate to the Calendar App.
+- Go to Configuration.
+- Select Import ICS File.
+
+
When importing, you have two options:
+
+- Specify start and end dates to import events occurring within that
+range.
+- Leave the date fields empty to import all events from the entire file.
+
+
+
+
+
Bugs are tracked on GitHub Issues.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us to smash it by providing a detailed and welcomed
+feedback.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
Do not contact contributors directly about support or help with
+technical issues.
+
+
+
+
This module is maintained by the OCA.
+
+
+
+
OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
This module is part of the OCA/calendar project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
+
diff --git a/calendar_import_ics/tests/__init__.py b/calendar_import_ics/tests/__init__.py
new file mode 100644
index 00000000..6bf23ec2
--- /dev/null
+++ b/calendar_import_ics/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_calendar_import_ics
diff --git a/calendar_import_ics/tests/sample_files/test_calendar.ics b/calendar_import_ics/tests/sample_files/test_calendar.ics
new file mode 100644
index 00000000..e74ae53b
--- /dev/null
+++ b/calendar_import_ics/tests/sample_files/test_calendar.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+PRODID:-//Fastmail/2020.5/EN
+X-APPLE-CALENDAR-COLOR:#3A429C
+X-WR-CALNAME:_CE
+X-WR-TIMEZONE:US/Pacific
+BEGIN:VEVENT
+DTEND:20061010T210000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061010T200000Z
+SEQUENCE:0
+SUMMARY:Event 1 Test
+TRANSP:OPAQUE
+UID:ed27f2b89f945c7692547a2903c20cbe80de6cc6
+END:VEVENT
+BEGIN:VEVENT
+DTEND;TZID=America/Denver:20061011T190000
+DTSTAMP:20240221T094129Z
+DTSTART;TZID=America/Denver:20061011T160000
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:4b1a0cb2081f6b3243bfdf191c985fe86237344c
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20061012T020000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061012T010000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:06b1c17713eca80e7219e8278330a4e9604f12e3
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20061014T000000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061013T150000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:3275dc6275325a10b715008ee2713808de24e516
+END:VEVENT
+END:VCALENDAR
diff --git a/calendar_import_ics/tests/sample_files/test_calendar_2.ics b/calendar_import_ics/tests/sample_files/test_calendar_2.ics
new file mode 100644
index 00000000..cb06394d
--- /dev/null
+++ b/calendar_import_ics/tests/sample_files/test_calendar_2.ics
@@ -0,0 +1,45 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+PRODID:-//Fastmail/2020.5/EN
+X-APPLE-CALENDAR-COLOR:#3A429C
+X-WR-CALNAME:_CE
+X-WR-TIMEZONE:US/Pacific
+BEGIN:VEVENT
+DTEND:20061010T220000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061010T210000Z
+SEQUENCE:0
+SUMMARY:Event 1 Test Renamed
+TRANSP:OPAQUE
+UID:ed27f2b89f945c7692547a2903c20cbe80de6cc6
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20061011T190000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061011T160000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:4b1a0cb2081f6b3243bfdf191c985fe86237344c
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20061012T020000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061012T010000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:06b1c17713eca80e7219e8278330a4e9604f12e3
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20061014T000000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061013T150000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:3275dc6275325a10b715008ee2713808de24e516
+END:VEVENT
+END:VCALENDAR
diff --git a/calendar_import_ics/tests/sample_files/test_calendar_3.ics b/calendar_import_ics/tests/sample_files/test_calendar_3.ics
new file mode 100644
index 00000000..def817d9
--- /dev/null
+++ b/calendar_import_ics/tests/sample_files/test_calendar_3.ics
@@ -0,0 +1,36 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+CALSCALE:GREGORIAN
+METHOD:PUBLISH
+PRODID:-//Fastmail/2020.5/EN
+X-APPLE-CALENDAR-COLOR:#3A429C
+X-WR-CALNAME:_CE
+X-WR-TIMEZONE:US/Pacific
+BEGIN:VEVENT
+DTEND:20061011T190000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20061011T160000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:4b1a0cb2081f6b3243bfdf191c985fe86237344c
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20221012T020000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20221012T010000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:06b1c17713eca80e7219e8278330a4e9604f12e3
+END:VEVENT
+BEGIN:VEVENT
+DTEND:20221014T000000Z
+DTSTAMP:20240221T094129Z
+DTSTART:20221013T150000Z
+SEQUENCE:0
+SUMMARY:Unavailable
+TRANSP:OPAQUE
+UID:3275dc6275325a10b715008ee2713808de24e516
+END:VEVENT
+END:VCALENDAR
diff --git a/calendar_import_ics/tests/test_calendar_import_ics.py b/calendar_import_ics/tests/test_calendar_import_ics.py
new file mode 100644
index 00000000..58331d56
--- /dev/null
+++ b/calendar_import_ics/tests/test_calendar_import_ics.py
@@ -0,0 +1,80 @@
+# Copyright (C) 2024 - ForgeFlow S.L.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import base64
+from datetime import datetime
+
+from odoo.exceptions import ValidationError
+from odoo.tests.common import TransactionCase
+from odoo.tools.misc import file_path
+
+
+class TestImportIcs(TransactionCase):
+ def setUp(self):
+ super().setUp()
+ self.event_model = self.env["calendar.event"]
+ self.import_wiz = self.env["calendar.import.ics"]
+
+ @classmethod
+ def _get_test_file(cls, file_name):
+ path = file_path(f"calendar_import_ics/tests/sample_files/{file_name}")
+ with open(path, "rb") as file:
+ return base64.encodebytes(file.read())
+
+ def test_import_ics(self):
+ events_before_imp = self.event_model.search([])
+ filename = "test_calendar.ics"
+ wiz = self.import_wiz.create(
+ {
+ "import_ics_file": self._get_test_file(filename),
+ "import_ics_filename": filename,
+ }
+ )
+ wiz.button_import()
+ events_after_imp = self.event_model.search([])
+ self.assertEqual(len(events_after_imp) - len(events_before_imp), 4)
+ uid = "ed27f2b89f945c7692547a2903c20cbe80de6cc6"
+ start_date = datetime(2006, 10, 10, 20, 00, 00)
+ end_date = datetime(2006, 10, 10, 21, 00, 00)
+ name = "Event 1 Test"
+ event_1 = self.event_model.search([("event_identifier", "=", uid)])
+ self.assertEqual(event_1.event_identifier, uid)
+ self.assertEqual(event_1.start, start_date)
+ self.assertEqual(event_1.stop, end_date)
+ self.assertEqual(event_1.name, name)
+ filename = "test_calendar_2.ics"
+ wiz = self.import_wiz.create(
+ {
+ "import_ics_file": self._get_test_file(filename),
+ "import_ics_filename": filename,
+ }
+ )
+ wiz.button_import()
+ event_1 = self.event_model.search([("event_identifier", "=", uid)])
+ start_date = datetime(2006, 10, 10, 21, 00, 00)
+ end_date = datetime(2006, 10, 10, 22, 00, 00)
+ name = "Event 1 Test Renamed"
+ self.assertEqual(event_1.event_identifier, uid)
+ self.assertEqual(event_1.start, start_date)
+ self.assertEqual(event_1.stop, end_date)
+ self.assertEqual(event_1.name, name)
+ filename = "test_calendar_3.ics"
+ wiz = self.import_wiz.create(
+ {
+ "import_ics_file": self._get_test_file(filename),
+ "import_ics_filename": filename,
+ "import_start_date": datetime(2004, 10, 10),
+ "import_end_date": datetime(2044, 10, 10),
+ }
+ )
+ wiz.button_import()
+ event_1 = self.event_model.search([("event_identifier", "=", uid)])
+ self.assertFalse(event_1)
+ wiz = self.import_wiz.create(
+ {
+ "import_ics_file": self._get_test_file(filename),
+ "import_ics_filename": "random_name.xslx",
+ }
+ )
+ with self.assertRaises(ValidationError):
+ wiz.button_import()
diff --git a/calendar_import_ics/wizards/__init__.py b/calendar_import_ics/wizards/__init__.py
new file mode 100644
index 00000000..9f9358b9
--- /dev/null
+++ b/calendar_import_ics/wizards/__init__.py
@@ -0,0 +1 @@
+from . import wizard_import_ics
diff --git a/calendar_import_ics/wizards/wizard_import_ics.py b/calendar_import_ics/wizards/wizard_import_ics.py
new file mode 100644
index 00000000..bdd17331
--- /dev/null
+++ b/calendar_import_ics/wizards/wizard_import_ics.py
@@ -0,0 +1,136 @@
+# Copyright (C) 2024 - ForgeFlow S.L.
+# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
+
+import base64
+from datetime import datetime
+
+import pytz
+from dateutil import parser
+
+from odoo import _, fields, models
+from odoo.exceptions import ValidationError
+
+
+class CalendarImportIcs(models.TransientModel):
+ """
+ This wizard is used to import ics files to calendar
+ """
+
+ _name = "calendar.import.ics"
+ _description = "Calendar Import Ics"
+
+ import_ics_file = fields.Binary(required=True)
+ import_ics_filename = fields.Char()
+ import_start_date = fields.Date("Start Import Date")
+ import_end_date = fields.Date("End Import Date")
+ partner_id = fields.Many2one("res.partner", string="Partner")
+ do_remove_old_event = fields.Boolean(
+ string="Remove old events?",
+ help="If checked, the previously imported events "
+ "that are not in this import will be deleted",
+ default=True,
+ )
+
+ def button_import(self):
+ imported_events = []
+ self.ensure_one()
+ assert self.import_ics_file
+ extension = self.import_ics_filename.split(".")[1]
+ if extension != "ics":
+ raise ValidationError(_("Only ics files are supported"))
+ if self.env.user and not self.partner_id:
+ self.partner_id = self.env.user.partner_id.id
+ file_decoded = base64.b64decode(self.import_ics_file)
+ file_str = file_decoded.decode("utf-8")
+ lines = file_str.split("\n")
+ ics_event = {}
+ for line in lines:
+ if line.startswith(("DTSTART", "DTEND")) and "TZID=" in line:
+ line = self.convert_date_to_z(line)
+ if line.startswith("BEGIN:VEVENT"):
+ ics_event = {}
+ elif line.startswith("END:VEVENT"):
+ self._process_event(ics_event, imported_events)
+ else:
+ if ":" in line:
+ key, value = line.strip().split(":", 1)
+ ics_event[key] = value
+ if self.do_remove_old_event:
+ self._delete_non_imported_events(imported_events)
+
+ def _process_event(self, ics_event, imported_events):
+ if "DTSTART" in ics_event and "DTEND" in ics_event:
+ event_start_date = self._parse_date(ics_event["DTSTART"])
+ event_end_date = self._parse_date(ics_event["DTEND"])
+ if (not self.import_start_date or not self.import_end_date) or (
+ self.import_start_date <= event_start_date.date()
+ and self.import_end_date >= event_end_date.date()
+ ):
+ imported_events.append(ics_event["UID"])
+ event = self.env["calendar.event"].search(
+ [("event_identifier", "=", ics_event["UID"])]
+ )
+ if event:
+ self._update_event(
+ event, ics_event, event_start_date, event_end_date
+ )
+ else:
+ self._create_event(ics_event, event_start_date, event_end_date)
+
+ def _parse_date(self, date_str):
+ return datetime.strptime(date_str, "%Y%m%dT%H%M%SZ")
+
+ def _update_event(self, event, ics_event, event_start_date, event_end_date):
+ vals = {}
+ if event.start != event_start_date:
+ vals["start"] = event_start_date
+ if event.stop != event_end_date:
+ vals["stop"] = event_end_date
+ if event.name != ics_event["SUMMARY"]:
+ vals["name"] = ics_event["SUMMARY"]
+ if self.partner_id not in event.partner_ids:
+ vals["partner_ids"] = [(4, self.partner_id.id, 0)]
+ event.write(vals)
+
+ def _create_event(self, ics_event, event_start_date, event_end_date):
+ self.env["calendar.event"].create(
+ {
+ "start": event_start_date,
+ "stop": event_end_date,
+ "name": ics_event["SUMMARY"],
+ "event_identifier": ics_event["UID"],
+ "partner_ids": [(4, self.partner_id.id)],
+ }
+ )
+
+ def _delete_non_imported_events(self, imported_events):
+ domain = [
+ ("event_identifier", "!=", False),
+ ("event_identifier", "not in", imported_events),
+ ("partner_ids", "in", self.partner_id.id),
+ ]
+
+ if self.import_start_date:
+ domain.append(("start", ">=", self.import_start_date))
+
+ if self.import_end_date:
+ domain.append(("stop", "<=", self.import_end_date))
+
+ non_imported_events = self.env["calendar.event"].search(domain)
+ for non_imported_event in non_imported_events:
+ non_imported_event.write({"partner_ids": [(3, self.partner_id.id)]})
+ if not non_imported_events.partner_ids:
+ non_imported_events.unlink()
+
+ def convert_date_to_z(self, line):
+ split_parts = line.split(":")
+ event_phase = split_parts[0].split(";TZID=")[0]
+ tz_id = split_parts[0].split(";TZID=")[1]
+ date = split_parts[1]
+
+ date_obj = parser.parse(date)
+ tz = pytz.timezone(tz_id)
+
+ utc_date = tz.localize(date_obj).astimezone(pytz.UTC)
+ utc_date_string = utc_date.strftime(event_phase + ":%Y%m%dT%H%M%SZ")
+ return utc_date_string
diff --git a/calendar_import_ics/wizards/wizard_import_ics.xml b/calendar_import_ics/wizards/wizard_import_ics.xml
new file mode 100644
index 00000000..58747e7c
--- /dev/null
+++ b/calendar_import_ics/wizards/wizard_import_ics.xml
@@ -0,0 +1,39 @@
+
+
+
+ calendar.import.ics.form
+ calendar.import.ics
+
+
+
+
+
+ Import Ics File
+ calendar.import.ics
+ form
+ new
+
+
+
From 40c25e854e31f0180db687f23a08c0901ebad276 Mon Sep 17 00:00:00 2001
From: Milan Topuzov
Date: Sun, 19 Oct 2025 18:37:46 +0200
Subject: [PATCH 2/2] [MIG] calendar_import_ics: Migration to 19.0
---
calendar_import_ics/README.rst | 10 +++++-----
calendar_import_ics/__manifest__.py | 3 ++-
.../security/calendar_import_security.xml | 2 +-
calendar_import_ics/static/description/index.html | 6 +++---
calendar_import_ics/wizards/wizard_import_ics.py | 4 ++--
5 files changed, 13 insertions(+), 12 deletions(-)
diff --git a/calendar_import_ics/README.rst b/calendar_import_ics/README.rst
index e35b2baa..6e0fcaf3 100644
--- a/calendar_import_ics/README.rst
+++ b/calendar_import_ics/README.rst
@@ -21,13 +21,13 @@ Calendar - Import ics
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fcalendar-lightgray.png?logo=github
- :target: https://github.com/OCA/calendar/tree/18.0/calendar_import_ics
+ :target: https://github.com/OCA/calendar/tree/19.0/calendar_import_ics
:alt: OCA/calendar
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/calendar-18-0/calendar-18-0-calendar_import_ics
+ :target: https://translation.odoo-community.org/projects/calendar-19-0/calendar-19-0-calendar_import_ics
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
- :target: https://runboat.odoo-community.org/builds?repo=OCA/calendar&target_branch=18.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/calendar&target_branch=19.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -66,7 +66,7 @@ Bug Tracker
Bugs are tracked on `GitHub Issues `_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-`feedback `_.
+`feedback `_.
Do not contact contributors directly about support or help with technical issues.
@@ -99,6 +99,6 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
-This module is part of the `OCA/calendar `_ project on GitHub.
+This module is part of the `OCA/calendar `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/calendar_import_ics/__manifest__.py b/calendar_import_ics/__manifest__.py
index cc71fbf1..f097135e 100644
--- a/calendar_import_ics/__manifest__.py
+++ b/calendar_import_ics/__manifest__.py
@@ -4,7 +4,7 @@
{
"name": "Calendar - Import ics",
"summary": "Allow importing an ics file to our calendar",
- "version": "18.0.1.0.1",
+ "version": "19.0.1.0.0",
"author": "ForgeFlow S.L.,Odoo Community Association (OCA)",
"website": "https://github.com/OCA/calendar",
"license": "AGPL-3",
@@ -14,4 +14,5 @@
"security/ir.model.access.csv",
"wizards/wizard_import_ics.xml",
],
+ "installable": True,
}
diff --git a/calendar_import_ics/security/calendar_import_security.xml b/calendar_import_ics/security/calendar_import_security.xml
index 1ad29a88..e9cebd7e 100644
--- a/calendar_import_ics/security/calendar_import_security.xml
+++ b/calendar_import_ics/security/calendar_import_security.xml
@@ -2,6 +2,6 @@
Calendar Import Ics
-
+
diff --git a/calendar_import_ics/static/description/index.html b/calendar_import_ics/static/description/index.html
index 2d45f67d..6c78e2ef 100644
--- a/calendar_import_ics/static/description/index.html
+++ b/calendar_import_ics/static/description/index.html
@@ -374,7 +374,7 @@ Calendar - Import ics
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:0b76e943a6186b958123d04eef313d6d66e7b9361daf4122b045fbca02b88b23
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This module adds a new wizard that allows you to import .ics files into
odoo calendar, importing events and the following attributes:
@@ -416,7 +416,7 @@
Bugs are tracked on GitHub Issues.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
-feedback.
+feedback.
Do not contact contributors directly about support or help with technical issues.
@@ -444,7 +444,7 @@
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
-
This module is part of the OCA/calendar project on GitHub.
+
This module is part of the OCA/calendar project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/calendar_import_ics/wizards/wizard_import_ics.py b/calendar_import_ics/wizards/wizard_import_ics.py
index bdd17331..49fc129d 100644
--- a/calendar_import_ics/wizards/wizard_import_ics.py
+++ b/calendar_import_ics/wizards/wizard_import_ics.py
@@ -7,7 +7,7 @@
import pytz
from dateutil import parser
-from odoo import _, fields, models
+from odoo import fields, models
from odoo.exceptions import ValidationError
@@ -37,7 +37,7 @@ def button_import(self):
assert self.import_ics_file
extension = self.import_ics_filename.split(".")[1]
if extension != "ics":
- raise ValidationError(_("Only ics files are supported"))
+ raise ValidationError(self.env._("Only ics files are supported"))
if self.env.user and not self.partner_id:
self.partner_id = self.env.user.partner_id.id
file_decoded = base64.b64decode(self.import_ics_file)