diff --git a/app/controllers/requests_controller.rb b/app/controllers/requests_controller.rb index b6787e5..c4a31db 100644 --- a/app/controllers/requests_controller.rb +++ b/app/controllers/requests_controller.rb @@ -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 ] @@ -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 @@ -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 } diff --git a/app/models/item_change.rb b/app/models/item_change.rb index 3124edc..66de039 100644 --- a/app/models/item_change.rb +++ b/app/models/item_change.rb @@ -18,4 +18,8 @@ class ItemChange < ApplicationRecord default_scope { order(quantity: :desc) } + def description + return "#{category.name} #{itemType} (Size #{size}) x #{quantity}" + end + end diff --git a/app/models/request.rb b/app/models/request.rb index 39f9f63..06b84fe 100644 --- a/app/models/request.rb +++ b/app/models/request.rb @@ -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 @@ -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 diff --git a/app/views/form_helpers/_item_selection.html.erb b/app/views/form_helpers/_item_selection.html.erb index 080262c..153e5fd 100644 --- a/app/views/form_helpers/_item_selection.html.erb +++ b/app/views/form_helpers/_item_selection.html.erb @@ -212,28 +212,54 @@ +<% send_to_settle ||= false %> +
<%= form.label :items, 'What items would you like to request?' %> - - - - + <% if current_user&.volunteer? && send_to_settle %> + + + + <% else %> + + + + + <% end %> + <% @itemsToStock = @request.getItemStock() %> <%= form.fields_for :item_changes do |item_change_form| %> + <% item_change = item_change_form.instance_variable_get(:@object) %> + > + <% if current_user&.volunteer? && send_to_settle %> + + + + <% else %> + <% end %> <%end%> diff --git a/app/views/public/index.html.erb b/app/views/public/index.html.erb index 5efa468..c10e297 100644 --- a/app/views/public/index.html.erb +++ b/app/views/public/index.html.erb @@ -50,6 +50,13 @@
<% end %> + <% if current_user&.volunteer? %> +
+ <%= link_to "Volunteer Requests", requests_volunteer_path, class: "home-link" %> +
+
+ <% end %> +
<%= link_to 'Request Clothing and Hygiene Items', new_request_url, class: "home-link"%>
diff --git a/app/views/requests/_item_change_form.html.erb b/app/views/requests/_item_change_form.html.erb new file mode 100644 index 0000000..c48b16a --- /dev/null +++ b/app/views/requests/_item_change_form.html.erb @@ -0,0 +1,25 @@ + +<%= form_with(model: request, html: { class: 'pure-form pure-form-stacked' }) do |form| %> + + <% if request.errors.any? %> +
+

<%= pluralize(request.errors.count, "error") %> prohibited this request from being saved:

+ + +
+ <% end %> + +
+ <%= render partial: 'form_helpers/item_selection', locals: { form: form, parent_f: :request, send_to_settle: send_to_settle } %> + +
+ <% send_to_settle ||= false %> + <%= form.hidden_field :send_to_settle, value: send_to_settle %> + <%= form.submit %> +
+
+<% end %> diff --git a/app/views/requests/_volunteer_request_block.html.erb b/app/views/requests/_volunteer_request_block.html.erb new file mode 100644 index 0000000..8ce798e --- /dev/null +++ b/app/views/requests/_volunteer_request_block.html.erb @@ -0,0 +1,27 @@ +
+ <% @itemsToStock = request.getItemStock() %> + + + Due in <%= time_ago_in_words(request.due_time) %> + +

<%= link_to 'View', settle_request_path(request) %>

+
\ No newline at end of file diff --git a/app/views/requests/settle.html.erb b/app/views/requests/settle.html.erb new file mode 100644 index 0000000..20f5738 --- /dev/null +++ b/app/views/requests/settle.html.erb @@ -0,0 +1,78 @@ + +
+

Request a Donation: Summary

+

Please review and confirm the summary of your request:

+ +

+ Urgency: + <%= Request::URGENCIES.key(@request.urgency) %> +

+ +

+ Full name: + <%= @request.full_name %> +

+ +

+ Email: + <%= @request.email %> +

+ +

+ Phone: + <%= @request.phone %> +

+ + <% if @request.meet %> +

+ County: + <%= Request::COUNTIES.key(@request.county) %> + +

+ <% else %> +

+ Address: + <%= @request.address %> +

+ <% end %> + +

+ Availability: + <%= @request.availability %> +

+ +

+ Comments: + <%= @request.comments %> +

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

+ Requested Items: + <% 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|%> +

<%= item.description %> + <%end%> + <% end %> +

