diff --git a/.gitignore b/.gitignore index b40534f17..f7690c651 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,7 @@ config/deploy_key db/encrypted_data/sample_data.sql.enc.gz db/encrypted_data/static_data.sql.enc.gz lib/templates/ldap/openldap-2.4-bcyrpt-module.tar.gz + +# RuboCop generates leftover config file when a common configuration is +# inherited from a remote source. To avoid this, we have to ignore the file. +/.rubocop-*-yml diff --git a/.rubocop.yml b/.rubocop.yml index f47a455e3..96ca11d57 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,4 +1,4 @@ -inherit_from: .rubocop_common.yml +inherit_from: https://raw.githubusercontent.com/omu/omu/master/etc/rubocop/rubocop.yml require: - rubocop-rails diff --git a/.rubocop_common.yml b/.rubocop_common.yml deleted file mode 100644 index e06768be5..000000000 --- a/.rubocop_common.yml +++ /dev/null @@ -1,25 +0,0 @@ -require: rubocop-performance - -Style/Documentation: - Enabled: false -Layout/HashAlignment: - Enabled: true - EnforcedHashRocketStyle: table - EnforcedColonStyle: table - -Layout/LineLength: - Max: 120 - -Naming/MemoizedInstanceVariableName: - EnforcedStyleForLeadingUnderscores: optional - -Style/AsciiComments: - Enabled: false -Style/FormatStringToken: - EnforcedStyle: template -Style/Lambda: - Enabled: false -Style/LambdaCall: - Enabled: false -Style/ParallelAssignment: - Enabled: false diff --git a/Gemfile b/Gemfile index f52da73fa..1dc3a578f 100644 --- a/Gemfile +++ b/Gemfile @@ -8,54 +8,54 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } # core gem 'bootsnap', require: false gem 'puma' -gem 'rails', '~> 6.0' -gem 'redis', '~> 4.0' +gem 'rails', '~> 6.0', '>= 6.0.3.2' +gem 'redis', '~> 4.2' gem 'sidekiq' # database gem 'pg' -gem 'pg_search' -gem 'pghero' -gem 'rein' +gem 'pg_search', '>= 2.3.2' +gem 'pghero', '>= 2.4.2' +gem 'rein', '>= 5.1.0' # active-record -gem 'ancestry' +gem 'ancestry', '>= 3.0.7' # active-storage gem 'aws-sdk-s3', require: false -gem 'image_processing', '~> 1.10.3' +gem 'image_processing', '~> 1.11.0' gem 'ruby-vips', '~> 2.0.17' # authentication gem 'authy' -gem 'devise' -gem 'omniauth_openid_connect' +gem 'devise', '>= 4.7.1' +gem 'omniauth_openid_connect', '>= 0.3.3' # authorization -gem 'pundit' +gem 'pundit', '>= 2.1.0' # ldap gem 'net-ldap' # assets: core asset dependencies # TODO: The following line should be removed when sassc-rails has the latest version of sassc. -gem 'sassc', '~> 2.2.1' -gem 'sassc-rails' +gem 'sassc', '~> 2.4.0' +gem 'sassc-rails', '>= 2.1.2' gem 'uglifier', '>= 1.3.0' -gem 'webpacker' +gem 'webpacker', '>= 5.1.1' # view helpers: tools for forms, views, etc. gem 'chartkick' gem 'cocoon' -gem 'font-awesome-rails' -gem 'groupdate' +gem 'font-awesome-rails', '>= 4.7.0.5' +gem 'groupdate', '>= 5.0.0' gem 'pagy' -gem 'simple_form' -gem 'wicked_pdf' +gem 'simple_form', '>= 5.0.2' +gem 'wicked_pdf', '>= 2.0.2' # api -gem 'jbuilder', '~> 2.9' +gem 'jbuilder', '~> 2.10', '>= 2.10.0' # security gem 'bcrypt', '~> 3.1.7' @@ -64,7 +64,7 @@ gem 'rack-attack' # validators gem 'telephone_number' -gem 'valid_email2' +gem 'valid_email2', '>= 3.2.2' # error tracking gem 'rollbar' @@ -74,25 +74,26 @@ gem 'slack-notifier' gem 'friendly_id', '~> 5.3.0' # sms -gem 'nexmo' -gem 'smstools' gem 'twilio-ruby' # log -gem 'lograge' +gem 'lograge', '>= 0.11.2' + +# cron +gem 'sidekiq-cron' group :development, :test do gem 'brakeman', require: false - gem 'bullet' + gem 'bullet', '>= 6.1.0' gem 'bundler-audit' gem 'byebug', platforms: %i[mri mingw x64_mingw] - gem 'dotenv-rails' - gem 'erb_lint', require: false - gem 'lol_dba' + gem 'dotenv-rails', '>= 2.7.5' + gem 'erb_lint', '>= 0.0.32', require: false + gem 'lol_dba', '>= 2.2.0' gem 'rubocop' gem 'rubocop-minitest' gem 'rubocop-performance' - gem 'rubocop-rails' + gem 'rubocop-rails', '>= 2.5.2' gem 'simplecov', require: false end @@ -100,7 +101,7 @@ group :test do gem 'capybara' gem 'codacy-coverage', require: false gem 'minitest-focus' - gem 'webdrivers', '~> 4.2' + gem 'webdrivers', '~> 4.4' gem 'webmock' end @@ -113,7 +114,7 @@ group :development do gem 'ruby-progressbar' gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' - gem 'web-console' + gem 'web-console', '>= 4.0.2' end # core plugins @@ -127,4 +128,4 @@ Dir['plugins/tenant/**/*.gemspec'].each do |gemspec| gem name, path: File.dirname(gemspec), require: false end -gem 'active_flag' +gem 'active_flag', '>= 1.5.0' diff --git a/Gemfile.lock b/Gemfile.lock index d5d64c6d9..cf6f45547 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ PATH remote: plugins/support specs: nokul-support (0.1.0) - activesupport (~> 6.0.0) + activesupport (>= 6.0.3.1) PATH remote: plugins/tenant/acme @@ -27,90 +27,90 @@ PATH GEM remote: https://rubygems.org/ specs: - actioncable (6.0.2.1) - actionpack (= 6.0.2.1) + actioncable (6.0.3.2) + actionpack (= 6.0.3.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.2.1) - actionpack (= 6.0.2.1) - activejob (= 6.0.2.1) - activerecord (= 6.0.2.1) - activestorage (= 6.0.2.1) - activesupport (= 6.0.2.1) + actionmailbox (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) mail (>= 2.7.1) - actionmailer (6.0.2.1) - actionpack (= 6.0.2.1) - actionview (= 6.0.2.1) - activejob (= 6.0.2.1) + actionmailer (6.0.3.2) + actionpack (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (6.0.2.1) - actionview (= 6.0.2.1) - activesupport (= 6.0.2.1) + actionpack (6.0.3.2) + actionview (= 6.0.3.2) + activesupport (= 6.0.3.2) rack (~> 2.0, >= 2.0.8) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.2.1) - actionpack (= 6.0.2.1) - activerecord (= 6.0.2.1) - activestorage (= 6.0.2.1) - activesupport (= 6.0.2.1) + actiontext (6.0.3.2) + actionpack (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) nokogiri (>= 1.8.5) - actionview (6.0.2.1) - activesupport (= 6.0.2.1) + actionview (6.0.3.2) + activesupport (= 6.0.3.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) active_flag (1.5.0) activerecord (>= 5) - activejob (6.0.2.1) - activesupport (= 6.0.2.1) + activejob (6.0.3.2) + activesupport (= 6.0.3.2) globalid (>= 0.3.6) - activemodel (6.0.2.1) - activesupport (= 6.0.2.1) - activerecord (6.0.2.1) - activemodel (= 6.0.2.1) - activesupport (= 6.0.2.1) - activestorage (6.0.2.1) - actionpack (= 6.0.2.1) - activejob (= 6.0.2.1) - activerecord (= 6.0.2.1) + activemodel (6.0.3.2) + activesupport (= 6.0.3.2) + activerecord (6.0.3.2) + activemodel (= 6.0.3.2) + activesupport (= 6.0.3.2) + activestorage (6.0.3.2) + actionpack (= 6.0.3.2) + activejob (= 6.0.3.2) + activerecord (= 6.0.3.2) marcel (~> 0.3.1) - activesupport (6.0.2.1) + activesupport (6.0.3.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) - zeitwerk (~> 2.2) + zeitwerk (~> 2.2, >= 2.2.2) addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) aes_key_wrap (1.0.1) ancestry (3.0.7) activerecord (>= 3.2.0) - ast (2.4.0) + ast (2.4.1) attr_required (1.0.1) authy (2.7.5) httpclient (>= 2.5.3.3) - aws-eventstream (1.0.3) - aws-partitions (1.269.0) - aws-sdk-core (3.89.1) - aws-eventstream (~> 1.0, >= 1.0.2) + aws-eventstream (1.1.0) + aws-partitions (1.336.0) + aws-sdk-core (3.102.1) + aws-eventstream (~> 1, >= 1.0.2) aws-partitions (~> 1, >= 1.239.0) aws-sigv4 (~> 1.1) jmespath (~> 1.0) - aws-sdk-kms (1.28.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-sdk-kms (1.35.0) + aws-sdk-core (~> 3, >= 3.99.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.60.1) - aws-sdk-core (~> 3, >= 3.83.0) + aws-sdk-s3 (1.72.0) + aws-sdk-core (~> 3, >= 3.102.1) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.1) - aws-sigv4 (1.1.0) - aws-eventstream (~> 1.0, >= 1.0.2) + aws-sigv4 (1.2.1) + aws-eventstream (~> 1, >= 1.0.2) bcrypt (3.1.13) - better_html (1.0.14) + better_html (1.0.15) actionview (>= 4.0) activesupport (>= 4.0) ast (~> 2.0) @@ -118,20 +118,20 @@ GEM html_tokenizer (~> 0.0.6) parser (>= 2.4) smart_properties - bindata (2.4.4) + bindata (2.4.7) bindex (0.8.1) - bootsnap (1.4.5) + bootsnap (1.4.6) msgpack (~> 1.0) - brakeman (4.7.2) + brakeman (4.8.2) builder (3.2.4) bullet (6.1.0) activesupport (>= 3.0.0) uniform_notifier (~> 1.11) - bundler-audit (0.6.1) + bundler-audit (0.7.0.1) bundler (>= 1.2.0, < 3) - thor (~> 0.18) - byebug (11.1.1) - capybara (3.30.0) + thor (>= 0.18, < 2) + byebug (11.1.3) + capybara (3.32.2) addressable mini_mime (>= 0.1.3) nokogiri (~> 1.8) @@ -142,11 +142,11 @@ GEM chartkick (3.3.1) childprocess (3.0.0) cocoon (1.2.14) - codacy-coverage (2.2.0) + codacy-coverage (2.2.1) simplecov - coderay (1.1.2) - concurrent-ruby (1.1.5) - connection_pool (2.2.2) + coderay (1.1.3) + concurrent-ruby (1.1.6) + connection_pool (2.2.3) crack (0.4.3) safe_yaml (~> 1.0.0) crass (1.0.6) @@ -161,49 +161,52 @@ GEM dotenv-rails (2.7.5) dotenv (= 2.7.5) railties (>= 3.2, < 6.1) - erb_lint (0.0.31) + erb_lint (0.0.33) activesupport better_html (~> 1.0.7) html_tokenizer rainbow - rubocop (~> 0.79.0) + rubocop (~> 0.79) smart_properties erubi (1.9.0) + et-orbi (1.2.4) + tzinfo execjs (2.7.0) - faraday (1.0.0) + faraday (1.0.1) multipart-post (>= 1.2, < 3) - ffi (1.12.1) + ffi (1.13.1) fit-commit (3.8.1) swearjar (~> 1.3) font-awesome-rails (4.7.0.5) railties (>= 3.2, < 6.1) friendly_id (5.3.0) activerecord (>= 4.0.0) + fugit (1.3.6) + et-orbi (~> 1.1, >= 1.1.8) + raabro (~> 1.3) globalid (0.4.2) activesupport (>= 4.2.0) - groupdate (4.3.0) + groupdate (5.0.0) activesupport (>= 5) - hashdiff (1.0.0) - hashie (3.6.0) + hashdiff (1.0.1) + hashie (4.1.0) html_tokenizer (0.0.7) httpclient (2.8.3) - i18n (1.8.2) + i18n (1.8.3) concurrent-ruby (~> 1.0) - image_processing (1.10.3) + image_processing (1.11.0) mini_magick (>= 4.9.5, < 5) ruby-vips (>= 2.0.17, < 3) - jaro_winkler (1.5.4) - jbuilder (2.9.1) - activesupport (>= 4.2.0) + jbuilder (2.10.0) + activesupport (>= 5.0.0) jmespath (1.4.0) - json (2.3.0) - json-jwt (1.11.0) + json-jwt (1.13.0) activesupport (>= 4.2) aes_key_wrap bindata jwt (2.2.1) - launchy (2.4.3) - addressable (~> 2.3) + launchy (2.5.0) + addressable (~> 2.7) letter_opener (1.7.0) launchy (~> 2.2) listen (3.2.1) @@ -214,42 +217,39 @@ GEM activesupport (>= 4) railties (>= 4) request_store (~> 1.0) - lol_dba (2.1.5) - actionpack (>= 3.0) - activerecord (>= 3.0) - railties (>= 3.0) - loofah (2.4.0) + lol_dba (2.2.0) + actionpack (>= 3.0, < 7.0) + activerecord (>= 3.0, < 7.0) + railties (>= 3.0, < 7.0) + loofah (2.6.0) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.1) mini_mime (>= 0.1.1) marcel (0.3.3) mimemagic (~> 0.3.2) - method_source (0.9.2) - mimemagic (0.3.3) + method_source (1.0.0) + mimemagic (0.3.5) mini_magick (4.10.1) mini_mime (1.0.2) mini_portile2 (2.4.0) - minitest (5.14.0) - minitest-focus (1.1.2) + minitest (5.14.1) + minitest-focus (1.2.1) minitest (>= 4, < 6) - msgpack (1.3.1) + msgpack (1.3.3) multipart-post (2.1.1) net-ldap (0.16.2) - nexmo (6.2.0) - jwt (~> 2) - zeitwerk (~> 2, >= 2.2) nio4r (2.5.2) - nokogiri (1.10.7) + nokogiri (1.10.9) mini_portile2 (~> 2.4.0) - omniauth (1.9.0) - hashie (>= 3.4.6, < 3.7.0) + omniauth (1.9.1) + hashie (>= 3.4.6) rack (>= 1.6.2, < 3) - omniauth_openid_connect (0.3.3) + omniauth_openid_connect (0.3.5) addressable (~> 2.5) omniauth (~> 1.9) openid_connect (~> 1.1) - openid_connect (1.1.8) + openid_connect (1.2.0) activemodel attr_required (>= 1.0.0) json-jwt (>= 1.5.0) @@ -260,106 +260,113 @@ GEM validate_url webfinger (>= 1.0.1) orm_adapter (0.5.0) - pagy (3.7.2) - parallel (1.19.1) - parser (2.7.0.2) - ast (~> 2.4.0) - pg (1.2.2) + pagy (3.8.2) + parallel (1.19.2) + parser (2.7.1.4) + ast (~> 2.4.1) + pg (1.2.3) pg_search (2.3.2) activerecord (>= 5.2) activesupport (>= 5.2) - pghero (2.4.1) + pghero (2.5.1) activerecord (>= 5) - pry (0.12.2) - coderay (~> 1.1.0) - method_source (~> 0.9.0) + pry (0.13.1) + coderay (~> 1.1) + method_source (~> 1.0) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (4.0.3) - puma (4.3.1) + public_suffix (4.0.5) + puma (4.3.5) nio4r (~> 2.0) pundit (2.1.0) activesupport (>= 3.0.0) - pwned (2.0.1) - rack (2.1.1) - rack-attack (6.2.2) + pwned (2.0.2) + raabro (1.3.1) + rack (2.2.3) + rack-attack (6.3.1) rack (>= 1.0, < 3) - rack-mini-profiler (1.1.4) + rack-mini-profiler (2.0.2) rack (>= 1.2.0) - rack-oauth2 (1.10.1) + rack-oauth2 (1.14.0) activesupport attr_required httpclient json-jwt (>= 1.11.0) - rack + rack (>= 2.1.0) rack-protection (2.0.8.1) rack rack-proxy (0.6.5) rack rack-test (1.1.0) rack (>= 1.0, < 3) - rails (6.0.2.1) - actioncable (= 6.0.2.1) - actionmailbox (= 6.0.2.1) - actionmailer (= 6.0.2.1) - actionpack (= 6.0.2.1) - actiontext (= 6.0.2.1) - actionview (= 6.0.2.1) - activejob (= 6.0.2.1) - activemodel (= 6.0.2.1) - activerecord (= 6.0.2.1) - activestorage (= 6.0.2.1) - activesupport (= 6.0.2.1) + rails (6.0.3.2) + actioncable (= 6.0.3.2) + actionmailbox (= 6.0.3.2) + actionmailer (= 6.0.3.2) + actionpack (= 6.0.3.2) + actiontext (= 6.0.3.2) + actionview (= 6.0.3.2) + activejob (= 6.0.3.2) + activemodel (= 6.0.3.2) + activerecord (= 6.0.3.2) + activestorage (= 6.0.3.2) + activesupport (= 6.0.3.2) bundler (>= 1.3.0) - railties (= 6.0.2.1) + railties (= 6.0.3.2) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) rails-html-sanitizer (1.3.0) loofah (~> 2.3) - railties (6.0.2.1) - actionpack (= 6.0.2.1) - activesupport (= 6.0.2.1) + railties (6.0.3.2) + actionpack (= 6.0.3.2) + activesupport (= 6.0.3.2) method_source rake (>= 0.8.7) thor (>= 0.20.3, < 2.0) rainbow (3.0.0) rake (13.0.1) - rb-fsevent (0.10.3) + rb-fsevent (0.10.4) rb-inotify (0.10.1) ffi (~> 1.0) - redis (4.1.3) - regexp_parser (1.6.0) + redis (4.2.1) + regexp_parser (1.7.1) rein (5.1.0) activerecord (>= 4.0.0) activesupport (>= 4.0.0) request_store (1.5.0) rack (>= 1.4) - responders (3.0.0) + responders (3.0.1) actionpack (>= 5.0) railties (>= 5.0) - rollbar (2.23.1) - rubocop (0.79.0) - jaro_winkler (~> 1.5.1) + rexml (3.2.4) + rollbar (2.26.0) + rubocop (0.86.0) parallel (~> 1.10) parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.7) + rexml + rubocop-ast (>= 0.0.3, < 1.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 1.4.0, < 1.7) - rubocop-minitest (0.5.1) + unicode-display_width (>= 1.4.0, < 2.0) + rubocop-ast (0.1.0) + parser (>= 2.7.0.1) + rubocop-minitest (0.9.0) rubocop (>= 0.74) - rubocop-performance (1.5.2) + rubocop-performance (1.6.1) rubocop (>= 0.71.0) - rubocop-rails (2.4.2) + rubocop-rails (2.6.0) + activesupport (>= 4.2.0) rack (>= 1.1) - rubocop (>= 0.72.0) + rubocop (>= 0.82.0) ruby-progressbar (1.10.1) ruby-vips (2.0.17) ffi (~> 1.9) - rubyzip (2.1.0) + rubyzip (2.3.0) safe_yaml (1.0.5) - sassc (2.2.1) + sassc (2.4.0) ffi (~> 1.9) sassc-rails (2.1.2) railties (>= 4.0.0) @@ -370,169 +377,171 @@ GEM selenium-webdriver (3.142.7) childprocess (>= 0.5, < 4.0) rubyzip (>= 1.2.2) - sidekiq (6.0.4) + semantic_range (2.3.0) + sidekiq (6.0.7) connection_pool (>= 2.2.2) - rack (>= 2.0.0) + rack (~> 2.0) rack-protection (>= 2.0.0) redis (>= 4.1.0) - simple_form (5.0.1) + sidekiq-cron (1.2.0) + fugit (~> 1.1) + sidekiq (>= 4.2.1) + simple_form (5.0.2) actionpack (>= 5.0) activemodel (>= 5.0) - simplecov (0.17.1) + simplecov (0.18.5) docile (~> 1.1) - json (>= 1.8, < 3) - simplecov-html (~> 0.10.0) - simplecov-html (0.10.2) + simplecov-html (~> 0.11) + simplecov-html (0.12.2) slack-notifier (2.3.2) smart_properties (1.15.0) - smstools (0.2.0) spring (2.1.0) spring-watcher-listen (2.0.1) listen (>= 2.7, < 4.0) spring (>= 1.2, < 3.0) - sprockets (4.0.0) + sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) sprockets-rails (3.2.1) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - swd (1.1.2) + swd (1.2.0) activesupport (>= 3) attr_required (>= 0.0.5) httpclient (>= 2.4) swearjar (1.3.1) - telephone_number (1.4.6) - thor (0.20.3) + telephone_number (1.4.7) + thor (1.0.1) thread_safe (0.3.6) tilt (2.0.10) - twilio-ruby (5.31.3) - faraday (~> 1.0.0) + twilio-ruby (5.37.0) + faraday (>= 0.9, < 2.0) jwt (>= 1.5, <= 2.5) nokogiri (>= 1.6, < 2.0) - tzinfo (1.2.6) + tzinfo (1.2.7) thread_safe (~> 0.1) uglifier (4.2.0) execjs (>= 0.3.0, < 3) - unicode-display_width (1.6.1) + unicode-display_width (1.7.0) uniform_notifier (1.13.0) - valid_email2 (3.1.3) + valid_email2 (3.2.2) activemodel (>= 3.2) mail (~> 2.5) validate_email (0.1.6) activemodel (>= 3.0) mail (>= 2.2.5) - validate_url (1.0.8) + validate_url (1.0.11) activemodel (>= 3.0.0) public_suffix warden (1.2.8) rack (>= 2.0.6) - web-console (4.0.1) + web-console (4.0.3) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webdrivers (4.2.0) + webdrivers (4.4.1) nokogiri (~> 1.6) rubyzip (>= 1.3.0) selenium-webdriver (>= 3.0, < 4.0) webfinger (1.1.0) activesupport httpclient (>= 2.4) - webmock (3.8.0) + webmock (3.8.3) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webpacker (4.2.2) - activesupport (>= 4.2) + webpacker (5.1.1) + activesupport (>= 5.2) rack-proxy (>= 0.6.1) - railties (>= 4.2) - websocket-driver (0.7.1) + railties (>= 5.2) + semantic_range (>= 2.3.0) + websocket-driver (0.7.2) websocket-extensions (>= 0.1.0) - websocket-extensions (0.1.4) - wicked_pdf (1.4.0) + websocket-extensions (0.1.5) + wicked_pdf (2.1.0) activesupport xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.2.2) + zeitwerk (2.3.1) PLATFORMS ruby DEPENDENCIES - active_flag - ancestry + active_flag (>= 1.5.0) + ancestry (>= 3.0.7) authy aws-sdk-s3 bcrypt (~> 3.1.7) bootsnap brakeman - bullet + bullet (>= 6.1.0) bundler-audit byebug capybara chartkick cocoon codacy-coverage - devise - dotenv-rails - erb_lint + devise (>= 4.7.1) + dotenv-rails (>= 2.7.5) + erb_lint (>= 0.0.32) fit-commit - font-awesome-rails + font-awesome-rails (>= 4.7.0.5) friendly_id (~> 5.3.0) - groupdate - image_processing (~> 1.10.3) - jbuilder (~> 2.9) + groupdate (>= 5.0.0) + image_processing (~> 1.11.0) + jbuilder (~> 2.10, >= 2.10.0) letter_opener listen (>= 3.0.5, < 3.3) - lograge - lol_dba + lograge (>= 0.11.2) + lol_dba (>= 2.2.0) minitest-focus net-ldap - nexmo nokul-support! nokul-tenant! nokul-tenant-acme! nokul-tenant-omu! - omniauth_openid_connect + omniauth_openid_connect (>= 0.3.3) pagy pg - pg_search - pghero + pg_search (>= 2.3.2) + pghero (>= 2.4.2) pry-rails puma - pundit + pundit (>= 2.1.0) pwned rack-attack rack-mini-profiler - rails (~> 6.0) - redis (~> 4.0) - rein + rails (~> 6.0, >= 6.0.3.2) + redis (~> 4.2) + rein (>= 5.1.0) rollbar rubocop rubocop-minitest rubocop-performance - rubocop-rails + rubocop-rails (>= 2.5.2) ruby-progressbar ruby-vips (~> 2.0.17) - sassc (~> 2.2.1) - sassc-rails + sassc (~> 2.4.0) + sassc-rails (>= 2.1.2) sidekiq - simple_form + sidekiq-cron + simple_form (>= 5.0.2) simplecov slack-notifier - smstools spring spring-watcher-listen (~> 2.0.0) telephone_number twilio-ruby uglifier (>= 1.3.0) - valid_email2 - web-console - webdrivers (~> 4.2) + valid_email2 (>= 3.2.2) + web-console (>= 4.0.2) + webdrivers (~> 4.4) webmock - webpacker - wicked_pdf + webpacker (>= 5.1.1) + wicked_pdf (>= 2.0.2) RUBY VERSION ruby 2.7.0p0 diff --git a/app.json b/app.json index 26a167923..51027eb27 100644 --- a/app.json +++ b/app.json @@ -7,7 +7,7 @@ "rails" ], "repository": "https://github.com/omu/nokul", - "version": "0.7.5", + "version": "0.7.6", "scripts": { "dokku": { "predeploy": "bin/plugdo bundle install -j4 --path /app/vendor/bundle --without development:test && bundle exec rails assets:precompile && bundle exec rails db:migrate" diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js index 6f144bc23..889079bc2 100644 --- a/app/assets/config/manifest.js +++ b/app/assets/config/manifest.js @@ -1,6 +1,6 @@ +//= link application.js +//= link coreui.js //= link_tree ../images //= link_directory ../stylesheets .css - -//= link application.js //= link chart.js //= link highcharts-langs/tr.js diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 8b150a265..f6fa57229 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -1,6 +1,6 @@ // Manifest file for common assets being used in panel -//= require coreui +//= require jquery/dist/jquery.min //= require helpers/dynamic_select //= require toastr/build/toastr.min //= require shared/toastr_config diff --git a/app/assets/javascripts/coreui.js b/app/assets/javascripts/coreui.js index 3e4bdc7d3..71d758e3f 100644 --- a/app/assets/javascripts/coreui.js +++ b/app/assets/javascripts/coreui.js @@ -1,10 +1,5 @@ // Manifest including all assets of CoreUI -// core-components -//= require jquery/dist/jquery.min -//= require popper.js/dist/umd/popper.min -//= require bootstrap/dist/js/bootstrap.min - // core-ui -//= require @coreui/coreui/dist/js/coreui.min +//= require @coreui/coreui/dist/js/coreui.bundle.min //= require @coreui/coreui/dist/js/coreui-utilities.min diff --git a/app/assets/javascripts/guest.js b/app/assets/javascripts/guest.js index 35270451b..9e7cdb162 100644 --- a/app/assets/javascripts/guest.js +++ b/app/assets/javascripts/guest.js @@ -1,7 +1,7 @@ // Manifest file for guest template. +//= require jquery/dist/jquery.min //= require rails-ujs -//= require coreui //= require toastr/build/toastr.min //= require shared/toastr_config //= require select2/dist/js/select2.min diff --git a/app/assets/stylesheets/custom/content_loader.css b/app/assets/stylesheets/custom/content_loader.css index 46ff552c2..072d1bf45 100644 --- a/app/assets/stylesheets/custom/content_loader.css +++ b/app/assets/stylesheets/custom/content_loader.css @@ -1,5 +1,3 @@ - - .loading { position: absolute; z-index: 1000; diff --git a/app/assets/stylesheets/custom/guest_footer.css b/app/assets/stylesheets/custom/guest_footer.css index a66c1b547..2c5a4f4f1 100644 --- a/app/assets/stylesheets/custom/guest_footer.css +++ b/app/assets/stylesheets/custom/guest_footer.css @@ -1,4 +1,4 @@ -.app-footer { +.c-footer { position: fixed; bottom: 0; width: 100%; diff --git a/app/controllers/account/yoksis_services_controller.rb b/app/controllers/account/yoksis_services_controller.rb index cc51bf304..89cb6d031 100644 --- a/app/controllers/account/yoksis_services_controller.rb +++ b/app/controllers/account/yoksis_services_controller.rb @@ -15,6 +15,7 @@ class YoksisServicesController < ApplicationController before_action :set_service def fetch + authorize(@service, policy_class: Account::YoksisServicePolicy) @service.public_send(:perform_later, current_user) flash.notice = t(".#{@service_name}") redirect_back fallback_location: params.fetch(:redirect, root_path) diff --git a/app/controllers/accreditation_management/accreditation_standards_controller.rb b/app/controllers/accreditation_management/accreditation_standards_controller.rb new file mode 100644 index 000000000..f50add428 --- /dev/null +++ b/app/controllers/accreditation_management/accreditation_standards_controller.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module AccreditationManagement + class AccreditationStandardsController < ApplicationController + include SearchableModule + + before_action :set_accreditation_standard, only: %i[show edit update destroy] + before_action :authorized? + + def index + standards = AccreditationStandard.includes(:accreditation_institution, :units).where( + params[:unit_id].present? ? { unit_accreditation_standards: { unit_id: params[:unit_id] } } : {} + ) + + @pagy, @accreditation_standards = pagy(standards.dynamic_search(search_params(AccreditationStandard))) + end + + def show + @learning_outcomes = @accreditation_standard.macro_learning_outcomes.ordered + end + + def new + @accreditation_standard = AccreditationStandard.new + end + + def edit; end + + def create + @accreditation_standard = AccreditationStandard.new(accreditation_standard_params) + @accreditation_standard.save ? redirect_with('success') : render(:new) + end + + def update + @accreditation_standard.update(accreditation_standard_params) ? redirect_with('success') : render(:edit) + end + + def destroy + message = @accreditation_standard.destroy ? 'success' : 'error' + redirect_with(message) + end + + def units + @units = params[:term].present? ? Unit.active.faculties.search(params[:term]) : Unit.active.faculties + respond_to :json + end + + private + + def redirect_with(message) + redirect_to accreditation_standards_path, flash: { info: t(".#{message}") } + end + + def set_accreditation_standard + @accreditation_standard = AccreditationStandard.find(params[:id]) + end + + def authorized? + authorize([:accreditation_management, @accreditation_standard || AccreditationStandard]) + end + + def accreditation_standard_params + params.require(:accreditation_standard).permit(:accreditation_institution_id, :version, :status, unit_ids: []) + end + end +end diff --git a/app/controllers/accreditation_management/learning_outcomes_controller.rb b/app/controllers/accreditation_management/learning_outcomes_controller.rb new file mode 100644 index 000000000..2328ddf67 --- /dev/null +++ b/app/controllers/accreditation_management/learning_outcomes_controller.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module AccreditationManagement + class LearningOutcomesController < ApplicationController + include SearchableModule + + before_action :set_accreditation_standard + before_action :set_learning_outcome, except: %i[new create] + before_action :authorized? + + def show; end + + def new + @learning_outcome = @accreditation_standard.learning_outcomes.new + end + + def edit; end + + def create + @learning_outcome = @accreditation_standard.learning_outcomes.new(learning_outcome_params) + @learning_outcome.save ? redirect_with('success') : render(:new) + end + + def update + @learning_outcome.update(learning_outcome_params) ? redirect_with('success') : render(:edit) + end + + def destroy + message = @learning_outcome.destroy ? 'success' : 'error' + redirect_with(message) + end + + private + + def redirect_with(message) + redirect_to accreditation_standard_path(@accreditation_standard), flash: { info: t(".#{message}") } + end + + def set_accreditation_standard + @accreditation_standard = AccreditationStandard.find(params[:accreditation_standard_id]) + end + + def set_learning_outcome + @learning_outcome = @accreditation_standard.macro_learning_outcomes.find(params[:id]) + end + + def authorized? + authorize([:accreditation_management, @learning_outcome || LearningOutcome]) + end + + def learning_outcome_params + params.require(:learning_outcome).permit( + :code, :name, :accreditation_standard_id, + micros_attributes: %i[id code name accreditation_standard_id _destroy] + ) + end + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3489bb908..2ac9d7b63 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,6 +25,19 @@ def default_url_options user_signed_in? ? { locale: nil } : { locale: I18n.locale } end + def switch_account + account = Patron::Account.find_by(user: current_user, type: params[:type], id: params[:id]) + path = root_path + + if account + session[:account] = account.identifier + flash[:notice] = t('switched_account') + path = [*account&.root_path, only_path: true] + end + + redirect_to path + end + protected def append_info_to_payload(payload) @@ -52,6 +65,19 @@ def configure_permitted_parameters devise_parameter_sanitizer.permit(:account_update, keys: %i[email]) end + def current_account + if session.key?(:account) && session.dig(:account, 'type').present? + Patron::Account.find_by( + user: current_user, type: session.dig(:account, 'type'), id: session.dig(:account, 'id') + ) + else + set_current_account + end + rescue Patron::Account::NotFound + set_current_account + end + helper_method :current_account + private def handle_unverified_request @@ -74,4 +100,13 @@ def update_preferred_language current_user.preferred_language = locale_params current_user.save(validate: false) end + + def set_current_account + if (account = current_user.accounts.first) + session[:account] = account.identifier + account + else + session.delete(:account) + end + end end diff --git a/app/controllers/calendar_management/calendar_event_types_controller.rb b/app/controllers/calendar_management/calendar_event_types_controller.rb index 2aca19e0d..9a9926986 100644 --- a/app/controllers/calendar_management/calendar_event_types_controller.rb +++ b/app/controllers/calendar_management/calendar_event_types_controller.rb @@ -5,6 +5,7 @@ class CalendarEventTypesController < ApplicationController include SearchableModule before_action :set_calendar_event_type, only: %i[edit update destroy] + before_action :authorized? def index @calendar_event_types = pagy_by_search(CalendarEventType.order(:name)) @@ -43,6 +44,10 @@ def set_calendar_event_type @calendar_event_type = CalendarEventType.find(params[:id]) end + def authorized? + authorize([:calendar_management, @calendar_event_type || CalendarEventType]) + end + def calendar_event_type_params params.require(:calendar_event_type).permit(:name, :identifier, :category) end diff --git a/app/controllers/calendar_management/calendars_controller.rb b/app/controllers/calendar_management/calendars_controller.rb index 49413e3ed..6a025a08b 100644 --- a/app/controllers/calendar_management/calendars_controller.rb +++ b/app/controllers/calendar_management/calendars_controller.rb @@ -5,6 +5,7 @@ class CalendarsController < ApplicationController include SearchableModule before_action :set_calendar, only: %i[show edit update destroy duplicate units] + before_action :authorized? def index @calendars = pagy_by_search(Calendar.includes(:academic_term).order(created_at: :desc)) @@ -59,6 +60,10 @@ def set_calendar .find(params[:id]) end + def authorized? + authorize([:calendar_management, @calendar || Calendar]) + end + def calendar_params params.require(:calendar) .permit( diff --git a/app/controllers/committee/agenda_types_controller.rb b/app/controllers/committee/agenda_types_controller.rb index ef5304d83..bcef45638 100644 --- a/app/controllers/committee/agenda_types_controller.rb +++ b/app/controllers/committee/agenda_types_controller.rb @@ -5,6 +5,7 @@ class AgendaTypesController < ApplicationController include SearchableModule before_action :set_agenda_type, only: %i[edit update destroy] + before_action :authorized? def index @agenda_types = pagy_by_search(AgendaType.order(:name)) @@ -39,6 +40,10 @@ def set_agenda_type @agenda_type = AgendaType.find(params[:id]) end + def authorized? + authorize([:committee, @agenda_type || AgendaType]) + end + def agenda_type_params params.require(:agenda_type).permit(:name) end diff --git a/app/controllers/committee/agendas_controller.rb b/app/controllers/committee/agendas_controller.rb index ec70cbf27..9f98ad1da 100644 --- a/app/controllers/committee/agendas_controller.rb +++ b/app/controllers/committee/agendas_controller.rb @@ -6,6 +6,7 @@ class AgendasController < ApplicationController before_action :set_committee before_action :set_agenda, only: %i[edit update destroy] + before_action :authorized? def index agendas = @@ -46,6 +47,10 @@ def set_agenda @agenda = @committee.agendas.find(params[:id]) end + def authorized? + authorize([:committee, @agenda || Agenda]) + end + def agenda_params params.require(:agenda).permit(:description, :status, :unit_id, :agenda_type_id, :agenda_file) end diff --git a/app/controllers/committee/dashboard_controller.rb b/app/controllers/committee/dashboard_controller.rb index 99108e29f..27e585dae 100644 --- a/app/controllers/committee/dashboard_controller.rb +++ b/app/controllers/committee/dashboard_controller.rb @@ -8,6 +8,8 @@ def index @committees = pagy_by_search( Unit.includes(:unit_type, district: :city).committees.active.order(:name) ) + + authorize(@committees, policy_class: Committee::DashboardPolicy) end end end diff --git a/app/controllers/committee/decisions_controller.rb b/app/controllers/committee/decisions_controller.rb index 15dfa160f..5a70279a0 100644 --- a/app/controllers/committee/decisions_controller.rb +++ b/app/controllers/committee/decisions_controller.rb @@ -4,6 +4,7 @@ module Committee class DecisionsController < ApplicationController before_action :set_committee_and_agenda before_action :set_decision, only: %i[show edit update] + before_action :authorized? def show; end @@ -30,13 +31,17 @@ def redirect_with(message) def set_committee_and_agenda @committee = Unit.committees.find(params[:committee_id]) - @agenda = @committee.meeting_agendas.find(params[:meeting_agenda_id]) + @agenda = @committee.meeting_agendas.find(params[:meeting_agenda_id]) end def set_decision @decision = @agenda.decision end + def authorized? + authorize(@decision || CommitteeDecision, policy_class: Committee::DecisionPolicy) + end + def decision_params params.require(:committee_decision).permit(:description) end diff --git a/app/controllers/committee/meetings_controller.rb b/app/controllers/committee/meetings_controller.rb index 84395af83..d6a465f33 100644 --- a/app/controllers/committee/meetings_controller.rb +++ b/app/controllers/committee/meetings_controller.rb @@ -4,6 +4,7 @@ module Committee class MeetingsController < ApplicationController before_action :set_committee before_action :set_meeting, only: %i[show edit update destroy] + before_action :authorized? def index @meetings = @committee.meetings.order(year: :desc, meeting_no: :asc) @@ -46,6 +47,10 @@ def set_meeting @meeting = @committee.meetings.find(params[:id]) end + def authorized? + authorize(@meeting || CommitteeMeeting, policy_class: Committee::MeetingPolicy) + end + def meeting_params params.require(:committee_meeting) .permit(:meeting_no, :meeting_date, diff --git a/app/controllers/concerns/reference_resource.rb b/app/controllers/concerns/reference_resource.rb index 2dd99b75d..d63ec213a 100644 --- a/app/controllers/concerns/reference_resource.rb +++ b/app/controllers/concerns/reference_resource.rb @@ -8,6 +8,12 @@ def self.included(base) def redirect_with(message) redirect_to(send("reference_#{controller_name}_path"), notice: t(".#{message}")) end + + protected + + def authorized? + authorize([:reference, instance_variable_get(@singular_variable.to_sym) || @model_name]) + end end end end diff --git a/app/controllers/concerns/yoksis_resource.rb b/app/controllers/concerns/yoksis_resource.rb index 84ad425cb..f99b945ff 100644 --- a/app/controllers/concerns/yoksis_resource.rb +++ b/app/controllers/concerns/yoksis_resource.rb @@ -7,6 +7,7 @@ module YoksisResource included do before_action :set_variables before_action :set_resource, only: %i[edit update destroy] + before_action :authorized? def index value = pagy_by_search(@model_name.order(:name)) @@ -46,6 +47,10 @@ def set_resource instance_variable_set(@singular_variable.to_sym, @model_name.find(params[:id])) end + def authorized? + authorize([:yoksis, instance_variable_get(@singular_variable.to_sym) || @model_name]) + end + def redirect_with(message) redirect_to(send("yoksis_#{controller_name}_path"), notice: t(".#{message}")) end diff --git a/app/controllers/course_management/available_course_groups_controller.rb b/app/controllers/course_management/available_course_groups_controller.rb index 7e0bcc2b3..1a081c998 100644 --- a/app/controllers/course_management/available_course_groups_controller.rb +++ b/app/controllers/course_management/available_course_groups_controller.rb @@ -5,6 +5,8 @@ class AvailableCourseGroupsController < ApplicationController before_action :set_available_course before_action :set_lecturers before_action :set_available_course_group, only: %i[edit update destroy] + before_action :authorized? + before_action :event_active?, except: %i[edit update] def new @available_course_group = @available_course.groups.new @@ -45,6 +47,16 @@ def set_lecturers @lecturers = @available_course.unit.subtree_employees end + def authorized? + authorize([:course_management, @available_course_group || AvailableCourseGroup]) + end + + def event_active? + return if @available_course.manageable? + + redirect_with('errors.not_proper_event_range') + end + def available_course_group_params params.require(:available_course_group).permit( :quota, :name, lecturers_attributes: %i[id lecturer_id coordinator _destroy] diff --git a/app/controllers/course_management/available_courses_controller.rb b/app/controllers/course_management/available_courses_controller.rb index 0a117a62d..1d1385c0b 100644 --- a/app/controllers/course_management/available_courses_controller.rb +++ b/app/controllers/course_management/available_courses_controller.rb @@ -5,6 +5,7 @@ class AvailableCoursesController < ApplicationController include SearchableModule before_action :set_available_course, only: %i[show edit update destroy] + before_action :authorized? def index available_courses = AvailableCourse.includes(:unit, :curriculum, :academic_term, curriculum_course: :course) @@ -27,13 +28,15 @@ def new def create @available_course = AvailableCourse.new(available_course_params) + return redirect_with('.errors.not_proper_event_range') unless @available_course.manageable? + @available_course.save ? redirect_to(@available_course, notice: t('.success')) : render(:new) end def edit; end def update - if @available_course.update(available_course_params) + if @available_course.update(available_course_params_for_update) redirect_to(@available_course, notice: t('.success')) else render(:edit) @@ -41,6 +44,8 @@ def update end def destroy + return redirect_with('.errors.not_proper_event_range') unless @available_course.manageable? + message = @available_course.destroy ? 'success' : 'error' redirect_with(message) end @@ -55,6 +60,10 @@ def set_available_course @available_course = AvailableCourse.find(params[:id]) end + def authorized? + authorize([:course_management, @available_course || AvailableCourse]) + end + def available_course_params params.require(:available_course).permit( :curriculum_id, :curriculum_course_id, :unit_id, :coordinator_id, @@ -62,5 +71,10 @@ def available_course_params lecturers_attributes: %i[id lecturer_id coordinator _destroy]] ) end + + def available_course_params_for_update + exception_keys = @available_course.manageable? ? [] : %i[curriculum_id curriculum_course_id unit_id] + available_course_params.except(*exception_keys) + end end end diff --git a/app/controllers/course_management/course_evaluation_types_controller.rb b/app/controllers/course_management/course_evaluation_types_controller.rb index e8a1e6a4d..4a5603341 100644 --- a/app/controllers/course_management/course_evaluation_types_controller.rb +++ b/app/controllers/course_management/course_evaluation_types_controller.rb @@ -4,6 +4,8 @@ module CourseManagement class CourseEvaluationTypesController < ApplicationController before_action :set_available_course before_action :set_course_evaluation_type, only: %i[edit update destroy] + before_action :authorized? + before_action :event_active?, except: %i[edit update] def new @evaluation_type = @available_course.evaluation_types.new @@ -40,6 +42,16 @@ def set_course_evaluation_type @evaluation_type = @available_course.evaluation_types.find(params[:id]) end + def authorized? + authorize([:course_management, @evaluation_type || CourseEvaluationType]) + end + + def event_active? + return if @available_course.manageable? + + redirect_with('errors.not_proper_event_range') + end + def course_evaluation_type_params params.require(:course_evaluation_type) .permit(:evaluation_type_id, :percentage, diff --git a/app/controllers/course_management/course_group_types_controller.rb b/app/controllers/course_management/course_group_types_controller.rb deleted file mode 100644 index cd0c3bb17..000000000 --- a/app/controllers/course_management/course_group_types_controller.rb +++ /dev/null @@ -1,50 +0,0 @@ -# frozen_string_literal: true - -module CourseManagement - class CourseGroupTypesController < ApplicationController - include SearchableModule - - before_action :set_course_group_type, only: %i[edit update destroy] - - def index - @course_group_types = pagy_by_search(CourseGroupType.order(:name)) - end - - def new - @course_group_type = CourseGroupType.new - end - - def create - @course_group_type = CourseGroupType.new(course_group_type_params) - @course_group_type.save ? redirect_with('success') : render(:new) - end - - def edit; end - - def update - @course_group_type.update(course_group_type_params) ? redirect_with('success') : render(:edit) - end - - def destroy - if @course_group_type.destroy - redirect_with('success') - else - redirect_to(course_group_types_path, alert: t('.warning')) - end - end - - private - - def redirect_with(message) - redirect_to(course_group_types_path, notice: t(".#{message}")) - end - - def set_course_group_type - @course_group_type = CourseGroupType.find(params[:id]) - end - - def course_group_type_params - params.require(:course_group_type).permit(:name) - end - end -end diff --git a/app/controllers/course_management/course_groups_controller.rb b/app/controllers/course_management/course_groups_controller.rb index 0f8369ea8..2b32fbe78 100644 --- a/app/controllers/course_management/course_groups_controller.rb +++ b/app/controllers/course_management/course_groups_controller.rb @@ -5,6 +5,7 @@ class CourseGroupsController < ApplicationController include Pagy::Backend before_action :set_course_group, only: %i[show edit update destroy] + before_action :authorized? def index course_groups = CourseGroup.includes(:unit, :course_group_type) @@ -45,6 +46,10 @@ def set_course_group @course_group = CourseGroup.find(params[:id]) end + def authorized? + authorize([:course_management, @course_group || CourseGroup]) + end + def course_group_params params.require(:course_group) .permit(:name, :total_ects_condition, :unit_id, :course_group_type_id, course_ids: []) diff --git a/app/controllers/course_management/course_types_controller.rb b/app/controllers/course_management/course_types_controller.rb deleted file mode 100644 index eb78a1b8e..000000000 --- a/app/controllers/course_management/course_types_controller.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -module CourseManagement - class CourseTypesController < ApplicationController - include SearchableModule - - before_action :set_course_type, only: %i[edit update destroy] - - def index - @course_types = pagy_by_search(CourseType.order(:name)) - end - - def new - @course_type = CourseType.new - end - - def create - @course_type = CourseType.new(course_type_params) - @course_type.save ? redirect_with('success') : render(:new) - end - - def edit; end - - def update - @course_type.update(course_type_params) ? redirect_with('success') : render(:edit) - end - - def destroy - message = @course_type.destroy ? 'success' : 'error' - redirect_with(message) - end - - private - - def redirect_with(message) - redirect_to(course_types_path, notice: t(".#{message}")) - end - - def set_course_type - @course_type = CourseType.find(params[:id]) - end - - def course_type_params - params.require(:course_type).permit(:name, :code, :min_credit) - end - end -end diff --git a/app/controllers/course_management/courses_controller.rb b/app/controllers/course_management/courses_controller.rb index 839084d02..05cfc8d63 100644 --- a/app/controllers/course_management/courses_controller.rb +++ b/app/controllers/course_management/courses_controller.rb @@ -5,6 +5,7 @@ class CoursesController < ApplicationController include SearchableModule before_action :set_course, only: %i[show edit update destroy] + before_action :authorized? def index courses = Course.includes(:unit, :language, :course_type) @@ -46,6 +47,10 @@ def set_course @course = Course.find(params[:id]) end + def authorized? + authorize([:course_management, @course || Course]) + end + def course_params params.require(:course).permit( :unit_id, :name, :code, :theoric, :practice, :program_type, diff --git a/app/controllers/course_management/curriculum_course_groups_controller.rb b/app/controllers/course_management/curriculum_course_groups_controller.rb index 138b18a6b..c9da4bd67 100644 --- a/app/controllers/course_management/curriculum_course_groups_controller.rb +++ b/app/controllers/course_management/curriculum_course_groups_controller.rb @@ -4,6 +4,7 @@ module CourseManagement class CurriculumCourseGroupsController < ApplicationController before_action :set_semester, only: %i[new create edit update destroy] before_action :set_curriculum_course_group, only: %i[edit update destroy] + before_action :authorized? def new @curriculum_course_groups = @semester.build_curriculum_course_groups @@ -41,6 +42,10 @@ def set_semester ) end + def authorized? + authorize([:course_management, @curriculum_course_group || CurriculumCourseGroup]) + end + def curriculum_course_params params.require(:curriculum_course_group).permit( :course_group_id, :ects, diff --git a/app/controllers/course_management/curriculum_courses_controller.rb b/app/controllers/course_management/curriculum_courses_controller.rb index 28e047575..b6f6cb9c4 100644 --- a/app/controllers/course_management/curriculum_courses_controller.rb +++ b/app/controllers/course_management/curriculum_courses_controller.rb @@ -4,6 +4,7 @@ module CourseManagement class CurriculumCoursesController < ApplicationController before_action :set_semester, only: %i[new create edit update destroy] before_action :set_curriculum_course, only: %i[edit update destroy] + before_action :authorized? def new @curriculum_course = @semester.curriculum_courses.new @@ -41,6 +42,10 @@ def set_semester ) end + def authorized? + authorize([:course_management, @curriculum_course || CurriculumCourse]) + end + def curriculum_course_params params.require(:curriculum_course).permit(:course_id, :ects) end diff --git a/app/controllers/course_management/curriculums_controller.rb b/app/controllers/course_management/curriculums_controller.rb index afdee3da3..fb7dd4a5e 100644 --- a/app/controllers/course_management/curriculums_controller.rb +++ b/app/controllers/course_management/curriculums_controller.rb @@ -5,6 +5,7 @@ class CurriculumsController < ApplicationController include SearchableModule before_action :set_curriculum, only: %i[show edit update destroy openable_courses] + before_action :authorized? def index curriculums = Curriculum.includes(:unit) @@ -57,10 +58,14 @@ def set_curriculum @curriculum = Curriculum.find(params[:id]) end + def authorized? + authorize([:course_management, @curriculum || Curriculum]) + end + def curriculum_params params.require(:curriculum).permit( :name, :unit_id, :status, :program_ids, - semesters_attributes: %i[id sequence year _destroy] + semesters_attributes: %i[id sequence year term _destroy] ) end end diff --git a/app/controllers/detsis/dashboard_controller.rb b/app/controllers/detsis/dashboard_controller.rb index b379ca022..868834e8f 100644 --- a/app/controllers/detsis/dashboard_controller.rb +++ b/app/controllers/detsis/dashboard_controller.rb @@ -2,6 +2,8 @@ module Detsis class DashboardController < ApplicationController - def index; end + def index + authorize %i[detsis dashboard] + end end end diff --git a/app/controllers/detsis/sdp_codes_controller.rb b/app/controllers/detsis/sdp_codes_controller.rb index e48041e31..2c4a9726c 100644 --- a/app/controllers/detsis/sdp_codes_controller.rb +++ b/app/controllers/detsis/sdp_codes_controller.rb @@ -6,6 +6,8 @@ class SdpCodesController < ApplicationController def index @sdp_codes = pagy_by_search(SdpCode.order(:main)) + + authorize [:detsis, @sdp_codes] end end end diff --git a/app/controllers/first_registration/prospective_employees_controller.rb b/app/controllers/first_registration/prospective_employees_controller.rb index e92646ccb..bd34b8a14 100644 --- a/app/controllers/first_registration/prospective_employees_controller.rb +++ b/app/controllers/first_registration/prospective_employees_controller.rb @@ -5,6 +5,7 @@ class ProspectiveEmployeesController < ApplicationController include SearchableModule before_action :set_prospective_employee, only: %i[edit update destroy] + before_action :authorized? def index prospective_employees = ProspectiveEmployee.not_archived.includes(:unit, :title) @@ -54,6 +55,10 @@ def set_prospective_employee @prospective_employee = ProspectiveEmployee.find(params[:id]) end + def authorized? + authorize([:first_registration, @prospective_employee || ProspectiveEmployee]) + end + # rubocop:disable Metrics/MethodLength def prospective_employee_params params.require(:prospective_employee).permit( diff --git a/app/controllers/first_registration/prospective_students_controller.rb b/app/controllers/first_registration/prospective_students_controller.rb index ba8f4d756..0b1299534 100644 --- a/app/controllers/first_registration/prospective_students_controller.rb +++ b/app/controllers/first_registration/prospective_students_controller.rb @@ -6,6 +6,7 @@ class ProspectiveStudentsController < ApplicationController before_action :set_prospective_student, only: %i[show edit update register] before_action :can_register?, only: :register + before_action :authorized? def index prospective_students = ProspectiveStudent.includes(:unit, :student_entrance_type) @@ -56,6 +57,10 @@ def set_prospective_student @prospective_student = ProspectiveStudent.find(params[:id]) end + def authorized? + authorize([:first_registration, @prospective_student || ProspectiveStudent]) + end + def can_register? alert = '.can_not_register' redirect_to(:prospective_students, alert: t(alert)) unless @prospective_student.can_temporarily_register? diff --git a/app/controllers/first_registration/registration_documents_controller.rb b/app/controllers/first_registration/registration_documents_controller.rb index 6fd23a074..91c967569 100644 --- a/app/controllers/first_registration/registration_documents_controller.rb +++ b/app/controllers/first_registration/registration_documents_controller.rb @@ -5,6 +5,7 @@ class RegistrationDocumentsController < ApplicationController include SearchableModule before_action :set_registration_document, only: %i[edit update destroy] + before_action :authorized? def index @registration_documents = pagy_by_search( @@ -51,6 +52,10 @@ def set_registration_document @registration_document = RegistrationDocument.find(params[:id]) end + def authorized? + authorize([:first_registration, @registration_document || RegistrationDocument]) + end + def registration_document_params params.require(:registration_document).permit(:unit_id, :academic_term_id, :document_type_id, :description) end diff --git a/app/controllers/instructiveness/assessments_controller.rb b/app/controllers/instructiveness/assessments_controller.rb new file mode 100644 index 000000000..6dd2542a0 --- /dev/null +++ b/app/controllers/instructiveness/assessments_controller.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +module Instructiveness + class AssessmentsController < ApplicationController + before_action :set_employee + before_action :set_course + before_action :set_assessment + before_action :authorized? + before_action :event_active?, except: :show + before_action :coordinator?, only: %i[save draft] + before_action :set_enrollments + before_action :editable?, only: %i[edit update] + + def show; end + + def edit + @grades = @assessment.grades_under_authority_of(@employee) + + @assessment.build_grades_for(@enrollments.where.not(id: @assessment.grades.map(&:course_enrollment_id))) + end + + def update + return redirect_with('success') if @assessment.update(assessment_params) + + @grades = @assessment.grades.select { |grade| @enrollments.ids.include?(grade.course_enrollment_id) } + render(:edit) + end + + def save + return redirect_with('not_draft_error') unless @assessment.draft? + return redirect_with('not_fully_graded_error') unless @assessment.fully_graded? + + @assessment.update(status: :saved) ? redirect_with('success') : redirect_with('error') + end + + def draft + return redirect_with('not_saved_error') unless @assessment.saved? + + @assessment.update(status: :draft) ? redirect_with('success') : redirect_with('error') + end + + private + + def redirect_with(message) + redirect_to given_course_assessment_path(@course, @assessment), flash: { info: t(".#{message}") } + end + + def set_employee + not_found if (@employee = current_user.current_employee).nil? + end + + def set_course + @course = @employee.given_courses.find(params[:given_course_id]) + end + + def set_assessment + assessment = @course.course_assessment_methods.find(params[:id]) + @assessment = AssessmentDecorator.new(assessment) + end + + def authorized? + authorize(@employee, policy_class: Instructiveness::AssessmentPolicy) + end + + def event_active? + redirect_with('.errors.not_proper_event_range') unless @assessment.gradable? + end + + def coordinator? + redirect_with('.errors.not_coordinator') unless @employee.coordinatorships.include?(@course) + end + + def set_enrollments + @enrollments = @course.enrollments_under_authority_of(@employee) + redirect_to given_course_path(@course), flash: { info: t('.errors.no_enrollments') } if @enrollments.empty? + end + + def editable? + redirect_with('.errors.saved') if @assessment.saved? + redirect_with('.errors.no_enrollments') if @enrollments.empty? + end + + def assessment_params + params.require(:course_assessment_method) + .permit(grades_attributes: %i[id course_enrollment_id lecturer_id point]) + .merge(status: :draft) + end + end +end diff --git a/app/controllers/instructiveness/given_courses_controller.rb b/app/controllers/instructiveness/given_courses_controller.rb new file mode 100644 index 000000000..ede05ab4b --- /dev/null +++ b/app/controllers/instructiveness/given_courses_controller.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module Instructiveness + class GivenCoursesController < ApplicationController + include SearchableModule + + before_action :set_employee + before_action :set_course, except: :index + before_action :set_groups, except: :index + before_action :authorized? + + def index + courses = @employee.given_courses.includes(:unit, :academic_term, curriculum_course: :course) + .order('units.name', 'courses.name') + .dynamic_search(search_params(AvailableCourse)) + + @pagy, @courses = pagy(courses) + end + + def show + @evaluation_types = + @course.evaluation_types.includes(:evaluation_type, course_assessment_methods: :assessment_method) + end + + def students; end + + private + + def set_employee + not_found if (@employee = current_user.current_employee).nil? + end + + def authorized? + authorize(@employee, policy_class: Instructiveness::GivenCoursePolicy) + end + + def set_groups + @groups = @course.groups_under_authority_of(@employee) + end + + def set_course + @course = @employee.given_courses.find(params[:id]) + end + end +end diff --git a/app/controllers/ldap_entities_controller.rb b/app/controllers/ldap_entities_controller.rb index 8788e34ec..5c93e1871 100644 --- a/app/controllers/ldap_entities_controller.rb +++ b/app/controllers/ldap_entities_controller.rb @@ -3,6 +3,7 @@ class LdapEntitiesController < ApplicationController include SearchableModule before_action :set_ldap_entity, only: %i[show start_sync] + before_action :authorized? def index entities = LdapEntity.includes(:user).order(created_at: :desc) @@ -24,6 +25,10 @@ def set_ldap_entity @entity = LdapEntity.find(params[:id]) end + def authorized? + authorize(@entity || LdapEntity) + end + def params_for_filter filters = search_params(LdapEntity) %i[created_at synchronized_at].each do |attribute| diff --git a/app/controllers/location/cities_controller.rb b/app/controllers/location/cities_controller.rb index e4c77f023..ef2992d70 100644 --- a/app/controllers/location/cities_controller.rb +++ b/app/controllers/location/cities_controller.rb @@ -6,6 +6,7 @@ class CitiesController < ApplicationController before_action :set_country before_action :set_city, only: %i[show edit update destroy] + before_action :authorized? def show @districts = pagy_by_search(@city.districts.order(:name)) @@ -44,6 +45,10 @@ def set_city @city = @country.cities.find(params[:id]) end + def authorized? + authorize([:location, @city || City]) + end + def city_params params.require(:city).permit(:name, :alpha_2_code, :latitude, :longitude) end diff --git a/app/controllers/location/countries_controller.rb b/app/controllers/location/countries_controller.rb index 186f258ed..edb11c9ca 100644 --- a/app/controllers/location/countries_controller.rb +++ b/app/controllers/location/countries_controller.rb @@ -5,6 +5,7 @@ class CountriesController < ApplicationController include SearchableModule before_action :set_country, only: %i[show edit update destroy] + before_action :authorized? def index @countries = pagy_by_search(Country.order(:name)) @@ -39,6 +40,10 @@ def set_country @country = Country.find(params[:id]) end + def authorized? + authorize([:location, @country || Country]) + end + def country_params params.require(:country).permit( :name, :alpha_2_code, :alpha_3_code, :numeric_code, :mernis_code, :yoksis_code, :continent, :currency_code, diff --git a/app/controllers/location/districts_controller.rb b/app/controllers/location/districts_controller.rb index 68721f090..27c0047fc 100644 --- a/app/controllers/location/districts_controller.rb +++ b/app/controllers/location/districts_controller.rb @@ -4,6 +4,7 @@ module Location class DistrictsController < ApplicationController before_action :set_city before_action :set_district, only: %i[edit update destroy] + before_action :authorized? def new @district = @city.districts.new @@ -46,6 +47,10 @@ def set_district @district = @city.districts.find(params[:id]) end + def authorized? + authorize([:location, @district || District]) + end + def district_params params.require(:district).permit(:name, :mernis_code, :active) end diff --git a/app/controllers/manager/dashboard_controller.rb b/app/controllers/manager/dashboard_controller.rb index 21210a6c4..4a0181e20 100644 --- a/app/controllers/manager/dashboard_controller.rb +++ b/app/controllers/manager/dashboard_controller.rb @@ -2,6 +2,8 @@ module Manager class DashboardController < ApplicationController - def stats; end + def stats + authorize(current_user, policy_class: Manager::DashboardPolicy) + end end end diff --git a/app/controllers/manager/stats/articles_controller.rb b/app/controllers/manager/stats/articles_controller.rb index d016d46af..7638af107 100644 --- a/app/controllers/manager/stats/articles_controller.rb +++ b/app/controllers/manager/stats/articles_controller.rb @@ -5,7 +5,9 @@ module Stats class ArticlesController < ApplicationController layout false - def index; end + def index + authorize(current_user, policy_class: Manager::Stats::ArticlePolicy) + end end end end diff --git a/app/controllers/manager/stats/employees_controller.rb b/app/controllers/manager/stats/employees_controller.rb index 704e9ae4c..af066dad1 100644 --- a/app/controllers/manager/stats/employees_controller.rb +++ b/app/controllers/manager/stats/employees_controller.rb @@ -4,6 +4,9 @@ module Manager module Stats class EmployeesController < ApplicationController layout false + before_action do + authorize(current_user, policy_class: Manager::Stats::EmployeePolicy) + end def index; end diff --git a/app/controllers/manager/stats/students_controller.rb b/app/controllers/manager/stats/students_controller.rb index 01b21859a..23138154e 100644 --- a/app/controllers/manager/stats/students_controller.rb +++ b/app/controllers/manager/stats/students_controller.rb @@ -4,11 +4,17 @@ module Manager module Stats class StudentsController < ApplicationController layout false + before_action do + authorize(current_user, policy_class: Manager::Stats::StudentPolicy) + end + rescue_from Support::RestClient::HTTPError, with: -> { + render status: :internal_server_error + } def index; end def cities - @series = Xokul::Ubs::Statistic::Student.by_cities( + @series = Xokul::UBS::Statistic::Student.by_cities( schema: { city: :name, total: :value @@ -17,7 +23,7 @@ def cities end def double_major_and_minor - @series = Xokul::Ubs::Statistic::Student.double_major_and_minor( + @series = Xokul::UBS::Statistic::Student.double_major_and_minor( schema: { category: :name, number_of_students: :y @@ -26,7 +32,7 @@ def double_major_and_minor end def genders - @series = Xokul::Ubs::Statistic::Student.by_genders( + @series = Xokul::UBS::Statistic::Student.by_genders( schema: { gender: :name, number_of_students: :y @@ -35,7 +41,7 @@ def genders end def genders_and_degrees - data = Xokul::Ubs::Statistic::Student.by_genders_and_degree + data = Xokul::UBS::Statistic::Student.by_genders_and_degree @degrees = data.map { |item| item[:degree] }.uniq @series = data.group_by { |item| item[:gender] }.map do |gender, values| { @@ -48,7 +54,7 @@ def genders_and_degrees end def non_graduates - data = Xokul::Ubs::Statistic::Student.non_graduates + data = Xokul::UBS::Statistic::Student.non_graduates @degrees = data.map { |item| item[:degree] }.uniq @series = data.group_by { |item| item[:status] }.map do |status, values| { diff --git a/app/controllers/meksis/buildings_controller.rb b/app/controllers/meksis/buildings_controller.rb index ea829eefc..20a28e6b2 100644 --- a/app/controllers/meksis/buildings_controller.rb +++ b/app/controllers/meksis/buildings_controller.rb @@ -5,6 +5,7 @@ class BuildingsController < ApplicationController include SearchableModule before_action :set_building, only: %i[edit show update] + before_action :authorized? def index @buildings = Building.includes(:place_type).order(:name).dynamic_search(search_params(Building)) @@ -31,6 +32,10 @@ def set_building @building = Building.find(params[:id]) end + def authorized? + authorize([:meksis, @building || Building]) + end + def secure_params params.require(:building).permit(:latitude, :longitude) end diff --git a/app/controllers/meksis/classrooms_controller.rb b/app/controllers/meksis/classrooms_controller.rb index f4ded8653..bdbef8eae 100644 --- a/app/controllers/meksis/classrooms_controller.rb +++ b/app/controllers/meksis/classrooms_controller.rb @@ -9,10 +9,13 @@ class ClassroomsController < ApplicationController def index classrooms = @building.classrooms.includes(:place_type).order(:name) @pagy, @classrooms = pagy(classrooms.dynamic_search(search_params(Classroom))) + + authorize([:meksis, @classrooms]) end def show @classroom = Classroom.find(params[:id]) + authorize([:meksis, @classroom]) end private diff --git a/app/controllers/meksis/dashboard_controller.rb b/app/controllers/meksis/dashboard_controller.rb index fb6ce4eda..5b7b56397 100644 --- a/app/controllers/meksis/dashboard_controller.rb +++ b/app/controllers/meksis/dashboard_controller.rb @@ -2,6 +2,8 @@ module Meksis class DashboardController < ApplicationController - def index; end + def index + authorize %i[meksis dashboard] + end end end diff --git a/app/controllers/meksis/place_types_controller.rb b/app/controllers/meksis/place_types_controller.rb index 176b2db02..844c5f8c8 100644 --- a/app/controllers/meksis/place_types_controller.rb +++ b/app/controllers/meksis/place_types_controller.rb @@ -6,11 +6,15 @@ class PlaceTypesController < ApplicationController def index @place_types = pagy_by_search(PlaceType.roots.order(:name)) + + authorize([:meksis, @place_types]) end def show @place_type = PlaceType.find(params[:id]) @children = pagy_by_search(@place_type.children.order(:name)) + + authorize([:meksis, @place_type]) end end end diff --git a/app/controllers/reference/accreditation_institutions_controller.rb b/app/controllers/reference/accreditation_institutions_controller.rb new file mode 100644 index 000000000..86a120448 --- /dev/null +++ b/app/controllers/reference/accreditation_institutions_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Reference + class AccreditationInstitutionsController < ApplicationController + include ReferenceResource + + private + + def secure_params + params.require(:accreditation_institution).permit(:name) + end + end +end diff --git a/app/controllers/reference/course_group_types_controller.rb b/app/controllers/reference/course_group_types_controller.rb new file mode 100644 index 000000000..c19f554af --- /dev/null +++ b/app/controllers/reference/course_group_types_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Reference + class CourseGroupTypesController < ApplicationController + include ReferenceResource + + private + + def secure_params + params.require(:course_group_type).permit(:name) + end + end +end diff --git a/app/controllers/reference/course_types_controller.rb b/app/controllers/reference/course_types_controller.rb new file mode 100644 index 000000000..47a9b9c58 --- /dev/null +++ b/app/controllers/reference/course_types_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Reference + class CourseTypesController < ApplicationController + include ReferenceResource + + private + + def secure_params + params.require(:course_type).permit(:name, :code, :min_credit) + end + end +end diff --git a/app/controllers/reference/dashboard_controller.rb b/app/controllers/reference/dashboard_controller.rb index d1b4106bd..ba28084ee 100644 --- a/app/controllers/reference/dashboard_controller.rb +++ b/app/controllers/reference/dashboard_controller.rb @@ -2,6 +2,8 @@ module Reference class DashboardController < ApplicationController - def index; end + def index + authorize %i[reference dashboard] + end end end diff --git a/app/controllers/reference/evaluation_types_controller.rb b/app/controllers/reference/evaluation_types_controller.rb index 6e3bdccff..1a8322d3b 100644 --- a/app/controllers/reference/evaluation_types_controller.rb +++ b/app/controllers/reference/evaluation_types_controller.rb @@ -7,7 +7,7 @@ class EvaluationTypesController < ApplicationController private def secure_params - params.require(:evaluation_type).permit(:name) + params.require(:evaluation_type).permit(:identifier, :name) end end end diff --git a/app/controllers/reference/scholarship_types_controller.rb b/app/controllers/reference/scholarship_types_controller.rb new file mode 100644 index 000000000..874f53d2e --- /dev/null +++ b/app/controllers/reference/scholarship_types_controller.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Reference + class ScholarshipTypesController < ApplicationController + include ReferenceResource + + private + + def secure_params + params.require(:scholarship_type).permit(:name, :active) + end + end +end diff --git a/app/controllers/students_controller.rb b/app/controllers/students_controller.rb new file mode 100644 index 000000000..93a2277a2 --- /dev/null +++ b/app/controllers/students_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class StudentsController < ApplicationController + before_action :set_student + before_action :authorized? + + def edit; end + + def update + @student.update(student_params) ? redirect_to(user_path(@student.user), notice: t('.success')) : render(:edit) + end + + private + + def set_student + @student = Student.find(params[:id]) + end + + def authorized? + authorize(@student || Student) + end + + def student_params + params.require(:student).permit( + :student_number, :unit_id, :year, :semester, :scholarship_type_id, :status, :exceeded_education_period, + :stage_id, :entrance_type_id, :registration_date, :registration_term_id, :other_studentship, :preparatory_class + ) + end +end diff --git a/app/controllers/studentship/course_enrollments_controller.rb b/app/controllers/studentship/course_enrollments_controller.rb index 90190996f..311463504 100644 --- a/app/controllers/studentship/course_enrollments_controller.rb +++ b/app/controllers/studentship/course_enrollments_controller.rb @@ -3,9 +3,10 @@ module Studentship class CourseEnrollmentsController < ApplicationController before_action :set_student + before_action :authorized? before_action :set_service, except: :index before_action :set_course_enrollment, only: :destroy - before_action :check_registrability, except: :index + before_action :event_active?, except: :index before_action :check_registration_status, except: %i[index list] def index @@ -58,7 +59,11 @@ def set_course_enrollment @course_enrollment = @student.current_registration.course_enrollments.find(params[:id]) end - def check_registrability + def authorized? + authorize(@student, policy_class: Studentship::CourseEnrollmentPolicy) + end + + def event_active? return if @student.registrable_for_online_course? redirect_to(student_course_enrollments_path(@student), alert: t('.errors.not_proper_register_event_range')) diff --git a/app/controllers/studentship/tuition_debts_controller.rb b/app/controllers/studentship/tuition_debts_controller.rb new file mode 100644 index 000000000..ede78ee71 --- /dev/null +++ b/app/controllers/studentship/tuition_debts_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +module Studentship + class TuitionDebtsController < ApplicationController + before_action :set_student + before_action :authorized? + + def index + @debts = @student.tuition_debts.includes(:academic_term).order(:due_date) + end + + private + + def set_student + @student = current_user.students.find(params[:student_id]) + end + + def authorized? + authorize(@student, policy_class: Studentship::TuitionDebtPolicy) + end + end +end diff --git a/app/controllers/tuition_management/tuition_debts_controller.rb b/app/controllers/tuition_management/tuition_debts_controller.rb new file mode 100644 index 000000000..6d0e972eb --- /dev/null +++ b/app/controllers/tuition_management/tuition_debts_controller.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module TuitionManagement + class TuitionDebtsController < ApplicationController + include SearchableModule + + before_action :set_tuition_debt, only: %i[edit update destroy] + before_action :authorized? + + def index + tuition_debts = + TuitionDebt.includes(:academic_term, student: %i[unit user]).joins(:student).where( + params[:unit_id].present? ? { students: { unit_id: params[:unit_id] } } : {} + ) + + @pagy, @tuition_debts = pagy(tuition_debts.dynamic_search(search_params(TuitionDebt))) + end + + def new + @tuition_debt = TuitionDebt.new + end + + def create + @tuition_debt = TuitionDebt.new(tuition_debt_params) + @tuition_debt.save ? redirect_to(:tuition_debts, notice: t('.success')) : render(:new) + end + + def edit; end + + def update + @tuition_debt.update(tuition_debt_params) ? redirect_to(:tuition_debts, notice: t('.success')) : render(:edit) + end + + def create_with_service + Debt::TuitionDebtJob.perform_later(params.dig(:tuition_debt, :unit_ids), + params.dig(:tuition_debt, :academic_term_id), + params.dig(:tuition_debt, :due_date)) + redirect_to(tuition_debts_path, notice: t('.will_update')) + end + + def destroy + return redirect_to(:tuition_debts, notice: t('.success')) if @tuition_debt.destroy + + redirect_to(:tuition_debts, alert: t('.warning')) + end + + private + + def set_tuition_debt + @tuition_debt = TuitionDebt.find(params[:id]) + end + + def authorized? + authorize([:tuition_management, @tuition_debt || TuitionDebt]) + end + + def tuition_debt_params + params.require(:tuition_debt).permit( + :student_id, :academic_term_id, :amount, :description, :type, :paid, :due_date + ) + end + end +end diff --git a/app/controllers/tuition_management/tuitions_controller.rb b/app/controllers/tuition_management/tuitions_controller.rb new file mode 100644 index 000000000..360aeda90 --- /dev/null +++ b/app/controllers/tuition_management/tuitions_controller.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module TuitionManagement + class TuitionsController < ApplicationController + include SearchableModule + + before_action :set_tuition, only: %i[edit update destroy] + before_action :authorized? + + def index + tuitions = Tuition.includes(:units, :unit_tuitions, :academic_term).where( + params[:unit_id].present? ? { units: { id: params[:unit_id] } } : {} + ) + + @pagy, @tuitions = pagy(tuitions.dynamic_search(search_params(Tuition))) + end + + def new + @tuition = Tuition.new + end + + def create + @tuition = Tuition.new(tuition_params) + @tuition.save ? redirect_to(:tuitions, notice: t('.success')) : render(:new) + end + + def edit; end + + def update + @tuition.update(tuition_params) ? redirect_to(:tuitions, notice: t('.success')) : render(:edit) + end + + def destroy + return redirect_to(:tuitions, notice: t('.success')) if @tuition.destroy + + redirect_to(:tuitions, alert: t('.warning')) + end + + def units + @units = params[:term].present? ? Unit.active.faculties.search(params[:term]) : Unit.active.faculties + respond_to :json + end + + private + + def set_tuition + @tuition = Tuition.find(params[:id]) + end + + def authorized? + authorize([:tuition_management, @tuition || Tuition]) + end + + def tuition_params + params.require(:tuition).permit(:academic_term_id, :fee, :foreign_student_fee, unit_ids: []) + end + end +end diff --git a/app/controllers/units_controller.rb b/app/controllers/units_controller.rb index 1ed27dfb6..18322bea9 100644 --- a/app/controllers/units_controller.rb +++ b/app/controllers/units_controller.rb @@ -3,7 +3,8 @@ class UnitsController < ApplicationController include SearchableModule - before_action :set_unit, only: %i[edit update destroy show courses programs curriculums employees] + before_action :set_unit, only: %i[edit update destroy show courses programs curriculums employees students] + before_action :authorized? def index units = Unit.includes( @@ -57,12 +58,20 @@ def employees .where(units: { id: @unit.subtree.active.ids }) end + def students + @students = @unit.students.active + end + private def set_unit @unit = Unit.find(params[:id]) end + def authorized? + authorize(@unit || Unit) + end + def unit_params params.require(:unit).permit( :name, :yoksis_id, :detsis_id, :foet_code, :founded_at, :duration, :district_id, :parent_id, :unit_status_id, diff --git a/app/controllers/user_management/disability_controller.rb b/app/controllers/user_management/disability_controller.rb new file mode 100644 index 000000000..cacf284ee --- /dev/null +++ b/app/controllers/user_management/disability_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module UserManagement + class DisabilityController < ApplicationController + before_action :set_user + + def edit; end + + def update + return redirect_to user_path(@user), notice: t('.success') if @user.update(user_params) + + render :edit + end + + private + + def set_user + @user = User.friendly.find(params[:id]) + end + + def authorized? + authorize(@user || User, policy_class: UserManagement::DisabilityPolicy) + end + + def user_params + params.require(:user).permit(:disability_rate) + end + end +end diff --git a/app/controllers/account/duties_controller.rb b/app/controllers/user_management/duties_controller.rb similarity index 88% rename from app/controllers/account/duties_controller.rb rename to app/controllers/user_management/duties_controller.rb index 9bab80c93..04c253985 100644 --- a/app/controllers/account/duties_controller.rb +++ b/app/controllers/user_management/duties_controller.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -module Account +module UserManagement class DutiesController < ApplicationController before_action :set_user before_action :set_duty, only: %i[edit update destroy] + before_action :authorized? def new @duty = @user.duties.new @@ -38,6 +39,10 @@ def set_duty @duty = @user.duties.find(params[:id]) end + def authorized? + authorize([:user_management, @duty || Duty]) + end + def duty_params params.require(:duty).permit(:temporary, :start_date, :end_date, :unit_id, :employee_id, :article) end diff --git a/app/controllers/account/employees_controller.rb b/app/controllers/user_management/employees_controller.rb similarity index 87% rename from app/controllers/account/employees_controller.rb rename to app/controllers/user_management/employees_controller.rb index f25f43268..6ccb6c025 100644 --- a/app/controllers/account/employees_controller.rb +++ b/app/controllers/user_management/employees_controller.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -module Account +module UserManagement class EmployeesController < ApplicationController before_action :set_user before_action :set_employee, only: %i[edit update destroy] + before_action :authorized? def new @employee = @user.employees.new @@ -32,12 +33,14 @@ def destroy def set_user @user = User.friendly.find(params[:user_id]) - not_found unless @user end def set_employee @employee = @user.employees.find(params[:id]) - not_found unless @employee + end + + def authorized? + authorize([:user_management, @employee || Employee]) end def employee_params diff --git a/app/controllers/account/positions_controller.rb b/app/controllers/user_management/positions_controller.rb similarity index 89% rename from app/controllers/account/positions_controller.rb rename to app/controllers/user_management/positions_controller.rb index bd0d1bcc2..ee888a817 100644 --- a/app/controllers/account/positions_controller.rb +++ b/app/controllers/user_management/positions_controller.rb @@ -1,10 +1,11 @@ # frozen_string_literal: true -module Account +module UserManagement class PositionsController < ApplicationController before_action :set_user before_action :set_duty, only: %i[create update] before_action :set_position, only: %i[edit update destroy] + before_action :authorized? def new @position = Position.new @@ -33,17 +34,18 @@ def destroy def set_user @user = User.friendly.find(params[:user_id]) - not_found unless @user end def set_duty @duty = @user.duties.find(position_params[:duty_id]) - not_found unless @duty end def set_position @position = @user.positions.find(params[:id]) - not_found unless @position + end + + def authorized? + authorize([:user_management, @position || Position]) end def position_params diff --git a/app/controllers/user_management/users_controller.rb b/app/controllers/user_management/users_controller.rb new file mode 100644 index 000000000..cda4eec67 --- /dev/null +++ b/app/controllers/user_management/users_controller.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +module UserManagement + class UsersController < ApplicationController + include SearchableModule + include UpdateableFromMernis + + before_action :set_user, except: :index + before_action :set_address_elapsed_time, only: %i[save_address_from_mernis] + before_action :set_identity_elapsed_time, only: %i[save_identity_from_mernis] + before_action :nullify_slug, only: :update + before_action :authorized? + + def index + @users = pagy_by_search(User.all) + end + + def show + @identities = @user.identities + @addresses = @user.addresses.includes(district: :city) + @employees = @user.employees.includes(:title).order(active: :desc) + @duties = @user.duties.includes(:unit) + @students = @user.students.includes(:unit, :scholarship_type, :stage) + @positions = @user.positions.includes(:administrative_function, :duty) + end + + def edit; end + + def update + if user_params[:password].blank? || user_params[:password_confirmation].blank? + @user.update_without_password(user_params) ? redirect_with('.success') : render(:edit) + else + @user.update(user_params) ? redirect_with('.success') : render(:edit) + end + end + + def destroy + @user.destroy ? redirect_with('.success') : redirect_with('.warning') + end + + def save_address_from_mernis + Kps::AddressSaveJob.perform_later(@user) + redirect_to(@user, notice: t('.will_update')) + end + + def save_identity_from_mernis + Kps::IdentitySaveJob.perform_later(@user) + redirect_to(@user, notice: t('.will_update')) + end + + private + + def set_user + @user = User.friendly.find(params[:id]) + end + + def authorized? + authorize([:user_management, @user || User]) + end + + def set_address_elapsed_time + formal_address = @user.addresses.formal + return if formal_address.blank? + + elapsed_time(formal_address.first) + end + + def set_identity_elapsed_time + formal_identity = @user.identities.user_identity + return if formal_identity.blank? + + elapsed_time(formal_identity) + end + + def nullify_slug + # generate a new slug when the e-mail changes + @user.slug = nil if user_params[:email].present? + end + + def user_params + params.require(:user).permit(:id_number, :email, :password, :password_confirmation, :preferred_language) + end + + def redirect_with(message) + redirect_to(users_path, notice: t(".#{message}")) + end + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb deleted file mode 100644 index c907a7d90..000000000 --- a/app/controllers/users_controller.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -class UsersController < ApplicationController - include SearchableModule - include UpdateableFromMernis - - before_action :set_user, except: :index - before_action :set_address_elapsed_time, only: %i[save_address_from_mernis] - before_action :set_identity_elapsed_time, only: %i[save_identity_from_mernis] - before_action :nullify_slug, only: :update - - def index - @users = pagy_by_search(User.all) - end - - def show - @identities = @user.identities - @addresses = @user.addresses.includes(district: :city) - @employees = @user.employees.includes(:title).order(active: :desc) - @duties = @user.duties.includes(:unit) - @positions = @user.positions.includes(:administrative_function, :duty) - end - - def edit; end - - def update - if user_params[:password].blank? || user_params[:password_confirmation].blank? - @user.update_without_password(user_params) ? redirect_with('.success') : render(:edit) - else - @user.update(user_params) ? redirect_with('.success') : render(:edit) - end - end - - def destroy - @user.destroy ? redirect_with('.success') : redirect_with('.warning') - end - - def save_address_from_mernis - Kps::AddressSaveJob.perform_later(@user) - redirect_to(@user, notice: t('.will_update')) - end - - def save_identity_from_mernis - Kps::IdentitySaveJob.perform_later(@user) - redirect_to(@user, notice: t('.will_update')) - end - - private - - def set_user - @user = User.friendly.find(params[:id]) - end - - def set_address_elapsed_time - formal_address = @user.addresses.formal - return if formal_address.blank? - - elapsed_time(formal_address.first) - end - - def set_identity_elapsed_time - formal_identity = @user.identities.user_identity - return if formal_identity.blank? - - elapsed_time(formal_identity) - end - - def nullify_slug - # generate a new slug when the e-mail changes - @user.slug = nil if user_params[:email].present? - end - - def user_params - params.require(:user).permit(:id_number, :email, :password, :password_confirmation, :preferred_language) - end - - def redirect_with(message) - redirect_to(users_path, notice: t(".#{message}")) - end -end diff --git a/app/controllers/yoksis/dashboard_controller.rb b/app/controllers/yoksis/dashboard_controller.rb index a0999c397..e4c4aa5f6 100644 --- a/app/controllers/yoksis/dashboard_controller.rb +++ b/app/controllers/yoksis/dashboard_controller.rb @@ -2,6 +2,8 @@ module Yoksis class DashboardController < ApplicationController - def index; end + def index + authorize %i[yoksis dashboard] + end end end diff --git a/app/decorators/assessment_decorator.rb b/app/decorators/assessment_decorator.rb new file mode 100644 index 000000000..bcf528bc5 --- /dev/null +++ b/app/decorators/assessment_decorator.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class AssessmentDecorator < SimpleDelegator + def editable? + !saved? && saved_enrollments.any? + end + + def saveable? + draft? && fully_graded? + end + + def gradable? + results_announcement_event.try(:active_now?) + end + + def gradable_date_range + results_announcement_event.date_range + end + + private + + def calendar + available_course.calendars.last + end + + def results_announcement_event + @results_announcement_event ||= + CalendarEventDecorator.new(calendar&.event("#{identifier}_results_announcement")) + end +end diff --git a/app/decorators/calendar_event_decorator.rb b/app/decorators/calendar_event_decorator.rb new file mode 100644 index 000000000..d79e21c1d --- /dev/null +++ b/app/decorators/calendar_event_decorator.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class CalendarEventDecorator < SimpleDelegator + def date_range + return I18n.t('calendar_management.calendar_events.undefined_date_range') unless presence + + "#{start_time&.strftime('%F %R')} - #{end_time&.strftime('%F %R')}" + end +end diff --git a/app/decorators/student_decorator.rb b/app/decorators/student_decorator.rb index 7e32d2670..24f835799 100644 --- a/app/decorators/student_decorator.rb +++ b/app/decorators/student_decorator.rb @@ -2,15 +2,11 @@ class StudentDecorator < SimpleDelegator def registrable_for_online_course? - event_online_course_registrations.try(:active_now?) + online_course_registrations_event.try(:active_now?) end - def registation_date_range - translate( - 'index.registration_date_range', - start_time: event_online_course_registrations&.start_time&.strftime('%F %R'), - end_time: event_online_course_registrations&.end_time&.strftime('%F %R') - ) + def registrable_for_online_course_date_range + online_course_registrations_event.date_range end private @@ -19,11 +15,8 @@ def calendar calendars.last end - def event_online_course_registrations - calendar&.event('online_course_registrations') - end - - def translate(key, params = {}) - I18n.t("studentship.course_enrollments.#{key}", **params) + def online_course_registrations_event + @online_course_registrations_event ||= + CalendarEventDecorator.new(calendar&.event('online_course_registrations')) end end diff --git a/app/jobs/debt/tuition_debt_job.rb b/app/jobs/debt/tuition_debt_job.rb new file mode 100644 index 000000000..d5c7e04e5 --- /dev/null +++ b/app/jobs/debt/tuition_debt_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Debt + class TuitionDebtJob < ApplicationJob + queue_as :high + + def perform(unit_ids, term_id, due_date) + units = Unit.where(id: unit_ids).select { |u| u.students.active.exists? } + + Debt::Tuition::Dispatch.perform(units, term_id, due_date) + end + end +end diff --git a/app/jobs/osym/import_prospective_students_job.rb b/app/jobs/osym/import_prospective_students_job.rb index 15c153cbf..99b462a7a 100644 --- a/app/jobs/osym/import_prospective_students_job.rb +++ b/app/jobs/osym/import_prospective_students_job.rb @@ -7,6 +7,7 @@ class ImportProspectiveStudentsJob < ApplicationJob # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/BlockLength + # rubocop:disable Metrics/CyclomaticComplexity def perform(file_path) content = Support::Sensitive.readlines(file_path) @@ -82,6 +83,7 @@ def perform(file_path) # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/BlockLength + # rubocop:enable Metrics/CyclomaticComplexity private diff --git a/app/jobs/rake_job.rb b/app/jobs/rake_job.rb new file mode 100644 index 000000000..47b2c0c0b --- /dev/null +++ b/app/jobs/rake_job.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class RakeJob < ApplicationJob + Nokul::Application.load_tasks if Rake::Task.tasks.empty? + + def perform(args = {}) + name = args.fetch('name') + + Rake::Task[name].invoke(*args['params']) + Rake::Task[name].reenable + Rake::Task[name].prerequisite_tasks.each(&:reenable) + end +end diff --git a/app/lib/patron/role_builder.rb b/app/lib/patron/role_builder.rb index a1a93c3fa..f876156ec 100644 --- a/app/lib/patron/role_builder.rb +++ b/app/lib/patron/role_builder.rb @@ -31,9 +31,7 @@ def initialize(identifier:, name:, permissions:) private def check! - raise ArgumentError, 'Permissions must not be empty' if permissions.blank? - - permissions.keys.each do |permission| + permissions.each_key do |permission| raise ArgumentError, "Permission not found: #{permission}" unless PermissionBuilder.all.key?(permission) end end diff --git a/app/models/academic_credential.rb b/app/models/academic_credential.rb index f9beda6be..79d5d6a19 100644 --- a/app/models/academic_credential.rb +++ b/app/models/academic_credential.rb @@ -2,8 +2,8 @@ class AcademicCredential < ApplicationRecord # enums - enum activity: { deleted: 0, active: 1 } - enum location: { domestic: 0, abroad: 1 } + enum activity: { passive: 0, active: 1 } + enum location: { domestic: 1, abroad: 2 } enum status: { full_time: 0, part_time: 1 } # relations diff --git a/app/models/academic_term.rb b/app/models/academic_term.rb index 83250b240..8ec3f6758 100644 --- a/app/models/academic_term.rb +++ b/app/models/academic_term.rb @@ -11,6 +11,8 @@ class AcademicTerm < ApplicationRecord has_many :prospective_students, dependent: :nullify has_many :registration_documents, dependent: :nullify has_many :semester_registrations, dependent: :nullify + has_many :tuitions, dependent: :nullify + has_many :tuition_debts, dependent: :nullify # validations validates :active, inclusion: { in: [true, false] } diff --git a/app/models/accreditation_institution.rb b/app/models/accreditation_institution.rb new file mode 100644 index 000000000..c473d8319 --- /dev/null +++ b/app/models/accreditation_institution.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class AccreditationInstitution < ApplicationRecord + # search + include ReferenceSearch + + # relations + has_many :accreditation_standards, dependent: :destroy + + # validations + validates :name, presence: true, uniqueness: true, length: { maximum: 255 } +end diff --git a/app/models/accreditation_standard.rb b/app/models/accreditation_standard.rb new file mode 100644 index 000000000..b255fdfe7 --- /dev/null +++ b/app/models/accreditation_standard.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class AccreditationStandard < ApplicationRecord + # search + include DynamicSearch + + # dynamic_search + search_keys :accreditation_institution_id, :version, :status + + # enums + enum status: { passive: 0, active: 1 } + + # relations + belongs_to :accreditation_institution + has_many :learning_outcomes, dependent: :destroy + has_many :macro_learning_outcomes, -> { where(parent_id: nil) }, + class_name: 'LearningOutcome', inverse_of: :accreditation_standard + has_many :unit_accreditation_standards, dependent: :destroy + has_many :units, through: :unit_accreditation_standards + + # validations + validates :status, inclusion: { in: statuses.keys } + validates :version, presence: true, uniqueness: { scope: :accreditation_institution_id }, length: { maximum: 50 }, + format: { with: /\A(\d+)(\.\d+)?(\.\d+)?\z/, message: I18n.t('errors.invalid_version') } + validates_associated :unit_accreditation_standards + + # delegates + delegate :name, to: :accreditation_institution +end diff --git a/app/models/article.rb b/app/models/article.rb index 21801fd4b..9dccb86a1 100644 --- a/app/models/article.rb +++ b/app/models/article.rb @@ -7,7 +7,7 @@ class Article < ApplicationRecord enum scope: { national: 0, international: 1 } enum review: { reviewed: 0, unreviewed: 1 } enum access_type: { printed: 1, electronic: 2, printed_and_electronic: 3 } - enum status: { deleted: 0, active: 1 } + enum status: { passive: 0, active: 1 } enum index: { ssci: 5, diff --git a/app/models/available_course.rb b/app/models/available_course.rb index cc6858e43..65d1574be 100644 --- a/app/models/available_course.rb +++ b/app/models/available_course.rb @@ -24,9 +24,13 @@ class AvailableCourse < ApplicationRecord belongs_to :curriculum belongs_to :unit has_many :evaluation_types, class_name: 'CourseEvaluationType', dependent: :destroy + has_many :calendars, -> { Calendar.active }, through: :unit + has_many :course_assessment_methods, through: :evaluation_types has_many :groups, class_name: 'AvailableCourseGroup', dependent: :destroy has_many :lecturers, through: :groups has_many :course_enrollments, dependent: :destroy + has_many :saved_enrollments, -> { saved }, class_name: 'CourseEnrollment', + inverse_of: :available_course has_one :course, through: :curriculum_course accepts_nested_attributes_for :groups, reject_if: :all_blank, allow_destroy: true @@ -56,15 +60,35 @@ class AvailableCourse < ApplicationRecord # custom methods def quota_full? - groups.sum(:quota) == course_enrollments.saved.count + groups.sum(:quota) == number_of_enrolled_students end def enrollable_groups groups.order(:name).reject(&:quota_full?) end + def number_of_enrolled_students + saved_enrollments.count + end + + def groups_under_authority_of(employee) + coordinator == employee ? groups : groups.joins(:lecturers).where('lecturer_id = ?', employee.id) + end + + def enrollments_under_authority_of(employee) + saved_enrollments.where(available_course_group: groups_under_authority_of(employee)) + end + + def manageable? + add_drop_available_courses_event.try(:active_now?) + end + private + def add_drop_available_courses_event + @add_drop_available_courses_event ||= calendars.active.last&.event('add_drop_available_courses') + end + def assign_academic_term self.academic_term = AcademicTerm.active.last end diff --git a/app/models/available_course_group.rb b/app/models/available_course_group.rb index 7a3b9e091..e7a8544d5 100644 --- a/app/models/available_course_group.rb +++ b/app/models/available_course_group.rb @@ -1,11 +1,16 @@ # frozen_string_literal: true class AvailableCourseGroup < ApplicationRecord + # callbacks + before_destroy :must_be_another_group + # relations belongs_to :available_course, counter_cache: :groups_count has_many :course_enrollments, dependent: :destroy has_many :lecturers, class_name: 'AvailableCourseLecturer', foreign_key: :group_id, inverse_of: :group, dependent: :destroy + has_many :saved_enrollments, -> { saved }, class_name: 'CourseEnrollment', + inverse_of: :available_course # nested models accepts_nested_attributes_for :lecturers, reject_if: :all_blank, allow_destroy: true @@ -18,6 +23,16 @@ class AvailableCourseGroup < ApplicationRecord # custom methods def quota_full? - quota == course_enrollments.saved.count + quota == number_of_enrolled_students + end + + def number_of_enrolled_students + saved_enrollments.count + end + + private + + def must_be_another_group + throw(:abort) if available_course.groups.where.not(id: id).empty? && !destroyed_by_association end end diff --git a/app/models/book.rb b/app/models/book.rb index dba8d5e2d..8f46517a7 100644 --- a/app/models/book.rb +++ b/app/models/book.rb @@ -4,7 +4,7 @@ class Book < ApplicationRecord self.inheritance_column = nil # enums - enum activity: { deleted: 0, active: 1 } + enum activity: { passive: 0, active: 1 } enum contribution_rate: { all_of: 0, chapters: 1 } enum scope: { national: 0, international: 1 } enum type: { diff --git a/app/models/certification.rb b/app/models/certification.rb index 6a68a4711..4656549da 100644 --- a/app/models/certification.rb +++ b/app/models/certification.rb @@ -5,17 +5,20 @@ class Certification < ApplicationRecord # enums enum type: { - certification: 1, - course: 2, - research: 3, - study: 4, - report: 5, - workshop: 6, - interview: 7, - essay: 8, - evaluation: 9, - conversation: 10, - translation: 11 + certification: 1, + course: 2, + research: 3, + study: 4, + report: 5, + workshop: 6, + interview: 7, + essay: 8, + evaluation: 9, + conversation: 10, + translation: 11, + seminar: 12, + speeches: 13, + organizing_congress: 14 } enum scope: { national: 0, international: 1 } diff --git a/app/models/course_assessment_method.rb b/app/models/course_assessment_method.rb index 5ff7cfb8a..e091b7d03 100644 --- a/app/models/course_assessment_method.rb +++ b/app/models/course_assessment_method.rb @@ -1,9 +1,16 @@ # frozen_string_literal: true class CourseAssessmentMethod < ApplicationRecord + # enums + enum status: { no_grade_entered: 0, draft: 1, saved: 2 } + # relations belongs_to :assessment_method belongs_to :course_evaluation_type + has_many :grades, dependent: :destroy + has_many :saved_enrollments, through: :course_evaluation_type + has_one :available_course, through: :course_evaluation_type + accepts_nested_attributes_for :grades # validations validates :percentage, numericality: { @@ -14,5 +21,21 @@ class CourseAssessmentMethod < ApplicationRecord validates :assessment_method, uniqueness: { scope: :course_evaluation_type } # delegates + delegate :identifier, to: :course_evaluation_type delegate :name, to: :assessment_method + + # custom methods + def grades_under_authority_of(employee) + grades.where(course_enrollment: available_course.enrollments_under_authority_of(employee)) + end + + def build_grades_for(enrollments) + grades.build(enrollments.collect { |enrollment| { course_enrollment_id: enrollment.id } }) + end + + def fully_graded? + return false if saved_enrollments.empty? + + grades.where.not(point: nil).where(course_enrollment_id: saved_enrollments.ids).count == saved_enrollments.ids.count + end end diff --git a/app/models/course_enrollment.rb b/app/models/course_enrollment.rb index 70d1a131f..2bcb2cc8e 100644 --- a/app/models/course_enrollment.rb +++ b/app/models/course_enrollment.rb @@ -8,10 +8,17 @@ class CourseEnrollment < ApplicationRecord belongs_to :available_course belongs_to :available_course_group belongs_to :semester_registration + has_many :grades, dependent: :destroy # validations validates :available_course, uniqueness: { scope: :semester_registration_id } # delegates delegate :ects, to: :available_course + delegate :student, :academic_term, to: :semester_registration + + # custom methods + def grade_of(assessment) + grades.find_by(course_assessment_method_id: assessment.id) + end end diff --git a/app/models/course_evaluation_type.rb b/app/models/course_evaluation_type.rb index 3a0690424..a42864943 100644 --- a/app/models/course_evaluation_type.rb +++ b/app/models/course_evaluation_type.rb @@ -6,6 +6,7 @@ class CourseEvaluationType < ApplicationRecord belongs_to :evaluation_type has_many :course_assessment_methods, dependent: :destroy has_many :assessment_methods, through: :course_assessment_methods + has_many :saved_enrollments, through: :available_course accepts_nested_attributes_for :course_assessment_methods, allow_destroy: true, reject_if: :all_blank # validations @@ -19,5 +20,5 @@ class CourseEvaluationType < ApplicationRecord validates_with CourseEvaluationTypeValidator # delegations - delegate :name, to: :evaluation_type + delegate :identifier, :name, to: :evaluation_type end diff --git a/app/models/curriculum.rb b/app/models/curriculum.rb index d9ee13508..2d1ac559d 100644 --- a/app/models/curriculum.rb +++ b/app/models/curriculum.rb @@ -40,28 +40,41 @@ class Curriculum < ApplicationRecord # validations validates :name, presence: true, uniqueness: { scope: :unit_id }, length: { maximum: 255 } validates :programs, presence: true - validates :semesters_count, numericality: { greater_than_or_equal_to: 0 } validates :status, inclusion: { in: statuses.keys } + validate :check_semesters_count # custom methods def build_semesters - return false unless valid? + return if semesters_count >= number_of_semesters - divisor = (semester_type.to_sym == :periodic ? 2 : 1) - number_of_semesters = duration * divisor - (1..number_of_semesters.to_i).each do |sequence| + (1..number_of_semesters).each do |sequence| semesters.build(sequence: sequence, - year: (sequence.to_f / divisor).round, + year: (sequence.to_f / number_of_semesters_for_one_year).round, term: %i[spring fall][sequence % 2]) end + semesters + end + + def number_of_semesters + @number_of_semesters ||= duration.to_i * number_of_semesters_for_one_year end private # delegates - delegate :semester_type, :duration, to: :program + delegate :semester_type, :duration, to: :program, allow_nil: true def program @program ||= programs.last end + + def number_of_semesters_for_one_year + semester_type&.to_sym == :periodic ? 2 : 1 + end + + def check_semesters_count + return if semesters.size == number_of_semesters + + errors.add(:semesters_count, :number_of_semesters_must_be_equal, count: number_of_semesters) + end end diff --git a/app/models/education_information.rb b/app/models/education_information.rb index e9ab5316b..16a020343 100644 --- a/app/models/education_information.rb +++ b/app/models/education_information.rb @@ -2,8 +2,8 @@ class EducationInformation < ApplicationRecord # enums - enum activity: { deleted: 0, active: 1 } - enum location: { domestic: 0, abroad: 1 } + enum activity: { passive: 0, active: 1 } + enum location: { domestic: 1, abroad: 2 } # relations belongs_to :user diff --git a/app/models/employee.rb b/app/models/employee.rb index 308c79ec5..64c514e11 100644 --- a/app/models/employee.rb +++ b/app/models/employee.rb @@ -13,6 +13,8 @@ class Employee < ApplicationRecord has_many :positions, through: :duties has_many :administrative_functions, through: :duties has_many :available_course_lecturers, foreign_key: :lecturer_id, inverse_of: :lecturer, dependent: :destroy + has_many :coordinatorships, class_name: 'AvailableCourse', foreign_key: :coordinator_id, + inverse_of: :coordinator, dependent: :destroy # validations validates :active, inclusion: { in: [true, false] } @@ -35,4 +37,9 @@ class Employee < ApplicationRecord def academic? title.branch.eql?('ÖE') end + + def given_courses + AvailableCourse.joins(groups: :lecturers) + .where('coordinator_id = ? OR lecturer_id = ?', id, id) + end end diff --git a/app/models/evaluation_type.rb b/app/models/evaluation_type.rb index 664994e91..f7101f98c 100644 --- a/app/models/evaluation_type.rb +++ b/app/models/evaluation_type.rb @@ -13,6 +13,7 @@ class EvaluationType < ApplicationRecord # validations validates :name, presence: true, uniqueness: true, length: { maximum: 255 } + validates :identifier, presence: true, uniqueness: true, length: { maximum: 255 } private diff --git a/app/models/grade.rb b/app/models/grade.rb new file mode 100644 index 000000000..a8e020c95 --- /dev/null +++ b/app/models/grade.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class Grade < ApplicationRecord + # relations + belongs_to :course_assessment_method + belongs_to :course_enrollment + belongs_to :lecturer, class_name: 'Employee' + + # validations + validates :point, numericality: { + only_integer: true, + allow_nil: true, + greater_than_or_equal_to: 0, + less_than_or_equal_to: 100 + } + validates :course_enrollment, uniqueness: { scope: :course_assessment_method_id } + + # delegates + delegate :student, :academic_term, to: :course_enrollment +end diff --git a/app/models/learning_outcome.rb b/app/models/learning_outcome.rb new file mode 100644 index 000000000..227396fff --- /dev/null +++ b/app/models/learning_outcome.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class LearningOutcome < ApplicationRecord + # relations + belongs_to :accreditation_standard + belongs_to :macro, class_name: 'LearningOutcome', foreign_key: :parent_id, + inverse_of: :micros, optional: true + has_many :micros, class_name: 'LearningOutcome', foreign_key: :parent_id, + inverse_of: :macro, dependent: :destroy + accepts_nested_attributes_for :micros, reject_if: :all_blank, allow_destroy: true + + # validations + validates :code, presence: true, uniqueness: { scope: :accreditation_standard_id }, length: { maximum: 10 } + validates :name, presence: true, length: { maximum: 255 } + validates_with LearningOutcomeValidator + + # scopes + scope :ordered, -> { order(:code) } +end diff --git a/app/models/paper.rb b/app/models/paper.rb index aa573d68b..0979931c0 100644 --- a/app/models/paper.rb +++ b/app/models/paper.rb @@ -4,7 +4,7 @@ class Paper < ApplicationRecord self.inheritance_column = nil # enums - enum activity: { deleted: 0, active: 1 } + enum activity: { passive: 0, active: 1 } enum presentation_type: { verbal: 33, guest_speaker: 39, poster: 38 } enum publication_status: { unpublished: 0, published: 1, accepted: 2 } enum scope: { national: 0, international: 1 } diff --git a/app/models/patron/account.rb b/app/models/patron/account.rb new file mode 100644 index 000000000..9dd7bc996 --- /dev/null +++ b/app/models/patron/account.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Patron + class Account + attr_reader :user, :accounts + + class Error < StandardError; end + class NotFound < Error; end + + def initialize(user) + @user = Utils::RoleQuerier.new(user) + @accounts = [] + build_accounts + end + + class << self + def call(user) + new(user).accounts + end + + def find_by(user:, type:, id:) + account = user.accounts.find { |item| item.type == type && item.id == id.to_i } + account&.verify? ? account : raise(NotFound) + end + end + + private + + def build_accounts + build_for_institution_manager + build_for_admin + build_for_employee + build_for_student + end + + def build_for_student + user.students.active.each { |student| accounts << Accounts::Student.new(student.id, user.id) } + end + + def build_for_employee + user.employees.active.each { |employee| accounts << Accounts::Employee.new(employee.id, user.id) } + end + + def build_for_admin + accounts << Accounts::Admin.new(user.id, user.id) if user.admin? + end + + def build_for_institution_manager + accounts << Accounts::InstitutionManager.new(user.id, user.id) if user.institution_manager? + end + end +end diff --git a/app/models/patron/accounts/admin.rb b/app/models/patron/accounts/admin.rb new file mode 100644 index 000000000..ed8f2d1f0 --- /dev/null +++ b/app/models/patron/accounts/admin.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Patron + module Accounts + class Admin < Base + def object + user + end + + def label + I18n.t('patron.accounts.admin') + end + + def type + 'admin' + end + + def verify? + object.present? + end + end + end +end diff --git a/app/models/patron/accounts/base.rb b/app/models/patron/accounts/base.rb new file mode 100644 index 000000000..599bb7527 --- /dev/null +++ b/app/models/patron/accounts/base.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Patron + module Accounts + class Base + attr_reader :id, :user_id + + def initialize(id, user_id) + @id = id.to_i + @user_id = user_id + end + + def user + @user ||= User.find(user_id) + end + + def object + raise NotImplementedError + end + + def label + raise NotImplementedError + end + + def type + raise NotImplementedError + end + + def verify? + raise NotImplementedError + end + + def root_path + :root + end + + def active_for?(account) + account&.identifier == identifier + end + + def identifier + { + type: type, + id: id + } + end + end + end +end diff --git a/app/models/patron/accounts/employee.rb b/app/models/patron/accounts/employee.rb new file mode 100644 index 000000000..a8156b0e5 --- /dev/null +++ b/app/models/patron/accounts/employee.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Patron + module Accounts + class Employee < Base + def object + @object ||= user.employees.find(id) + end + + def label + "#{I18n.t('patron.accounts.employee')} - #{object&.title_name} (#{object&.staff_number})" + end + + def type + 'employee' + end + + def verify? + object.present? + end + end + end +end diff --git a/app/models/patron/accounts/institution_manager.rb b/app/models/patron/accounts/institution_manager.rb new file mode 100644 index 000000000..d048d7ada --- /dev/null +++ b/app/models/patron/accounts/institution_manager.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Patron + module Accounts + class InstitutionManager < Base + def object + user + end + + def label + I18n.t('patron.accounts.institution_manager') + end + + def type + 'institution_manager' + end + + def verify? + object.present? + end + + def root_path + %i[manager stats] + end + end + end +end diff --git a/app/models/patron/accounts/student.rb b/app/models/patron/accounts/student.rb new file mode 100644 index 000000000..e882dad41 --- /dev/null +++ b/app/models/patron/accounts/student.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Patron + module Accounts + class Student < Base + def object + @object ||= user.students.find(id) + end + + def label + "#{I18n.t('patron.accounts.student')} - #{object&.student_number}" + end + + def type + 'student' + end + + def verify? + object.present? + end + end + end +end diff --git a/app/models/patron/utils/role_querier.rb b/app/models/patron/utils/role_querier.rb new file mode 100644 index 000000000..b5c0c41e2 --- /dev/null +++ b/app/models/patron/utils/role_querier.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Patron + module Utils + class RoleQuerier < SimpleDelegator + def admin? + any_roles? :manager, :admin + end + + def institution_manager? + positions&.active&.exists?( + administrative_function_id: AdministrativeFunction.where( + name: ['Rektör', 'Rektör Yardımcısı'] + ) + ) + end + end + end +end diff --git a/app/models/prospective_student.rb b/app/models/prospective_student.rb index 010689d1f..7c7c36ce6 100644 --- a/app/models/prospective_student.rb +++ b/app/models/prospective_student.rb @@ -82,16 +82,23 @@ def can_temporarily_register? military_status end + # rubocop:disable Metrics/MethodLength def registered_user(user) Student.new( user: user, unit: unit, permanently_registered: can_permanently_register?, + status: can_permanently_register? ? :active : :passive, student_number: id_number, # TODO: must be generated year: 1, # TODO: can have a different value depending on the characteristics of the program - semester: 1 + semester: 1, + entrance_type: student_entrance_type, + other_studentship: !obs_status, + registration_date: Time.zone.now, + registration_term: academic_term ) end + # rubocop:enable Metrics/MethodLength private diff --git a/app/models/scholarship_type.rb b/app/models/scholarship_type.rb new file mode 100644 index 000000000..00afd4a8e --- /dev/null +++ b/app/models/scholarship_type.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +class ScholarshipType < ApplicationRecord + # search + include ReferenceSearch + + # relations + has_many :students, dependent: :nullify + + # validations + validates :name, presence: true, uniqueness: true, length: { maximum: 255 } + validates :active, inclusion: { in: [true, false] } +end diff --git a/app/models/student.rb b/app/models/student.rb index 803a02f71..cba1f5614 100644 --- a/app/models/student.rb +++ b/app/models/student.rb @@ -5,7 +5,20 @@ class Student < ApplicationRecord include LDAP::Trigger ldap_trigger :user + # enums + enum status: { + active: 1, + passive: 2, + disengaged: 3, + unenrolled: 4, + graduated: 5 + } + # relations + belongs_to :entrance_type, class_name: 'StudentEntranceType' + belongs_to :registration_term, class_name: 'AcademicTerm', optional: true + belongs_to :scholarship_type, optional: true + belongs_to :stage, class_name: 'StudentGrade', optional: true belongs_to :user belongs_to :unit has_one :identity, dependent: :destroy @@ -13,21 +26,33 @@ class Student < ApplicationRecord has_many :curriculums, through: :unit has_many :semester_registrations, dependent: :destroy has_many :course_enrollments, through: :semester_registrations + has_many :tuition_debts, dependent: :destroy # scopes - # TODO: Query will be organized according to activity status - scope :active, -> { where(permanently_registered: true) } + scope :exceeded, -> { where(exceeded_education_period: true) } + scope :not_scholarships, -> { where(scholarship_type_id: nil) } + scope :scholarships, -> { where.not(scholarship_type_id: nil) } + scope :preparations, -> { where(stage: StudentGrade.preparation) } # validations + validates :exceeded_education_period, inclusion: { in: [true, false] } validates :unit_id, uniqueness: { scope: %i[user] } + validates :other_studentship, inclusion: { in: [true, false] } + validates :permanently_registered, inclusion: { in: [true, false] } # TODO: Will set equal_to: N, when we decide about student numbers + validates :preparatory_class, numericality: { only_integer: true, + greater_than_or_equal_to: 0, + less_than_or_equal_to: 2 } validates :student_number, presence: true, uniqueness: true, length: { maximum: 255 } - validates :permanently_registered, inclusion: { in: [true, false] } validates :semester, numericality: { greater_than: 0 } + validates :status, inclusion: { in: statuses.keys } validates :year, numericality: { greater_than_or_equal_to: 0 } # delegations delegate :addresses, to: :user + delegate :name, to: :stage, prefix: true, allow_nil: true + delegate :name, to: :unit, prefix: true + delegate :name, to: :scholarship_type, prefix: true, allow_nil: true # background jobs after_create_commit :build_identity_information, if: proc { identity.nil? } @@ -44,6 +69,22 @@ def current_registration semester_registrations.find_by(semester: semester) || semester_registrations.create end + def preparation? + Student.preparations.ids.include?(id) + end + + def scholarship? + scholarship_type_id? + end + + def preparatory_class_repetition? + preparatory_class.to_i >= 2 + end + + def prospective_student + user.prospective_students.find_by(unit_id: unit_id) + end + private def build_identity_information diff --git a/app/models/student_grade.rb b/app/models/student_grade.rb index c65dc43f6..6215bbfe6 100644 --- a/app/models/student_grade.rb +++ b/app/models/student_grade.rb @@ -4,4 +4,14 @@ class StudentGrade < ApplicationRecord include ReferenceCallbacks include ReferenceSearch include ReferenceValidations + + # relations + has_many :students, foreign_key: :stage_id, inverse_of: :stage, dependent: :nullify + + # scopes + scope :course, -> { where(code: 9) } + scope :preparation, -> { where(code: 1) } + scope :phd_qualify, -> { where(code: 11) } + scope :project, -> { where(code: 14) } + scope :thesis, -> { where(code: 10) } end diff --git a/app/models/tuition.rb b/app/models/tuition.rb new file mode 100644 index 000000000..0187ce481 --- /dev/null +++ b/app/models/tuition.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class Tuition < ApplicationRecord + # search + include DynamicSearch + + # dynamic_search + search_keys :academic_term_id + + # relations + belongs_to :academic_term + has_many :unit_tuitions, dependent: :destroy + has_many :units, through: :unit_tuitions + + # validations + validates :fee, numericality: { greater_than_or_equal_to: 0 } + validates :foreign_student_fee, numericality: { greater_than: 0 } + validates_associated :unit_tuitions +end diff --git a/app/models/tuition_debt.rb b/app/models/tuition_debt.rb new file mode 100644 index 000000000..4470e295f --- /dev/null +++ b/app/models/tuition_debt.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +class TuitionDebt < ApplicationRecord + self.inheritance_column = nil + + # virtual attributes + attr_accessor :unit_ids, :unit_id + + # search + include DynamicSearch + + # dynamic_search + search_keys :academic_term_id, :student_id + + # enums + enum description: { disability: 1, no_discount: 2 } + enum type: { personal: 1, bulk: 2 } + + # relations + belongs_to :student + belongs_to :academic_term + belongs_to :unit_tuition, optional: true + has_one :unit, through: :unit_tuition + + # validations + validates :academic_term_id, uniqueness: { scope: :student_id } + validates :amount, numericality: { greater_than: 0 } + validates :description, allow_nil: true, inclusion: { in: descriptions.keys } + validates :due_date, presence: true + validates :paid, inclusion: { in: [true, false] } + validates :type, inclusion: { in: types.keys } +end diff --git a/app/models/unit.rb b/app/models/unit.rb index 03af71455..cfca5c0b3 100644 --- a/app/models/unit.rb +++ b/app/models/unit.rb @@ -47,6 +47,9 @@ class Unit < ApplicationRecord has_many :available_courses, dependent: :destroy has_many :unit_calendars, dependent: :destroy has_many :calendars, through: :unit_calendars + has_many :unit_accreditation_standards, dependent: :destroy + has_many :unit_tuitions, dependent: :destroy + has_many :tuitions, through: :unit_tuitions # validations validates :name, presence: true, @@ -64,6 +67,7 @@ class Unit < ApplicationRecord scope :administratives, -> { where(unit_type: UnitType.administrative) } scope :committees, -> { where(unit_type: UnitType.committee) } scope :departments, -> { where(unit_type: UnitType.department) } + scope :evenings, -> { where(unit_instruction_type: UnitInstructionType.evening) } scope :faculties, -> { where(unit_type: UnitType.faculty) } scope :graduate_programs, -> { where(unit_type: UnitType.graduate_program) } scope :institutes, -> { where(unit_type: UnitType.institute) } @@ -97,6 +101,7 @@ class Unit < ApplicationRecord scope :eventable, -> { faculties .or(institutes) + .or(departments) .or(programs) .or(research_centers) .or(others) @@ -123,17 +128,27 @@ def program? UnitType.program.ids.include?(unit_type_id) end + def evening? + Unit.evenings.exists?(id: id) + end + # custom methods def subprograms descendants.programs end def subtree_employees - Employee.includes(:user, :title).joins(:units, user: :identities) + Employee.active.includes(:user, :title).joins(:units, user: :identities) .where(units: { id: subtree.active.ids }) end def effective_unit Unit.find_by(yoksis_id: effective_yoksis_id) if effective_yoksis_id.present? end + + def self.actively_coursable + active.coursable.joins(calendars: [calendar_events: :calendar_event_type]) + .where(calendar_event_types: { identifier: 'add_drop_available_courses' }) + .where('? BETWEEN start_time AND end_time', Time.current) + end end diff --git a/app/models/unit_accreditation_standard.rb b/app/models/unit_accreditation_standard.rb new file mode 100644 index 000000000..c7ffd2f04 --- /dev/null +++ b/app/models/unit_accreditation_standard.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class UnitAccreditationStandard < ApplicationRecord + # relations + belongs_to :unit + belongs_to :accreditation_standard + + # validations + validates :accreditation_standard, uniqueness: { scope: :unit } + validates_with UnitAccreditationStandardValidator +end diff --git a/app/models/unit_instruction_type.rb b/app/models/unit_instruction_type.rb index 0cbbe9cfd..cf74f0d62 100644 --- a/app/models/unit_instruction_type.rb +++ b/app/models/unit_instruction_type.rb @@ -7,4 +7,10 @@ class UnitInstructionType < ApplicationRecord # relations has_many :units, dependent: :nullify + + # scopes + scope :daytime, -> { where(code: 1) } + scope :distance, -> { where(code: 3) } + scope :evening, -> { where(code: 2) } + scope :open, -> { where(code: 4) } end diff --git a/app/models/unit_tuition.rb b/app/models/unit_tuition.rb new file mode 100644 index 000000000..aa25eff36 --- /dev/null +++ b/app/models/unit_tuition.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class UnitTuition < ApplicationRecord + # relations + belongs_to :unit + belongs_to :tuition + has_many :tuition_debts, dependent: :nullify + + # validations + validates :tuition_id, uniqueness: { scope: :unit_id } + validates_with UnitTuitionValidator + + # delegates + delegate :academic_term, :fee, :foreign_student_fee, to: :tuition +end diff --git a/app/models/user.rb b/app/models/user.rb index dabca7a29..65f016925 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -57,7 +57,12 @@ class User < ApplicationRecord foreign_key: :id_number, dependent: :nullify, inverse_of: :user + has_one :current_employee, -> { active }, class_name: 'Employee', inverse_of: :user + # validations + validates :disability_rate, numericality: { only_integer: true, + greater_than_or_equal_to: 0, + less_than_or_equal_to: 100 } validates :email, presence: true, uniqueness: true, length: { maximum: 255 }, 'valid_email_2/email': { mx: true, disposable: true, @@ -123,7 +128,7 @@ def send_devise_notification(notification, *args) # custom methods def accounts - (students + employees).flatten + Patron::Account.call(self) end def title @@ -142,6 +147,10 @@ def academic? employees.active.academic.exists? end + def disabled? + disability_rate.positive? + end + def self.with_most_articles where.not(articles_count: 0).order('articles_count desc').limit(10) end diff --git a/app/policies/account/yoksis_service_policy.rb b/app/policies/account/yoksis_service_policy.rb new file mode 100644 index 000000000..e2c24ea3d --- /dev/null +++ b/app/policies/account/yoksis_service_policy.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module Account + class YoksisServicePolicy < ApplicationPolicy + def fetch? + user.employee? && user.current_employee&.academic? + end + end +end diff --git a/app/policies/accreditation_management/accreditation_standard_policy.rb b/app/policies/accreditation_management/accreditation_standard_policy.rb new file mode 100644 index 000000000..100f4ee05 --- /dev/null +++ b/app/policies/accreditation_management/accreditation_standard_policy.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module AccreditationManagement + class AccreditationStandardPolicy < ApplicationPolicy + include CrudPolicyMethods + + def units? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :accreditation_management, privileges + end + end +end diff --git a/app/policies/accreditation_management/learning_outcome_policy.rb b/app/policies/accreditation_management/learning_outcome_policy.rb new file mode 100644 index 000000000..7809a068f --- /dev/null +++ b/app/policies/accreditation_management/learning_outcome_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module AccreditationManagement + class LearningOutcomePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index? + + private + + def permitted?(*privileges) + user.privilege? :accreditation_management, privileges + end + end +end diff --git a/app/policies/calendar_management/calendar_event_type_policy.rb b/app/policies/calendar_management/calendar_event_type_policy.rb new file mode 100644 index 000000000..2dabd3cef --- /dev/null +++ b/app/policies/calendar_management/calendar_event_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CalendarManagement + class CalendarEventTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :calendar_management, privileges + end + end +end diff --git a/app/policies/calendar_management/calendar_policy.rb b/app/policies/calendar_management/calendar_policy.rb new file mode 100644 index 000000000..0f54c955c --- /dev/null +++ b/app/policies/calendar_management/calendar_policy.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module CalendarManagement + class CalendarPolicy < ApplicationPolicy + include CrudPolicyMethods + + def duplicate? + permitted? :write + end + + def units? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :calendar_management, privileges + end + end +end diff --git a/app/policies/committee/agenda_policy.rb b/app/policies/committee/agenda_policy.rb new file mode 100644 index 000000000..9861b95b0 --- /dev/null +++ b/app/policies/committee/agenda_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Committee + class AgendaPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :agenda_management, privileges + end + end +end diff --git a/app/policies/committee/agenda_type_policy.rb b/app/policies/committee/agenda_type_policy.rb new file mode 100644 index 000000000..142a7993e --- /dev/null +++ b/app/policies/committee/agenda_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Committee + class AgendaTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :agenda_management, privileges + end + end +end diff --git a/app/policies/committee/dashboard_policy.rb b/app/policies/committee/dashboard_policy.rb new file mode 100644 index 000000000..d92eefa1c --- /dev/null +++ b/app/policies/committee/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Committee + class DashboardPolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :committee_management, privileges + end + end +end diff --git a/app/policies/committee/decision_policy.rb b/app/policies/committee/decision_policy.rb new file mode 100644 index 000000000..10df86a52 --- /dev/null +++ b/app/policies/committee/decision_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Committee + class DecisionPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :destroy?, :index? + + private + + def permitted?(*privileges) + user.privilege? :decision_management, privileges + end + end +end diff --git a/app/policies/committee/meeting_policy.rb b/app/policies/committee/meeting_policy.rb new file mode 100644 index 000000000..923972266 --- /dev/null +++ b/app/policies/committee/meeting_policy.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Committee + class MeetingPolicy < ApplicationPolicy + include CrudPolicyMethods + + private + + def permitted?(*privileges) + user.privilege? :meeting_management, privileges + end + end +end diff --git a/app/policies/concerns/crud_policy_methods.rb b/app/policies/concerns/crud_policy_methods.rb new file mode 100644 index 000000000..49d7e9e95 --- /dev/null +++ b/app/policies/concerns/crud_policy_methods.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module CrudPolicyMethods + extend ActiveSupport::Concern + + included do + def create? + permitted? :write + end + + def destroy? + permitted? :destroy + end + + def edit? + update? + end + + def index? + permitted? :read + end + + def new? + create? + end + + def show? + permitted? :read + end + + def update? + permitted? :write + end + end +end diff --git a/app/policies/course_management/available_course_group_policy.rb b/app/policies/course_management/available_course_group_policy.rb new file mode 100644 index 000000000..ff2a342f6 --- /dev/null +++ b/app/policies/course_management/available_course_group_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class AvailableCourseGroupPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :available_course_management, privileges + end + end +end diff --git a/app/policies/course_management/available_course_policy.rb b/app/policies/course_management/available_course_policy.rb new file mode 100644 index 000000000..638341e65 --- /dev/null +++ b/app/policies/course_management/available_course_policy.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module CourseManagement + class AvailableCoursePolicy < ApplicationPolicy + include CrudPolicyMethods + + private + + def permitted?(*privileges) + user.privilege? :available_course_management, privileges + end + end +end diff --git a/app/policies/course_management/course_evaluation_type_policy.rb b/app/policies/course_management/course_evaluation_type_policy.rb new file mode 100644 index 000000000..121f70a2d --- /dev/null +++ b/app/policies/course_management/course_evaluation_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class CourseEvaluationTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :available_course_management, privileges + end + end +end diff --git a/app/policies/course_management/course_group_policy.rb b/app/policies/course_management/course_group_policy.rb new file mode 100644 index 000000000..9897e2ed7 --- /dev/null +++ b/app/policies/course_management/course_group_policy.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module CourseManagement + class CourseGroupPolicy < ApplicationPolicy + include CrudPolicyMethods + + private + + def permitted?(*privileges) + user.privilege? :course_management, privileges + end + end +end diff --git a/app/policies/course_management/course_group_type_policy.rb b/app/policies/course_management/course_group_type_policy.rb new file mode 100644 index 000000000..b52e8fdc3 --- /dev/null +++ b/app/policies/course_management/course_group_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class CourseGroupTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :course_management, privileges + end + end +end diff --git a/app/policies/course_management/course_policy.rb b/app/policies/course_management/course_policy.rb new file mode 100644 index 000000000..8404caef7 --- /dev/null +++ b/app/policies/course_management/course_policy.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module CourseManagement + class CoursePolicy < ApplicationPolicy + include CrudPolicyMethods + + private + + def permitted?(*privileges) + user.privilege? :course_management, privileges + end + end +end diff --git a/app/policies/course_management/course_type_policy.rb b/app/policies/course_management/course_type_policy.rb new file mode 100644 index 000000000..be010d062 --- /dev/null +++ b/app/policies/course_management/course_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class CourseTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :course_management, privileges + end + end +end diff --git a/app/policies/course_management/curriculum_course_group_policy.rb b/app/policies/course_management/curriculum_course_group_policy.rb new file mode 100644 index 000000000..832576243 --- /dev/null +++ b/app/policies/course_management/curriculum_course_group_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class CurriculumCourseGroupPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :curriculum_management, privileges + end + end +end diff --git a/app/policies/course_management/curriculum_course_policy.rb b/app/policies/course_management/curriculum_course_policy.rb new file mode 100644 index 000000000..3c1698da3 --- /dev/null +++ b/app/policies/course_management/curriculum_course_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module CourseManagement + class CurriculumCoursePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :curriculum_management, privileges + end + end +end diff --git a/app/policies/course_management/curriculum_policy.rb b/app/policies/course_management/curriculum_policy.rb new file mode 100644 index 000000000..dcc211fd3 --- /dev/null +++ b/app/policies/course_management/curriculum_policy.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module CourseManagement + class CurriculumPolicy < ApplicationPolicy + include CrudPolicyMethods + + def openable_courses? + permitted?(:read) || user.privilege?(:available_course_management, :write) + end + + private + + def permitted?(*privileges) + user.privilege? :curriculum_management, privileges + end + end +end diff --git a/app/policies/detsis/dashboard_policy.rb b/app/policies/detsis/dashboard_policy.rb new file mode 100644 index 000000000..c2c35d20f --- /dev/null +++ b/app/policies/detsis/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Detsis + class DashboardPolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :detsis_management, privileges + end + end +end diff --git a/app/policies/detsis/sdp_code_policy.rb b/app/policies/detsis/sdp_code_policy.rb new file mode 100644 index 000000000..8d2b76db2 --- /dev/null +++ b/app/policies/detsis/sdp_code_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Detsis + class SdpCodePolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :detsis_management, privileges + end + end +end diff --git a/app/policies/first_registration/prospective_employee_policy.rb b/app/policies/first_registration/prospective_employee_policy.rb new file mode 100644 index 000000000..69c2ef15d --- /dev/null +++ b/app/policies/first_registration/prospective_employee_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module FirstRegistration + class ProspectiveEmployeePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :registration_management_for_employees, privileges + end + end +end diff --git a/app/policies/first_registration/prospective_student_policy.rb b/app/policies/first_registration/prospective_student_policy.rb new file mode 100644 index 000000000..e597267d3 --- /dev/null +++ b/app/policies/first_registration/prospective_student_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module FirstRegistration + class ProspectiveStudentPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :destroy? + + def register? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :registration_management_for_students, privileges + end + end +end diff --git a/app/policies/first_registration/registration_document_policy.rb b/app/policies/first_registration/registration_document_policy.rb new file mode 100644 index 000000000..ebfa98174 --- /dev/null +++ b/app/policies/first_registration/registration_document_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module FirstRegistration + class RegistrationDocumentPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :registration_management_for_students, privileges + end + end +end diff --git a/app/policies/instructiveness/assessment_policy.rb b/app/policies/instructiveness/assessment_policy.rb new file mode 100644 index 000000000..88f7568d0 --- /dev/null +++ b/app/policies/instructiveness/assessment_policy.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Instructiveness + class AssessmentPolicy < ApplicationPolicy + def show? + permitted? + end + + def edit? + permitted? + end + + def update? + permitted? + end + + def save? + permitted? + end + + def draft? + permitted? + end + + private + + def permitted? + user&.employee? && user.current_employee&.academic? + end + end +end diff --git a/app/policies/instructiveness/given_course_policy.rb b/app/policies/instructiveness/given_course_policy.rb new file mode 100644 index 000000000..14390bdd8 --- /dev/null +++ b/app/policies/instructiveness/given_course_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Instructiveness + class GivenCoursePolicy < ApplicationPolicy + def index? + permitted? + end + + def show? + permitted? + end + + def students? + permitted? + end + + private + + def permitted? + user&.employee? && user.current_employee&.academic? + end + end +end diff --git a/app/policies/ldap_entity_policy.rb b/app/policies/ldap_entity_policy.rb new file mode 100644 index 000000000..1da2eb7c5 --- /dev/null +++ b/app/policies/ldap_entity_policy.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class LdapEntityPolicy < ApplicationPolicy + def index? + permitted? :read + end + + def show? + permitted? :read + end + + def start_sync? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :ldap_management, privileges + end +end diff --git a/app/policies/location/city_policy.rb b/app/policies/location/city_policy.rb new file mode 100644 index 000000000..d7448f774 --- /dev/null +++ b/app/policies/location/city_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Location + class CityPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index? + + private + + def permitted?(*privileges) + user.privilege? :location_management, privileges + end + end +end diff --git a/app/policies/location/country_policy.rb b/app/policies/location/country_policy.rb new file mode 100644 index 000000000..31ec0a9f5 --- /dev/null +++ b/app/policies/location/country_policy.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Location + class CountryPolicy < ApplicationPolicy + include CrudPolicyMethods + + private + + def permitted?(*privileges) + user.privilege? :location_management, privileges + end + end +end diff --git a/app/policies/location/district_policy.rb b/app/policies/location/district_policy.rb new file mode 100644 index 000000000..3667d2e1c --- /dev/null +++ b/app/policies/location/district_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Location + class DistrictPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :location_management, privileges + end + end +end diff --git a/app/policies/manager/dashboard_policy.rb b/app/policies/manager/dashboard_policy.rb new file mode 100644 index 000000000..6b54df40c --- /dev/null +++ b/app/policies/manager/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Manager + class DashboardPolicy < ApplicationPolicy + def stats? + user.role?(:admin) || manager? + end + + private + + def manager? + Patron::Utils::RoleQuerier.new(user).institution_manager? + end + end +end diff --git a/app/policies/manager/stats/article_policy.rb b/app/policies/manager/stats/article_policy.rb new file mode 100644 index 000000000..2fbbe3791 --- /dev/null +++ b/app/policies/manager/stats/article_policy.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Manager + module Stats + class ArticlePolicy < ApplicationPolicy + def index? + user.role?(:admin) || manager? + end + + private + + def manager? + Patron::Utils::RoleQuerier.new(user).institution_manager? + end + end + end +end diff --git a/app/policies/manager/stats/employee_policy.rb b/app/policies/manager/stats/employee_policy.rb new file mode 100644 index 000000000..60b176c9c --- /dev/null +++ b/app/policies/manager/stats/employee_policy.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Manager + module Stats + class EmployeePolicy < ApplicationPolicy + def academic? + user.role?(:admin) || manager? + end + + def index? + user.role?(:admin) || manager? + end + + private + + def manager? + Patron::Utils::RoleQuerier.new(user).institution_manager? + end + end + end +end diff --git a/app/policies/manager/stats/student_policy.rb b/app/policies/manager/stats/student_policy.rb new file mode 100644 index 000000000..23ac8598c --- /dev/null +++ b/app/policies/manager/stats/student_policy.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Manager + module Stats + class StudentPolicy < ApplicationPolicy + def cities? + user.role?(:admin) || manager? + end + + def double_major_and_minor? + user.role?(:admin) || manager? + end + + def genders? + user.role?(:admin) || manager? + end + + def genders_and_degrees? + user.role?(:admin) || manager? + end + + def index? + user.role?(:admin) || manager? + end + + def non_graduates? + user.role?(:admin) || manager? + end + + private + + def manager? + Patron::Utils::RoleQuerier.new(user).institution_manager? + end + end + end +end diff --git a/app/policies/meksis/building_policy.rb b/app/policies/meksis/building_policy.rb new file mode 100644 index 000000000..fc6247a65 --- /dev/null +++ b/app/policies/meksis/building_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Meksis + class BuildingPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :new?, :create?, :destroy? + + private + + def permitted?(*privileges) + user.privilege? :meksis_management, privileges + end + end +end diff --git a/app/policies/meksis/classroom_policy.rb b/app/policies/meksis/classroom_policy.rb new file mode 100644 index 000000000..a92927cb3 --- /dev/null +++ b/app/policies/meksis/classroom_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Meksis + class ClassroomPolicy < ApplicationPolicy + def index? + permitted? :read + end + + def show? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :meksis_management, privileges + end + end +end diff --git a/app/policies/meksis/dashboard_policy.rb b/app/policies/meksis/dashboard_policy.rb new file mode 100644 index 000000000..94380b46e --- /dev/null +++ b/app/policies/meksis/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Meksis + class DashboardPolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :meksis_management, privileges + end + end +end diff --git a/app/policies/meksis/place_type_policy.rb b/app/policies/meksis/place_type_policy.rb new file mode 100644 index 000000000..2e6092a44 --- /dev/null +++ b/app/policies/meksis/place_type_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Meksis + class PlaceTypePolicy < ApplicationPolicy + def index? + permitted? :read + end + + def show? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :meksis_management, privileges + end + end +end diff --git a/app/policies/patron/assignment_policy.rb b/app/policies/patron/assignment_policy.rb index e0e3f1848..3d02dc26c 100644 --- a/app/policies/patron/assignment_policy.rb +++ b/app/policies/patron/assignment_policy.rb @@ -2,21 +2,9 @@ module Patron class AssignmentPolicy < ApplicationPolicy - def index? - permitted? :read - end - - def show? - permitted? :read - end - - def update? - permitted? :write - end + include CrudPolicyMethods - def edit? - update? - end + undef :new?, :create?, :destroy? def preview_scope? permitted? :read diff --git a/app/policies/patron/query_store_policy.rb b/app/policies/patron/query_store_policy.rb index 5433532b4..f95d0f9f0 100644 --- a/app/policies/patron/query_store_policy.rb +++ b/app/policies/patron/query_store_policy.rb @@ -2,33 +2,7 @@ module Patron class QueryStorePolicy < ApplicationPolicy - def index? - permitted? :read - end - - def show? - permitted? :read - end - - def new? - create? - end - - def create? - permitted? :write - end - - def update? - permitted? :write - end - - def edit? - update? - end - - def destroy? - permitted? :destroy - end + include CrudPolicyMethods def preview? permitted? :read diff --git a/app/policies/reference/academic_term_policy.rb b/app/policies/reference/academic_term_policy.rb new file mode 100644 index 000000000..e6d51970b --- /dev/null +++ b/app/policies/reference/academic_term_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class AcademicTermPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/accreditation_institution_policy.rb b/app/policies/reference/accreditation_institution_policy.rb new file mode 100644 index 000000000..22104e7db --- /dev/null +++ b/app/policies/reference/accreditation_institution_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class AccreditationInstitutionPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/assessment_method_policy.rb b/app/policies/reference/assessment_method_policy.rb new file mode 100644 index 000000000..262e8596b --- /dev/null +++ b/app/policies/reference/assessment_method_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class AssessmentMethodPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/course_group_type_policy.rb b/app/policies/reference/course_group_type_policy.rb new file mode 100644 index 000000000..14940768c --- /dev/null +++ b/app/policies/reference/course_group_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class CourseGroupTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/course_type_policy.rb b/app/policies/reference/course_type_policy.rb new file mode 100644 index 000000000..3d9e7fb4c --- /dev/null +++ b/app/policies/reference/course_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class CourseTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/dashboard_policy.rb b/app/policies/reference/dashboard_policy.rb new file mode 100644 index 000000000..f57582adb --- /dev/null +++ b/app/policies/reference/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class DashboardPolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/document_type_policy.rb b/app/policies/reference/document_type_policy.rb new file mode 100644 index 000000000..c1a11e900 --- /dev/null +++ b/app/policies/reference/document_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class DocumentTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/evaluation_type_policy.rb b/app/policies/reference/evaluation_type_policy.rb new file mode 100644 index 000000000..669bf926f --- /dev/null +++ b/app/policies/reference/evaluation_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class EvaluationTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/high_school_type_policy.rb b/app/policies/reference/high_school_type_policy.rb new file mode 100644 index 000000000..c7425bd5f --- /dev/null +++ b/app/policies/reference/high_school_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class HighSchoolTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/language_policy.rb b/app/policies/reference/language_policy.rb new file mode 100644 index 000000000..2b322f40d --- /dev/null +++ b/app/policies/reference/language_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class LanguagePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/scholarship_type_policy.rb b/app/policies/reference/scholarship_type_policy.rb new file mode 100644 index 000000000..510060128 --- /dev/null +++ b/app/policies/reference/scholarship_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class ScholarshipTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/reference/title_policy.rb b/app/policies/reference/title_policy.rb new file mode 100644 index 000000000..e44399bee --- /dev/null +++ b/app/policies/reference/title_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Reference + class TitlePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :reference_management, privileges + end + end +end diff --git a/app/policies/student_policy.rb b/app/policies/student_policy.rb new file mode 100644 index 000000000..fece2d4ab --- /dev/null +++ b/app/policies/student_policy.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class StudentPolicy < ApplicationPolicy + def edit? + permitted? :write + end + + def update? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :student_management, privileges + end +end diff --git a/app/policies/studentship/course_enrollment_policy.rb b/app/policies/studentship/course_enrollment_policy.rb new file mode 100644 index 000000000..3475e3fe5 --- /dev/null +++ b/app/policies/studentship/course_enrollment_policy.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Studentship + class CourseEnrollmentPolicy < ApplicationPolicy + def create? + permitted? + end + + def destroy? + permitted? + end + + def index? + permitted? + end + + def list? + permitted? + end + + def new? + permitted? + end + + def save? + permitted? + end + + private + + def permitted? + user&.student? + end + end +end diff --git a/app/policies/studentship/tuition_debt_policy.rb b/app/policies/studentship/tuition_debt_policy.rb new file mode 100644 index 000000000..854ca75c2 --- /dev/null +++ b/app/policies/studentship/tuition_debt_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Studentship + class TuitionDebtPolicy < ApplicationPolicy + def index? + permitted? + end + + private + + def permitted? + user&.student? + end + end +end diff --git a/app/policies/tuition_management/tuition_debt_policy.rb b/app/policies/tuition_management/tuition_debt_policy.rb new file mode 100644 index 000000000..e807592e9 --- /dev/null +++ b/app/policies/tuition_management/tuition_debt_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module TuitionManagement + class TuitionDebtPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + def create_with_service? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :tuition_management, privileges + end + end +end diff --git a/app/policies/tuition_management/tuition_policy.rb b/app/policies/tuition_management/tuition_policy.rb new file mode 100644 index 000000000..3e13ec772 --- /dev/null +++ b/app/policies/tuition_management/tuition_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module TuitionManagement + class TuitionPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + def units? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :tuition_management, privileges + end + end +end diff --git a/app/policies/unit_policy.rb b/app/policies/unit_policy.rb new file mode 100644 index 000000000..9ddea273a --- /dev/null +++ b/app/policies/unit_policy.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class UnitPolicy < ApplicationPolicy + include CrudPolicyMethods + + def courses? + permitted? :read + end + + def curriculums? + permitted? :read + end + + def employees? + permitted? :read + end + + def programs? + permitted? :read + end + + def students? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :unit_management, privileges + end +end diff --git a/app/policies/user_management/disability_policy.rb b/app/policies/user_management/disability_policy.rb new file mode 100644 index 000000000..4120ed180 --- /dev/null +++ b/app/policies/user_management/disability_policy.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module UserManagement + class DisabilityPolicy < ApplicationPolicy + def edit? + permitted? :write + end + + def update? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :user_management, privileges + end + end +end diff --git a/app/policies/user_management/duty_policy.rb b/app/policies/user_management/duty_policy.rb new file mode 100644 index 000000000..1b0fcd3ae --- /dev/null +++ b/app/policies/user_management/duty_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module UserManagement + class DutyPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :employee_management, privileges + end + end +end diff --git a/app/policies/user_management/employee_policy.rb b/app/policies/user_management/employee_policy.rb new file mode 100644 index 000000000..c3d08b1a7 --- /dev/null +++ b/app/policies/user_management/employee_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module UserManagement + class EmployeePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :employee_management, privileges + end + end +end diff --git a/app/policies/user_management/position_policy.rb b/app/policies/user_management/position_policy.rb new file mode 100644 index 000000000..a03b37eb4 --- /dev/null +++ b/app/policies/user_management/position_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module UserManagement + class PositionPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :index?, :show? + + private + + def permitted?(*privileges) + user.privilege? :employee_management, privileges + end + end +end diff --git a/app/policies/user_management/user_policy.rb b/app/policies/user_management/user_policy.rb new file mode 100644 index 000000000..5bd198572 --- /dev/null +++ b/app/policies/user_management/user_policy.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module UserManagement + class UserPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :create?, :new? + + def save_address_from_mernis? + permitted? :write + end + + def save_identity_from_mernis? + permitted? :write + end + + private + + def permitted?(*privileges) + user.privilege? :user_management, privileges + end + end +end diff --git a/app/policies/yoksis/administrative_function_policy.rb b/app/policies/yoksis/administrative_function_policy.rb new file mode 100644 index 000000000..00d472540 --- /dev/null +++ b/app/policies/yoksis/administrative_function_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class AdministrativeFunctionPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + protected + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/dashboard_policy.rb b/app/policies/yoksis/dashboard_policy.rb new file mode 100644 index 000000000..cbdfca981 --- /dev/null +++ b/app/policies/yoksis/dashboard_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class DashboardPolicy < ApplicationPolicy + def index? + permitted? :read + end + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_disability_type_policy.rb b/app/policies/yoksis/student_disability_type_policy.rb new file mode 100644 index 000000000..4e63ac448 --- /dev/null +++ b/app/policies/yoksis/student_disability_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentDisabilityTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_drop_out_type_policy.rb b/app/policies/yoksis/student_drop_out_type_policy.rb new file mode 100644 index 000000000..3d391e4d8 --- /dev/null +++ b/app/policies/yoksis/student_drop_out_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentDropOutTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_education_level_policy.rb b/app/policies/yoksis/student_education_level_policy.rb new file mode 100644 index 000000000..04c125c93 --- /dev/null +++ b/app/policies/yoksis/student_education_level_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentEducationLevelPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_entrance_point_type_policy.rb b/app/policies/yoksis/student_entrance_point_type_policy.rb new file mode 100644 index 000000000..9a2a430da --- /dev/null +++ b/app/policies/yoksis/student_entrance_point_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentEntrancePointTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_entrance_type_policy.rb b/app/policies/yoksis/student_entrance_type_policy.rb new file mode 100644 index 000000000..3dd2d4730 --- /dev/null +++ b/app/policies/yoksis/student_entrance_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentEntranceTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_grade_policy.rb b/app/policies/yoksis/student_grade_policy.rb new file mode 100644 index 000000000..78672d2c9 --- /dev/null +++ b/app/policies/yoksis/student_grade_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentGradePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_grading_system_policy.rb b/app/policies/yoksis/student_grading_system_policy.rb new file mode 100644 index 000000000..682f62b2d --- /dev/null +++ b/app/policies/yoksis/student_grading_system_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentGradingSystemPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_punishment_type_policy.rb b/app/policies/yoksis/student_punishment_type_policy.rb new file mode 100644 index 000000000..8c3f75c5b --- /dev/null +++ b/app/policies/yoksis/student_punishment_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentPunishmentTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/student_studentship_status_policy.rb b/app/policies/yoksis/student_studentship_status_policy.rb new file mode 100644 index 000000000..6945defff --- /dev/null +++ b/app/policies/yoksis/student_studentship_status_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class StudentStudentshipStatusPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/unit_instruction_language_policy.rb b/app/policies/yoksis/unit_instruction_language_policy.rb new file mode 100644 index 000000000..72a3d39dd --- /dev/null +++ b/app/policies/yoksis/unit_instruction_language_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class UnitInstructionLanguagePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/unit_instruction_type_policy.rb b/app/policies/yoksis/unit_instruction_type_policy.rb new file mode 100644 index 000000000..f42342412 --- /dev/null +++ b/app/policies/yoksis/unit_instruction_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class UnitInstructionTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/unit_status_policy.rb b/app/policies/yoksis/unit_status_policy.rb new file mode 100644 index 000000000..ba577cafc --- /dev/null +++ b/app/policies/yoksis/unit_status_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class UnitStatusPolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/unit_type_policy.rb b/app/policies/yoksis/unit_type_policy.rb new file mode 100644 index 000000000..456aef142 --- /dev/null +++ b/app/policies/yoksis/unit_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class UnitTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/policies/yoksis/university_type_policy.rb b/app/policies/yoksis/university_type_policy.rb new file mode 100644 index 000000000..faef83f6b --- /dev/null +++ b/app/policies/yoksis/university_type_policy.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Yoksis + class UniversityTypePolicy < ApplicationPolicy + include CrudPolicyMethods + + undef :show? + + private + + def permitted?(*privileges) + user.privilege? :yoksis_management, privileges + end + end +end diff --git a/app/services/debt/tuition/debt.rb b/app/services/debt/tuition/debt.rb new file mode 100644 index 000000000..2e4040a11 --- /dev/null +++ b/app/services/debt/tuition/debt.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Debt + module Tuition + class Debt + attr_reader :academic_term, :due_date, :student, :user, :unit_tuition + attr_accessor :amount + + def initialize(student, unit_tuition, due_date) + @student = student + @user = student.user + @unit_tuition = unit_tuition + @amount = unit_tuition.fee + @academic_term = unit_tuition.academic_term + @due_date = due_date + end + end + end +end diff --git a/app/services/debt/tuition/dispatch.rb b/app/services/debt/tuition/dispatch.rb new file mode 100644 index 000000000..914850a0f --- /dev/null +++ b/app/services/debt/tuition/dispatch.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Dispatch + module_function + + def perform(units, term_id, due_date) + units.each do |unit| + unit.students.active.each do |student| + tuition = unit.tuitions.find_by(academic_term_id: term_id) + next if tuition.nil? + + chain = set_chain(unit, student) + debt = Debt.new(student, tuition.unit_tuitions.first, due_date) + chain.call(debt) + end + end + end + + def set_chain(unit, student) + if unit.evening? + Process::EveningEducation.new(student).chain + else + Process::DaytimeEducation.new(student).chain + end + end + end + end +end diff --git a/app/services/debt/tuition/handler.rb b/app/services/debt/tuition/handler.rb new file mode 100644 index 000000000..814ed138e --- /dev/null +++ b/app/services/debt/tuition/handler.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Debt + module Tuition + class Handler + attr_reader :successor + + def initialize(successor = nil) + @successor = successor + end + + def operate(_); end + + def fulfill?(_) + true + end + + def call(tuition) + return operate(tuition) if fulfill?(tuition) + return successor.call(tuition) if successor + + Rails.logger.warn("Any tuition chain operation couldn't possible for #{tuition.user.id_number}.") + end + end + end +end diff --git a/app/services/debt/tuition/operation/creation.rb b/app/services/debt/tuition/operation/creation.rb new file mode 100644 index 000000000..1b1e042be --- /dev/null +++ b/app/services/debt/tuition/operation/creation.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Operation + module Creation + def create_debt(debt, description) + TuitionDebt.create( + student: debt.student, + academic_term: debt.academic_term, + unit_tuition: debt.unit_tuition, + amount: debt.amount, + description: description, + type: :bulk, + due_date: debt.due_date + ) + end + end + end + end +end diff --git a/app/services/debt/tuition/operation/disability.rb b/app/services/debt/tuition/operation/disability.rb new file mode 100644 index 000000000..4d7e7d5c7 --- /dev/null +++ b/app/services/debt/tuition/operation/disability.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Operation + class Disability < Handler + include Creation + + def operate(debt) + compute(debt) + create_debt(debt, :disability) + end + + def fulfill?(debt) + debt.user.disabled? + end + + private + + def compute(debt) + debt.amount -= (debt.amount * debt.user.disability_rate) / 100 + end + end + end + end +end diff --git a/app/services/debt/tuition/operation/free.rb b/app/services/debt/tuition/operation/free.rb new file mode 100644 index 000000000..6b70fa1ff --- /dev/null +++ b/app/services/debt/tuition/operation/free.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Operation + class Free < Handler + end + end + end +end diff --git a/app/services/debt/tuition/operation/no_discount.rb b/app/services/debt/tuition/operation/no_discount.rb new file mode 100644 index 000000000..756c6564e --- /dev/null +++ b/app/services/debt/tuition/operation/no_discount.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Operation + class NoDiscount < Handler + include Creation + + def operate(debt) + create_debt(debt, :no_discount) + end + end + end + end +end diff --git a/app/services/debt/tuition/operation/scholarship.rb b/app/services/debt/tuition/operation/scholarship.rb new file mode 100644 index 000000000..3f60e9851 --- /dev/null +++ b/app/services/debt/tuition/operation/scholarship.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Operation + class Scholarship < Handler + def fulfill?(debt) + debt.student.scholarship? + end + end + end + end +end diff --git a/app/services/debt/tuition/process/daytime_education.rb b/app/services/debt/tuition/process/daytime_education.rb new file mode 100644 index 000000000..e45d9762f --- /dev/null +++ b/app/services/debt/tuition/process/daytime_education.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Process + class DaytimeEducation + attr_reader :student + + def initialize(student) + @student = student + end + + def chain + if exceeded? || other_studentship? || student.preparatory_class_repetition? + Operation::Disability.new(Operation::NoDiscount.new) + else + Operation::Free.new + end + end + + private + + def exceeded? + student.exceeded_education_period + end + + def other_studentship? + student.other_studentship + end + end + end + end +end diff --git a/app/services/debt/tuition/process/evening_education.rb b/app/services/debt/tuition/process/evening_education.rb new file mode 100644 index 000000000..e25c064f2 --- /dev/null +++ b/app/services/debt/tuition/process/evening_education.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Debt + module Tuition + module Process + class EveningEducation + attr_reader :student + + def initialize(student) + @student = student + end + + def chain + if student.exceeded_education_period + Operation::Disability.new(Operation::NoDiscount.new) + else + Operation::Scholarship.new(Operation::Disability.new(Operation::NoDiscount.new)) + end + end + end + end + end +end diff --git a/app/services/nexmo/error_handler.rb b/app/services/nexmo/error_handler.rb deleted file mode 100644 index 40527c051..000000000 --- a/app/services/nexmo/error_handler.rb +++ /dev/null @@ -1,40 +0,0 @@ -# frozen_string_literal: true - -module Nexmo - module ErrorHandler - SOFT_FAIL_CODES = { - '2' => 'Missing Parameters', - '3' => 'Invalid Parameters', - '6' => 'Invalid Message', - '12' => 'Message Too Long', - '22' => 'Invalid Network Code', - '33' => 'Number De-activated' - }.freeze - - HARD_FAIL_CODES = { - '1' => 'Throttled', - '4' => 'Invalid Credentials', - '5' => 'Internal Error', - '7' => 'Number Barred', - '8' => 'Partner Account Barred', - '9' => 'Partner Quota Violation', - '10' => 'Too Many Existing Binds', - '11' => 'Account Not Enabled For HTTP', - '14' => 'Invalid Signature', - '15' => 'Invalid Sender Address', - '23' => 'Invalid Callback Url', - '32' => 'Signature And API Secret Disallowed' - }.freeze - - def log_or_notify_admin(response) - status = response.status - notifier = Slack::Notifier.new Tenant.credentials.slack[:panik_hook] - - if status == '0' - Rails.logger.info "Sent message id=#{response.message_id}" - elsif SOFT_FAIL_CODES.key?(status) || HARD_FAIL_CODES.key?(status) - notifier.ping "Nexmo SMS Error (code: #{status}, text: #{response.error_text})" - end - end - end -end diff --git a/app/services/nexmo/sms.rb b/app/services/nexmo/sms.rb deleted file mode 100644 index 3d7445b7c..000000000 --- a/app/services/nexmo/sms.rb +++ /dev/null @@ -1,74 +0,0 @@ -# frozen_string_literal: true - -require_relative 'error_handler' - -module Nexmo - class Sms - include ErrorHandler - - # Some countries and operators do not support concatenation and unicode. - # Enable multipart and unicode type thoughtfully! - # Nexmo supports all the standard GSM characters + characters from the GSM extended table. - # Test GSM-7 encoding: http://chadselph.github.io/smssplit/ - def initialize(to_number, message, country = 'tr', multipart = false, type = 'text') - @to_number = to_number.to_s - @message = type.eql?('text') ? message.asciified.squish : message - @country = country - @multipart = multipart - @type = type - - process_message - end - - private - - def process_message - check_country - check_destination_number - check_multipart - check_encoding - send_message - end - - def check_country - @country = Country.find_by(alpha_2_code: @country.upcase) - - raise ActiveRecord::RecordNotFound, 'Country can not be found!' unless @country - end - - def check_destination_number - valid_number = TelephoneNumber.valid?(@to_number, @country.alpha_2_code.to_sym, [:mobile]) - - raise InvalidPhoneNumberError unless valid_number - - parser = TelephoneNumber.parse(@to_number, @country.alpha_2_code.to_sym) - @to_number = parser.country.country_code + parser.formatter.normalized_number - end - - def check_multipart - encoding = SmsTools::EncodingDetection.new(@message) - - raise ConcatenationError if @multipart.eql?(false) && encoding.concatenated? - raise ConcatenationError if @multipart.eql?(false) && !@country.sms_concatenation - end - - def check_encoding - encoding = SmsTools::EncodingDetection.new(@message).encoding.to_s - encoding = 'text' if encoding.eql?('gsm') || encoding.eql?('ascii') - - raise EncodingMismatchError unless encoding == @type - raise UnicodeSupportError if encoding.eql?('unicode') && !@country.sms_unicode - end - - def send_message - response = NEXMO_CLIENT.sms.send( - from: Tenant.credentials.dig(:nexmo, :from), - to: @to_number, - type: @type, - text: @message - ).messages.first - - log_or_notify_admin(response) - end - end -end diff --git a/app/services/xokul/kps/address.rb b/app/services/xokul/kps/address.rb index e94ac6096..aef878b6c 100644 --- a/app/services/xokul/kps/address.rb +++ b/app/services/xokul/kps/address.rb @@ -7,8 +7,9 @@ class Address def initialize(id_number) @response = Connection.request( - '/kps/queries/addresses', params: { id_number: id_number } - ) + '/kps/queries/addresses', + params: { id_number: id_number } + ) || {} end def full_informations @@ -31,7 +32,7 @@ def model_data private def current_address - @response[:current_address] + @response[:current_address] || {} end def district diff --git a/app/services/xokul/kps/identity.rb b/app/services/xokul/kps/identity.rb index 0d9144d36..2d297501a 100644 --- a/app/services/xokul/kps/identity.rb +++ b/app/services/xokul/kps/identity.rb @@ -7,8 +7,9 @@ class Identity def initialize(id_number) @response = Connection.request( - '/kps/queries/identities', params: { id_number: id_number } - ) + '/kps/queries/identities', + params: { id_number: id_number } + ) || {} end def full_informations diff --git a/app/services/xokul/ubs/statistic/student.rb b/app/services/xokul/ubs/statistic/student.rb index 011f9e16b..df56b6987 100644 --- a/app/services/xokul/ubs/statistic/student.rb +++ b/app/services/xokul/ubs/statistic/student.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Xokul - module Ubs + module UBS module Statistic module Student NAMESPACE = '/ubs/api/students/statistics' @@ -9,38 +9,47 @@ module Student module_function def by_genders_and_degree - Connection.request("#{NAMESPACE}/genders_and_degree?locale=#{I18n.locale}") + request("#{NAMESPACE}/genders_and_degree?locale=#{I18n.locale}") end def by_genders(schema: {}) transform_keys( - Connection.request("#{NAMESPACE}/genders?locale=#{I18n.locale}"), schema + request("#{NAMESPACE}/genders?locale=#{I18n.locale}"), schema ) end def by_cities(schema: {}) transform_keys( - Connection.request("#{NAMESPACE}/cities?locale=#{I18n.locale}"), schema + request("#{NAMESPACE}/cities?locale=#{I18n.locale}"), schema ) end def by_units - Connection.request("#{NAMESPACE}/units?locale=#{I18n.locale}") + request("#{NAMESPACE}/units?locale=#{I18n.locale}") end def non_graduates - Connection.request("#{NAMESPACE}/non_graduates?locale=#{I18n.locale}") + request("#{NAMESPACE}/non_graduates?locale=#{I18n.locale}") end def double_major_and_minor(schema: {}) transform_keys( - Connection.request("#{NAMESPACE}/double_major_and_minor?locale=#{I18n.locale}"), schema + request("#{NAMESPACE}/double_major_and_minor?locale=#{I18n.locale}"), schema ) end def transform_keys(results, schema) results.map { |item| item.transform_keys { |key| schema.fetch(key, key) } } end + + def request(path) + response = Connection.request(path) + response || raise(Support::RestClient::HTTPError) + rescue Net::HTTPExceptions => e + raise Support::RestClient::HTTPError, e + end + + private_class_method :request end end end diff --git a/app/validators/learning_outcome_validator.rb b/app/validators/learning_outcome_validator.rb new file mode 100644 index 000000000..0925e0c26 --- /dev/null +++ b/app/validators/learning_outcome_validator.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +class LearningOutcomeValidator < ActiveModel::Validator + def validate(record) + @record = record + @micros = record.micros + @codes = @micros.map(&:code).push(record.code) + return if codes_unique? + + add_error_message_to_invalid_records + @record.errors.add(:base, message('invalid_code')) + end + + def codes_unique? + @codes.uniq.size == @codes.size + end + + def add_error_message_to_invalid_records + invalid_codes = @codes.select { |code| @codes.count(code) > 1 }.uniq + + @micros.each do |learning_outcome| + learning_outcome.errors.add(:code, message('must_be_uniq')) if invalid_codes.include?(learning_outcome.code) + end + + @record.errors.add(:code, message('must_be_uniq')) if invalid_codes.include?(@record.code) + end + + private + + def message(key) + I18n.t(key, scope: %i[validators learning_outcome]) + end +end diff --git a/app/validators/unit_accreditation_standard_validator.rb b/app/validators/unit_accreditation_standard_validator.rb new file mode 100644 index 000000000..4ed8d4a93 --- /dev/null +++ b/app/validators/unit_accreditation_standard_validator.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class UnitAccreditationStandardValidator < ActiveModel::Validator + def validate(record) + @record = record + @accreditation_standard = record.accreditation_standard + return unless @accreditation_standard.active? + return unless already_exists? + + @accreditation_standard.errors.add( + :base, + I18n.t('defined', name: @record&.unit&.name, scope: %i[validators unit_accreditation_standard]) + ) + end + + def already_exists? + UnitAccreditationStandard.includes(:accreditation_standard) + .where.not(id: @record.id) + .where(unit: @record.unit_id, accreditation_standards: { status: :active }).exists? + end +end diff --git a/app/validators/unit_tuition_validator.rb b/app/validators/unit_tuition_validator.rb new file mode 100644 index 000000000..47a6cff73 --- /dev/null +++ b/app/validators/unit_tuition_validator.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class UnitTuitionValidator < ActiveModel::Validator + def validate(record) + @record = record + @tuition = record.tuition + return unless already_exists? + + @tuition.errors.add( + :base, + I18n.t('defined', name: @record&.unit&.name, scope: %i[validators unit_tuition]) + ) + end + + def already_exists? + UnitTuition.includes(tuition: :academic_term) + .where.not(id: @record.id) + .where(unit: @record.unit_id, tuitions: { academic_term_id: @tuition&.academic_term_id }).exists? + end +end diff --git a/app/views/account/profile/index.html.erb b/app/views/account/profile/index.html.erb index 81306f45b..fbea4f84d 100644 --- a/app/views/account/profile/index.html.erb +++ b/app/views/account/profile/index.html.erb @@ -101,21 +101,21 @@