class Property include Mongoid::Document include Mongoid::Timestamps include OrbitTag::Taggable include OrbitCategory::Categorizable include Slug FIELDSNAME=["hiring_person_email","hiring_person_number","hiring_person_name","reason_for_hire","note_for_hire","organization" ,"person_in_charge" ,"tel_of_person_in_charge" ,"department" ,"contact_person" ,"tel_of_contact_person" , "mobile_phone_of_contact_person" ,"contact_person_Email" ,"contact_person_department"] field :need_change_tmp_reason, type: Boolean, default: false field :need_hire_before, type: Integer, default: 0 #0代表沒有限制 field :need_hire_before_unit, type: String, default: "day" #month, day, hour, minute field :custom_calendar_type, type: Integer, default: 0 #0=>預設, 1=> 顯示, 2=> 不顯示 field :custom_carousel_image_width, type: String, default: "" field :display_img, :type => Boolean, :default => false field :image_display_class, type: String, default: "full-size-img" #3 choices: full-size-img , pull-left , pull-right field :order_position, type: Integer, default: -1 field :title, as: :slug_title, :localize => true field :property_usage, :localize => true field :note, :localize => true field :property_number field :can_be_hired, type: Boolean, default: true field :purchase_date, type: DateTime field :owners, type: Array, :default => [] field :other_owner field :owner_email field :owner_phone field :price field :other_location mount_uploader :image, ImageUploader # unavailibility fields field :can_hire_before_months, type: Integer, default: 0 field :set_availability, type: Boolean, default: false field :set_unavailibility, type: Boolean, default: false field :start_time field :end_time field :weekdays, type: Array, default: [] field :start_date, type: DateTime field :end_date, type: DateTime field :description, :localize => true field :unavailibility_note, :localize => true field :hiring_person_email, type: Hash, default: {"enable"=>"1","required"=>"true"} field :hiring_person_number, type: Hash, default: {"enable"=>"1","required"=>"true"} field :hiring_person_name, type: Hash, default: {"enable"=>"1","required"=>"true"} field :reason_for_hire, type: Hash, default: {"enable"=>"1","required"=>"true"} field :note_for_hire, type: Hash, default: {"enable"=>"1","required"=>"false"} field :organization, type: Hash, default: {"enable"=>"1","required"=>"false"} field :person_in_charge, type: Hash, default: {"enable"=>"1","required"=>"false"} field :tel_of_person_in_charge, type: Hash, default: {"enable"=>"1","required"=>"false"} field :department, type: Hash, default: {"enable"=>"1","required"=>"false"} field :contact_person, type: Hash, default: {"enable"=>"1","required"=>"false"} field :tel_of_contact_person, type: Hash, default: {"enable"=>"1","required"=>"false"} field :mobile_phone_of_contact_person, type: Hash, default: {"enable"=>"1","required"=>"false"} field :contact_person_Email, type: Hash, default: {"enable"=>"1","required"=>"false"} field :contact_person_department, type: Hash, default: {"enable"=>"1","required"=>"false"} field :enable_notes_selector , type: Boolean, default: false field :notes_selector ,type: Hash, default: {} field :enable_fields_sort , type: Boolean, default: false field :custom_field_names, type: Array field :default_field_names, type: Array belongs_to :property_location has_many :p_hires has_many :hire_email_sets, :autosave => true, :dependent => :destroy, :inverse_of => :property accepts_nested_attributes_for :hire_email_sets, :allow_destroy => true has_many :p_hire_fields, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :p_hire_fields, :allow_destroy => true has_many :property_field_sets, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :property_field_sets, :allow_destroy => true has_many :property_day_settings, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :property_day_settings, :allow_destroy => true has_many :property_carousel_images, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :property_carousel_images, :allow_destroy => true has_many :property_files, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :property_files, :allow_destroy => true has_many :property_links, :autosave => true, :dependent => :destroy accepts_nested_attributes_for :property_links, :allow_destroy => true scope :sort_order, ->{order_by([:order_position,:asc],[:created_at,:desc])} WEEKDAYS = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ] CAlENDARTYPE = ["default","display","not_display"] after_initialize do unless self.new_record? save_flag = false @no_validate = true if self.default_field_names.nil? self.default_field_names = self.get_all_fields(true) save_flag = true end if self.custom_field_names.nil? self.custom_field_names = self.get_all_fields(true) save_flag = true end if save_flag self.save end end end before_create do max_position = self.class.max(:order_position) max_position = -1 if max_position.nil? self.order_position = max_position + 1 @no_validate = true self.default_field_names = self.get_all_fields(true) self.custom_field_names = self.get_all_fields(true) end before_save do unless @no_validate self.custom_field_names = [] if self.custom_field_names.nil? self.default_field_names = self.get_all_fields(true) self.class::FIELDSNAME.each do |f| if((self.send(f)["enable"] == "1" rescue true) && !(self.custom_field_names.include?(f))) self.custom_field_names << f end end end end after_save do self.change_day_setting_status end def p_hire_fields_enabled self.p_hire_fields.where(disabled: false) end def all_day_settings self.property_day_settings.asc(:key).group_by(&:day) end def change_day_setting_status if self.property_day_settings.count != 0 if self.set_unavailibility && self.weekdays.count != 0 self.property_day_settings.where(:day.nin=>self.weekdays).update_all(:enable=>true) tmp_start_time = self.start_time.blank? ? "00:00" : self.start_time tmp_end_time = self.end_time.blank? ? "24:00" : self.end_time self.property_day_settings.where(:day.in=>self.weekdays).each do |setting| if setting.end_time < tmp_start_time || setting.start_time > tmp_end_time setting.enable = true else setting.enable = false end setting.save end else self.property_day_settings.update_all(:enable=>true) end end end def calendar_type (self.custom_calendar_type == 0 ? (PropertyHireSetting.last.calendar_type rescue 0) : (self.custom_calendar_type - 1)) end def self.init_class_variables setting = PropertyHireSetting.last if setting @@disable_content_page = setting.disable_content_page rescue false @@disable_view_calendar_page = setting.disable_view_calendar_page rescue false @@disable_no_logins_view_calendar = setting.disable_no_logins_view_calendar rescue false else @@disable_content_page = false @@disable_view_calendar_page = false @@disable_no_logins_view_calendar = false end end init_class_variables def disable_content_page @@disable_content_page end def disable_view_calendar_page @@disable_view_calendar_page end def disable_no_logins_view_calendar @@disable_no_logins_view_calendar end def custom_text(field_name,type="name",locale=nil) locale = locale || I18n.locale default_text = I18n.with_locale(locale){I18n.t("property_hire.#{field_name}")} if (self.send(field_name)[type][locale.to_s].present? rescue false) self.send(field_name)[type][locale.to_s] else default_text end end def render_unavailable_message message = "" property = self weekdays_options = self.class::WEEKDAYS weekdays_options = weekdays_options.map do |weekday| trans = I18n.t("property_hire.#{weekday}", :default=>'') if trans != "" trans else weekday end end if property.set_unavailibility if property.weekdays.length > 0 translation_missing = (I18n.t('property_hire.unavailable_hint1', {:default => ''}) == "") str1 = (!property.start_date.nil? ? (I18n.t("property_hire.from",:default=>" from ") + property.start_date.strftime("%Y-%m-%d")) : "") str2 = (!property.end_date.nil? ? (I18n.t("property_hire.to",:default=>" to ") + property.end_date.strftime("%Y-%m-%d")) : "") if str1 == "" && str2 != "" str1 = I18n.t("property_hire.from_now_on") end if str1 != "" || str2 != "" str2 += I18n.t("property_hire.of",:default=>"") else str2 += I18n.t("property_hire.at",:default=>"") end week_str = "" if translation_missing week_str += "every " else week_str += I18n.t("property_hire.every") end dot_trans = I18n.t("property_hire.dot",:default=>", ") property.weekdays.each_with_index do |d,i| if i < (property.weekdays.count - 1) week_str += (weekdays_options[d.to_i] + dot_trans) else week_str += weekdays_options[d.to_i] end end str3 = "" if property.end_time.blank? if property.start_time.blank? str3 = "." if translation_missing else str3 = I18n.t("property_hire.from_time",{:time=>property.start_time,:default=>" from #{property.start_time}."}) end else if I18n.locale.to_s != "zh_tw" str3 = " between #{property.start_time} & #{property.end_time}." else str3 = I18n.t("property_hire.time1_to_time2",{:time1=>property.start_time,:time2=>property.end_time}) end end if str3 != "" str3 = I18n.t("property_hire.of",:default=>"") + str3 end hint1 = I18n.t('property_hire.unavailable_hint1', {:str1=>str1,:str2=>str2,:week_str=>week_str,:str3=>str3, :default => ''}) if hint1 == "" message += "This property is unavaliable#{str1}#{str2} #{week_str}#{str3}" else message += hint1 end end if property.need_hire_before != 0 if message != "" message += "
" end default_msg = "This property must be reserved #{property.need_hire_before} #{property.need_hire_before_unit}s in advance." message += I18n.t("property_hire.unavailable_hint3",{:month=>property.need_hire_before,:unit=>I18n.t("property_hire._#{property.need_hire_before_unit}",:default=>property.need_hire_before_unit),:default=>default_msg}) end if property.can_hire_before_months != 0 if message != "" message += "
" end default_msg = "This property is unavaliable to reserved before #{property.can_hire_before_months} month ago." message += I18n.t("property_hire.unavailable_hint2",{:month=>property.can_hire_before_months,:default=>default_msg}) end end return message.html_safe end def get_location_name return self.property_location.nil? ? self.other_location : self.property_location.title end def owner_profiles MemberProfile.find(self.owners) rescue [] end def is_available_for_hire?(stime, etime, interval = nil, recurring_end_date = nil, time_setting_id = nil) available = 0 return 1 if self.set_unavailibility == false return 1 if self.weekdays.empty? && self.can_hire_before_months == 0 time_now = Time.now.to_datetime if self.can_hire_before_months != 0 return 2 if ((stime - (self.can_hire_before_months).month) > time_now) end if self.need_hire_before != 0 return 3 if (time_now + (self.need_hire_before).send(self.need_hire_before_unit) > stime) end startt = self.start_date.nil? ? stime : self.start_date endt = self.end_date.nil? ? etime : self.end_date available = 1 if (startt > stime && endt > etime) available = 1 if (endt < stime) weekdays = self.weekdays.collect{|w| w.to_i} if !startt.nil? if available == 0 common_dates = (startt..endt) & (stime..etime) available = 1 if common_dates.nil? if available == 0 time_weekdays = [] Property.time_iterate(common_dates.min, common_dates.max, 1.day) do |t| time_weekdays << t.wday end time_weekdays.uniq! weekdays = weekdays & time_weekdays if weekdays.blank? available = 1 else startt = DateTime.parse(stime.strftime("%Y-%m-%d " + (self.start_time.blank? ? "00:00" : self.start_time) + Time.zone.to_s)) endt = DateTime.parse(etime.strftime("%Y-%m-%d " + (self.end_time.blank? ? "23:59" : self.end_time) + Time.zone.to_s)) common_dates = (startt..endt) & (stime..etime) available = common_dates.nil? ? 1 : 0 end end end if available == 1 if !recurring_end_date.blank? case interval when 'week' d_step = 1.week when 'month' d_step = 1.month else d_step = 0 end if d_step != 0 if etime >= stime Property.time_iterate(etime,recurring_end_date,d_step).each do |date_time| new_etime = date_time new_stime = stime + (new_etime - etime) available = self.is_available_for_hire?(new_stime, new_etime, nil, nil) break if available != 1 end else available = 0 end end end return available else return available end end end def not_yet_hired?(stime, etime, interval, recurring_end_date,phire_id=nil,time_setting_id=nil) phires = self.p_hires stime = stime.utc etime = etime.utc bookings_count = 0 reservation_limit = 0 if time_setting_id time_setting = PropertyDaySetting.find(time_setting_id) etime = stime + 1.day etime = etime.utc reservation_limit = time_setting.reservation_limit if reservation_limit == 0 return true end bookings_count = phires.where(:start_time.lte => stime,:end_time.gte => stime,:recurring => false,:id.ne=>phire_id,:property_day_setting_id=>time_setting_id).count + phires.where(:start_time.gte => stime,:end_time.lte => etime,:recurring => false,:id.ne=>phire_id,:property_day_setting_id=>time_setting_id).count +phires.where(:start_time.lte => etime,:end_time.gte => etime,:recurring => false,:id.ne=>phire_id,:property_day_setting_id=>time_setting_id).count if bookings_count < reservation_limit bookings_count = 0 end else bookings_count = phires.where(:start_time.lte => stime,:end_time.gte => stime,:recurring => false,:id.ne=>phire_id).count + phires.where(:start_time.gte => stime,:end_time.lte => etime,:recurring => false,:id.ne=>phire_id).count +phires.where(:start_time.lte => etime,:end_time.gte => etime,:recurring => false,:id.ne=>phire_id).count end available = true if bookings_count != 0 available = false end if available recurring_bookings = phires.where(:recurring_end_date.gte => stime, :recurring => true,:id.ne=>phire_id) bookings = phires.where(:recurring => false,:recurring_end_date => nil,:id.ne=>phire_id) case interval when 'week' d_step = 1.week when 'month' d_step = 1.month else d_step = 0 end not_in_ids = bookings.any_of([{:end_time.lt=>stime},{:start_time.gt=>etime}]).pluck(:id) bookings = bookings.where(:id.nin=>not_in_ids) if time_setting_id recurring_bookings = recurring_bookings.where(:property_day_setting_id=>time_setting_id) bookings = recurring_bookings.where(:property_day_setting_id=>time_setting_id) end if true#d_step != 0 bookings += recurring_bookings end if time_setting_id tmp = {} stime_date = stime.strftime("%Y-%m-%d") bookings = bookings.each_with_index do |booking,i| if booking.date.wday != stime.wday next end b_interval = booking.recurring_interval b_recurring_end_date = booking.recurring_end_date ? booking.recurring_end_date.utc : nil booking_date = booking.start_time.utc.strftime("%Y-%m-%d") if (b_interval == 'month' || b_interval == 'week') && (booking.recurring_end_date.nil? || !booking.recurring) b_interval = nil end if stime_date == booking_date if tmp[booking_date].nil? tmp[booking_date] = 0 end tmp[booking_date] += 1 available = false if tmp[booking_date] > reservation_limit end break if available == false if b_interval.present? b_interval = (1).send(b_interval) b_sdata = booking.start_time.utc b_datas = Property.time_iterate(b_sdata,b_recurring_end_date,b_interval) all_stime_datas = Property.time_iterate(stime,b_recurring_end_date,b_interval).map{|t| t.utc.strftime("%Y-%m-%d")} b_datas.each do |b_data| booking_date = b_data.utc.strftime("%Y-%m-%d") if all_stime_datas.include?(booking_date) if tmp[booking_date].nil? tmp[booking_date] = 0 end tmp[booking_date] += 1 available = false if tmp[booking_date] > reservation_limit end break if available == false end end break if available == false end else bookings.each_with_index do |booking,i| stime_tp = stime.clone etime_tp = etime.clone b_interval = booking.recurring_interval b_recurring_end_date = booking.recurring_end_date ? booking.recurring_end_date.utc : nil b_sdata = booking.start_time.utc b_edata = booking.end_time.utc b_delta = b_edata - b_sdata if (b_interval == 'month' || b_interval == 'week') && (booking.recurring_end_date.nil? || !booking.recurring) b_interval = nil end b_datas = [] if b_interval.present? b_interval = (1).send(b_interval) b_datas = Property.time_iterate(b_edata,b_recurring_end_date,b_interval) b_datas = b_datas.map{|b_end| [b_end-b_delta,b_end]} start_index = b_datas.count b_datas.each_with_index do |(b_start,b_end),i| if b_start >= stime_tp || b_end <= etime_tp start_index = i break end end b_datas = b_datas[start_index..-1].to_a.map{|b_start,b_end| (b_start..b_end)} else b_datas = [b_sdata..b_edata] end while b_datas.present? available = b_datas.find{|b_range| b_range & (stime_tp..etime_tp)}.nil? stime_tp = stime_tp + d_step etime_tp = etime_tp + d_step break if !available || recurring_end_date.blank? || d_step==0 start_index = b_datas.find_index{|b_range| b_range.first >= stime_tp} if start_index b_datas = b_datas[start_index..-1] else b_datas = [] end end break if available == false end end end return available end def check_require_fields(booking_p) fields_name = self.get_all_fields has_p_hire_fields = self.p_hire_fields_enabled.count != 0 p_hire_fields = {} if has_p_hire_fields p_hire_fields = self.p_hire_fields_enabled.map{|rf| [rf.id.to_s,rf]}.to_h end error_messages = "" form_index = 0 cross_lang_types = ['select','date','radio_button','checkbox'] unfilled_text = I18n.t('property_hire.unfilled') available_locales = I18n.available_locales available_locales_trans = available_locales.map{|l| [l, I18n.t(l)]}.to_h fields_name.each do |field_name| if has_p_hire_fields && field_name.include?("p_hire_fields") rf = p_hire_fields[field_name.sub("p_hire_fields.",'')] if rf && rf.markup != 'hint_text' && rf.to_require v = booking_p["p_hire_field_values_attributes"][form_index.to_s]['value'] if cross_lang_types.include?(rf.markup) || rf.get_data["cross_lang"] == "true" if v.blank? error_messages += "#{rf.title}: #{unfilled_text}\n" end else available_locales.each do |l| if (v[l].blank? rescue true) error_messages += "#{rf.title}(#{available_locales_trans[l]}): #{unfilled_text}\n" end end end end form_index = form_index +1 else field_setting = self.send(field_name) if field_setting && field_setting["required"] == "true" && booking_p[field_name].blank? error_messages += "#{self.custom_text(field_name,"name")}: #{unfilled_text}\n" end end end error_messages end def self.time_iterate(start_time, end_time, step, &block) times = [] if block_given? begin times << start_time yield(start_time, end_time) end while (start_time += step) <= end_time else start_time = start_time.clone begin times << start_time end while (start_time += step) <= end_time end times end def carousel_image_width (self.custom_carousel_image_width.blank? ? PropertyHireSetting.last.carousel_image_width : self.custom_carousel_image_width) rescue "75%" end def get_attribute_value(attribute_field, signup_id) SeminarSignupValue.find_by(seminar_signup_field_id: attribute_field.id, seminar_signup_id: signup_id) end def get_attribute_values(attribute_type=nil) @attribute_values = attribute_type.p_hire_field_values rescue nil end def get_value_from_field_id(field_id,attribute_type=nil) values = get_attribute_values(attribute_type) value = values.detect {|value| value.p_hire_field_id == field_id} rescue nil value ? value : nil end def get_basic_fields basic_fields = self.class::FIELDSNAME basic_fields = basic_fields.select{|f| (self.send(f)["enable"] == "1" rescue true)} end def get_all_fields(get_default=false) @default_field_names = nil if @default_field_names.nil? if get_default if @default_field_names.nil? basic_fields = self.get_basic_fields custom_fields = [] self.p_hire_fields_enabled.each do |p_hire_field| unless p_hire_field.disabled custom_fields << "p_hire_fields.#{p_hire_field.id}" end end @default_field_names = basic_fields + custom_fields end return @default_field_names else self.enable_fields_sort ? self.custom_field_names : self.default_field_names end end def can_be_hired_frontend user = OrbitHelper.current_user self.can_be_hired || (user && (user.is_admin? || property.owners.include?(user.member_profile_id))) end def can_be_show_frontend user = OrbitHelper.current_user (!disable_no_logins_view_calendar && self.can_be_hired) || user end end