Skip to content

Commit 36b9034

Browse files
authored
5165 add print picklist (#5188)
* Test: Adds missing test coverage for 'print unfulfilled' request UI * Test: Adds missing tests for requests controller #print_unfulfilled * Test: Adds overlooked cases for picklist pdf * Feature: Adds routing for new print picklist * Feature: Adds print_picklist to requests controller * Feature: Adds links to UI to access print_picklist * Docs: Adds placeholder for update to picklist docs until PR is finalized * Feature-5165 update docs to reflect new print individual picklist * Fixes rubocop issues * Removes duplicate spec case
1 parent 0acf065 commit 36b9034

File tree

10 files changed

+161
-3
lines changed

10 files changed

+161
-3
lines changed

app/controllers/requests_controller.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,23 @@ def start
4242
redirect_to new_distribution_path(request_id: request.id)
4343
end
4444

45+
def print_picklist
46+
request = current_organization
47+
.requests
48+
.includes(:item_requests, partner: [:profile])
49+
.find(params[:id])
50+
51+
respond_to do |format|
52+
format.any do
53+
pdf = PicklistsPdf.new(current_organization, [request])
54+
send_data pdf.compute_and_render,
55+
filename: format("Picklists_%s.pdf", Time.current.to_fs(:long)),
56+
type: "application/pdf",
57+
disposition: "inline"
58+
end
59+
end
60+
end
61+
4562
def print_unfulfilled
4663
requests = current_organization
4764
.requests

app/pdfs/picklists_pdf.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require "prawn/table"
2+
13
# Configures a Prawn PDF template for generating Distribution manifests
24
class PicklistsPdf
35
include Prawn::View

app/views/requests/_request_row.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@
2323
<td class="text-right">
2424
<%= view_button_to request_path(request_row) %>
2525
<%= button_to 'Cancel', new_request_cancelation_path(request_id: request_row.id), method: :get, class: 'btn btn-danger btn-xs' %>
26-
</td>
26+
<%= print_button_to print_picklist_request_path(request_row), { format: :pdf, text: "Print", size: "xs" } %>
27+
</td>
2728
</tr>

app/views/requests/show.html.erb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
<div class="card-footer flex flex-row space-x-2">
109109
<%= submit_button_to start_request_path(@request), {text: "Fulfill request", size: "md"} unless @request.distribution %>
110110
<%= view_button_to(distribution_path(@request.distribution), {text: "View Associated Distribution", size: "md"}) if @request.distribution %>
111+
<%= print_button_to print_picklist_request_path(@request), { format: :pdf, text: "Print", size: "md" } %>
111112
<%= button_to 'Cancel', new_request_cancelation_path(request_id: @request.id),
112113
method: :get, form_class: 'd-inline', class: 'btn btn-danger btn-md' %>
113114
</div>

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ def set_up_flipper
241241
post :start
242242
end
243243
get :print_unfulfilled, on: :collection
244+
get :print_picklist, on: :member
244245
end
245246
resources :requests, except: %i(destroy) do
246247
resource :cancelation, only: [:new, :create], controller: 'requests/cancelation'

docs/user_guide/bank/essentials_requests.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ This will create a .csv file with the following information for each filtered Re
138138
- the quantity requested
139139
Note: If you use custom units, there will be a column for each item/unit that is available to be requested.
140140

141+
## Printing Request picklists
142+
You can print out a "picklist" for any Request you can see. This function produces a pdf showing all items requested for the selected Request.
143+
144+
This is visible on the Request detail page:
145+
![Print Picklist](images/essentials/requests/essentials_requests_print_picklist_navigation.png)
146+
147+
148+
And also visible on the Request list page:
149+
![Print Picklist](images/essentials/requests/essentials_requests_print_picklist_navigation2.png)
150+
141151
## Printing unfulfilled Request picklists
142152

143153
Finally, you can also print "picklists" for your unfulfilled Requests.
192 KB
Loading
381 KB
Loading

spec/pdfs/picklists_pdf_spec.rb

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55

66
describe "#compute_and_render" do
77
it "renders multiple requests correctly" do
8-
request1 = create(:request, :pending, organization: organization)
9-
request2 = create(:request, :pending, organization: organization)
8+
request1 = create(:request, :pending, organization: organization, comments: "Request 1 comments")
9+
request2 = create(:request, :pending, organization: organization, comments: "Request 2 comments")
1010
create(:item_request, request: request1, item: item1, name: "Item 1")
1111
create(:item_request, request: request2, item: item2, name: "Item 2")
1212

@@ -19,6 +19,7 @@
1919
expect(pdf_test.page(1).text).to include("Requested on:")
2020
expect(pdf_test.page(1).text).to include("Items Received Year-to-Date:")
2121
expect(pdf_test.page(1).text).to include("Comments")
22+
expect(pdf_test.page(1).text).to include(request1.comments)
2223
expect(pdf_test.page(1).text).to include("Items Requested")
2324
expect(pdf_test.page(1).text).to include("Item 1")
2425

@@ -28,10 +29,29 @@
2829
expect(pdf_test.page(2).text).to include("Requested on:")
2930
expect(pdf_test.page(2).text).to include("Items Received Year-to-Date:")
3031
expect(pdf_test.page(2).text).to include("Comments")
32+
expect(pdf_test.page(2).text).to include(request2.comments)
3133
expect(pdf_test.page(2).text).to include("Items Requested")
3234
expect(pdf_test.page(2).text).to include("Item 2")
3335
end
3436

37+
context "when ytd_on_distribution_printout is enabled for the organization" do
38+
before { organization.update(ytd_on_distribution_printout: true) }
39+
40+
it "renders the YTD quantity" do
41+
partner = create(:partner)
42+
request = create(:request, :pending, organization: organization, partner: partner)
43+
create(:item_request, request: request, item: item1, name: "Item 1", quantity: 17)
44+
45+
# stub out the quantity_year_to_date method, it's not the PDF's job to make sure the calculation is correct
46+
allow(partner).to receive(:quantity_year_to_date).and_return(17827)
47+
pdf = described_class.new(organization, [request])
48+
pdf_test = PDF::Reader.new(StringIO.new(pdf.compute_and_render))
49+
50+
expect(pdf_test.page(1).text).to include("Items Received Year-to-Date:")
51+
expect(pdf_test.page(1).text).to include("17827")
52+
end
53+
end
54+
3555
context "When partner pickup person is set" do
3656
it "renders pickup person details" do
3757
partner = create(:partner)

spec/requests/partners/requests_spec.rb

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,112 @@
431431
end
432432
end
433433

434+
describe "GET #print_unfulfilled" do
435+
let(:item1) { create(:item, name: "Good item") }
436+
let(:item2) { create(:item, name: "Crap item") }
437+
let(:partner1) { create(:partner, organization: organization) }
438+
let(:partner_user) { partner1.primary_user }
439+
let!(:pending_request) { create(:request, :with_item_requests, :pending, partner: partner1, request_items: [{ item_id: item1.id, quantity: '100' }]) }
440+
let!(:started_request) { create(:request, :with_item_requests, :started, partner: partner1, request_items: [{ item_id: item2.id, quantity: '50' }]) }
441+
let!(:discarded_request) { create(:request, :with_item_requests, :discarded, partner: partner1, request_items: [{ item_id: item2.id, quantity: '30' }]) }
442+
let!(:fulfilled_request) { create(:request, :with_item_requests, :fulfilled, partner: partner1, request_items: [{ item_id: item2.id, quantity: '20' }]) }
443+
444+
before do
445+
partner_user.add_role(Role::ORG_ADMIN, organization)
446+
sign_in(partner_user)
447+
get print_unfulfilled_requests_path(format: :pdf)
448+
end
449+
450+
it "returns a PDF file" do
451+
PDF::Reader.new(StringIO.new(response.body))
452+
expect(response.content_type).to eq('application/pdf')
453+
expect(response.headers['Content-Disposition']).to include('inline')
454+
expect(response.body.bytes[0..3]).to eq('%PDF'.bytes)
455+
end
456+
457+
it "includes only 'pending' and 'started' requests" do
458+
pdf_content = PDF::Reader.new(StringIO.new(response.body))
459+
# this is a semi-lazy check, since we're ensuring 1 page for each request. In real world,
460+
# it's possible that there could be more than 1 page per request if the request is long.
461+
462+
expect(pdf_content.page_count).to eq(2)
463+
end
464+
465+
it "calls compute_and_render with the 2 matching requests" do
466+
# Create a double for the PDF instance
467+
pdf_double = double("PicklistsPdf")
468+
469+
# Expect PicklistsPdf.new to be called with correct args and return our double
470+
expect(PicklistsPdf).to receive(:new)
471+
.with(organization, kind_of(ActiveRecord::Relation))
472+
.and_return(pdf_double)
473+
474+
# Expect compute_and_render to be called on our double and return some PDF data
475+
# We don't really care about the content, the PDF model is tested elsewhere
476+
expect(pdf_double).to receive(:compute_and_render)
477+
.and_return("fake pdf content")
478+
479+
# Make the request
480+
get print_unfulfilled_requests_path(format: :pdf)
481+
482+
# Verify the response
483+
expect(response).to be_successful
484+
expect(response.content_type).to eq("application/pdf")
485+
expect(response.headers["Content-Disposition"]).to include("inline")
486+
expect(response.body).to eq("fake pdf content")
487+
end
488+
end
489+
490+
describe "GET #print_picklist" do
491+
let(:organization) { create(:organization) }
492+
let(:partner) { create(:partner, organization: organization) }
493+
let(:partner_user) { partner.primary_user }
494+
let(:org_admin) { create(:organization_admin, organization: organization) }
495+
let(:request) { create(:request, :with_item_requests, organization: organization, partner: partner, partner_user: org_admin) }
496+
497+
before do
498+
sign_in(org_admin)
499+
end
500+
501+
it "generates a PDF for a single request" do
502+
# Create a double for the PDF instance
503+
pdf_double = double("PicklistsPdf")
504+
505+
# Expect PicklistsPdf.new to be called with correct args and return our double
506+
expect(PicklistsPdf).to receive(:new)
507+
.with(organization, [request])
508+
.and_return(pdf_double)
509+
510+
# Expect compute_and_render to be called on our double and return some PDF data
511+
expect(pdf_double).to receive(:compute_and_render)
512+
.and_return("fake pdf content")
513+
514+
# Make the request
515+
get print_picklist_request_path(request, format: :pdf)
516+
517+
# Verify the response
518+
expect(response).to be_successful
519+
expect(response.content_type).to eq("application/pdf")
520+
expect(response.headers["Content-Disposition"]).to include("inline")
521+
expect(response.headers["Content-Disposition"]).to include("Picklists_")
522+
expect(response.body).to eq("fake pdf content")
523+
end
524+
525+
it "includes correct associations in the query" do
526+
pdf_double = double("PicklistsPdf", compute_and_render: "pdf content")
527+
528+
expect(PicklistsPdf).to receive(:new) do |org, requests|
529+
# Verify the request includes the necessary associations
530+
expect(requests.first.association(:item_requests)).to be_loaded
531+
expect(requests.first.association(:partner)).to be_loaded
532+
expect(requests.first.partner.association(:profile)).to be_loaded
533+
pdf_double
534+
end
535+
536+
get print_picklist_request_path(request, format: :pdf)
537+
end
538+
end
539+
434540
describe 'POST #validate' do
435541
it 'should handle missing CSRF gracefully' do
436542
sign_in(partner_user)

0 commit comments

Comments
 (0)