diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6476dd4b70..7babb1147a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -198,7 +198,7 @@ Only take multiple issues if they are related and you can solve all of them at t
Users that are frequent contributors and are involved in discussion (join the slack channel! :)) may be given direct Contributor access to the Repo so they can submit Pull Requests directly instead of Forking first.
## Debugging
-If starting server directly, via `rail s` or `rail console`, or built-in debugger in RubyMine, or running `bundle exec rspec path/to/spec.rb:line_no`, then you can use `binding.pry` to debug. Drop the pry where you want the execution to pause.
+If starting server directly, via `rails s` or `rails console`, or built-in debugger in RubyMine, or running `bundle exec rspec path/to/spec.rb:line_no`, then you can use `binding.pry` to debug. Drop the pry where you want the execution to pause.
If starting via Procfile with `bin/start`, then drop a ``binding.remote_pry`` into the line where you want execution to pause at. Then run ``pry-remote`` in the terminal to connect to it.
https://github.com/Mon-Ouie/pry-remote
diff --git a/app/controllers/requests_controller.rb b/app/controllers/requests_controller.rb
index ce5677da0c..2315a67486 100644
--- a/app/controllers/requests_controller.rb
+++ b/app/controllers/requests_controller.rb
@@ -8,6 +8,11 @@ def index
.undiscarded
.during(helpers.selected_range)
.class_filter(filter_params)
+
+ if params[:include_cancelled]
+ @requests = @requests.with_discarded
+ end
+
@unfulfilled_requests_count = current_organization.requests.where(status: [:pending, :started]).during(helpers.selected_range).class_filter(filter_params).count
@paginated_requests = @requests.includes(:partner).page(params[:page])
@calculate_product_totals = RequestsTotalItemsService.new(requests: @requests).calculate
@@ -20,6 +25,7 @@ def index
@selected_request_item = filter_params[:by_request_item_id]
@selected_partner = filter_params[:by_partner]
@selected_status = filter_params[:by_status]
+ @include_cancelled = params[:include_cancelled]
respond_to do |format|
format.html
diff --git a/app/models/request.rb b/app/models/request.rb
index 9d54f11fab..6c4590975b 100644
--- a/app/models/request.rb
+++ b/app/models/request.rb
@@ -31,13 +31,15 @@ class Request < ApplicationRecord
accepts_nested_attributes_for :item_requests, allow_destroy: true, reject_if: proc { |attributes| attributes["quantity"].blank? }
has_many :child_item_requests, through: :item_requests
- enum :status, { pending: 0, started: 1, fulfilled: 2, discarded: 3 }, prefix: true
+ enum :status, { pending: 0, started: 1, fulfilled: 2, cancelled: 3 }, prefix: true
enum :request_type, %w[quantity individual child].map { |v| [v, v] }.to_h
validates :distribution_id, uniqueness: true, allow_nil: true
validate :item_requests_uniqueness_by_item_id
validate :not_completely_empty
- validate :cannot_change_status_once_fulfilled, on: :update
+ validate :cannot_change_status_once_fulfilled,
+ :cannot_change_status_once_cancelled,
+ on: :update
after_validation :sanitize_items_data
@@ -93,4 +95,10 @@ def cannot_change_status_once_fulfilled
errors.add(:status, "cannot be changed once fulfilled")
end
end
+
+ def cannot_change_status_once_cancelled
+ if status_changed? && status_was == "cancelled"
+ errors.add(:status, "cannot be changed once cancelled")
+ end
+ end
end
diff --git a/app/services/request_destroy_service.rb b/app/services/request_destroy_service.rb
index 1aeb9d6019..09e8b33f6a 100644
--- a/app/services/request_destroy_service.rb
+++ b/app/services/request_destroy_service.rb
@@ -11,7 +11,7 @@ def call
request.discarded_at = Time.current
request.discard_reason = reason
- request.status = :discarded
+ request.status = :cancelled
request.save!
unless request.partner.deactivated?
@@ -29,7 +29,7 @@ def valid?
if request.blank?
errors.add(:base, 'request_id is invalid')
elsif request.discarded_at.present?
- errors.add(:base, 'request already discarded')
+ errors.add(:base, 'request already cancelled')
end
errors.none?
diff --git a/app/views/requests/_request_row.html.erb b/app/views/requests/_request_row.html.erb
index 58b4fdc3df..66af59903d 100644
--- a/app/views/requests/_request_row.html.erb
+++ b/app/views/requests/_request_row.html.erb
@@ -22,7 +22,9 @@
<%= view_button_to request_path(request_row) %>
- <%= cancel_button_to new_request_cancelation_path(request_id: request_row.id), method: :get, data: { disable_with: "Please wait..." }, size: "xs", type: "danger" %>
+ <% unless request_row.status_cancelled? %>
+ <%= cancel_button_to new_request_cancelation_path(request_id: request_row.id), method: :get, data: { disable_with: "Please wait..." }, size: "xs", type: "danger" %>
+ <% end %>
<%= print_button_to print_picklist_request_path(request_row), { format: :pdf, text: "Print", size: "xs" } %>
- |
+
diff --git a/app/views/requests/_status.html.erb b/app/views/requests/_status.html.erb
index 7f2cdc5027..4280cbac1d 100644
--- a/app/views/requests/_status.html.erb
+++ b/app/views/requests/_status.html.erb
@@ -6,5 +6,5 @@
<% when "fulfilled" %>
Fulfilled
<% else %>
- Errored
+ Cancelled
<% end %>
diff --git a/app/views/requests/index.html.erb b/app/views/requests/index.html.erb
index 57462d00f5..c7152f2158 100644
--- a/app/views/requests/index.html.erb
+++ b/app/views/requests/index.html.erb
@@ -57,8 +57,11 @@
<%= label_tag "Date Range" %>
<%= render partial: "shared/date_range_picker", locals: {css_class: "form-control"} %>
-
-
+
+ <%= filter_checkbox(label: "Include Cancelled?", scope: "include_cancelled", selected: @include_cancelled) %>
+
+
+
diff --git a/docs/user_guide/bank/essentials_requests.md b/docs/user_guide/bank/essentials_requests.md
index 1e2eefcfb4..56e7d19ae3 100644
--- a/docs/user_guide/bank/essentials_requests.md
+++ b/docs/user_guide/bank/essentials_requests.md
@@ -29,7 +29,7 @@ The list contains:
- pending -- haven't started fulfilling it yet
- started -- have started fulfilling, but haven't saved the resulting distribution
- fulfilled -- have created the distribution for this Request
- - discarded -- have cancelled the Request
+ - cancelled -- have cancelled the Request
- and the actions you can take on that Request
### Filtering your Requests
diff --git a/docs/user_guide/bank/exports.md b/docs/user_guide/bank/exports.md
index 3dbed2243d..2d3ccde6dd 100644
--- a/docs/user_guide/bank/exports.md
+++ b/docs/user_guide/bank/exports.md
@@ -404,7 +404,7 @@ Click 'Requests' in the left-hand menu, then "Export Requests"
You can filter the exported Requests by the following:
- Item
- Partner
-- Status (Pending, Started, Fulfilled, Discarded)
+- Status (Pending, Started, Fulfilled, Cancelled)
- Date Range
Make your selection, then click 'Filter' before clicking 'Export Requests'
diff --git a/spec/factories/requests.rb b/spec/factories/requests.rb
index 017a36b787..c4e77031ba 100644
--- a/spec/factories/requests.rb
+++ b/spec/factories/requests.rb
@@ -74,8 +74,9 @@ def random_request_items
status { 'pending' }
end
- trait :discarded do
- status { 'discarded' }
+ trait :cancelled do
+ status { 'cancelled' }
+ discarded_at { Time.current }
end
trait :with_varied_quantities do
diff --git a/spec/models/request_spec.rb b/spec/models/request_spec.rb
index 16db16da2a..e203179deb 100644
--- a/spec/models/request_spec.rb
+++ b/spec/models/request_spec.rb
@@ -40,6 +40,32 @@
.to raise_error(ActiveRecord::RecordInvalid, /cannot be changed once fulfilled/)
end
+ it "does not regress from fulfilled to cancelled" do
+ expect { request_fulfilled.status_cancelled! }
+ .to raise_error(ActiveRecord::RecordInvalid, /cannot be changed once fulfilled/)
+ end
+
+ it "does not regress from cancelled to started" do
+ cancelled_request = create(:request, :cancelled)
+
+ expect { cancelled_request.status_started! }
+ .to raise_error(ActiveRecord::RecordInvalid, /cannot be changed once cancelled/)
+ end
+
+ it "does not regress from cancelled to pending" do
+ cancelled_request = create(:request, :cancelled)
+
+ expect { cancelled_request.status_pending! }
+ .to raise_error(ActiveRecord::RecordInvalid, /cannot be changed once cancelled/)
+ end
+
+ it "does not regress from cancelled to fulfilled" do
+ cancelled_request = create(:request, :cancelled)
+
+ expect { cancelled_request.status_fulfilled! }
+ .to raise_error(ActiveRecord::RecordInvalid, /cannot be changed once cancelled/)
+ end
+
it "allows normal transitions" do
expect { request_pending.status_started! }.not_to raise_error
expect { request_started.status_fulfilled! }.not_to raise_error
diff --git a/spec/requests/partners/requests_spec.rb b/spec/requests/partners/requests_spec.rb
index 55b88df00a..c81a98c127 100644
--- a/spec/requests/partners/requests_spec.rb
+++ b/spec/requests/partners/requests_spec.rb
@@ -438,7 +438,7 @@
let(:partner_user) { partner1.primary_user }
let!(:pending_request) { create(:request, :with_item_requests, :pending, partner: partner1, request_items: [{ item_id: item1.id, quantity: '100' }]) }
let!(:started_request) { create(:request, :with_item_requests, :started, partner: partner1, request_items: [{ item_id: item2.id, quantity: '50' }]) }
- let!(:discarded_request) { create(:request, :with_item_requests, :discarded, partner: partner1, request_items: [{ item_id: item2.id, quantity: '30' }]) }
+ let!(:cancelled_request) { create(:request, :with_item_requests, :cancelled, partner: partner1, request_items: [{ item_id: item2.id, quantity: '30' }]) }
let!(:fulfilled_request) { create(:request, :with_item_requests, :fulfilled, partner: partner1, request_items: [{ item_id: item2.id, quantity: '20' }]) }
before do
diff --git a/spec/requests/requests_requests_spec.rb b/spec/requests/requests_requests_spec.rb
index 32e8760cf2..f05e4558f1 100644
--- a/spec/requests/requests_requests_spec.rb
+++ b/spec/requests/requests_requests_spec.rb
@@ -11,10 +11,6 @@
response
end
- before do
- create(:request)
- end
-
context "html" do
let(:response_format) { 'html' }
@@ -25,35 +21,103 @@
let(:response_format) { 'csv' }
it { is_expected.to be_successful }
+
+ context 'when exporting as CSV' do
+ it "exports only the cancelled requests CSV when 'Include Cancelled' is checked and 'Filter by Status' is 'Cancelled'" do
+ create(:request, :started)
+ create(:request, :cancelled)
+
+ get requests_path(format: :csv, params: {include_cancelled: "1", filters: { by_status: :cancelled}})
+
+ csv = CSV.parse(response.body, headers: true)
+
+ expect(csv.count).to eq(1)
+ expect(csv.first["Status"]).to eq("Cancelled")
+ end
+
+ it "exports the requests CSV including cancelled requests when 'Include Cancelled' is checked" do
+ create(:request, :started)
+ create(:request, :cancelled)
+
+ get requests_path(format: :csv, params: {include_cancelled: "1"})
+
+ csv = CSV.parse(response.body, headers: true)
+
+ expect(csv.count).to eq(2)
+ expect(csv[0]["Status"]).to eq("Started")
+ expect(csv[1]["Status"]).to eq("Cancelled")
+ end
+ end
end
context "when there are pending or started requests" do
- it "shows print unfulfilled picklists button with correct quantity" do
- Request.delete_all
-
+ it "shows print unfulfilled picklists button with correct quantity, excluding cancelled requests by default" do
create(:request, :pending)
create(:request, :started)
create(:request, :fulfilled)
- create(:request, :discarded)
+ create(:request, :cancelled)
get requests_path
expect(response.body).to include('Print Unfulfilled Picklists (2)')
+ expect(response.body).not_to match(%r{\s*Cancelled\s*})
+ end
+ end
+
+ context "when 'include_cancelled' param is present" do
+ it 'does not display the Cancel button for cancelled requests' do
+ pending_request = create(:request, :pending)
+ cancelled_request = create(:request, :cancelled)
+
+ get requests_path, params: {include_cancelled: "1"}
+
+ page = Nokogiri::HTML(response.body)
+
+ cancelled_request_cancel_button = page.at_css("form[action='/requests/#{cancelled_request.id}/cancelation/new']")
+ expect(cancelled_request_cancel_button).to be_nil
+
+ pending_request_cancel_button = page.at_css("a[href='/requests/#{pending_request.id}/cancelation/new']")
+ expect(pending_request_cancel_button).to be_present
+ end
+
+ it "shows print unfulfilled picklists button with correct quantity including cancelled requests" do
+ create(:request, :pending)
+ create(:request, :started)
+ create(:request, :cancelled)
+
+ get requests_path, params: {include_cancelled: "1"}
+
+ expect(response.body).to include('Print Unfulfilled Picklists (2)')
+ expect(response.body).to match(%r{\s*Cancelled\s*})
+ end
+ end
+
+ context "when 'Include Cancelled?' is checked and filter by Cancelled" do
+ it "constrains the list for cancelled requests only" do
+ create(:request, :started, comments: "Need more supplies")
+ create(:request, :pending, comments: "Awaiting for confirmation")
+ create(:request, :cancelled, comments: 'Not necessary anymore')
+
+ get requests_path, params: {include_cancelled: "1", filters: { by_status: :cancelled}}
+
+ expect(response.body).to include("Not necessary anymore")
+ expect(response.body).not_to include("Need more supplies")
+ expect(response.body).not_to include("Awaiting for confirmation")
end
end
context "when there is a filter applied" do
it "shows only filtered requests, print unfulfilled picklists button with correct quantity" do
- Request.delete_all
-
create(:request, :started, comments: "Started request - should appear")
create(:request, :pending, comments: "Pending request - should not appear")
+ create(:request, :cancelled, comments: 'Cancelled request - a comment')
get requests_path({ filters: { by_status: :started} })
expect(response.body).to include("Print Unfulfilled Picklists (1)")
expect(response.body).to include("Started request - should appear")
expect(response.body).not_to include("Pending request - should not appear")
+ expect(response.body).not_to include("Cancelled request - a comment")
end
end
end
@@ -156,6 +220,24 @@
expect(response.body).not_to include('Units (if applicable)')
end
end
+
+ context 'when the request has a cancelled status' do
+ it 'does not display the Cancel and Fulfill request buttons' do
+ cancelled_request = create(:request, :cancelled, organization:)
+
+ get request_path(cancelled_request)
+
+ page = Nokogiri::HTML(response.body)
+
+ cancel_button = page.at_css("form[action='/requests/#{cancelled_request.id}/cancelation/new']")
+ fulfill_button = page.at_css("form[action='/requests/#{cancelled_request.id}/start']")
+ expect(cancel_button).to be_nil
+ expect(fulfill_button).to be_nil
+
+ print_link = page.at_css("a[href='/requests/#{cancelled_request.id}/print_picklist']")
+ expect(print_link).to be_present
+ end
+ end
end
describe 'POST #start' do
diff --git a/spec/services/request_destroy_service_spec.rb b/spec/services/request_destroy_service_spec.rb
index abe8cfa6ac..7a033133b2 100644
--- a/spec/services/request_destroy_service_spec.rb
+++ b/spec/services/request_destroy_service_spec.rb
@@ -16,13 +16,13 @@
end
end
- context 'when the request is already discarded' do
+ context 'when the request is already cancelled' do
before do
request.discard!
end
it 'should not be successful and have errors' do
- expect(subject.errors.full_messages).to eq(['request already discarded'])
+ expect(subject.errors.full_messages).to eq(['request already cancelled'])
end
end
@@ -37,7 +37,7 @@
end
it 'should update the status column on the request' do
- expect { subject }.to change { request.reload.status_discarded? }.from(false).to(true)
+ expect { subject }.to change { request.reload.status_cancelled? }.from(false).to(true)
end
it 'should send a email notification to the partner' do
@@ -51,7 +51,7 @@
let(:request) { create(:request, partner: partner) }
it 'should update the status column on the request' do
- expect { subject }.to change { request.reload.status_discarded? }.from(false).to(true)
+ expect { subject }.to change { request.reload.status_cancelled? }.from(false).to(true)
end
it 'should not send a email notification to the partner' do
diff --git a/spec/system/dashboard_system_spec.rb b/spec/system/dashboard_system_spec.rb
index d329f81b80..f3ce2ef9a4 100644
--- a/spec/system/dashboard_system_spec.rb
+++ b/spec/system/dashboard_system_spec.rb
@@ -109,8 +109,8 @@
# expect(org_dashboard_page.outstanding_requests).to be_empty
end
- it "does not display a discarded request" do
- create :request, :discarded
+ it "does not display a cancelled request" do
+ create :request, :cancelled
org_dashboard_page.visit
org_dashboard_page.outstanding_section # wait for the section
expect(org_dashboard_page.outstanding_requests).to be_empty
diff --git a/spec/system/request_system_spec.rb b/spec/system/request_system_spec.rb
index 1ddba256d0..c1d3c936b9 100644
--- a/spec/system/request_system_spec.rb
+++ b/spec/system/request_system_spec.rb
@@ -4,8 +4,8 @@
let(:item1) { create(:item, name: "Good item") }
let(:item2) { create(:item, name: "Crap item") }
- let(:partner1) { create(:partner, organization:, name: "This Guy", email: "thisguy@example.com") }
- let(:partner2) { create(:partner, organization:, name: "That Guy", email: "ntg@example.com") }
+ let(:partner1) { build(:partner, organization:, name: "This Guy", email: "thisguy@example.com") }
+ let(:partner2) { build(:partner, organization:, name: "That Guy", email: "ntg@example.com") }
let!(:storage_location) { create(:storage_location, organization: organization) }
before do
@@ -114,6 +114,7 @@
end
end
end
+
it_behaves_like "Date Range Picker", Request, :created_at
it "doesn't display New Quantity Request link" do
@@ -253,7 +254,7 @@
visit requests_path
end
- it 'should set the request as canceled/discarded and contain the reason' do
+ it 'should set the request as canceled and contain the reason' do
click_on 'Cancel'
fill_in 'Cancellation reason *', with: reason
click_on 'Yes. Cancel Request'