solidus_core 2.1.1 → 2.2.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +0 -1
- data/app/assets/config/solidus_core_manifest.js +1 -0
- data/app/assets/javascripts/spree.js.erb +72 -0
- data/app/helpers/spree/store_helper.rb +5 -0
- data/app/jobs/spree/promotion_code_batch_job.rb +24 -0
- data/app/mailers/spree/promotion_code_batch_mailer.rb +13 -0
- data/app/models/concerns/spree/calculated_adjustments.rb +1 -1
- data/app/models/concerns/spree/ordered_property_value_list.rb +2 -2
- data/app/models/concerns/spree/user_address_book.rb +4 -4
- data/app/models/concerns/spree/user_methods.rb +7 -0
- data/app/models/concerns/spree/user_payment_source.rb +12 -5
- data/app/models/spree/address.rb +14 -3
- data/app/models/spree/adjustment.rb +13 -1
- data/app/models/spree/app_configuration.rb +0 -19
- data/app/models/spree/base.rb +2 -0
- data/app/models/spree/credit_card.rb +34 -43
- data/app/models/spree/gateway/bogus.rb +1 -1
- data/app/models/spree/gateway.rb +6 -4
- data/app/models/spree/inventory_unit.rb +3 -2
- data/app/models/spree/order/checkout.rb +187 -273
- data/app/models/spree/order.rb +137 -71
- data/app/models/spree/order_contents.rb +1 -1
- data/app/models/spree/order_inventory.rb +11 -11
- data/app/models/spree/order_promotion.rb +2 -0
- data/app/models/spree/order_update_attributes.rb +1 -8
- data/app/models/spree/order_updater.rb +67 -63
- data/app/models/spree/payment.rb +0 -1
- data/app/models/spree/payment_create.rb +27 -7
- data/app/models/spree/payment_method/store_credit.rb +3 -3
- data/app/models/spree/payment_method.rb +4 -1
- data/app/models/spree/payment_source.rb +45 -0
- data/app/models/spree/product/scopes.rb +24 -24
- data/app/models/spree/product.rb +4 -4
- data/app/models/spree/promotion.rb +2 -0
- data/app/models/spree/promotion_code/batch_builder.rb +63 -0
- data/app/models/spree/promotion_code.rb +1 -0
- data/app/models/spree/promotion_code_batch.rb +25 -0
- data/app/models/spree/promotion_handler/cart.rb +2 -2
- data/app/models/spree/promotion_handler/coupon.rb +1 -2
- data/app/models/spree/promotion_handler/free_shipping.rb +32 -21
- data/app/models/spree/promotion_handler/page.rb +1 -1
- data/app/models/spree/reimbursement.rb +1 -1
- data/app/models/spree/return_authorization.rb +0 -28
- data/app/models/spree/return_item.rb +1 -1
- data/app/models/spree/shipment.rb +4 -4
- data/app/models/spree/shipping_method.rb +2 -2
- data/app/models/spree/shipping_rate.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +16 -17
- data/app/models/spree/stock/coordinator.rb +3 -3
- data/app/models/spree/stock/package.rb +1 -1
- data/app/models/spree/stock/quantifier.rb +5 -4
- data/app/models/spree/stock_location.rb +2 -2
- data/app/models/spree/store.rb +2 -2
- data/app/models/spree/store_credit.rb +1 -1
- data/app/models/spree/tax/tax_helpers.rb +3 -3
- data/app/models/spree/tax_rate.rb +7 -1
- data/app/models/spree/taxonomy.rb +1 -1
- data/app/models/spree/variant/scopes.rb +5 -5
- data/app/models/spree/variant/vat_price_generator.rb +8 -5
- data/app/models/spree/variant.rb +1 -0
- data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +19 -10
- data/app/models/spree/wallet/default_payment_builder.rb +6 -6
- data/app/models/spree/wallet.rb +71 -0
- data/app/models/spree/wallet_payment_source.rb +17 -0
- data/app/models/spree/zone.rb +1 -1
- data/app/views/spree/carton_mailer/shipped_email.text.erb +1 -1
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_errored.text.erb +2 -0
- data/app/views/spree/promotion_code_batch_mailer/promotion_code_batch_finished.text.erb +2 -0
- data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +0 -7
- data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +0 -5
- data/app/views/spree/shared/_error_messages.html.erb +1 -1
- data/app/views/spree/shipment_mailer/shipped_email.html.erb +1 -1
- data/config/initializers/assets.rb +1 -1
- data/config/initializers/friendly_id.rb +1 -1
- data/config/locales/en.yml +50 -12
- data/db/default/spree/store_credit.rb +2 -1
- data/db/migrate/20130826062534_add_depth_to_spree_taxons.rb +4 -6
- data/db/migrate/20160420044191_create_spree_wallet_payment_sources.rb +23 -0
- data/db/migrate/20160420181916_migrate_credit_cards_to_wallet_payment_sources.rb +26 -0
- data/db/migrate/20161017102621_create_spree_promotion_code_batch.rb +36 -0
- data/db/migrate/20161129035810_add_index_to_spree_payments_number.rb +5 -0
- data/db/migrate/20170223235001_remove_spree_store_credits_column.rb +5 -0
- data/lib/generators/spree/dummy/templates/rails/application.rb +1 -1
- data/lib/generators/spree/dummy/templates/rails/test.rb +1 -1
- data/lib/generators/spree/install/install_generator.rb +6 -5
- data/lib/spree/core/controller_helpers/payment_parameters.rb +54 -0
- data/lib/spree/core/engine.rb +6 -9
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +0 -1
- data/lib/spree/money.rb +18 -0
- data/lib/spree/permission_sets/default_customer.rb +1 -1
- data/lib/spree/permitted_attributes.rb +1 -1
- data/lib/spree/testing_support/authorization_helpers.rb +1 -0
- data/lib/spree/testing_support/capybara_ext.rb +13 -0
- data/lib/spree/testing_support/factories/order_factory.rb +5 -1
- data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
- data/lib/spree/testing_support/factories/shipment_factory.rb +0 -1
- data/solidus_core.gemspec +3 -3
- data/spec/jobs/promotion_code_batch_job_spec.rb +65 -0
- data/spec/lib/calculated_adjustments_spec.rb +105 -1
- data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +4 -1
- data/spec/lib/spree/core/testing_support/factories/payment_factory_spec.rb +8 -0
- data/spec/lib/spree/money_spec.rb +32 -0
- data/spec/lib/spree/permission_sets/default_customer_spec.rb +20 -0
- data/spec/mailers/promotion_code_batch_mailer_spec.rb +45 -0
- data/spec/models/spree/credit_card_spec.rb +86 -86
- data/spec/models/spree/gateway_spec.rb +3 -1
- data/spec/models/spree/inventory_unit_spec.rb +12 -4
- data/spec/models/spree/order/checkout_spec.rb +11 -32
- data/spec/models/spree/order/tax_spec.rb +2 -2
- data/spec/models/spree/order_contents_spec.rb +24 -1
- data/spec/models/spree/order_inventory_spec.rb +130 -83
- data/spec/models/spree/order_spec.rb +15 -117
- data/spec/models/spree/order_update_attributes_spec.rb +1 -44
- data/spec/models/spree/order_updater_spec.rb +10 -13
- data/spec/models/spree/payment_create_spec.rb +5 -1
- data/spec/models/spree/payment_method_spec.rb +16 -0
- data/spec/models/spree/payment_spec.rb +14 -8
- data/spec/models/spree/promotion_code/batch_builder_spec.rb +61 -0
- data/spec/models/spree/promotion_code_batch_spec.rb +58 -0
- data/spec/models/spree/promotion_code_spec.rb +4 -0
- data/spec/models/spree/promotion_spec.rb +3 -6
- data/spec/models/spree/return_authorization_spec.rb +0 -59
- data/spec/models/spree/shipment_spec.rb +4 -4
- data/spec/models/spree/stock/availability_validator_spec.rb +64 -9
- data/spec/models/spree/tax/item_adjuster_spec.rb +1 -2
- data/spec/models/spree/unit_cancel_spec.rb +0 -85
- data/spec/models/spree/user_spec.rb +3 -1
- data/spec/models/spree/variant/vat_price_generator_spec.rb +8 -2
- data/spec/models/spree/variant_spec.rb +16 -4
- data/spec/models/spree/wallet_payment_source_spec.rb +46 -0
- data/spec/models/spree/wallet_spec.rb +128 -0
- data/spec/support/concerns/payment_source.rb +64 -0
- metadata +51 -25
- data/app/assets/javascripts/spree.js.coffee.erb +0 -64
- data/app/models/spree/promotion_builder.rb +0 -55
- data/app/models/spree/promotion_code/code_builder.rb +0 -62
- data/config/initializers/premailer_assets.rb +0 -1
- data/lib/spree/core/unreturned_item_charger.rb +0 -106
- data/lib/tasks/exchanges.rake +0 -47
- data/spec/lib/spree/core/unreturned_item_charger_spec.rb +0 -126
- data/spec/lib/tasks/exchanges_spec.rb +0 -220
- data/spec/models/spree/promotion_builder_spec.rb +0 -120
- data/spec/models/spree/promotion_code/code_builder_spec.rb +0 -77
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Rails.application.config.assets.precompile += %w( ink.css )
|
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
module Spree
|
|
2
|
-
class UnreturnedItemCharger
|
|
3
|
-
class ChargeFailure < StandardError
|
|
4
|
-
attr_accessor :new_order
|
|
5
|
-
def initialize(message, new_order)
|
|
6
|
-
@new_order = new_order
|
|
7
|
-
super(message)
|
|
8
|
-
end
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
class_attribute :failure_handler
|
|
12
|
-
|
|
13
|
-
attr_reader :original_order, :new_order
|
|
14
|
-
|
|
15
|
-
def initialize(shipment, return_items)
|
|
16
|
-
@shipment = shipment
|
|
17
|
-
@original_order = @shipment.order
|
|
18
|
-
@return_items = return_items
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def charge_for_items
|
|
22
|
-
self.new_order = Spree::Order.create!(exchange_order_attributes)
|
|
23
|
-
|
|
24
|
-
new_order.associate_user!(@original_order.user) if @original_order.user
|
|
25
|
-
|
|
26
|
-
add_exchange_variants_to_order
|
|
27
|
-
set_shipment_for_new_order
|
|
28
|
-
|
|
29
|
-
new_order.update!
|
|
30
|
-
set_order_payment
|
|
31
|
-
|
|
32
|
-
# There are several checks in the order state machine to skip
|
|
33
|
-
# certain transitions when an order is an unreturned exchange
|
|
34
|
-
if !new_order.unreturned_exchange?
|
|
35
|
-
raise ChargeFailure.new('order is not an unreturned exchange', new_order)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Transitions will call update_totals on the order
|
|
39
|
-
until new_order.can_complete?
|
|
40
|
-
new_order.next!
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
new_order.contents.approve(name: self.class.name)
|
|
44
|
-
new_order.complete!
|
|
45
|
-
Spree::OrderCapturing.new(new_order).capture_payments if Spree::Config[:auto_capture_exchanges] && !Spree::Config[:auto_capture]
|
|
46
|
-
|
|
47
|
-
@return_items.each(&:expired!)
|
|
48
|
-
create_new_rma if Spree::Config[:create_rma_for_unreturned_exchange]
|
|
49
|
-
|
|
50
|
-
if !new_order.completed?
|
|
51
|
-
raise ChargeFailure.new('order not complete', new_order)
|
|
52
|
-
elsif !new_order.valid?
|
|
53
|
-
raise ChargeFailure.new('order not valid', new_order)
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
private
|
|
58
|
-
|
|
59
|
-
attr_writer :new_order
|
|
60
|
-
|
|
61
|
-
def add_exchange_variants_to_order
|
|
62
|
-
@return_items.group_by(&:exchange_variant).map do |variant, variant_return_items|
|
|
63
|
-
variant_inventory_units = variant_return_items.map(&:exchange_inventory_unit)
|
|
64
|
-
line_item = Spree::LineItem.create!(variant: variant, quantity: variant_return_items.count, order: new_order)
|
|
65
|
-
variant_inventory_units.each { |i| i.update_attributes!(line_item_id: line_item.id, order_id: new_order.id) }
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def set_shipment_for_new_order
|
|
70
|
-
@shipment.update_attributes!(order_id: new_order.id)
|
|
71
|
-
new_order.shipments.reset
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def set_order_payment
|
|
75
|
-
unless new_order.payments.present?
|
|
76
|
-
card_to_reuse = @original_order.valid_credit_cards.first
|
|
77
|
-
card_to_reuse = @original_order.user.credit_cards.default.first if !card_to_reuse && @original_order.user
|
|
78
|
-
new_order.payments.create!(
|
|
79
|
-
payment_method_id: card_to_reuse.try(:payment_method_id),
|
|
80
|
-
source: card_to_reuse,
|
|
81
|
-
amount: new_order.total
|
|
82
|
-
)
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def create_new_rma
|
|
87
|
-
@return_items.group_by(&:return_authorization).each do |rma, return_items|
|
|
88
|
-
new_return_items = return_items.map { |ri| Spree::ReturnItem.create!(inventory_unit: ri.inventory_unit) }
|
|
89
|
-
Spree::ReturnAuthorization.create!(order: rma.order,
|
|
90
|
-
reason: rma.reason,
|
|
91
|
-
stock_location: rma.stock_location,
|
|
92
|
-
return_items: new_return_items)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def exchange_order_attributes
|
|
97
|
-
{
|
|
98
|
-
bill_address: @original_order.bill_address,
|
|
99
|
-
ship_address: @original_order.ship_address,
|
|
100
|
-
email: @original_order.email,
|
|
101
|
-
store_id: @original_order.store_id,
|
|
102
|
-
frontend_viewable: false
|
|
103
|
-
}
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
data/lib/tasks/exchanges.rake
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
namespace :exchanges do
|
|
2
|
-
desc 'Takes unreturned exchanged items and creates a new order to charge
|
|
3
|
-
the customer for not returning them'
|
|
4
|
-
task charge_unreturned_items: :environment do
|
|
5
|
-
unreturned_return_items = Spree::ReturnItem.expecting_return.exchange_processed.joins(:exchange_inventory_unit).where([
|
|
6
|
-
"spree_inventory_units.created_at < :days_ago AND spree_inventory_units.state = :iu_state",
|
|
7
|
-
days_ago: Spree::Config[:expedited_exchanges_days_window].days.ago, iu_state: "shipped"
|
|
8
|
-
]).preload(:exchange_inventory_unit).to_a
|
|
9
|
-
|
|
10
|
-
# Determine that a return item has already been deemed unreturned and therefore charged
|
|
11
|
-
# by the fact that its exchange inventory unit has popped off to a different order
|
|
12
|
-
unreturned_return_items.select! { |ri| ri.inventory_unit.order_id == ri.exchange_inventory_unit.order_id }
|
|
13
|
-
|
|
14
|
-
failures = []
|
|
15
|
-
|
|
16
|
-
unreturned_return_items.group_by(&:exchange_shipment).each do |shipment, return_items|
|
|
17
|
-
item_charger = Spree::UnreturnedItemCharger.new(shipment, return_items)
|
|
18
|
-
|
|
19
|
-
begin
|
|
20
|
-
item_charger.charge_for_items
|
|
21
|
-
rescue Spree::UnreturnedItemCharger::ChargeFailure => e
|
|
22
|
-
failure = { message: e.message }
|
|
23
|
-
rescue => e
|
|
24
|
-
failure = { message: "#{e.class}: #{e.message}" }
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
next unless failure
|
|
28
|
-
failure[:new_order] = item_charger.new_order.number if item_charger.new_order
|
|
29
|
-
failures << failure.merge({
|
|
30
|
-
order: item_charger.original_order.number,
|
|
31
|
-
shipment: shipment.number,
|
|
32
|
-
return_items: return_items.map(&:id),
|
|
33
|
-
order_errors: item_charger.original_order.errors.full_messages
|
|
34
|
-
})
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
if failures.any?
|
|
38
|
-
if Spree::UnreturnedItemCharger.failure_handler
|
|
39
|
-
Spree::UnreturnedItemCharger.failure_handler.call(failures)
|
|
40
|
-
else
|
|
41
|
-
raise Spree::ChargeUnreturnedItemsFailures.new(failures.to_json)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
class Spree::ChargeUnreturnedItemsFailures < StandardError; end
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Spree::UnreturnedItemCharger do
|
|
4
|
-
let(:ship_address) { create(:address) }
|
|
5
|
-
let(:shipped_order) { create(:shipped_order, ship_address: ship_address, line_items_count: 1, with_cartons: false) }
|
|
6
|
-
let(:original_shipment) { shipped_order.shipments.first }
|
|
7
|
-
let(:original_stock_location) { original_shipment.stock_location }
|
|
8
|
-
let(:original_inventory_unit) { shipped_order.inventory_units.first }
|
|
9
|
-
let(:original_variant) { original_inventory_unit.variant }
|
|
10
|
-
let(:shipping_method) { create(:shipping_method, tax_category: original_variant.tax_category) }
|
|
11
|
-
|
|
12
|
-
let(:exchange_shipment) do
|
|
13
|
-
create(:shipment,
|
|
14
|
-
order: shipped_order,
|
|
15
|
-
state: 'shipped',
|
|
16
|
-
stock_location: original_stock_location,
|
|
17
|
-
created_at: 5.days.ago,
|
|
18
|
-
shipping_method: shipping_method)
|
|
19
|
-
end
|
|
20
|
-
let(:exchange_inventory_unit) { exchange_shipment.inventory_units.first }
|
|
21
|
-
let(:return_item) do
|
|
22
|
-
create(:exchange_return_item,
|
|
23
|
-
inventory_unit: original_inventory_unit,
|
|
24
|
-
exchange_inventory_unit: exchange_inventory_unit)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
let!(:unreturned_item_charger) { Spree::UnreturnedItemCharger.new(exchange_shipment, [return_item]) }
|
|
28
|
-
|
|
29
|
-
before do
|
|
30
|
-
exchange_shipment.finalize!
|
|
31
|
-
exchange_inventory_unit.ship!
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
shared_examples 'charge_for_items success' do
|
|
35
|
-
let(:new_order) do
|
|
36
|
-
subject
|
|
37
|
-
exchange_inventory_unit.shipment.order.reload
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it "reuses the same inventory unit" do
|
|
41
|
-
expect { subject }.not_to change { Spree::InventoryUnit.count }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
it "reuses the same shipment" do
|
|
45
|
-
expect { subject }.not_to change { Spree::Shipment.count }
|
|
46
|
-
expect(new_order.shipments.count).to eq 1
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
context 'in tax zone' do
|
|
50
|
-
let!(:tax_zone) { create(:zone, countries: [ship_address.country]) }
|
|
51
|
-
let!(:tax_rate) { create(:tax_rate, zone: tax_zone, tax_category: original_variant.tax_category) }
|
|
52
|
-
|
|
53
|
-
it "applies tax" do
|
|
54
|
-
exchange_order = exchange_shipment.order
|
|
55
|
-
exchange_order.update!
|
|
56
|
-
subject
|
|
57
|
-
expect(new_order.additional_tax_total).to be > 0
|
|
58
|
-
expect(new_order.line_items[0].additional_tax_total).to be > 0
|
|
59
|
-
expect(new_order.shipments[0].additional_tax_total).to be > 0
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it "creates a new completed order" do
|
|
64
|
-
expect { subject }.to change { Spree::Order.count }.by(1)
|
|
65
|
-
expect(new_order).to_not eq(shipped_order)
|
|
66
|
-
expect(new_order).to be_completed
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it "authorizes payment" do
|
|
70
|
-
expect { subject }.to change { Spree::Payment.count }.by(1)
|
|
71
|
-
expect(new_order.payments.count).to eq 1
|
|
72
|
-
expect(new_order.payments.first).to be_pending
|
|
73
|
-
expect(new_order.payments.first.response_code).to be_present
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "delivers confirmation email" do
|
|
77
|
-
expect { subject }.to change { ActionMailer::Base.deliveries.count }.by(1)
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
context 'with auto_capture_exchanges' do
|
|
81
|
-
before { Spree::Config[:auto_capture_exchanges] = true }
|
|
82
|
-
|
|
83
|
-
it "captures payment" do
|
|
84
|
-
expect { subject }.to change { Spree::Payment.count }.by(1)
|
|
85
|
-
expect(new_order.payments.count).to eq 1
|
|
86
|
-
expect(new_order.payments.first).to be_completed
|
|
87
|
-
expect(new_order.payment_state).to eq "paid"
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
describe "#charge_for_items" do
|
|
93
|
-
before do
|
|
94
|
-
original_variant.update_attributes!(track_inventory: true)
|
|
95
|
-
original_variant.stock_items.update_all(backorderable: false)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
subject { unreturned_item_charger.charge_for_items }
|
|
99
|
-
|
|
100
|
-
context "new order is not an unreturned exchange" do
|
|
101
|
-
before do
|
|
102
|
-
allow_any_instance_of(Spree::Shipment).to receive(:update_attributes!)
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
it "raises an error" do
|
|
106
|
-
expect { subject }.to raise_error(Spree::UnreturnedItemCharger::ChargeFailure, 'order is not an unreturned exchange')
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
context "item is in stock" do
|
|
111
|
-
before do
|
|
112
|
-
original_variant.stock_items.map { |si| si.set_count_on_hand(10) }
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
include_examples 'charge_for_items success'
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
context "item is now out of stock" do
|
|
119
|
-
before do
|
|
120
|
-
original_variant.stock_items.map { |si| si.set_count_on_hand(0) }
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
include_examples 'charge_for_items success'
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe "exchanges:charge_unreturned_items" do
|
|
4
|
-
include_context(
|
|
5
|
-
'rake',
|
|
6
|
-
task_name: 'exchanges:charge_unreturned_items',
|
|
7
|
-
task_path: Spree::Core::Engine.root.join('lib/tasks/exchanges.rake'),
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
subject { task }
|
|
11
|
-
|
|
12
|
-
describe '#prerequisites' do
|
|
13
|
-
it { expect(subject.prerequisites).to include("environment") }
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
before do
|
|
17
|
-
Spree::Config[:expedited_exchanges] = true
|
|
18
|
-
Spree::StockItem.update_all(count_on_hand: 10)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
context "there are no unreturned items" do
|
|
22
|
-
it { expect { subject.invoke }.not_to change { Spree::Order.count } }
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
context "there are return items in an intermediate return status" do
|
|
26
|
-
let!(:order) { create(:shipped_order, line_items_count: 2) }
|
|
27
|
-
let(:return_item_1) { build(:exchange_return_item, inventory_unit: order.inventory_units.first) }
|
|
28
|
-
let(:return_item_2) { build(:exchange_return_item, inventory_unit: order.inventory_units.last) }
|
|
29
|
-
let!(:rma) { create(:return_authorization, order: order, return_items: [return_item_1, return_item_2]) }
|
|
30
|
-
let(:zone) { create(:zone, countries: [order.tax_address.country])}
|
|
31
|
-
let!(:tax_rate) { create(:tax_rate, zone: zone, tax_category: return_item_2.exchange_variant.tax_category) }
|
|
32
|
-
before do
|
|
33
|
-
rma.save!
|
|
34
|
-
Spree::Shipment.last.ship!
|
|
35
|
-
return_item_1.lost!
|
|
36
|
-
return_item_2.give!
|
|
37
|
-
Timecop.travel((Spree::Config[:expedited_exchanges_days_window] + 1).days)
|
|
38
|
-
end
|
|
39
|
-
after { Timecop.return }
|
|
40
|
-
it { expect { subject.invoke }.not_to change { Spree::Order.count } }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
context "there are unreturned items" do
|
|
44
|
-
let!(:order) { create(:shipped_order, line_items_count: 2) }
|
|
45
|
-
let(:return_item_1) { build(:exchange_return_item, inventory_unit: order.inventory_units.first) }
|
|
46
|
-
let(:return_item_2) { build(:exchange_return_item, inventory_unit: order.inventory_units.last) }
|
|
47
|
-
let!(:rma) { create(:return_authorization, order: order, return_items: [return_item_1, return_item_2]) }
|
|
48
|
-
let(:zone) { create(:zone, countries: [order.tax_address.country])}
|
|
49
|
-
let!(:tax_rate) { create(:tax_rate, zone: zone, tax_category: return_item_2.exchange_variant.tax_category) }
|
|
50
|
-
|
|
51
|
-
before do
|
|
52
|
-
rma.save!
|
|
53
|
-
Spree::Shipment.last.ship!
|
|
54
|
-
return_item_1.receive!
|
|
55
|
-
Timecop.travel travel_time
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
after { Timecop.return }
|
|
59
|
-
|
|
60
|
-
context "fewer than the config allowed days have passed" do
|
|
61
|
-
let(:travel_time) { (Spree::Config[:expedited_exchanges_days_window] - 1).days }
|
|
62
|
-
|
|
63
|
-
it "does not create a new order" do
|
|
64
|
-
expect { subject.invoke }.not_to change { Spree::Order.count }
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
context "more than the config allowed days have passed" do
|
|
69
|
-
let(:travel_time) { (Spree::Config[:expedited_exchanges_days_window] + 1).days }
|
|
70
|
-
|
|
71
|
-
it "creates a new completed order" do
|
|
72
|
-
expect { subject.invoke }.to change { Spree::Order.count }
|
|
73
|
-
expect(Spree::Order.last).to be_completed
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "sets frontend_viewable to false" do
|
|
77
|
-
subject.invoke
|
|
78
|
-
expect(Spree::Order.last).not_to be_frontend_viewable
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
it "moves the shipment for the unreturned items to the new order" do
|
|
82
|
-
subject.invoke
|
|
83
|
-
new_order = Spree::Order.last
|
|
84
|
-
expect(new_order.shipments.count).to eq 1
|
|
85
|
-
expect(return_item_2.reload.exchange_shipment.order).to eq Spree::Order.last
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it "creates line items on the order for the unreturned items" do
|
|
89
|
-
subject.invoke
|
|
90
|
-
expect(Spree::Order.last.line_items.map(&:variant)).to eq [return_item_2.exchange_variant]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
it "associates the exchanges inventory units with the new line items" do
|
|
94
|
-
subject.invoke
|
|
95
|
-
expect(return_item_2.reload.exchange_inventory_unit.try(:line_item).try(:order)).to eq Spree::Order.last
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
it "uses the credit card from the previous order" do
|
|
99
|
-
subject.invoke
|
|
100
|
-
new_order = Spree::Order.last
|
|
101
|
-
expect(new_order.credit_cards).to be_present
|
|
102
|
-
expect(new_order.credit_cards.first).to eq order.valid_credit_cards.first
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
context "payments" do
|
|
106
|
-
it "authorizes the order for the full amount of the unreturned items including taxes" do
|
|
107
|
-
expect { subject.invoke }.to change { Spree::Payment.count }.by(1)
|
|
108
|
-
new_order = Spree::Order.last
|
|
109
|
-
expected_amount = return_item_2.reload.exchange_variant.price + new_order.additional_tax_total + new_order.included_tax_total + new_order.shipment_total
|
|
110
|
-
expect(new_order.total).to eq expected_amount
|
|
111
|
-
payment = new_order.payments.first
|
|
112
|
-
expect(payment.amount).to eq expected_amount
|
|
113
|
-
expect(new_order.item_total).to eq return_item_2.reload.exchange_variant.price
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
context "auto_capture_exchanges is true" do
|
|
117
|
-
before do
|
|
118
|
-
Spree::Config[:auto_capture_exchanges] = true
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
it 'creates a pending payment' do
|
|
122
|
-
expect { subject.invoke }.to change { Spree::Payment.count }.by(1)
|
|
123
|
-
payment = Spree::Payment.last
|
|
124
|
-
expect(payment).to be_completed
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
context "auto_capture_exchanges is false" do
|
|
129
|
-
before do
|
|
130
|
-
Spree::Config[:auto_capture_exchanges] = false
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
it 'captures payment' do
|
|
134
|
-
expect { subject.invoke }.to change { Spree::Payment.count }.by(1)
|
|
135
|
-
payment = Spree::Payment.last
|
|
136
|
-
expect(payment).to be_pending
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "does not attempt to create a new order for the item more than once" do
|
|
142
|
-
subject.invoke
|
|
143
|
-
subject.reenable
|
|
144
|
-
expect { subject.invoke }.not_to change { Spree::Order.count }
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
it "associates the store of the original order with the exchange order" do
|
|
148
|
-
store = order.store
|
|
149
|
-
expect(Spree::Order).to receive(:create!).once.with(hash_including({ store_id: store.id })).and_call_original
|
|
150
|
-
subject.invoke
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
it 'approves the order' do
|
|
154
|
-
subject.invoke
|
|
155
|
-
new_order = Spree::Order.last
|
|
156
|
-
expect(new_order).to be_approved
|
|
157
|
-
expect(new_order.is_risky?).to eq false
|
|
158
|
-
expect(new_order.approver_name).to eq "Spree::UnreturnedItemCharger"
|
|
159
|
-
expect(new_order.approver).to be nil
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
context "there is no card from the previous order" do
|
|
163
|
-
let!(:credit_card) { create(:credit_card, user: order.user, default: true, gateway_customer_profile_id: "BGS-123") }
|
|
164
|
-
before { allow_any_instance_of(Spree::Order).to receive(:valid_credit_cards) { [] } }
|
|
165
|
-
|
|
166
|
-
it "attempts to use the user's default card" do
|
|
167
|
-
expect { subject.invoke }.to change { Spree::Payment.count }.by(1)
|
|
168
|
-
new_order = Spree::Order.last
|
|
169
|
-
expect(new_order.credit_cards).to be_present
|
|
170
|
-
expect(new_order.credit_cards.first).to eq credit_card
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
context "it is unable to authorize the credit card" do
|
|
175
|
-
before { allow_any_instance_of(Spree::Payment).to receive(:authorize!).and_raise(RuntimeError) }
|
|
176
|
-
|
|
177
|
-
it "raises an error with the order" do
|
|
178
|
-
expect { subject.invoke }.to raise_error(Spree::ChargeUnreturnedItemsFailures)
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
|
|
182
|
-
context "the exchange inventory unit is not shipped" do
|
|
183
|
-
before { return_item_2.reload.exchange_inventory_unit.update_columns(state: "on hand") }
|
|
184
|
-
it "does not create a new order" do
|
|
185
|
-
expect { subject.invoke }.not_to change { Spree::Order.count }
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
context "the exchange inventory unit has been returned" do
|
|
190
|
-
before { return_item_2.reload.exchange_inventory_unit.update_columns(state: "returned") }
|
|
191
|
-
it "does not create a new order" do
|
|
192
|
-
expect { subject.invoke }.not_to change { Spree::Order.count }
|
|
193
|
-
end
|
|
194
|
-
end
|
|
195
|
-
|
|
196
|
-
context 'rma for unreturned exchanges' do
|
|
197
|
-
context 'config to not create' do
|
|
198
|
-
before { Spree::Config[:create_rma_for_unreturned_exchange] = false }
|
|
199
|
-
|
|
200
|
-
it 'does not create rma' do
|
|
201
|
-
expect { subject.invoke }.not_to change { Spree::ReturnAuthorization.count }
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
context 'config to create' do
|
|
206
|
-
before do
|
|
207
|
-
Spree::Config[:create_rma_for_unreturned_exchange] = true
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
it 'creates with return items' do
|
|
211
|
-
expect { subject.invoke }.to change { Spree::ReturnAuthorization.count }
|
|
212
|
-
rma = Spree::ReturnAuthorization.last
|
|
213
|
-
|
|
214
|
-
expect(rma.return_items.all?(&:awaiting?)).to be true
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
end
|
|
220
|
-
end
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Spree::PromotionBuilder do
|
|
4
|
-
let(:promotion) { build(:promotion) }
|
|
5
|
-
let(:base_code) { 'abc' }
|
|
6
|
-
let(:number_of_codes) { 1 }
|
|
7
|
-
let(:promotion_attrs) { { name: 'some promo' } }
|
|
8
|
-
let(:builder) {
|
|
9
|
-
Spree::PromotionBuilder.new(
|
|
10
|
-
{
|
|
11
|
-
base_code: base_code,
|
|
12
|
-
number_of_codes: number_of_codes
|
|
13
|
-
},
|
|
14
|
-
promotion_attrs
|
|
15
|
-
)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
describe '#initialize' do
|
|
19
|
-
subject { builder }
|
|
20
|
-
it 'has the right base code' do
|
|
21
|
-
expect(subject.base_code).to eq 'abc'
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
it 'has the right base code' do
|
|
25
|
-
expect(subject.number_of_codes).to eq 1
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
describe '#valid?' do
|
|
30
|
-
subject { builder.valid? }
|
|
31
|
-
|
|
32
|
-
it 'is true' do
|
|
33
|
-
expect(subject).to be
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
context 'promotion is not valid' do
|
|
37
|
-
let(:promotion_attrs) { { name: nil } }
|
|
38
|
-
|
|
39
|
-
it 'is true' do
|
|
40
|
-
expect(subject).to_not be
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'has errors on the promotion' do
|
|
44
|
-
subject
|
|
45
|
-
expect(builder.errors).to_not be_empty
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
context 'number of codes is invalid' do
|
|
50
|
-
let(:number_of_codes) { -1 }
|
|
51
|
-
|
|
52
|
-
it 'is false ' do
|
|
53
|
-
expect(subject).to_not be
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
it 'validates numericality' do
|
|
57
|
-
subject
|
|
58
|
-
expect(builder.errors.full_messages).to eq ["Number of codes must be greater than 0"]
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
describe '#number_of_codes=' do
|
|
64
|
-
it 'coerces a string' do
|
|
65
|
-
builder.number_of_codes = '3'
|
|
66
|
-
expect(builder.number_of_codes).to eq 3
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
it 'is nil for empty string' do
|
|
70
|
-
builder.number_of_codes = ''
|
|
71
|
-
expect(builder.number_of_codes).to be_nil
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "#perform" do
|
|
76
|
-
subject { builder.perform }
|
|
77
|
-
|
|
78
|
-
context 'when the builder is invalid' do
|
|
79
|
-
let(:number_of_codes) { 'sups' }
|
|
80
|
-
|
|
81
|
-
it 'returns false' do
|
|
82
|
-
expect(subject).to_not be
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
context "when the builder is valid" do
|
|
87
|
-
context "when the builder cant build promotion codes" do
|
|
88
|
-
let(:number_of_codes) { nil }
|
|
89
|
-
|
|
90
|
-
it "doesn't create any new codes" do
|
|
91
|
-
subject
|
|
92
|
-
expect(builder.promotion.codes).to be_empty
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
context "when the builder can build promotion codes" do
|
|
97
|
-
let(:number_of_codes) { 1 }
|
|
98
|
-
|
|
99
|
-
it "creates the correct number of codes" do
|
|
100
|
-
subject
|
|
101
|
-
expect(builder.promotion.codes.length).to eq number_of_codes
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it "creates the promotion with the correct code" do
|
|
105
|
-
subject
|
|
106
|
-
expect(builder.promotion.codes.first.value).to eq base_code
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
it "saves the promotion" do
|
|
111
|
-
subject
|
|
112
|
-
expect(builder.promotion).to be_persisted
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
it "returns true on success" do
|
|
116
|
-
expect(subject).to be true
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
end
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
|
-
describe Spree::PromotionCode::CodeBuilder do
|
|
4
|
-
let(:promotion) { build_stubbed :promotion }
|
|
5
|
-
let(:base_code) { "abc" }
|
|
6
|
-
let(:builder) do
|
|
7
|
-
described_class.new promotion, base_code, num_codes
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
describe "#build_promotion_codes" do
|
|
11
|
-
subject { builder.build_promotion_codes }
|
|
12
|
-
|
|
13
|
-
context "with one code" do
|
|
14
|
-
let(:num_codes) { 1 }
|
|
15
|
-
|
|
16
|
-
it "builds a single promotion code" do
|
|
17
|
-
subject
|
|
18
|
-
expect(builder.promotion.codes.size).to eq(num_codes)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
it "creates the promotion code with the correct value" do
|
|
22
|
-
subject
|
|
23
|
-
expect(builder.promotion.codes.first.value).to eq base_code
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
context "with more than one code" do
|
|
28
|
-
let(:num_codes) { 2 }
|
|
29
|
-
|
|
30
|
-
it "builds the correct number of codes" do
|
|
31
|
-
subject
|
|
32
|
-
expect(builder.promotion.codes.size).to eq(num_codes)
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
it "builds codes with distinct values" do
|
|
36
|
-
subject
|
|
37
|
-
expect(builder.promotion.codes.map(&:value).uniq.size).to eq(num_codes)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it "builds codes with the same base prefix" do
|
|
41
|
-
subject
|
|
42
|
-
values = builder.promotion.codes.map(&:value)
|
|
43
|
-
expect(values.all? { |val| val.starts_with?("#{base_code}_") }).to be true
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
context 'when every possible code must be used' do
|
|
47
|
-
before do
|
|
48
|
-
@old_length = described_class.random_code_length
|
|
49
|
-
described_class.random_code_length = 1
|
|
50
|
-
end
|
|
51
|
-
after { described_class.random_code_length = @old_length }
|
|
52
|
-
|
|
53
|
-
let(:num_codes) { 26 }
|
|
54
|
-
|
|
55
|
-
it "resolves the collision" do
|
|
56
|
-
subject
|
|
57
|
-
expect(builder.promotion.codes.map(&:value).uniq.size).to eq(num_codes)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
context "when collisions occur" do
|
|
62
|
-
before do
|
|
63
|
-
@old_length = described_class.random_code_length
|
|
64
|
-
described_class.random_code_length = 1
|
|
65
|
-
end
|
|
66
|
-
after { described_class.random_code_length = @old_length }
|
|
67
|
-
|
|
68
|
-
let(:num_codes) { 25 } # every possible code except 1
|
|
69
|
-
|
|
70
|
-
it "resolves the collisions and returns the correct number of codes" do
|
|
71
|
-
subject
|
|
72
|
-
expect(builder.promotion.codes.map(&:value).uniq.size).to eq(num_codes)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|