# frozen_string_literal: true # Services around slots module Slots; end # Maintain the cache of reserved places for a slot class Slots::PlacesCacheService class << self # @param slot [Slot] def refresh(slot) return if slot.nil? reservables = case slot.availability.available_type when 'machines' slot.availability.machines when 'training' slot.availability.trainings when 'space' slot.availability.spaces when 'event' Event.where(id: slot.availability.event.id) else Rails.logger.warn "[Slots::PlacesCacheService#update] Availability #{slot.availability_id} with unknown " \ "type #{slot.availability.available_type}" [nil] end browser = reservables.respond_to?(:find_each) ? :find_each : :each places = [] reservables.try(browser) do |reservable| reservations = Slots::ReservationsService.reservations(slot.slots_reservations, [reservable]) pending = Slots::ReservationsService.pending_reservations(slot.cart_item_reservation_slots.map(&:id), [reservable]) reserved_places = (reservations[:reservations].count || 0) + (pending[:reservations].count || 0) if slot.availability.available_type == 'event' reserved_places = if slot.availability.event.nb_total_places.nil? 0 else slot.availability.event.nb_total_places - slot.availability.event.nb_free_places end end places.push({ reservable_type: reservable.class.name, reservable_id: reservable.try(&:id), reserved_places: reserved_places, user_ids: reservations[:user_ids] + pending[:user_ids] }) end slot.update(places: places) end # @param slot [Slot] # @param reservable_type [String] # @param reservable_id [Number] # @param places [Number] # @param operation [Symbol] :+ OR :- def change_places(slot, reservable_type, reservable_id, places, operation = :+) return if slot.nil? ActiveRecord::Base.connection.execute <<-SQL.squish with reservable_places as ( select ('{'||index-1||',reserved_places}')::text[] as path ,(place->>'reserved_places')::decimal as reserved_places from slots ,jsonb_array_elements(places) with ordinality arr(place, index) where place->>'reservable_type' = '#{reservable_type}' and place->>'reservable_id' = '#{reservable_id}' and id = #{slot.id} ) update slots set places = jsonb_set(places, reservable_places.path, (reservable_places.reserved_places #{operation} #{places})::varchar::jsonb, true) from reservable_places where id = #{slot.id}; SQL end # @param slot [Slot] # @param reservable_type [String] # @param reservable_id [Number] # @param user_ids [Array] def remove_users(slot, reservable_type, reservable_id, user_ids) return if slot.nil? ActiveRecord::Base.connection.execute <<-SQL.squish with users as ( select ('{'||index-1||',user_ids}')::text[] as path ,place->>'user_ids' as user_ids from slots ,jsonb_array_elements(places) with ordinality arr(place, index) where place->>'reservable_type' = '#{reservable_type}' and place->>'reservable_id' = '#{reservable_id}' and id = #{slot.id} ), all_users as ( select (ids.id)::text::int as all_ids from users ,jsonb_array_elements(users.user_ids::jsonb) with ordinality ids(id, index) ), remaining_users as ( SELECT array_to_json(array(SELECT unnest(array_agg(all_users.all_ids)) EXCEPT SELECT unnest('{#{user_ids.to_s.gsub(/\]| |\[|/, '')}}'::int[])))::jsonb as ids from all_users ) update slots set places = jsonb_set(places, users.path, remaining_users.ids, false) from users, remaining_users where id = #{slot.id}; SQL end # @param slot [Slot] # @param reservable_type [String] # @param reservable_id [Number] # @param user_ids [Array] def add_users(slot, reservable_type, reservable_id, user_ids) return if slot.nil? ActiveRecord::Base.connection.execute <<-SQL.squish with users as ( select ('{'||index-1||',user_ids}')::text[] as path ,place->>'user_ids' as user_ids from slots ,jsonb_array_elements(places) with ordinality arr(place, index) where place->>'reservable_type' = '#{reservable_type}' and place->>'reservable_id' = '#{reservable_id}' and id = #{slot.id} ), all_users as ( select (ids.id)::text::int as all_ids from users ,jsonb_array_elements(users.user_ids::jsonb) with ordinality ids(id, index) ), new_users as ( SELECT array_to_json(array_cat(array_agg(all_users.all_ids), '{#{user_ids.to_s.gsub(/\]| |\[|/, '')}}'::int[]))::jsonb as ids from all_users ) update slots set places = jsonb_set(places, users.path, new_users.ids, false) from users, new_users where id = #{slot.id}; SQL end end end