diff --git a/Gemfile b/Gemfile index f62ef445..4b181f9e 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source 'https://rubygems.org' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '>= 5.0.0.rc1', '< 5.1' +gem 'rails', '>= 5.0.7' # Use postgresql as the database for Active Record gem 'pg', '~> 0.18' # Use Puma as the app server @@ -17,8 +17,7 @@ gem 'coffee-rails', '~> 4.1.0' # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' -# Use bootstrap3 datetimepicker library -gem 'bootstrap3-datetimepicker-rails' +gem 'flatpickr' gem 'momentjs-rails' # Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks gem 'turbolinks', '~> 5.x' @@ -52,6 +51,10 @@ gem 'crono' gem 'sinatra', require: nil gem 'daemons' gem 'flowdock' +gem 'act-fluent-logger-rails' +gem 'lograge' +gem 'logstash-event' +gem 'capybara' group :development, :test do gem 'awesome_print' diff --git a/Gemfile.lock b/Gemfile.lock index da8807f9..7040e18b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -27,45 +27,49 @@ GEM specs: aasm (4.12.0) concurrent-ruby (~> 1.0) - actioncable (5.0.3) - actionpack (= 5.0.3) + act-fluent-logger-rails (0.4.0) + activesupport (>= 4, < 5.2) + fluent-logger + railties (>= 4, < 5.2) + actioncable (5.0.7) + actionpack (= 5.0.7) nio4r (>= 1.2, < 3.0) websocket-driver (~> 0.6.1) - actionmailer (5.0.3) - actionpack (= 5.0.3) - actionview (= 5.0.3) - activejob (= 5.0.3) + actionmailer (5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.3) - actionview (= 5.0.3) - activesupport (= 5.0.3) + actionpack (5.0.7) + actionview (= 5.0.7) + activesupport (= 5.0.7) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.3) - activesupport (= 5.0.3) + actionview (5.0.7) + activesupport (= 5.0.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) active_hash (1.5.1) activesupport (>= 2.2.2) - activejob (5.0.3) - activesupport (= 5.0.3) + activejob (5.0.7) + activesupport (= 5.0.7) globalid (>= 0.3.6) - activemodel (5.0.3) - activesupport (= 5.0.3) - activerecord (5.0.3) - activemodel (= 5.0.3) - activesupport (= 5.0.3) + activemodel (5.0.7) + activesupport (= 5.0.7) + activerecord (5.0.7) + activemodel (= 5.0.7) + activesupport (= 5.0.7) arel (~> 7.0) activerecord-import (0.18.3) activerecord (>= 3.2) - activesupport (5.0.3) + activesupport (5.0.7) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.0) @@ -73,7 +77,7 @@ GEM airbrussh (1.2.0) sshkit (>= 1.6.1, != 1.7.0) arel (7.1.4) - ast (2.3.0) + ast (2.4.0) autoprefixer-rails (7.1.0) execjs awesome_print (1.7.0) @@ -85,8 +89,6 @@ GEM bootstrap-sass (3.3.7) autoprefixer-rails (>= 5.2.1) sass (>= 3.3.4) - bootstrap3-datetimepicker-rails (4.17.47) - momentjs-rails (>= 2.8.1) builder (3.2.3) byebug (9.0.6) cancancan (1.16.0) @@ -106,6 +108,13 @@ GEM capistrano-bundler (~> 1.1) capistrano-upload-config (0.7.0) capistrano (>= 3.0) + capybara (2.16.1) + addressable + mini_mime (>= 0.1.3) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) chronic (0.10.2) clavius (1.0.2) cocoon (1.2.10) @@ -120,6 +129,7 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.0.5) + crass (1.0.4) crono (1.1.2) activerecord (>= 4.0) activesupport (>= 4.0) @@ -146,10 +156,13 @@ GEM faker (1.7.3) i18n (~> 0.5) ffi (1.9.18) + flatpickr (2.6.3.0) flowdock (0.7.1) httparty (~> 0.7) multi_json - globalid (0.4.0) + fluent-logger (0.7.1) + msgpack (>= 1.0.0, < 2) + globalid (0.4.1) activesupport (>= 4.2.0) haml (5.0.1) temple (>= 0.8.0) @@ -160,7 +173,8 @@ GEM unicode-display_width (~> 0.1.1) httparty (0.15.5) multi_xml (>= 0.5.2) - i18n (0.8.1) + i18n (0.9.5) + concurrent-ruby (~> 1.0) i18n-debug (1.1.0) i18n (< 1) jbuilder (2.6.4) @@ -190,36 +204,41 @@ GEM listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - loofah (2.0.3) + lograge (0.5.1) + actionpack (>= 4, < 5.2) + activesupport (>= 4, < 5.2) + railties (>= 4, < 5.2) + logstash-event (1.2.02) + loofah (2.2.2) + crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.6.5) - mime-types (>= 1.16, < 4) + mail (2.7.0) + mini_mime (>= 0.1.1) method_source (0.8.2) - mime-types (3.1) - mime-types-data (~> 3.2015) - mime-types-data (3.2016.0521) - mini_portile2 (2.1.0) - minitest (5.10.2) + mini_mime (1.0.0) + mini_portile2 (2.3.0) + minitest (5.11.3) momentjs-rails (2.17.1) railties (>= 3.1) + msgpack (1.1.0) multi_json (1.12.1) multi_xml (0.6.0) mustermann (1.0.0) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (4.1.0) - nio4r (2.0.0) - nokogiri (1.7.2) - mini_portile2 (~> 2.1.0) + nio4r (2.3.1) + nokogiri (1.8.3) + mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) paranoia (2.3.1) activerecord (>= 4.0, < 5.2) - parser (2.4.0.0) - ast (~> 2.2) + parser (2.5.1.0) + ast (~> 2.4.0) pg (0.20.0) polyamorous (1.3.1) activerecord (>= 3.0) - powerpack (0.1.1) + powerpack (0.1.2) pry (0.10.4) coderay (~> 1.1.0) method_source (~> 0.8.1) @@ -229,24 +248,24 @@ GEM pry (~> 0.10) public_suffix (2.0.5) puma (3.8.2) - rack (2.0.3) + rack (2.0.5) rack-mini-profiler (0.10.2) rack (>= 1.2.0) rack-protection (2.0.0) rack rack-test (0.6.3) rack (>= 1.0) - rails (5.0.3) - actioncable (= 5.0.3) - actionmailer (= 5.0.3) - actionpack (= 5.0.3) - actionview (= 5.0.3) - activejob (= 5.0.3) - activemodel (= 5.0.3) - activerecord (= 5.0.3) - activesupport (= 5.0.3) - bundler (>= 1.3.0, < 2.0) - railties (= 5.0.3) + rails (5.0.7) + actioncable (= 5.0.7) + actionmailer (= 5.0.7) + actionpack (= 5.0.7) + actionview (= 5.0.7) + activejob (= 5.0.7) + activemodel (= 5.0.7) + activerecord (= 5.0.7) + activesupport (= 5.0.7) + bundler (>= 1.3.0) + railties (= 5.0.7) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.1) actionpack (~> 5.x) @@ -255,17 +274,17 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - railties (5.0.3) - actionpack (= 5.0.3) - activesupport (= 5.0.3) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + railties (5.0.7) + actionpack (= 5.0.7) + activesupport (= 5.0.7) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rainbow (2.2.2) rake - rake (12.0.0) + rake (12.3.1) rb-fsevent (0.9.8) rb-inotify (0.9.8) ffi (>= 0.5.0) @@ -294,7 +313,7 @@ GEM powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.7) - ruby-progressbar (1.8.1) + ruby-progressbar (1.9.0) sass (3.4.23) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -329,10 +348,10 @@ GEM spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (3.7.1) + sprockets (3.7.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.2.0) + sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -340,14 +359,14 @@ GEM net-scp (>= 1.1.2) net-ssh (>= 2.8.0) temple (0.8.0) - thor (0.19.4) + thor (0.20.0) thread_safe (0.3.6) tilt (2.0.7) timecop (0.8.1) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.3) - tzinfo (1.2.3) + tzinfo (1.2.5) thread_safe (~> 0.1) uglifier (3.2.0) execjs (>= 0.3.0, < 3) @@ -361,27 +380,30 @@ GEM railties (>= 5.0) websocket-driver (0.6.5) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.2) + websocket-extensions (0.1.3) whenever (0.9.7) chronic (>= 0.6.3) + xpath (2.1.0) + nokogiri (~> 1.3) PLATFORMS ruby DEPENDENCIES aasm + act-fluent-logger-rails active_hash activerecord-import awesome_print biz bootstrap-sass - bootstrap3-datetimepicker-rails byebug cancancan capistrano (~> 3.8.1) capistrano-crono capistrano-rails (~> 1.1) capistrano-upload-config + capybara cocoon codeclimate-test-reporter (~> 1.0.0) coffee-rails (~> 4.1.0) @@ -392,6 +414,7 @@ DEPENDENCIES devise factory_girl_rails faker + flatpickr flowdock haml hirb-unicode @@ -401,6 +424,8 @@ DEPENDENCIES kaminari letter_opener listen (~> 3.0.5) + lograge + logstash-event momentjs-rails paranoia (~> 2.2) pg (~> 0.18) @@ -408,7 +433,7 @@ DEPENDENCIES pry-byebug puma (~> 3.0) rack-mini-profiler (~> 0.10.1) - rails (>= 5.0.0.rc1, < 5.1) + rails (>= 5.0.7) rails-controller-testing rails-observers! ransack! @@ -432,4 +457,4 @@ DEPENDENCIES whenever BUNDLED WITH - 1.15.1 + 1.16.2 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index f8d38c50..63bbb180 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -4,6 +4,8 @@ //= require bootstrap-sprockets //= require cocoon //= require moment -//= require bootstrap-datetimepicker +//= require flatpickr +//= require datepicker +//= require datetimepicker //= require base -//= require bootstrap3-datetimepicker +//= require page_alert diff --git a/app/assets/javascripts/bootstrap3-datetimepicker.js b/app/assets/javascripts/bootstrap3-datetimepicker.js deleted file mode 100644 index f1f43463..00000000 --- a/app/assets/javascripts/bootstrap3-datetimepicker.js +++ /dev/null @@ -1,25 +0,0 @@ -document.addEventListener('turbolinks:load', function() { - var datePicker = { - $datePickers: $("*[data-input='datepicker']"), - - initDatePickers: function(elem) { - $(elem).datetimepicker({ - format: $(elem).data('format'), - sideBySide: true, - stepping: 30, - useCurrent: false, - // TODO: disabledTimeIntervals should be changed according to business hours settings - disabledTimeIntervals: [[moment({ h: 0 }), moment({ h: 9, m: 29 })], [moment({ h: 18, m: 31 }), moment({ h: 24 })]] - }); - }, - - init: function() { - var $this = this; - $this.$datePickers.each(function() { - $this.initDatePickers(this); - }); - } - }; - - datePicker.init(); -}); diff --git a/app/assets/javascripts/datepicker.js b/app/assets/javascripts/datepicker.js new file mode 100644 index 00000000..1c2ce6a3 --- /dev/null +++ b/app/assets/javascripts/datepicker.js @@ -0,0 +1,21 @@ +document.addEventListener('turbolinks:load', function() { + var datePicker = { + $datePickers: $("*[data-input='datepicker']"), + + initDatePickers: function(elem) { + $(elem).flatpickr({ + enableTime: false, + dateFormat: $(elem).data('format') + }); + }, + + init: function() { + var $this = this; + $this.$datePickers.each(function() { + $this.initDatePickers(this); + }); + } + }; + + datePicker.init(); +}); diff --git a/app/assets/javascripts/datetimepicker.js b/app/assets/javascripts/datetimepicker.js new file mode 100644 index 00000000..9607daf4 --- /dev/null +++ b/app/assets/javascripts/datetimepicker.js @@ -0,0 +1,25 @@ +document.addEventListener('turbolinks:load', function() { + var dateTimePicker = { + $dateTimePickers: $("*[data-input='datetimepicker']"), + + initDateTimePickers: function(elem) { + $(elem).flatpickr({ + defaultHour: 9, + defaultMinute: 30, + enableTime: true, + dateFormat: $(elem).data('format'), + time_24hr: true, + minuteIncrement: 30 + }); + }, + + init: function() { + var $this = this; + $this.$dateTimePickers.each(function() { + $this.initDateTimePickers(this); + }); + } + } + + dateTimePicker.init(); +}); diff --git a/app/assets/javascripts/page_alert.js b/app/assets/javascripts/page_alert.js new file mode 100644 index 00000000..45c533ed --- /dev/null +++ b/app/assets/javascripts/page_alert.js @@ -0,0 +1,6 @@ +document.addEventListener("turbolinks:load", function(){ + var $modal = $('#page-alert-modal').modal({ + show: true + }); +}); + diff --git a/app/assets/javascripts/pages/backend/leave_applications.js b/app/assets/javascripts/pages/backend/leave_applications.js index 75546f14..406ede1f 100644 --- a/app/assets/javascripts/pages/backend/leave_applications.js +++ b/app/assets/javascripts/pages/backend/leave_applications.js @@ -16,14 +16,20 @@ document.addEventListener("turbolinks:load", function() { Turbolinks.visit("/backend/leave_applications?status=" + status + "&year=" + year ); }) + //員工休假統計頁 var $statistics_year = $("select[id=year]") var $statistics_month = $("select[id=month]") + var $statistics_role = $("select[id=role]") $statistics_year.on('change', function() { - Turbolinks.visit("/backend/leave_applications/statistics?year=" + $statistics_year.val() + "&month=" + $statistics_month.val()); + Turbolinks.visit("/backend/leave_applications/statistics?year=" + $statistics_year.val() + "&month=" + $statistics_month.val() + "&role=" + $statistics_role.val()); }); $statistics_month.on('change', function() { - Turbolinks.visit("/backend/leave_applications/statistics?year=" + $statistics_year.val() + "&month=" + $statistics_month.val()); + Turbolinks.visit("/backend/leave_applications/statistics?year=" + $statistics_year.val() + "&month=" + $statistics_month.val() + "&role=" + $statistics_role.val()); + }); + + $statistics_role.on("change", function() { + Turbolinks.visit("/backend/leave_applications/statistics?year=" + $statistics_year.val() + "&month=" + $statistics_month.val() + "&role=" + $statistics_role.val()); }); }) diff --git a/app/assets/javascripts/pages/leave_applications.js b/app/assets/javascripts/pages/leave_applications.js index 8ec42498..c4576289 100644 --- a/app/assets/javascripts/pages/leave_applications.js +++ b/app/assets/javascripts/pages/leave_applications.js @@ -1,57 +1,4 @@ document.addEventListener("turbolinks:load", function() { - // datetimepicker - var $dateTimePickerStart = $('#datetimepicker_start'); - var $dateTimePickerEnd = $('#datetimepicker_end'); - - $dateTimePickerStart.datetimepicker( { - format: 'YYYY-MM-DD HH:mm', - disabledTimeIntervals: [[moment({ h: 0 }), moment({ h: 9, m: 29 })], [moment({ h: 18, m: 31 }), moment({ h: 24 })]], - stepping: 30, - useCurrent: false, - sideBySide: true, - viewDate: moment(new Date()).format('YYYY-MM-DD') + " 09:30", - }); - - $dateTimePickerEnd.datetimepicker( { - format: 'YYYY-MM-DD HH:mm', - disabledTimeIntervals: [[moment({ h: 0 }), moment({ h: 9, m: 29 })], [moment({ h: 18, m: 31 }), moment({ h: 24 })]], - stepping: 30, - useCurrent: false, - sideBySide: true, - viewDate: moment(new Date()).format('YYYY-MM-DD') + " 10:30", - }); - - var startTimeChangeFirst = false; - var $startTimeInput = $dateTimePickerStart.find("#leave_application_start_time"); - var $endTimeInput = $dateTimePickerEnd.find("#leave_application_end_time"); - - $dateTimePickerStart.on( 'dp.change', function(e){ - if ( startTimeChangeFirst == false) { - startTimeChangeFirst = true; - } - - if (startTimeChangeFirst) { - var startTime = moment( $startTimeInput.val() ); - var endTime = moment( $endTimeInput.val() ); - var m = startTime.clone().add(1, 'hours'); - - $dateTimePickerEnd.data('DateTimePicker').minDate(m); - - if (startTime > endTime) { - $dateTimePickerEnd.data('DateTimePicker').date(m); - } - } - }); - - $dateTimePickerEnd.on( 'dp.change', function(e){ - if (startTimeChangeFirst) { - var endTime = moment( $endTimeInput.val() ); - var m = endTime.clone().subtract(1, 'hours'); - - $dateTimePickerStart.data('DateTimePicker').maxDate( m ); - } - }); - $year = $("#year"); $leaveStatus = $("#status"); diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 604b71c7..41c17f1e 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -4,7 +4,6 @@ @import "bootstrap-sprockets"; @import "bootstrap"; -@import "bootstrap-datetimepicker"; @import "base"; @import "bootstrap_override"; -@import "bootstrap3-datetimepicker"; +@import "flatpickr"; diff --git a/app/assets/stylesheets/pages/backend/leave_applications.scss b/app/assets/stylesheets/pages/backend/leave_applications.scss index e69de29b..f7daa1e2 100644 --- a/app/assets/stylesheets/pages/backend/leave_applications.scss +++ b/app/assets/stylesheets/pages/backend/leave_applications.scss @@ -0,0 +1,3 @@ +.highlight{ + color: red; +} \ No newline at end of file diff --git a/app/assets/stylesheets/pages/backend/leave_times.scss b/app/assets/stylesheets/pages/backend/leave_times.scss index 3c38fe1d..04080da9 100644 --- a/app/assets/stylesheets/pages/backend/leave_times.scss +++ b/app/assets/stylesheets/pages/backend/leave_times.scss @@ -4,3 +4,7 @@ text-align: center; } } + +.checkbox-inline:first-child { + margin-left: 10px; +} \ No newline at end of file diff --git a/app/controllers/backend/leave_applications_controller.rb b/app/controllers/backend/leave_applications_controller.rb index 4079519c..ae82169d 100644 --- a/app/controllers/backend/leave_applications_controller.rb +++ b/app/controllers/backend/leave_applications_controller.rb @@ -7,6 +7,16 @@ def index @users = User.all end + def create + @current_object = collection_scope.new(resource_params) + if @current_object.save + @current_object.approve!(current_user) + action_success + else + render action: :new + end + end + def verify; end def update @@ -21,7 +31,11 @@ def update end def statistics - @statistics = LeaveApplication.statistics_table(year: specific_year.to_i, month: specific_month.to_i) + @summary = LeaveTimeSummaryService.new( + specific_year.to_i, + specific_month.to_i, + specific_role + ).summary end private @@ -36,7 +50,7 @@ def approve end def reject - if current_object.pending? + if current_object.pending? or current_object.approved? current_object.reject!(current_user) action_success else @@ -53,7 +67,10 @@ def collection_scope end def resource_params - params.require(:leave_application).permit(:comment) + case action_name + when 'create' then params.require(:leave_application).permit(:user_id, :leave_type, :start_time, :end_time, :description, :comment) + when 'update' then params.require(:leave_application).permit(:comment) + end end def search_params diff --git a/app/controllers/backend/leave_times_controller.rb b/app/controllers/backend/leave_times_controller.rb index bb7376f5..556a1cb6 100644 --- a/app/controllers/backend/leave_times_controller.rb +++ b/app/controllers/backend/leave_times_controller.rb @@ -6,11 +6,41 @@ def index @users = User.all end + def new + @current_object = LeaveTime.new + end + + def create + @current_object = LeaveTime.new(resource_params) + return render action: :new unless @current_object.save + + if request.env['HTTP_REFERER'].include?('leave_application_id') + verify_id = request.env['HTTP_REFERER'].partition('=').last + action_success(verify_backend_leave_application_path(verify_id)) + else + action_success + end + + end + def append_quota @current_object = collection_scope.new(leave_time_params_by_leave_application) render :new end + def batch_new + @current_object = LeaveTime.new + end + + def batch_create + user_ids = params[:leave_time][:user_id] + user_ids.each do |user_id| + @current_object = LeaveTime.new(batch_leave_time_params.merge(user_id: user_id)) + return render action: :batch_new unless @current_object.save + end + action_success + end + private def leave_time_params_by_leave_application @@ -36,6 +66,11 @@ def resource_params ) end + def batch_leave_time_params + params.require(:leave_time).permit( + :leave_type, :quota, :effective_date, :expiration_date, :usable_hours, :used_hours, :remark) + end + def search_params params.fetch(:q, {})&.permit(:s, :leave_type_eq, :effective_true, :user_id_eq) end diff --git a/app/controllers/backend/users_controller.rb b/app/controllers/backend/users_controller.rb index 3c9838d5..27e1cb2a 100644 --- a/app/controllers/backend/users_controller.rb +++ b/app/controllers/backend/users_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Backend::UsersController < Backend::BaseController before_action :set_query_object - before_action :set_minimum_password_length, only: %i[new edit] + before_action :set_minimum_password_length, only: %i(new edit) def show @leave_times = current_object.leave_times diff --git a/app/controllers/base_controller.rb b/app/controllers/base_controller.rb index 2d93bade..79ee8b4b 100644 --- a/app/controllers/base_controller.rb +++ b/app/controllers/base_controller.rb @@ -48,7 +48,7 @@ def action_fail(message, action) end def set_actions - @actions = [:create, :update, :destroy] + @actions = [:create, :update, :destroy, :batch_create] end def action_success(url = nil) @@ -103,4 +103,8 @@ def specific_year def specific_month params[:month] || Time.current.month end + + def specific_role + params[:role].empty? ? %w(employee parttime) : params[:role] + end end diff --git a/app/controllers/leave_applications_controller.rb b/app/controllers/leave_applications_controller.rb index 39b16b27..f5899dc4 100644 --- a/app/controllers/leave_applications_controller.rb +++ b/app/controllers/leave_applications_controller.rb @@ -2,11 +2,12 @@ class LeaveApplicationsController < BaseController include Selectable before_action :set_query_object + after_action :auto_approve_for_contractor, only: [:create, :update], if: :contractor? def index - @current_collection = collection_scope.with_year(specific_year) + @current_collection = collection_scope.page(params[:page]) + @current_collection = Kaminari.paginate_array(@current_collection.first(5)).page(params[:page]) unless query? @current_collection = @current_collection.with_status(params[:status]) if status_selected? - @current_collection = @current_collection.page(params[:page]) end def create @@ -55,10 +56,13 @@ def collection_scope end end + def query? + !params[:q].nil? + end + def search_params @search_params = params.fetch(:q, {})&.permit( :s, :leave_type_eq, :status_eq, :end_date_gteq, :start_date_lteq) - @search_params.present? ? @search_params : @search_params.merge(status_eq: :pending) end def set_query_object @@ -73,9 +77,17 @@ def resource_params def url_after(action) if @actions.include?(action) - url_for(action: :index, controller: controller_path, params: { status: :pending }) + url_for(action: :index, controller: controller_path) else request.env['HTTP_REFERER'] end end + + def auto_approve_for_contractor + current_object.approve!(current_user) + end + + def contractor? + current_user.contractor? + end end diff --git a/app/controllers/leave_times_controller.rb b/app/controllers/leave_times_controller.rb index a7632b60..d4bd8ae4 100644 --- a/app/controllers/leave_times_controller.rb +++ b/app/controllers/leave_times_controller.rb @@ -2,7 +2,7 @@ class LeaveTimesController < BaseController STARTING_YEAR = Settings.misc.starting_year.to_i SHOWINGS = %i(all effective).freeze - DEFAULT_SHOWING = 'effective' + DEFAULT_SHOWING = 'effective'.freeze before_action :set_query_object helper_method :showing @@ -28,6 +28,7 @@ def set_query_object end def search_params - params.fetch(:q, {})&.permit(:s, :leave_type_eq, :effective_true) + @search_params = params.fetch(:q, {})&.permit(:s, :leave_type_eq, :effective_true) + @search_params.present? ? @search_params : @search_params.merge(effective_true: true) end end diff --git a/app/controllers/remote_controller.rb b/app/controllers/remote_controller.rb index 8bbc5ef5..4f874811 100644 --- a/app/controllers/remote_controller.rb +++ b/app/controllers/remote_controller.rb @@ -2,7 +2,7 @@ class RemoteController < BaseController skip_load_and_authorize_resource load_and_authorize_resource class: LeaveApplication - def create + def create @current_object = collection_scope.new(resource_params) if @current_object.save action_success @@ -11,25 +11,23 @@ def create end end + private - private - - def collection_scope - current_user.leave_applications.where(leave_type: :remote) - end + def collection_scope + current_user.leave_applications.where(leave_type: :remote) + end - def resource_params - params.require(:remote_application).permit( - :start_time, :end_time, :description - ) - end + def resource_params + params.require(:remote_application).permit( + :start_time, :end_time, :description + ) + end - def url_after(action) + def url_after(action) if @actions.include?(action) url_for(controller: :leave_applications, action: :index) else request.env['HTTP_REFERER'] end end - end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 2b40ff6b..08fd964e 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -81,11 +81,20 @@ def specific_month params[:month] || Time.current.month end + def specific_role + params[:role].empty? ? %w(employee parttime) : params[:role] + end + def hours_to_humanize(hours) return '-' if hours.to_i.zero? I18n.t('time.humanize_working_hour', days: hours.to_i / 8, hours: hours % 8, total: hours) end + def hours_for_total(hours) + return '-' if hours.to_i.zero? + I18n.t('time.total_hour', total: hours) + end + def type_selector(name, label, options, default) render 'shared/type_selector', name: name, label: label, options: options, default: default end diff --git a/app/helpers/leave_applications_helper.rb b/app/helpers/leave_applications_helper.rb index c7fbdb15..a5670713 100644 --- a/app/helpers/leave_applications_helper.rb +++ b/app/helpers/leave_applications_helper.rb @@ -23,4 +23,22 @@ def colored_state_label(status) haml_tag :span, humanize_status, class: 'label label-danger' end end + + def need_deduct_salary?(leave_type, leave_time) + return if leave_time == 0 + leave_type == 'personal' || leave_type == 'halfpaid_sick' + end + + def summary_from(summary, user_id) + leave_types = Settings.leave_times.quota_types.keys + summary[user_id] || Hash[leave_types.collect{ |type| [type, 0]}] + end + + def leave_type_dropdown_menu(action_name, user) + if user.role == 'contractor' + LeaveApplication.enum_attributes_for_select(:leave_types, except = [:remote, :sick, :maternity, :marriage, :compassionate, :official, :occpational_sick, :menstrual]) + else + LeaveApplication.enum_attributes_for_select(:leave_types) + end + end end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 24289009..7548f09d 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true class ApplicationMailer < ActionMailer::Base - default from: 'from@example.com' + default from: 'hi@5xruby.tw' + layout 'mailer' + prepend_view_path Rails.root.join('app', 'views', 'mailers') end diff --git a/app/mailers/information_mailer.rb b/app/mailers/information_mailer.rb new file mode 100644 index 00000000..eb09bb5b --- /dev/null +++ b/app/mailers/information_mailer.rb @@ -0,0 +1,33 @@ +class InformationMailer < ApplicationMailer + ADMIN_EMAIL_LISTS = [ + 'hi@5xruby.tw' + ].freeze + + DEVELOP_EMAIL_LISTS = [ + 'eileen@5xruby.tw' + ].freeze + + def new_application(leave_application) + @leave_application = leave_application + + mail( + to: receiver_lists, + subject: "New Leave Application about #{@leave_application.user.name}" + ) + end + + def cancel_application(leave_application) + @leave_application = leave_application + + mail( + to: receiver_lists, + subject: "#{@leave_application.user.name} just canceled leave application" + ) + end + + private + + def receiver_lists + Rails.env.production? ? ADMIN_EMAIL_LISTS : DEVELOP_EMAIL_LISTS + end +end diff --git a/app/mailers/previews/information_mailer_preview.rb b/app/mailers/previews/information_mailer_preview.rb new file mode 100644 index 00000000..d2a91682 --- /dev/null +++ b/app/mailers/previews/information_mailer_preview.rb @@ -0,0 +1,6 @@ +class InformationMailerPreview < ActionMailer::Preview + def new_application + test_leave_application = LeaveApplication.last + InformationMailer.new_application(test_leave_application) + end +end diff --git a/app/mailers/previews/user_mailer_preview.rb b/app/mailers/previews/user_mailer_preview.rb new file mode 100644 index 00000000..c2f034b9 --- /dev/null +++ b/app/mailers/previews/user_mailer_preview.rb @@ -0,0 +1,6 @@ +class UserMailerPreview < ActionMailer::Preview + def reply_leave_applicaiton_email + test_leave_application = LeaveApplication.last + UserMailer.reply_leave_applicaiton_email(test_leave_application) + end +end diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 00000000..13ef1caf --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,16 @@ +class UserMailer < ApplicationMailer + def reply_leave_applicaiton_email(leave_application) + @leave_application = leave_application + if %w(staging development).include?(Rails.env) + mail( + to: @leave_application.user.email, + subject: "[測試信]<請假結果通知> 你的#{LeaveApplication.human_enum_value(:leave_type, @leave_application.leave_type)}已被#{LeaveApplication.human_enum_value(:status, @leave_application.status)}" + ) + else + mail( + to: @leave_application.user.email, + subject: "<請假結果通知> 你的#{LeaveApplication.human_enum_value(:leave_type, @leave_application.leave_type)}已被#{LeaveApplication.human_enum_value(:status, @leave_application.status)}" + ) + end + end +end diff --git a/app/models/ability.rb b/app/models/ability.rb index 9645a214..77d4f529 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -7,9 +7,11 @@ def initialize(user) case user.role when 'manager', 'hr' can :manage, :all - when 'employee' + when 'employee', 'parttime', 'intern' can :read, LeaveTime, user_id: user.id can :manage, LeaveApplication, user_id: user.id + when 'contractor' + can :manage, LeaveApplication, user_id: user.id end end end diff --git a/app/models/leave_application.rb b/app/models/leave_application.rb index 3503cd93..3d62859c 100644 --- a/app/models/leave_application.rb +++ b/app/models/leave_application.rb @@ -29,7 +29,7 @@ class LeaveApplication < ApplicationRecord ) } - scope :personal, ->(user_id, beginning, ending, status_array = %w[pending approved]) { + scope :personal, ->(user_id, beginning, ending, status_array = %w(pending approved)) { where(status: status_array, user_id: user_id).leave_within_range(beginning, ending) } @@ -59,11 +59,11 @@ class LeaveApplication < ApplicationRecord end event :reject, before: proc { |manager| sign(manager) } do - transitions to: :rejected, from: :pending + transitions to: :rejected, from: %i(pending approved) end event :revise do - transitions to: :pending, from: %i[pending approved] + transitions to: :pending, from: %i(pending approved) end event :cancel do @@ -94,7 +94,7 @@ def aasm_event?(event) end def special_type? - %w(marriage compassionate official maternity).include? self.leave_type + %w(marriage compassionate official maternity occpational_sick menstrual paid_vacation).include? self.leave_type end def available_leave_times @@ -114,7 +114,7 @@ def leave_time_params { user_id: self.user_id, leave_type: self.leave_type, - effective_date: self.start_time.to_date, + effective_date: self.start_time.to_date } end diff --git a/app/models/leave_application_observer.rb b/app/models/leave_application_observer.rb index 613175c1..6d753107 100644 --- a/app/models/leave_application_observer.rb +++ b/app/models/leave_application_observer.rb @@ -16,7 +16,8 @@ def after_save(record) def after_create(record) create_leave_time_usages(record) - FlowdockService.new(leave_application: record).send_create_notification + FlowdockService.new(leave_application: record).send_create_notification if Rails.env.production? + InformationMailer.new_application(record).deliver_later if Rails.env.production? end def before_update(record) @@ -25,7 +26,9 @@ def before_update(record) def after_update(record) create_leave_time_usages(record) if record.aasm_event?(:revise) - FlowdockService.new(leave_application: record).send_update_notification(record.aasm.to_state) + FlowdockService.new(leave_application: record).send_update_notification(record.aasm.to_state) if Rails.env.production? + UserMailer.reply_leave_applicaiton_email(record).deliver_later if record.aasm_event?(:approve) or record.aasm_event?(:reject) + InformationMailer.cancel_application(record).deliver_later if record.aasm_event?(:cancel) && Rails.env.production? end private @@ -47,7 +50,7 @@ def create_leave_hours_by_date(record) end def create_leave_time_usages(record) - raise ActiveRecord::Rollback if not LeaveTimeUsageBuilder.new(record).build_leave_time_usages and not record.special_type? + raise ActiveRecord::Rollback if !LeaveTimeUsageBuilder.new(record).build_leave_time_usages and !record.special_type? end def hours_transfer(record) @@ -65,21 +68,13 @@ def transfer_locked_hours_to_used_hours(record) def return_leave_time_usable_hours(record) record.leave_time_usages.each { |usage| revert_hours(record, usage) } - record.leave_time_usages.delete_all - end - - def revert_used_hours_to_usable_hours(record) - record.leave_time_usages.each { |usage| usage.leave_time.unuse_hours!(usage.used_hours) } - record.leave_time_usages.delete_all + record.leave_time_usages.destroy_all end def revert_hours(record, usage) - if record.aasm.from_state == :pending - usage.leave_time.unlock_hours!(usage.used_hours) - elsif record.aasm.from_state == :approved - usage.leave_time.unuse_hours!(usage.used_hours) - else - raise ActiveRecord::Rollback - end + usage.reload + return usage.leave_time.unlock_hours!(usage.used_hours) if record.aasm.from_state == :pending + return usage.leave_time.unuse_hours!(usage.used_hours) if record.aasm.from_state == :approved + raise ActiveRecord::Rollback end -end \ No newline at end of file +end diff --git a/app/models/leave_time.rb b/app/models/leave_time.rb index 13b1737a..1c31c9ef 100644 --- a/app/models/leave_time.rb +++ b/app/models/leave_time.rb @@ -54,7 +54,7 @@ def cover?(time_format) end def special_type? - %w(marriage compassionate official maternity).include? self.leave_type + %w(marriage compassionate official maternity occpational_sick menstrual).include? self.leave_type end def lock_hours(hours) diff --git a/app/models/leave_time_usage.rb b/app/models/leave_time_usage.rb index 32dddc73..9f07b697 100644 --- a/app/models/leave_time_usage.rb +++ b/app/models/leave_time_usage.rb @@ -8,6 +8,6 @@ class LeaveTimeUsage < ApplicationRecord private def transfer_leave_time_hours - self.leave_time.reload.lock_hours!(self.used_hours) + self.leave_time.reload.lock_hours!(self.used_hours) end end diff --git a/app/models/user.rb b/app/models/user.rb index 0937498a..04609718 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -34,21 +34,26 @@ class User < ApplicationRecord scope :valid, -> { where('join_date <= :now', now: Date.current) - .where.not(role: %w[pending resigned]) + .where.not(role: %w(pending resigned)) } scope :fulltime, -> { - where('role in (?)', %w[manager employee hr]) + where('role in (?)', %w(manager employee hr)) .valid .order(id: :desc) } scope :parttime, -> { - where('role in (?)', %w[contractor intern]) + where('role in (?)', %w(contractor intern)) .valid .order(id: :desc) } + scope :filter_by_role, ->(role) { + where(role: role) + .order(id: :desc) + } + Settings.roles.each do |key, role| define_method "is_#{role}?" do self.role.to_s == role @@ -61,7 +66,7 @@ def seniority(time = Date.current) end def fulltime? - %w[manager hr employee].include?(role) + %w(manager hr employee).include?(role) end def this_year_join_anniversary diff --git a/app/services/flowdock_service.rb b/app/services/flowdock_service.rb index 58d44b65..7ac372a9 100644 --- a/app/services/flowdock_service.rb +++ b/app/services/flowdock_service.rb @@ -4,7 +4,7 @@ class FlowdockService attr_accessor :leave_application - def initialize(attributes={}) + def initialize(attributes = {}) super @flow_token_client = Flowdock::Client.new(flow_token: Settings.flowdock.token) end @@ -14,45 +14,45 @@ def send_create_notification subject: "#{leave_application.user.name} 新增了一筆 #{LeaveApplication.human_enum_value(:leave_type, leave_application.leave_type)} 假單", content: (LeaveApplicationsController.render partial: 'leave_applications/notification', locals: { leave_application: leave_application }), url: verify_backend_leave_application_url(id: leave_application), - color: "green", - value: "Create", + color: 'green', + value: 'Create', thread_id: leave_application.id ) end def send_update_notification(event) return if event.blank? - executer = %i[canceled pending].include?(event) ? leave_application.user : leave_application.manager + executer = %i(canceled pending).include?(event) ? leave_application.user : leave_application.manager notify( subject: "#{executer.name} #{LeaveApplication.human_enum_value(:modify_actions, event)}了一筆 #{leave_application.user.name} 的 #{LeaveApplication.human_enum_value(:leave_type, leave_application.leave_type)} 假單", content: (LeaveApplicationsController.render partial: 'leave_applications/notification', locals: { leave_application: leave_application }), url: verify_backend_leave_application_url(id: leave_application), - color: "yellow", - value: "Update", + color: 'yellow', + value: 'Update', thread_id: leave_application.id ) end private - def notify(subject: , content: , url: , color: , value: , thread_id:) + def notify(subject:, content:, url:, color:, value:, thread_id:) @flow_token_client.post_to_thread( - event: "activity", - author: { - name: "Daikichi", - avatar: "http://i.imgur.com/ycz7jqg.png", - }, - title: "5xRuby", - external_thread_id: thread_id, - thread: { + event: 'activity', + author: { + name: 'Daikichi', + avatar: 'http://i.imgur.com/ycz7jqg.png' + }, + title: '5xRuby', + external_thread_id: thread_id, + thread: { title: subject, body: content, external_url: url, status: { - color: color, - value: value + color: color, + value: value } } ) - end -end \ No newline at end of file + end +end diff --git a/app/services/leave_time_batch_builder.rb b/app/services/leave_time_batch_builder.rb index 1d5ac345..cda874b3 100644 --- a/app/services/leave_time_batch_builder.rb +++ b/app/services/leave_time_batch_builder.rb @@ -10,20 +10,16 @@ def initialize(forced: false) def automatically_import batch_join_date_based_import batch_monthly_import + batch_weekly_import end private def batch_join_date_based_import leed_day = Time.current + JOIN_DATE_BASED_LEED_DAYS.days - users = if @forced - User.valid.where('(EXTRACT(MONTH FROM join_date), EXTRACT(DAY FROM join_date)) <= (:month, :date)', month: leed_day.month, date: leed_day.day) - .where.not(join_date: ((Time.current - 1.year).to_date)..(Time.current)) - else - User.valid.filter_by_join_date(reaching_join_date.month, reaching_join_date.day) - end - return unless users.present? - users.find_each do |user| + find_user_by_forced(User.valid, leed_day) + return if @users.empty? + @users.each do |user| if @forced LeaveTimeBuilder.new(user).join_date_based_import else @@ -43,6 +39,17 @@ def batch_monthly_import end end + def batch_weekly_import + return if !Time.current.monday? && !@forced + User.valid.find_each do |user| + if @forced + LeaveTimeBuilder.new(user).weekly_import + else + LeaveTimeBuilder.new(user).weekly_import(prebuild: true) + end + end + end + def end_of_working_month? @end_of_working_month_bool ||= Daikichi::Config::Biz.time(MONTHLY_LEED_DAYS, :days).after(Time.current.beginning_of_day).to_date == Daikichi::Config::Biz.periods.before(Time.current.end_of_month).first.end_time.to_date end @@ -50,4 +57,13 @@ def end_of_working_month? def reaching_join_date @reaching_join_date ||= Time.zone.today + JOIN_DATE_BASED_LEED_DAYS.days end + + def find_user_by_forced(valid_users, leed_day) + if @forced + @users = [] + valid_users.where.not(join_date: (Time.current.to_date)..(Time.current)).find_each.map { |user| @users << user if user.this_year_join_anniversary - leed_day.to_date <= 60 } + else + @users = valid_users.filter_by_join_date(reaching_join_date.month, reaching_join_date.day) + end + end end diff --git a/app/services/leave_time_builder.rb b/app/services/leave_time_builder.rb index 0f8a766f..53ca89bf 100644 --- a/app/services/leave_time_builder.rb +++ b/app/services/leave_time_builder.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class LeaveTimeBuilder MONTHLY_LEAVE_TYPES = Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'monthly' } + WEEKLY_LEAVE_TYPES = Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'weekly' } JOIN_DATE_BASED_LEAVE_TYPES = Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'join_date_based' } JOIN_DATE_BASED_LEED_DAY = Settings.leed_days.join_date_based.day @@ -11,23 +12,35 @@ def initialize(user) def automatically_import(by_assign_date: false) monthly_import by_assign_date: by_assign_date join_date_based_import by_assign_date: by_assign_date + weekly_import by_assign_date: by_assign_date end def join_date_based_import(by_assign_date: false, prebuild: false) + return create_leave_time('personal', 2920, @user.join_date, @user.join_date.next_year) if @user.role == 'contractor' JOIN_DATE_BASED_LEAVE_TYPES.each do |leave_type, config| - build_join_date_based_leave_types(leave_type, config, by_assign_date, prebuild) + build_join_date_based_leave_types(leave_type, config, prebuild, by_assign_date) end end def monthly_import(by_assign_date: false, prebuild: false) + return if @user.role == 'contractor' MONTHLY_LEAVE_TYPES.each do |leave_type, config| - build_monthly_leave_types(leave_type, config, by_assign_date, prebuild) + build_monthly_leave_types(leave_type, config, prebuild, by_assign_date) + end + end + + def weekly_import(by_assign_date: false, prebuild: false) + return if @user.role == 'contractor' + return unless by_assign_date || Time.current.monday? + date = Time.zone.today + 4.weeks + WEEKLY_LEAVE_TYPES.each do |leave_type, config| + build_weekly_leave_types(leave_type, config, date, by_assign_date) end end private - def build_join_date_based_leave_types(leave_type, config, build_by_assign_date = false, prebuild) + def build_join_date_based_leave_types(leave_type, config, prebuild, build_by_assign_date = false) return unless user_can_have_leave_type?(@user, config) quota = extract_quota(config, @user, prebuild: prebuild) if build_by_assign_date @@ -60,7 +73,7 @@ def join_date_based_by_assign_date(leave_type, quota) create_leave_time(leave_type, quota, date, expiration_date) if Time.zone.now.to_date + JOIN_DATE_BASED_LEED_DAY >= date or @user.join_date + 1.year >= Time.zone.now.to_date end - def build_monthly_leave_types(leave_type, config, build_by_assign_date = false, prebuild) + def build_monthly_leave_types(leave_type, config, prebuild, build_by_assign_date = false) quota = extract_quota(config, @user, prebuild: prebuild) if build_by_assign_date date = @user.assign_date >= @user.join_date ? @user.assign_date : @user.join_date @@ -76,6 +89,22 @@ def build_monthly_leave_types(leave_type, config, build_by_assign_date = false, end end + def build_weekly_leave_types(leave_type, config, date, build_by_assign_date) + quota = extract_quota(config, @user) + if build_by_assign_date + effective_date = @user.assign_date + expiration_date = @user.assign_date.end_of_week + create_leave_time(leave_type, quota, effective_date, expiration_date) + create_leave_time(leave_type, quota, effective_date.beginning_of_week + 1.week, expiration_date + 1.week) + create_leave_time(leave_type, quota, effective_date.beginning_of_week + 2.week, expiration_date + 2.week) + create_leave_time(leave_type, quota, effective_date.beginning_of_week + 3.week, expiration_date + 3.week) + else + effective_date = date.beginning_of_week + expiration_date = date.end_of_week + create_leave_time(leave_type, quota, effective_date, expiration_date) + end + end + def create_leave_time(leave_type, quota, effective_date, expiration_date) @user.leave_times.create( leave_type: leave_type, diff --git a/app/services/leave_time_summary_service.rb b/app/services/leave_time_summary_service.rb new file mode 100644 index 00000000..bee63d98 --- /dev/null +++ b/app/services/leave_time_summary_service.rb @@ -0,0 +1,49 @@ +class LeaveTimeSummaryService + def initialize(year, month, role = %w(employee parttime)) + @range = ( + Time.zone.local(year, month).beginning_of_month.. + Time.zone.local(year, month).end_of_month + ) + @types = Settings.leave_times.quota_types.keys + @role = role + end + + def user_applications + @user_applications ||= LeaveApplication.where(user: User.filter_by_role(@role)).leave_within_range(@range.min, @range.max) + .approved + .includes(:leave_time_usages, :leave_times) + .group_by(&:user_id) + end + + def application_to_usage(applications) + applications.map(&:leave_time_usages) + .flatten + .group_by { |usage| usage.leave_time.leave_type } + .map { |k, v| [k, used_hours_in_month(v).sum] } + end + + def summary + @summary ||= Hash[ + user_applications.map do |(id, applications)| + [ + id, + default_columns.merge(Hash[application_to_usage(applications)]) + ] + end + ] + end + + private + + def default_columns + Hash[@types.collect { |type| [type, 0] }] + end + + def used_hours_in_month(usages) + usages_in_month(usages).map(&:used_hours) + end + + def usages_in_month(usages) + usages.select { |usage| usage.date.between? @range.min, @range.max } + end +end diff --git a/app/services/leave_time_usage_builder.rb b/app/services/leave_time_usage_builder.rb index 1c7e515c..8a785036 100644 --- a/app/services/leave_time_usage_builder.rb +++ b/app/services/leave_time_usage_builder.rb @@ -19,15 +19,16 @@ def build_leave_time_usages @available_leave_times.each do |lt| @leave_hours_by_date.keys.each do |date| + hours = @leave_hours_by_date[date] break if usable_hours_is_empty?(lt) - next if corresponding_leave_hours_date_is_zero?(date) or not in_leave_time_inteval_range?(lt, date) + next if corresponding_leave_hours_date_is_zero?(date) or !in_leave_time_inteval_range?(lt, date) deduct_leave_hours_by_date(lt, date) + stack_leave_time_usage_record(lt, date, hours - @leave_hours_by_date[date]) end - stack_leave_time_usage_record(lt) break if leave_hours_by_date_is_empty? end - if not leave_hours_by_date_is_empty? + if !leave_hours_by_date_is_empty? rollback_with_error_message unless @leave_application.special_type? else create_leave_time_usage @@ -53,7 +54,6 @@ def validate_application_covered_by_leave_time_interval include_end_time = true if lt.cover?(@leave_hours_by_date.keys.last) break if include_start_time && include_end_time end - rollback_with_error_message unless include_start_time && include_end_time end @@ -79,17 +79,14 @@ def in_leave_time_inteval_range?(leave_time, date) date.between?(leave_time.effective_date, leave_time.expiration_date) end - def stack_leave_time_usage_record(leave_time) - @leave_time_usages.push(leave_time: leave_time, used_hours: leave_time.usable_hours_was - leave_time.usable_hours) + def stack_leave_time_usage_record(leave_time, date, used_hours) + @leave_time_usages.push(leave_time: leave_time, used_hours: used_hours, date: date) end def leave_hours_by_date_is_empty? @leave_hours_by_date.values.all?(&:zero?) end - def unless_remain_leave_hours_by_date - end - def rollback_with_error_message append_leave_application_error_message raise ActiveRecord::Rollback @@ -102,7 +99,7 @@ def append_leave_application_error_message def create_leave_time_usage @leave_time_usages.each do |lt_usage| - @leave_application.leave_time_usages.create!(leave_time: lt_usage[:leave_time], used_hours: lt_usage[:used_hours]) + @leave_application.leave_time_usages.create!(leave_time: lt_usage[:leave_time], used_hours: lt_usage[:used_hours], date: lt_usage[:date]) end end end diff --git a/app/views/backend/leave_applications/_form.html.haml b/app/views/backend/leave_applications/_form.html.haml index 2b6a4668..b2cdf7bc 100644 --- a/app/views/backend/leave_applications/_form.html.haml +++ b/app/views/backend/leave_applications/_form.html.haml @@ -1,6 +1,21 @@ -= simple_form_for current_object, url: backend_leave_application_path(current_object), method: :put do |f| - = f.input :comment - = f.button :submit, t("title.backend/leave_applications.approve"), disabled: !current_object.may_approve?, name: "approve", class: "btn btn-primary" - = f.button :submit, t("title.backend/leave_applications.reject"), name: "reject", class: "btn btn-danger" - - if current_object.special_type? and not current_object.may_approve? - = link_to t("title.backend/leave_applications.append_quota"), append_quota_backend_leave_times_path(leave_application_id: current_object), method: :post, class: 'btn btn-warning' +- if action == :verify + = simple_form_for current_object, url: backend_leave_application_path(current_object), method: :put do |f| + = f.input :comment + = f.button :submit, t("title.backend/leave_applications.approve"), disabled: !current_object.may_approve?, name: "approve", class: "btn btn-primary" + = f.button :submit, t("title.backend/leave_applications.reject"), name: "reject", class: "btn btn-danger" + - if current_object.special_type? and not current_object.may_approve? + = link_to t("title.backend/leave_applications.append_quota"), append_quota_backend_leave_times_path(leave_application_id: current_object), method: :post, class: 'btn btn-warning' + +- elsif action == :create + %h2= t("captions.backend/leave_applications.#{action_name}") + %p= t("captions.backend/leave_applications.#{action_name}/hint") + = simple_form_for current_object, url: url_for(action: action), method: :post do |f| + = f.association :user, collection: User.where.not(role: 'resigned'), label_method: :name, value_method: :id, required: true + = f.error :base, error: sanitize(f.object.errors[:base].join('
'), tags: %w(a br), attributes: %w(href target)) + = f.input :leave_type, required: true, collection: LeaveApplication.enum_attributes_for_select(:leave_types) + = f.input :start_time, as: :string, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } + = f.input :end_time, as: :string, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } + = f.error :hours, error: sanitize(f.object.errors[:hours].join('
'), tags: %w(br)) + = f.input :description, required: true + = f.input :comment + = f.button :submit, class: "submit" diff --git a/app/views/backend/leave_applications/index.html.haml b/app/views/backend/leave_applications/index.html.haml index 53a8a376..dbcf8db3 100644 --- a/app/views/backend/leave_applications/index.html.haml +++ b/app/views/backend/leave_applications/index.html.haml @@ -13,9 +13,9 @@ include_blank: t('.plz_select_user'), label_method: :name, value_method: :id, wrapper_html: { class: 'col-md-4' } = f.input :end_date_gteq, wrapper_html: { class: 'date-input col-md-6' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } }, required: false + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } }, required: false = f.input :start_date_lteq, wrapper_html: { class: 'date-input col-md-6' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } }, required: false + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } }, required: false .text-right = f.button :submit, class: 'btn-warning' diff --git a/app/views/backend/leave_applications/new.html.haml b/app/views/backend/leave_applications/new.html.haml new file mode 100644 index 00000000..2c587d9e --- /dev/null +++ b/app/views/backend/leave_applications/new.html.haml @@ -0,0 +1 @@ += render "form", action: :create \ No newline at end of file diff --git a/app/views/backend/leave_applications/statistics.html.haml b/app/views/backend/leave_applications/statistics.html.haml index 8aa5535c..59cdf0ec 100644 --- a/app/views/backend/leave_applications/statistics.html.haml +++ b/app/views/backend/leave_applications/statistics.html.haml @@ -1,44 +1,41 @@ -%h2= t("captions.backend/leave_applications.statistics", year: specific_year, month: specific_month) +%h2= t("captions.backend/leave_applications.statistics", year: specific_year, month: specific_month, role: params[:role]) .panel.panel-default .panel-body .year-mon-selector .row - .col-md-6.col-sm-6.col-xs-12 + .col-md-4.col-sm-4.col-xs-12 .form-group %label{:for=>"year"} Year = select_tag "year", options_for_select(2016 .. Time.now.year, specific_year), class: "form-control" - .col-md-6.col-sm-6.col-xs-12 + .col-md-4.col-sm-4.col-xs-12 .form-group %label{:for=>"month"} Month = select_tag "month", options_for_select(1..12, specific_month), class: "form-control" + .col-md-4.col-sm-4.col-xs-12 + .form-group + %label{:for=>"role"} Role + = select_tag "role", options_for_select(User.enum_attributes_for_select(:roles), params[:role]), + include_blank:true, class: "form-control" -- if @statistics.columns[0].present? - %table.table.table-striped.table-bordered.table-hover - %thead - %tr.info - %th.text-center.name= t_attribute(:name, User) - - @statistics.columns.each do |leave_type| - %th.text-right= LeaveTime.human_enum_value(:leave_type, leave_type.header) - %th.text-right 總計 - - %tbody - - @statistics.rows.each do |sum| - %tr - %td.text-center.name= sum.header - - sum.data.each do |data| - - if data.present? - %td.text-right= hours_to_humanize data.sum - - else - %td.text-right - - %td.text-right= hours_to_humanize sum.total - -- else - = no_data_alert +%table.table.table-striped.table-bordered.table-hover + %thead + %tr.info + %th.text-center.name= t_attribute(:name, User) + - Settings.leave_times.quota_types.keys.each do |leave_type| + %th.text-right= t("activerecord.attributes.leave_time.leave_types.#{leave_type}") + %th.text-right 總計 + %tbody + - User.filter_by_role(specific_role).find_each do |user| + %tr + %td.text-center.name= user.name + - summary_from(@summary, user.id).each do |key, value| + %td.text-right{ class: ('highlight' if need_deduct_salary?(key, value)) }= hours_for_total value + %td.text-right= hours_to_humanize summary_from(@summary, user.id).values.sum diff --git a/app/views/backend/leave_applications/verify.html.haml b/app/views/backend/leave_applications/verify.html.haml index 6f347c82..55b4b408 100644 --- a/app/views/backend/leave_applications/verify.html.haml +++ b/app/views/backend/leave_applications/verify.html.haml @@ -3,6 +3,11 @@ %h4.panel-title= t("captions.backend/leave_applications.verify") %table.table + + %tr + %th= t_attribute :user, LeaveApplication + %td= current_object.user_name + %tr %th= t_attribute(:leave_type, current_object) %td= LeaveTime.human_enum_value(:leave_types, current_object.leave_type) @@ -50,6 +55,7 @@ %tr %th.text-right # %th.text-center 假別 + %th.text-right 日期 %th.text-right 使用時數 %th.text-right 餘額 %th.text-right 生效日期 @@ -59,11 +65,13 @@ %tr %td.text-right= link_to usage.leave_time_id, backend_leave_time_path(usage.leave_time_id) %td.text-center= LeaveTime.human_enum_value(:leave_type, usage.leave_time.leave_type) + %td.text-right= usage.date + %td.text-right= hours_to_humanize usage.used_hours %td.text-right= hours_to_humanize usage.leave_time.usable_hours %td.text-right= l usage.leave_time.effective_date, format: :detailed %td.text-right= l usage.leave_time.expiration_date, format: :detailed -- if current_object.pending? - = render "form" +- if current_object.pending? or current_object.approved? + = render "form", action: :verify \ No newline at end of file diff --git a/app/views/backend/leave_times/_form.html.haml b/app/views/backend/leave_times/_form.html.haml index db80eb15..e1d31925 100644 --- a/app/views/backend/leave_times/_form.html.haml +++ b/app/views/backend/leave_times/_form.html.haml @@ -1,13 +1,13 @@ %h2= t("captions.backend/leave_times.#{action_name}") = simple_form_for [:backend, current_object] do |f| - = f.association :user, required: true + = f.association :user, required: true, collection: User.where.not(role: 'resigned') = f.input :leave_type, required: true, collection: LeaveTime.enum_attributes_for_select(:leave_types) = f.input :quota = f.input :effective_date, as: :string, required: true, wrapper_html: { class: 'date-input' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } } + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } = f.input :expiration_date, as: :string, required: true, wrapper_html: { class: 'date-input' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } } + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } = f.input :remark, as: :text - unless current_object.new_record? = f.input :usable_hours diff --git a/app/views/backend/leave_times/batch_new.html.haml b/app/views/backend/leave_times/batch_new.html.haml new file mode 100644 index 00000000..5e8f9042 --- /dev/null +++ b/app/views/backend/leave_times/batch_new.html.haml @@ -0,0 +1,16 @@ +%h2= t("captions.backend/leave_times.#{action_name}") + += simple_form_for current_object, url: batch_create_backend_leave_times_path do |f| + = f.association :user, required: true, collection: User.where.not(role: 'resigned'),as: :check_boxes, include_hidden: false, item_wrapper_class: "checkbox-inline", label: false + = f.input :leave_type, required: true, collection: LeaveTime.enum_attributes_for_select(:leave_types) + = f.input :quota + = f.input :effective_date, as: :string, required: true, wrapper_html: { class: 'date-input' }, + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } + = f.input :expiration_date, as: :string, required: true, wrapper_html: { class: 'date-input' }, + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } + = f.input :remark, as: :text + - unless current_object.new_record? + = f.input :usable_hours + = f.input :used_hours + = link_to t("title.backend/leave_times.delete"), backend_leave_time_path(current_object), method: :delete, class: "btn btn-sm btn-danger", data: {confirm: "Are you sure?"} + = f.button :submit diff --git a/app/views/backend/leave_times/show.html.haml b/app/views/backend/leave_times/show.html.haml index dd280529..21b453f0 100644 --- a/app/views/backend/leave_times/show.html.haml +++ b/app/views/backend/leave_times/show.html.haml @@ -45,18 +45,19 @@ %th.text-right= t_attribute :start_time, LeaveApplication %th.text-right= t_attribute :end_time, LeaveApplication %tbody - - current_object.leave_time_usages.each do |usage| + - current_object.leave_time_usages.group_by(&:leave_application).each do |usage| %tr %td.text-right - = link_to usage.leave_application_id, verify_backend_leave_application_path(usage.leave_application_id) - %td.text-center= LeaveApplication.human_enum_value(:leave_type, usage.leave_application.leave_type) - %td.text-right= usage.leave_application.pending? ? hours_to_humanize(usage.used_hours) : '-' - %td.text-right= usage.leave_application.approved? ? hours_to_humanize(usage.used_hours) : '-' - %td.text-right= hours_to_humanize usage.leave_application.hours + = link_to usage[0].id, verify_backend_leave_application_path(usage[0]) + %td.text-center= LeaveApplication.human_enum_value(:leave_type, usage[0].leave_type) + %td.text-right= usage[0].pending? ? hours_to_humanize(usage[1].sum(&:used_hours)) : '-' + %td.text-right= usage[0].approved? ? hours_to_humanize(usage[1].sum(&:used_hours)) : '-' + %td.text-right= hours_to_humanize usage[0].hours %td.text-center - - colored_state_label(usage.leave_application.status) - %td.text-right= l usage.leave_application.start_time, format: :long - %td.text-right= l usage.leave_application.end_time, format: :long + - colored_state_label(usage[0].status) + %td.text-right= l usage[0].start_time, format: :long + %td.text-right= l usage[0].end_time, format: :long + %tr %th.active.text-right{colspan: 2}= t('.sum') %td.text-right= hours_to_humanize current_object.leave_time_usages.select { |u| u.leave_application.pending? }.sum(&:used_hours) diff --git a/app/views/backend/users/_form.html.haml b/app/views/backend/users/_form.html.haml index c2edd44c..009fbb87 100644 --- a/app/views/backend/users/_form.html.haml +++ b/app/views/backend/users/_form.html.haml @@ -5,14 +5,14 @@ = f.input :login_name, required: true = f.input :email, required: true = f.input :join_date, as: :string, required: true, wrapper_html: { class: 'date-input' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } } + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } = f.input :leave_date, as: :string, wrapper_html: { class: 'date-input' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } } + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } - if %w(new create).include? action_name = f.input :assign_leave_time, as: :boolean = f.input :assign_date, wrapper_html: { class: 'date-input' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } } + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } } = f.input :password, required: true, hint: ("密碼長度須 #{@minimum_password_length} 字以上" if @minimum_password_length) = f.input :password_confirmation, required: true - else diff --git a/app/views/backend/users/index.html.haml b/app/views/backend/users/index.html.haml index c034d01f..a97e5dbe 100644 --- a/app/views/backend/users/index.html.haml +++ b/app/views/backend/users/index.html.haml @@ -16,7 +16,7 @@ wrapper_html: { class: 'col-md-4' }, label: t('.id_eq') = f.input :role_eq, collection: User.enum_attributes_for_select(:roles), required: false, include_blank: t('.plz_select_role'), wrapper_html: { class: 'col-md-4' },label: t('.role_eq') - + .text-right = f.button :submit, class: 'btn-warning' @@ -39,17 +39,20 @@ %tbody - current_collection.each do |user| - %tr - %td.text-right= user.id - %td.text-center= user.name - %td.text-center= user.login_name - %td.text-center= user.email - %td.text-center= User.human_enum_value(:role, user.role) - %td.text-right= l user.join_date, format: :detailed - %td.text-center - = link_to t("title.backend/users.show"), - backend_user_path(user), - class: "btn btn-primary" - = link_to t("title.backend/users.edit"), - edit_backend_user_path(user), - class: "btn btn-warning" + - unless user.role == 'resigned' + %tr + %td.text-right= user.id + %td.text-center= user.name + %td.text-center= user.login_name + %td.text-center= user.email + %td.text-center= User.human_enum_value(:role, user.role) + %td.text-right= l user.join_date, format: :detailed + %td.text-center + = link_to t("title.backend/users.show"), + backend_user_path(user), + class: "btn btn-primary" + = link_to t("title.backend/users.edit"), + edit_backend_user_path(user), + class: "btn btn-warning" +.paginate + = paginate current_collection diff --git a/app/views/backend/users/show.html.haml b/app/views/backend/users/show.html.haml index ef13cb7f..4afde2ea 100644 --- a/app/views/backend/users/show.html.haml +++ b/app/views/backend/users/show.html.haml @@ -14,11 +14,11 @@ %td.col-md-9= User.human_enum_value(:roles, current_object.role) %tr %th.col-md-3= t_attribute(:join_date, User) - %td.col-md-9= l current_object.join_date, format: :long + %td.col-md-9= l current_object.join_date, format: :default - if current_object.leave_date %tr %th.col-md-3= t_attribute(:leave_date, User) - %td.col-md-9= l current_object.leave_date, format: :long + %td.col-md-9= l current_object.leave_date, format: :default @@ -47,7 +47,7 @@ %td.text-right= hours_to_humanize leave_time.locked_hours %td.text-right= hours_to_humanize leave_time.used_hours %td.text-right= hours_to_humanize leave_time.usable_hours - %td.text-right= l leave_time.effective_date, format: :long - %td.text-right= l leave_time.expiration_date, format: :long + %td.text-right= l leave_time.effective_date, format: :default + %td.text-right= l leave_time.expiration_date, format: :default diff --git a/app/views/devise/mailer/confirmation_instructions.text.erb b/app/views/devise/mailer/confirmation_instructions.text.erb new file mode 100644 index 00000000..e349c8ab --- /dev/null +++ b/app/views/devise/mailer/confirmation_instructions.text.erb @@ -0,0 +1,5 @@ +Welcome <%= @email %>! + +You can confirm your account email through the link below: + +<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %> \ No newline at end of file diff --git a/app/views/devise/mailer/password_change.text.erb b/app/views/devise/mailer/password_change.text.erb new file mode 100644 index 00000000..4f9f9620 --- /dev/null +++ b/app/views/devise/mailer/password_change.text.erb @@ -0,0 +1,3 @@ +Hello <%= @resource.email %>! + +We're contacting you to notify you that your password has been changed. diff --git a/app/views/devise/mailer/reset_password_instructions.text.erb b/app/views/devise/mailer/reset_password_instructions.text.erb new file mode 100644 index 00000000..a307afa8 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.text.erb @@ -0,0 +1,8 @@ +Hello <%= @resource.email %>! + +Someone has requested a link to change your password. You can do this through the link below. + +<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %> + +If you didn't request this, please ignore this email. +Your password won't change until you access the link above and create a new one. \ No newline at end of file diff --git a/app/views/devise/mailer/unlock_instructions.text.erb b/app/views/devise/mailer/unlock_instructions.text.erb new file mode 100644 index 00000000..e3ed33d0 --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.text.erb @@ -0,0 +1,7 @@ +Hello <%= @resource.email %>! + +Your account has been locked due to an excessive number of unsuccessful sign in attempts. + +Click the link below to unlock your account: + +<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %> diff --git a/app/views/leave_applications/_form.html.haml b/app/views/leave_applications/_form.html.haml index 937c77d1..f8589924 100644 --- a/app/views/leave_applications/_form.html.haml +++ b/app/views/leave_applications/_form.html.haml @@ -1,14 +1,13 @@ %h2= t("captions.leave_applications.#{action_name}") = simple_form_for current_object, url: url_for(action: action), method: method do |f| - = f.error :base, error: sanitize(f.object.errors[:base].join('
'), tags: %w(a br), attributes: %w(href target)) - - if action_name == "edit" - = f.input :leave_type, required: true, collection: LeaveApplication.enum_attributes_for_select(:leave_types), disabled: true + = f.error :base, error: sanitize(f.object.errors[:base].join('
'), tags: %w(a br), attributes: %w(href target)) + - if action_name == 'edit' + = f.input :leave_type, required: true, collection: LeaveApplication.enum_attributes_for_select(:leave_types, except = [:remote]), disabled: true - else - = f.input :leave_type, require: true, collection: LeaveApplication.enum_attributes_for_select(:leave_types, except: [:remote]) - = f.input :start_time, date_time_picker_hash(:start, current_object[:start_time]) - = f.input :end_time, date_time_picker_hash(:end, current_object[:end_time]) + = f.input :leave_type, required: true, collection: leave_type_dropdown_menu(action_name, current_user) + = f.input :start_time, as: :string, required: true, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } + = f.input :end_time, as: :string, required: true, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } = f.error :hours, error: sanitize(f.object.errors[:hours].join('
'), tags: %w(br)) = f.input :description, required: true - = f.button :submit, class: "submit" - + = f.button :submit, class: "submit" \ No newline at end of file diff --git a/app/views/leave_applications/index.html.haml b/app/views/leave_applications/index.html.haml index 424b11d6..29d0747f 100644 --- a/app/views/leave_applications/index.html.haml +++ b/app/views/leave_applications/index.html.haml @@ -1,4 +1,4 @@ -%h2= t("captions.leave_applications.index", year: specific_year) +%h2= t("captions.leave_applications.index") .panel.panel-default .panel-heading= t('panel_header.filter_conditions') @@ -10,9 +10,9 @@ = f.input :leave_type_eq, collection: LeaveApplication.enum_attributes_for_select(:leave_types), include_blank: t('.plz_select_leave_type'), required: false, wrapper_html: { class: 'col-md-6' } = f.input :end_date_gteq, wrapper_html: { class: 'date-input col-md-6' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } }, required: false + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } }, required: false = f.input :start_date_lteq, wrapper_html: { class: 'date-input col-md-6' }, - input_html: { data: { input: 'datepicker', format: 'YYYY/MM/DD' } }, required: false + input_html: { data: { input: 'datepicker', format: 'Y/m/d' } }, required: false .text-right = f.button :submit, class: 'btn-warning' @@ -46,11 +46,11 @@ = link_to t("title.leave_applications.show"), leave_application_path(leave), class: "btn btn-primary" - - if leave.may_revise? + - if leave.may_revise? and !leave.happened? = link_to t("title.leave_applications.edit"), edit_leave_application_path(leave), class: "btn btn-warning" - - if leave.may_cancel? + - if leave.may_cancel? and !leave.happened? = link_to t("title.leave_applications.cancel"), cancel_leave_application_path(leave), class: "btn btn-danger", @@ -62,4 +62,3 @@ .paginate = paginate current_collection - \ No newline at end of file diff --git a/app/views/leave_applications/show.html.haml b/app/views/leave_applications/show.html.haml index 7c0439af..d0c7bc8d 100644 --- a/app/views/leave_applications/show.html.haml +++ b/app/views/leave_applications/show.html.haml @@ -44,26 +44,29 @@ %th= t_attribute(:updated_at, current_object) %td= l current_object.updated_at, format: :long - - -.panel.panel-info - .panel-heading - %h4= t_attribute(:leave_time_usages, current_object) - %table.table - %thead - %tr - %th.text-right # - %th.text-center= t_attribute(:leave_type, LeaveTime) - %th.text-right 使用時數 - %th.text-right= t_attribute(:quota,LeaveTime) - %th.text-right= t_attribute(:effective_date,LeaveTime) - %th.text-right= t_attribute(:expiration_date,LeaveTime) - %tbody - - current_object.leave_time_usages.each do |usage| +- unless current_user.contractor? + .panel.panel-info + .panel-heading + %h4= t_attribute(:leave_time_usages, current_object) + %table.table + %thead %tr - %td.text-right= link_to usage.leave_time_id, leave_time_path(usage.leave_time_id) - %td.text-center= LeaveTime.human_enum_value(:leave_type, usage.leave_time.leave_type) - %td.text-right= hours_to_humanize usage.used_hours - %td.text-right= hours_to_humanize usage.leave_time.usable_hours - %td.text-right= l usage.leave_time.effective_date, format: :long - %td.text-right= l usage.leave_time.expiration_date, format: :long + %th.text-right # + %th.text-center= t_attribute(:leave_type, LeaveTime) + %th.text-right 日期 + %th.text-right 使用時數 + %th.text-right= t_attribute(:quota,LeaveTime) + %th.text-right= t_attribute(:effective_date,LeaveTime) + %th.text-right= t_attribute(:expiration_date,LeaveTime) + %tbody + - current_object.leave_time_usages.each do |usage| + %tr + - if current_object.leave_type == 'sick' + %td.text-right= usage.leave_time_id + - else + %td.text-right= link_to usage.leave_time_id, leave_time_path(usage.leave_time_id) + %td.text-center= LeaveTime.human_enum_value(:leave_type, usage.leave_time.leave_type) + %td.text-right= usage.date + %td.text-right= hours_to_humanize usage.used_hours + %td.text-right= hours_to_humanize usage.leave_time.usable_hours + %td.text-right= l usage.leave_time.effective_date, format: :long \ No newline at end of file diff --git a/app/views/leave_times/index.html.haml b/app/views/leave_times/index.html.haml index eedce259..bc69e9c1 100644 --- a/app/views/leave_times/index.html.haml +++ b/app/views/leave_times/index.html.haml @@ -6,7 +6,7 @@ .panel-body = simple_form_for @q, url: url_for(action: :index), method: :get do |f| = f.hidden_field :s, value: params.dig(:q, :s) - = f.input :leave_type_eq, collection: LeaveTime.enum_attributes_for_select(:leave_types, [:fullpaid_sick, :halfpaid_sick]), + = f.input :leave_type_eq, collection: LeaveTime.enum_attributes_for_select(:leave_types), include_blank: t('.plz_select_leave_type'), required: false, wrapper_html: { class: 'col-md-6' } = f.input :effective_true, collection: [[I18n.t('activerecord.attributes.leave_time.state.effective'), true], [I18n.t('activerecord.attributes.leave_time.state.expired'), false]], @@ -36,7 +36,6 @@ = sort_link(@q, :expiration_date) %tbody - current_collection.each do |quota| - - next if quota.leave_type == "fullpaid_sick" or quota.leave_type == "halfpaid_sick" %tr %td.text-right= link_to quota.id, leave_time_path(quota.id) %td.text-center= LeaveTime.human_enum_value(:leave_type, quota.leave_type) @@ -56,5 +55,3 @@ .text-center = paginate current_collection - - diff --git a/app/views/leave_times/show.html.haml b/app/views/leave_times/show.html.haml index 302e4795..25bebdec 100644 --- a/app/views/leave_times/show.html.haml +++ b/app/views/leave_times/show.html.haml @@ -43,18 +43,18 @@ %th.text-right= t_attribute :start_time, LeaveApplication %th.text-right= t_attribute :end_time, LeaveApplication %tbody - - current_object.leave_time_usages.each do |usage| + - current_object.leave_time_usages.group_by(&:leave_application).each do |usage| %tr %td.text-right - = link_to usage.leave_application_id, leave_application_path(usage.leave_application_id) - %td.text-center= LeaveApplication.human_enum_value(:leave_type, usage.leave_application.leave_type) - %td.text-right= usage.leave_application.pending? ? hours_to_humanize(usage.used_hours) : '-' - %td.text-right= usage.leave_application.approved? ? hours_to_humanize(usage.used_hours) : '-' - %td.text-right= hours_to_humanize usage.leave_application.hours + = link_to usage[0].id, verify_backend_leave_application_path(usage[0]) + %td.text-center= LeaveApplication.human_enum_value(:leave_type, usage[0].leave_type) + %td.text-right= usage[0].pending? ? hours_to_humanize(usage[1].sum(&:used_hours)) : '-' + %td.text-right= usage[0].approved? ? hours_to_humanize(usage[1].sum(&:used_hours)) : '-' + %td.text-right= hours_to_humanize usage[0].hours %td.text-center - - colored_state_label(usage.leave_application.status) - %td.text-right= l usage.leave_application.start_time, format: :long - %td.text-right= l usage.leave_application.end_time, format: :long + - colored_state_label(usage[0].status) + %td.text-right= l usage[0].start_time, format: :long + %td.text-right= l usage[0].end_time, format: :long %tr %th.active.text-right{colspan: 2}= t('.sum') %td.text-right= hours_to_humanize current_object.leave_time_usages.select { |u| u.leave_application.pending? }.sum(&:used_hours) diff --git a/app/views/mailers/information_mailer/cancel_application.html.haml b/app/views/mailers/information_mailer/cancel_application.html.haml new file mode 100644 index 00000000..d9bc7dce --- /dev/null +++ b/app/views/mailers/information_mailer/cancel_application.html.haml @@ -0,0 +1,7 @@ +%p Hello, +%p + = @leave_application.user.name + just canceled a #{@leave_application.leave_type} leave application. +%p + It's start_time is #{@leave_application.start_time} and end_time is #{@leave_application.end_time}. += link_to "Go check it. :)", "https://daikichi.5xruby.tw/backend/leave_applications/#{@leave_application.id}/verify" \ No newline at end of file diff --git a/app/views/mailers/information_mailer/cancel_application.text.erb b/app/views/mailers/information_mailer/cancel_application.text.erb new file mode 100644 index 00000000..c9566f7b --- /dev/null +++ b/app/views/mailers/information_mailer/cancel_application.text.erb @@ -0,0 +1,5 @@ +Hello, +<%= @leave_application.user.name %> just canceled a <%= @leave_application.leave_type %> leave application. +It's start_time is <%= @leave_application.start_time %> and end_time is <%= @leave_application.end_time%>. +Go check it. :) +https://daikichi.5xruby.tw/backend/leave_applications/<%= @leave_application.id%>/verify \ No newline at end of file diff --git a/app/views/mailers/information_mailer/new_application.html.haml b/app/views/mailers/information_mailer/new_application.html.haml new file mode 100644 index 00000000..f6bafb44 --- /dev/null +++ b/app/views/mailers/information_mailer/new_application.html.haml @@ -0,0 +1,7 @@ +%p Hello, +%p + = @leave_application.user.name + just applies a #{@leave_application.leave_type} leave application. +%p + It's start_time is #{@leave_application.start_time} and end_time is #{@leave_application.end_time}. += link_to "Go check it. :)", "https://daikichi.5xruby.tw/backend/leave_applications/#{@leave_application.id}/verify" diff --git a/app/views/mailers/information_mailer/new_application.text.erb b/app/views/mailers/information_mailer/new_application.text.erb new file mode 100644 index 00000000..02e9bc31 --- /dev/null +++ b/app/views/mailers/information_mailer/new_application.text.erb @@ -0,0 +1,5 @@ +Hello, +<%= @leave_application.user.name %> just applies a <%= @leave_application.leave_type %> leave application. +It's start_time is <%= @leave_application.start_time %> and end_time is <%= @leave_application.end_time%>. +Go check it. :) +https://daikichi.5xruby.tw/backend/leave_applications/<%= @leave_application.id%>/verify diff --git a/app/views/mailers/user_mailer/reply_leave_applicaiton_email.html.haml b/app/views/mailers/user_mailer/reply_leave_applicaiton_email.html.haml new file mode 100644 index 00000000..9d2e357f --- /dev/null +++ b/app/views/mailers/user_mailer/reply_leave_applicaiton_email.html.haml @@ -0,0 +1,5 @@ +%p + 嗨 #{@leave_application.user.name}, +%p + 你 #{l @leave_application.start_time, format: :long} 的 #{LeaveApplication.human_enum_value(:leave_type, @leave_application.leave_type)} 已經被 #{LeaveApplication.human_enum_value(:status, @leave_application.status)} 囉! += link_to "點此連結查看詳情", "https://daikichi.5xruby.tw/leave_applications/#{@leave_application.id}" diff --git a/app/views/mailers/user_mailer/reply_leave_applicaiton_email.text.erb b/app/views/mailers/user_mailer/reply_leave_applicaiton_email.text.erb new file mode 100644 index 00000000..db3126c4 --- /dev/null +++ b/app/views/mailers/user_mailer/reply_leave_applicaiton_email.text.erb @@ -0,0 +1,4 @@ +嗨 <%= @leave_application.user.name %>, +你的 <%= l @leave_application.start_time, format: :long%> 的 <%= LeaveApplication.human_enum_value(:leave_type, @leave_application.leave_type) %> 已經被 <%= LeaveApplication.human_enum_value(:status, @leave_application.status) %> 囉! +<%= link_to "點此連結查看詳情:)", "https://daikichi.5xruby.tw/leave_applications/#{@leave_application.id}" %> + diff --git a/app/views/pages/_alert_modal.html.haml b/app/views/pages/_alert_modal.html.haml new file mode 100644 index 00000000..845a1a7d --- /dev/null +++ b/app/views/pages/_alert_modal.html.haml @@ -0,0 +1,13 @@ +#page-alert-modal.modal{"aria-hidden" => "true", "aria-labelledby" => "mainModalLabel", :role => "dialog", :tabindex => "-1"} + .modal-dialog + .modal-content + .modal-header + %h3{:style => "display: inline-block;"} 注意 + %button.close{"data-dismiss" => "modal", :type => "button"} + %span{"aria-hidden" => "true"} × + %span.sr-only x + .modal-body + %p 申請 remote 或請假,麻煩主動通知 Sabrina 或老闆,以利審核進度。未審核代表未完成請假手續 ,敬請協助配合,謝謝。 + .modal-footer + %button.btn{"aria-hidden" => "true", "data-dismiss" => "modal"} OK + diff --git a/app/views/pages/index.html.haml b/app/views/pages/index.html.haml index e69de29b..e94ff4f9 100644 --- a/app/views/pages/index.html.haml +++ b/app/views/pages/index.html.haml @@ -0,0 +1,20 @@ +.panel.panel-info + .panel-heading 簡易操作說明 + .panel-body + 1. 工時設定為每日 (9:30 - 12:30;12:30 - 18:30),且請假的最小單位為一小時,若早於 9:30 會視同 9:30 或晚於 18:30 會視同 18:30。 + %br + 2. 若需請整天、多天連續假,都可以直接選取頭尾日期時間,系統會自動扣除非上班小時數。 + %br + %br + %br + 備註: + %br + ☞ 若有任何問題請至 + %a{href: 'https://github.com/5xRuby/daikichi/issues'}>_Github_ + 開 issue,會盡快修復 + + %br + ☞ 關於請假規則有任何疑問請查看 + %a{href: 'https://docs.google.com/document/d/1ei1AT3qlV87MV2Gbn-dij-66rGXDkctpQDruGE_mWMg/edit'}>_請假手冊_ + + diff --git a/app/views/remote/new.html.haml b/app/views/remote/new.html.haml index 7b7a2471..9919cfad 100644 --- a/app/views/remote/new.html.haml +++ b/app/views/remote/new.html.haml @@ -1,12 +1,10 @@ -= content_for :additional_script, javascript_include_tag("pages/leave_applications") - %h2= t("captions.remote.#{action_name}") = simple_form_for current_object, url: url_for(action: :create), method: :post, as: :remote_application do |f| = f.error :base, error: sanitize(f.object.errors[:base].join('
'), tags: %w(a br), attributes: %w(href target)) = f.input :leave_type, disabled: true, label: "類型", input_html: { value: I18n.t("activerecord.attributes.leave_application.leave_types.remote") } - = f.input :start_time, date_time_picker_hash(:start, current_object[:start_time]) - = f.input :end_time, date_time_picker_hash(:end, current_object[:end_time]) + = f.input :start_time, as: :string, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } + = f.input :end_time, as: :string, input_html: { data: { input: 'datetimepicker', format: 'Y/m/d H:i' } } = f.error :hours, error: sanitize(f.object.errors[:hours].join('
'), tags: %w(br)) = f.input :description, required: true = f.button :submit, class: "submit" diff --git a/app/views/shared/_leave_application_dropdown.html.haml b/app/views/shared/_leave_application_dropdown.html.haml new file mode 100644 index 00000000..2c1a9737 --- /dev/null +++ b/app/views/shared/_leave_application_dropdown.html.haml @@ -0,0 +1,7 @@ +%li.dropdown + = dropdown_title t("nav.leave_applications") + %ul.dropdown-menu + %li= link_to t("title.leave_applications.new"), new_leave_application_path + %li= link_to t("title.leave_applications.new_remote"), new_remote_path + %li= link_to t("title.leave_times.index"), leave_times_path + %li= link_to t("title.leave_applications.index"), leave_applications_path \ No newline at end of file diff --git a/app/views/shared/_leave_applocation_dropdown_contractor.html.haml b/app/views/shared/_leave_applocation_dropdown_contractor.html.haml new file mode 100644 index 00000000..2018675c --- /dev/null +++ b/app/views/shared/_leave_applocation_dropdown_contractor.html.haml @@ -0,0 +1,5 @@ +%li.dropdown + = dropdown_title t("nav.leave_applications") + %ul.dropdown-menu + %li= link_to t("title.leave_applications.new"), new_leave_application_path + %li= link_to t("title.leave_applications.index"), leave_applications_path \ No newline at end of file diff --git a/app/views/shared/_navigation.html.haml b/app/views/shared/_navigation.html.haml index 6d788dce..c6ec50ab 100644 --- a/app/views/shared/_navigation.html.haml +++ b/app/views/shared/_navigation.html.haml @@ -10,14 +10,10 @@ #nav-collapse.collapse.navbar-collapse %ul.nav.navbar-nav - - if can? :view, LeaveApplication - %li.dropdown - = dropdown_title t("nav.leave_applications") - %ul.dropdown-menu - %li= link_to t("title.leave_applications.new"), new_leave_application_path - %li= link_to t("title.leave_applications.new_remote"), new_remote_path - %li= link_to t("title.leave_times.index"), leave_times_path - %li= link_to t("title.leave_applications.index"), leave_applications_path + - if (can? :view, LeaveApplication) && !current_user.try(:contractor?) + = render partial: 'shared/leave_application_dropdown' + - if current_user.try(:contractor?) + = render partial: 'shared/leave_applocation_dropdown_contractor' - if can? :manage, User %li.dropdown @@ -31,10 +27,12 @@ = dropdown_title t("nav.manage") %ul.dropdown-menu %li= link_to t("title.backend/leave_times.new"), new_backend_leave_time_path + %li= link_to t("title.backend/leave_times.batch_append_quota"),batch_new_backend_leave_times_path + %li= link_to t("title.backend/leave_applications.new"), new_backend_leave_application_path %li= link_to t("title.backend/leave_applications.index"), backend_leave_applications_path(status: :pending) %li= link_to t("captions.backend/leave_applications.statistics"), - statistics_backend_leave_applications_path(year: specific_year, month: specific_month) + statistics_backend_leave_applications_path(year: specific_year, month: specific_month, role: "") %li= link_to t("title.backend/leave_times.index"), backend_leave_times_path diff --git a/config/application.yml.sample b/config/application.yml.sample index 1ce7dc8d..940c09fc 100644 --- a/config/application.yml.sample +++ b/config/application.yml.sample @@ -43,57 +43,65 @@ defaults: &defaults rejected: rejected canceled: canceled leave_types: - annual: annual - bonus: bonus - # unpaid: unpaid - sick: sick + fullpaid_sick: fullpaid_sick + halfpaid_sick: halfpaid_sick + personal: personal official: official + menstrual: menstrual # 生理假 + occpational_sick: occpational_sick # 公傷病假 marriage: marriage compassionate: compassionate - personal: personal + maternity: maternity remote: remote + paid_vacation: paid_vacation available_quota_types: - annual: - - annual - bonus: - - bonus - unpaid: - - unpaid - sick: - - sick # Deprecated + fullpaid_sick: - fullpaid_sick + halfpaid_sick: - halfpaid_sick + personal: + - bonus + - annual + - personal official: - official marriage: - marriage compassionate: - compassionate - # Deprecated - personal: - - bonus - - annual - - personal + maternity: + - maternity remote: - remote + # 生理假 + menstrual: + - menstrual + # 公傷病假 + occpational_sick: + - occpational_sick + # 旅遊假 + paid_vacation: + - paid_vacation leave_times: quota_types: - annual: annual bonus: bonus - unpaid: unpaid - sick: sick # Deprecated + annual: annual + personal: personal #should be deprecated fullpaid_sick: fullpaid_sick halfpaid_sick: halfpaid_sick + remote: remote official: official marriage: marriage compassionate: compassionate - remote: remote - personal: personal #should be deprecated + maternity: maternity + menstrual: menstrual # 生理假 + occpational_sick: occpational_sick # 公傷病假 + paid_vacation: paid_vacation leed_days: monthly: 5 # working.days - join_date_based: 40 # days + join_date_based: 60 # days leave_types: annual: @@ -139,8 +147,8 @@ defaults: &defaults creation: join_date_based quota: 23 remote: - creation: monthly - quota: 2 # days + creation: weekly + quota: 1 # days marriage: creation: manually quota: 8 # days @@ -154,6 +162,12 @@ defaults: &defaults creation: none bonus: creation: none + menstrual: + creation: manually + quota: 1 + effective: 2 # days + occpational_sick: + creation: none backend: default_leave_pool_type: bonus @@ -170,3 +184,6 @@ test: production: <<: *defaults + +staging: + <<: *defaults diff --git a/config/deploy.rb b/config/deploy.rb index c9fc4596..f81d7179 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -30,11 +30,12 @@ set :pty, false # Default value for :linked_files is [] -set :linked_files, %w{config/database.yml config/application.yml config/secrets.yml} +set :linked_files, %w{config/database.yml config/application.yml config/secrets.yml config/fluent-logger.yml} # Default value for linked_dirs is [] -# set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system') +set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system') + # Default value for default_env is {} set :default_env, { path: "$PATH:/usr/local/ruby23/bin:/usr/local/ruby-2.4.1/bin:" } diff --git a/config/deploy/production.rb b/config/deploy/production.rb index 9510baf3..08cb0d48 100644 --- a/config/deploy/production.rb +++ b/config/deploy/production.rb @@ -1,4 +1,4 @@ set :deploy_to, '/home/deploy/daikichi.5xruby.tw' -role :app, %w{deploy@10.128.128.154} -role :web, %w{deploy@10.128.128.154} -role :db, %w{deploy@10.128.128.154} \ No newline at end of file +role :app, %w{deploy@do.5xruby.tw} +role :web, %w{deploy@do.5xruby.tw} +role :db, %w{deploy@do.5xruby.tw} \ No newline at end of file diff --git a/config/environments/development.rb b/config/environments/development.rb index 8ec4ca90..869adbbd 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -48,5 +48,17 @@ # Use an evented file watcher to asynchronously detect changes in source code, # routes, locales, etc. This feature depends on the listen gem. config.file_watcher = ActiveSupport::EventedFileUpdateChecker + config.action_mailer.show_previews = true + config.action_mailer.preview_path = "#{Rails.root}/app/mailers/previews" config.action_mailer.delivery_method = :letter_opener + # config.action_mailer.delivery_method = :smtp + # config.action_mailer.smtp_settings = { + # user_name: Settings.smtp.username, + # password: Settings.smtp.password, + # domain: Settings.smtp.domain, + # address: Settings.smtp.address, + # port: Settings.smtp.port, + # authentication: Settings.smtp.authentication, + # enable_starttls_auto: Settings.smtp.enable_starttls_auto + # } end diff --git a/config/environments/production.rb b/config/environments/production.rb index e6167eef..0aea947d 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -96,4 +96,8 @@ authentication: Settings.smtp.authentication, enable_starttls_auto: Settings.smtp.enable_starttls_auto } + + #config.logger = ActFluentLoggerRails::Logger.new + #config.lograge.enabled = true + #config.lograge.formatter = Lograge::Formatters::Logstash.new end diff --git a/config/fluent-logger.yml.example b/config/fluent-logger.yml.example new file mode 100644 index 00000000..bd13c260 --- /dev/null +++ b/config/fluent-logger.yml.example @@ -0,0 +1,17 @@ +development: + fluent_host: '127.0.0.1' + fluent_port: 24224 + tag: '5xruby.daikichi' + messages_type: 'string' + +test: + fluent_host: '127.0.0.1' + fluent_port: 24224 + tag: '5xruby.daikichi' + messages_type: 'string' + +production: + fluent_host: '127.0.0.1' + fluent_port: 24224 + tag: '5xruby.daikichi' + messages_type: 'string' diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 533511f9..e756fba5 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -10,3 +10,4 @@ # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # Rails.application.config.assets.precompile += %w( search.js ) Rails.application.config.assets.precompile += [ "pages/*" ] +Rails.application.config.assets.precompile += %w( page_alert.js ) diff --git a/config/initializers/biz.rb b/config/initializers/biz.rb index 6f1b653d..73470c70 100644 --- a/config/initializers/biz.rb +++ b/config/initializers/biz.rb @@ -61,6 +61,7 @@ class Config Date.new(2017, 7, 22), # 週六休假 Date.new(2017, 7, 29), # 週六休假 Date.new(2017, 8, 5), # 週六休假 + Date.new(2017, 8, 12), # 週六休假 Date.new(2017, 8, 19), # 週六休假 Date.new(2017, 8, 26), # 週六休假 Date.new(2017, 9, 2), # 週六休假 @@ -80,7 +81,75 @@ class Config Date.new(2017, 12, 9), # 週六休假 Date.new(2017, 12, 16), # 週六休假 Date.new(2017, 12, 23), # 週六休假 - Date.new(2017, 12, 30) # 週六休假 + Date.new(2017, 12, 30), # 週六休假 + + # 2018 休假日設定 + Date.new(2018, 1, 1), + Date.new(2018, 2, 15), # 除夕放假 + Date.new(2018, 2, 16), # 春節 + Date.new(2018, 2, 19), # 春節放三天逢週六、週日補假二天 + Date.new(2018, 2, 20), # 春節放三天逢週六、週日補假二天 + Date.new(2018, 2, 28), # 和平紀念日 + Date.new(2018, 4, 4), # 兒童節、清明節連假 + Date.new(2018, 4, 5), # 兒童節、清明節連假 + Date.new(2018, 4, 6), # 兒童節、清明節連假 + Date.new(2018, 5, 1), # 五一勞動節 + Date.new(2018, 6, 18), # 端午節 + Date.new(2018, 9, 24), # 中秋節放假 + Date.new(2018, 10, 10), # 國慶日放假 + Date.new(2018, 12, 31), # 元旦逢週二,彈性調整放假 + + # 週六放假設定 + Date.new(2018, 1, 6), # 週六休假 + Date.new(2018, 1, 13), # 週六休假 + Date.new(2018, 1, 20), # 週六休假 + Date.new(2018, 1, 27), # 週六休假 + Date.new(2018, 2, 3), # 週六休假 + Date.new(2018, 2, 10), # 週六休假 + Date.new(2018, 2, 17), # 週六休假 + Date.new(2018, 2, 24), # 週六休假 + Date.new(2018, 3, 3), # 週六休假 + Date.new(2018, 3, 10), # 週六休假 + Date.new(2018, 3, 17), # 週六休假 + Date.new(2018, 3, 24), # 週六休假 + Date.new(2018, 4, 7), # 週六休假 + Date.new(2018, 4, 14), # 週六休假 + Date.new(2018, 4, 21), # 週六休假 + Date.new(2018, 4, 28), # 週六休假 + Date.new(2018, 5, 5), # 週六休假 + Date.new(2018, 5, 12), # 週六休假 + Date.new(2018, 5, 19), # 週六休假 + Date.new(2018, 5, 26), # 週六休假 + Date.new(2018, 6, 2), # 週六休假 + Date.new(2018, 6, 9), # 週六休假 + Date.new(2018, 6, 16), # 週六休假 + Date.new(2018, 6, 23), # 週六休假 + Date.new(2018, 6, 30), # 週六休假 + Date.new(2018, 7, 7), # 週六休假 + Date.new(2018, 7, 14), # 週六休假 + Date.new(2018, 7, 21), # 週六休假 + Date.new(2018, 7, 28), # 週六休假 + Date.new(2018, 8, 4), # 週六休假 + Date.new(2018, 8, 11), # 週六休假 + Date.new(2018, 8, 18), # 週六休假 + Date.new(2018, 8, 25), # 週六休假 + Date.new(2018, 9, 1), # 週六休假 + Date.new(2018, 9, 8), # 週六休假 + Date.new(2018, 9, 15), # 週六休假 + Date.new(2018, 9, 22), # 週六休假 + Date.new(2018, 9, 29), # 週六休假 + Date.new(2018, 10, 6), # 週六休假 + Date.new(2018, 10, 13), # 週六休假 + Date.new(2018, 10, 20), # 週六休假 + Date.new(2018, 10, 27), # 週六休假 + Date.new(2018, 11, 3), # 週六休假 + Date.new(2018, 11, 10), # 週六休假 + Date.new(2018, 11, 17), # 週六休假 + Date.new(2018, 11, 24), # 週六休假 + Date.new(2018, 12, 1), # 週六休假 + Date.new(2018, 12, 8), # 週六休假 + Date.new(2018, 12, 15), # 週六休假 + Date.new(2018, 12, 29) # 週六休假 ] config.time_zone = 'Asia/Taipei' diff --git a/config/locales/meta_data.zh_TW.yml b/config/locales/meta_data.zh_TW.yml index 322b17ab..86fa4d95 100644 --- a/config/locales/meta_data.zh_TW.yml +++ b/config/locales/meta_data.zh_TW.yml @@ -38,7 +38,9 @@ zh-TW: bonus: '補休' compassionate: '喪假' sick: '病假' - unpaid: '無薪事假' + menstrual: '生理假' + occpational_sick: '公傷病假' + paid_vacation: '旅遊假' user: "員工" name: "擁有者" leave_type: "假別" @@ -70,13 +72,17 @@ zh-TW: leave_types: annual: '特別休假' bonus: '補充休假' - sick: '病假' - personal: '無薪事假' + fullpaid_sick: '全薪病假' + halfpaid_sick: '半薪病假' + personal: '事假' remote: '遠端工作' official: '公假' marriage: '婚假' compassionate: '喪假' maternity: '產假' + menstrual: '生理假' + occpational_sick: '公傷病假' + paid_vacation: '旅遊假' user: "員工" start_time: "開始時間" end_time: "結束時間" @@ -151,6 +157,7 @@ zh-TW: edit: "編輯" destroy: "刪除" backend/leave_applications: + new: "新增員工假單" index: "審核假單" show: "檢視" verify: "審核" @@ -161,6 +168,7 @@ zh-TW: backend/leave_times: index: "員工休假額度" new: "新增員工休假額度" + batch_append_quota: "批次新增員工休假額度" edit: "修改" delete: "刪除" change_type: "切換休假種類至" @@ -190,7 +198,7 @@ zh-TW: leave_times: index: "請假額度" leave_applications: - index: "%{year} 年 所有假單" + index: "歷年假單" show: "%{start_day} - %{end_day} %{type}假單" new: "新增假單" create: "新增假單" @@ -206,12 +214,17 @@ zh-TW: create: "新增員工" edit: "修正員工資料" backend/leave_applications: + new: "新增員工假單" + create: "新增員工假單" + new/hint: "提醒:管理員主動新增之假單狀態會直接是核准。" + create/hint: "提醒:管理員主動新增之假單狀態會直接是核准。" index: "審核假單" verify: "審核假單" statistics: "員工休假統計" backend/leave_times: index: "員工休假額度" new: "新增員工休假額度" + batch_new: "批次新增員工休假額度" append_quota: "新增員工休假額度" edit: "修改員工休假" tools: "工具" @@ -222,6 +235,7 @@ zh-TW: destroy: "刪除成功" revise: "已修改假單" cancel: "已取消假單" + batch_create: '批次新增員工休假額度成功' warnings: self_manage: "申請人不得自行審核" diff --git a/config/locales/simple_form.zh_TW.yml b/config/locales/simple_form.zh_TW.yml index d1f0195c..cdabde7b 100644 --- a/config/locales/simple_form.zh_TW.yml +++ b/config/locales/simple_form.zh_TW.yml @@ -23,6 +23,7 @@ zh-TW: maternity: '產假' compassionate: '喪假' absent: '曠職' + paid_vacation: '旅遊假' labels: q: leave_type_eq: 依額度類別篩選 diff --git a/config/locales/zh_TW.yml b/config/locales/zh_TW.yml index fa264623..4a2c7007 100644 --- a/config/locales/zh_TW.yml +++ b/config/locales/zh_TW.yml @@ -2,13 +2,13 @@ zh-TW: date: abbr_day_names: - - 周日 - - 周一 - - 周二 - - 周三 - - 周四 - - 周五 - - 周六 + - 週日 + - 週一 + - 週二 + - 週三 + - 週四 + - 週五 + - 週六 abbr_month_names: - - 1月 @@ -35,7 +35,7 @@ zh-TW: default: "%Y-%m-%d" long: "%Y年%b%d日" short: "%b%d日" - detailed: "%Y年 %m月 %d日 (%a)" + detailed: "%Y-%m-%d (%a)" month_names: - - 1月 @@ -200,7 +200,8 @@ zh-TW: am: 上午 formats: default: "%Y年%b%d日 %A %H:%M:%S %Z" - long: "%Y年 %m月 %d日 (%a) %H:%M" + long: "%Y-%m-%d (%a) %H:%M" short: "%b%d日 %H:%M" pm: 下午 humanize_working_hour: "%{days} 天 %{hours} 小時 (%{total}小時)" + total_hour: "%{total} 小時" diff --git a/config/routes.rb b/config/routes.rb index 74010c8a..3ca954ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,13 +7,15 @@ namespace :backend do resources :users - resources :leave_applications, only: [:index, :update] do + resources :leave_applications, except: [:show, :destroy] do get :verify, on: :member get :statistics, on: :collection end resources :leave_times, except: [:edit, :update, :destroy] do post :append_quota, on: :collection + get :batch_new, on: :collection + post :batch_create, on: :collection end resources :bonus_leave_time_logs, only: [:index, :update] diff --git a/config/schedule.rb b/config/schedule.rb index 81dedd0f..512d7292 100644 --- a/config/schedule.rb +++ b/config/schedule.rb @@ -1,9 +1,4 @@ -# 每年 10/1 00:05 初始化隔年休假額度 -every '5 0 1 10 *' do - rake "leave_time:init[Time.current.year + 1,'force']" -end - -# 每日 00:10 檢查 fulltime employee 是否獲得 8hr 特休 -every "10 0 * * *" do - rake "leave_time:refill" +# 每日 00:00 檢查是否初始化額度(Monthly and Join_date_base) +every "0 0 * * *" do + rake "import:import" end diff --git a/db/migrate/20170616091946_delete_column_of_leave_application.rb b/db/migrate/20170616091946_delete_column_of_leave_application.rb index b71395bb..2c286d28 100644 --- a/db/migrate/20170616091946_delete_column_of_leave_application.rb +++ b/db/migrate/20170616091946_delete_column_of_leave_application.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true class DeleteColumnOfLeaveApplication < ActiveRecord::Migration[5.0] def change - remove_column :leave_applications, :uuid + remove_column :leave_applications, :uuid end end diff --git a/db/migrate/20170620024946_delete_leave_application_logs.rb b/db/migrate/20170620024946_delete_leave_application_logs.rb index de84942e..014da3f4 100644 --- a/db/migrate/20170620024946_delete_leave_application_logs.rb +++ b/db/migrate/20170620024946_delete_leave_application_logs.rb @@ -7,7 +7,7 @@ def change t.integer :amount, default: 0 t.boolean :returning?, default: false - t.timestamps + t.timestamps end end end diff --git a/db/migrate/20170629025755_create_crono_jobs.rb b/db/migrate/20170629025755_create_crono_jobs.rb index 60706fa5..a98cb66f 100644 --- a/db/migrate/20170629025755_create_crono_jobs.rb +++ b/db/migrate/20170629025755_create_crono_jobs.rb @@ -2,7 +2,7 @@ class CreateCronoJobs < ActiveRecord::Migration def self.up create_table :crono_jobs do |t| t.string :job_id, null: false - t.text :log, limit: 1073741823 # LONGTEXT for MySQL + t.text :log, limit: 1_073_741_823 # LONGTEXT for MySQL t.datetime :last_performed_at t.boolean :healthy t.timestamps null: false diff --git a/db/migrate/20170810032132_update_leave_application_leave_type.rb b/db/migrate/20170810032132_update_leave_application_leave_type.rb new file mode 100644 index 00000000..d622cdb2 --- /dev/null +++ b/db/migrate/20170810032132_update_leave_application_leave_type.rb @@ -0,0 +1,10 @@ +class UpdateLeaveApplicationLeaveType < ActiveRecord::Migration[5.0] + def self.up + LeaveApplication.where(leave_type: [:annual, :bonus]).each do |q| + q.update!(leave_type: :personal) + end + end + + def self.down + end +end diff --git a/db/migrate/20171229103357_add_date_to_leave_time_usages.rb b/db/migrate/20171229103357_add_date_to_leave_time_usages.rb new file mode 100644 index 00000000..a49f844e --- /dev/null +++ b/db/migrate/20171229103357_add_date_to_leave_time_usages.rb @@ -0,0 +1,5 @@ +class AddDateToLeaveTimeUsages < ActiveRecord::Migration[5.0] + def change + add_column :leave_time_usages, :date, :date + end +end diff --git a/db/schema.rb b/db/schema.rb index 9454d1fc..62ee4cba 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20170629025755) do +ActiveRecord::Schema.define(version: 20171229103357) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -68,6 +68,7 @@ t.integer "used_hours" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.date "date" t.index ["leave_application_id"], name: "index_leave_time_usages_on_leave_application_id", using: :btree t.index ["leave_time_id"], name: "index_leave_time_usages_on_leave_time_id", using: :btree end diff --git a/lib/tasks/create_new_format_usages.rake b/lib/tasks/create_new_format_usages.rake new file mode 100644 index 00000000..4ea432a4 --- /dev/null +++ b/lib/tasks/create_new_format_usages.rake @@ -0,0 +1,31 @@ +# frozen_string_literal: true +namespace :generate_usages do + desc "Generate leave time usage in new format for approved applications" + task approved: :environment do + LeaveApplication.approved.find_each do |la| + la.leave_time_usages.each do |usage| + usage.leave_time.unuse_hours!(usage.used_hours) + usage.destroy + end + raise ActiveRecord::Rollback if !LeaveTimeUsageBuilder.new(la).build_leave_time_usages and !la.special_type? + end + + LeaveApplication.approved.find_each do |la| + la.leave_time_usages.group_by(&:leave_time_id).each do |usages| + leave_time = LeaveTime.find(usages[0]) + leave_time.use_hours!(usages[1].sum(&:used_hours)) + end + end + end + + desc "Generate leave time usage in new format for pending leave applications" + task pending: :environment do + LeaveApplication.pending.find_each do |la| + la.leave_time_usages.each do |usage| + usage.leave_time.unlock_hours!(usage.used_hours) + usage.destroy + end + raise ActiveRecord::Rollback if !LeaveTimeUsageBuilder.new(la).build_leave_time_usages and !la.special_type? + end + end +end diff --git a/lib/tasks/import_data.rake b/lib/tasks/import_data.rake index ca390b2e..a505f97a 100644 --- a/lib/tasks/import_data.rake +++ b/lib/tasks/import_data.rake @@ -8,6 +8,7 @@ namespace :import_data do user.email = Settings.admin_user.email user.name = Settings.admin_user.name user.role = Settings.admin_user.role + user.join_date = Date.current user.password = Settings.admin_user.password user.password_confirmation = Settings.admin_user.password end diff --git a/spec/controllers/backend/leave_times_controller_spec.rb b/spec/controllers/backend/leave_times_controller_spec.rb index d6f70bd1..761b0bde 100644 --- a/spec/controllers/backend/leave_times_controller_spec.rb +++ b/spec/controllers/backend/leave_times_controller_spec.rb @@ -1,4 +1,15 @@ # frozen_string_literal: true require 'rails_helper' RSpec.describe Backend::LeaveTimesController, type: :controller do + describe 'POST #batch_create' do + before do + sign_in create(:manager_eddie) + 5.times { FactoryGirl.create(:user) } + end + it 'add leave time to each users' do + expect do + post :batch_create, params: { leave_time: { 'user_id' => User.select(:id).last(4), 'leave_type' => 'personal', 'quota' => '3', 'effective_date' => '2017/12/15', 'expiration_date' => '2017/12/25', 'remark' => 'fd' } } + end.to change(LeaveTime, :count).by(4) + end + end end diff --git a/spec/controllers/remote_controller_spec.rb b/spec/controllers/remote_controller_spec.rb index 4345a68c..5420bdd0 100644 --- a/spec/controllers/remote_controller_spec.rb +++ b/spec/controllers/remote_controller_spec.rb @@ -1,5 +1,4 @@ require 'rails_helper' RSpec.describe RemoteController, type: :controller do - end diff --git a/spec/factories/leave_applicaiton.rb b/spec/factories/leave_applicaiton.rb index 46bd5946..fbcf26da 100644 --- a/spec/factories/leave_applicaiton.rb +++ b/spec/factories/leave_applicaiton.rb @@ -7,8 +7,12 @@ start_time { Daikichi::Config::Biz.periods.after(Time.current.beginning_of_day).first.start_time } end_time { Daikichi::Config::Biz.periods.after(Time.current.beginning_of_day).first(2).second.end_time } - trait :sick do - leave_type 'sick' + trait :halfpaid_sick do + leave_type 'halfpaid_sick' + end + + trait :fullpaid_sick do + leave_type 'fullpaid_sick' end trait :personal do diff --git a/spec/features/leave_application/create_leave_application.rb b/spec/features/leave_application/create_leave_application.rb new file mode 100644 index 00000000..bab42211 --- /dev/null +++ b/spec/features/leave_application/create_leave_application.rb @@ -0,0 +1,128 @@ +require 'rails_helper' + +feature "special type leave application" do + context "created" do + let!(:user) { FactoryGirl.create(:user, :fulltime, join_date: Date.current - 1.year - 1.day) } + + before :each do + visit '/users/sign_in' + login user.login_name, user.password + end + + scenario "menstrual" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('生理假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'menstrual') + end + click_button '送出' + expect(page).to have_content '生理假' + end + + scenario "occpational_sick" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('公傷病假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'occpational_sick') + end + click_button '送出' + expect(page).to have_content '公傷病假' + end + + scenario "official" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('公假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'official') + end + click_button '送出' + expect(page).to have_content '公假' + end + + scenario "compassionate" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('喪假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'compassionate') + end + click_button '送出' + expect(page).to have_content '喪假' + end + + scenario "marriage" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('婚假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'marriage') + end + click_button '送出' + expect(page).to have_content '婚假' + end + + scenario "maternity" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('產假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'maternity') + end + click_button '送出' + expect(page).to have_content '產假' + end + + scenario "paid_vacation" do + visit '/leave_applications/new' + within("form#new_leave_application") do + select('旅遊假', from: 'leave_application_leave_type') + find('[id^="leave_application_start_time"]').set("2018/02/12 09:30") + find('[id^="leave_application_start_time"]').set("2018/02/12 18:30") + fill_in('事由', with: 'paid_vacation') + end + click_button '送出' + expect(page).to have_content '旅遊假' + end + + end + + context "verified" do + let!(:user) { FactoryGirl.create(:user, :manager, join_date: Date.current - 1.year - 1.day) } + let!(:leave_application) { LeaveApplication.create(user_id: user.id, leave_type: 'maternity', start_time: Time.zone.local(Time.current.year, 8, 15, 9, 30, 0), end_time: Time.zone.local(Time.current.year, 8, 15, 18, 30, 0), description: 'test') } + let!(:leave_application_paid_vaca) { LeaveApplication.create(user_id: user.id, leave_type: 'paid_vacation', start_time: Time.zone.local(Time.current.year, 8, 15, 9, 30, 0), end_time: Time.zone.local(Time.current.year, 8, 15, 18, 30, 0), description: 'test') } + + before :each do + visit '/users/sign_in' + login user.login_name, user.password + end + + scenario "menstrual" do + visit "/backend/leave_applications/#{leave_application.id}/verify" + + expect(page).to have_content '新增額度' + end + + scenario "paid_vacation" do + visit "/backend/leave_applications/#{leave_application_vaca.id}/verify" + expect(page).to have_content '新增額度' + end + + end + + private + + def login(email, password) + fill_in "員工帳號", with: email + fill_in "密碼", with: password + click_button "送出" + end +end diff --git a/spec/mailers/information_spec.rb b/spec/mailers/information_spec.rb new file mode 100644 index 00000000..e895f114 --- /dev/null +++ b/spec/mailers/information_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe InformationMailer, type: :mailer do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/mailers/previews/information_preview.rb b/spec/mailers/previews/information_preview.rb new file mode 100644 index 00000000..b5fa3ae6 --- /dev/null +++ b/spec/mailers/previews/information_preview.rb @@ -0,0 +1,3 @@ +# Preview all emails at http://localhost:3000/rails/mailers/information +class InformationPreview < ActionMailer::Preview +end diff --git a/spec/mailers/previews/user_preview.rb b/spec/mailers/previews/user_preview.rb new file mode 100644 index 00000000..afe9d42b --- /dev/null +++ b/spec/mailers/previews/user_preview.rb @@ -0,0 +1,3 @@ +# Preview all emails at http://localhost:3000/rails/mailers/user +class UserPreview < ActionMailer::Preview +end diff --git a/spec/mailers/user_spec.rb b/spec/mailers/user_spec.rb new file mode 100644 index 00000000..ac8c5f14 --- /dev/null +++ b/spec/mailers/user_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe UserMailer, type: :mailer do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/leave_application_spec.rb b/spec/models/leave_application_spec.rb index 803641ba..8519b5db 100644 --- a/spec/models/leave_application_spec.rb +++ b/spec/models/leave_application_spec.rb @@ -20,11 +20,11 @@ end describe 'validation' do - let(:params) { FactotyGirl.attributes_for(:leave_application) } + # let(:params) { FactoryGirl.attributes_for(:leave_application) } subject { described_class.new(params) } context 'has a valid factory' do - subject { build(:leave_application, :with_leave_time, :annual) } + subject { build(:leave_application, :with_leave_time, :personal) } it { expect(subject).to be_valid } end @@ -42,14 +42,14 @@ leave = LeaveApplication.new( start_time: start_time, end_time: one_hour_later, - leave_type: 'sick' + leave_type: 'fullpaid_sick' ) expect(leave).to be_invalid expect(leave.errors.messages[:description].first).to eq '請簡述原因' end it 'hours應為正整數' do - leave = LeaveApplication.new start_time: start_time, leave_type: 'sick', description: 'test' + leave = LeaveApplication.new start_time: start_time, leave_type: 'fullpaid_sick', description: 'test' leave.end_time = one_hour_ago expect(leave).to be_invalid expect(leave.errors.messages[:start_time].first).to eq '開始時間必須早於結束時間' @@ -84,13 +84,13 @@ User.skip_callback(:create, :after, :auto_assign_leave_time) create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) end - after { User.set_callback(:create, :after, :auto_assign_leave_time) } + after { User.set_callback(:create, :after, :auto_assign_leave_time) } - subject { build(:leave_application, :annual, user: user, start_time: beginning, end_time: ending) } + subject { build(:leave_application, :personal, user: user, start_time: beginning, end_time: ending) } shared_examples 'invalid' do |overlap_section, status| it "should be invalid when overlaps #{overlap_section} of the #{status} leave application" do - la = create(:leave_application, :annual, status, user: user, start_time: start_time, end_time: end_time, description: 'test string') + la = create(:leave_application, :personal, status, user: user, start_time: start_time, end_time: end_time, description: 'test string') expect(subject.valid?).to be_falsy expect(subject.errors[:base]).to include I18n.t( 'activerecord.errors.models.leave_application.attributes.base.overlap_application', @@ -118,7 +118,7 @@ shared_examples 'valid' do |boundary, status| it "is valid to overlap on #{boundary} of the other #{status} application" do - create(:leave_application, :annual, status, user: user, start_time: start_time, end_time: end_time, description: 'test string') + create(:leave_application, :personal, status, user: user, start_time: start_time, end_time: end_time, description: 'test string') expect(subject.valid?).to be_truthy end end @@ -179,6 +179,7 @@ it_should_behave_like 'transitions', from: :pending, to: :approved, with_action: :approve, manager_required: true it_should_behave_like 'transitions', from: :pending, to: :canceled, with_action: :cancel it_should_behave_like 'transitions', from: :pending, to: :rejected, with_action: :reject, manager_required: true + it_should_behave_like 'transitions', from: :approved, to: :rejected, with_action: :reject, manager_required: true it_should_behave_like 'transitions', from: :pending, to: :pending, with_action: :revise it_should_behave_like 'transitions', from: :approved, to: :pending, with_action: :revise @@ -188,9 +189,9 @@ let(:expiration_date) { Time.zone.local(2017, 6, 30).to_date } let(:start_time) { Time.zone.local(2017, 6, 1, 9, 30) } let(:end_time) { Time.zone.local(2017, 6, 5, 12, 30) } - before { create(:leave_time, :annual, user: user, quota: 100, usable_hours: 100) } + before { create(:leave_time, :annual, user: user, quota: 100, usable_hours: 100, effective_date: effective_date, expiration_date: expiration_date) } it 'can transition from approved to canceled unless LeaveApplication happened already' do - leave_application = create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time).reload + leave_application = create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time).reload leave_application.approve!(create(:user, :hr)) expect(leave_application.happened?).to be_truthy expect(leave_application.approved?).to be_truthy @@ -292,10 +293,10 @@ let(:beginning) { effective_date.beginning_of_day } let(:ending) { expiration_date.end_of_day } let!(:leave_time) { create(:leave_time, :annual, user: user, quota: 100, usable_hours: 100, effective_date: effective_date, expiration_date: expiration_date) } - let!(:pending) { create(:leave_application, :annual, user: user, start_time: Time.zone.local(2017, 5, 2, 9, 30), end_time: Time.zone.local(2017, 5, 4, 12, 30)) } - let!(:approved) { create(:leave_application, :annual, :approved, user: user, start_time: Time.zone.local(2017, 5, 9, 9, 30), end_time: Time.zone.local(2017, 5, 11, 12, 30)) } - let!(:canceled) { create(:leave_application, :annual, :canceled, user: user, start_time: Time.zone.local(2017, 5, 16, 9, 30), end_time: Time.zone.local(2017, 5, 18, 12, 30)) } - let!(:rejected) { create(:leave_application, :annual, :rejected, user: user, start_time: Time.zone.local(2017, 5, 23, 9, 30), end_time: Time.zone.local(2017, 5, 25, 12, 30)) } + let!(:pending) { create(:leave_application, :personal, user: user, start_time: Time.zone.local(2017, 5, 2, 9, 30), end_time: Time.zone.local(2017, 5, 4, 12, 30)) } + let!(:approved) { create(:leave_application, :personal, :approved, user: user, start_time: Time.zone.local(2017, 5, 9, 9, 30), end_time: Time.zone.local(2017, 5, 11, 12, 30)) } + let!(:canceled) { create(:leave_application, :personal, :canceled, user: user, start_time: Time.zone.local(2017, 5, 16, 9, 30), end_time: Time.zone.local(2017, 5, 18, 12, 30)) } + let!(:rejected) { create(:leave_application, :personal, :rejected, user: user, start_time: Time.zone.local(2017, 5, 23, 9, 30), end_time: Time.zone.local(2017, 5, 25, 12, 30)) } after { User.set_callback(:create, :after, :auto_assign_leave_time) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index bf7a1799..2d3116c5 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -73,7 +73,8 @@ end end it_should_behave_like 'different roles create LeaveTime with different leave_type', %i(manager hr employee fulltime), %w(annual personal fullpaid_sick halfpaid_sick remote) - it_should_behave_like 'different roles create LeaveTime with different leave_type', %i(intern contractor parttime), %w(personal fullpaid_sick halfpaid_sick remote) + it_should_behave_like 'different roles create LeaveTime with different leave_type', %i(intern), %w(personal fullpaid_sick halfpaid_sick remote) + it_should_behave_like 'different roles create LeaveTime with different leave_type', %i(contractor), %w(personal) shared_examples 'specific roles should not create any LeaveTime' do |roles| roles.each do |role| @@ -96,7 +97,7 @@ end all_roles = %i(manager hr employee intern) it_should_behave_like 'leave_type created with specific quota', all_roles, 'personal', 112 - it_should_behave_like 'leave_type created with specific quota', all_roles, 'remote', 16 + it_should_behave_like 'leave_type created with specific quota', all_roles, 'remote', 8 it_should_behave_like 'leave_type created with specific quota', all_roles, 'fullpaid_sick', 56 it_should_behave_like 'leave_type created with specific quota', all_roles, 'halfpaid_sick', 184 @@ -107,7 +108,7 @@ it 'should create LeaveTime of the user if specified assign_leave_time and assign_date' do user = create(:user, :fulltime) - expect(user.leave_times.any?).to be_truthy + expect(user.leave_times.any?).to be_truthy end it 'should have error message when assign_leave_time is true while assign_date is nil' do diff --git a/spec/observers/leave_application_observer_spec.rb b/spec/observers/leave_application_observer_spec.rb index f27e1c5b..afd781dd 100644 --- a/spec/observers/leave_application_observer_spec.rb +++ b/spec/observers/leave_application_observer_spec.rb @@ -7,7 +7,7 @@ let(:start_time) { Time.zone.local(2017, 5, 2, 9, 30) } let(:end_time) { Time.zone.local(2017, 5, 5, 10, 30) } let(:total_leave_hours) { Daikichi::Config::Biz.within(start_time, end_time).in_hours } - let(:leave_application) { create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) } + let(:leave_application) { create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) } before { User.skip_callback(:create, :after, :auto_assign_leave_time) } after { User.set_callback(:create, :after, :auto_assign_leave_time) } @@ -17,14 +17,15 @@ context 'after_create' do it 'should successfully create LeaveTimeUsage on sufficient LeaveTime hours' do leave_time_usage = leave_application.leave_time_usages.first + total_used_hours = leave_application.leave_time_usages.map(&:used_hours).sum leave_time.reload - expect(leave_time_usage.used_hours).to eq total_leave_hours + expect(total_used_hours).to eq total_leave_hours expect(leave_time_usage.leave_time).to eq leave_time expect(leave_time.locked_hours).to eq total_leave_hours end it 'should not create LeaveTimeUsage when insufficient LeaveTime hours' do - la = create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time + 1.hour) + la = create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time + 1.hour) expect(la.errors.any?).to be_truthy expect(la.leave_time_usages.any?).to be false expect(leave_time.usable_hours).to eq total_leave_hours @@ -33,10 +34,11 @@ context 'after_update' do it 'should recreate LeaveTimeUsage only when AASM event is "revise"' do - la = create(:leave_application, :annual, :approved, user: user, start_time: start_time, end_time: end_time) + la = create(:leave_application, :personal, :approved, user: user, start_time: start_time, end_time: end_time) leave_time_usage = la.leave_time_usages.first + total_used_hours = la.leave_time_usages.map(&:used_hours).sum leave_time.reload - expect(leave_time_usage.used_hours).to eq total_leave_hours + expect(total_used_hours).to eq total_leave_hours expect(leave_time_usage.leave_time).to eq leave_time expect(leave_time.used_hours).to eq total_leave_hours @@ -44,38 +46,39 @@ la.revise! leave_time_usage = la.leave_time_usages.first leave_time.reload - expect(leave_time_usage.used_hours).to eq (total_leave_hours - 1) + total_used_hours = la.leave_time_usages.map(&:used_hours).sum + expect(total_used_hours).to eq(total_leave_hours - 1) expect(leave_time_usage.leave_time).to eq leave_time - expect(leave_time.locked_hours).to eq (total_leave_hours - 1) + expect(leave_time.locked_hours).to eq(total_leave_hours - 1) end end end describe '.hours_update' do let(:quota) { 100 } - let!(:leave_time) { create(:leave_time, :annual, user: user, quota: quota, usable_hours: quota, effective_date: effective_date, expiration_date: expiration_date) } + let!(:leave_time) { create(:leave_time, :annual, user: user, quota: quota, usable_hours: quota, effective_date: effective_date, expiration_date: expiration_date) } context 'before_update' do describe 'AASM "approve" event' do it 'should transfer locked_hours to used_hours' do leave_time_usage = leave_application.leave_time_usages.first leave_time.reload expect(leave_time_usage.leave_time).to eq leave_time - expect(leave_time.usable_hours).to eq (quota - total_leave_hours) + expect(leave_time.usable_hours).to eq(quota - total_leave_hours) expect(leave_time.locked_hours).to eq total_leave_hours leave_application.reload.approve! user leave_time.reload - expect(leave_time.usable_hours).to eq (quota - total_leave_hours) + expect(leave_time.usable_hours).to eq(quota - total_leave_hours) expect(leave_time.used_hours).to eq total_leave_hours end end - shared_examples 'return locked_hours back to used_hours' do |event, required_user| + shared_examples 'return locked_hours back to usable_hours' do |event, required_user| describe "AASM \"#{event}\" event" do - it "should return locked_hours back to used_hours when #{event}ed" do + it "should return locked_hours back to usable_hours when pending to #{event}ed" do leave_time_usage = leave_application.leave_time_usages.first leave_time.reload expect(leave_time_usage.leave_time).to eq leave_time - expect(leave_time.usable_hours).to eq (quota - total_leave_hours) + expect(leave_time.usable_hours).to eq(quota - total_leave_hours) expect(leave_time.locked_hours).to eq total_leave_hours leave_application.reload.send :"#{event}!", (required_user ? user : nil) leave_time.reload @@ -85,8 +88,27 @@ end end - it_should_behave_like 'return locked_hours back to used_hours', :reject, true - it_should_behave_like 'return locked_hours back to used_hours', :cancel + describe 'AASM "reject" event' do + it_should_behave_like 'return locked_hours back to usable_hours', :reject, true + + it 'should return used_hours back to usable_hours when approved to rejected' do + leave_application.reload.approve! user + leave_time.reload + expect(leave_time.usable_hours).to eq(quota - total_leave_hours) + expect(leave_time.used_hours).to eq total_leave_hours + expect(leave_time.locked_hours).to be_zero + leave_application.reload.reject! user + leave_time.reload + expect(leave_time.usable_hours).to eq quota + expect(leave_time.used_hours).to be_zero + expect(leave_time.locked_hours).to be_zero + expect(leave_application.leave_time_usages).to be_empty + end + end + + describe 'AASM "cancel" event' do + it_should_behave_like 'return locked_hours back to usable_hours', :cancel + end describe 'AASM "revise" event' do shared_examples 'revise attribute' do |attribute, value| @@ -96,10 +118,11 @@ leave_application.reload used_hours = Daikichi::Config::Biz.within(leave_application.start_time, leave_application.end_time).in_hours leave_time_usage = leave_application.leave_time_usages.first + total_used_hours = leave_application.leave_time_usages.map(&:used_hours).sum leave_time.reload expect(leave_application.hours).to eq used_hours - expect(leave_application.status).to eq "pending" - expect(leave_time_usage.used_hours).to eq used_hours + expect(leave_application.status).to eq 'pending' + expect(total_used_hours).to eq used_hours expect(leave_time_usage.leave_time).to eq leave_time expect(leave_time.usable_hours).to eq quota - used_hours expect(leave_time.locked_hours).to eq used_hours @@ -107,15 +130,15 @@ end context 'pending application' do - let!(:leave_application) { create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) } + let!(:leave_application) { create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) } it_should_behave_like 'revise attribute', :start_time, Time.zone.local(2017, 5, 3, 9, 30) it_should_behave_like 'revise attribute', :end_time, Time.zone.local(2017, 5, 3, 12, 30) it_should_behave_like 'revise attribute', :description, Faker::Lorem.paragraph end context 'approved application' do - let!(:leave_application) do - create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) + let!(:leave_application) do + create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) user.leave_applications.first.approve! user user.leave_applications.first end diff --git a/spec/services/leave_time_batch_builder_spec.rb b/spec/services/leave_time_batch_builder_spec.rb index 3189f513..e13a44fa 100644 --- a/spec/services/leave_time_batch_builder_spec.rb +++ b/spec/services/leave_time_batch_builder_spec.rb @@ -5,6 +5,7 @@ let(:monthly_lead_days) { Settings.leed_days.monthly } let(:join_date_based_leed_days) { Settings.leed_days.join_date_based } let(:monthly_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'monthly' } } + let(:weekly_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'weekly' } } let(:join_date_based_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'join_date_based' } } let(:seniority_based_leave_types) do join_date_based_leave_types.select do |lt| @@ -18,7 +19,8 @@ context 'is forced' do let!(:fulltime) { FactoryGirl.create(:user, :fulltime, join_date: Date.current - 1.year - 1.day) } - let!(:parttime) { FactoryGirl.create(:user, :parttime, join_date: Date.current - 1.year - 1.day) } + let!(:parttime) { FactoryGirl.create(:user, :intern, join_date: Date.current - 1.year - 1.day) } + let!(:contractor) { FactoryGirl.create(:user, :contractor, join_date: Date.current - 1.year - 1.day) } let!(:user) { FactoryGirl.create(:user, join_date: Date.current - 1.year - 1.day) } before do @@ -26,36 +28,39 @@ end it 'should run join_date_based_import and monthly import with prebuild option for all users' do - leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id]) - expect(leave_times.reload.size).to eq((monthly_leave_types.size + join_date_based_leave_types.size) * 3 - seniority_based_leave_types.size) - monthly_leave_time = leave_times.find { |x| x.leave_type == monthly_leave_types.first.first } - expect(monthly_leave_time.effective_date).to eq Time.zone.today - expect(monthly_leave_time.expiration_date).to eq Time.zone.today.end_of_month + unless monthly_leave_types.blank? + leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id, contractor.id]) + expect(leave_times.reload.size).to eq(1 + (monthly_leave_types.size + join_date_based_leave_types.size) * 3 - seniority_based_leave_types.size) + monthly_leave_time = leave_times.find { |x| x.leave_type == monthly_leave_types.first.first } + expect(monthly_leave_time.effective_date).to eq Time.zone.today + expect(monthly_leave_time.expiration_date).to eq Time.zone.today.end_of_month - join_date_based_leave_time = leave_times.find { |x| x.leave_type == join_date_based_leave_types.first.first } - join_anniversary = user.next_join_anniversary - expect(join_date_based_leave_time.effective_date).to eq join_anniversary - expect(join_date_based_leave_time.expiration_date).to eq join_anniversary + 1.year - 1.day + join_date_based_leave_time = leave_times.find { |x| x.leave_type == join_date_based_leave_types.first.first } + join_anniversary = user.next_join_anniversary + expect(join_date_based_leave_time.effective_date).to eq join_anniversary + expect(join_date_based_leave_time.expiration_date).to eq join_anniversary + 1.year - 1.day + end end end context 'not forced' do let!(:fulltime) { FactoryGirl.create(:user, :fulltime, join_date: join_date) } - let!(:parttime) { FactoryGirl.create(:user, :parttime, join_date: join_date) } + let!(:parttime) { FactoryGirl.create(:user, :intern, join_date: join_date) } + let!(:contractor) { FactoryGirl.create(:user, :contractor, join_date: join_date) } let!(:user) { FactoryGirl.create(:user, join_date: join_date - 1.day) } + let!(:datetime) { Time.zone.local(2017, 5, 4, 9, 30) } context 'end of working month' do - let(:join_date) { Daikichi::Config::Biz.time(monthly_lead_days, :days).before(Daikichi::Config::Biz.periods.before(Time.current.end_of_month).first.end_time) - 2.years + join_date_based_leed_days.days } - + let(:join_date) { Daikichi::Config::Biz.time(monthly_lead_days, :days).before(Daikichi::Config::Biz.periods.before(datetime.end_of_month).first.end_time) - 2.years + join_date_based_leed_days.days } before do - Timecop.freeze(Daikichi::Config::Biz.time(monthly_lead_days, :days).before(Daikichi::Config::Biz.periods.before(Time.current.end_of_month).first.end_time)) + Timecop.freeze(Daikichi::Config::Biz.time(monthly_lead_days, :days).before(Daikichi::Config::Biz.periods.before(datetime.end_of_month).first.end_time)) described_class.new.automatically_import end after { Timecop.return } it 'should run join date based import only for users that join_date anniversary is comming and monthly import without prebuild option for all users' do - leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id]) - expect(leave_times.reload.size).to eq(monthly_leave_types.size * 3 + join_date_based_leave_types.size * 2 - seniority_based_leave_types.size) + leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id, contractor.id]) + expect(leave_times.reload.size).to eq(1 + weekly_leave_types.size * 3 + join_date_based_leave_types.size * 2 - seniority_based_leave_types.size) join_date_based_leave_time = leave_times.find { |x| x.leave_type == join_date_based_leave_types.first.first } join_anniversary = fulltime.next_join_anniversary @@ -66,16 +71,19 @@ context 'not end of working month' do let(:join_date) { Date.current.end_of_month + 3.days - 2.years + join_date_based_leed_days.days } - before do - Timecop.freeze(Date.current.end_of_month + 3.days) + if (Date.current.end_of_month + 3.days - 2.years).month <= 2 && (Date.current.end_of_month + 3.days - 2.years).leap? + Timecop.freeze(Date.current.end_of_month + 2.days) + else + Timecop.freeze(Date.current.end_of_month + 3.days) + end described_class.new.automatically_import end after { Timecop.return } it 'should run join date based import for users that join_date anniversary is comming only' do - leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id]) - expect(leave_times.reload.size).to eq(join_date_based_leave_types.size * 2 - seniority_based_leave_types.size) + leave_times = LeaveTime.where(user_id: [fulltime.id, parttime.id, user.id, contractor.id]) + expect(leave_times.reload.size).to eq(1 + join_date_based_leave_types.size * 2 - seniority_based_leave_types.size) join_date_based_leave_time = leave_times.find { |x| x.leave_type == join_date_based_leave_types.first.first } join_anniversary = fulltime.next_join_anniversary diff --git a/spec/services/leave_time_builder_spec.rb b/spec/services/leave_time_builder_spec.rb index a7dce2db..800dc23f 100644 --- a/spec/services/leave_time_builder_spec.rb +++ b/spec/services/leave_time_builder_spec.rb @@ -2,8 +2,9 @@ require 'rails_helper' describe LeaveTimeBuilder do - let(:user) { FactoryGirl.create(:user) } + let(:user) { FactoryGirl.create(:user, assign_date: Time.zone.today) } let(:monthly_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'monthly' } } + let(:weekly_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'weekly' } } let(:join_date_based_leave_types) { Settings.leave_types.to_a.select { |lt| lt.second['creation'] == 'join_date_based' } } let(:seniority_based_leave_types) do join_date_based_leave_types.select do |lt| @@ -16,7 +17,7 @@ context 'fulltime employee' do context 'import join date based LeaveTime with specific assign_date' do let(:user) { User.new(FactoryGirl.attributes_for(:user, :fulltime)) } - let(:current_date) { Date.parse "2017/06/14" } + let(:current_date) { Date.parse '2017/06/14' } before do Timecop.freeze current_date @@ -80,22 +81,22 @@ def join_date_based_leave_times(user) expect(leave_times.count).to eq 1 expect(leave_times.first.effective_date).to eq Date.parse '2017/07/14' expect(leave_times.first.expiration_date).to eq Date.parse '2018/05/13' - end + end end it 'should build LeaveTime based on assign_date when assign_date is after current date and after this year leed day' do - user.join_date = current_date + join_date_based_leed_day - 2.year + user.join_date = current_date + join_date_based_leed_day - 2.years user.assign_date = (current_date + join_date_based_leed_day).to_s user.save! join_date_based_leave_times(user) do |leave_times| expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/24' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/23' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/13' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/12' end end it 'should build LeaveTime based on assign_date when assign_date is after current date and before this year leed day' do - user.join_date = current_date + join_date_based_leed_day - 2.year + 1.day + user.join_date = current_date + join_date_based_leed_day - 2.years + 1.day user.assign_date = (current_date + join_date_based_leed_day + 1.day).to_s user.save! join_date_based_leave_times(user) do |leave_times| @@ -113,8 +114,8 @@ def join_date_based_leave_times(user) user.save! join_date_based_leave_times(user) do |leave_times| expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/24' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/23' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/13' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/12' end end @@ -124,8 +125,8 @@ def join_date_based_leave_times(user) user.save! join_date_based_leave_times(user) do |leave_times| expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/25' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/24' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/14' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/13' end end end @@ -133,12 +134,12 @@ def join_date_based_leave_times(user) context 'import monthly LeaveTime with specific assign_date' do let(:user) { User.new(FactoryGirl.attributes_for(:user, :fulltime)) } - let(:current_date) { Date.parse "2017/06/14" } + let(:current_date) { Date.parse '2017/06/14' } join_date = Date.parse('2017/06/14') - 1.year - 1.month before_join_date = join_date - 1.month after_join_date_before_current_date = join_date + 1.month let(:after_current_date) { current_date + 1.month } - + before do Timecop.freeze current_date user.assign_leave_time = '1' @@ -174,7 +175,7 @@ def monthly_leave_times(user) end end end - + it_should_behave_like 'build monthly LeaveTime', based_on: 'join_date', when: 'assign_date is before join_date', assign_date: before_join_date it_should_behave_like 'build monthly LeaveTime', based_on: 'join_date', when: 'assign_date is on join_date', assign_date: join_date it_should_behave_like 'build monthly LeaveTime', based_on: 'assign_date', when: 'assign_date is after join_date and before current_date', assign_date: after_join_date_before_current_date @@ -190,8 +191,8 @@ def monthly_leave_times(user) context 'partime employee' do context 'import join date based LeaveTime with specific assign_date' do - let(:user) { User.new(FactoryGirl.attributes_for(:user, :parttime)) } - let(:current_date) { Date.parse "2017/06/14" } + let(:user) { User.new(FactoryGirl.attributes_for(:user, :intern)) } + let(:current_date) { Date.parse '2017/06/14' } before do Timecop.freeze current_date @@ -271,11 +272,11 @@ def join_date_based_leave_times(user) expect(leave_times.count).to eq 1 expect(leave_times.first.effective_date).to eq Date.parse '2017/07/14' expect(leave_times.first.expiration_date).to eq Date.parse '2018/05/13' - end + end end it 'should build LeaveTime based on assign_date when assign_date is after current date and after this year leed day' do - user.join_date = current_date + join_date_based_leed_day - 2.year + user.join_date = current_date + join_date_based_leed_day - 2.years user.assign_date = (current_date + join_date_based_leed_day).to_s user.save! join_date_based_leave_times(user) do |leave_times, leave_type| @@ -284,13 +285,13 @@ def join_date_based_leave_times(user) next end expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/24' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/23' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/13' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/12' end end it 'should build LeaveTime based on assign_date when assign_date is after current date and before this year leed day' do - user.join_date = current_date + join_date_based_leed_day - 2.year + 1.day + user.join_date = current_date + join_date_based_leed_day - 2.years + 1.day user.assign_date = (current_date + join_date_based_leed_day + 1.day).to_s user.save! join_date_based_leave_times(user) do |leave_times, leave_type| @@ -316,8 +317,8 @@ def join_date_based_leave_times(user) next end expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/24' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/23' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/13' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/12' end end @@ -331,8 +332,8 @@ def join_date_based_leave_times(user) next end expect(leave_times.count).to eq 1 - expect(leave_times.first.effective_date).to eq Date.parse '2017/07/25' - expect(leave_times.first.expiration_date).to eq Date.parse '2018/07/24' + expect(leave_times.first.effective_date).to eq Date.parse '2017/08/14' + expect(leave_times.first.expiration_date).to eq Date.parse '2018/08/13' end end end @@ -340,12 +341,12 @@ def join_date_based_leave_times(user) context 'import monthly LeaveTime with specific assign_date' do let(:user) { User.new(FactoryGirl.attributes_for(:user, :fulltime)) } - let(:current_date) { Date.parse "2017/06/14" } + let(:current_date) { Date.parse '2017/06/14' } join_date = Date.parse('2017/06/14') - 1.year - 1.month before_join_date = join_date - 1.month after_join_date_before_current_date = join_date + 1.month let(:after_current_date) { current_date + 1.month } - + before do Timecop.freeze current_date user.assign_leave_time = '1' @@ -381,7 +382,7 @@ def monthly_leave_times(user) end end end - + it_should_behave_like 'build monthly LeaveTime', based_on: 'join_date', when: 'assign_date is before join_date', assign_date: before_join_date it_should_behave_like 'build monthly LeaveTime', based_on: 'join_date', when: 'assign_date is on join_date', assign_date: join_date it_should_behave_like 'build monthly LeaveTime', based_on: 'assign_date', when: 'assign_date is after join_date and before current_date', assign_date: after_join_date_before_current_date @@ -428,13 +429,13 @@ def monthly_leave_times(user) end context 'parttime employee' do - let(:user) { FactoryGirl.create(:user, :parttime) } + let(:user) { FactoryGirl.create(:user, :intern) } it 'should not get seniority_based leave_times' do leave_times = user.leave_times expect(leave_times.size).to eq join_date_based_leave_types.size - seniority_based_leave_types.size leave_time = leave_times.first - initial_quota = join_date_based_leave_types.select { |lt| lt.first == leave_time.leave_type }.first.second['quota'] * 8 + initial_quota = join_date_based_leave_types.find { |lt| lt.first == leave_time.leave_type }.second['quota'] * 8 expect(leave_time.quota).to eq initial_quota expect(leave_time.usable_hours).to eq initial_quota expect(leave_time.used_hours).to eq 0 @@ -459,15 +460,17 @@ def monthly_leave_times(user) end it 'is build for the comming month' do - leave_times = user.leave_times.reload - expect(leave_times.size).to eq monthly_leave_types.size - leave_time = leave_times.first - initial_quota = monthly_leave_types.select { |lt| lt.first == leave_time.leave_type }.first.second['quota'] * 8 - expect(leave_time.quota).to eq initial_quota - expect(leave_time.usable_hours).to eq initial_quota - expect(leave_time.used_hours).to eq 0 - expect(leave_time.effective_date).to eq Time.zone.today.next_month.beginning_of_month - expect(leave_time.expiration_date).to eq Time.zone.today.next_month.end_of_month + unless monthly_leave_types.blank? + leave_times = user.leave_times.reload + expect(leave_times.size).to eq monthly_leave_types.size + leave_time = leave_times.first + initial_quota = monthly_leave_types.find { |lt| lt.first == leave_time.leave_type }.second['quota'] * 8 + expect(leave_time.quota).to eq initial_quota + expect(leave_time.usable_hours).to eq initial_quota + expect(leave_time.used_hours).to eq 0 + expect(leave_time.effective_date).to eq Time.zone.today.next_month.beginning_of_month + expect(leave_time.expiration_date).to eq Time.zone.today.next_month.end_of_month + end end end @@ -477,15 +480,67 @@ def monthly_leave_times(user) end it 'is build for current month' do + unless monthly_leave_types.blank? + leave_times = user.leave_times.reload + expect(leave_times.size).to eq monthly_leave_types.size + leave_time = leave_times.first + initial_quota = monthly_leave_types.find { |lt| lt.first == leave_time.leave_type }.second['quota'] * 8 + expect(leave_time.quota).to eq initial_quota + expect(leave_time.usable_hours).to eq initial_quota + expect(leave_time.used_hours).to eq 0 + expect(leave_time.effective_date).to eq Time.zone.today + expect(leave_time.expiration_date).to eq Time.zone.today.end_of_month + end + end + end + end + + describe '.weekly_import' do + before do + User.skip_callback(:create, :after, :auto_assign_leave_time) + end + + after do + User.set_callback(:create, :after, :auto_assign_leave_time) + end + + it 'create new user' do + LeaveTimeBuilder.new(user).weekly_import(by_assign_date: true) + leave_times = user.leave_times.reload + expect(leave_times.size).to eq weekly_leave_types.size * 4 + date = Time.zone.today + leave_time = leave_times.first + initial_quota = weekly_leave_types.find { |lt| lt.first == leave_time.leave_type }.second['quota'] * 8 + expect(leave_time.quota).to eq initial_quota + expect(leave_time.usable_hours).to eq initial_quota + expect(leave_time.used_hours).to eq 0 + expect(leave_time.effective_date).to eq user.assign_date + expect(leave_time.expiration_date).to eq user.assign_date.end_of_week + end + + it 'build leave time for after four weeks if today is Monday' do + if Time.zone.today.monday? + LeaveTimeBuilder.new(user).weekly_import leave_times = user.leave_times.reload - expect(leave_times.size).to eq monthly_leave_types.size + expect(leave_times.size).to eq weekly_leave_types.size + date = Time.zone.today leave_time = leave_times.first - initial_quota = monthly_leave_types.select { |lt| lt.first == leave_time.leave_type }.first.second['quota'] * 8 + initial_quota = weekly_leave_types.find { |lt| lt.first == leave_time.leave_type }.second['quota'] * 8 expect(leave_time.quota).to eq initial_quota expect(leave_time.usable_hours).to eq initial_quota expect(leave_time.used_hours).to eq 0 - expect(leave_time.effective_date).to eq Time.zone.today - expect(leave_time.expiration_date).to eq Time.zone.today.end_of_month + expect(leave_time.effective_date).to eq (date + 4.week).beginning_of_week + expect(leave_time.expiration_date).to eq (date + 4.week).end_of_week + end + end + + it 'will not build leave time for after four weeks because today is not Monday' do + unless Time.zone.today.monday? + LeaveTimeBuilder.new(user).weekly_import + leave_times = user.leave_times.reload + expect(leave_times.size).to eq 0 + leave_time = leave_times.first + expect(leave_time).to eq nil end end end diff --git a/spec/services/leave_time_summary_service_spec.rb b/spec/services/leave_time_summary_service_spec.rb new file mode 100644 index 00000000..6b8d1ca1 --- /dev/null +++ b/spec/services/leave_time_summary_service_spec.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true +require 'rails_helper' + +describe LeaveTimeSummaryService do + before { Timecop.freeze(Time.zone.local(2018, 1, 2)) } + let!(:manager) { create(:user, :manager) } + let!(:user) { create(:user, :employee, join_date: Time.zone.local(2018, 1, 2)) } + let!(:user2) { create(:user, :employee, join_date: Time.zone.local(2016, 1, 2)) } + + describe 'summary total leave_time' do + context 'same leave type' do + let(:start_time) { Time.zone.local(2018, 1, 5, 9, 30) } + let(:end_time) { Time.zone.local(2018, 1, 10, 18, 30) } + it 'should calculate total leave' do + leave_application = create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, :personal, user: user2, start_time: start_time, end_time: end_time).reload + leave_application.approve!(manager) + summary = LeaveTimeSummaryService.new(Date.current.year, Date.current.month).summary + expect(summary[user.id]['annual']).to eq 32 + expect(summary[user2.id]['annual']).to eq 32 + end + end + + context 'different leave type' do + let(:personal_start_time) { Time.zone.local(2018, 1, 18, 9, 30) } + let(:personal_end_time) { Time.zone.local(2018, 1, 31, 18, 30) } + it 'should calculate total leave times for different leave type' do + leave_application = create(:leave_application, user: user, start_time: personal_start_time, end_time: personal_end_time).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, user: user2, start_time: personal_start_time, end_time: personal_end_time).reload + leave_application.approve!(manager) + summary = LeaveTimeSummaryService.new(2018, 1).summary + expect(summary[user.id]['annual']).to eq 56 + expect(summary[user.id]['personal']).to eq 24 + expect(summary[user2.id]['annual']).to eq 80 + end + end + + context 'start time and end time in different month' do + let(:start_time) { Time.zone.local(2018, 1, 25, 10, 30) } + let(:start_time2) { Time.zone.local(2018, 1, 30, 9, 30) } + let(:end_time) { Time.zone.local(2018, 1, 29, 18, 30) } + let(:end_time2) { Time.zone.local(2018, 2, 9, 18, 30) } + it 'should calculate total sick leave times in specified month' do + leave_application = create(:leave_application, :halfpaid_sick, user: user, start_time: start_time, end_time: end_time).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, :halfpaid_sick, user: user, start_time: start_time2, end_time: end_time2).reload + leave_application.approve!(manager) + summary = LeaveTimeSummaryService.new(2018, 1).summary + expect(summary[user.id]['halfpaid_sick']).to eq 39 + summary = LeaveTimeSummaryService.new(2018, 2).summary + expect(summary[user.id]['halfpaid_sick']).to eq 56 + end + + it 'should calculate total personal leave times in specified month' do + create(:leave_time, :bonus, user: user, quota: 8, usable_hours: 8) + leave_application = create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, :personal, user: user, start_time: start_time2, end_time: end_time2).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, :personal, user: user2, start_time: start_time, end_time: end_time).reload + leave_application.approve!(manager) + leave_application = create(:leave_application, :personal, user: user2, start_time: start_time2, end_time: end_time2).reload + leave_application.approve!(manager) + summary = LeaveTimeSummaryService.new(2018, 1).summary + expect(summary[user.id]['bonus']).to eq 8 + expect(summary[user.id]['annual']).to eq 31 + expect(summary[user2.id]['annual']).to eq 39 + summary = LeaveTimeSummaryService.new(2018, 2).summary + expect(summary[user.id]['annual']).to eq 25 + expect(summary[user.id]['personal']).to eq 31 + expect(summary[user2.id]['annual']).to eq 41 + expect(summary[user2.id]['personal']).to eq 15 + end + end + end +end diff --git a/spec/services/leave_time_usage_builder_spec.rb b/spec/services/leave_time_usage_builder_spec.rb index 55a3996f..615c304b 100644 --- a/spec/services/leave_time_usage_builder_spec.rb +++ b/spec/services/leave_time_usage_builder_spec.rb @@ -4,7 +4,7 @@ let(:user) { create(:user) } let(:start_time) { Time.zone.local(2017, 5, 4, 9, 30) } let(:end_time) { Time.zone.local(2017, 5, 9, 12, 30) } - let(:leave_application) { create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) } + let(:leave_application) { create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) } let(:total_used_hours) { Daikichi::Config::Biz.within(start_time, end_time).in_hours } before { User.skip_callback(:create, :after, :auto_assign_leave_time) } @@ -18,7 +18,7 @@ it 'returns a hash to represent leave time as hours by date' do builder = described_class.new leave_application - expect(builder.leave_hours_by_date).to eq ({ + expect(builder.leave_hours_by_date).to eq({ Date.parse('2017-05-04') => 8, Date.parse('2017-05-05') => 8, Date.parse('2017-05-08') => 8, @@ -36,17 +36,19 @@ before { create(:leave_time, :annual, user: user, quota: quota, usable_hours: quota, effective_date: effective_date, expiration_date: expiration_date) } it 'should create successfully with sufficient usable hours' do - leave_application = create(:leave_application, :annual, user: user, start_time: Time.zone.local(2017, 5, 1, 9, 30), end_time: Time.zone.local(2017, 5, 5, 12, 30)) + leave_application = create(:leave_application, :personal, user: user, start_time: Time.zone.local(2017, 5, 1, 9, 30), end_time: Time.zone.local(2017, 5, 5, 12, 30)) leave_time_usage = leave_application.leave_time_usages.first + usage_total_used_hours = leave_application.leave_time_usages.map(&:used_hours).sum leave_time = leave_time_usage.leave_time - expect(leave_application.leave_time_usages.size).to eq 1 - expect(leave_time_usage.used_hours).to eq leave_application.hours - expect(leave_time.usable_hours).to eq (quota - leave_application.hours) + work_periods_by_date = Daikichi::Config::Biz.periods.after(leave_application.start_time).timeline.until(leave_application.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count + expect(leave_application.leave_time_usages.size).to eq work_periods_by_date + expect(usage_total_used_hours).to eq leave_application.hours + expect(leave_time.usable_hours).to eq(quota - leave_application.hours) expect(leave_time.locked_hours).to eq leave_application.hours end it 'should create failed with insufficient usable hours' do - leave_application = create(:leave_application, :annual, user: user, start_time: Time.zone.local(2017, 5, 1, 9, 30), end_time: Time.zone.local(2017, 5, 31, 12, 30)) + leave_application = create(:leave_application, :personal, user: user, start_time: Time.zone.local(2017, 5, 1, 9, 30), end_time: Time.zone.local(2017, 5, 31, 12, 30)) leave_time = user.leave_times.first expect(user.leave_applications).to be_empty expect(LeaveTimeUsage.where(leave_application: leave_application)).to be_empty @@ -61,13 +63,14 @@ create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: Date.parse('2017-05-06'), expiration_date: Date.parse('2017-05-10')) end - subject { create(:leave_application, :annual, user: user, start_time: Time.zone.local(2017, 5, 3, 9, 30), end_time: Time.zone.local(2017, 5, 12, 12, 30)) } + subject { create(:leave_application, :personal, user: user, start_time: Time.zone.local(2017, 5, 3, 9, 30), end_time: Time.zone.local(2017, 5, 12, 12, 30)) } it 'should create LeaveTimeUsage successfully with sufficent hours' do create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: Date.parse('2017-05-11'), expiration_date: Date.parse('2017-05-15')) + work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count expect(subject.errors).to be_empty expect(user.leave_applications.size).to be 1 - expect(subject.leave_time_usages.size).to be 3 + expect(subject.leave_time_usages.size).to be work_periods_by_date end it 'should failed to create LeaveTimeUsage with insufficient hours' do @@ -81,7 +84,7 @@ context 'didn\'t completely covered LeaveApplication' do let(:start_time) { Time.zone.local(2017, 5, 3, 9, 30) } let(:end_time) { Time.zone.local(2017, 5, 12, 12, 30) } - subject { create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) } + subject { create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) } shared_examples 'not covered partially' do |part, lt_params_1, lt_params_2, error_date, lack_hours| it "create failed with LeaveTimes not covering the #{part} of LeaveApplication" do @@ -104,14 +107,15 @@ let(:nearly_expired_date) { expiration_date - 1.day } let(:start_time) { Time.zone.local(2017, 5, 3, 9, 30) } let(:end_time) { Time.zone.local(2017, 5, 12, 12, 30) } - subject { create(:leave_application, :annual, user: user, start_time: start_time, end_time: end_time) } + subject { create(:leave_application, :personal, user: user, start_time: start_time, end_time: end_time) } context 'different expiration date' do let!(:nearly_expired_leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: nearly_expired_date) } let!(:leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) } it 'should use LeaveTime where nearly to expired prior to other LeaveTimes' do + work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to be 2 + expect(subject.leave_time_usages.size).to be work_periods_by_date + 1 nearly_expired_leave_time.reload leave_time.reload expect(nearly_expired_leave_time.usable_hours).to be_zero @@ -125,8 +129,9 @@ let!(:less_usable_hours_leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 49, locked_hours: 1, effective_date: effective_date, expiration_date: expiration_date) } let!(:leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) } it 'should use LeaveTime where less usable_hours prior to other LeaveTimes' do + work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to be 2 + expect(subject.leave_time_usages.size).to be work_periods_by_date + 1 less_usable_hours_leave_time.reload leave_time.reload expect(less_usable_hours_leave_time.usable_hours).to be_zero @@ -140,8 +145,9 @@ let!(:nearly_expired_leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: nearly_expired_date) } let!(:less_usable_hours_leave_time) { create(:leave_time, :annual, user: user, quota: 50, usable_hours: 49, locked_hours: 1, effective_date: effective_date, expiration_date: expiration_date) } it 'should use LeaveTime where nearly to expired prior to other LeaveTime with less usable_hours' do + work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to be 2 + expect(subject.leave_time_usages.size).to be work_periods_by_date + 1 nearly_expired_leave_time.reload less_usable_hours_leave_time.reload expect(nearly_expired_leave_time.usable_hours).to be_zero @@ -151,45 +157,49 @@ end end - context 'leave_type :sick' do - let!(:fullpaid_sick) { create(:leave_time, :fullpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) } - subject { create(:leave_application, :sick, user: user, start_time: start_time, end_time: end_time) } - it 'should use fullpaid_sick prior to halfpaid_sick' do - halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) - expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to eq 2 - fullpaid_sick.reload - halfpaid_sick.reload - expect(fullpaid_sick.usable_hours).to be_zero - expect(fullpaid_sick.locked_hours).to be 50 - expect(halfpaid_sick.usable_hours).to be 41 - expect(halfpaid_sick.locked_hours).to be 9 - end - - it 'should use fullpaid_sick prior to nearly expired halfpaid_sick' do - nearly_expired_halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: nearly_expired_date) - expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to be 2 - fullpaid_sick.reload - nearly_expired_halfpaid_sick.reload - expect(fullpaid_sick.usable_hours).to be_zero - expect(fullpaid_sick.locked_hours).to be 50 - expect(nearly_expired_halfpaid_sick.usable_hours).to be 41 - expect(nearly_expired_halfpaid_sick.locked_hours).to be 9 - end - - it 'should use fullpaid_sick prior to less usable_hours halfpaid_sick' do - less_usable_hours_halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 49, locked_hours: 1, effective_date: effective_date, expiration_date: expiration_date) - expect(subject.errors).to be_empty - expect(subject.leave_time_usages.size).to be 2 - fullpaid_sick.reload - less_usable_hours_halfpaid_sick.reload - expect(fullpaid_sick.usable_hours).to be_zero - expect(fullpaid_sick.locked_hours).to be 50 - expect(less_usable_hours_halfpaid_sick.usable_hours).to be 40 - expect(less_usable_hours_halfpaid_sick.locked_hours).to be 10 - end - end + #TODO: add spec about an application which is used two or more leave_times. + #context 'leave_type :sick' do + # let!(:fullpaid_sick) { create(:leave_time, :fullpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) } + # subject { create(:leave_application, :sick, user: user, start_time: start_time, end_time: end_time) } + # it 'should use fullpaid_sick prior to halfpaid_sick' do + # halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: expiration_date) + # expect(subject.errors).to be_empty + # work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count + # expect(subject.leave_time_usages.size).to eq work_periods_by_date + 1 + # fullpaid_sick.reload + # halfpaid_sick.reload + # expect(fullpaid_sick.usable_hours).to be_zero + # expect(fullpaid_sick.locked_hours).to be 50 + # expect(halfpaid_sick.usable_hours).to be 41 + # expect(halfpaid_sick.locked_hours).to be 9 + # end + + # it 'should use fullpaid_sick prior to nearly expired halfpaid_sick' do + # nearly_expired_halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 50, effective_date: effective_date, expiration_date: nearly_expired_date) + # expect(subject.errors).to be_empty + # work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count + # expect(subject.leave_time_usages.size).to be work_periods_by_date + 1 + # fullpaid_sick.reload + # nearly_expired_halfpaid_sick.reload + # expect(fullpaid_sick.usable_hours).to be_zero + # expect(fullpaid_sick.locked_hours).to be 50 + # expect(nearly_expired_halfpaid_sick.usable_hours).to be 41 + # expect(nearly_expired_halfpaid_sick.locked_hours).to be 9 + # end + + # it 'should use fullpaid_sick prior to less usable_hours halfpaid_sick' do + # less_usable_hours_halfpaid_sick = create(:leave_time, :halfpaid_sick, user: user, quota: 50, usable_hours: 49, locked_hours: 1, effective_date: effective_date, expiration_date: expiration_date) + # expect(subject.errors).to be_empty + # work_periods_by_date = Daikichi::Config::Biz.periods.after(subject.start_time).timeline.until(subject.end_time).to_a.group_by { |wp| wp.start_time.localtime.to_date }.count + # expect(subject.leave_time_usages.size).to be work_periods_by_date + 1 + # fullpaid_sick.reload + # less_usable_hours_halfpaid_sick.reload + # expect(fullpaid_sick.usable_hours).to be_zero + # expect(fullpaid_sick.locked_hours).to be 50 + # expect(less_usable_hours_halfpaid_sick.usable_hours).to be 40 + # expect(less_usable_hours_halfpaid_sick.locked_hours).to be 10 + # end + #end end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 98e07898..21301724 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require 'aasm/rspec' require 'simplecov' +require 'capybara/rspec' SimpleCov.start # This file was generated by the `rails generate rspec:install` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.