diff --git a/backend/app/views/spree/admin/store_credits/index.html.erb b/backend/app/views/spree/admin/store_credits/index.html.erb index 2045a131fdd..e89661b2ed6 100644 --- a/backend/app/views/spree/admin/store_credits/index.html.erb +++ b/backend/app/views/spree/admin/store_credits/index.html.erb @@ -12,56 +12,60 @@ <% end %> <% if @store_credits.any? %> -
- - <%= Spree.t("admin.store_credits.current_balance") %> - <%= @user.display_total_available_store_credit %> - + <% @store_credits.group_by(&:currency).each do |currency, credits| %> +
+ + <%= currency %> <%= Spree.t("admin.store_credits.current_balance") %> + <% total = credits.sum(&:amount_remaining) %> + <%= Spree::Money.new(total, currency: currency) %> + - - - - - - - - - - - - - <% @store_credits.each do |store_credit| %> - - - - - - - - - - - <% end %> - -
<%= Spree::StoreCredit.human_attribute_name(:amount_credited) %><%= Spree::StoreCredit.human_attribute_name(:amount_used) %><%= Spree::StoreCredit.human_attribute_name(:amount_authorized) %><%= Spree::StoreCredit.human_attribute_name(:category_id) %><%= Spree::StoreCredit.human_attribute_name(:created_by_id) %><%= Spree::StoreCredit.human_attribute_name(:created_at) %><%= Spree::StoreCredit.human_attribute_name(:invalidated_at) %>
- <%= store_credit.display_amount.to_html %> - - <%= store_credit.display_amount_used.to_html %> - - <%= store_credit.display_amount_authorized.to_html %> - - <%= store_credit.category_name %> - - <%= store_credit.created_by_email %> - - <%= l store_credit.created_at.to_date %> - - <%= store_credit.invalidated? %> - - <% if can?(:show, store_credit) %> - <%= link_to_edit_url admin_user_store_credit_path(@user, store_credit), { no_text: true, class: 'edit' } %> - <% end %> -
+ + + + + + + + + + + + + <% credits.each do |store_credit| %> + + + + + + + + + + + <% end %> + +
<%= Spree::StoreCredit.human_attribute_name(:amount_credited) %><%= Spree::StoreCredit.human_attribute_name(:amount_used) %><%= Spree::StoreCredit.human_attribute_name(:amount_authorized) %><%= Spree::StoreCredit.human_attribute_name(:category_id) %><%= Spree::StoreCredit.human_attribute_name(:created_by_id) %><%= Spree::StoreCredit.human_attribute_name(:created_at) %><%= Spree::StoreCredit.human_attribute_name(:invalidated_at) %>
+ <%= store_credit.display_amount.to_html %> + + <%= store_credit.display_amount_used.to_html %> + + <%= store_credit.display_amount_authorized.to_html %> + + <%= store_credit.category_name %> + + <%= store_credit.created_by_email %> + + <%= l store_credit.created_at.to_date %> + + <%= store_credit.invalidated? %> + + <% if can?(:show, store_credit) %> + <%= link_to_edit_url admin_user_store_credit_path(@user, store_credit), { no_text: true, class: 'edit' } %> + <% end %> +
+
+ <% end %> <% else %>
<%= render 'spree/admin/shared/no_objects_found', @@ -69,4 +73,3 @@ new_resource_url: new_object_url %>
<% end %> -
diff --git a/backend/spec/features/admin/store_credits_spec.rb b/backend/spec/features/admin/store_credits_spec.rb index 247bd810a8f..36b8a22f104 100644 --- a/backend/spec/features/admin/store_credits_spec.rb +++ b/backend/spec/features/admin/store_credits_spec.rb @@ -23,7 +23,7 @@ click_link "Store Credit" expect(page.current_path).to eq spree.admin_user_store_credits_path(store_credit.user) - store_credit_table = page.find("#sc-table") + store_credit_table = page.find(".sc-table") expect(store_credit_table).to have_css('tr', count: 1) expect(store_credit_table).to have_content(Spree::Money.new(store_credit.amount).to_s) expect(store_credit_table).to have_content(Spree::Money.new(store_credit.amount_used).to_s) @@ -48,7 +48,7 @@ click_button "Create" expect(page.current_path).to eq spree.admin_user_store_credits_path(store_credit.user) - store_credit_table = page.find("#sc-table") + store_credit_table = page.find(".sc-table") expect(store_credit_table).to have_css('tr', count: 2) expect(Spree::StoreCredit.count).to eq 2 end @@ -67,7 +67,7 @@ end it "updates the store credit's amount" do - page.find("#sc-table td.actions a.fa-edit").click + page.find(".sc-table td.actions a.fa-edit").click expect(page).to have_content 'Store credit history' click_link "Change amount" expect(page).to have_content 'Editing store credit amount' diff --git a/core/app/models/concerns/spree/user_methods.rb b/core/app/models/concerns/spree/user_methods.rb index 4c1fac30f63..426ebe8f0dd 100644 --- a/core/app/models/concerns/spree/user_methods.rb +++ b/core/app/models/concerns/spree/user_methods.rb @@ -21,7 +21,9 @@ module UserMethods has_many :store_credits, -> { includes(:credit_type) }, foreign_key: "user_id", class_name: "Spree::StoreCredit" has_many :store_credit_events, through: :store_credits + money_methods :total_available_store_credit + deprecate display_total_available_store_credit: :display_available_store_credit_total, deprecator: Spree::Deprecation has_many :credit_cards, class_name: "Spree::CreditCard", foreign_key: :user_id has_many :wallet_payment_sources, foreign_key: 'user_id', class_name: 'Spree::WalletPaymentSource', inverse_of: :user @@ -66,5 +68,19 @@ def last_incomplete_spree_order(store: nil, only_frontend_viewable: true) def total_available_store_credit store_credits.reload.to_a.sum(&:amount_remaining) end + deprecate total_available_store_credit: :available_store_credit_total, deprecator: Spree::Deprecation + + def available_store_credit_total(currency:) + store_credits.reload.to_a. + select { |c| c.currency == currency }. + sum(&:amount_remaining) + end + + def display_available_store_credit_total(currency:) + Spree::Money.new( + available_store_credit_total(currency: currency), + currency: currency, + ) + end end end diff --git a/core/app/models/spree/order.rb b/core/app/models/spree/order.rb index aca8f89f4cc..55b47d579cb 100644 --- a/core/app/models/spree/order.rb +++ b/core/app/models/spree/order.rb @@ -620,7 +620,7 @@ def tax_total def add_store_credit_payments return if user.nil? - return if payments.store_credits.checkout.empty? && user.total_available_store_credit.zero? + return if payments.store_credits.checkout.empty? && user.available_store_credit_total(currency: currency).zero? payments.store_credits.checkout.each(&:invalidate!) @@ -631,10 +631,12 @@ def add_store_credit_payments remaining_total = outstanding_balance - authorized_total - if user.store_credits.any? + matching_store_credits = user.store_credits.where(currency: currency) + + if matching_store_credits.any? payment_method = Spree::PaymentMethod::StoreCredit.first - user.store_credits.order_by_priority.each do |credit| + matching_store_credits.order_by_priority.each do |credit| break if remaining_total.zero? next if credit.amount_remaining.zero? @@ -664,13 +666,13 @@ def add_store_credit_payments def covered_by_store_credit? return false unless user - user.total_available_store_credit >= total + user.available_store_credit_total(currency: currency) >= total end alias_method :covered_by_store_credit, :covered_by_store_credit? def total_available_store_credit return 0.0 unless user - user.total_available_store_credit + user.available_store_credit_total(currency: currency) end def order_total_after_store_credit @@ -681,7 +683,7 @@ def total_applicable_store_credit if can_complete? || complete? payments.store_credits.valid.sum(:amount) else - [total, (user.try(:total_available_store_credit) || 0.0)].min + [total, (user.try(:available_store_credit_total, currency: currency) || 0.0)].min end end diff --git a/core/app/models/spree/store_credit.rb b/core/app/models/spree/store_credit.rb index eb0cbf311fc..2d74100bb99 100644 --- a/core/app/models/spree/store_credit.rb +++ b/core/app/models/spree/store_credit.rb @@ -250,7 +250,7 @@ def store_event event.update_attributes!({ amount: action_amount || amount, authorization_code: action_authorization_code || event.authorization_code || generate_authorization_code, - user_total_amount: user.total_available_store_credit, + user_total_amount: user.available_store_credit_total(currency: currency), originator: action_originator, update_reason: update_reason }) diff --git a/core/spec/models/spree/concerns/user_methods_spec.rb b/core/spec/models/spree/concerns/user_methods_spec.rb index 35955274249..8045caa17b3 100644 --- a/core/spec/models/spree/concerns/user_methods_spec.rb +++ b/core/spec/models/spree/concerns/user_methods_spec.rb @@ -57,4 +57,63 @@ end end end + + describe '#available_store_credit_total' do + subject do + test_user.available_store_credit_total(currency: 'USD') + end + + context 'when the user does not have any credit' do + it { is_expected.to eq(0) } + end + + context 'when the user has credits' do + let!(:credit_1) { create(:store_credit, user: test_user, amount: 100) } + let!(:credit_2) { create(:store_credit, user: test_user, amount: 200) } + + it { is_expected.to eq(100 + 200) } + + context 'when some has been used' do + before { credit_1.update_attributes!(amount_used: 35) } + + it { is_expected.to eq(100 + 200 - 35) } + + context 'when some has been authorized' do + before { credit_1.update_attributes!(amount_authorized: 10) } + + it { is_expected.to eq(100 + 200 - 35 - 10) } + end + end + + context 'when some has been authorized' do + before { credit_1.update_attributes!(amount_authorized: 10) } + + it { is_expected.to eq(100 + 200 - 10) } + end + + context 'with credits of multiple currencies' do + let!(:credit_3) { create(:store_credit, user: test_user, amount: 400, currency: 'GBP') } + + it 'separates the currencies' do + expect(test_user.available_store_credit_total(currency: 'USD')).to eq(100 + 200) + expect(test_user.available_store_credit_total(currency: 'GBP')).to eq(400) + end + end + end + end + + describe '#display_available_store_credit_total' do + subject do + test_user.display_available_store_credit_total(currency: 'USD') + end + + context 'without credit' do + it { is_expected.to eq(Spree::Money.new(0)) } + end + + context 'with credit' do + let!(:credit) { create(:store_credit, user: test_user, amount: 100) } + it { is_expected.to eq(Spree::Money.new(100)) } + end + end end diff --git a/core/spec/models/spree/order_spec.rb b/core/spec/models/spree/order_spec.rb index 47980fff703..ea2b6a85009 100644 --- a/core/spec/models/spree/order_spec.rb +++ b/core/spec/models/spree/order_spec.rb @@ -1177,7 +1177,24 @@ def generate expect(order.payments.first.amount).to eq order_total end end + end + + context 'there is store credit in another currency' do + let(:order) { create(:order_with_totals, user: user, line_items_price: order_total).tap(&:recalculate) } + let!(:store_credit_usd) { create(:store_credit, user: user, amount: 1, currency: 'USD') } + let!(:store_credit_gbp) { create(:store_credit, user: user, amount: 1, currency: 'GBP') } + let(:user) { create(:user) } + it 'only adds the credit in the matching currency' do + expect { + order.add_store_credit_payments + }.to change { + order.payments.count + }.by(1) + + applied_store_credits = order.payments.store_credits.map(&:source) + expect(applied_store_credits).to match_array([store_credit_usd]) + end end context "there is enough store credit to pay for the entire order" do @@ -1286,65 +1303,43 @@ def generate end describe "#covered_by_store_credit" do - context "order doesn't have an associated user" do - subject { create(:order, user: nil) } - - it "returns false" do - expect(subject.covered_by_store_credit).to be false - end + subject do + order.covered_by_store_credit end - context "order has an associated user" do - let(:user) { create(:user) } + let(:order) { create(:order_with_line_items, user: user, store: store) } - subject { create(:order, user: user) } + context "order doesn't have an associated user" do + let(:user) { nil } + it { is_expected.to eq(false) } + end + context "order has an associated user" do context "user has enough store credit to pay for the order" do - before do - allow(user).to receive_messages(total_available_store_credit: 10.0) - allow(subject).to receive_messages(total: 5.0) - end - - it "returns true" do - expect(subject.covered_by_store_credit).to be true - end + let!(:credit) { create(:store_credit, user: user, amount: 1000) } + it { is_expected.to eq(true) } end context "user does not have enough store credit to pay for the order" do - before do - allow(user).to receive_messages(total_available_store_credit: 0.0) - allow(subject).to receive_messages(total: 5.0) - end - - it "returns false" do - expect(subject.covered_by_store_credit).to be false - end + let!(:credit) { create(:store_credit, user: user, amount: 1) } + it { is_expected.to eq(false) } end end end describe "#total_available_store_credit" do - context "order does not have an associated user" do - subject { create(:order, user: nil) } + subject do + order.total_available_store_credit + end - it "returns 0" do - expect(subject.total_available_store_credit).to be_zero - end + context "order does not have an associated user" do + let(:user) { nil } + it { is_expected.to eq(0) } end context "order has an associated user" do - let(:user) { create(:user) } - let(:available_store_credit) { 25.0 } - - subject { create(:order, user: user) } - - before do - allow(user).to receive_messages(total_available_store_credit: available_store_credit) - end - - it "returns the user's available store credit" do - expect(subject.total_available_store_credit).to eq available_store_credit - end + let!(:credit) { create(:store_credit, user: user, amount: 25) } + it { is_expected.to eq(25) } end end diff --git a/core/spec/models/spree/user_spec.rb b/core/spec/models/spree/user_spec.rb index 695678d0f52..890c1a926c4 100644 --- a/core/spec/models/spree/user_spec.rb +++ b/core/spec/models/spree/user_spec.rb @@ -168,7 +168,16 @@ def load_orders end end + # TODO: Remove this after the method has been fully removed describe "#total_available_store_credit" do + before do + allow_any_instance_of(Spree::LegacyUser).to receive(:total_available_store_credit).and_wrap_original do |method, *args| + Spree::Deprecation.silence do + method.call(*args) + end + end + end + context "user does not have any associated store credits" do subject { create(:user) }