Skip to content

Commit

Permalink
Request Inventory Match (#121)
Browse files Browse the repository at this point in the history
* Add status to Request

Requests default to ‘pending’ when created. Volunteers may optionally claim the request or mark it ready for delivery when items have been pulled from inventory.

Adds the following methods:
- For a given request: `.pending?`, `.claimed?`, `.delivery_ready?`
- For all requests: `Request.pending`, `Request.claimed`, `Request.delivery_ready` will give lists of all Requests with the used status

* Added initial volunteer requests view

* Extracted shared block to partial

* Added seeding of Requests and its item changes

* add ability to change status of request for volunteers

* add blank checkoxes

* Add settled to item_change

* Add item settling to interface

* add limited stock & out of stock messages, started ability to remove from inventory

* add limited/outof stock message to item selection for volunteers

* Moved settle view to seperate view

Co-authored-by: Nicholas Grana <[email protected]>
  • Loading branch information
2 people authored and tumbleshack committed Apr 24, 2021
1 parent d1dbd5c commit d9ce3d8
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 11 deletions.
49 changes: 46 additions & 3 deletions app/controllers/requests_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class RequestsController < ApplicationController
before_action :set_request, only: %i[ show edit update destroy ]
before_action :set_request, only: %i[ show edit update destroy settle ]

before_action :authenticate_user!, only: %i[ index, my_requests ]

Expand All @@ -18,6 +18,45 @@ def index
def show
@allCategories = Category.all
@allItems = Item.all
@itemsToStock = @request.getItemStock()

end

def volunteer
end

def settle
@show_settled = true
end

def next_status
@request = Request.find(params[:format])
@request.status = @request.status == "pending" ? "delivery_ready" : "claimed"

if @request.status == "claimed"
# iterate through items table and update quantity
for item in @request.item_changes
# match item for inventory
matchingItems = Item.where(:itemType => item.itemType, :size => item.size, :category_id => item.category_id)
quantityFulfilled = item.quantity

# contiously remove from each item until the set quantity has been removed
for matchedItem in matchingItems
if matchedItem.quantity <= quantityFulfilled
# perform the update quantity
# ex: quantityFulfilled = 20, and amount in inventory = 15, then quantityFulfilled becomes 5 and amount in inventory should become 0
# it will then continue to loop to see if there are any more items it can remove
amountToRemove = quantityFulfilled
quantityFulfilled -= matchedItem.quantity
matchedItem.quantity -= amountToRemove
matchedItem.save
end
end
end
end
@request.save

redirect_to @request
end

# GET /requests/new
Expand Down Expand Up @@ -74,8 +113,12 @@ def update
@request.items = "#{request_params["items_quantity"]}x #{request_params["items_category"]} #{request_params["items_itemType"]} Size #{request_params["items_sizes"]}"
@request.save

format.html { redirect_to @request, notice: "Request was successfully updated." }
format.json { render :show, status: :ok, location: @request }
if request_params[:send_to_settle] && current_user&.volunteer?
format.html { redirect_to settle_request_path(@request), notice: "Request was successfully updated." }
else
format.html { redirect_to @request, notice: "Request was successfully updated." }
format.json { render :show, status: :ok, location: @request }
end
else
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @request.errors, status: :unprocessable_entity }
Expand Down
4 changes: 4 additions & 0 deletions app/models/item_change.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ class ItemChange < ApplicationRecord

default_scope { order(quantity: :desc) }

def description
return "#{category.name} #{itemType} (Size #{size}) x #{quantity}"
end

end
46 changes: 45 additions & 1 deletion app/models/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ class Request < ApplicationRecord
'Other (provide your address below)': 8
}.freeze

enum status: { pending: 0, claimed: 1, delivery_ready: 2 }
# scope :pending, -> { where(status: :pending) }
# scope :claimed, -> { where(status: :claimed) }
# scope :delivery_ready, -> { where(status: :delivery_ready) }

has_many :item_changes
accepts_nested_attributes_for :item_changes, allow_destroy: true

Expand All @@ -51,5 +56,44 @@ def any_blank(att)

validates :full_name,
length: { in: 2..80 }


# Returns how much time is left before the request is "past due," i.e. the
# set urgency window has passed
def due_time
offset = 1.day
case urgency
when URGENCIES.fetch(:"Within 24 hours"); offset = 1.day
when URGENCIES.fetch(:"Within 48 hours"); offset = 2.days
when URGENCIES.fetch(:"Within a week"); offset = 1.week
end

created_at + offset
end

