質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.21%

チェックボックスを押されているときに翌日分の時間を足すようにしたい

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 656

ikt_erk122

score 28

【やりたいこと】
出社・退社時間を編集する機能を追加する勉強をしています。編集する際にチェックボックスを押して編集すると翌日に退社する(24時間後)という機能を追加したいです。

【困っていること】
チェックボックスは実装できているが、翌日の時間を取得する方法や書き方を調べてもわからず押しても何も変わらない状態。
自分の考えはDateクラスのDate.tomorrowを使えばいけるのかなと思っています。

・attendances/edit.html.erb

<%= form_for(@user, url: update_attendances_path, method: :patch) do |f| %>
    <table class = "table-bordered table-striped table-condensed">
      <thead>
        <tr>
          <th>日付</th>
          <th>曜日</th>
          <th>出社時間</th>
          <th>退社時間</th>
          <th>翌日</th>
        </tr>
      </thead>
      <tbody>
        <% @dates.each do |day| %>
          <%= fields_for "attendances[]", day do |af| %>
            <tr>
              <td><%= day.worked_on.to_s(:date) %></td>
              <td><%= %w{日 月 火 水 木 金 土}[day.worked_on.wday] %></td>
              <td>
                <% if day.worked_on > Date.today %>
                  <%= af.time_field :started_at, :readonly => true, class: "form-control" %>
                <% else %>
                  <%= af.time_field :started_at, class: "form-control" %>
                <% end %>  
              </td>
              <td>
                <% if day.worked_on > Date.today %>
                  <%= af.time_field :finished_at, :readonly => true, class: "form-control" %>
                <% else %>
                  <%= af.time_field :finished_at, class: "form-control" %>
                <% end %>  
              </td>
              <td>
                <% if day.worked_on > Date.today %>                            
                <% else %>
                  <%= af.check_box :next_day, {}, "true", "false" %>
                <% end %>
              </td>
            </tr>
          <% end %>
        <% end %>
      </tbody>
    </table>
    <div class="btn-attendances-update">
      <%= f.submit "更新", class: "btn btn-primary" %>
      <%= link_to "キャンセル", user_path(@user, params:{first_day: @first_day }), class: "btn btn-default btn-block" %>
    </div>
  <% end %>


・attedances_controller.rb

class AttendancesController < ApplicationController
  before_action :logged_in_user,  only: :edit
  before_action :general_user,    only: :edit
  before_action :hidden,          only: :edit

  def create
    @user = User.find(params[:user_id])
    @attendance = @user.attendances.find_by(worked_on: Date.today)
    if @attendance.started_at.nil?
      @attendance.update_attributes(started_at: current_time)
      flash[:info] = 'おはようございます。'
    elsif @attendance.finished_at.nil?
      @attendance.update_attributes(finished_at: current_time)
      flash[:info] = 'おつかれさまでした。'
    else
      flash[:danger] = 'トラブルがあり、登録出来ませんでした。'
    end
    redirect_to @user
  end

  def edit
    @user = User.find(params[:id])
    @first_day = first_day(params[:date])
    @last_day = @first_day.end_of_month
    @dates = user_attendances_month_date
  end

  def update   
    @user = User.find(params[:id])
    if attendances_invalid?
      attendances_params.each do |id, item|
        attendance = Attendance.find(id)
        attendance.update_attributes(item)
      end
      flash[:success] = '勤怠情報を更新しました。'
      redirect_to user_path(@user, params:{first_day: params[:date]})
      if params[:next_day] == true
        Time.now.tomorrow
      end
    else
      redirect_to edit_attendances_path(@user, params[:date])
    end
  end

  private
    def attendances_params
      params.permit(attendances: [:started_at, :finished_at, :note])[:attendances]
    end

    # ログインしていない一般ユーザーは勤怠編集画面を開けない
    def general_user
      @user = User.find(params[:id])
      if !current_user?(@user) && !current_user.admin?
        redirect_to(root_url)
      end
    end

    # ログイン済みユーザーか確認
    def logged_in_user
      unless logged_in?
        store_location
        flash[:danger] = "ログインしてください。"
        redirect_to login_url
      end
    end

    #管理者は勤怠編集画面の表示禁止
    def hidden
      if current_user.admin?
        redirect_to(root_url)
      end
    end
end


・attendances_helper

def attendances_invalid?
    attendances = true
    attendances_params.each do |id, item|
      if item[:started_at].blank? && item[:finished_at].blank?
        next
      elsif item[:started_at].blank? || item[:finished_at].blank?
        attendances = false
        flash[:danger] = "出社時間または退社時間を入力してください。"
        break
      elsif item[:started_at] > item[:finished_at]
        attendances = false
        flash[:danger] = "出社時間を退社時間より遅い時間に設定することはできません。"
        break
      end
    end
    return attendances
  end