+ + <% if current_user&.volunteer? && @request.status == "pending" %> + <%= link_to "Mark Ready for Delivery", requests_next_status_path(@request), class: "home-link" %> +
+ <% end %> + + <% if current_user&.volunteer? && @request.status == "delivery_ready" %> + <%= link_to "Mark Order Complete", requests_next_status_path(@request), class: "home-link" %> +
+ <% end %> + + <%= link_to 'Edit', edit_request_path(@request) %> | + <%= link_to 'Back', :back %> +
diff --git a/app/views/requests/show.html.erb b/app/views/requests/show.html.erb index 46ffd9c..51580d5 100644 --- a/app/views/requests/show.html.erb +++ b/app/views/requests/show.html.erb @@ -46,10 +46,11 @@

- Items: - <%@request.item_changes.each do |item|%> -

<%= item.quantity %> <%= Category.find(item.category_id).name %> <%= item.itemType %>, size: <%= item.size %>

+ Requested Items: + <% @request.item_changes.each do |item|%> +

<%= item.quantity %> <%= Category.find(item.category_id).name %> <%= item.itemType %>, size: <%= item.size %>

<%end%> +

@@ -57,6 +58,22 @@ <%= @request.comments %>

+<% if current_user&.volunteer? %> + Order Status: + <%= @request.status.titleize %> +
+<% end %> + +<% if current_user&.volunteer? && @request.status == "pending" %> + <%= link_to "Mark Ready for Delivery", requests_next_status_path(@request), class: "home-link" %> +
+<% end %> + +<% if current_user&.volunteer? && @request.status == "delivery_ready" %> + <%= link_to "Mark Order Complete", requests_next_status_path(@request), class: "home-link" %> +
+<% end %> + <%= link_to 'Edit', edit_request_path(@request) %> | <%= link_to 'Back', :back %> diff --git a/app/views/requests/volunteer.html.erb b/app/views/requests/volunteer.html.erb new file mode 100644 index 0000000..18b7f7b --- /dev/null +++ b/app/views/requests/volunteer.html.erb @@ -0,0 +1,9 @@ +

Pending Requests (<%= Request.pending.count %>)

+<% Request.pending.each do |pending_request| %> + <%= render partial: 'volunteer_request_block', locals: { request: pending_request } %> +<% end %> + +

Ready to Deliver (<%= Request.delivery_ready.count %>)

+<% Request.delivery_ready.each do |delivery_ready| %> + <%= render partial: 'volunteer_request_block', locals: { request: delivery_ready } %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index bf759d7..bf30e02 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,8 +3,11 @@ resources :donations resources :categories resources :items + get 'requests/next_status' => 'requests#next_status' get '/requests/my_requests' => 'requests#my_requests' get '/requests/popup' => 'requests#popup' + get '/requests/volunteer' => 'requests#volunteer' + get '/requests/:id/settle' => 'requests#settle', as: :settle_request get '/users/manage' => 'users#manage' resources :requests get 'public/index' @@ -14,4 +17,5 @@ resources :users # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html root to: "public#index" + end diff --git a/db/migrate/20210401212902_add_status_to_request.rb b/db/migrate/20210401212902_add_status_to_request.rb new file mode 100644 index 0000000..80f15cd --- /dev/null +++ b/db/migrate/20210401212902_add_status_to_request.rb @@ -0,0 +1,5 @@ +class AddStatusToRequest < ActiveRecord::Migration[6.1] + def change + add_column :requests, :status, :integer, default: 0 + end +end diff --git a/db/migrate/20210402174537_add_settled_to_item_change.rb b/db/migrate/20210402174537_add_settled_to_item_change.rb new file mode 100644 index 0000000..339055a --- /dev/null +++ b/db/migrate/20210402174537_add_settled_to_item_change.rb @@ -0,0 +1,5 @@ +class AddSettledToItemChange < ActiveRecord::Migration[6.1] + def change + add_column :item_changes, :settle, :integer, default: 0 + end +end diff --git a/db/schema.rb b/db/schema.rb index 3261d60..fb08778 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -78,6 +78,7 @@ t.bigint "category_id" t.bigint "request_id" t.bigint "donation_id" + t.integer "settle", default: 0 t.index ["category_id"], name: "index_item_changes_on_category_id" t.index ["donation_id"], name: "index_item_changes_on_donation_id" t.index ["request_id"], name: "index_item_changes_on_request_id" @@ -106,6 +107,7 @@ t.text "comments" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.integer "status", default: 0 end create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
QuantityCategoryItem TypeSizeSettledQuantityDescriptionQuantityCategoryItem TypeSize
<%= item_change_form.number_field :settled %><%= item_change.quantity %> + <%= 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 %> + <%= item_change_form.number_field :quantity %> <%= item_change_form.select :category_id, Category.all.map { |category| [ category.name, category.id ] }, options = {}, html_options = {:onchange => "updateItemTypes(this)", :data => { selectorType: "category" }} %> <%= item_change_form.select :itemType, Item.all.map{ |item| item.itemType }, options = {}, html_options = {:onchange => "updateItemSizes(this)", :data => { selectorType: "itemType" }} %> <%= item_change_form.select :size, Item.all.map{ |item| item.size }, options = {}, html_options = {:data => { selectorType: "size" }} %>