# Returns a hash which stores the inventory items that match each element inside request.item_changes to the actual inventory quantity.
# If the item is either out of stock or in limited stock it will show up in this hash, otherwise it will not.
# So, an item that has enough stock to fulfill the order will not be in the hash, otherwise it will have the form:
# [itemChangeID: inventoryStock]. If someone requested 15 Boy's Size 6 Shoe's with ID 2 and there were only 5 in stock it would show
# [2: 5], where 2 is the ID of the 15 Boy's Size 6 in item_change and 5 is the actual stock in the "items" table
def getItemStock
itemsToStock = {}
for item in item_changes
# match item for inventory
matchingItems = Item.where(:itemType => item.itemType, :size => item.size, :category_id => item.category_id)

# loop through and calculate the total inventory size
inventoryQuantity = 0
for matchedItem in matchingItems
inventoryQuantity += matchedItem.quantity
end

# this indicates there is not enough stock, so store the inventory quantity
if inventoryQuantity < item.quantity
itemsToStock[item.id] = inventoryQuantity
end
end

return itemsToStock
end

end
34 changes: 30 additions & 4 deletions app/views/form_helpers/_item_selection.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -212,28 +212,54 @@

</script>

<% send_to_settle ||= false %>

<div class="field">
<%= form.label :items, 'What items would you like to request?' %>
<table id="items-table">
<thead>
<tr>
<th>Quantity</th>
<th>Category</th>
<th>Item Type</th>
<th>Size</th>
<% if current_user&.volunteer? && send_to_settle %>
<th>Settled</th>
<th>Quantity</th>
<th>Description</th>
<% else %>
<th>Quantity</th>
<th>Category</th>
<th>Item Type</th>
<th>Size</th>
<% end %>
<th></th>
</tr>
</thead>

<% @itemsToStock = @request.getItemStock() %>
<tbody id="table-body">
<%= form.fields_for :item_changes do |item_change_form| %>
<% item_change = item_change_form.instance_variable_get(:@object) %>

<tr data-id=<%=item_change_form.index%>>
<% if current_user&.volunteer? && send_to_settle %>
<td><%= item_change_form.number_field :settled %></td>
<td><%= item_change.quantity %></td>
<td>
<%= item_change.description %>
<% if @itemsToStock.keys.any?(item_change.id) %>
<% if @itemsToStock[item_change.id] > 0 %>
- Limited Stock (<%= @itemsToStock[item_change.id] =%>)
<% else %>
- Out of Stock
<% end %>
<% end %>
</td>
<% else %>
<td><%= item_change_form.number_field :quantity %></td>
<td><%= item_change_form.select :category_id, Category.all.map { |category| [ category.name, category.id ] }, options = {}, html_options = {:onchange => "updateItemTypes(this)", :data => { selectorType: "category" }} %></td>
<td><%= item_change_form.select :itemType, Item.all.map{ |item| item.itemType }, options = {}, html_options = {:onchange => "updateItemSizes(this)", :data => { selectorType: "itemType" }} %></td>
<td><%= item_change_form.select :size, Item.all.map{ |item| item.size }, options = {}, html_options = {:data => { selectorType: "size" }} %></td>
<td hidden><%= item_change_form.hidden_field :change_type, value:ItemChange::CHANGE_TYPES.fetch(parent_f) %></td>
<td hidden><%= item_change_form.hidden_field :_destroy, html_options = {:onchange => "updateItemSizes(this)", :data => { type: "destroy" }}%></td>
<% end %>
<td><button type="button" onclick="removeItemRow(this);">Remove</button></td>
</tr>
<%end%>
Expand Down
7 changes: 7 additions & 0 deletions app/views/public/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,13 @@
<br />
<% end %>

<% if current_user&.volunteer? %>
<div class="centered-div">
<%= link_to "Volunteer Requests", requests_volunteer_path, class: "home-link" %>
</div>
<br />
<% end %>

<div class = "centered-div">
<%= link_to 'Request Clothing and Hygiene Items', new_request_url, class: "home-link"%>
</div>
Expand Down
25 changes: 25 additions & 0 deletions app/views/requests/_item_change_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

<%= form_with(model: request, html: { class: 'pure-form pure-form-stacked' }) do |form| %>

<% if request.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(request.errors.count, "error") %> prohibited this request from being saved:</h2>

<ul>
<% request.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="margins-needed">
<%= render partial: 'form_helpers/item_selection', locals: { form: form, parent_f: :request, send_to_settle: send_to_settle } %>