end


・users_controller.rb

class UsersController < ApplicationController


  def new
    @user = User.new
  end

  def show
    @user = User.find(params[:id])
    if params[:first_day].nil?
      @first_day = Date.today.beginning_of_month
    else
      @first_day = Date.parse(params[:first_day])
    end
    @last_day = @first_day.end_of_month
    (@first_day..@last_day).each do |day|
      unless @user.attendances.any? {|attendance| attendance.worked_on == day}
        record = @user.attendances.build(worked_on: day)
        record.save
      end
    end
    @dates = user_attendances_month_date
    @worked_sum = @dates.where.not(started_at: nil).count
    respond_to do |format|
      format.html
      format.csv do
        send_data render_to_string, filename: "#{@user.name}.csv", type: :csv
      end
    end
  end

  def create
    @user = User.new(user_params)
    if @user.save
      log_in @user
      flash[:success] = "ユーザーの新規作成に成功しました。"
      redirect_to @user
    else
      render 'new'
    end
    registered_count = import_emails
    redirect_to emails_path, notice: "#{registered_count}件登録しました"
  end

  def edit
    @user = User.find(params[:id])
  end

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(user_params)
      flash[:success] = "ユーザー情報を更新しました。"
      redirect_to @user
    else
      render 'edit'
    end
  end

  def index
    if params[:q] && params[:q].reject { |key, value| value.blank? }.present?
      @q = User.ransack(search_params, activated_true: true)
      @title = "検索結果"
    else
      @q = User.ransack(activated_true: true)
      @title = "ユーザー一覧"
    end
    @users = @q.result.paginate(page: params[:page])
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "削除しました。"
    redirect_to users_url
  end

  def edit_index
    @user = User.find(params[:id])
  end

  def update_index
    @user = User.find(params[:id])
    if @user.update_attributes(update_index_params)
      flash[:success] = "更新しました。"
      redirect_to users_url   
    else
      render 'index'
    end
  end  
end


・schema.rb

create_table "attendances", force: :cascade do |t|
    t.date "worked_on"
    t.datetime "started_at"
    t.datetime "finished_at"
    t.string "note"
    t.integer "user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.boolean "next_day", default: false
    t.index ["user_id"], name: "index_attendances_on_user_id"
  end


・show.html.erb

<tbody>
<% @dates.each do |day| %>
  <%= fields_for "attendances[]", day do |af| %>
  <tr>
    <td><%= day.worked_on.to_s(:date) %></td>
    <td class="<%= css_class %>"><%= %w{日 月 火 水 木 金 土}[day.worked_on.wday] %></td>
    <td><%= day.started_at.to_s(:hour) if day.started_at.present? %></td>
    <td><%= day.started_at.floor_to(15.minutes).to_s(:min) if day.started_at.present? %></td>
    <td>
      <% if day.worked_on == Date.today && day.started_at.nil? %>
        <%= button_to "出社", user_attendances_path(@user), class: "btn btn-xs btn-primary" %>
      <% end %>
    </td>
    <td><%= day.finished_at.to_s(:hour) if day.finished_at.present? %></td>
    <td><%= day.finished_at.floor_to(15.minutes).to_s(:min) if day.finished_at.present? %></td>
    <td>
      <% if day.worked_on == Date.today && day.started_at.present? && day.finished_at.nil? %>
        <%= button_to "退社", user_attendances_path(@user), class: "btn btn-xs btn-primary" %>
      <% end %>
    </td>
  <td>
     <% if day.started_at.present? && day.finished_at.present? %>
       <%= working_times(day.started_at, day.finished_at) %>
       <% seconds = (day.finished_at - day.started_at).to_i %>
       <% @total_seconds = @total_seconds.to_i + seconds %>
     <% end %>
   </td>
  </tr>
  <% end %>
<% end %>
</tbody>


・見本(編集前)
イメージ説明
・見本(編集後)
イメージ説明

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

0

def edit を見ただけでは どこに入れたいのかが分からないのでとりあえず、24時間後の日時を得る方法だけ。
Date ではなく Time です。Dateでは時刻がないので

