Skip to content

Commit 05aaec9

Browse files
authored
Merge pull request #4179 from rubyforgood/invalid-fixes
Disable inactivating item with inventory and don't show inactive item…
2 parents 3facc8d + c81dbac commit 05aaec9

20 files changed

Lines changed: 317 additions & 193 deletions

app/controllers/items_controller.rb

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,12 @@
22
# they like with their own Items.
33
class ItemsController < ApplicationController
44
def index
5-
@items = current_organization.items.includes(:base_item, :kit).alphabetized.class_filter(filter_params)
5+
@items = current_organization
6+
.items
7+
.includes(:base_item, :kit, :line_items)
8+
.alphabetized
9+
.class_filter(filter_params)
10+
.group('items.id')
611
@items = @items.active unless params[:include_inactive_items]
712

813
@item_categories = current_organization.item_categories.includes(:items).order('name ASC')
@@ -94,10 +99,27 @@ def update
9499
end
95100
end
96101

102+
def deactivate
103+
item = current_organization.items.find(params[:id])
104+
begin
105+
item.deactivate!
106+
rescue => e
107+
flash[:error] = e.message
108+
redirect_back(fallback_location: items_path)
109+
return
110+
end
111+
112+
flash[:notice] = "#{item.name} has been deactivated."
113+
redirect_to items_path
114+
end
115+
97116
def destroy
98117
item = current_organization.items.find(params[:id])
99-
ActiveRecord::Base.transaction do
100-
item.destroy
118+
item.destroy
119+
if item.errors.any?
120+
flash[:error] = item.errors.full_messages.join("\n")
121+
redirect_back(fallback_location: items_path)
122+
return
101123
end
102124

103125
flash[:notice] = "#{item.name} has been removed."

app/models/item.rb

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ class Item < ApplicationRecord
9494
.or(where("base_items.category = 'Miscellaneous'"))
9595
}
9696

97+
before_destroy :validate_destroy, prepend: true
98+
9799
def self.barcoded_items
98100
joins(:barcode_items).order(:name).group(:id)
99101
end
@@ -111,20 +113,49 @@ def self.reactivate(item_ids)
111113
Item.where(id: item_ids).find_each { |item| item.update(active: true) }
112114
end
113115

116+
def has_inventory?(inventory = nil)
117+
if inventory
118+
inventory.quantity_for(item_id: id).positive?
119+
else
120+
inventory_items.where("quantity > 0").any?
121+
end
122+
end
123+
124+
def is_in_kit?
125+
organization.kits
126+
.active
127+
.joins(:line_items)
128+
.where(line_items: { item_id: id}).any?
129+
end
130+
131+
def can_delete?(inventory = nil)
132+
can_deactivate_or_delete?(inventory) && line_items.none? && !barcode_count&.positive?
133+
end
134+
114135
# @return [Boolean]
115-
def can_deactivate?
136+
def can_deactivate_or_delete?(inventory = nil)
137+
if inventory.nil? && Event.read_events?(organization)
138+
inventory = View::Inventory.new(organization_id)
139+
end
116140
# Cannot deactivate if it's currently in inventory in a storage location. It doesn't make sense
117141
# to have physical inventory of something we're now saying isn't valid.
118-
inventory_items.where("quantity > 0").none? &&
119-
# If an active kit includes this item, then changing kit allocations would change inventory
120-
# for an inactive item - which we said above we don't want to allow.
121-
organization.kits
122-
.active
123-
.joins(:line_items)
124-
.where(line_items: { item_id: id}).none?
142+
# If an active kit includes this item, then changing kit allocations would change inventory
143+
# for an inactive item - which we said above we don't want to allow.
144+
145+
!has_inventory?(inventory) && !is_in_kit?
146+
end
147+
148+
def validate_destroy
149+
unless can_delete?
150+
errors.add(:base, "Cannot delete item - it has already been used!")
151+
throw(:abort)
152+
end
125153
end
126154

127-
def deactivate
155+
def deactivate!
156+
unless can_deactivate_or_delete?
157+
raise "Cannot deactivate item - it is in a storage location or kit!"
158+
end
128159
if kit
129160
kit.deactivate
130161
else
@@ -136,27 +167,6 @@ def other?
136167
partner_key == "other"
137168
end
138169

139-
# Override `destroy` to ensure Item isn't accidentally destroyed
140-
# without first being disassociated with its historical presence
141-
def destroy
142-
if has_history?
143-
deactivate
144-
else
145-
super
146-
end
147-
end
148-
149-
def has_history?
150-
return true if line_items.any? || barcode_items.any?
151-
152-
if Event.read_events?(organization)
153-
inventory = View::Inventory.new(organization_id)
154-
inventory.quantity_for(item_id: id).positive?
155-
else
156-
inventory_items.any?
157-
end
158-
end
159-
160170
def self.gather_items(current_organization, global = false)
161171
if global
162172
where(id: current_organization.barcode_items.all.pluck(:barcodeable_id))

