diff --git a/app/aggregates/deciding.rb b/app/aggregates/deciding.rb new file mode 100644 index 000000000..144bb8102 --- /dev/null +++ b/app/aggregates/deciding.rb @@ -0,0 +1,26 @@ +module Deciding + class DecisionNotFound < StandardError; end + class ApplicationNotAssignedToUser < StandardError; end + + class DraftCreated < RailsEventStore::Event; end + class InterestsOfJusticeSet < RailsEventStore::Event; end + class FundingDecisionSet < RailsEventStore::Event; end + + class << self + def stream_name(decision_id) + "Deciding$#{decision_id}" + end + end + + class Configuration + def call(event_store) + event_store.subscribe( + DecisionHandler, to: [Reviewing::AddDecision] + ) + end + end + + class DecisionHandler + def call(event); end + end +end diff --git a/app/aggregates/deciding/command.rb b/app/aggregates/deciding/command.rb new file mode 100644 index 000000000..198bfd8ce --- /dev/null +++ b/app/aggregates/deciding/command.rb @@ -0,0 +1,31 @@ +module Deciding + class Command < Dry::Struct + attribute :decision_id, Types::Uuid + + def with_decision(&block) + repository.with_aggregate( + Decision.new(decision_id), + stream_name, + &block + ) + end + + private + + def repository + @repository ||= AggregateRoot::Repository.new( + Rails.configuration.event_store + ) + end + + def stream_name + Deciding.stream_name(decision_id) + end + + class << self + def call(args) + new(args).call + end + end + end +end diff --git a/app/aggregates/deciding/commands/create_draft.rb b/app/aggregates/deciding/commands/create_draft.rb new file mode 100644 index 000000000..fb1a1afd2 --- /dev/null +++ b/app/aggregates/deciding/commands/create_draft.rb @@ -0,0 +1,12 @@ +module Deciding + class CreateDraft < Command + attribute :application_id, Types::Uuid + attribute :user_id, Types::Uuid + + def call + with_decision do |decision| + decision.create_draft(user_id:, application_id:) + end + end + end +end diff --git a/app/aggregates/deciding/commands/load_decision.rb b/app/aggregates/deciding/commands/load_decision.rb new file mode 100644 index 000000000..df336909e --- /dev/null +++ b/app/aggregates/deciding/commands/load_decision.rb @@ -0,0 +1,7 @@ +module Deciding + class LoadDecision < Command + def call + repository.load(Decision.new(decision_id), stream_name) + end + end +end diff --git a/app/aggregates/deciding/commands/set_funding_decision.rb b/app/aggregates/deciding/commands/set_funding_decision.rb new file mode 100644 index 000000000..190d8f5c3 --- /dev/null +++ b/app/aggregates/deciding/commands/set_funding_decision.rb @@ -0,0 +1,13 @@ +module Deciding + class SetFundingDecision < Command + attribute :user_id, Types::Uuid + attribute :result, Types::FundingDecisionResult + attribute :details, Types::String + + def call + with_decision do |decision| + decision.set_funding_decision(user_id:, result:, details:) + end + end + end +end diff --git a/app/aggregates/deciding/commands/set_interests_of_justice.rb b/app/aggregates/deciding/commands/set_interests_of_justice.rb new file mode 100644 index 000000000..1709b71bb --- /dev/null +++ b/app/aggregates/deciding/commands/set_interests_of_justice.rb @@ -0,0 +1,12 @@ +module Deciding + class SetInterestsOfJustice < Command + attribute :user_id, Types::Uuid + attribute :interests_of_justice, Types::InterestsOfJusticeDecision + + def call + with_decision do |decision| + decision.set_interests_of_justice(user_id:, interests_of_justice:) + end + end + end +end diff --git a/app/aggregates/deciding/decision.rb b/app/aggregates/deciding/decision.rb new file mode 100644 index 000000000..537e082c6 --- /dev/null +++ b/app/aggregates/deciding/decision.rb @@ -0,0 +1,54 @@ +module Deciding + class Decision + include AggregateRoot + + def initialize(decision_id) + @decision_id = decision_id + @interests_of_justice = nil + end + + attr_accessor :application_id, :decision_id, :result, :details, :state + + def create_draft(user_id:, application_id:) + apply DraftCreated.new( + data: { decision_id:, application_id:, user_id: } + ) + end + + def set_interests_of_justice(user_id:, interests_of_justice:) + apply InterestsOfJusticeSet.new( + data: { decision_id:, application_id:, user_id:, interests_of_justice: } + ) + end + + def set_funding_decision(user_id:, result:, details:) + apply FundingDecisionSet.new( + data: { decision_id:, application_id:, user_id:, result:, details: } + ) + end + + on DraftCreated do |event| + @application_id = event.data.fetch(:application_id) + @state = Types::DecisionState[:draft] + end + + on InterestsOfJusticeSet do |event| + @interests_of_justice = event.data.fetch(:interests_of_justice) + end + + on FundingDecisionSet do |event| + @result = event.data.fetch(:result) + @details = event.data.fetch(:details) + end + + def interests_of_justice + return if @interests_of_justice.nil? + + Types::InterestsOfJusticeDecision[@interests_of_justice] + end + + def to_param + decision_id + end + end +end diff --git a/app/aggregates/reviewing.rb b/app/aggregates/reviewing.rb index e75eb17a4..8692bebea 100644 --- a/app/aggregates/reviewing.rb +++ b/app/aggregates/reviewing.rb @@ -15,6 +15,9 @@ class CannotMarkAsReadyWhenSentBack < Error; end class CannotSendBackWhenCompleted < Error; end class NotReceived < Error; end + class DecisionAdded < Event; end + class DecisionRemoved < Event; end + class << self def stream_name(application_id) "Reviewing$#{application_id}" diff --git a/app/aggregates/reviewing/available_reviewer_actions.rb b/app/aggregates/reviewing/available_reviewer_actions.rb index c08ca749b..e4cbd48e1 100644 --- a/app/aggregates/reviewing/available_reviewer_actions.rb +++ b/app/aggregates/reviewing/available_reviewer_actions.rb @@ -6,8 +6,7 @@ class AvailableReviewerActions marked_as_ready: [:complete, :send_back], }, non_means: { - open: [:complete, :send_back], - marked_as_ready: [:complete, :send_back] # TODO: remove once all non-means in this state processed + open: FeatureFlags.adding_decisions.enabled? ? [:add_funding_decision, :send_back] : [:complete, :send_back], }, pse: { open: [:complete] diff --git a/app/aggregates/reviewing/command.rb b/app/aggregates/reviewing/command.rb index 829e1583f..c00e6bbe4 100644 --- a/app/aggregates/reviewing/command.rb +++ b/app/aggregates/reviewing/command.rb @@ -17,7 +17,7 @@ def repository end def stream_name - Reviewing.stream_name(application_id) + "Reviewing$#{application_id}" end class << self diff --git a/app/aggregates/reviewing/commands/add_decision.rb b/app/aggregates/reviewing/commands/add_decision.rb new file mode 100644 index 000000000..83cb234dd --- /dev/null +++ b/app/aggregates/reviewing/commands/add_decision.rb @@ -0,0 +1,13 @@ +module Reviewing + class AddDecision < Command + attribute :application_id, Types::Uuid + attribute :user_id, Types::Uuid + attribute :decision_id, Types::Uuid + + def call + with_review do |review| + review.add_decision(user_id:, decision_id:) + end + end + end +end diff --git a/app/aggregates/reviewing/commands/remove_decision.rb b/app/aggregates/reviewing/commands/remove_decision.rb new file mode 100644 index 000000000..3b3ef061d --- /dev/null +++ b/app/aggregates/reviewing/commands/remove_decision.rb @@ -0,0 +1,13 @@ +module Reviewing + class RemoveDecision < Command + attribute :application_id, Types::Uuid + attribute :user_id, Types::Uuid + attribute :decision_id, Types::Uuid + + def call + with_review do |review| + review.remove_decision(user_id:, decision_id:) + end + end + end +end diff --git a/app/aggregates/reviewing/review.rb b/app/aggregates/reviewing/review.rb index e4f3fae03..64184458f 100644 --- a/app/aggregates/reviewing/review.rb +++ b/app/aggregates/reviewing/review.rb @@ -4,22 +4,13 @@ class Review def initialize(id) @id = id - @application_type = nil - @state = nil - @return_reason = nil - @reviewer_id = nil - @reviewed_at = nil - @received_at = nil - @submitted_at = nil - @superseded_at = nil - @superseded_by = nil - @parent_id = nil - @work_stream = nil - end - - attr_reader :id, :state, :return_reason, :reviewed_at, :reviewer_id, - :submitted_at, :superseded_by, :superseded_at, :parent_id, - :work_stream, :application_type + @decision_ids = [] + end + + attr_accessor :state, :return_reason, :reviewed_at, :reviewer_id, :submitted_at, :superseded_by, + :superseded_at, :parent_id, :work_stream, :application_type + + attr_reader :id, :decision_ids alias application_id id @@ -68,6 +59,18 @@ def mark_as_ready(user_id:) ) end + def add_decision(user_id:, decision_id:) + apply DecisionAdded.new( + data: { application_id:, user_id:, decision_id: } + ) + end + + def remove_decision(user_id:, decision_id:) + apply DecisionRemoved.new( + data: { application_id:, user_id:, decision_id: } + ) + end + on ApplicationReceived do |event| @state = Types::ReviewState[:open] @received_at = event.timestamp @@ -77,6 +80,14 @@ def mark_as_ready(user_id:) @work_stream = event.data.fetch(:work_stream, Types::WorkStreamType['criminal_applications_team']) end + on DecisionAdded do |event| + @decision_ids << event.data.fetch(:decision_id) + end + + on DecisionRemoved do |event| + @decision_ids -= [event.data.fetch(:decision_id)] + end + on SentBack do |event| @state = Types::ReviewState[:sent_back] @return_reason = event.data.fetch(:reason, nil) @@ -108,15 +119,11 @@ def received? end def business_day - return nil unless @submitted_at - - BusinessDay.new(day_zero: @submitted_at) + BusinessDay.new(day_zero: @submitted_at) if @submitted_at.present? end def reviewed_on - return nil unless @reviewed_at - - @reviewed_at.in_time_zone('London').to_date + @reviewed_at.in_time_zone('London').to_date if @reviewed_at.present? end def available_reviewer_actions diff --git a/app/components/decision_component.rb b/app/components/decision_component.rb new file mode 100644 index 000000000..9363b116f --- /dev/null +++ b/app/components/decision_component.rb @@ -0,0 +1,82 @@ +class DecisionComponent < ViewComponent::Base + include ActionView::Helpers + include AppTextHelper + + def initialize(decision:, decision_iteration:) + @decision = decision + @decision_iteration = decision_iteration + + super + end + + def call + govuk_summary_card(title:, actions:) do |_card| + govuk_summary_list do |list| + if interests_of_justice.present? + list.with_row do |row| + row.with_key { label_text(:result, scope: [:decision, :interests_of_justice]) } + row.with_value { ioj_result } + end + + list.with_row do |row| + row.with_key { label_text(:details, scope: [:decision, :interests_of_justice]) } + row.with_value { simple_format(interests_of_justice[:details]) } + end + + list.with_row do |row| + row.with_key { label_text(:assessed_by, scope: [:decision, :interests_of_justice]) } + row.with_value { interests_of_justice[:assessed_by] } + end + + list.with_row do |row| + row.with_key { label_text(:assessed_on, scope: [:decision, :interests_of_justice]) } + row.with_value { l interests_of_justice[:assessed_on], format: :compact } + end + end + + list.with_row do |row| + row.with_key { label_text(:result, scope: [:decision]) } + row.with_value { render DecisionResultComponent.new(result: decision.result) } + end + + list.with_row do |row| + row.with_key { label_text(:details, scope: [:decision]) } + row.with_value { decision.details } + end + end + end + end + + private + + def ioj_result + t(interests_of_justice[:result], scope: [:values, :decision_result]) + end + + attr_reader :decision, :decision_iteration + + delegate :means, :interests_of_justice, to: :decision + + def actions + [change_link, remove_link].compact + end + + def remove_link + button_to('Remove', { action: :destroy, id: decision.decision_id }, method: :delete) + end + + def change_link + govuk_link_to('Change', { action: :edit, id: decision.decision_id }) + end + + def title + safe_join(['Case', count].compact, ' ') + end + + def count + return unless decision_iteration + return unless decision_iteration.size > 1 + + decision_iteration.index + 1 + end +end diff --git a/app/components/decision_result_component.rb b/app/components/decision_result_component.rb new file mode 100644 index 000000000..d3447e6ef --- /dev/null +++ b/app/components/decision_result_component.rb @@ -0,0 +1,29 @@ +class DecisionResultComponent < ViewComponent::Base + def initialize(result: nil) + @result = result + + super + end + + def call + return if result.nil? + + govuk_tag( + text: t(result, scope: [:values, :decision_result]), + colour: colour + ) + end + + private + + attr_reader :result + + def colour + case result + when /fail/ + 'red' + else + 'green' + end + end +end diff --git a/app/components/funding_decision_component.rb b/app/components/funding_decision_component.rb new file mode 100644 index 000000000..9dcc2a6c5 --- /dev/null +++ b/app/components/funding_decision_component.rb @@ -0,0 +1,29 @@ +class FundingDecisionComponent < ViewComponent::Base + # Wraps the Govuk Summary Card component so that when used with + # .with_collection the item number is added to the card title. + + def initialize(decision:, decision_iteration:) + @item = item + @item_title = title + @item_iteration = item_iteration + + super + end + + def call + app_card_list + end + + def title + safe_join([@item_title, count].compact, ' ') + end + + private + + def count + return unless item_iteration + return unless item_iteration.size > 1 + + item_iteration.index + 1 + end +end diff --git a/app/components/review_action_component.rb b/app/components/review_action_component.rb index ed08301f1..48cddfa40 100644 --- a/app/components/review_action_component.rb +++ b/app/components/review_action_component.rb @@ -24,15 +24,28 @@ def target complete_crime_application_path(application) when :send_back new_crime_application_return_path(application) + when :add_funding_decision + crime_application_decisions_path(application) when :mark_as_ready ready_crime_application_path(application) end end def method - return :get if action == :send_back + case action + when :send_back + :get + when :add_funding_decision + add_funding_decision_method + else + :put + end + end + + def add_funding_decision_method + return :get unless application.review.decision_ids.empty? - :put + :post end def warning diff --git a/app/controllers/casework/decisions/funding_decisions_controller.rb b/app/controllers/casework/decisions/funding_decisions_controller.rb new file mode 100644 index 000000000..634ad9f49 --- /dev/null +++ b/app/controllers/casework/decisions/funding_decisions_controller.rb @@ -0,0 +1,26 @@ +module Casework + module Decisions + # rename overall result? + class FundingDecisionsController < Casework::BaseController + include EditableDecision + + before_action :set_form + + private + + def form_class + ::Decisions::FundingDecisionForm + end + + def permitted_params + params[:decisions_funding_decision_form].permit( + :result, :details + ) + end + + def next_step + crime_application_decisions_path + end + end + end +end diff --git a/app/controllers/casework/decisions/interests_of_justices_controller.rb b/app/controllers/casework/decisions/interests_of_justices_controller.rb new file mode 100644 index 000000000..45992f3a4 --- /dev/null +++ b/app/controllers/casework/decisions/interests_of_justices_controller.rb @@ -0,0 +1,25 @@ +module Casework + module Decisions + class InterestsOfJusticesController < Casework::BaseController + include EditableDecision + + before_action :set_form + + private + + def form_class + ::Decisions::InterestsOfJusticeForm + end + + def permitted_params + params[:decisions_interests_of_justice_form].permit( + :result, :details, :assessed_by, :assessed_on + ) + end + + def next_step + edit_crime_application_decision_funding_decision_path + end + end + end +end diff --git a/app/controllers/casework/decisions/means_controller.rb b/app/controllers/casework/decisions/means_controller.rb new file mode 100644 index 000000000..9991a69ca --- /dev/null +++ b/app/controllers/casework/decisions/means_controller.rb @@ -0,0 +1,57 @@ +module Casework + module Decisions + class InterestsOfJusticesController < Casework::BaseController + before_action :set_crime_application + + def edit + @interests_of_justice = ::Decisions::InterestsOfJustice.new + end + + def update + @interests_of_justice = ::Decisions::InterestsOfJustice.new( + interests_of_justice_params + ) + + @interests_of_justice.validate! + + Deciding::SetInterestsOfJustice.new( + decision_id: params[:decision_id], + user_id: current_user_id, + details: @interests_of_justice.attributes.deep_symbolize_keys + ).call + + flash_and_redirect :success, :sent_back + rescue ActiveModel::ValidationError + render :edit + end + + private + + def draft_decision + @crime_application.decision + end + + def interests_of_justice_params + params[:decisions_interests_of_justice].permit( + :result, :reason, :assessed_by, :assessed_on + ) + end + + def create_draft_decision + Reviewing::CreateDraftDecision.new( + application_id: params[:crime_application_id], + user_id: current_user_id + ).call + end + + def set_ioj_decision + Reviewing::CreateDraftDecision.new( + application_id: params[:crime_application_id], + user_id: current_user_id + ).call + end + + def set_means_decision; end + end + end +end diff --git a/app/controllers/casework/decisions_controller.rb b/app/controllers/casework/decisions_controller.rb new file mode 100644 index 000000000..6ded05759 --- /dev/null +++ b/app/controllers/casework/decisions_controller.rb @@ -0,0 +1,58 @@ +module Casework + class DecisionsController < Casework::BaseController + include EditableDecision + + before_action :set_decision, except: [:index, :create] + + def index + @decisions = current_crime_application.review.decision_ids.map do |decision_id| + Deciding::LoadDecision.call( + application_id: params[:crime_application_id], + decision_id: decision_id + ) + end + end + + def edit + redirect_to edit_crime_application_decision_interests_of_justice_path( + crime_application_id: params[:crime_application_id], decision_id: + params[:id] + ) + end + + def confirm_destroy; end + + def create + decision_id = SecureRandom.uuid + + args = { + application_id: @crime_application.id, + user_id: current_user_id, + decision_id: decision_id + } + + ActiveRecord::Base.transaction do + Reviewing::AddDecision.call(**args) + Deciding::CreateDraft.call(**args) + end + + redirect_to edit_crime_application_decision_interests_of_justice_path( + crime_application_id: params[:crime_application_id], decision_id: decision_id + ) + end + + def destroy + args = { + application_id: @crime_application.id, + user_id: current_user_id, + decision_id: @decision.decision_id + } + + ActiveRecord::Base.transaction do + Reviewing::RemoveDecision.new(**args).call + end + + redirect_to action: :index + end + end +end diff --git a/app/controllers/concerns/editable_decision.rb b/app/controllers/concerns/editable_decision.rb new file mode 100644 index 000000000..743ed598d --- /dev/null +++ b/app/controllers/concerns/editable_decision.rb @@ -0,0 +1,41 @@ +module EditableDecision + extend ActiveSupport::Concern + + included do + before_action :set_crime_application + before_action :set_decision + before_action :confirm_assigned + end + + def edit; end + + def update + @form_object.update_with_user!( + permitted_params, current_user_id + ) + + redirect_to next_step + rescue ActiveModel::ValidationError + render :edit + end + + private + + def set_decision + decision = Deciding::LoadDecision.call( + decision_id: params[:decision_id] || params[:id] + ) + + raise Deciding::DecisionNotFound unless decision.application_id == current_crime_application.id + + @decision = decision + end + + def set_form + @form_object = form_class.build(@decision) + end + + def confirm_assigned + raise Deciding::DecisionNotFound unless @crime_application.reviewable_by?(current_user_id) + end +end diff --git a/app/helpers/components_helper.rb b/app/helpers/components_helper.rb index 712e8a332..ff84f1f48 100644 --- a/app/helpers/components_helper.rb +++ b/app/helpers/components_helper.rb @@ -11,6 +11,10 @@ def offence_dates(offence) render OffenceDatesComponent.new(offence:) end + def decision_result(result) + render DecisionResultComponent.new(result:) + end + def conflict_of_interest(codefendant) render ConflictOfInterestComponent.new(codefendant:) end diff --git a/app/lib/types.rb b/app/lib/types.rb index 48efbcfb7..ee3005bb5 100644 --- a/app/lib/types.rb +++ b/app/lib/types.rb @@ -34,6 +34,8 @@ module Types ReviewState = Symbol.default(:open).enum(*%i[open sent_back completed marked_as_ready]) + DecisionState = Symbol.enum(*%i[draft]) + ReviewType = Symbol.enum(*%i[means non_means pse]) CASEWORKER_ROLE = 'caseworker'.freeze @@ -57,6 +59,18 @@ module Types details: String ) + # MAAT also returns other result values which we may also need to handle. + MeansResult = String.enum('pass', 'fail') + InterestsOfJusticeResult = String.enum('pass', 'fail') + FundingDecisionResult = String.enum(*%w[granted_on_ioj fail_on_ioj]) + + InterestsOfJusticeDecision = Hash.schema( + result: InterestsOfJusticeResult, + details: String, + assessed_by: String, + assessed_on: Date + ) + Report = String.enum(*%w[ caseworker_report processed_report diff --git a/app/models/crime_application.rb b/app/models/crime_application.rb index f55b1cf2e..a396312a5 100644 --- a/app/models/crime_application.rb +++ b/app/models/crime_application.rb @@ -43,6 +43,10 @@ def history @history ||= ApplicationHistory.new(application: self) end + def funding_decisions + @funding_decisions ||= [Mock::FundingDecision.draft_maat] + end + def to_param id end diff --git a/app/models/decision.rb b/app/models/decision.rb new file mode 100644 index 000000000..2520d8317 --- /dev/null +++ b/app/models/decision.rb @@ -0,0 +1,6 @@ +class Decision < ApplicationStruct + attribute? :interests_of_justice, Decisions::InterestsOfJustice + attribute? :result, Types::String + attribute? :details, Types::String + attribute? :decision_id, Types::Uuid +end diff --git a/app/models/decisions/funding_decision_form.rb b/app/models/decisions/funding_decision_form.rb new file mode 100644 index 000000000..bc5c7ef3d --- /dev/null +++ b/app/models/decisions/funding_decision_form.rb @@ -0,0 +1,41 @@ +module Decisions + class FundingDecisionForm + include ActiveModel::Model + include ActiveModel::Attributes + include ActiveRecord::AttributeAssignment + include ActiveModel::Dirty + + attribute :application_id, :immutable_string + attribute :decision_id, :immutable_string + + attribute :result, :string + attribute :details, :string + + validates :result, inclusion: { in: :possible_results } + + def possible_results + Types::FundingDecisionResult.values + end + + def update_with_user!(attributes, user_id) + assign_attributes(attributes) + validate! + return unless changed? + + Deciding::SetFundingDecision.new( + decision_id:, user_id:, result:, details: + ).call + end + + class << self + def build(decision) + new( + application_id: decision.application_id, + decision_id: decision.decision_id, + result: decision.result, + details: decision.details + ) + end + end + end +end diff --git a/app/models/decisions/interests_of_justice.rb b/app/models/decisions/interests_of_justice.rb new file mode 100644 index 000000000..2aa237334 --- /dev/null +++ b/app/models/decisions/interests_of_justice.rb @@ -0,0 +1,8 @@ +module Decisions + class InterestsOfJustice < ApplicationStruct + attribute :result, Types::InterestsOfJusticeResult + attribute :details, Types::String + attribute :assessed_by, Types::String + attribute :assessed_on, Types::Date + end +end diff --git a/app/models/decisions/interests_of_justice_form.rb b/app/models/decisions/interests_of_justice_form.rb new file mode 100644 index 000000000..5bddd89ec --- /dev/null +++ b/app/models/decisions/interests_of_justice_form.rb @@ -0,0 +1,46 @@ +module Decisions + class InterestsOfJusticeForm + include ActiveModel::Model + include ActiveModel::Attributes + include ActiveRecord::AttributeAssignment + include ActiveModel::Dirty + + attribute :application_id, :immutable_string + attribute :decision_id, :immutable_string + + attribute :result, :string + attribute :details, :string + attribute :assessed_by, :string + attribute :assessed_on, :date + + validates :result, inclusion: { in: :possible_results } + validates :details, :assessed_by, presence: true + validates :assessed_on, presence: true + + def possible_results + Types::InterestsOfJusticeResult.values + end + + def update_with_user!(attributes, user_id) + assign_attributes(attributes) + validate! + return unless changed? + + Deciding::SetInterestsOfJustice.new( + decision_id: decision_id, + user_id: user_id, + interests_of_justice: self.attributes.deep_symbolize_keys + ).call + end + + class << self + def build(decision) + new( + **(decision.interests_of_justice || {}), + application_id: decision.application_id, + decision_id: decision.decision_id + ) + end + end + end +end diff --git a/app/models/decisions/outcome.rb b/app/models/decisions/outcome.rb new file mode 100644 index 000000000..c9807bb3d --- /dev/null +++ b/app/models/decisions/outcome.rb @@ -0,0 +1,10 @@ +module Decisions + class Outcome < ApplicationStruct + OUTCOMES = [:failed_interests_of_justice, :granted].freeze + + attribute? :outcome, Types::Nil | Types::String + attribute? :reason, Types::Nil | Types::String + + validates :outcome, presence: true + end +end diff --git a/app/models/interests_of_justice_decision.rb b/app/models/interests_of_justice_decision.rb new file mode 100644 index 000000000..1cc300315 --- /dev/null +++ b/app/models/interests_of_justice_decision.rb @@ -0,0 +1,18 @@ +class InterestsOfJusticeDecision < ApplicationStruct + attribute :interests_of_justice, InterestsOfJusticeDecision +end + +class InterestsOfJusticeDecision + include ActiveModel::Model + include ActiveModel::Attributes + include ActiveRecord::AttributeAssignment + + attribute :result, :string + attribute :details, :string + attribute :assessed_by, :string + attribute :assessed_on, :date + + validates :result, inclusion: { in: Types::InterestsOfJusticeResult.values } + validates :reason, :assessed_by, presence: true + validates :assessed_on, presence: true +end diff --git a/app/views/casework/decisions/funding_decisions/edit.html.erb b/app/views/casework/decisions/funding_decisions/edit.html.erb new file mode 100644 index 000000000..e2e0a67ae --- /dev/null +++ b/app/views/casework/decisions/funding_decisions/edit.html.erb @@ -0,0 +1,25 @@ +<% title t '.title' %> + +
+ <%= @crime_application.reference %> +
+ ++ <%= @crime_application.reference %> +
+ ++ <%= @crime_application.reference %> +
+ ++ <%= @crime_application.reference %> +
+ + <%= form_with model: @interests_of_justice, url: crime_application_add_ioj_decision_path(@crime_application), method: :post do |f| %> + + <%= f.govuk_error_summary %> + + <%= f.govuk_collection_radio_buttons :result, + ::Types::InterestsOfJusticeResult.values, + :to_s + %> + <%= f.govuk_text_area :reason %> + <%= f.govuk_text_field :assessed_by %> + <%= f.govuk_date_field :assessed_on, maxlength_enabled: true %> + <%= f.govuk_submit %> + <% end %> ++ <%= @crime_application.reference %> +
+ + <%= form_with model: @form_object, url: crime_application_funding_decisions_path(@crime_application.id) do |f| %> + <%= f.text_field :reason %> + + <%= f.govuk_error_summary %> + + <%= f.govuk_submit %> + <% end %> +