Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bookings limit #96

Merged
merged 9 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
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 CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Changelog
[mamico]

- Remove Contributor from the package permissions map
- Add configurable simultaneous bookings limit for the same user.
[folix-01]


Expand Down
70 changes: 60 additions & 10 deletions src/redturtle/prenotazioni/adapters/booker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from datetime import timedelta
from DateTime import DateTime
from plone import api
from plone.memoize.instance import memoize
from random import choice
Expand All @@ -9,6 +10,7 @@
from redturtle.prenotazioni.adapters.slot import BaseSlot
from redturtle.prenotazioni.config import VERIFIED_BOOKING
from redturtle.prenotazioni.content.prenotazione import VACATION_TYPE
from redturtle.prenotazioni.exceptions import BookingsLimitExceded
from redturtle.prenotazioni.prenotazione_event import MovedPrenotazione
from redturtle.prenotazioni.utilities.dateutils import exceedes_date_limit
from six.moves.urllib.parse import parse_qs
Expand Down Expand Up @@ -45,6 +47,44 @@ def prenotazioni(self):
"""The prenotazioni context state view"""
return self.context.unrestrictedTraverse("@@prenotazioni_context_state") # noqa

def _validate_user_limit(self, fiscalcode):
"""Control if user did not exceed the limit yet

Args:
fiscalcode (str): User's fiscal code

Returns:
None

Raises:
BookingsLimitExceded: User exceeded limit
"""

if not self.context.max_bookings_allowed:
return

if len(self.get_future_bookings_by_fiscalcode(fiscalcode)) >= (
self.context.max_bookings_allowed
):
raise BookingsLimitExceded

def get_future_bookings_by_fiscalcode(self, fiscalcode):
"""Find all the future bookings registered for the same fiscalcode"""
result = []

for booking in api.content.find(
mamico marked this conversation as resolved.
Show resolved Hide resolved
portal_type="Prenotazione",
fiscalcode=fiscalcode,
path={"query": "/".join(self.context.getPhysicalPath())},
Date={"query": DateTime(), "range": "min"},
review_state={
"query": ("confirmed", "pending", "private"),
},
):
result.append(booking.getObject())

return result

def get_available_gate(self, booking_date, booking_expiration_date=None):
"""
Find which gate is free to serve this booking
Expand Down Expand Up @@ -101,28 +141,35 @@ def _create(self, data, duration=-1, force_gate=""):
gate = available_gate
else:
gate = force_gate

fiscalcode = data.get("fiscalcode", "").upper()
user = api.user.get_current()

if not fiscalcode:
fiscalcode = (
user.getProperty("fiscalcode", "") or user.getId() or ""
).upper() # noqa

mamico marked this conversation as resolved.
Show resolved Hide resolved
params["fiscalcode"] = fiscalcode

self._validate_user_limit(fiscalcode)

obj = api.content.create(
type="Prenotazione",
container=container,
booking_expiration_date=booking_expiration_date,
gate=gate,
**params
**params,
)

annotations = IAnnotations(obj)

annotations[VERIFIED_BOOKING] = False

if not api.user.is_anonymous() and not api.user.has_permission(
"Modify portal content", obj=container
):
user = api.user.get_current()
data_fiscalcode = getattr(obj, "fiscalcode", "") or ""
fiscalcode = data_fiscalcode.upper()
if not fiscalcode:
obj.fiscalcode = (
user.getProperty("fiscalcode", "") or user.getId() or ""
).upper() # noqa
elif user.hasProperty("fiscalcode") and fiscalcode:
if user.hasProperty("fiscalcode") and fiscalcode:
if (user.getProperty("fiscalcode") or "").upper() == fiscalcode:
logger.info("Booking verified: {}".format(obj.absolute_url()))
annotations[VERIFIED_BOOKING] = True
Expand Down Expand Up @@ -192,7 +239,10 @@ def move(self, booking, data):
data["booking_type"] = booking.getBooking_type()
conflict_manager = self.prenotazioni.conflict_manager
current_data = booking.getBooking_date()
current = {"booking_date": current_data, "booking_type": data["booking_type"]}
current = {
"booking_date": current_data,
"booking_type": data["booking_type"],
}
current_slot = conflict_manager.get_choosen_slot(current)
current_gate = getattr(booking, "gate", "")
exclude = {current_gate: [current_slot]}
Expand Down
8 changes: 3 additions & 5 deletions src/redturtle/prenotazioni/adapters/ical.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
# -*- coding: utf-8 -*-
import icalendar

from Acquisition import aq_inner
from plone.app.event.base import default_timezone
from plone.app.event.ical.exporter import ICalendarEventComponent
Expand All @@ -9,14 +7,14 @@
from plone.event.interfaces import IICalendar
from plone.event.interfaces import IICalendarEventComponent
from plone.registry.interfaces import IRegistry
from plone.stringinterp.interfaces import IStringSubstitution
from plone.stringinterp.interfaces import IContextWrapper
from plone.stringinterp.interfaces import IStringSubstitution
from redturtle.prenotazioni import _
from zope.component import getAdapter
from zope.component import getUtility
from zope.interface import implementer


from redturtle.prenotazioni import _
import icalendar


def construct_icalendar(context, bookings):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,10 @@ def user_can_search(self):
"""States if the user can see the search button"""
return self.user_can_manage

@property
def user_exeed_limit(self):
mamico marked this conversation as resolved.
Show resolved Hide resolved
"""Check if user did not exceed the bookings limit"""

@property
@memoize
def booker(self):
Expand Down
5 changes: 3 additions & 2 deletions src/redturtle/prenotazioni/browser/prenotazioni_search.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from DateTime import DateTime
import json
import base64

# from ZPublisher.Iterators import filestream_iterator
from io import BytesIO
Expand Down Expand Up @@ -33,6 +31,9 @@
from zope.schema import ValidationError
from zope.schema.interfaces import IVocabularyFactory

import base64
import json


class InvalidDate(ValidationError):
__doc__ = _("invalid_end:search_date", "Invalid start or end date")
Expand Down
12 changes: 12 additions & 0 deletions src/redturtle/prenotazioni/content/prenotazioni_folder.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,18 @@ def data_validation(data):
required=False,
)

max_bookings_allowed = schema.Int(
title=_(
"max_bookings_allowed_label",
default="Maximum bookings number allowed",
),
description=_(
"max_bookings_allowed_description",
default="Number of simultaneous bookins allowed for the same user.",
),
required=False,
)

model.fieldset(
"dates",
label=_("Date validità"),
Expand Down
2 changes: 2 additions & 0 deletions src/redturtle/prenotazioni/exceptions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
from .booker import BookingsLimitExceded # noqa
9 changes: 9 additions & 0 deletions src/redturtle/prenotazioni/exceptions/booker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
class BookerException(Exception):
pass


class BookingsLimitExceded(BookerException):
def __init__(self, *args, **kwargs):
if "message" not in kwargs.keys():
kwargs["message"] = "Booking limit is exceed for the current user"
Loading