Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions app/controllers/audits_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@ def index
@selected_location = filter_params[:at_location]
@audits = current_organization.audits.includes(:line_items, :storage_location).class_filter(filter_params)
@storage_locations = StorageLocation.with_audits_for(current_organization).select(:id, :name)

respond_to do |format|
format.html
format.csv do
send_data Audit.generate_csv(@audits), filename: "Audits-#{Time.zone.today}.csv"
end
end
end

def show
Expand Down
21 changes: 21 additions & 0 deletions app/models/audit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class Audit < ApplicationRecord
belongs_to :storage_location
belongs_to :adjustment, optional: true

include Exportable
include Itemizable
include Filterable
scope :at_location, ->(location_id) { where(storage_location_id: location_id) }
Expand Down Expand Up @@ -49,6 +50,26 @@ def user_is_organization_admin_of_the_organization
end
end

def self.generate_csv(audits)
audited_items = audits.flat_map { |audit| audit.line_items.map(&:name) }.uniq
headers = csv_export_headers + audited_items

CSV.generate(write_headers: true, headers: headers) do |csv|
audits.map do |audit|
audit_hash = audited_items.index_with(0)
audit.line_items.each { |li| audit_hash[li.name] = li.quantity }

row = [audit.updated_at.strftime("%B %d %Y"), audit.status, audit.storage_location.name] + audit_hash.values

csv << row.map { |attr| normalize_csv_attribute(attr) }
end
end
end

def self.csv_export_headers
["Audit Date", "Audit Status", "Storage Location Name"]
end

private

def line_items_unique_by_item_id
Expand Down
7 changes: 4 additions & 3 deletions app/views/audits/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,13 @@
<%= filter_button %>
<%= clear_filter_button %>

<div class="btn-group pull-right">
<span class="float-right">
<%= download_button_to(audits_path(format: :csv), {text: "Export Audits", size: "md"}) %>
<%= new_button_to new_audit_path, {text: "New Audit"} %>
</div>
</span>
<% end # form %>
</div>
</div>
</div>
<!-- /.card -->
</div>
<!--/.col (left) -->
Expand Down
14 changes: 14 additions & 0 deletions docs/user_guide/bank/exports.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Many of these can be filtered down to the information you might need for a speci
The exports available include (in alphabetical order):
- Adjustments
- Annual Survey
- Audits
- Barcode Items
- Distributions
- Donations
Expand Down Expand Up @@ -89,6 +90,19 @@ For more information on these, please see the [Annual Survey Report](reports_ann
- % difference in yearly Donations,
- % difference in total money donated,
- % difference in disposable diaper Donations

## Audits
### Navigating to export Audits
Click "Inventory", then "Inventory Audit" in the left-hand menu. Then click "Export Audits".

### Contents of Audit exports
For each Audit of Storage Location in the list:
- Audit Date
- Audit Status
- Storage Location Name
- Audited Items
- Quantity for each of the audited items.

## Barcode Items
### Navigating to export barcode Items
Click "Inventory" then "Barcode Items" in the left-hand menu. Then click "Export Barcode Items."
Expand Down
29 changes: 29 additions & 0 deletions spec/models/audit_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,35 @@
expect(Audit.finalized_since?(xfer3, storage_location4)).to be false # no audits at location
expect(Audit.finalized_since?(xfer3, storage_location5)).to be false # since status isn't finalized
end

describe ".generate_csv" do
let!(:item_1) { create(:item, name: "Baby Diapers") }
let!(:item_2) { create(:item, name: "Adult Diapers") }
let!(:sl_1) { create(:storage_location, organization: organization, name: "Diaperhaus") }
let!(:sl_2) { create(:storage_location, organization: organization, name: "Pawnee Diaper Bank") }
let!(:time_1) { Time.zone.parse("2025-10-02 01:00:00") }
let!(:time_2) { Time.zone.parse("2025-10-03 01:00:00") }
let!(:audit) { create(:audit, organization: organization, storage_location: sl_1, status: 1, updated_at: time_1) }
let!(:audit_2) { create(:audit, organization: organization, storage_location: sl_2, status: 2, updated_at: time_2) }

before do
audit.line_items.create!(item: item_1, quantity: 150)
audit_2.line_items.create!(item: item_2, quantity: 250)
end

it "generates a CSV" do
csv_data = described_class.generate_csv([audit, audit_2])

expect(csv_data).to be_a(String)
expect(csv_data).to eq(
<<~CSV
Audit Date,Audit Status,Storage Location Name,Baby Diapers,Adult Diapers
October 02 2025,confirmed,Diaperhaus,150,0
October 03 2025,finalized,Pawnee Diaper Bank,0,250
CSV
)
end
end
end

describe "versioning" do
Expand Down
36 changes: 30 additions & 6 deletions spec/requests/audits_requests_spec.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
RSpec.describe "Audits", type: :request do
let(:organization) { create(:organization) }
let(:organization_admin) { create(:organization_admin, organization: organization) }

let(:storage_location) { create(:storage_location, organization: organization) }
let(:valid_attributes) do
{
organization_id: organization.id,
storage_location_id: create(:storage_location, organization: organization).id,
storage_location_id: storage_location.id,
user_id: create(:organization_admin, organization: organization).id
}
end
Expand All @@ -30,10 +30,34 @@
end

describe "GET #index" do
it "is successful" do
Audit.create! valid_attributes
get audits_path
expect(response).to be_successful
context "html" do
it "is successful" do
Audit.create! valid_attributes
get audits_path
expect(response).to be_successful
end
end

context "csv" do
let(:response_format) { 'csv' }
let(:csv_body) { "Stubbed CSV" }

before do
allow(Audit).to receive(:generate_csv).and_return(csv_body)
end

it "succeeds" do
get audits_path(format: response_format)
expect(response.content_type).to eq("text/csv")
expect(response).to be_successful
end

it "returns a CSV" do
get audits_path(format: response_format)

expect(Audit).to have_received(:generate_csv)
expect(response.body).to eq(csv_body)
end
end
end

Expand Down