<div class="actions">
<% send_to_settle ||= false %>
<%= form.hidden_field :send_to_settle, value: send_to_settle %>
<%= form.submit %>
</div>
</div>
<% end %>
27 changes: 27 additions & 0 deletions app/views/requests/_volunteer_request_block.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<div class="volunteer-request-list-block">
<% @itemsToStock = request.getItemStock() %>
<ul>
<% request.item_changes.limit(5).each do |item| %>
<li>
<%= item.description %>
<% if @itemsToStock.keys.any?(item.id) %>
<% if @itemsToStock[item.id] > 0 %>
- Limited Stock (<%= @itemsToStock[item.id] =%>)
<% else %>
- Out of Stock
<% end %>
<% end %>

</li>

<% end %>

<% if request.item_changes.count > 5 %>
<li><i><%= request.item_changes.count - 5 %> more items</i></li>
<% end %>
</ul>

<b><i>Due in <%= time_ago_in_words(request.due_time) %></i></b>

<p><%= link_to 'View', settle_request_path(request) %></p>
</div>
78 changes: 78 additions & 0 deletions app/views/requests/settle.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@

<div class="margins-needed">
<h1>Request a Donation: Summary</h1>
<h2>Please review and confirm the summary of your request:</h2>

<p>
<strong>Urgency:</strong>
<%= Request::URGENCIES.key(@request.urgency) %>
</p>

<p>
<strong>Full name:</strong>
<%= @request.full_name %>
</p>

<p>
<strong>Email:</strong>
<%= @request.email %>
</p>

<p>
<strong>Phone:</strong>
<%= @request.phone %>
</p>

<% if @request.meet %>
<p>
<strong>County:</strong>
<%= Request::COUNTIES.key(@request.county) %>

</p>
<% else %>
<p>
<strong>Address:</strong>
<%= @request.address %>
</p>
<% end %>

<p>
<strong>Availability:</strong>
<%= @request.availability %>
</p>

<p>
<strong>Comments:</strong>
<%= @request.comments %>
</p>

<% if current_user&.volunteer? %>
<strong>Order Status:</strong>
<%= @request.status.titleize %>
<br>
<% end %>

<p>
<strong>Requested Items:</strong>
<% if current_user&.volunteer? %>
<%= render partial: "item_change_form", locals: { request: @request, send_to_settle: current_user&.volunteer? } %>
<% else %>
<%@request.item_changes.each do |item|%>
<p><%= item.description %>
<%end%>
<% end %>
</p>

<% if current_user&.volunteer? && @request.status == "pending" %>
<%= link_to "Mark Ready for Delivery", requests_next_status_path(@request), class: "home-link" %>
<br>
<% end %>

<% if current_user&.volunteer? && @request.status == "delivery_ready" %>
<%= link_to "Mark Order Complete", requests_next_status_path(@request), class: "home-link" %>
<br>
<% end %>

<%= link_to 'Edit', edit_request_path(@request) %> |
<%= link_to 'Back', :back %>
</div>
23 changes: 20 additions & 3 deletions app/views/requests/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,34 @@
</p>

<p>
<strong>Items:</strong>
<%@request.item_changes.each do |item|%>
<p><%= item.quantity %> <%= Category.find(item.category_id).name %> <%= item.itemType %>, size: <%= item.size %></p>
<strong>Requested Items:</strong>
<% @request.item_changes.each do |item|%>
<p><%= item.quantity %> <%= Category.find(item.category_id).name %> <%= item.itemType %>, size: <%= item.size %></p>
<%end%>

</p>

<p>
<strong>Comments:</strong>
<%= @request.comments %>
</p>

<% if current_user&.volunteer? %>
<strong>Order Status:</strong>
<%= @request.status.titleize %>
<br>
<% end %>

<% if current_user&.volunteer? && @request.status == "pending" %>
<%= link_to "Mark Ready for Delivery", requests_next_status_path(@request), class: "home-link" %>
<br>
<% end %>

<% if current_user&.volunteer? && @request.status == "delivery_ready" %>
<%= link_to "Mark Order Complete", requests_next_status_path(@request), class: "home-link" %>
<br>
<% end %>

<%= link_to 'Edit', edit_request_path(@request) %> |
<%= link_to 'Back', :back %>
</div>
9 changes: 9 additions & 0 deletions app/views/requests/volunteer.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<h1>Pending Requests (<%= Request.pending.count %>)</h1>
<% Request.pending.each do |pending_request| %>
<%= render partial: 'volunteer_request_block', locals: { request: pending_request } %>
<% end %>

<h1>Ready to Deliver (<%= Request.delivery_ready.count %>)</h1>
<% Request.delivery_ready.each do |delivery_ready| %>
<%= render partial: 'volunteer_request_block', locals: { request: delivery_ready } %>
<% end %>
Loading

0 comments on commit d9ce3d8

Please sign in to comment.