Time.now.tomorrow

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/06/24 16:46

    いいえ、違います。

    params[:next_day] が常に nil であることは理解していますか?

    if nil == true は常に false であるため Time.now.tomorrow は必ず実行されません。

    さて、ここで何が問題かわかりますでしょうか。
    そもそも if nil == true はあなたの行いたかったことでしょうか?
    違うと思います。期待していたものは
    if (true もしくは false) == true のはずです

    ではなぜそれができていないのか、
    それは params[:next_day] が ture または false を取得できていないからです。

    これは単に書き方が違うからです。
    その書き方ではパラメータから期待する値を取得できていません。
    そういう意味で書き方を間違えていると言っています。

    キャンセル

  • 2019/06/24 21:50

    すみません、他の書き方がわからないのですが、ヒントでいいので教えていただけないでしょうか?

    キャンセル

  • 2019/06/25 00:57

    params[:attendances]で"attendances"=>{ ... }の値{ ... }部分が取得できます。
    そしてparams[:attendances]["63"]で"attendances"=>{ "63"=> { ... } }の値{ ... }部分が取得できます。
    この要領で:next_dayまでたどるとparams[:attendances]["63"][:next_day]となります。

    ただ、["63"]となっている部分が["64"],["65"]と複数あるので複雑になっています。
    あと、一応言っておくとparams[:attendances]["63"][:next_day]で取り出せる値は
    "true"もしくは"false"となりますが、どちらも文字列、Stringです。Booleanではないので注意してください。

    キャンセル

0

時間を取らせてしまい申し訳なかったです。
あまり進展が望めないようなので、コードを読んで回答しようと思いました。が、
ちょっとコードを読んでも結局何がしたいのかよくわからなかったため
「送信されてきたパラメータの退社時間を、翌日チェックがあれば翌日、なければ当日の時刻として出力する」
コードを書きます。

前提: 以下の戻り値をパラメータとする
変更点は以下

  • 時間だけだと当日、翌日の判別が付かないので日付がわかるものを付与した
  • パラメータの2つ目以降にも仮の日時の値を入れた
  • パラメータの量が多すぎるので3つに減らした
ActionController::Parameters.new "utf8"=>"✓", "authenticity_token"=>"sDgo050bPWePwVXakqTW8UdgOkO/mdm53j9FTQu2nf0OGvhMtDjs47N48VwJw5BKk1+6LYOFoyHjlNJnueCq9Q==", "attendances"=>{"63"=>{"started_at"=>"2019-06-24 10:00:00", "finished_at"=>"2019-06-24 22:00:00", "next_day"=>"false", "note"=>""}, "64"=>{"started_at"=>"2019-06-24 09:00:00", "finished_at"=>"2019-06-24 21:00:00", "next_day"=>"true", "note"=>""}, "65"=>{"started_at"=>"2019-06-24 9:30:00", "finished_at"=>"2019-06-24 21:30:00", "next_day"=>"false", "note"=>""}}

コード

def attendances_params
  params.require(:attendances).permit("63": [:started_at, :finished_at, :next_day, :note], "64": [:started_at, :finished_at, :next_day, :note], "65": [:started_at, :finished_at, :next_day, :note])
end

attendances_params.values.each do |data|
  if data[:next_day] == "true"
    puts data[:finished_at].to_time.tomorrow
  else
    puts data[:finished_at].to_time
  end
end

# 2019-06-24 22:00:00 +0900
# 2019-06-25 21:00:00 +0900
# 2019-06-24 21:30:00 +0900

自分でアプリケーションを作成する前にRailsチュートリアルで基礎を身につけてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

params の読み方ですが、logをみると(必要なところだけ抜き出すと)

Parameters:  {
 "attendances"=>{
  "63"=>{"started_at"=>"10:00:00.000", 
          "finished_at"=>"22:00",
          "next_day"=>"false",
          "note"=>""
          },


という構造をしている、というのが見えるかと思います。
これをみれば読み方が判るかと思います。

show.html.erb と 画像のtableが一致しない(翌日チェックボックスや在社時間のTDがない)ので,確認出来ないのですが 在社時間の計算まではできている、と考えて良いですか?
その前提で。
やりたいことは 
「9:00から18:00まで在社した」となってるが、
じつは翌日の18:00だから それで在社時間を計算する
ということだと思います。
すると、「翌日18:00の時間を求めて」しなくても「当日の18:00」で在社を計算した後、24時間を加えればよいのでは。

で、そのnext_dayの得方ですが、
viewでは next_day はbooleanになっているのにschemaではdatetimeになっています。ですので controllerで取り込んで day.next_day にしまうときに false/trueをdatetimeに変換するで、うまく行っていないのでは。
next_dayをbooleanに変更したmodelにしておけば、day.next_day でtrue/falseが得られる様になるかと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.21%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる