solidus_core 2.2.2 → 2.3.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/README.md +4 -7
- data/app/assets/javascripts/spree.js.erb +2 -2
- data/app/helpers/spree/base_helper.rb +3 -4
- data/app/models/spree/address.rb +1 -1
- data/app/models/spree/adjustment.rb +3 -1
- data/app/models/spree/app_configuration.rb +43 -0
- data/app/models/spree/billing_integration.rb +2 -2
- data/app/models/spree/calculator/default_tax.rb +3 -1
- data/app/models/spree/calculator/distributed_amount.rb +24 -0
- data/app/models/spree/calculator/free_shipping.rb +0 -1
- data/app/models/spree/calculator/tiered_flat_rate.rb +17 -3
- data/app/models/spree/calculator/tiered_percent.rb +18 -3
- data/app/models/spree/distributed_amounts_handler.rb +43 -0
- data/app/models/spree/gateway/bogus.rb +7 -83
- data/app/models/spree/gateway/bogus_simple.rb +7 -20
- data/app/models/spree/gateway.rb +8 -58
- data/app/models/spree/image.rb +1 -1
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/option_value.rb +1 -1
- data/app/models/spree/order/checkout.rb +1 -4
- data/app/models/spree/order/number_generator.rb +43 -0
- data/app/models/spree/order.rb +33 -38
- data/app/models/spree/order_contents.rb +1 -1
- data/app/models/spree/order_taxation.rb +79 -0
- data/app/models/spree/order_update_attributes.rb +0 -2
- data/app/models/spree/order_updater.rb +55 -33
- data/app/models/spree/payment.rb +0 -1
- data/app/models/spree/payment_method/bogus_credit_card.rb +87 -0
- data/app/models/spree/payment_method/check.rb +14 -6
- data/app/models/spree/payment_method/credit_card.rb +41 -0
- data/app/models/spree/payment_method/simple_bogus_credit_card.rb +24 -0
- data/app/models/spree/payment_method/store_credit.rb +5 -13
- data/app/models/spree/payment_method.rb +126 -40
- data/app/models/spree/preferences/preferable.rb +5 -1
- data/app/models/spree/preferences/store.rb +2 -2
- data/app/models/spree/product/scopes.rb +14 -1
- data/app/models/spree/product.rb +10 -4
- data/app/models/spree/promotion_action.rb +4 -0
- data/app/models/spree/promotion_code/batch_builder.rb +3 -2
- data/app/models/spree/promotion_rule.rb +4 -0
- data/app/models/spree/role.rb +2 -0
- data/app/models/spree/role_user.rb +2 -0
- data/app/models/spree/shipment.rb +4 -2
- data/app/models/spree/shipping_method.rb +3 -1
- data/app/models/spree/shipping_rate.rb +1 -1
- data/app/models/spree/state.rb +10 -2
- data/app/models/spree/stock_item.rb +3 -3
- data/app/models/spree/store.rb +5 -0
- data/app/models/spree/store_credit.rb +2 -2
- data/app/models/spree/store_credit_event.rb +1 -1
- data/app/models/spree/store_selector/by_server_name.rb +30 -0
- data/app/models/spree/store_selector/legacy.rb +48 -0
- data/app/models/spree/tax/item_tax.rb +20 -0
- data/app/models/spree/tax/order_adjuster.rb +2 -14
- data/app/models/spree/tax/order_tax.rb +18 -0
- data/app/models/spree/tax/shipping_rate_taxer.rb +4 -13
- data/app/models/spree/tax/tax_helpers.rb +5 -3
- data/app/models/spree/tax_calculator/default.rb +83 -0
- data/app/models/spree/tax_calculator/shipping_rate.rb +46 -0
- data/app/models/spree/tax_category.rb +9 -1
- data/app/models/spree/tax_rate.rb +31 -7
- data/app/models/spree/tax_rate_tax_category.rb +6 -0
- data/app/models/spree/taxon.rb +1 -1
- data/app/models/spree/variant.rb +1 -1
- data/app/views/spree/{shipment_mailer → carton_mailer}/shipped_email.html.erb +3 -3
- data/app/views/spree/order_mailer/cancel_email.html.erb +3 -3
- data/app/views/spree/order_mailer/confirm_email.html.erb +2 -2
- data/app/views/spree/order_mailer/inventory_cancellation_email.html.erb +26 -0
- data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +2 -2
- data/app/views/spree/test_mailer/test_email.html.erb +2 -2
- data/config/locales/en.yml +66 -57
- data/db/default/spree/refund_reasons.rb +1 -0
- data/db/default/spree/shipping_categories.rb +1 -0
- data/db/default/spree/stock_locations.rb +2 -0
- data/db/default/spree/stores.rb +3 -4
- data/db/migrate/20170412103617_transform_tax_rate_category_relation.rb +48 -0
- data/db/migrate/20170422134804_add_roles_unique_constraints.rb +6 -0
- data/db/migrate/20170522143442_add_time_range_to_tax_rate.rb +6 -0
- data/db/migrate/20170608074534_rename_bogus_gateways.rb +13 -0
- data/lib/generators/spree/custom_user/custom_user_generator.rb +1 -1
- data/lib/generators/spree/dummy/dummy_generator.rb +10 -4
- data/lib/generators/spree/dummy/templates/rails/database.yml +12 -12
- data/lib/generators/spree/install/install_generator.rb +5 -5
- data/lib/generators/spree/install/templates/config/initializers/{spree.rb → solidus.rb} +0 -0
- data/lib/solidus/migrations/rename_gateways.rb +39 -0
- data/lib/spree/core/controller_helpers/auth.rb +1 -1
- data/lib/spree/core/controller_helpers/order.rb +10 -5
- data/lib/spree/core/controller_helpers/store.rb +1 -9
- data/lib/spree/core/current_store.rb +6 -14
- data/lib/spree/core/engine.rb +4 -3
- data/lib/spree/core/importer/order.rb +4 -4
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +0 -1
- data/lib/spree/localized_number.rb +2 -1
- data/lib/spree/permitted_attributes.rb +12 -6
- data/lib/spree/testing_support/capybara_ext.rb +0 -1
- data/lib/spree/testing_support/factories/adjustment_factory.rb +5 -1
- data/lib/spree/testing_support/factories/order_factory.rb +26 -24
- data/lib/spree/testing_support/factories/payment_factory.rb +4 -0
- data/lib/spree/testing_support/factories/payment_method_factory.rb +3 -3
- data/lib/spree/testing_support/factories/shipment_factory.rb +7 -3
- data/lib/spree/testing_support/factories/tax_rate_factory.rb +1 -1
- data/lib/spree/testing_support/factories/variant_factory.rb +3 -1
- data/lib/tasks/migrations/copy_order_bill_address_to_credit_card.rake +0 -4
- data/lib/tasks/migrations/migrate_user_addresses.rake +2 -2
- data/lib/tasks/migrations/rename_gateways.rake +19 -0
- data/solidus_core.gemspec +2 -3
- data/spec/lib/spree/core/controller_helpers/order_spec.rb +32 -6
- data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +0 -1
- data/spec/lib/spree/core/current_store_spec.rb +6 -11
- data/spec/lib/spree/core/price_migrator_spec.rb +4 -4
- data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +199 -91
- data/spec/lib/spree/core/testing_support/factories/variant_factory_spec.rb +18 -0
- data/spec/lib/spree/localized_number_spec.rb +6 -0
- data/spec/mailers/carton_mailer_spec.rb +3 -3
- data/spec/models/spree/address_spec.rb +3 -3
- data/spec/models/spree/adjustment_spec.rb +71 -27
- data/spec/models/spree/calculator/default_tax_spec.rb +72 -1
- data/spec/models/spree/calculator/distributed_amount_spec.rb +32 -0
- data/spec/models/spree/calculator/tiered_flat_rate_spec.rb +20 -1
- data/spec/models/spree/calculator/tiered_percent_spec.rb +20 -1
- data/spec/models/spree/distributed_amounts_handler_spec.rb +79 -0
- data/spec/models/spree/gateway/bogus_simple.rb +7 -13
- data/spec/models/spree/gateway/bogus_spec.rb +8 -4
- data/spec/models/spree/gateway_spec.rb +6 -105
- data/spec/models/spree/image_spec.rb +23 -0
- data/spec/models/spree/order/checkout_spec.rb +3 -18
- data/spec/models/spree/order/number_generator_spec.rb +45 -0
- data/spec/models/spree/order/outstanding_balance_integration_spec.rb +135 -0
- data/spec/models/spree/order/payment_spec.rb +7 -2
- data/spec/models/spree/order/state_machine_spec.rb +4 -2
- data/spec/models/spree/order_capturing_spec.rb +8 -8
- data/spec/models/spree/order_contents_spec.rb +8 -1
- data/spec/models/spree/order_shipping_spec.rb +5 -1
- data/spec/models/spree/order_spec.rb +156 -83
- data/spec/models/spree/order_taxation_spec.rb +126 -0
- data/spec/models/spree/order_update_attributes_spec.rb +1 -5
- data/spec/models/spree/order_updater_spec.rb +20 -21
- data/spec/models/spree/payment_create_spec.rb +14 -6
- data/spec/models/spree/payment_method/bogus_credit_card_spec.rb +8 -0
- data/spec/models/spree/payment_method/check_spec.rb +78 -0
- data/spec/models/spree/payment_method/credit_card_spec.rb +66 -0
- data/spec/models/spree/payment_method/simple_bogus_credit_card_spec.rb +18 -0
- data/spec/models/spree/payment_method_spec.rb +47 -2
- data/spec/models/spree/payment_spec.rb +6 -8
- data/spec/models/spree/preference_spec.rb +1 -1
- data/spec/models/spree/price_spec.rb +1 -1
- data/spec/models/spree/product/scopes_spec.rb +46 -0
- data/spec/models/spree/promotion_action_spec.rb +4 -0
- data/spec/models/spree/promotion_code/batch_builder_spec.rb +25 -3
- data/spec/models/spree/promotion_code_batch_spec.rb +0 -6
- data/spec/models/spree/promotion_handler/coupon_spec.rb +1 -1
- data/spec/models/spree/promotion_rule_spec.rb +5 -0
- data/spec/models/spree/reimbursement_type/original_payment_spec.rb +1 -1
- data/spec/models/spree/shipment_spec.rb +24 -3
- data/spec/models/spree/shipping_rate_spec.rb +5 -5
- data/spec/models/spree/state_spec.rb +31 -4
- data/spec/models/spree/stock/coordinator_spec.rb +24 -0
- data/spec/models/spree/stock/estimator_spec.rb +1 -1
- data/spec/models/spree/store_selector/by_server_name_spec.rb +26 -0
- data/spec/models/spree/store_selector/legacy_spec.rb +44 -0
- data/spec/models/spree/store_spec.rb +10 -2
- data/spec/models/spree/tax/order_adjuster_spec.rb +11 -21
- data/spec/models/spree/tax/shipping_rate_taxer_spec.rb +10 -3
- data/spec/models/spree/tax/taxation_integration_spec.rb +43 -8
- data/spec/models/spree/tax_calculator/default_spec.rb +54 -0
- data/spec/models/spree/tax_rate_spec.rb +92 -0
- data/spec/models/spree/variant/vat_price_generator_spec.rb +4 -4
- data/spec/models/spree/variant_spec.rb +8 -2
- data/spec/spec_helper.rb +2 -1
- data/spec/support/test_gateway.rb +1 -1
- metadata +45 -24
- data/app/models/spree/tax/item_adjuster.rb +0 -51
- data/spec/models/spree/tax/item_adjuster_spec.rb +0 -82
data/app/models/spree/order.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'spree/core/validators/email'
|
|
2
2
|
require 'spree/order/checkout'
|
|
3
|
+
require 'spree/order/number_generator'
|
|
3
4
|
|
|
4
5
|
module Spree
|
|
5
6
|
# The customers cart until completed, then acts as permanent record of the transaction.
|
|
@@ -298,25 +299,15 @@ module Spree
|
|
|
298
299
|
assign_attributes(attrs_to_set)
|
|
299
300
|
end
|
|
300
301
|
|
|
301
|
-
def generate_order_number(options =
|
|
302
|
-
options
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
self.number ||= loop do
|
|
310
|
-
# Make a random number.
|
|
311
|
-
random = "#{options[:prefix]}#{(0...options[:length]).map { possible.sample }.join}"
|
|
312
|
-
# Use the random number if no other order exists with it.
|
|
313
|
-
if self.class.exists?(number: random)
|
|
314
|
-
# If over half of all possible options are taken add another digit.
|
|
315
|
-
options[:length] += 1 if self.class.count > (10**options[:length] / 2)
|
|
316
|
-
else
|
|
317
|
-
break random
|
|
318
|
-
end
|
|
302
|
+
def generate_order_number(options = nil)
|
|
303
|
+
if options
|
|
304
|
+
Spree::Deprecation.warn \
|
|
305
|
+
"Passing options to Order#generate_order_number is deprecated. " \
|
|
306
|
+
"Please add your own instance of the order number generator " \
|
|
307
|
+
"with your options (#{options.inspect}) and store it as " \
|
|
308
|
+
"Spree::Config.order_number_generator in your stores config."
|
|
319
309
|
end
|
|
310
|
+
self.number ||= Spree::Config.order_number_generator.generate
|
|
320
311
|
end
|
|
321
312
|
|
|
322
313
|
def shipped_shipments
|
|
@@ -364,15 +355,18 @@ module Spree
|
|
|
364
355
|
end
|
|
365
356
|
deprecate create_tax_charge!: :update!, deprecator: Spree::Deprecation
|
|
366
357
|
|
|
358
|
+
def reimbursement_total
|
|
359
|
+
reimbursements.sum(:total)
|
|
360
|
+
end
|
|
361
|
+
|
|
367
362
|
def outstanding_balance
|
|
368
363
|
# If reimbursement has happened add it back to total to prevent balance_due payment state
|
|
369
364
|
# See: https://github.com/spree/spree/issues/6229
|
|
370
|
-
adjusted_payment_total = payment_total + refund_total
|
|
371
365
|
|
|
372
366
|
if state == 'canceled'
|
|
373
|
-
-1 *
|
|
367
|
+
-1 * payment_total
|
|
374
368
|
else
|
|
375
|
-
total -
|
|
369
|
+
total - reimbursement_total - payment_total
|
|
376
370
|
end
|
|
377
371
|
end
|
|
378
372
|
|
|
@@ -475,6 +469,7 @@ module Spree
|
|
|
475
469
|
line_items.destroy_all
|
|
476
470
|
adjustments.destroy_all
|
|
477
471
|
shipments.destroy_all
|
|
472
|
+
order_promotions.destroy_all
|
|
478
473
|
|
|
479
474
|
update!
|
|
480
475
|
end
|
|
@@ -497,6 +492,7 @@ module Spree
|
|
|
497
492
|
end
|
|
498
493
|
end
|
|
499
494
|
end
|
|
495
|
+
deprecate :state_changed, deprecator: Spree::Deprecation
|
|
500
496
|
|
|
501
497
|
def coupon_code=(code)
|
|
502
498
|
@coupon_code = begin
|
|
@@ -696,16 +692,26 @@ module Spree
|
|
|
696
692
|
self.ship_address = Spree::Address.immutable_merge(ship_address, attributes)
|
|
697
693
|
end
|
|
698
694
|
|
|
699
|
-
|
|
695
|
+
# Assigns a default bill_address and ship_address to the order based on the
|
|
696
|
+
# associated user's bill_address and ship_address.
|
|
697
|
+
# @note This doesn't persist the change bill_address or ship_address
|
|
698
|
+
def assign_default_user_addresses
|
|
700
699
|
if user
|
|
700
|
+
bill_address = (user.bill_address || user.default_address)
|
|
701
|
+
ship_address = (user.ship_address || user.default_address)
|
|
701
702
|
# this is one of 2 places still using User#bill_address
|
|
702
|
-
self.bill_address ||=
|
|
703
|
+
self.bill_address ||= bill_address if bill_address.try!(:valid?)
|
|
703
704
|
# Skip setting ship address if order doesn't have a delivery checkout step
|
|
704
705
|
# to avoid triggering validations on shipping address
|
|
705
|
-
self.ship_address ||=
|
|
706
|
+
self.ship_address ||= ship_address if ship_address.try!(:valid?) && checkout_steps.include?("delivery")
|
|
706
707
|
end
|
|
707
708
|
end
|
|
708
709
|
|
|
710
|
+
alias_method :assign_default_user_addresses!, :assign_default_user_addresses
|
|
711
|
+
deprecate assign_default_user_addresses!: :assign_default_user_addresses, deprecator: Spree::Deprecation
|
|
712
|
+
alias_method :assign_default_addresses!, :assign_default_user_addresses
|
|
713
|
+
deprecate assign_default_addresses!: :assign_default_user_addresses, deprecator: Spree::Deprecation
|
|
714
|
+
|
|
709
715
|
def persist_user_address!
|
|
710
716
|
if !temporary_address && user && user.respond_to?(:persist_order_address) && bill_address_id
|
|
711
717
|
user.persist_order_address(self)
|
|
@@ -736,20 +742,9 @@ module Spree
|
|
|
736
742
|
alias_method :assign_default_credit_card, :add_default_payment_from_wallet
|
|
737
743
|
deprecate assign_default_credit_card: :add_default_payment_from_wallet, deprecator: Spree::Deprecation
|
|
738
744
|
|
|
739
|
-
def
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
end
|
|
743
|
-
|
|
744
|
-
def validate_payments_attributes(attributes)
|
|
745
|
-
attributes = Array.wrap(attributes)
|
|
746
|
-
# Ensure the payment methods specified are allowed for this user
|
|
747
|
-
payment_methods = Spree::PaymentMethod.where(id: available_payment_methods)
|
|
748
|
-
attributes.each do |payment_attributes|
|
|
749
|
-
payment_method_id = payment_attributes[:payment_method_id]
|
|
750
|
-
|
|
751
|
-
# raise RecordNotFound unless it is an allowed payment method
|
|
752
|
-
payment_methods.find(payment_method_id) if payment_method_id
|
|
745
|
+
def record_ip_address(ip_address)
|
|
746
|
+
if last_ip_address != ip_address
|
|
747
|
+
update_attributes!(last_ip_address: ip_address)
|
|
753
748
|
end
|
|
754
749
|
end
|
|
755
750
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Relatively simple class used to apply a {Spree::Tax::OrderTax} to a
|
|
3
|
+
# {Spree::Order}.
|
|
4
|
+
#
|
|
5
|
+
# This class will create or update adjustments on the taxed items and remove
|
|
6
|
+
# any now inapplicable tax adjustments from the order.
|
|
7
|
+
class OrderTaxation
|
|
8
|
+
# Create a new order taxation.
|
|
9
|
+
#
|
|
10
|
+
# @param [Spree::Order] order the order to apply taxes to
|
|
11
|
+
# @return [Spree::OrderTaxation] a {Spree::OrderTaxation} object
|
|
12
|
+
def initialize(order)
|
|
13
|
+
@order = order
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Apply taxes to the order.
|
|
17
|
+
#
|
|
18
|
+
# This method will create or update adjustments on all line items and
|
|
19
|
+
# shipments in the order to reflect the appropriate taxes passed in. It
|
|
20
|
+
# will also remove any now inapplicable tax adjustments.
|
|
21
|
+
#
|
|
22
|
+
# @param [Spree::Tax::OrderTax] taxes the taxes to apply to the order
|
|
23
|
+
# @return [void]
|
|
24
|
+
def apply(taxes)
|
|
25
|
+
@order.line_items.each do |item|
|
|
26
|
+
taxed_items = taxes.line_item_taxes.select { |i| i.item_id == item.id }
|
|
27
|
+
update_adjustments(item, taxed_items)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
@order.shipments.each do |item|
|
|
31
|
+
taxed_items = taxes.shipment_taxes.select { |i| i.item_id == item.id }
|
|
32
|
+
update_adjustments(item, taxed_items)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# Walk through the taxes for an item and update adjustments for it. Once
|
|
39
|
+
# all of the taxes have been added as adjustments, remove any old tax
|
|
40
|
+
# adjustments that weren't touched.
|
|
41
|
+
#
|
|
42
|
+
# @private
|
|
43
|
+
# @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
|
|
44
|
+
# @param [Array<Spree::Tax::ItemTax>] taxed_items a list of calculated taxes for an item
|
|
45
|
+
# @return [void]
|
|
46
|
+
def update_adjustments(item, taxed_items)
|
|
47
|
+
tax_adjustments = item.adjustments.select(&:tax?)
|
|
48
|
+
|
|
49
|
+
active_adjustments = taxed_items.map do |tax_item|
|
|
50
|
+
update_adjustment(item, tax_item)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Remove any tax adjustments tied to rates which no longer match.
|
|
54
|
+
unmatched_adjustments = tax_adjustments - active_adjustments
|
|
55
|
+
item.adjustments.destroy(unmatched_adjustments)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Update or create a new tax adjustment on an item.
|
|
59
|
+
#
|
|
60
|
+
# @private
|
|
61
|
+
# @param [#adjustments] item a {Spree::LineItem} or {Spree::Shipment}
|
|
62
|
+
# @param [Spree::Tax::ItemTax] tax_item calculated taxes for an item
|
|
63
|
+
# @return [Spree::Adjustment] the created or updated tax adjustment
|
|
64
|
+
def update_adjustment(item, tax_item)
|
|
65
|
+
tax_adjustment = item.adjustments.detect do |adjustment|
|
|
66
|
+
adjustment.source == tax_item.tax_rate
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
tax_adjustment ||= item.adjustments.new(
|
|
70
|
+
source: tax_item.tax_rate,
|
|
71
|
+
order_id: item.order_id,
|
|
72
|
+
label: tax_item.label,
|
|
73
|
+
included: tax_item.included_in_price
|
|
74
|
+
)
|
|
75
|
+
tax_adjustment.update_attributes!(amount: tax_item.amount)
|
|
76
|
+
tax_adjustment
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -14,8 +14,6 @@ module Spree
|
|
|
14
14
|
# Assign the attributes to the order and save the order
|
|
15
15
|
# @return true if saved, otherwise false and errors will be set on the order
|
|
16
16
|
def apply
|
|
17
|
-
order.validate_payments_attributes(@payments_attributes)
|
|
18
|
-
|
|
19
17
|
assign_order_attributes
|
|
20
18
|
assign_payments_attributes
|
|
21
19
|
|
|
@@ -44,26 +44,10 @@ module Spree
|
|
|
44
44
|
#
|
|
45
45
|
# The +shipment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
|
|
46
46
|
def update_shipment_state
|
|
47
|
-
|
|
48
|
-
order.shipment_state =
|
|
49
|
-
else
|
|
50
|
-
# get all the shipment states for this order
|
|
51
|
-
shipment_states = shipments.states
|
|
52
|
-
if shipment_states.size > 1
|
|
53
|
-
# multiple shiment states means it's most likely partially shipped
|
|
54
|
-
order.shipment_state = 'partial'
|
|
55
|
-
else
|
|
56
|
-
# will return nil if no shipments are found
|
|
57
|
-
order.shipment_state = shipment_states.first
|
|
58
|
-
# TODO: inventory unit states?
|
|
59
|
-
# if order.shipment_state && order.inventory_units.where(:shipment_id => nil).exists?
|
|
60
|
-
# shipments exist but there are unassigned inventory units
|
|
61
|
-
# order.shipment_state = 'partial'
|
|
62
|
-
# end
|
|
63
|
-
end
|
|
47
|
+
log_state_change('shipment') do
|
|
48
|
+
order.shipment_state = determine_shipment_state
|
|
64
49
|
end
|
|
65
50
|
|
|
66
|
-
order.state_changed('shipment')
|
|
67
51
|
order.shipment_state
|
|
68
52
|
end
|
|
69
53
|
|
|
@@ -76,22 +60,46 @@ module Spree
|
|
|
76
60
|
#
|
|
77
61
|
# The +payment_state+ value helps with reporting, etc. since it provides a quick and easy way to locate Orders needing attention.
|
|
78
62
|
def update_payment_state
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
order.payment_state = 'failed'
|
|
82
|
-
elsif order.state == 'canceled' && order.payment_total == 0
|
|
83
|
-
order.payment_state = 'void'
|
|
84
|
-
else
|
|
85
|
-
order.payment_state = 'balance_due' if order.outstanding_balance > 0
|
|
86
|
-
order.payment_state = 'credit_owed' if order.outstanding_balance < 0
|
|
87
|
-
order.payment_state = 'paid' if !order.outstanding_balance?
|
|
63
|
+
log_state_change('payment') do
|
|
64
|
+
order.payment_state = determine_payment_state
|
|
88
65
|
end
|
|
89
|
-
|
|
66
|
+
|
|
90
67
|
order.payment_state
|
|
91
68
|
end
|
|
92
69
|
|
|
93
70
|
private
|
|
94
71
|
|
|
72
|
+
def determine_payment_state
|
|
73
|
+
if payments.present? && payments.valid.empty? && order.outstanding_balance != 0
|
|
74
|
+
'failed'
|
|
75
|
+
elsif order.state == 'canceled' && order.payment_total.zero?
|
|
76
|
+
'void'
|
|
77
|
+
elsif order.outstanding_balance > 0
|
|
78
|
+
'balance_due'
|
|
79
|
+
elsif order.outstanding_balance < 0
|
|
80
|
+
'credit_owed'
|
|
81
|
+
else
|
|
82
|
+
# outstanding_balance == 0
|
|
83
|
+
'paid'
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def determine_shipment_state
|
|
88
|
+
if order.backordered?
|
|
89
|
+
'backorder'
|
|
90
|
+
else
|
|
91
|
+
# get all the shipment states for this order
|
|
92
|
+
shipment_states = shipments.states
|
|
93
|
+
if shipment_states.size > 1
|
|
94
|
+
# multiple shiment states means it's most likely partially shipped
|
|
95
|
+
'partial'
|
|
96
|
+
else
|
|
97
|
+
# will return nil if no shipments are found
|
|
98
|
+
shipment_states.first
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
95
103
|
# This will update and select the best promotion adjustment, update tax
|
|
96
104
|
# adjustments, update cancellation adjustments, and then update the total
|
|
97
105
|
# fields (promo_total, included_tax_total, additional_tax_total, and
|
|
@@ -177,6 +185,21 @@ module Spree
|
|
|
177
185
|
order.save!(validate: false)
|
|
178
186
|
end
|
|
179
187
|
|
|
188
|
+
def log_state_change(name)
|
|
189
|
+
state = "#{name}_state"
|
|
190
|
+
old_state = order.public_send(state)
|
|
191
|
+
yield
|
|
192
|
+
new_state = order.public_send(state)
|
|
193
|
+
if old_state != new_state
|
|
194
|
+
order.state_changes.new(
|
|
195
|
+
previous_state: old_state,
|
|
196
|
+
next_state: new_state,
|
|
197
|
+
name: name,
|
|
198
|
+
user_id: order.user_id
|
|
199
|
+
)
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
180
203
|
def round_money(n)
|
|
181
204
|
(n * 100).round / 100.0
|
|
182
205
|
end
|
|
@@ -229,11 +252,10 @@ module Spree
|
|
|
229
252
|
[*line_items, *shipments].each do |item|
|
|
230
253
|
# The cancellation_total isn't persisted anywhere but is included in
|
|
231
254
|
# the adjustment_total
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
item_cancellation_total
|
|
255
|
+
item.adjustment_total = item.adjustments.
|
|
256
|
+
select(&:eligible?).
|
|
257
|
+
reject(&:included?).
|
|
258
|
+
sum(&:amount)
|
|
237
259
|
|
|
238
260
|
if item.changed?
|
|
239
261
|
item.update_columns(
|
data/app/models/spree/payment.rb
CHANGED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
class PaymentMethod::BogusCreditCard < PaymentMethod::CreditCard
|
|
3
|
+
TEST_VISA = ['4111111111111111', '4012888888881881', '4222222222222']
|
|
4
|
+
TEST_MC = ['5500000000000004', '5555555555554444', '5105105105105100']
|
|
5
|
+
TEST_AMEX = ['378282246310005', '371449635398431', '378734493671000', '340000000000009']
|
|
6
|
+
TEST_DISC = ['6011000000000004', '6011111111111117', '6011000990139424']
|
|
7
|
+
|
|
8
|
+
VALID_CCS = ['1', TEST_VISA, TEST_MC, TEST_AMEX, TEST_DISC].flatten
|
|
9
|
+
|
|
10
|
+
attr_accessor :test
|
|
11
|
+
|
|
12
|
+
def gateway_class
|
|
13
|
+
self.class
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def create_profile(payment)
|
|
17
|
+
return if payment.source.has_payment_profile?
|
|
18
|
+
# simulate the storage of credit card profile using remote service
|
|
19
|
+
if success = VALID_CCS.include?(payment.source.number)
|
|
20
|
+
payment.source.update_attributes(gateway_customer_profile_id: generate_profile_id(success))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def authorize(_money, credit_card, _options = {})
|
|
25
|
+
profile_id = credit_card.gateway_customer_profile_id
|
|
26
|
+
if VALID_CCS.include?(credit_card.number) || (profile_id && profile_id.starts_with?('BGS-'))
|
|
27
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'D' })
|
|
28
|
+
else
|
|
29
|
+
ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def purchase(_money, credit_card, _options = {})
|
|
34
|
+
profile_id = credit_card.gateway_customer_profile_id
|
|
35
|
+
if VALID_CCS.include?(credit_card.number) || (profile_id && profile_id.starts_with?('BGS-'))
|
|
36
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'M' })
|
|
37
|
+
else
|
|
38
|
+
ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def credit(_money, _credit_card, _response_code, _options = {})
|
|
43
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def capture(_money, authorization, _gateway_options)
|
|
47
|
+
if authorization == '12345'
|
|
48
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true)
|
|
49
|
+
else
|
|
50
|
+
ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', error: 'Bogus Gateway: Forced failure', test: true)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def void(_response_code, _credit_card, _options = {})
|
|
55
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def cancel(_response_code)
|
|
59
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test?
|
|
63
|
+
# Test mode is not really relevant with bogus gateway (no such thing as live server)
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def payment_profiles_supported?
|
|
68
|
+
true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def actions
|
|
72
|
+
%w(capture void credit)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def generate_profile_id(success)
|
|
78
|
+
record = true
|
|
79
|
+
prefix = success ? 'BGS' : 'FAIL'
|
|
80
|
+
while record
|
|
81
|
+
random = "#{prefix}-#{Array.new(6){ rand(6) }.join}"
|
|
82
|
+
record = Spree::CreditCard.where(gateway_customer_profile_id: random).first
|
|
83
|
+
end
|
|
84
|
+
random
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class PaymentMethod::Check < PaymentMethod
|
|
3
3
|
def actions
|
|
4
|
-
%w{capture void}
|
|
4
|
+
%w{capture void credit}
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
# Indicates whether its possible to capture the payment
|
|
@@ -14,18 +14,26 @@ module Spree
|
|
|
14
14
|
payment.state != 'void'
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
def capture(*
|
|
18
|
-
|
|
17
|
+
def capture(*)
|
|
18
|
+
simulated_successful_billing_response
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
-
def cancel(
|
|
21
|
+
def cancel(*); end
|
|
22
22
|
|
|
23
|
-
def void(*
|
|
24
|
-
|
|
23
|
+
def void(*)
|
|
24
|
+
simulated_successful_billing_response
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def credit(*)
|
|
28
|
+
simulated_successful_billing_response
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
def source_required?
|
|
28
32
|
false
|
|
29
33
|
end
|
|
34
|
+
|
|
35
|
+
def simulated_successful_billing_response
|
|
36
|
+
ActiveMerchant::Billing::Response.new(true, "", {}, {})
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# An implementation of a `Spree::PaymentMethod` for credit card payments.
|
|
3
|
+
#
|
|
4
|
+
# It's a good candidate as base class for other credit card based payment methods.
|
|
5
|
+
#
|
|
6
|
+
# See https://github.com/solidusio/solidus_gateway/ for
|
|
7
|
+
# officially supported payment method implementations.
|
|
8
|
+
#
|
|
9
|
+
class PaymentMethod::CreditCard < PaymentMethod
|
|
10
|
+
def payment_source_class
|
|
11
|
+
CreditCard
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def partial_name
|
|
15
|
+
'gateway'
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def supports?(source)
|
|
19
|
+
return true unless gateway_class.respond_to? :supports?
|
|
20
|
+
return true if source.brand && gateway_class.supports?(source.brand)
|
|
21
|
+
source.has_payment_profile?
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reusable_sources_by_order(order)
|
|
25
|
+
source_ids = order.payments.where(payment_method_id: id).pluck(:source_id).uniq
|
|
26
|
+
payment_source_class.where(id: source_ids).select(&:reusable?)
|
|
27
|
+
end
|
|
28
|
+
alias_method :sources_by_order, :reusable_sources_by_order
|
|
29
|
+
deprecate sources_by_order: :reusable_sources_by_order, deprecator: Spree::Deprecation
|
|
30
|
+
|
|
31
|
+
def reusable_sources(order)
|
|
32
|
+
if order.completed?
|
|
33
|
+
reusable_sources_by_order(order)
|
|
34
|
+
elsif order.user_id
|
|
35
|
+
order.user.wallet.wallet_payment_sources.map(&:payment_source).select(&:reusable?)
|
|
36
|
+
else
|
|
37
|
+
[]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
# Bogus Gateway that doesn't support payment profiles.
|
|
3
|
+
class PaymentMethod::SimpleBogusCreditCard < PaymentMethod::BogusCreditCard
|
|
4
|
+
def payment_profiles_supported?
|
|
5
|
+
false
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def authorize(_money, credit_card, _options = {})
|
|
9
|
+
if VALID_CCS.include? credit_card.number
|
|
10
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
|
|
11
|
+
else
|
|
12
|
+
ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def purchase(_money, credit_card, _options = {})
|
|
17
|
+
if VALID_CCS.include? credit_card.number
|
|
18
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
|
|
19
|
+
else
|
|
20
|
+
ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -4,19 +4,11 @@ module Spree
|
|
|
4
4
|
::Spree::StoreCredit
|
|
5
5
|
end
|
|
6
6
|
|
|
7
|
-
def can_capture?(payment)
|
|
8
|
-
['checkout', 'pending'].include?(payment.state)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def can_void?(payment)
|
|
12
|
-
payment.pending?
|
|
13
|
-
end
|
|
14
|
-
|
|
15
7
|
def authorize(amount_in_cents, provided_store_credit, gateway_options = {})
|
|
16
8
|
if provided_store_credit.nil?
|
|
17
9
|
ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit.unable_to_find'), {}, {})
|
|
18
10
|
else
|
|
19
|
-
action = ->
|
|
11
|
+
action = ->(store_credit) {
|
|
20
12
|
store_credit.authorize(
|
|
21
13
|
amount_in_cents / 100.0.to_d,
|
|
22
14
|
gateway_options[:currency],
|
|
@@ -28,7 +20,7 @@ module Spree
|
|
|
28
20
|
end
|
|
29
21
|
|
|
30
22
|
def capture(amount_in_cents, auth_code, gateway_options = {})
|
|
31
|
-
action = ->
|
|
23
|
+
action = ->(store_credit) {
|
|
32
24
|
store_credit.capture(
|
|
33
25
|
amount_in_cents / 100.0.to_d,
|
|
34
26
|
auth_code,
|
|
@@ -55,14 +47,14 @@ module Spree
|
|
|
55
47
|
end
|
|
56
48
|
|
|
57
49
|
def void(auth_code, gateway_options = {})
|
|
58
|
-
action = ->
|
|
50
|
+
action = ->(store_credit) {
|
|
59
51
|
store_credit.void(auth_code, action_originator: gateway_options[:originator])
|
|
60
52
|
}
|
|
61
53
|
handle_action(action, :void, auth_code)
|
|
62
54
|
end
|
|
63
55
|
|
|
64
56
|
def credit(amount_in_cents, auth_code, gateway_options = {})
|
|
65
|
-
action = ->
|
|
57
|
+
action = ->(store_credit) do
|
|
66
58
|
currency = gateway_options[:currency] || store_credit.currency
|
|
67
59
|
originator = gateway_options[:originator]
|
|
68
60
|
|
|
@@ -109,7 +101,7 @@ module Spree
|
|
|
109
101
|
|
|
110
102
|
def handle_action(action, action_name, auth_code)
|
|
111
103
|
# Find first event with provided auth_code
|
|
112
|
-
store_credit = Spree::StoreCreditEvent.
|
|
104
|
+
store_credit = Spree::StoreCreditEvent.find_by(authorization_code: auth_code).try(:store_credit)
|
|
113
105
|
|
|
114
106
|
if store_credit.nil?
|
|
115
107
|
ActiveMerchant::Billing::Response.new(false, Spree.t('store_credit.unable_to_find_for_action', auth_code: auth_code, action: action_name), {}, {})
|