Commit 57bbfe52 authored by Kaspar Vollenweider's avatar Kaspar Vollenweider 👻
Browse files

Merge branch 'features/client_beenden' into 'develop'

Features/client beenden

See merge request !467
parents 3ab62797 c0a5c71d
Pipeline #13964 passed with stage
in 22 minutes and 36 seconds
......@@ -3,15 +3,14 @@ class ClientsController < ApplicationController
include NestedAttributes
include ContactAttributes
before_action :set_client, only: [:show, :edit, :update, :destroy]
before_action :set_social_worker_collection
before_action :set_client, only: [:show, :edit, :update, :set_terminated]
before_action :set_social_worker_collection, only: [:new, :edit]
def index
authorize Client
@q = policy_scope(Client).ransack(params[:q])
@q = policy_scope(Client).ransack(default_filter)
@q.sorts = ['created_at desc'] if @q.sorts.empty?
@clients = @q.result
activity_filter
respond_to do |format|
format.xlsx
format.html do
......@@ -47,34 +46,65 @@ class ClientsController < ApplicationController
@client.involved_authority ||= current_user if current_user.social_worker?
authorize @client
if @client.save
if @client.user.social_worker? && ClientNotification.active.any?
redirect_to @client, notice: ClientNotification.active.pluck(:body).to_sentence
else
redirect_to @client, make_notice
end
redirect_to @client, create_success_notice
else
render :new
end
end
def update
if @client.update(client_params)
redirect_to @client, make_notice
@client.assign_attributes(client_params)
@client.resigned_by = current_user if @client.will_save_change_to_acceptance?(to: :resigned)
if @client.save
redirect_to @client, create_update_redirect_notice
else
@custom_notice = resigned_fail_notice if @client.errors.messages[:acceptance].present?
render :edit
end
end
def destroy
@client.destroy
redirect_to clients_url, make_notice
def set_terminated
if @client.update(acceptance: :resigned, resigned_by: current_user)
redirect_back(fallback_location: client_path(@client),
notice: 'Klient/in wurde erfolgreich beendet.')
else
redirect_back(fallback_location: client_path(@client), notice: resigned_fail_notice)
end
end
private
def activity_filter
return unless params[:q] && params[:q][:active_eq]
@clients = params[:q][:active_eq] == 'true' ? @clients.active : @clients.inactive
def create_success_notice
if @client.user.social_worker? && ClientNotification.active.any?
{ notice: ClientNotification.active.pluck(:body).to_sentence }
else
make_notice
end
end
def create_update_redirect_notice
if @client.saved_change_to_acceptance?(to: :resigned)
{ notice: 'Klient/in wurde erfolgreich beendet.' }
else
make_notice
end
end
def resigned_fail_notice
{
message: 'Beenden fehlgeschlagen.', model_message: @client.errors.messages[:acceptance].first,
action_link: { text: 'Begleitung bearbeiten', path: edit_assignment_path(@client.assignment) }
}
end
def default_filter
return { acceptance_not_eq: 2 } if params[:q].blank?
filters = params.to_unsafe_hash[:q]
if filters[:acceptance_eq].present? || filters[:contact_full_name_cont].present?
filters.except(:acceptance_not_eq)
else
filters.merge(acceptance_not_eq: 2)
end
end
def set_client
......
module NotificationHelper
def notification_warning_bubble(text)
tag.div(class: 'alert alert-warning alert-dismissible', role: 'alert') do
concat notification_close_button
concat text
end
end
def notification_close_button(text = nil)
tag.button(
class: 'close', aria: { label: 'Schliessen' }, data: { dismiss: 'alert' }, type: 'button'
) do
if text.present?
concat text
else
tag.span('&times;', aria: { hidden: 'true' })
end
end
end
end
......@@ -5,6 +5,8 @@ class Client < ApplicationRecord
include ZuerichScopes
include ImportRelation
before_update :record_acceptance_change, if: :going_to_change_to_resigned?
enum acceptance: { accepted: 0, rejected: 1, resigned: 2 }
enum cost_unit: { city: 0, municipality: 1, canton: 2 }
......@@ -15,6 +17,7 @@ class Client < ApplicationRecord
belongs_to :user, -> { with_deleted }
belongs_to :involved_authority, class_name: 'User', optional: true
belongs_to :resigned_by, class_name: 'User', optional: true
has_one :assignment, dependent: :destroy
has_many :assignment_logs
......@@ -33,14 +36,18 @@ class Client < ApplicationRecord
validates :salutation, presence: true
scope :need_accompanying, lambda {
inactive.order(created_at: :asc)
}
validates :acceptance, exclusion: {
in: ['resigned'],
message: 'Klient/in kann nicht beendet werden, solange noch ein laufendes Tandem existiert.'
}, unless: :terminatable?
scope :with_assignment, (-> { joins(:assignment) })
scope :with_active_assignment, (-> { with_assignment.merge(Assignment.active) })
scope :need_accompanying, lambda {
inactive.order(created_at: :asc)
}
scope :with_active_assignment_between, lambda { |start_date, end_date|
with_assignment.merge(Assignment.active_between(start_date, end_date))
}
......@@ -62,6 +69,18 @@ class Client < ApplicationRecord
accepted.without_assignment.or(with_inactive_assignment)
}
def terminatable?
assignment.blank? || assignment.no_period? || assignment.ended?
end
def self.acceptences_restricted
acceptances.except('resigned')
end
def self.acceptance_collection_restricted
acceptences_restricted.keys.map(&:to_sym)
end
def self.acceptance_collection
acceptances.keys.map(&:to_sym)
end
......@@ -88,4 +107,19 @@ class Client < ApplicationRecord
def german_missing?
language_skills.german.blank?
end
# allow ransack to use defined scopes
def self.ransackable_scopes(auth_object = nil)
['active', 'inactive']
end
private
def going_to_change_to_resigned?
will_save_change_to_acceptance?(to: 'resigned')
end
def record_acceptance_change
self.resigned_on = Time.zone.today
end
end
......@@ -30,7 +30,9 @@ module GroupAssignmentAndAssignmentCommon
scope :start_after, ->(date) { where("#{model_name.plural}.period_start > ?", date) }
scope :start_at_or_after, ->(date) { where("#{model_name.plural}.period_start >= ?", date) }
scope :start_within, ->(date_range) { where(period_start: date_range) }
scope :started_six_months_ago, (-> { where("#{model_name.plural}.period_start < ?", 6.months.ago) })
scope :started_six_months_ago, lambda {
where("#{model_name.plural}.period_start < ?", 6.months.ago)
}
scope :started_ca_six_weeks_ago, lambda {
start_at_or_after(8.weeks.ago).start_at_or_before(6.weeks.ago)
}
......@@ -103,12 +105,16 @@ module GroupAssignmentAndAssignmentCommon
period_start.present? && period_start <= Time.zone.today
end
def ending?
period_start.present? && period_end.present?
end
def ended?
ending? && period_end <= Time.zone.today
end
def ending?
period_start.present? && period_end.present?
def no_period?
period_start.blank? && period_end.blank?
end
end
end
......@@ -49,6 +49,8 @@ class User < ApplicationRecord
has_many :group_offer_terminations_verified, class_name: 'GroupOffer',
foreign_key: 'termination_verified_by_id', inverse_of: 'termination_verified_by'
has_many :resigned_clients, class_name: 'Client', foreign_key: 'resigned_by_id'
# Mailing process done relation
has_many :process_submitted_by, class_name: 'ReminderMailingVolunteer'
......
......@@ -84,8 +84,12 @@ class ApplicationPolicy
superadmin? || user_owns_record?
end
def department_managers_record?
department_manager? && user_owns_record?
end
def superadmin_or_department_managers_record?
superadmin? || department_manager? && user_owns_record?
superadmin? || department_managers_record?
end
def superadmin_or_department_managers_registration?
......
......@@ -15,9 +15,16 @@ class ClientPolicy < ApplicationPolicy
alias_method :show?, :superadmin_or_department_manager_or_social_worker?
alias_method :edit?, :superadmin_or_record_owner?
alias_method :update?, :superadmin_or_record_owner?
alias_method :termination?, :superadmin_or_department_managers_record?
alias_method :destroy?, :superadmin?
alias_method :set_terminated?, :superadmin_or_department_managers_record?
# suplementary policies
alias_method :superadmin_privileges?, :superadmin?
def acceptance_collection
if superadmin_or_department_managers_record?
Client.acceptance_collection
else
Client.acceptance_collection_restricted
end
end
end
- if notice.present? || alert.present?
.alert.alert-warning.alert-dismissible role='alert'
button.close aria-label='Close' data-dismiss='alert' type='button'
span aria-hidden='true' &times;
= notice || alert
- if alert.present?
= notification_warning_bubble(alert)
- elsif notice.present?
- if notice.is_a? String
= notification_warning_bubble(notice)
- elsif notice.is_a? Hash
= render 'notification_alert_with_action', notice: notice
- elsif @custom_notice.present?
- @custom_notice[:action_link].stringify_keys!
= render 'notification_alert_with_action', notice: @custom_notice.stringify_keys
.alert.alert-danger.alert-dismissible role='alert'
= notification_close_button
h4= notice['message']
- if notice['model_message'].present?
p= notice['model_message']
p
= notification_close_button('Abbrechen')
- if notice['action_link'].present?
= link_to(notice['action_link']['text'], notice['action_link']['path'], target: '_blank', class: 'btn btn-default')
......@@ -22,4 +22,9 @@ tr
td.button-acceptance = link_to t(".acceptance.#{client.acceptance}"), '#',
class: "btn btn-xs btn-acceptance-#{client.acceptance}"
td= client.comments
= render 'index_actions', subject: client
td.index-action-cell.hidden-print
= link_to t_action(:show), client_path(client)
- if policy(client).edit?
= link_to t_action(:edit), edit_client_path(client)
- if policy(client).set_terminated?
= link_to 'Beenden', set_terminated_client_path(client), method: :patch, data: { confirm: 'Klient/in wirklich beenden?' }
......@@ -47,8 +47,9 @@
fieldset
legend Interne Kriterien
- if policy(@client).set_terminated?
= f.input :acceptance, collection: policy(@client).acceptance_collection, include_blank: false
- if policy(Client).superadmin_privileges?
= f.input :acceptance, collection: Client.acceptance_collection, include_blank: false
= f.association :involved_authority, collection: @social_workers
= f.input :competent_authority
......
......@@ -12,7 +12,9 @@ nav.navbar.section-navigation.hidden-print
= list_filter_dropdown(:salutation, Client::SALUTATIONS)
- if policy(Client).superadmin_privileges?
= enum_filter_dropdown(:acceptance, Client.acceptances)
= boolean_toggler_filter_dropdown(:active, 'Tandem', 'Aktiv', 'Inaktiv')
= custom_filter_dropdown('Tandem',
{ q: :active, text: 'Aktiv', value: 'true' },
{ q: :inactive, text: 'Inktiv', value: 'true' })
li= button_link t('clear_filters'), clients_path, dimension: :sm
li= button_link navigation_fa_icon(:xlsx), url_for(format: :xlsx, q: search_parameters), dimension: :sm
li= link_to navigation_glyph(:print), url_for(print: :true, q: search_parameters), { class: 'btn btn-default btn-sm', target: '_blank' }
......
nav.navbar.section-navigation
ul.list-inline
- if policy(Client).superadmin_privileges?
li.button-acceptance = link_to t(".acceptance.#{@client.acceptance}"), '#',
li.button-acceptance = link_to t_attr(@client.acceptance, Client), '#',
class: "btn btn-acceptance-#{@client.acceptance}"
- if policy(@client).edit?
li= button_link t_action(:edit), edit_client_path(@client)
li= button_link navigation_glyph(:print), client_path(@client, print: true)
- if policy(Client).destroy?
li= link_to navigation_glyph(:delete), @client, confirm_deleting(@client, 'btn btn-default')
- if !@client.resigned? && policy(@client).set_terminated?
li= link_to 'Beenden', set_terminated_client_path(@client), class: 'btn btn-default', method: :patch, data: { confirm: 'Klient/in wirklich beenden?' }
li= button_link navigation_glyph(:back), clients_path
- if policy(Journal).index?
ul.list-inline.pull-right
......
......@@ -56,8 +56,9 @@ Rails.application.routes.draw do
resources :assignments, concerns: [:update_submitted_at, :search, :termination_actions]
resources :client_notifications, :departments, :performance_reports, :email_templates, :users
resources :clients, concerns: :search do
resources :clients, except: [:destroy], concerns: :search do
resources :journals, except: [:show]
patch :set_terminated, on: :member
end
resources :feedbacks, only: [:new, :create]
......
class AddTerminationFieldsToClient < ActiveRecord::Migration[5.1]
def change
change_table :clients do |t|
t.date :resigned_on
t.references :resigned_by, references: :users, index: true
end
end
end
......@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20180126101119) do
ActiveRecord::Schema.define(version: 20180130140401) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......@@ -166,8 +166,11 @@ ActiveRecord::Schema.define(version: 20180126101119) do
t.integer "acceptance", default: 0
t.integer "cost_unit"
t.bigint "involved_authority_id"
t.date "resigned_on"
t.bigint "resigned_by_id"
t.index ["deleted_at"], name: "index_clients_on_deleted_at"
t.index ["involved_authority_id"], name: "index_clients_on_involved_authority_id"
t.index ["resigned_by_id"], name: "index_clients_on_resigned_by_id"
t.index ["user_id"], name: "index_clients_on_user_id"
end
......
......@@ -82,7 +82,7 @@ end
puts_model_counts('After Volunteer created', User, Volunteer, Client)
# Create clients for each acceptance type
Client.acceptance_collection.each do |acceptance|
Client.acceptance_collection_restricted.each do |acceptance|
FactoryBot.create(:client, acceptance: acceptance, user: User.superadmins.first)
end
puts_model_counts('After Client created', User, Volunteer, Client)
......
......@@ -7,54 +7,50 @@ class ClientActivityFilterTest < ActionDispatch::IntegrationTest
create :assignment_active, client: @client_accepted
@client_rejected = create :client, acceptance: 'rejected'
create :assignment_active, client: @client_rejected
@client_resigned = create :client, acceptance: 'resigned'
create :assignment_active, client: @client_resigned
@client_resigned = create :client, acceptance: 'accepted'
create :assignment_active, client: @client_resigned, period_end: 2.days.ago
@client_resigned.resigned!
login_as @superadmin
end
test 'client_tandem_active_filter_returns_accepted_active_client' do
get clients_path(q: { active_eq: 'true' })
get clients_path(q: { active: 'true' })
assert response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
end
test 'client_tandem_inactive_filter_returns_accepted_inactive_client' do
get clients_path(q: { active_eq: 'false' })
get clients_path(q: { inactive: 'true' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
end
test 'client_acceptance_tandem_filters_work_together' do
get clients_path(q: { acceptance_eq: '0', active_eq: 'true' })
get clients_path(q: { acceptance_eq: '0', active: 'true' })
assert response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
get clients_path(q: { acceptance_eq: '0', active_eq: 'false' })
get clients_path(q: { acceptance_eq: '0', inactive: 'true' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
get clients_path(q: { acceptance_eq: '1', active_eq: 'true' })
get clients_path(q: { acceptance_eq: '1', active: 'true' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
get clients_path(q: { acceptance_eq: '1', active_eq: 'false' })
get clients_path(q: { acceptance_eq: '1', inactive: 'true' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
get clients_path(q: { acceptance_eq: '2', active_eq: 'true' })
get clients_path(q: { acceptance_eq: '2' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
get clients_path(q: { acceptance_eq: '2', active_eq: 'false' })
refute response.body.include? @client_accepted.contact.full_name
refute response.body.include? @client_rejected.contact.full_name
refute response.body.include? @client_resigned.contact.full_name
assert response.body.include? @client_resigned.contact.full_name
end
end
require 'test_helper'
class ClientPolicyTest < PolicyAssertions::Test
def setup
@actions = ['index?', 'search?', 'new?', 'create?', 'show?', 'edit?', 'update?', 'termination?',
'destroy?', 'superadmin_privileges?']
end
test 'superadmin_can_use_all_actions' do
assert_permit(create(:user), Client, *@actions)
assert_permit(create(:user), Client, 'superadmin_privileges?', *actions_list.values)
end
test 'department_manager_has_limited_access' do
department_manager = create :department_manager, :with_clients
assert_permit(department_manager, Client, *@actions[0..4])
assert_permit(department_manager, Client.first, *@actions[5..7])
refute_permit(department_manager, create(:client), *@actions[5..7])
refute_permit(department_manager, Client, *@actions[-2..-1])
manager = create :department_manager, :with_clients
assert_permit(manager, manager.clients.first, *actions_list(:edit, :update, :set_terminated))
assert_permit(manager, Client, *actions_list(:index, :search, :new, :create, :show))
refute_permit(manager, create(:client), *actions_list(:edit, :update, :set_terminated))
refute_permit(manager, Client, 'superadmin_privileges?', *actions_list(:set_terminated))
end
test 'social_worker_has_limited_access' do
social_worker = create :social_worker, :with_clients
assert_permit(social_worker, Client, *@actions[0..4])
assert_permit(social_worker, Client.first, *@actions[5..6])
refute_permit(social_worker, create(:client), *@actions[5..6])
refute_permit(social_worker, Client, *@actions[-3..-1])
assert_permit(social_worker, Client, *actions_list(:index, :search, :new, :create, :show))
assert_permit(social_worker, social_worker.clients.first, *actions_list(:edit, :update))
refute_permit(social_worker, create(:client), *actions_list(:edit, :update, :set_terminated))
refute_permit(social_worker, Client, 'superadmin_privileges?', *actions_list(:set_terminated))
end
end
......@@ -40,7 +40,7 @@ class ClientsFilterDropdownsTest < ApplicationSystemTestCase
within 'tbody' do
assert page.has_text? @accepted_mrs_same_age_old
assert page.has_text? @accepted_mr_no_matter_age_old
assert page.has_text? @resigned_mrs_same_age_middle
refute page.has_text? @resigned_mrs_same_age_middle
assert page.has_text? @rejected_mr_no_matter_age_middle
end
end
......@@ -79,7 +79,7 @@ class ClientsFilterDropdownsTest < ApplicationSystemTestCase
within 'tbody' do
assert page.has_text? @accepted_mrs_same_age_old
assert page.has_text? @accepted_mr_no_matter_age_old
assert page.has_text? @resigned_mrs_same_age_middle
refute page.has_text? @resigned_mrs_same_age_middle
assert page.has_text? @rejected_mr_no_matter_age_middle
end
end
......@@ -123,7 +123,7 @@ class ClientsFilterDropdownsTest < ApplicationSystemTestCase
within 'tbody' do
assert page.has_text? @accepted_mrs_same_age_old
assert page.has_text? @accepted_mr_no_matter_age_old
assert page.has_text? @resigned_mrs_same_age_middle
refute page.has_text? @resigned_mrs_same_age_middle
assert page.has_text? @rejected_mr_no_matter_age_middle
end
end
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment