学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

【民泊5.1】【MacOSX】カレンダーとの連動

ホスト用カレンダーで設定した内容が検索結果や予約カレンダーに反映するようにします。


記述更新 app\controllers\pages_controller.rb
48行目から53行目の内容を以下の記述に更新します。

        not_available_in_calendar = Calendar.where(
          "room_id = ? AND status = ? AND day <= ? AND day >= ?",
          room.id, 1, end_date, start_date
        ).limit(1)

        if not_available.length > 0 || not_available_in_calendar.length > 0
          @arrRooms.delete(room)
        end



app\controllers\pages_controller.rb

class PagesController < ApplicationController

  def home
      @rooms = Room.where(active: true).limit(3)
  end
  
  def search

    # ステップ 1

    if params[:search].present? && params[:search].strip != ""
      session[:loc_search] = params[:search]
    end
    arrResult = Array.new

    # ステップ 2

    if session[:loc_search] && session[:loc_search] != ""
      @rooms_address = Room.where(active: true).near(session[:loc_search], 5, order: 'distance')
    else
      @rooms_address = Room.where(active: true).all
    end

    # ステップ 3

    @search = @rooms_address.ransack(params[:q])
    @rooms = @search.result
    @arrRooms = @rooms.to_a

    # ステップ 4
    
    if (params[:start_date] && params[:end_date] && !params[:start_date].empty? &&  !params[:end_date].empty?)
      start_date = Date.parse(params[:start_date])
      end_date = Date.parse(params[:end_date])
      @rooms.each do |room|

        not_available = room.reservations.where(
          "((? <= start_date AND start_date <= ?)
          OR (? <= end_date AND end_date <= ?)
          OR (start_date < ? AND ? < end_date))
          AND status = ?",
          start_date, end_date,
          start_date, end_date,
          start_date, end_date,
          1
        ).limit(1)

        not_available_in_calendar = Calendar.where(
          "room_id = ? AND status = ? AND day <= ? AND day >= ?",
          room.id, 1, end_date, start_date
        ).limit(1)

        if not_available.length > 0 || not_available_in_calendar.length > 0
          @arrRooms.delete(room)
        end
        
      end
    end
  end
  
end



記述更新1 app\controllers\rooms_controller.rb
66行目の「preload()」メソッドの内容を以下の記述に更新します。

  def preload
    today = Date.today
    reservations = @room.reservations.where("(start_date >= ? OR end_date >= ?) AND status = ?", today, today, 1)

    unavailable_dates = @room.calendars.where("status = ? AND day > ?", 1, today)

    special_dates = @room.calendars.where("status = ? AND day > ? AND price <> ?",0, today, @room.price)

    render json: {
        reservations: reservations,
        unavailable_dates: unavailable_dates,
        special_dates: special_dates
    }
  end



記述更新2 app\controllers\rooms_controller.rb
91行目のプライベートメソッド「is_conflict()」の内容を以下の記述に変更しています。

    def is_conflict(start_date, end_date, room)
      check = room.reservations.where("(? < start_date AND end_date < ?) AND status = ?", start_date, end_date, 1)
      check_2 = room.calendars.where("day BETWEEN ? AND ? AND status = ?", start_date, end_date, 1).limit(1)

      check.size > 0 || check_2.size > 0 ? true : false
    end



app\controllers\rooms_controller.rb

