Skip to content

Commit

Permalink
5015 Draft Support //
Browse files Browse the repository at this point in the history
draft pr with lots of failing tests
  • Loading branch information
Sam Williams committed Nov 27, 2023
1 parent eee44f6 commit 923b3e8
Show file tree
Hide file tree
Showing 26 changed files with 724 additions and 284 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ gem "sprockets-rails" # The original asset pipeline for Rails [https://github.co
gem "stimulus-rails"
gem "strong_migrations"
gem "tzinfo-data", platforms: %i[mingw mswin x64_mingw jruby] # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem "wicked"
gem "rswag-api"
gem "rswag-ui"
gem "blueprinter" # for JSON serialization
Expand Down
5 changes: 4 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,8 @@ GEM
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wicked (2.0.0)
railties (>= 3.0.7)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.6.12)
Expand Down Expand Up @@ -604,9 +606,10 @@ DEPENDENCIES
view_component
web-console (>= 3.3.0)
webmock
wicked

RUBY VERSION
ruby 3.2.2p53

BUNDLED WITH
2.4.19
2.4.22
232 changes: 118 additions & 114 deletions app/controllers/case_contacts_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# frozen_string_literal: true

class CaseContactsController < ApplicationController
before_action :set_case_contact, only: %i[edit update destroy]
include Wicked::Wizard

before_action :set_contact_types, only: %i[new edit update create]
before_action :require_organization!
after_action :verify_authorized
before_action :set_progress, only: %i[show update]

steps :base_info, :details, :travel, :notes

def index
authorize CaseContact
Expand All @@ -20,71 +24,45 @@ def index
) || return

case_contacts = @filterrific.find.group_by(&:casa_case_id)
@drafts_count = case_contact_drafts.count

@presenter = CaseContactPresenter.new(case_contacts)
end

def drafts
authorize CaseContact

@case_contacts = case_contact_drafts
end

# wizard_path
def show
@case_contact = CaseContact.find(params[:case_contact_id])
authorize @case_contact
get_cases_and_contact_types

render_wizard
end

def new
store_referring_location
authorize CaseContact
@casa_cases = policy_scope(current_organization.casa_cases)

# Select the most likely case option
# - If there are cases defined in the params, select those cases (often coming from the case page)
# - If there is only one case, select that case
# - If there are no hints, let them select their case
@selected_cases =
casa_cases = policy_scope(current_organization.casa_cases)
draft_case_ids =
if params.dig(:case_contact, :casa_case_id).present?
@casa_cases.where(id: params.dig(:case_contact, :casa_case_id))
elsif @casa_cases.count == 1
@casa_cases[0, 1]
casa_cases.where(id: params.dig(:case_contact, :casa_case_id)).pluck(:id)
elsif casa_cases.count == 1
casa_cases.first.id
else
[]
end

@case_contact = CaseContact.new

@selected_case_contact_types = @casa_cases.flat_map(&:contact_types)

@current_organization_groups =
if @selected_case_contact_types.present?
@selected_case_contact_types.map(&:contact_type_group).uniq
else
current_organization
.contact_type_groups
.joins(:contact_types)
.where(contact_types: {active: true})
.alphabetically
.uniq
end
end

def create
# These variables are used to re-render the form (render :new) if there are
# validation errors so that the user does not lose inputs to fields that
# they did previously enter.

@casa_cases = policy_scope(current_organization.casa_cases)
@case_contact = CaseContact.new(create_case_contact_params.except(:casa_case_attributes))
authorize @case_contact
@current_organization_groups = current_organization.contact_type_groups

@selected_cases = @casa_cases.where(id: params.dig(:case_contact, :casa_case_id))
if @selected_cases.empty?
flash[:alert] = "At least one case must be selected"
render :new
return
end
# Create a case contact for every case that was checked
case_contacts = create_case_contact_for_every_selected_casa_case(@selected_cases)
if case_contacts.any?(&:new_record?)
@case_contact = case_contacts.first
@casa_cases = [@case_contact.casa_case]
render :new
else
flash[:notice] = "Case #{"contact".pluralize(@selected_cases.count)} successfully created"
redirect_back_to_referer(fallback_location: case_contacts_path(success: true))
end
@case_contact = CaseContact.create!(creator: current_user, draft_case_ids: draft_case_ids)
redirect_to wizard_path(steps.first, case_contact_id: @case_contact.id)
end

