Skip to content
Draft
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a6b1a6d
added volunteer_company_holiday, extended recurrent_generator, draft …
Genepi314 Feb 19, 2026
fe0dfa4
cancel holiday shift operational, still lacks company implementation,…
Genepi314 Feb 20, 2026
79748ee
commit before switching branch
Genepi314 Feb 23, 2026
e7198ae
draft test_cancel_holiday_shift with company + correct README
Genepi314 Feb 24, 2026
acc87b7
Add models and views for volunteer leaves, volunteer leave types, and…
Genepi314 Mar 3, 2026
a584534
Refacto _cancel_holiday_shift, now using a dictionary and filters by …
Genepi314 Mar 3, 2026
130eeb9
Add tracking to is_maintained_during_holiday (bool) in generator.
Genepi314 Mar 3, 2026
5218d91
Add freezegun for test_company_holiday
Genepi314 Mar 4, 2026
642f6cf
Syntax and decorator fixes in volunteer_company_holidayù
Genepi314 Mar 4, 2026
be54c46
Add company for volunteer_leave, record rules for all models, row com…
Genepi314 Mar 4, 2026
57a9f43
Add cron and function to cancel volunteer participations during their…
Genepi314 Mar 5, 2026
5e83781
small fix _cancel_volunteer_leave_participation().
Genepi314 Mar 5, 2026
e260401
Add test for _cancel_volunteer_leave_participation().
Genepi314 Mar 5, 2026
8488f36
Add demo data for company holiday, volunteer leave and volunteer leav…
Genepi314 Mar 9, 2026
15a6db1
draft new function to uncancel shifts in volunteer_company_holiday (f…
Genepi314 Mar 10, 2026
e9688bd
removed copy of method in volunteer_leave, add config for notificatio…
Genepi314 Mar 12, 2026
8b1dc76
Add method to send notification for end of volunteer leave, add confi…
Genepi314 Mar 17, 2026
6992be1
add badge on shift form view if on holiday, models volunteer_shift an…
Genepi314 Mar 21, 2026
432576c
Changed time range to 12 months, for demonstration purpose.
Genepi314 Mar 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions setup/volunteer_holiday/odoo/addons/volunteer_holiday
6 changes: 6 additions & 0 deletions setup/volunteer_holiday/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
62 changes: 62 additions & 0 deletions volunteer_holiday/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
=================
Volunteer Holiday
=================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:d68150a24904527787d7cde7a5cb68c88f101992b43ec7c25f9a3450eb8d6184
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-beescoop%2FObeesdoo-lightgray.png?logo=github
:target: https://github.com/beescoop/Obeesdoo/tree/16.0/volunteer_holiday
:alt: beescoop/Obeesdoo

|badge1| |badge2| |badge3|

Add volunteer and company holidays to volunteer app, and manage shifts and generators accordingly.

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/beescoop/Obeesdoo/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/beescoop/Obeesdoo/issues/new?body=module:%20volunteer_holiday%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
~~~~~~~

* Coop IT Easy SC

Contributors
~~~~~~~~~~~~

* `Coop IT Easy SC <https://coopiteasy.be>`_:

* Geneviève Ernould

Maintainers
~~~~~~~~~~~

This module is part of the `beescoop/Obeesdoo <https://github.com/beescoop/Obeesdoo/tree/16.0/volunteer_holiday>`_ project on GitHub.

You are welcome to contribute.
5 changes: 5 additions & 0 deletions volunteer_holiday/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SPDX-FileCopyrightText: 2025 Coop IT Easy SC
#
# SPDX-License-Identifier: AGPL-3.0-or-later

from . import models
31 changes: 31 additions & 0 deletions volunteer_holiday/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# SPDX-FileCopyrightText: 2026 Coop IT Easy SC
#
# SPDX-License-Identifier: AGPL-3.0-or-later

{
"name": "Volunteer Holiday",
"summary": "Add holidays for companies and volunteers",
"version": "16.0.0.5.0",
"category": "Volunteer management",
"website": "https://github.com/beescoop/Obeesdoo",
"author": "Coop IT Easy SC",
"license": "AGPL-3",
"application": False,
"depends": ["base", "volunteer", "mail"],
"data": [
"data/cron.xml",
"security/ir.model.access.csv",
"security/volunteer_security.xml",
"views/volunteer_shift_generator_views.xml",
"views/volunteer_company_holiday_view.xml",
"views/volunteer_volunteer_view.xml",
"views/volunteer_volunteer_leave_type_view.xml",
"views/volunteer_volunteer_leave_view.xml",
"views/volunteer_menu.xml",
],
"demo": [
"demo/volunteer_company_holiday_demo.xml",
"demo/volunteer_volunteer_leave_type_demo.xml",
"demo/volunteer_volunteer_leave_demo.xml",
],
}
1 change: 1 addition & 0 deletions volunteer_holiday/controllers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import controllers
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should remove the controllers directory if your have nothing more than the __init__.py file in it.

31 changes: 31 additions & 0 deletions volunteer_holiday/data/cron.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
SPDX-FileCopyrightText: 2025 Coop IT Easy SC

SPDX-License-Identifier: AGPL-3.0-or-later
-->
<odoo>
<!-- Après test, passer en noupdate=1 -->
<record id="ir_cron_cancel_holiday_shift" model="ir.cron">
<field name="name">Cancel Generated Shifts During Company Holidays</field>
<field name="model_id" ref="model_volunteer_company_holiday" />
<field name="state">code</field>
<field name="code">model._cancel_holiday_shift()</field>
<field name="interval_number">24</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
</record>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apply your comment here. :)