class RoomsController < ApplicationController

  before_action :set_room, except: [:index, :new, :create]
  before_action :authenticate_user!, except: [:show]
  before_action :is_authorised, only: [:listing, :pricing, :description, :photo_upload, :amenities, :location, :update]

  def index
     @rooms = current_user.rooms
  end

  def new
    @room = current_user.rooms.build
  end

  def create

    if !current_user.is_active_host
      return redirect_to payout_method_path, alert: "振込口座の登録を先に行ってください。"
    end    

    @room = current_user.rooms.build(room_params)
    if @room.save
      redirect_to listing_room_path(@room), notice: "保存しました。"
    else
      flash[:alert] = "問題が発生しました。"
      render :new
    end
  end

  def show
    @photos = @room.photos
    @guest_reviews = @room.guest_reviews
  end

  def listing
  end

  def pricing
  end

  def description
  end

  def photo_upload
      @photos = @room.photos
  end

  def amenities
  end

  def location
  end

  def update
    new_params = room_params
    new_params = room_params.merge(active: true) if is_ready_room
    if @room.update(new_params)
      flash[:notice] = "保存しました。"
    else
      flash[:alert] = "問題が発生しました。"
    end
    redirect_back(fallback_location: request.referer)
  end

  # 予約 開始日のAJAX
  def preload
    today = Date.today
    reservations = @room.reservations.where("(start_date >= ? OR end_date >= ?) AND status = ?", today, today, 1)
    unavailable_dates = @room.calendars.where("status = ? AND day > ?", 1, today)
    special_dates = @room.calendars.where("status = ? AND day > ? AND price <> ?",0, today, @room.price)
    render json: {
        reservations: reservations,
        unavailable_dates: unavailable_dates,
        special_dates: special_dates
    }
  end

  # 予約 終了日のAJAX
  def preview
    start_date = Date.parse(params[:start_date])
    end_date = Date.parse(params[:end_date])
    output = {
      conflict: is_conflict(start_date, end_date, @room)
    }
    render json: output
  end

  private

  # 予約 プライベートメソッド
  def is_conflict(start_date, end_date, room)
    check = room.reservations.where("(? < start_date AND end_date < ?) AND status = ?", start_date, end_date, 1)
    check_2 = room.calendars.where("day BETWEEN ? AND ? AND status = ?", start_date, end_date, 1).limit(1)
    check.size > 0 || check_2.size > 0 ? true : false
  end

  def set_room
      @room = Room.find(params[:id])
  end
  
  def is_authorised
    redirect_to root_path, alert: "権限がありません。" unless current_user.id == @room.user_id
  end

  def is_ready_room
    !@room.active && !@room.price.blank? && !@room.listing_name.blank? && !@room.photos.blank? && !@room.address.blank?
  end

  def room_params
    params.require(:room).permit(:home_type, :room_type, :accommodate, :bed_room, :bath_room, :listing_name, :summary, :address, :is_tv, :is_kitchen, :is_air, :is_heating, :is_internet, :price, :active, :instant)
  end

end



記述追加 app\controllers\reservations_controller.rb
21行目と35行目に記述を追加し、32行目をコメントアウトしています。
コードをコピーしてファイルを置き換えて下さい。

class ReservationsController < ApplicationController
    before_action :authenticate_user!
    before_action :set_reservation, only: [:approve, :decline]
  
    def create
      room = Room.find(params[:room_id])
  
      if current_user == room.user
        flash[:alert] = "オーナーが予約することはできません。"

      elsif current_user.stripe_id.blank?
        flash[:alert] = "予約する前にクレジットカードを登録する必要があります。"
        return redirect_to payment_method_path

      else
  
          start_date = Date.parse(reservation_params[:start_date])
          end_date = Date.parse(reservation_params[:end_date])
          days = (end_date - start_date).to_i

          special_dates = room.calendars.where(
            "status = ? AND day BETWEEN ? AND ? AND price <> ?",
            0, start_date, end_date, room.price
          )

        if days == 0
          flash[:alert] = "宿泊日数が1泊以上でなければ予約することはできません。"
        else
          @reservation = current_user.reservations.build(reservation_params)
          @reservation.room = room
          @reservation.price = room.price
          #@reservation.total = room.price * days
          #@reservation.save

          @reservation.total = room.price * (days - special_dates.count)
          special_dates.each do |date|
              @reservation.total += date.price
          end          

          if @reservation.Waiting!
            if room.Request?
              flash[:notice] = "予約承認申請を送信しました。予約が承認されるまでしばらくお待ち下さい。"
            else
              charge(room, @reservation)
            end
          else
            flash[:alert] = "ご予約できません!"
          end
        end
      end
      redirect_to room
    end

    # 宿泊者用予約確認
    def your_trips
      @trips = current_user.reservations.order(start_date: :asc)
    end

    #ホスト用予約確認
    def your_reservations
      @rooms = current_user.rooms
    end

    def approve
      charge(@reservation.room, @reservation)
      redirect_to your_reservations_path
    end
  
    def decline
      @reservation.Declined!
      redirect_to your_reservations_path
    end
    
    private

    def send_sms(room, reservation)
      @client = Twilio::REST::Client.new
      @client.messages.create(
        from: '+12056565281',
        to: room.user.phone_number,
        body: "#{reservation.user.fullname}様、#{room.listing_name}を予約しました。"
      )
    end

    def charge(room, reservation)

      host_amount = (reservation.total * 0.8).to_i # 売上の80%がホストに入る

      if !reservation.user.stripe_id.blank?
        customer = Stripe::Customer.retrieve(reservation.user.stripe_id)
        charge = Stripe::Charge.create(
          :customer => customer.id,
          :amount => reservation.total,
          :description => room.listing_name,
          :currency => "jpy",
          transfer_data: {
            amount: host_amount, 
            destination: room.user.merchant_id, # ホストのストライプID
          },
        )

  
        if charge
          reservation.Approved!
          
          send_sms(room, reservation) if reservation.user.setting.enable_sms
          ReservationMailer.send_email_to_guest(reservation.user, room, reservation).deliver_later if reservation.user.setting.enable_email
          flash[:notice] = "お支払い手続きが完了し、ご予約されました。お越しをお待ちしております!"
        else
          reservation.Declined!
          flash[:notice] = "お支払い手続きができません。予約ができませんでした。"
        end
      end
    rescue Stripe::CardError => e
      reservation.declined!
      flash[:alert] = e.message
    end

    def set_reservation
      @reservation = Reservation.find(params[:id])
    end

    def reservation_params
      params.require(:reservation).permit(:start_date, :end_date)
    end

  end
  



記述更新 app\views\reservations\_form.html.erb
32行目に記述を追加しています。
55行目からのスクリプトを大幅に変更しています。
コードをコピーしてファイルを置き換えてください。

<div class="panel panel-default">
  <div class="panel-heading">
    <!-- 承認制の場合はアイコンを表示しない -->
    <span><% if @room.Instant? %><i class="fa fa-bolt" style="color: #ffb400"></i><% end %></span>
    <span class="pull-right">1泊 <%= number_to_currency(@room.price) %></span>
    <br/>
  </div>
  <div class="panel-body">
    <%= form_for([@room, @room.reservations.new]) do |f| %>
      <div class="row">
        <div class="col-md-6">
          <label>チェックイン</label>
          <%= f.text_field :start_date, readonly: true, placeholder: "チェックイン日", class: "form-control datepicker" %>
        </div>
        <div class="col-md-6">
          <label>チェックアウト</label>
          <%= f.text_field :end_date, readonly: true, placeholder: "チェックアウト日", class: "form-control datepicker", disabled: true %>
        </div>
      </div>
      <h4 class="message-alert text-center"><span id="message"></span></h4>
      <div id="preview" style="display: none">
        <table class="reservation-table">
          <tbody>
             <tr>
               <td>宿泊費用</td>
               <td class="text-right", style="white-space: nowrap"><%= number_to_currency(@room.price) %></td>
             </tr>
             <tr>
               <td>宿泊日数</td>
               <td class="text-right"><span id="reservation_nights"></span></td>
             </tr>
              <tr id="special_details">
                <td colspan="2" class="total">
                  特別価格<br/>
                  <ul id="special_list"></ul>
                </td>
             </tr>
             <tr>
               <td class="total">合計金額</td>
               <td class="text-right", style="white-space: nowrap"><span id="reservation_total"></span></td>
             </tr>
           </tbody>
        </table>
      </div>
      <br/>
      <% if @room.Instant? %>
        <%= f.submit "予約する", id: "btn_book", class: "btn btn-normal btn-block", disabled: true %>
      <% else %>
          <%= f.submit "予約承認申請を送る", id: "btn_book", class: "btn btn-normal btn-block", disabled: true %>
      <% end %>
    <% end %>
  </div>