def edit
Expand All @@ -96,25 +74,18 @@ def edit
end

def update
@case_contact = CaseContact.find(params[:case_contact_id])
authorize @case_contact
@casa_cases = [@case_contact.casa_case]
@selected_cases = @casa_cases
@current_organization_groups = current_organization.contact_type_groups

if @case_contact.update_cleaning_contact_types(update_case_contact_params)
if additional_expense_params&.any? && policy(:case_contact).additional_expenses_allowed?
update_or_create_additional_expense(additional_expense_params, @case_contact)
end
if @case_contact.valid?
created_at = @case_contact.created_at.strftime("%-I:%-M %p on %m-%e-%Y")
flash[:notice] = "Case contact created at #{created_at}, was successfully updated."
send_reimbursement_email(@case_contact)
redirect_to casa_case_path(@case_contact.casa_case)
else
render :edit
params[:case_contact][:status] = step.to_s unless @case_contact.active?
remove_unwanted_contact_types
if @case_contact.update(case_contact_params)
if params[:complete]
finish_editing
end
render_wizard @case_contact, {}, { case_contact_id: @case_contact.id } if step != steps.last
else
render :edit
get_cases_and_contact_types
render step
end
end

Expand All @@ -137,6 +108,41 @@ def restore

private

def get_cases_and_contact_types
@casa_cases = policy_scope(current_organization.casa_cases)
@casa_cases = @casa_cases.where(id: @case_contact.casa_case_id) if @case_contact.active?

@selected_case_contact_types = @casa_cases.flat_map(&:contact_types)

@current_organization_groups =
if @selected_case_contact_types.present?
@selected_case_contact_types.map(&:contact_type_group).uniq
else
current_organization
.contact_type_groups
.joins(:contact_types)
.where(contact_types: {active: true})
.alphabetically
.uniq
end
end

def finish_editing
message = ""
if @case_contact.active?
message = "Case contact successfully updated"
else
message = "Case #{"contact".pluralize(@case_contact.draft_case_ids.count)} successfully created"
create_additional_case_contacts(@case_contact)
first_casa_case_id = @case_contact.draft_case_ids.slice(0)
@case_contact.update!(status: "active", draft_case_ids: [first_casa_case_id], casa_case_id: first_casa_case_id)
end
update_volunteer_address(@case_contact.creator, @case_contact.volunteer_address)
send_reimbursement_email(@case_contact)
flash[:notice] = message
redirect_back_to_referer(fallback_location: case_contacts_path(success: true))
end

def update_or_create_additional_expense(all_ae_params, cc)
all_ae_params.each do |ae_params|
id = ae_params[:id]
Expand All @@ -151,37 +157,28 @@ def update_or_create_additional_expense(all_ae_params, cc)
end
end

def save_or_add_error(obj, case_contact)
obj.valid? ? obj.save : case_contact.errors.add(:base, obj.errors.full_messages.to_sentence)
end

def create_case_contact_for_every_selected_casa_case(selected_cases)
selected_cases.map do |casa_case|
if policy(:case_contact).additional_expenses_allowed?
new_cc = casa_case.case_contacts.new(create_case_contact_params.except(:casa_case_attributes))
update_or_create_additional_expense(additional_expense_params, new_cc)
if new_cc.valid?
new_cc.save!
else
new_cc.errors
end
else
new_cc = casa_case.case_contacts.create(create_case_contact_params.except(:casa_case_attributes))
# Makes a copy of the draft for all selected cases not including the first one. The draft becomes the contact for
# the first case.
#
# Duplication does not duplicate associated records, so if other associations are made in the form, they need to be
# added here, explicitly (ie. case_contact_contact_type, additional_expenses). Alternatively, could look at a gem
# that does deep associations.
def create_additional_case_contacts(case_contact)
case_contact.draft_case_ids.drop(1).each do |casa_case_id|
new_case_contact = case_contact.dup
new_case_contact.status = "active"
new_case_contact.draft_case_ids = [casa_case_id]
new_case_contact.casa_case_id = casa_case_id
case_contact.case_contact_contact_type.each do |ccct|
new_case_contact.case_contact_contact_type.new(contact_type_id: ccct.contact_type_id)
end

case_contact = @case_contact.dup

