Skip to content

Commit 43d771e

Browse files
author
Olexii Kasianenko
committed
fix(kit_creation): Add ItemUpdateService
1 parent a96b468 commit 43d771e

File tree

5 files changed

+204
-38
lines changed

5 files changed

+204
-38
lines changed

app/controllers/items_controller.rb

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,18 @@ def show
7373

7474
def update
7575
@item = current_organization.items.find(params[:id])
76-
@item.attributes = item_params
7776
deactivated = @item.active_changed? && !@item.active
7877
if deactivated && !@item.can_deactivate?
7978
flash.now[:error] = "Can't deactivate this item - it is currently assigned to either an active kit or a storage location!"
8079
render action: :edit
8180
return
8281
end
82+
result = ItemUpdateService.new(item: @item, params: item_params, request_unit_ids: request_unit_ids).call
8383

84-
if update_item
84+
if result.success?
8585
redirect_to items_path, notice: "#{@item.name} updated!"
8686
else
87-
flash.now[:error] = "Something didn't work quite right -- try again? #{@item.errors.map { |error| "#{error.attribute}: #{error.message}" }}"
87+
flash.now[:error] = result.error.record.errors.full_messages.to_sentence
8888
render action: :edit
8989
end
9090
end
@@ -185,27 +185,6 @@ def request_unit_ids
185185
params.require(:item).permit(request_unit_ids: []).fetch(:request_unit_ids, [])
186186
end
187187

188-
# We need to update both the item and the request_units together and fail together
189-
def update_item
190-
if Flipper.enabled?(:enable_packs)
191-
update_item_and_request_units
192-
else
193-
@item.save
194-
end
195-
end
196-
197-
def update_item_and_request_units
198-
begin
199-
Item.transaction do
200-
@item.save!
201-
@item.sync_request_units!(request_unit_ids)
202-
end
203-
rescue
204-
return false
205-
end
206-
true
207-
end
208-
209188
helper_method \
210189
def filter_params(_parameters = nil)
211190
return {} unless params.key?(:filters)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# frozen_string_literal: true
2+
3+
class ItemUpdateService
4+
attr_reader :item, :params, :request_unit_ids
5+
def initialize(item:, params:, request_unit_ids: [])
6+
@item = item
7+
@request_unit_ids = request_unit_ids
8+
@params = params
9+
end
10+
11+
def call
12+
ActiveRecord::Base.transaction do
13+
item.update!(params)
14+
update_kit_value
15+
if Flipper.enabled?(:enable_packs)
16+
item.sync_request_units!(request_unit_ids)
17+
end
18+
end
19+
Result.new(value: item)
20+
rescue StandardError => e
21+
Result.new(error: e)
22+
end
23+
24+
private
25+
26+
def update_kit_value
27+
return unless item.kit
28+
29+
kit_value_in_cents = item.kit.items.reduce(0) do |sum, i|
30+
sum + i.value_in_cents.to_i * item.kit.line_items.find_by(item_id: i.id).quantity.to_i
31+
end
32+
item.kit.update!(value_in_cents: kit_value_in_cents)
33+
end
34+
end

app/views/items/_form.html.erb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,25 +22,25 @@
2222
<%= f.input :reporting_category, label: 'NDBN Reporting Category', collection: Item.reporting_categories_for_select, disabled: !!@item.kit_id, required: !@item.kit_id, hint: @reporting_category_hint %>
2323
</div>
2424

25-
<%= f.input :name, label: "Value per item", wrapper: :input_group do %>
25+
<%= f.input :value_in_dollars, label: "Value per item", wrapper: :input_group do %>
2626
<span class="input-group-text"><i class="fa fa-money"></i></span>
2727
<%= f.input_field :value_in_dollars, class: "form-control" %>
2828
<% end %>
2929

30-
<%= f.input :name, label: "Quantity Per Individual", wrapper: :input_group do %>
30+
<%= f.input :distribution_quantity, label: "Quantity Per Individual", wrapper: :input_group do %>
3131
<%= f.input_field :distribution_quantity, class: "form-control" %>
3232
<% end %>
3333

