From c6f54ae154778ab3f36394517b0a59da856d1f22 Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Fri, 5 Sep 2025 13:58:43 +0200 Subject: [PATCH 1/3] fix: Exclude micro overlap when counting remaining slots --- bookings/bookings/doctype/item_booking/item_booking.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/bookings/bookings/doctype/item_booking/item_booking.py b/bookings/bookings/doctype/item_booking/item_booking.py index 59996222..c7d7b1ce 100644 --- a/bookings/bookings/doctype/item_booking/item_booking.py +++ b/bookings/bookings/doctype/item_booking/item_booking.py @@ -1998,6 +1998,7 @@ def get_booking_count(item=None, starts_on=None, ends_on=None): ends_on = starts_on starts_on, ends_on = get_datetime(starts_on), get_datetime(ends_on) + assert starts_on and ends_on simultaneous_bookings_enabled = frappe.db.get_single_value( "Venue Settings", "enable_simultaneous_booking" @@ -2009,7 +2010,11 @@ def get_booking_count(item=None, starts_on=None, ends_on=None): else: capacity = 1 - events = _get_events(starts_on, ends_on, item=item_doc) + # Because the query is datetime-inclusive, exclude every result that: + # - ends on the start of the range + # - starts on the end of the range + one_sec = timedelta(seconds=1) + events = _get_events(starts_on + one_sec, ends_on - one_sec, item=item_doc) current = len(events) return {"capacity": capacity, "current": current, "remaining": capacity - current} -- GitLab From 287288661ca0d17d9cf4a4da7aa76530ef9368b9 Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Fri, 5 Sep 2025 13:59:22 +0200 Subject: [PATCH 2/3] chore: fmt --- .../doctype/item_booking/item_booking.py | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/bookings/bookings/doctype/item_booking/item_booking.py b/bookings/bookings/doctype/item_booking/item_booking.py index c7d7b1ce..6a12d06c 100644 --- a/bookings/bookings/doctype/item_booking/item_booking.py +++ b/bookings/bookings/doctype/item_booking/item_booking.py @@ -151,6 +151,7 @@ class ExceptionTooManyBookings(ExceptionBookingOverlap): exc=cls, ) + class ItemBooking(Document): # begin: auto-generated types # This code is auto-generated. Do not modify anything in this block. @@ -383,7 +384,7 @@ class ItemBooking(Document): return # Preserve title of events pulled from Google Calendar - if getattr(self.flags, 'pulled_from_google_calendar', False) and self.title: + if getattr(self.flags, "pulled_from_google_calendar", False) and self.title: return if not self.title: @@ -427,7 +428,7 @@ class ItemBooking(Document): child.item = item child.booking_resource = frappe.db.get_value("Booking Resource", dict(item=item, disabled=False)) child.parent_item_booking = self.name - child.flags.ignore_permissions = True # If user is able to registered the parent booking it should be able to register the child bookings + child.flags.ignore_permissions = True # If user is able to registered the parent booking it should be able to register the child bookings child.save() if frappe.db.exists("Product Bundle", dict(new_item_code=self.item, disabled=False)): @@ -1296,7 +1297,9 @@ def _get_subscriptions_as_events(start, end, item=None, user=None, fields=None, subscriptions = _get_booking_subscriptions_between( start, end, item=item, user=user, fields=fields, filters=filters ) - display_subscriptions = bool(frappe.db.get_single_value("Venue Settings", "display_subscriptions_in_calendar")) + display_subscriptions = bool( + frappe.db.get_single_value("Venue Settings", "display_subscriptions_in_calendar") + ) events = [] for sub in subscriptions: @@ -1332,7 +1335,7 @@ def _get_subscriptions_as_events(start, end, item=None, user=None, fields=None, "all_day": 1, "startEditable": False, "durationEditable": False, - "display": 'auto' if display_subscriptions else 'none' + "display": "auto" if display_subscriptions else "none", } ) ) @@ -1807,7 +1810,9 @@ def insert_event_in_google_calendar(doc, method=None): if ( not doc.sync_with_google_calendar or doc.flags.pulled_from_google_calendar - or not frappe.db.exists("Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1}) + or not frappe.db.exists( + "Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1} + ) ): return @@ -1849,7 +1854,9 @@ def update_event_in_google_calendar(doc, method=None): doc.flags.pulled_from_google_calendar or doc.modified == doc.creation or not doc.sync_with_google_calendar - or not frappe.db.exists("Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1}) + or not frappe.db.exists( + "Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1} + ) ): return @@ -1899,7 +1906,9 @@ def delete_event_in_google_calendar(doc, method=None): not doc.google_calendar_event_id or doc.flags.pulled_from_google_calendar or not doc.sync_with_google_calendar - or not frappe.db.exists("Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1}) + or not frappe.db.exists( + "Google Calendar", {"name": doc.google_calendar, "push_to_google_calendar": 1} + ) ): return @@ -1955,12 +1964,13 @@ def move_booking_with_event(doc, method): "server_action": "erpnext.venue.doctype.item_booking.item_booking.offset_linked_bookings", "args": {"event": doc.name, "offset": days}, "hide_on_success": True, - "label": _("Offset bookings") - } + "label": _("Offset bookings"), + }, ) + def offset_linked_bookings(args): - parsed_args: dict = frappe.parse_json(args) # type: ignore + parsed_args: dict = frappe.parse_json(args) # type: ignore event = parsed_args.get("event") offset = parsed_args.get("offset") or 0 @@ -1973,17 +1983,19 @@ def offset_linked_bookings(args): for booking in bookings: try: - doc: ItemBooking = frappe.get_doc("Item Booking", booking.name) # type: ignore + doc: ItemBooking = frappe.get_doc("Item Booking", booking.name) # type: ignore doc.starts_on = add_to_date(booking.starts_on, days=offset, as_datetime=True) doc.ends_on = add_to_date(booking.ends_on, days=offset, as_datetime=True) doc.flags.ignore_permissions = True doc.save() except ExceptionTooManyBookings: frappe.msgprint( - msg=_("Booking for {0} ({1}) could not be offset because it overlaps with existing bookings on the same slot").format(doc.booking_resource, doc.name), + msg=_( + "Booking for {0} ({1}) could not be offset because it overlaps with existing bookings on the same slot" + ).format(doc.booking_resource, doc.name), alert=True, indicator="red", - realtime=True + realtime=True, ) -- GitLab From 222da21503c5a2aaf5df67256531da7e4ed4241d Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Fri, 5 Sep 2025 13:59:28 +0200 Subject: [PATCH 3/3] chore: Fix types --- front/src/types.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/front/src/types.ts b/front/src/types.ts index feb37824..5c5b2d0f 100644 --- a/front/src/types.ts +++ b/front/src/types.ts @@ -52,12 +52,13 @@ export interface booking_resource_t { _perms?: permissions_t; } export interface booking_resource_slot_t { - "start": string - "end": string - "id": string - "status": "available" | "selected" | "confirmed" - "number": number - "total_available": number + start: string + end: string + id: string + status: "available" | "selected" | "confirmed" + remaining: number + number: number + total_available: number } export interface book_resource_param_t { start: string | Date -- GitLab