send_reimbursement_email(case_contact)

case_contact.casa_case = casa_case
if @selected_cases.count == 1 && case_contact.valid?
if current_role == "Volunteer"
update_volunteer_address
elsif ["Supervisor", "Casa Admin"].include?(current_role) && casa_case.volunteers.count == 1
update_volunteer_address(casa_case.volunteers[0])
end
case_contact.additional_expenses.each do |ae|
new_case_contact.additional_expenses.new(
other_expense_amount: ae.other_expense_amount,
other_expenses_describe: ae.other_expenses_describe
)
end
new_cc
new_case_contact.save!
end
end

Expand All @@ -191,35 +188,22 @@ def send_reimbursement_email(case_contact)
end
end

def update_volunteer_address(volunteer = current_user)
content = create_case_contact_params.dig(:casa_case_attributes, :volunteers_attributes, "0", :address_attributes, :content)
return if content.blank?
def update_volunteer_address(volunteer, address)
return unless address

if volunteer.address
volunteer.address.update!(content: content)
volunteer.address.update(content: address)
else
volunteer.address = Address.new(content: content)
volunteer.address = Address.new(content: address)
volunteer.save!
end
end

def set_case_contact
if current_organization.case_contacts.exists?(params[:id])
@case_contact = authorize(current_organization.case_contacts.find(params[:id]))
else
redirect_to authenticated_user_root_path
end
end

def set_contact_types
@contact_types = ContactType.for_organization(current_organization)
end

def create_case_contact_params
CaseContactParameters.new(params, creator: current_user)
end

def update_case_contact_params
# Updating a case contact should not change its original creator
def case_contact_params
CaseContactParameters.new(params)
end

Expand All @@ -242,4 +226,24 @@ def all_case_contacts
def additional_expense_params
@additional_expense_params ||= AdditionalExpenseParamsService.new(params).calculate
end

# Deletes the current associations (from the join table) only if the submitted form body has the parameters for
# the contact_type ids.
def remove_unwanted_contact_types
if params.dig(:case_contact, :case_contact_contact_type_attributes)
@case_contact.case_contact_contact_type.destroy_all
end
end

def set_progress
@progress = if wizard_steps.any? && wizard_steps.index(step).present?
((wizard_steps.index(step) + 1).to_d / wizard_steps.count.to_d) * 100
else
0
end
end

def case_contact_drafts
CaseContact.where(creator: current_user).where.not(status: "active")
end
end
29 changes: 21 additions & 8 deletions app/decorators/case_contact_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,21 @@ class CaseContactDecorator < Draper::Decorator
def duration_minutes
minutes = object.duration_minutes

return "#{minutes} minutes" if minutes <= 60

formatted_hour_value = minutes / 60
formatted_minutes_value = minutes.remainder(60)

if formatted_minutes_value.zero?
"#{formatted_hour_value} #{"hour".pluralize(formatted_hour_value)}"
str = if !minutes
"Duration not set"
elsif minutes <= 60
"#{minutes} minutes"
else
"#{formatted_hour_value} #{"hour".pluralize(formatted_hour_value)} #{formatted_minutes_value} minutes"
formatted_hour_value = minutes / 60
formatted_minutes_value = minutes.remainder(60)

if formatted_minutes_value.zero?
"#{formatted_hour_value} #{"hour".pluralize(formatted_hour_value)}"
else
"#{formatted_hour_value} #{"hour".pluralize(formatted_hour_value)} #{formatted_minutes_value} minutes"
end
end
str
end

def report_duration_minutes
Expand Down Expand Up @@ -100,4 +105,12 @@ def show_contact_type?(contact_type_id)
def additional_expenses_count
object.additional_expenses.any? ? object.additional_expenses.length : 0
end

def creator_address
volunteer_address || creator.address&.content
end

def draft_state
active? ? "Editing" : "New"
end
end
2 changes: 1 addition & 1 deletion app/javascript/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require('bootstrap-datepicker')
require('./src/add_additional_expense')
require('./src/add_to_calendar_button')
require('./src/case_contact')
require('./src/case_contact_autosave')
// require('./src/case_contact_autosave')
require('./src/case_emancipation')
require('./src/casa_case')
require('./src/new_casa_case')
Expand Down
Loading

0 comments on commit 923b3e8

Please sign in to comment.