3434
<% if current_user.has_cached_role?(Role::ORG_ADMIN, current_organization) %>
35-
<%= f.input :name, label: "On hand minimum quantity", wrapper: :input_group do %>
35+
<%= f.input :on_hand_minimum_quantity, label: "On hand minimum quantity", wrapper: :input_group do %>
3636
<%= f.input_field :on_hand_minimum_quantity, input_html: {value: 0}, class: "form-control" %>
3737
<% end %>
38-
<%= f.input :name, label: "On hand recommended quantity", wrapper: :input_group do %>
38+
<%= f.input :on_hand_recommended_quantity, label: "On hand recommended quantity", wrapper: :input_group do %>
3939
<%= f.input_field :on_hand_recommended_quantity, class: "form-control" %>
4040
<% end %>
4141
<% end %>
4242

43-
<%= f.input :name, label: "Package size", wrapper: :input_group do %>
43+
<%= f.input :package_size, label: "Package size", wrapper: :input_group do %>
4444
<%= f.input_field :package_size, class: "form-control", min: 0 %>
4545
<% end %>
4646

@@ -50,7 +50,7 @@
5050
<% end %>
5151
<% end %>
5252

53-
<%= f.input :visible, label: "Item is Visible to Partners?", wrapper: :input_group do %>
53+
<%= f.input :visible_to_partners, label: "Item is Visible to Partners?", wrapper: :input_group do %>
5454
<%= f.check_box :visible_to_partners, {class: "input-group-text", id: "visible_to_partners"}, "true", "false" %>
5555
<% end %>
5656

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# frozen_string_literal: true
2+
3+
require 'rspec'
4+
5+
RSpec.describe ItemUpdateService, type: :service do
6+
describe '.call' do
7+
subject { described_class.new(item: item, params: params, request_unit_ids: request_unit_ids).call }
8+
9+
let(:kit) { create(:kit) }
10+
let(:item) { create(:item, kit: kit) }
11+
let(:item2) { create(:item, kit: kit) }
12+
let(:params) do
13+
{
14+
name: 'Updated Item Name',
15+
reporting_category: 'pads',
16+
value_in_cents: 2000,
17+
}
18+
end
19+
let(:request_unit_ids) { [] }
20+
let(:kit_value_in_cents) do
21+
kit.line_items.reduce(0) do |sum, li|
22+
item = Item.find(li.item_id)
23+
sum + item.value_in_cents.to_i * li.quantity.to_i
24+
end
25+
end
26+
27+
context 'params are ok' do
28+
it 'returns a Result with success? true and the item' do
29+
result = subject
30+
expect(result).to be_a_kind_of(Result)
31+
expect(result.success?).to eq(true)
32+
expect(result.value).to eq(item)
33+
end
34+
35+
it 'updates the item attributes' do
36+
subject
37+
item.reload
38+
expect(item.name).to eq('Updated Item Name')
39+
expect(item.value_in_cents).to eq(2000)
40+
end
41+
42+
it 'updates the kit value_in_cents' do
43+
subject
44+
kit.reload
45+
expect(kit.value_in_cents).to eq(kit_value_in_cents)
46+
end
47+
end
48+
49+
context 'params are invalid' do
50+
let(:params) do
51+
{
52+
name: '', # Invalid as name can't be blank
53+
}
54+
end
55+
56+
it 'returns a Result with success? false and an error' do
57+
result = subject
58+
expect(result).to be_a_kind_of(Result)
59+
expect(result.success?).to eq(false)
60+
expect(result.error).to be_a(ActiveRecord::RecordInvalid)
61+
expect(result.error.message).to include("Validation failed: Name can't be blank")
62+
end
63+
64+
it 'does not update the item attributes' do
65+
original_name = item.name
66+
subject
67+
item.reload
68+
expect(item.name).to eq(original_name)
69+
end
70+
71+
it 'does not update the kit value_in_cents' do
72+
original_kit_value = kit.value_in_cents
73+
subject
74+
kit.reload
75+
expect(kit.value_in_cents).to eq(original_kit_value)
76+
end
77+
end
78+
end
79+
end

spec/system/item_system_spec.rb

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
RSpec.describe "Item management", type: :system do
22
let(:organization) { create(:organization) }
3-
let(:user) { create(:user, organization: organization) }
3+
let(:user) { create(:organization_admin, organization: organization) }
44

55
before do
66
sign_in(user)
@@ -39,12 +39,86 @@
3939
expect(Item.last.value_in_cents).to eq(123_456)
4040
end
4141

