Skip to content

Commit

Permalink
Create CavcTask model (#15304)
Browse files Browse the repository at this point in the history
Resolves #15282

### Description
Adds CavcTask to be the parent for all CAVC-related tasks. 
This task blocks distribution until all its children and itself are completed. 
There are no actions available to any user for this task.

## Acceptance criteria
- [x] Verify a new task of type "CavcTask" model is created
- [x] Verify the task ~is~ [can only be] a child of a distribution task on the appeal
- [x] Verify task is completed when all of its children are completed/cancelled
- [x] Verify label reads "All CAVC related tasks"
- [x] Verify the task is Assigned to Bva

### Testing Plan
```ruby
# Find an appeal with an open DistributionTask to play with
appeal=DistributionTask.open.first.appeal
appeal.treee
                                         ┌──────────────────────────────────────────────────────────────┐
Appeal 11 (evidence_submission) ──────── │ ID │ STATUS   │ ASGN_BY │ ASGN_TO  │ UPDATED_AT              │
└── RootTask                             │ 24 │ on_hold  │         │ Bva      │ 2020-09-22 22:46:52 UTC │
    └── DistributionTask                 │ 25 │ on_hold  │         │ Bva      │ 2020-09-22 22:46:52 UTC │
        └── EvidenceSubmissionWindowTask │ 26 │ assigned │         │ MailTeam │ 2020-09-22 22:46:52 UTC │
                                         └──────────────────────────────────────────────────────────────┘

c=CavcTask.create(appeal: appeal, parent: appeal.root_task)
# should fail because parent should be a DistributionTask
appeal.reload.treee

parent_task=appeal.tasks.open.where(type: DistributionTask.name).first 
t=CavcTask.create(appeal: appeal, parent: parent_task)
appeal.reload.treee
t.assigned_to
t.label
t.available_actions(nil) # argument is not used

# create child tasks (as many as you want)
child_task=TranslationTask.create(appeal: appeal, parent: t, assigned_to: User.all.sample)
appeal.reload.treee
child_task.cancelled!
appeal.reload.treee
t.reload.cancelled?
t.reload.closed?

# repeat the above except complete the child_task instead
child_task.completed!
```
### Code Documentation Updates
- [x] Add or update code comments at the top of the class, module, and/or component.
  • Loading branch information
yoomlam authored Sep 29, 2020
1 parent 6e72295 commit a22fa11
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 1 deletion.
39 changes: 39 additions & 0 deletions app/models/tasks/cavc_task.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# frozen_string_literal: true

##
# This task is used to track all related CAVC subtasks for AMA Appeal Streams.
# If this task is still open, there is still more CAVC-specific work to be done of this appeal.
# This task should be a child of DistributionTask, and so it blocks distribution until all its children are closed.
# There are no actions available to any user for this task.

class CavcTask < Task
validates :parent, presence: true, parentTask: { task_type: DistributionTask }, on: :create

before_validation :set_assignee

def self.label
"All CAVC-related tasks"
end

def default_instructions
[COPY::CAVC_TASK_DEFAULT_INSTRUCTIONS]
end

def available_actions(_user)
[]
end

def verify_org_task_unique
true
end

private

def set_assignee
self.assigned_to = Bva.singleton
end

def cascade_closure_from_child_task?(_child_task)
true
end
end
22 changes: 22 additions & 0 deletions app/models/validators/parent_task_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# frozen_string_literal: true

##
# Validates the parent task
#
# Usage example: For the parent field, calls the built-in PresenceValidator and
# ParentTaskValidator with argument DistributionTask when the record is being created:
# validates :parent, presence: true, parentTask: { task_type: DistributionTask }, on: :create

class ParentTaskValidator < ActiveModel::Validator
def validate(record)
record.errors.add(:parent, "parent should be a #{options[:task_type].name}") unless correct_parent_type?(record)
end

private

def correct_parent_type?(record)
return true if options[:task_type].nil?

record.parent&.type == options[:task_type].name
end
end
2 changes: 1 addition & 1 deletion app/serializers/hearings/hearing_day_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class HearingDaySerializer

def self.get_readable_request_type(hearing_day, params)
if params[:video_hearing_days_request_types].nil?
fail ArgumentError, "params must have video_hearing_days_request_tyeps"
fail ArgumentError, "params must have video_hearing_days_request_types"
end

# `video_hearing_days_request_types` should be constructed with
Expand Down
2 changes: 2 additions & 0 deletions client/COPY.json
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,8 @@
"HEARING_TASK_ASSOCIATION_MISSING_MESASAGE": "Hearing task (%s) is missing an associated hearing. This means that either the hearing was deleted in VACOLS or the hearing association has been deleted.",
"HEARING_TASK_DEFAULT_INSTRUCTIONS": "This task will be auto-completed when all hearing-related tasks have been completed.",

"CAVC_TASK_DEFAULT_INSTRUCTIONS": "This task will be auto-completed when all CAVC-related tasks have been closed.",

"ASSIGN_HEARING_DISPOSITION_TASK_DEFAULT_INSTRUCTIONS": "Postpone or cancel a hearing prior to the hearing date. This task will be auto-completed after the hearing's scheduled date.",

"CHANGE_HEARING_DISPOSITION_TASK_DEFAULT_INSTRUCTIONS": "Change hearing disposition (Held, Canceled, No Show, Postponed) if it was marked in error.",
Expand Down
12 changes: 12 additions & 0 deletions spec/factories/task.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# frozen_string_literal: true

FactoryBot.define do
module FactoryBotHelper
def self.find_first_task_or_create(appeal, task_type)
(appeal.tasks.open.where(type: task_type.name).first if appeal) ||
FactoryBot.create(task_type.name.underscore.to_sym, appeal: appeal)
end
end

# By default, this task is created in a new Legacy appeal
factory :task do
assigned_at { rand(30..35).days.ago }
Expand Down Expand Up @@ -290,6 +297,7 @@
end

factory :distribution_task, class: DistributionTask do
parent { appeal.root_task || create(:root_task, appeal: appeal) }
assigned_by { nil }
assigned_to { Bva.singleton }

Expand Down Expand Up @@ -347,6 +355,10 @@
factory :translation_task, class: TranslationTask do
end

factory :cavc_task, class: CavcTask do
parent { FactoryBotHelper.find_first_task_or_create(appeal, DistributionTask) }
end

factory :hearing_task, class: HearingTask do
assigned_to { Bva.singleton }
parent { appeal.root_task || create(:root_task, appeal: appeal) }
Expand Down
124 changes: 124 additions & 0 deletions spec/models/tasks/cavc_task_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
# frozen_string_literal: true

describe CavcTask, :postgres do
describe ".create" do
subject { described_class.create(appeal: appeal, parent: parent_task) }
let(:appeal) { create(:appeal) }
let!(:parent_task) { create(:distribution_task, appeal: appeal) }

context "parent is DistributionTask" do
it "creates task" do
new_task = subject
expect(new_task.valid?)
expect(new_task.errors.messages[:parent]).to be_empty

expect(appeal.tasks).to include new_task
expect(parent_task.children).to include new_task

expect(new_task.assigned_to).to eq Bva.singleton
expect(new_task.label).to eq "All CAVC-related tasks"
expect(new_task.default_instructions).to eq [COPY::CAVC_TASK_DEFAULT_INSTRUCTIONS]
end
end

context "parent is not a DistributionTask" do
let(:parent_task) { create(:root_task) }
it "fails to create task" do
new_task = subject
expect(new_task.invalid?)
expect(new_task.errors.messages[:parent]).to include("parent should be a DistributionTask")
end
end

context "parent is nil" do
let(:parent_task) { nil }
it "fails to create task" do
new_task = subject
expect(new_task.invalid?)
expect(new_task.errors.messages[:parent]).to include("can't be blank")
end
end
end

describe "FactoryBot.create(:cavc_task) with different arguments" do
context "appeal is provided" do
let(:appeal) { create(:appeal) }
let!(:parent_task) { create(:distribution_task, appeal: appeal) }
let!(:cavc_task) { create(:cavc_task, appeal: appeal) }
it "finds existing distribution_task to use as parent" do
expect(Appeal.count).to eq 1
expect(RootTask.count).to eq 1
expect(DistributionTask.count).to eq 1
expect(CavcTask.count).to eq 1
end
end
context "parent task is provided" do
let(:parent_task) { create(:distribution_task) }
let!(:cavc_task) { create(:cavc_task, parent: parent_task) }
it "uses existing distribution_task" do
expect(Appeal.count).to eq 1
expect(RootTask.count).to eq 1
expect(DistributionTask.count).to eq 1
expect(CavcTask.count).to eq 1
end
end
context "nothing is provided" do
let!(:cavc_task) { create(:cavc_task) }
it "creates realistic task tree" do
expect(Appeal.count).to eq 1
expect(RootTask.count).to eq 1
expect(DistributionTask.count).to eq 1
expect(CavcTask.count).to eq 1
end
end
end

describe "#available_actions" do
let(:user) { create(:user) }
let(:cavc_task) { create(:cavc_task) }
it "returns empty" do
expect(cavc_task.available_actions(user)).to be_empty
end
end

context "closing child tasks" do
let(:user) { create(:user) }
let(:cavc_task) { create(:cavc_task) }
let!(:child_task) { create(:ama_task, parent: cavc_task) }
context "as complete" do
it "completes parent CavcTask" do
child_task.completed!
expect(cavc_task.closed?)
expect(cavc_task.status).to eq "completed"
end
end
context "as cancelled" do
it "cancels parent CavcTask" do
child_task.cancelled!
expect(cavc_task.closed?)
expect(cavc_task.status).to eq "cancelled"
end
end
context "has multiple children" do
let!(:child_task2) { create(:ama_task, parent: cavc_task) }
it "leaves parent CavcTask open when completing 1 child" do
child_task.completed!
expect(cavc_task.open?)
expect(cavc_task.status).to eq "on_hold"
end
it "leaves parent CavcTask open when cancelling 1 child" do
child_task.cancelled!
expect(cavc_task.open?)
expect(cavc_task.status).to eq "on_hold"
end
it "closes parent CavcTask when closing last open child" do
child_task.cancelled!
expect(cavc_task.open?)
expect(cavc_task.status).to eq "on_hold"
child_task2.completed!
expect(cavc_task.closed?)
expect(cavc_task.status).to eq "completed"
end
end
end
end

0 comments on commit a22fa11

Please sign in to comment.