</div>

<script>

  function checkDate(date) {
    dmy = date.getDate() + "-" + (date.getMonth() + 1) + "-" + date.getFullYear();
    return [$.inArray(dmy, unavailableDates) == -1];
  }

  $(function() {

    unavailableDates = [];

    specialDates = [];

    function specialCheck() {

      var start_date = $('#reservation_start_date').datepicker('getDate');
      var end_date = $('#reservation_end_date').datepicker('getDate');
      //2日選択すると1泊になる
      var nights = (end_date - start_date)/1000/60/60/24

      var input = {
        'start_date': start_date,
        'end_date': end_date
      }

      $.ajax({
        url: '<%= preview_room_path(@room) %>',
        data: input,
        success: function (data) {

          if (data.conflict) {
            $('#message').text("この日付はご利用できません。");
            $('#preview').hide();
            $('#btn_book').attr('disabled', true);
          } else {
            $('#message').text("");
            $('#preview').show();
            $('#btn_book').attr('disabled', false);

            //----------- 更新 始まり

            $('#special_list').empty();
            var selectedDates = [];

            // ステップ 1: 選択した日付の配列を作成します
            while (start_date <= end_date) {
                selectedDates.push($.datepicker.formatDate('d-m-yy', new Date(start_date)));
                start_date.setDate(start_date.getDate() + 1);
            };

            // ステップ 2: 特別な日付の配列を作成します
            var selectedSpecialDate = specialDates.filter(function(sDate){
                var d = $.datepicker.formatDate('d-m-yy',new Date(sDate.day));
                return selectedDates.indexOf(d) !== -1;
            });

            var total = (nights - selectedSpecialDate.length) * <%= @room.price %>;
            if (selectedSpecialDate.length > 0) {
              $('#special_details').show();

              selectedSpecialDate.map(function (date) {
                  total += date.price
                  $('#special_list').append('<li class="text-right">('+ date.day + '): ' + date.price +'円</li>')
              });
            } else {
              $('#special_details').hide();
            }

            //----------- 更新 終わり

            $('#reservation_nights').text(nights);
            $('#reservation_total').text(total);
          }
        }
      });
    }

    $.ajax({
      url: '<%= preload_room_path(@room) %>',
      dateTyp: 'json',
      success: function(data) {

        specialDates = data.special_dates;

        $.each(data.reservations, function(arrID, arrValue) {
            for(var d = new Date(arrValue.start_date); d <= new Date(arrValue.end_date); d.setDate(d.getDate() + 1)) {
              unavailableDates.push($.datepicker.formatDate('d-m-yy', d));
            }
        });

        $.each(data.unavailable_dates, function (arrID, arrValue) {
            unavailableDates.push($.datepicker.formatDate('d-m-yy', new Date(arrValue.day)));
        });

        $('#reservation_start_date').datepicker({
          dateFormat: 'dd-mm-yy',
          //今日から3ヶ月先まで予約可能
          minDate: 0,
          maxDate: '3m',
          beforeShowDay: checkDate,
          onSelect: function(selected) {
            $('#reservation_end_date').datepicker("option", "minDate", selected);
            $('#reservation_end_date').attr("disabled", false);
            specialCheck();
          }
        });

        $('#reservation_end_date').datepicker({
          dateFormat: 'dd-mm-yy',
          //今日から3ヶ月先まで予約可能
          minDate: 0,
          maxDate: '3m',
          beforeShowDay: checkDate,
          onSelect: function(selected) {
            $('#reservation_start_date').datepicker("option", "maxDate", selected);
            specialCheck();
          }
        });
      }
    });

  });
</script>



ブラウザ確認
http://localhost:3000/host_calendar


ホスト用カレンダーで特別価格や利用不可を設定します。

ホスト用カレンダー設定
ホスト用カレンダー設定


予約カレンダーの連動を確認します。
予約カレンダーとの連動
予約カレンダーとの連動


特別価格の反映を確認します。
特別価格の反映
特別価格の反映