42-
it "can update an existing item as a user" do
43-
item = create(:item)
44-
visit edit_item_path(item.id)
45-
click_button "Save"
42+
context "update item" do
43+
let!(:item) { create(:item, organization: organization, name: "Old Name") }
44+
let(:params) do
45+
{
46+
name: "New Name",
47+
item_category_id: nil,
48+
reporting_category: "Pads",
49+
partner_key: "other",
50+
value_in_cents: 1234,
51+
package_size: 20,
52+
on_hand_minimum_quantity: 5,
53+
on_hand_recommended_quantity: 10,
54+
distribution_quantity: 2,
55+
visible_to_partners: true,
56+
active: true,
57+
additional_info: "Some additional info"
58+
}
59+
end
60+
61+
before do
62+
visit edit_item_path(item.id)
63+
fill_in "Name", with: params[:name]
64+
select params[:reporting_category], from: "Reporting Category"
65+
fill_in "Value per item", with: params[:value_in_cents] / 100.00
66+
fill_in "Package size", with: params[:package_size]
67+
fill_in "On hand minimum quantity", with: params[:on_hand_minimum_quantity]
68+
fill_in "On hand recommended quantity", with: params[:on_hand_recommended_quantity]
69+
fill_in "Quantity Per Individual", with: params[:distribution_quantity]
70+
fill_in "Additional Info", with: params[:additional_info]
71+
end
72+
73+
subject { click_button "Save" }
74+
75+
it "updates the item with valid inputs" do
76+
subject
77+
item.reload
78+
expect(page.find(".alert")).to have_content "#{item.name} updated!"
79+
expect(item.name).to eq(params[:name])
80+
expect(item.item_category_id).to eq(params[:item_category_id])
81+
expect(item.reporting_category).to eq(params[:reporting_category].underscore)
82+
expect(item.value_in_cents).to eq(params[:value_in_cents])
83+
expect(item.package_size).to eq(params[:package_size])
84+
expect(item.on_hand_minimum_quantity).to eq(params[:on_hand_minimum_quantity])
85+
expect(item.on_hand_recommended_quantity).to eq(params[:on_hand_recommended_quantity])
86+
expect(item.distribution_quantity).to eq(params[:distribution_quantity])
87+
expect(item.visible_to_partners).to eq(params[:visible_to_partners])
88+
expect(item.active).to eq(params[:active])
89+
expect(item.additional_info).to eq(params[:additional_info])
90+
end
4691

47-
expect(page.find(".alert")).to have_content "updated"
92+
context "item belongs to a kit" do
93+
let!(:kit) { create(:kit, organization: organization) }
94+
let!(:item2) { create(:item, organization: organization) }
95+
let(:kit_value_in_cents) { item.value_in_cents.to_i + item2.value_in_cents.to_i }
96+
97+
before do
98+
item.update!(kit: kit)
99+
item2.update!(kit: kit)
100+
visit edit_item_path(item.id)
101+
fill_in "Name", with: params[:name]
102+
end
103+
104+
it "does not allow changing reporting category" do
105+
expect(page).to have_field("Reporting Category", disabled: true)
106+
expect(page).to have_content("Kits are reported based on their contents.")
107+
subject
108+
expect(kit.value_in_cents).to eq(kit_value_in_cents)
109+
end
110+
end
111+
112+
context "with invalid inputs" do
113+
let(:params) do
114+
super().merge(name: "", reporting_category: "")
115+
end
116+
117+
it "shows the error messages" do
118+
subject
119+
expect(page.find(".alert")).to have_content "Name can't be blank and Reporting category can't be blank"
120+
end
121+
end
48122
end
49123

50124
it "can update an existing item with empty attributes as a user" do
@@ -53,7 +127,7 @@
53127
fill_in "Name", with: ""
54128
click_button "Save"
55129

56-
expect(page.find(".alert")).to have_content "didn't work"
130+
expect(page.find(".alert")).to have_content "Name can't be blank"
57131
end
58132

59133
it "can make the item invisible to partners" do
@@ -111,7 +185,7 @@
111185
end
112186

113187
# Consolidated these into one to reduce the setup/teardown
114-
it "should display items in separate tabs", js: true do
188+
it "should display items in separate tabs", js: true, driver: :selenium_chrome do
115189
tab_items_only_text = page.find("#items-table", visible: true).text
116190
expect(tab_items_only_text).to have_content item_pullups.name
117191
expect(tab_items_only_text).to have_content item_tampons.name

0 commit comments

Comments
 (0)