app/models/view/inventory.rb

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def initialize(organization_id, event_time: nil)
2323
# @param event_time [DateTime]
2424
def reload(event_time = nil)
2525
@inventory = InventoryAggregate.inventory_for(organization_id, event_time: event_time)
26-
@items = Item.where(organization_id: organization_id)
26+
@items = Item.where(organization_id: organization_id).active
2727
@db_storage_locations = StorageLocation.where(organization_id: organization_id).active_locations
2828
load_item_details
2929
end
@@ -38,9 +38,12 @@ def storage_location_name(id)
3838
# @param include_omitted [Boolean]
3939
# @return [Array<EventTypes::EventItem>]
4040
def items_for_location(storage_location_id, include_omitted: false)
41-
items = @inventory.storage_locations[storage_location_id]&.items&.values || []
41+
items = @inventory.storage_locations[storage_location_id]
42+
&.items
43+
&.values
44+
&.select { |i| i.quantity.positive? } || []
4245
if include_omitted
43-
db_items = Item.where(organization_id: @inventory.organization_id).where.not(id: items.map(&:item_id))
46+
db_items = Item.active.where(organization_id: @inventory.organization_id).where.not(id: items.map(&:item_id))
4447
zero_items = db_items.map do |item|
4548
ViewInventoryItem.new(
4649
item_id: item.id,
@@ -113,14 +116,17 @@ def all_items
113116

114117
def load_item_details
115118
@inventory.storage_locations.values.each do |loc|
116-
loc.items.values.each do |item|
119+
loc.items.delete_if do |_, item|
117120
db_item = @items.find { |i| i.id == item.item_id }
121+
next true if db_item.nil?
122+
118123
loc.items[item.item_id] = ViewInventoryItem.new(
119124
item_id: item.item_id,
120125
storage_location_id: loc.id,
121126
quantity: item.quantity,
122127
db_item: db_item
123128
)
129+
false
124130
end
125131
end
126132
end

app/queries/items_by_storage_collection_and_quantity_query.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
class ItemsByStorageCollectionAndQuantityQuery
55
def self.call(organization:, filter_params:, inventory: nil)
66
if inventory
7-
items = organization.items.order(name: :asc).class_filter(filter_params)
7+
items = organization.items.active.order(name: :asc).class_filter(filter_params)
88
return items.to_h do |item|
99
locations = inventory.storage_locations_for_item(item.id).map do |sl|
1010
{

app/views/items/_form.html.erb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@
4343
<%= f.input_field :package_size, class: "form-control" %>
4444
<% end %>
4545

46-
<%= f.input :active, label: "Item is Active?", wrapper: :input_group do %>
47-
<%= f.check_box :active, {class: "input-group-text", id: "active"}, "true", "false" %>
48-
<% end %>
49-
5046
<%= f.input :visible, label: "Item is Visible to Partners?", wrapper: :input_group do %>
5147
<%= f.check_box :visible_to_partners, {class: "input-group-text", id: "visible_to_partners"}, "true", "false" %>
5248
<% end %>

app/views/items/_item_list.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
</tr>
1515
</thead>
1616
<tbody>
17-
<%= render partial: "items/item_row", collection: items, as: :item_row %>
17+
<%= render partial: "items/item_row", collection: items, as: :item_row, locals: { inventory: inventory } %>
1818
</tbody>
1919
</table>
2020
</div><!-- /.box-body.table-responsive -->

app/views/items/_item_row.html.erb

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,19 @@
66
<td class="text-right">
77
<%= view_button_to item_path(item_row) %>
88
<%= edit_button_to edit_item_path(item_row) %>
9-
<% if item_row.kit.blank? %>
10-
<%= delete_button_to item_path(item_row), { confirm: confirm_delete_msg(item_row.name) } if item_row.active %>
9+
<% if item_row.active? %>
10+
<% if item_row.can_delete?(inventory) %>
11+
<%= delete_button_to item_path(item_row),
12+
text: 'Delete',
13+
confirm: confirm_delete_msg(item_row.name) %>
14+
<% else %>
15+
<% can_deactivate = item_row.can_deactivate_or_delete?(inventory) %>
16+
<%= delete_button_to deactivate_item_path(item_row),
17+
text: 'Deactivate',
18+
enabled: can_deactivate,
19+
confirm: confirm_deactivate_msg(item_row.name) %>
1120
<% end %>
21+
<% end %>
1222
<%= restore_button_to restore_item_path(item_row), { confirm: confirm_restore_msg(item_row.name) } unless item_row.active %>
1323
</td>
1424
</tr>

app/views/items/index.html.erb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
</div>
9494
<div class="card-body">
9595
<div class="tab-content" id="custom-tabs-three-tabContent">
96-
<%= render partial: 'item_list', locals: { items: @items } %>
96+
<%= render partial: 'item_list', locals: { items: @items, inventory: @inventory } %>
9797
<%= render partial: 'item_categories', locals: { item_categories: @item_categories } %>
9898
<%= render partial: 'items_quantity_and_location' %>
9999
<%= render partial: 'items_inventory' %>

app/views/storage_locations/show.html.erb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,15 +96,15 @@
9696
</thead>
9797
<tbody>
9898
<% if @inventory %>
99-
<% @inventory.items_for_location(@storage_location.id).each do |item| %>
99+
<% @inventory.items_for_location(@storage_location.id, include_omitted: true).each do |item| %>
100100
<tr>
101101
<td><%= link_to item.name, item_path(item.item_id) %></td>
102102
<td><%= number_with_delimiter(item.quantity) %></td>
103103
</tr>
104104
<% end %>
105105
<% else %>
106106
<%= render partial: "inventory_item_row",
107-
collection: @storage_location.inventory_items,
107+
collection: @storage_location.inventory_items.joins(:item).where(items: { active: true }),
108108
locals: { version_date: params[:version_date] } %>
109109
<% end %>
110110
</tbody>

config/routes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ def set_up_flipper
166166

167167
resources :profiles, only: %i(edit update)
168168
resources :items do
169+
delete :deactivate, on: :member
169170
patch :restore, on: :member
170171
patch :remove_category, on: :member
171172
end

0 commit comments

Comments
 (0)