diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a1c7e260a3..f8a23fd295 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,13 +17,21 @@ You won't be yelled at for giving your best effort. The worst that can happen is
# Getting Started
## Local Environment 🛠️
+
+#### Install WSL2 first if Using Windows
+Follow [documentation](https://docs.microsoft.com/en-us/windows/wsl/install-win10) from Microsoft for enabling and installing Windows Subsystem For Linux 2 on your machine.
+
+Make sure to install **Ubuntu** as your Linux distribution. (This should be default.)
+
+**Note:** If you run into any issues with a command not running, restart your machine.
+
+
1. Install Ruby
- Install the version specified in [`.ruby-version`](.ruby-version).
- Visit the [Install Ruby on Rails](https://gorails.com/setup/osx/12-monterey) guide by GoRails for Ubuntu, Windows, and macOSX setup. ⚠️ Follow only the Installing Ruby step, as our project setup differs ⚠️ It is highly recommended you use a ruby version manager such as [rbenv](https://github.com/rbenv/rbenv), [asdf](https://asdf-vm.com/), or [rvm](https://rvm.io/).
- Verify that your Ruby installation works by running `ruby -v`.
2. Install Postgres
- Follow one of these guides: [MacOSX](https://www.digitalocean.com/community/tutorials/how-to-use-postgresql-with-your-ruby-on-rails-application-on-macos), [Ubuntu](https://www.digitalocean.com/community/tutorials/how-to-use-postgresql-with-your-ruby-on-rails-application-on-ubuntu-18-04).
- - Do you develop on Windows? We'd love to hear (and for you to submit a PR explaining) how you do it. 🙏🏻
- Create a `database.yml` file on `config/` directory with your database configurations. You can also copy the existing files called [`database.yml.example`](config/database.yml.example) and [`.env.example`](.env.example) and change the credentials.
3. Clone the project and switch to its directory
4. Run `bin/setup`
diff --git a/Gemfile b/Gemfile
index 8bc1e4f459..d3d0473d11 100644
--- a/Gemfile
+++ b/Gemfile
@@ -150,14 +150,14 @@ group :development, :test do
# Debugger which supports rdbg and Shopify Ruby LSP VSCode extension
gem "debug", ">= 1.0.0"
# RSpec behavioral testing framework for Rails.
- gem "rspec-rails", "~> 6.1.3"
+ gem "rspec-rails", "~> 6.1.4"
# Static analysis / linter.
gem "rubocop"
# Rails add-on for static analysis.
gem 'rubocop-performance'
gem "rubocop-rails", "~> 2.25.1"
# Default rules for Rubocop.
- gem "standard", "~> 1.39"
+ gem "standard", "~> 1.40"
# Erb linter.
gem "erb_lint"
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 54039a468f..39090ec92c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -141,7 +141,7 @@ GEM
activesupport
tzinfo
coderay (1.1.3)
- concurrent-ruby (1.3.3)
+ concurrent-ruby (1.3.4)
connection_pool (2.4.1)
coverband (6.1.2)
redis (>= 3.0)
@@ -243,7 +243,7 @@ GEM
faraday-httpclient (1.0.1)
faraday-multipart (1.0.4)
multipart-post (~> 2)
- faraday-net_http (1.0.1)
+ faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
@@ -315,7 +315,7 @@ GEM
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.7.2)
- irb (1.13.2)
+ irb (1.14.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jbuilder (2.12.0)
@@ -373,7 +373,7 @@ GEM
method_source (1.1.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
- minitest (5.24.1)
+ minitest (5.25.1)
monetize (1.12.0)
money (~> 6.12)
money (6.16.0)
@@ -383,8 +383,9 @@ GEM
monetize (~> 1.9)
money (~> 6.13)
railties (>= 3.0)
- multi_xml (0.6.0)
- multipart-post (2.4.0)
+ multi_xml (0.7.1)
+ bigdecimal (~> 3.1)
+ multipart-post (2.4.1)
mustermann (3.0.0)
ruby2_keywords (~> 0.0.1)
mutex_m (0.2.0)
@@ -400,7 +401,7 @@ GEM
timeout
net-smtp (0.5.0)
net-protocol
- newrelic_rpm (9.12.0)
+ newrelic_rpm (9.13.0)
nio4r (2.7.3)
nokogiri (1.16.7-arm64-darwin)
racc (~> 1.4)
@@ -422,7 +423,7 @@ GEM
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
- omniauth-google-oauth2 (1.1.2)
+ omniauth-google-oauth2 (1.1.3)
jwt (>= 2.0)
oauth2 (~> 2.0)
omniauth (~> 2.0)
@@ -440,8 +441,8 @@ GEM
paper_trail (15.1.0)
activerecord (>= 6.1)
request_store (~> 1.4)
- parallel (1.25.1)
- parser (3.3.4.0)
+ parallel (1.26.3)
+ parser (3.3.4.2)
ast (~> 2.4.1)
racc
pdf-core (0.9.0)
@@ -552,7 +553,7 @@ GEM
responders (3.1.1)
actionpack (>= 5.2)
railties (>= 5.2)
- rexml (3.3.4)
+ rexml (3.3.6)
strscan
rolify (6.0.1)
rouge (4.1.2)
@@ -562,13 +563,13 @@ GEM
rspec-mocks (~> 3.13.0)
rspec-core (3.13.0)
rspec-support (~> 3.13.0)
- rspec-expectations (3.13.1)
+ rspec-expectations (3.13.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
rspec-mocks (3.13.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0)
- rspec-rails (6.1.3)
+ rspec-rails (6.1.4)
actionpack (>= 6.1)
activesupport (>= 6.1)
railties (>= 6.1)
@@ -577,18 +578,18 @@ GEM
rspec-mocks (~> 3.13)
rspec-support (~> 3.13)
rspec-support (3.13.1)
- rubocop (1.64.1)
+ rubocop (1.65.1)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
- regexp_parser (>= 1.8, < 3.0)
+ regexp_parser (>= 2.4, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.31.1, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
- rubocop-ast (1.31.3)
+ rubocop-ast (1.32.1)
parser (>= 3.3.1.0)
rubocop-performance (1.21.1)
rubocop (>= 1.48.1, < 2.0)
@@ -648,10 +649,10 @@ GEM
actionpack (>= 5.2)
activesupport (>= 5.2)
sprockets (>= 3.0.0)
- standard (1.39.2)
+ standard (1.40.0)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.0)
- rubocop (~> 1.64.0)
+ rubocop (~> 1.65.0)
standard-custom (~> 1.0.0)
standard-performance (~> 1.4)
standard-custom (1.0.2)
@@ -660,7 +661,7 @@ GEM
standard-performance (1.4.0)
lint_roller (~> 1.1)
rubocop-performance (~> 1.21.0)
- stimulus-rails (1.3.3)
+ stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.1.1)
strong_migrations (1.8.0)
@@ -668,7 +669,7 @@ GEM
strscan (3.1.0)
terser (1.2.3)
execjs (>= 0.3.0, < 3)
- thor (1.3.1)
+ thor (1.3.2)
tilt (2.2.0)
timeout (0.4.1)
ttfunk (1.7.0)
@@ -699,7 +700,7 @@ GEM
xpath (3.2.0)
nokogiri (~> 1.8)
yard (0.9.34)
- zeitwerk (2.6.16)
+ zeitwerk (2.6.17)
PLATFORMS
arm64-darwin-20
@@ -782,7 +783,7 @@ DEPENDENCIES
recaptcha
redis (~> 5.2)
rolify (~> 6.0)
- rspec-rails (~> 6.1.3)
+ rspec-rails (~> 6.1.4)
rubocop
rubocop-performance
rubocop-rails (~> 2.25.1)
@@ -791,7 +792,7 @@ DEPENDENCIES
simple_form
simplecov
sprockets (~> 4.2.1)
- standard (~> 1.39)
+ standard (~> 1.40)
stimulus-rails
strong_migrations (= 1.8.0)
terser
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index d2ee0e734a..74c027ce0e 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -192,3 +192,30 @@ div.low_priority_warning {
margin: 5px;
text-align: center;
}
+
+.distribution-title {
+ display: flex;
+}
+
+legend.with-request {
+ float: none;
+ width: 80%;
+ display: inline-block;
+}
+
+div.distribution-request-unit {
+ display: inline-block;
+ flex: 1;
+ text-align: right;
+ margin-right: 20px;
+ font-size: 1.5em;
+}
+
+.li-requested {
+ font-size: 1.5em;
+ width: 100px;
+ min-width: 100px;
+ text-align: right;
+ margin-right: 20px;
+ white-space: nowrap;
+}
diff --git a/app/controllers/distributions_controller.rb b/app/controllers/distributions_controller.rb
index e2a10393a9..f7f0e0d62a 100644
--- a/app/controllers/distributions_controller.rb
+++ b/app/controllers/distributions_controller.rb
@@ -109,7 +109,11 @@ def create
# does not match any known Request
@distribution.request = Request.find(request_id)
end
- @distribution.line_items.build if @distribution.line_items.size.zero?
+ if @distribution.line_items.size.zero?
+ @distribution.line_items.build
+ elsif request_id
+ @distribution.initialize_request_items
+ end
@items = current_organization.items.alphabetized
if Event.read_events?(current_organization)
inventory = View::Inventory.new(@distribution.organization_id)
@@ -166,6 +170,7 @@ def show
def edit
@distribution = Distribution.includes(:line_items).includes(:storage_location).find(params[:id])
+ @distribution.initialize_request_items
if (!@distribution.complete? && @distribution.future?) ||
current_user.has_role?(Role::ORG_ADMIN, current_organization)
@distribution.line_items.build if @distribution.line_items.size.zero?
@@ -202,6 +207,7 @@ def update
else
flash[:error] = insufficient_error_message(result.error.message)
@distribution.line_items.build if @distribution.line_items.size.zero?
+ @distribution.initialize_request_items
@items = current_organization.items.alphabetized
@storage_locations = current_organization.storage_locations.active_locations.alphabetized
render :edit
diff --git a/app/models/distribution.rb b/app/models/distribution.rb
index f8d591e25b..bc5a652e9f 100644
--- a/app/models/distribution.rb
+++ b/app/models/distribution.rb
@@ -102,6 +102,31 @@ def copy_from_donation(donation_id, storage_location_id)
self.storage_location = StorageLocation.find(storage_location_id) if storage_location_id
end
+ # This is meant for the Edit page - we will be adding any request items that aren't in the
+ # distribution for whatever reason, with zero quantity.
+ def initialize_request_items
+ return if request.nil?
+
+ item_ids = Set.new
+ line_items.each do |line_item|
+ item_request = request.item_requests.find { |r| r.item_id == line_item.item_id }
+ if item_request
+ item_ids.add(item_request)
+ line_item.requested_item = item_request
+ end
+ end
+
+ request.item_requests.each do |item_request|
+ next if item_ids.include?(item_request)
+
+ line_items.new(
+ requested_item: item_request,
+ quantity: 0,
+ item_id: item_request.item_id
+ )
+ end
+ end
+
def copy_from_request(request_id)
request = Request.find(request_id)
self.request = request
@@ -110,12 +135,12 @@ def copy_from_request(request_id)
self.agency_rep = request.partner_user&.formatted_email
self.comment = request.comments
self.issued_at = Time.zone.today + 1.day
- request.request_items.each do |item|
+ request.item_requests.each do |item_request|
line_items.new(
- quantity: item["quantity"],
- item: Item.eager_load(:base_item).find_by(organization: request.organization, id: item["item_id"]),
- itemizable_id: request.id,
- itemizable_type: "Distribution"
+ requested_item: item_request,
+ # if there is a custom unit, don't prefill with the quantity - they have to enter it
+ quantity: item_request.request_unit.present? ? nil : item_request.quantity,
+ item_id: item_request.item_id
)
end
end
diff --git a/app/models/line_item.rb b/app/models/line_item.rb
index e93a52e047..8cdcb79b7c 100644
--- a/app/models/line_item.rb
+++ b/app/models/line_item.rb
@@ -28,6 +28,10 @@ class LineItem < ApplicationRecord
delegate :name, to: :item
+ # Used in a distribution that was initialized from a request. The `item_request` will be
+ # populated here.
+ attr_accessor :requested_item
+
def quantity_must_be_a_number_within_range
if quantity && quantity > MAX_INT
errors.add(:quantity, "must be less than #{MAX_INT}")
diff --git a/app/services/distribution_update_service.rb b/app/services/distribution_update_service.rb
index 5889d2a622..da612d0f95 100644
--- a/app/services/distribution_update_service.rb
+++ b/app/services/distribution_update_service.rb
@@ -9,6 +9,9 @@ def call
perform_distribution_service do
@old_issued_at = distribution.issued_at
@old_delivery_method = distribution.delivery_method
+ @params[:line_items_attributes]&.delete_if { |_, a| a[:quantity].to_i.zero? }
+
+ # remove line_items with zero quantity
ItemizableUpdateService.call(
itemizable: distribution,
diff --git a/app/views/distributions/_form.html.erb b/app/views/distributions/_form.html.erb
index 2948b48d6c..b72329f950 100644
--- a/app/views/distributions/_form.html.erb
+++ b/app/views/distributions/_form.html.erb
@@ -33,13 +33,18 @@
<%= f.input :comment, label: "Comment" %>