<record id="ir_cron_cancel_volunteer_leave_participation" model="ir.cron">
<field name="name">Cancel Participations During Volunteer Leaves</field>
<field name="model_id" ref="model_volunteer_volunteer_leave" />
<field name="state">code</field>
<field name="code">model._cancel_volunteer_leave_participation()</field>
<field name="interval_number">24</field>
<field name="interval_type">hours</field>
<field name="numbercall">-1</field>
<field name="doall" eval="False" />
</record>

</odoo>
37 changes: 37 additions & 0 deletions volunteer_holiday/demo/volunteer_company_holiday_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
SPDX-FileCopyrightText: 2025 Coop IT Easy SC

SPDX-License-Identifier: AGPL-3.0-or-later
-->
<odoo>
<record
id="volunteer_company_holiday_demo_christmas"
model="volunteer.company.holiday"
>
<field name="name">Christmas Holidays</field>
<field
name="start_date"
eval="(datetime.today().replace(month=12, day=24)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting with python 3.9 (odoo 16.0 uses 3.10), prefer using isformat() instead of strftime for standard iso formatting.

See method here.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After some tests on these cases, neither strftime() nor isoformat() seems to be required. On my side, loading works fine without any formatting. That said, I'm not sure of all the details behind it.

/>
<field
name="end_date"
eval="(datetime.today().replace(month=12, day=25)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat()

/>
</record>

<record
id="volunteer_company_holiday_demo_store_inventory"
model="volunteer.company.holiday"
>
<field name="name">Store Inventory</field>
<field
name="start_date"
eval="(datetime.today().replace(month=7, day=1)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
<field
name="end_date"
eval="(datetime.today().replace(month=7, day=7)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
</record>
</odoo>
42 changes: 42 additions & 0 deletions volunteer_holiday/demo/volunteer_volunteer_leave_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
SPDX-FileCopyrightText: 2025 Coop IT Easy SC

SPDX-License-Identifier: AGPL-3.0-or-later
-->
<odoo>
<record
id="volunteer_volunteer_leave_demo_vol_a_leave"
model="volunteer.volunteer.leave"
>
<field name="volunteer_id" ref="volunteer.volunteer_demo_a" />
<field name="type_id" ref="volunteer_volunteer_leave_type_demo_vacation" />
<field
name="start_date"
eval="(datetime.now().date() + timedelta(weeks=4, days=4)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
<field
name="end_date"
eval="(datetime.now().date() + timedelta(weeks=4, days=14)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
</record>

<record
id="volunteer_volunteer_leave_demo_vol_c_leave"
model="volunteer.volunteer.leave"
>
<field name="volunteer_id" ref="volunteer.volunteer_demo_c" />
<field
name="type_id"
ref="volunteer_volunteer_leave_type_demo_parental_leave"
/>
<field
name="start_date"
eval="(datetime.now().date() + timedelta(days=4)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
<field
name="end_date"
eval="(datetime.now().date() + timedelta(weeks=9, days=4)).strftime('%Y-%m-%d')"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isoformat

/>
</record>
</odoo>
31 changes: 31 additions & 0 deletions volunteer_holiday/demo/volunteer_volunteer_leave_type_demo.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
SPDX-FileCopyrightText: 2025 Coop IT Easy SC

SPDX-License-Identifier: AGPL-3.0-or-later
-->
<odoo>
<record
id="volunteer_volunteer_leave_type_demo_sick_leave"
model="volunteer.volunteer.leave.type"
>
<field name="name">Sick Leave</field>
<field name="description">Leave due to illness</field>
</record>

<record
id="volunteer_volunteer_leave_type_demo_parental_leave"
model="volunteer.volunteer.leave.type"
>
<field name="name">Parental Leave</field>
<field name="description">Leave due to pregnancy/childcare</field>
</record>

<record
id="volunteer_volunteer_leave_type_demo_vacation"
model="volunteer.volunteer.leave.type"
>
<field name="name">Vacation</field>
<field name="description">Vacation</field>
</record>
</odoo>
9 changes: 9 additions & 0 deletions volunteer_holiday/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2025 Coop IT Easy SC
#
# SPDX-License-Identifier: AGPL-3.0-or-later

