Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
97 changes: 97 additions & 0 deletions spec/booking_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,103 @@ module PlaceOS::Model
deleted_booking.not_nil!.deleted.should be_true
end

it "expands a cancelled series as cancelled occurrences only when include_deleted is set" do
tenant_id = Generator.tenant.id.not_nil!
user_email = "steve@place.tech"

parent_booking = Booking.new(
booking_type: "desk",
asset_ids: ["desk1"],
booking_start: 1.day.from_now.at_beginning_of_day.to_unix + 9.hours.total_seconds.to_i64,
booking_end: 1.day.from_now.at_beginning_of_day.to_unix + 10.hours.total_seconds.to_i64,
timezone: "UTC",
user_email: PlaceOS::Model::Email.new(user_email),
user_name: "Steve",
booked_by_email: PlaceOS::Model::Email.new(user_email),
booked_by_name: "Steve",
tenant_id: tenant_id,
booked_by_id: "user-1234",
history: [] of Booking::History,
recurrence_type: Booking::Recurrence::DAILY,
recurrence_days: 0b1111111,
recurrence_interval: 1
).save!

start_time = 1.day.from_now.at_beginning_of_day
end_time = start_time + 5.days
occurrences = parent_booking.calculate_daily(start_time, end_time).instances.map(&.to_unix)
occurrences.size.should eq 5

# an occurrence with its own override, checked in before the series was cancelled
checked_in_at = occurrences[1]
BookingInstance.new(
id: parent_booking.id.not_nil!,
instance_start: checked_in_at,
tenant_id: tenant_id,
booking_start: checked_in_at,
booking_end: checked_in_at + 1.hour.total_seconds.to_i64,
checked_in: true,
checked_in_at: checked_in_at,
).save!

parent_booking.deleted = true
parent_booking.deleted_at = Time.utc.to_unix
parent_booking.save!

# without include_deleted the cancelled series stays a single, unexpanded row
hidden = Booking.expand_bookings!(start_time, end_time, [parent_booking], include_deleted: false).bookings
hidden.map(&.instance).should eq [nil]

expanded = Booking.expand_bookings!(start_time, end_time, [parent_booking], include_deleted: true).bookings
expanded.compact_map(&.instance).sort!.should eq occurrences
expanded.all?(&.deleted).should be_true
expanded.all? { |booking| booking.deleted_at == parent_booking.deleted_at }.should be_true

checked_in = expanded.find! { |booking| booking.instance == checked_in_at }
checked_in.checked_in.should be_true
end

it "hydrates an occurrence of a cancelled series as cancelled" do
tenant_id = Generator.tenant.id.not_nil!
user_email = "steve@place.tech"

parent_booking = Booking.new(
booking_type: "desk",
asset_ids: ["desk1"],
booking_start: 1.day.from_now.at_beginning_of_day.to_unix + 9.hours.total_seconds.to_i64,
booking_end: 1.day.from_now.at_beginning_of_day.to_unix + 10.hours.total_seconds.to_i64,
timezone: "UTC",
user_email: PlaceOS::Model::Email.new(user_email),
user_name: "Steve",
booked_by_email: PlaceOS::Model::Email.new(user_email),
booked_by_name: "Steve",
tenant_id: tenant_id,
booked_by_id: "user-1234",
history: [] of Booking::History,
recurrence_type: Booking::Recurrence::DAILY,
recurrence_days: 0b1111111,
recurrence_interval: 1
).save!

starting = parent_booking.booking_start.not_nil!
override = BookingInstance.new(
id: parent_booking.id.not_nil!,
instance_start: starting,
tenant_id: tenant_id,
booking_start: starting,
booking_end: starting + 1.hour.total_seconds.to_i64,
).save!
override.hydrate_booking(parent_booking).deleted.should be_false

parent_booking.deleted = true
parent_booking.deleted_at = Time.utc.to_unix
parent_booking.save!

hydrated = override.hydrate_booking(parent_booking)
hydrated.deleted.should be_true
hydrated.deleted_at.should eq parent_booking.deleted_at
end

it "sanitizes extension_data string values before saving", tags: "extension_data_sanitization" do
tenant_id = Generator.tenant.id
user_email = "test@place.tech"
Expand Down
9 changes: 6 additions & 3 deletions src/placeos-models/booking.cr
Original file line number Diff line number Diff line change
Expand Up @@ -849,8 +849,11 @@ module PlaceOS::Model
# Recurring booking expansion
# ===

def recurring_booking? : Bool
!recurrence_type.none? && !deleted && !rejected && instance.nil?
# a series whose occurrences `expand_bookings!` generates. With
# `include_deleted`, a cancelled series counts too, and every one of its
# occurrences comes back cancelled.
def recurring_booking?(include_deleted : Bool = false) : Bool
!recurrence_type.none? && (include_deleted || !deleted) && !rejected && instance.nil?
end

def recurring_instance? : Bool
Expand All @@ -874,7 +877,7 @@ module PlaceOS::Model
is_checked_out : Bool? = nil,
include_deleted : Bool = false,
) : ExpansionDetails
recurring = parents.select(&.recurring_booking?)
recurring = parents.select(&.recurring_booking?(include_deleted))
return ExpansionDetails.new(parents, 0, 0) if recurring.empty?
parent_ids = recurring.compact_map(&.id)
recurring.each { |booking| parents.delete booking }
Expand Down
7 changes: 5 additions & 2 deletions src/placeos-models/booking_instance.cr
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,11 @@ module PlaceOS::Model
instance.checked_in = self.checked_in
instance.checked_in_at = self.checked_in_at
instance.checked_out_at = self.checked_out_at
instance.deleted = self.deleted
instance.deleted_at = self.deleted_at
# an occurrence of a cancelled series is cancelled, whatever its override says
if self.deleted || !main.deleted
instance.deleted = self.deleted
instance.deleted_at = self.deleted_at
end
instance.process_state = self.process_state
# A non-empty extension data object is a complete snapshot for this
# occurrence and replaces the parent booking's extension data wholesale.
Expand Down
Loading