from . import volunteer_company_holiday
from . import volunteer_shift_recurrent_generator
from . import volunteer_volunteer
from . import volunteer_volunteer_leave_type
from . import volunteer_volunteer_leave
111 changes: 111 additions & 0 deletions volunteer_holiday/models/volunteer_company_holiday.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# SPDX-FileCopyrightText: 2025 Coop IT Easy SC
#
# SPDX-License-Identifier: AGPL-3.0-or-later

from datetime import datetime

from dateutil.relativedelta import relativedelta

from odoo import api, fields, models


class VolunteerCompanyHoliday(models.Model):
_name = "volunteer.company.holiday"
_description = "Company Holidays"
_order = "start_date"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
_order = "start_date"
_order = "start_date desc, company_id, id"

Setting a default _order has a cost and it default to id. But I understand that most recent holliday should be shown on top (so use desc).

Also, start_date is not a unique field (several records can have the same value), so you need to specify other fields to order them correctly. Here in my suggestion, company_id is not needed, but id at the end is mandatory to be sure that we can correctly order all element.


# Fields

name = fields.Char(required=True)

company_id = fields.Many2one(
comodel_name="res.company",
string="Company",
default=lambda self: self.env.user.company_id,
required=True,
)

start_date = fields.Date(required=True)

end_date = fields.Date(required=True)

# Methods

# Retirer la limite de temps, regarder les shifts futurs
def _cancel_holiday_shift(self, time_in_months=3):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that you should apply your comment, and remove the time limit. Just check all shifts, and see if they are overlapping a holiday.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that for now it’s ok if the method starts at now() and all future shifts.

Because, we don’t want to change the past.

"""Cancel shifts if they cover holiday period within time range"""
today_midnight = datetime.today().replace(
hour=0, minute=0, second=0, microsecond=0
)
# Setting the time range we want to work with, here 3 months
date_time_range = today_midnight + relativedelta(months=time_in_months)

confirmed_future_generated_shifts_in_range = (
self.env["volunteer.shift"]
.sudo()
.search(
[
("state", "=", "confirmed"),
("start_time", ">=", today_midnight),
("start_time", "<=", date_time_range),
("generator_id", "!=", None),
("generator_id.is_maintained_during_holiday", "=", False),
]
)
)

future_company_holidays_in_range = (
self.env["volunteer.company.holiday"]
.sudo()
.search(
[
("start_date", ">=", today_midnight),
("start_date", "<=", date_time_range.date()),
]
)
)

# Create dictionary key-company for list of values-shifts
shifts_by_company = {}
for shift in confirmed_future_generated_shifts_in_range:
shifts_by_company.setdefault(shift.company_id, []).append(shift)
Comment on lines +79 to +82
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just order them by company_id when using the search() method.

Also, as there is less holiday than shifts, it’s better to do so with holiday, or even fetch holiday for the company when needed.


for holiday in future_company_holidays_in_range:
same_company_shifts = shifts_by_company.get(holiday.company_id, [])
for shift in same_company_shifts:
if shift.state != "canceled" and self._shift_covers_holiday(
shift.start_time,
shift.end_time,
holiday.start_date,
holiday.end_date,
):
shift.sudo().write(
{
"stage_id": self.env.ref(
"volunteer.volunteer_shift_stage_canceled"
).id
}
)
Comment on lines +84 to +99
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If your shifts are ordered by company_id, you can use itertools.groupby() to iterate through all the shifts, company by company. Then you can fetch the holiday of the comany, each time you face a new company.

Using itertools.groupby() is more efficient for such a task.


@api.model
def _shift_covers_holiday(
self, shift_start_time, shift_end_time, holiday_start_date, holiday_end_date
):
"""Compare two periods of time, return true if they overlap."""
shift_start_date = shift_start_time.date()
shift_end_date = shift_end_time.date()

return (
(
shift_start_date >= holiday_start_date
and shift_start_date <= holiday_end_date
)
or (
shift_end_date >= holiday_start_date
and shift_start_date <= holiday_end_date
)
or (
shift_start_date <= holiday_start_date
and shift_end_date >= holiday_end_date
)
)
13 changes: 13 additions & 0 deletions volunteer_holiday/models/volunteer_shift_recurrent_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# SPDX-FileCopyrightText: 2026 Coop IT Easy SC
#
# SPDX-License-Identifier: AGPL-3.0-or-later

from odoo import fields, models


class VolunteerShiftRecurrentGenerator(models.Model):
_inherit = "volunteer.shift.recurrent.generator"

is_maintained_during_holiday = fields.Boolean(
"Maintain During Holidays", tracking=True
)
Loading