solidus_core 1.1.0.pre1 → 1.1.0.pre2

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.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/app/mailers/spree/carton_mailer.rb +12 -1
  3. data/app/models/concerns/spree/user_address_book.rb +17 -8
  4. data/app/models/concerns/spree/user_methods.rb +3 -6
  5. data/app/models/spree/ability.rb +1 -1
  6. data/app/models/spree/adjustment.rb +1 -1
  7. data/app/models/spree/app_configuration.rb +11 -0
  8. data/app/models/spree/order.rb +6 -6
  9. data/app/models/spree/order/checkout.rb +9 -0
  10. data/app/models/spree/order_capturing.rb +4 -1
  11. data/app/models/spree/order_contents.rb +5 -0
  12. data/app/models/spree/order_shipping.rb +1 -1
  13. data/app/models/spree/payment.rb +5 -2
  14. data/app/models/spree/payment/processing.rb +1 -1
  15. data/app/models/spree/product.rb +1 -1
  16. data/app/models/spree/promotion.rb +8 -1
  17. data/app/models/spree/promotion/rules/first_repeat_purchase_since.rb +36 -0
  18. data/app/models/spree/promotion/rules/product.rb +2 -1
  19. data/app/models/spree/promotion_handler/cart.rb +1 -14
  20. data/app/models/spree/prototype.rb +3 -1
  21. data/app/models/spree/prototype_taxon.rb +6 -0
  22. data/app/models/spree/taxon.rb +2 -1
  23. data/app/models/spree/tracker.rb +1 -1
  24. data/app/models/spree/transfer_item.rb +2 -6
  25. data/config/locales/en.yml +10 -1
  26. data/db/default/spree/zones.rb +2 -2
  27. data/db/migrate/20140713140455_create_spree_return_authorization_reasons.rb +13 -11
  28. data/db/migrate/20150128032538_remove_environment_from_tracker.rb +6 -0
  29. data/db/migrate/20150515211137_fix_adjustment_order_id.rb +80 -0
  30. data/db/migrate/20150609093816_increase_scale_on_pre_tax_amounts.rb +16 -0
  31. data/db/migrate/20150826002555_convert_habtm_to_hmt_for_taxon_prototypes.rb +15 -0
  32. data/db/migrate/20151010003252_add_foreign_keys_for_taxons_prototypes.rb +6 -0
  33. data/db/migrate/20151013222921_remove_token_permissions_table.rb +16 -0
  34. data/db/migrate/20151014213349_rename_identifier_to_number_for_payment.rb +7 -0
  35. data/db/migrate/20151015203732_add_foreign_keys_for_product_promotion_rules.rb +9 -0
  36. data/db/migrate/20151021113730_add_sale_to_spree_promotions.rb +6 -0
  37. data/db/migrate/20151021163309_convert_sale_promotions.rb +28 -0
  38. data/lib/spree/core.rb +2 -0
  39. data/lib/spree/core/controller_helpers/strong_parameters.rb +7 -0
  40. data/lib/spree/core/engine.rb +2 -1
  41. data/lib/spree/deprecation.rb +3 -0
  42. data/lib/spree/mailer_previews/carton_preview.rb +1 -1
  43. data/lib/spree/permission_sets.rb +23 -0
  44. data/{app/models → lib}/spree/permission_sets/base.rb +0 -0
  45. data/{app/models → lib}/spree/permission_sets/configuration_display.rb +0 -0
  46. data/{app/models → lib}/spree/permission_sets/configuration_management.rb +0 -0
  47. data/{app/models → lib}/spree/permission_sets/dashboard_display.rb +0 -0
  48. data/{app/models → lib}/spree/permission_sets/default_customer.rb +2 -1
  49. data/{app/models → lib}/spree/permission_sets/order_display.rb +0 -0
  50. data/{app/models → lib}/spree/permission_sets/order_management.rb +0 -0
  51. data/{app/models → lib}/spree/permission_sets/product_display.rb +0 -0
  52. data/{app/models → lib}/spree/permission_sets/product_management.rb +0 -0
  53. data/{app/models → lib}/spree/permission_sets/promotion_display.rb +0 -0
  54. data/{app/models → lib}/spree/permission_sets/promotion_management.rb +0 -0
  55. data/{app/models → lib}/spree/permission_sets/report_display.rb +0 -0
  56. data/{app/models → lib}/spree/permission_sets/restricted_stock_display.rb +0 -0
  57. data/{app/models → lib}/spree/permission_sets/restricted_stock_management.rb +0 -0
  58. data/{app/models → lib}/spree/permission_sets/restricted_stock_transfer_display.rb +0 -0
  59. data/{app/models → lib}/spree/permission_sets/restricted_stock_transfer_management.rb +22 -12
  60. data/{app/models → lib}/spree/permission_sets/stock_display.rb +0 -0
  61. data/{app/models → lib}/spree/permission_sets/stock_management.rb +0 -0
  62. data/{app/models → lib}/spree/permission_sets/stock_transfer_display.rb +0 -0
  63. data/{app/models → lib}/spree/permission_sets/stock_transfer_management.rb +0 -0
  64. data/{app/models → lib}/spree/permission_sets/super_user.rb +0 -0
  65. data/{app/models → lib}/spree/permission_sets/user_display.rb +0 -0
  66. data/lib/spree/permission_sets/user_management.rb +21 -0
  67. data/lib/spree/permitted_attributes.rb +7 -4
  68. data/lib/spree/testing_support/factories/order_factory.rb +6 -2
  69. data/lib/spree/testing_support/factories/tracker_factory.rb +0 -1
  70. data/lib/spree/testing_support/order_walkthrough.rb +22 -19
  71. metadata +41 -27
  72. data/app/models/spree/permission_sets/user_management.rb +0 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a97651829c856767b16419843fcf2aa267271bc
4
- data.tar.gz: d875f33165835fb31607620cdc6d668e10b6babb
3
+ metadata.gz: ae8ca76f31ff2adc9e441466cdb0612a00327773
4
+ data.tar.gz: 025e77575cb92628ce8ecc1aceb3834b8e07c475
5
5
  SHA512:
6
- metadata.gz: cc874e200a655bd5ed0cdbb3e91ed51dc723391c875fce148b59a78c5b1b2dd27c15e53a08da8a7f909d790406a4055a60c20ea01cbc38760802c65ccd45f821
7
- data.tar.gz: 728d34af08ff76b34b2c8e69bd1cb532c8ec27762dbea4a5a10d3f7b0a6970383d2a8ab554adeea518f89e7624f00f91f6a02c9a79bd126bf36095acf94501b0
6
+ metadata.gz: 2bea81b1bd27228bfacf973daedec5e4db6c0b66758eaaeedd4f4d2fa720435fd9a32b63d857f885bedc103ffadf0ee3e42ddc7f92b6eb34f28517e05f6e9bb7
7
+ data.tar.gz: f846e9f87d9951c77ab83ae46af5668469d3b1e58f1fe744c40772b988513fb54527d80b8a1e3d1e417a879e190783d436b95efefb4b5083b35c571b3d1965c6
@@ -1,7 +1,18 @@
1
1
  module Spree
2
2
  class CartonMailer < BaseMailer
3
3
  # Send an email to customers to notify that an individual carton has been
4
- # shipped.
4
+ # shipped. If a carton contains items from multiple orders then this will be
5
+ # called with that carton one time for each order.
6
+ #
7
+ # @param carton [Spree::Carton] the shipped carton
8
+ # @param order [Spree::Order] one of the orders with items in the carton
9
+ # @param resend [Boolean] indicates whether the email is a 'resend' (e.g.
10
+ # triggered by the admin "resend" button)
11
+ # @return [Mail::Message]
12
+ #
13
+ # Note: The signature of this method has changed. The new (non-deprecated)
14
+ # signature is:
15
+ # def shipped_email(carton:, order:, resend: false)
5
16
  def shipped_email(options, deprecated_options={})
6
17
  if options.is_a?(Integer)
7
18
  ActiveSupport::Deprecation.warn "Calling shipped_email with a carton_id is DEPRECATED. Instead use CartonMailer.shipped_email(order: order, carton: carton)"
@@ -54,19 +54,28 @@ module Spree
54
54
  alias_method :ship_address, :default_address
55
55
  alias_method :ship_address_attributes=, :default_address_attributes=
56
56
 
57
+ # saves address in address book
58
+ # sets address to the default if automatic_default_address is set to true
59
+ # if address is nil, does nothing and returns nil
57
60
  def ship_address=(address)
58
61
  be_default = Spree::Config.automatic_default_address
59
- save_in_address_book(address.attributes, be_default)
62
+ save_in_address_book(address.attributes, be_default) if address
60
63
  end
61
64
 
65
+ # saves order.ship_address and order.bill_address in address book
66
+ # sets ship_address to the default if automatic_default_address is set to true
67
+ # sets bill_address to the default if automatic_default_address is set to true and there is no ship_address
68
+ # if one address is nil, does not save that address
62
69
  def persist_order_address(order)
63
- if Spree::Config.automatic_default_address
64
- save_in_address_book(order.ship_address.attributes, true)
65
- save_in_address_book(order.bill_address.attributes, order.ship_address.nil?)
66
- else
67
- save_in_address_book(order.ship_address.attributes)
68
- save_in_address_book(order.bill_address.attributes)
69
- end
70
+ save_in_address_book(
71
+ order.ship_address.attributes,
72
+ Spree::Config.automatic_default_address
73
+ ) if order.ship_address
74
+
75
+ save_in_address_book(
76
+ order.bill_address.attributes,
77
+ order.ship_address.nil? && Spree::Config.automatic_default_address
78
+ ) if order.bill_address
70
79
  end
71
80
 
72
81
  # Add an address to the user's list of saved addresses for future autofill
@@ -23,13 +23,10 @@ module Spree
23
23
  has_many :store_credit_events, through: :store_credits
24
24
  money_methods :total_available_store_credit
25
25
 
26
- def self.ransackable_associations(auth_object=nil)
27
- %w[addresses]
28
- end
26
+ include Spree::RansackableAttributes unless included_modules.include?(Spree::RansackableAttributes)
29
27
 
30
- def self.ransackable_attributes(auth_object=nil)
31
- %w[id email]
32
- end
28
+ self.whitelisted_ransackable_associations = %w[addresses]
29
+ self.whitelisted_ransackable_attributes = %w[id email]
33
30
  end
34
31
 
35
32
  # has_spree_role? simply needs to return true or false whether a user has a role or not.
@@ -51,7 +51,7 @@ module Spree
51
51
  def register_extension_abilities
52
52
  Ability.abilities.each do |clazz|
53
53
  ability = clazz.send(:new, user)
54
- @rules = rules + ability.send(:rules)
54
+ merge(ability)
55
55
  end
56
56
  end
57
57
 
@@ -16,7 +16,7 @@ module Spree
16
16
  class Adjustment < Spree::Base
17
17
  belongs_to :adjustable, polymorphic: true, touch: true
18
18
  belongs_to :source, polymorphic: true
19
- belongs_to :order, class_name: "Spree::Order"
19
+ belongs_to :order, class_name: 'Spree::Order', inverse_of: :all_adjustments
20
20
  belongs_to :promotion_code, :class_name => 'Spree::PromotionCode'
21
21
  belongs_to :adjustment_reason, class_name: 'Spree::AdjustmentReason', inverse_of: :adjustments
22
22
 
@@ -265,6 +265,17 @@ module Spree
265
265
  @promotion_chooser_class ||= Spree::PromotionChooser
266
266
  end
267
267
 
268
+ # Allows providing your own Mailer for shipped cartons.
269
+ #
270
+ # @!attribute [rw] carton_shipped_email_class
271
+ # @return [ActionMailer::Base] an object that responds to "shipped_email"
272
+ # (e.g. an ActionMailer with a "shipped_email" method) with the same
273
+ # signature as Spree::CartonMailer.shipped_email.
274
+ attr_writer :carton_shipped_email_class
275
+ def carton_shipped_email_class
276
+ @carton_shipped_email_class ||= Spree::CartonMailer
277
+ end
278
+
268
279
  def static_model_preferences
269
280
  @static_model_preferences ||= Spree::Preferences::StaticModelPreferences.new
270
281
  end
@@ -59,6 +59,11 @@ module Spree
59
59
  has_many :products, through: :variants
60
60
  has_many :variants, through: :line_items
61
61
  has_many :refunds, through: :payments
62
+ has_many :all_adjustments,
63
+ class_name: 'Spree::Adjustment',
64
+ foreign_key: :order_id,
65
+ dependent: :destroy,
66
+ inverse_of: :order
62
67
 
63
68
  has_many :order_stock_locations, class_name: "Spree::OrderStockLocation"
64
69
  has_many :stock_locations, through: :order_stock_locations
@@ -145,11 +150,6 @@ module Spree
145
150
  self.line_item_comparison_hooks.add(hook)
146
151
  end
147
152
 
148
- def all_adjustments
149
- Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
150
- order_id: self.id)
151
- end
152
-
153
153
  # For compatiblity with Calculator::PriceSack
154
154
  def amount
155
155
  line_items.inject(0.0) { |sum, li| sum + li.amount }
@@ -400,7 +400,7 @@ module Spree
400
400
 
401
401
  # Helper methods for checkout steps
402
402
  def paid?
403
- payment_state == 'paid' || payment_state == 'credit_owed'
403
+ %w(paid credit_owed).include?(payment_state)
404
404
  end
405
405
 
406
406
  def available_payment_methods
@@ -126,6 +126,15 @@ module Spree
126
126
  order.update_totals
127
127
  order.persist_totals
128
128
  end
129
+
130
+ after_transition do |order, transition|
131
+ order.logger.debug "Order #{order.number} transitioned from #{transition.from} to #{transition.to} via #{transition.event}"
132
+ end
133
+
134
+
135
+ after_failure do |order, transition|
136
+ order.logger.debug "Order #{order.number} halted transition on event #{transition.event} state #{transition.from}: #{order.errors.full_messages.join}"
137
+ end
129
138
  end
130
139
 
131
140
  alias_method :save_state, :save
@@ -33,7 +33,10 @@ class Spree::OrderCapturing
33
33
  end
34
34
  end
35
35
  ensure
36
- @order.update!
36
+ # FIXME: Adding the inverse_of on the payments relation for orders -should- fix this,
37
+ # however it only appears to make it worse (calling with changes three times instead of once.
38
+ # Warrants an investigation. Reloading for now.
39
+ @order.reload.update!
37
40
  end
38
41
  end
39
42
  end
@@ -16,6 +16,11 @@ module Spree
16
16
  after_add_or_remove(line_item, options)
17
17
  end
18
18
 
19
+ def remove_line_item(line_item, options = {})
20
+ line_item.destroy!
21
+ after_add_or_remove(line_item, options)
22
+ end
23
+
19
24
  def update_cart(params)
20
25
  if order.update_attributes(params)
21
26
  unless order.completed?
@@ -87,7 +87,7 @@ class Spree::OrderShipping
87
87
 
88
88
  def send_shipment_emails(carton)
89
89
  carton.orders.each do |order|
90
- Spree::CartonMailer.shipped_email(order: order, carton: carton).deliver_later
90
+ Spree::Config.carton_shipped_email_class.shipped_email(order: order, carton: carton).deliver_later
91
91
  end
92
92
  end
93
93
  end
@@ -2,6 +2,9 @@ module Spree
2
2
  class Payment < Spree::Base
3
3
  include Spree::Payment::Processing
4
4
 
5
+ alias_attribute :identifier, :number
6
+ deprecate :identifier, :identifier=, deprecator: Spree::Deprecation
7
+
5
8
  IDENTIFIER_CHARS = (('A'..'Z').to_a + ('0'..'9').to_a - %w(0 1 I O)).freeze
6
9
  NON_RISKY_AVS_CODES = ['B', 'D', 'H', 'J', 'M', 'Q', 'T', 'V', 'X', 'Y'].freeze
7
10
  RISKY_AVS_CODES = ['A', 'C', 'E', 'F', 'G', 'I', 'K', 'L', 'N', 'O', 'P', 'R', 'S', 'U', 'W', 'Z'].freeze
@@ -255,8 +258,8 @@ module Spree
255
258
  # See https://github.com/spree/spree/issues/1998#issuecomment-12869105
256
259
  def set_unique_identifier
257
260
  begin
258
- self.identifier = generate_identifier
259
- end while self.class.exists?(identifier: self.identifier)
261
+ self.number = generate_identifier
262
+ end while self.class.exists?(number: self.number)
260
263
  end
261
264
 
262
265
  def generate_identifier
@@ -218,7 +218,7 @@ module Spree
218
218
 
219
219
  # The unique identifier to be passed in to the payment gateway
220
220
  def gateway_order_id
221
- "#{order.number}-#{self.identifier}"
221
+ "#{order.number}-#{self.number}"
222
222
  end
223
223
 
224
224
  def token_based?
@@ -25,7 +25,7 @@ module Spree
25
25
  has_many :classifications, dependent: :delete_all, inverse_of: :product
26
26
  has_many :taxons, through: :classifications, before_remove: :remove_taxon
27
27
 
28
- has_many :product_promotion_rules
28
+ has_many :product_promotion_rules, dependent: :destroy
29
29
  has_many :promotion_rules, through: :product_promotion_rules
30
30
 
31
31
  belongs_to :tax_category, class_name: 'Spree::TaxCategory'
@@ -16,7 +16,7 @@ module Spree
16
16
  has_many :order_promotions, class_name: "Spree::OrderPromotion"
17
17
  has_many :orders, through: :order_promotions
18
18
 
19
- has_many :codes, class_name: "Spree::PromotionCode", inverse_of: :promotion
19
+ has_many :codes, class_name: "Spree::PromotionCode", inverse_of: :promotion, dependent: :destroy
20
20
  alias_method :promotion_codes, :codes
21
21
 
22
22
  accepts_nested_attributes_for :promotion_actions, :promotion_rules
@@ -28,6 +28,7 @@ module Spree
28
28
  validates :usage_limit, numericality: { greater_than: 0, allow_nil: true }
29
29
  validates :per_code_usage_limit, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
30
30
  validates :description, length: { maximum: 255 }
31
+ validate :apply_automatically_disallowed_with_codes_or_paths
31
32
 
32
33
  before_save :normalize_blank_values
33
34
 
@@ -228,5 +229,11 @@ module Spree
228
229
  def match_all?
229
230
  match_policy == "all"
230
231
  end
232
+
233
+ def apply_automatically_disallowed_with_codes_or_paths
234
+ return unless apply_automatically
235
+ errors.add(:apply_automatically, :disallowed_with_code) if codes.any?
236
+ errors.add(:apply_automatically, :disallowed_with_path) if path.present?
237
+ end
231
238
  end
232
239
  end
@@ -0,0 +1,36 @@
1
+ module Spree
2
+ class Promotion
3
+ module Rules
4
+ class FirstRepeatPurchaseSince < PromotionRule
5
+ preference :days_ago, :integer, default: 365
6
+ validates :preferred_days_ago, numericality: { only_integer: true, greater_than: 0 }
7
+
8
+ # This promotion is applicable to orders only.
9
+ def applicable?(promotable)
10
+ promotable.is_a?(Spree::Order)
11
+ end
12
+
13
+ # This is never eligible if the order does not have a user, and that user does not have any previous completed orders.
14
+ #
15
+ # This is eligible if the user's most recently completed order is more than the preferred days ago
16
+ # @param order [Spree::Order]
17
+ # @option options
18
+ def eligible?(order, options = {})
19
+ return false unless order.user
20
+
21
+ last_order = last_completed_order(order.user)
22
+ return false unless last_order
23
+
24
+ last_order.completed_at < preferred_days_ago.days.ago
25
+ end
26
+
27
+ private
28
+
29
+ def last_completed_order user
30
+ user.orders.complete.order(:completed_at).last
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -6,7 +6,8 @@ module Spree
6
6
  # either come from assigned product group or are assingned directly to
7
7
  # the rule.
8
8
  class Product < PromotionRule
9
- has_many :product_promotion_rules, class_name: 'Spree::ProductPromotionRule', foreign_key: :promotion_rule_id
9
+ has_many :product_promotion_rules, dependent: :destroy, foreign_key: :promotion_rule_id,
10
+ class_name: 'Spree::ProductPromotionRule'
10
11
  has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
11
12
 
12
13
  MATCH_POLICIES = %w(any all none)
@@ -40,20 +40,7 @@ module Spree
40
40
  end
41
41
 
42
42
  def sale_promotions
43
- promo_table = Promotion.arel_table
44
- code_table = PromotionCode.arel_table
45
-
46
- promotion_code_join = promo_table.join(code_table, Arel::Nodes::OuterJoin).on(
47
- promo_table[:id].eq(code_table[:promotion_id])
48
- ).join_sources
49
-
50
- Promotion.active.includes(:promotion_rules).
51
- joins(promotion_code_join).
52
- where(
53
- code_table[:value].eq(nil).and(
54
- promo_table[:path].eq(nil)
55
- )
56
- ).distinct
43
+ Promotion.where(apply_automatically: true).active.includes(:promotion_rules)
57
44
  end
58
45
 
59
46
  def promotion_code(promotion)
@@ -2,7 +2,9 @@ module Spree
2
2
  class Prototype < Spree::Base
3
3
  has_and_belongs_to_many :properties, join_table: :spree_properties_prototypes
4
4
  has_and_belongs_to_many :option_types, join_table: :spree_option_types_prototypes
5
- has_and_belongs_to_many :taxons, join_table: :spree_taxons_prototypes
5
+
6
+ has_many :prototype_taxons, dependent: :destroy
7
+ has_many :taxons, through: :prototype_taxons
6
8
 
7
9
  validates :name, presence: true
8
10
  end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ class PrototypeTaxon < Spree::Base
3
+ belongs_to :prototype
4
+ belongs_to :taxon
5
+ end
6
+ end
@@ -6,7 +6,8 @@ module Spree
6
6
  has_many :classifications, -> { order(:position) }, dependent: :delete_all, inverse_of: :taxon
7
7
  has_many :products, through: :classifications
8
8
 
9
- has_and_belongs_to_many :prototypes, join_table: :spree_taxons_prototypes
9
+ has_many :prototype_taxons, dependent: :destroy
10
+ has_many :prototypes, through: :prototype_taxons
10
11
 
11
12
  before_create :set_permalink
12
13
 
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class Tracker < Spree::Base
3
3
  def self.current
4
- tracker = where(active: true, environment: Rails.env).first
4
+ tracker = where(active: true).first
5
5
  tracker.analytics_id.present? ? tracker : nil if tracker
6
6
  end
7
7
  end
@@ -21,7 +21,7 @@ module Spree
21
21
  private
22
22
 
23
23
  def ensure_stock_transfer_not_closed
24
- if stock_transfer_closed?
24
+ if stock_transfer.closed?
25
25
  errors.add(:base, Spree.t('errors.messages.cannot_modify_transfer_item_closed_stock_transfer'))
26
26
  end
27
27
  end
@@ -47,12 +47,8 @@ module Spree
47
47
  end
48
48
  end
49
49
 
50
- def stock_transfer_closed?
51
- stock_transfer.closed?
52
- end
53
-
54
50
  def check_stock?
55
- !stock_transfer_closed? && stock_transfer.source_location.check_stock_on_transfer?
51
+ !stock_transfer.shipped? && stock_transfer.source_location.check_stock_on_transfer?
56
52
  end
57
53
  end
58
54
  end
@@ -299,6 +299,11 @@ en:
299
299
  attributes:
300
300
  currency:
301
301
  must_match_order_currency: "Must match order currency"
302
+ spree/promotion:
303
+ attributes:
304
+ apply_automatically:
305
+ disallowed_with_code: Disallowed for promotions with a code
306
+ disallowed_with_path: Disallowed for promotions with a path
302
307
  spree/refund:
303
308
  attributes:
304
309
  amount:
@@ -589,6 +594,7 @@ en:
589
594
  cannot_rebuild_shipments_shipments_not_pending: Cannot rebuild shipments for an order with non-pending shipments.
590
595
  cannot_perform_operation: Cannot perform requested operation
591
596
  cannot_set_shipping_method_without_address: Cannot set shipping method until customer details are provided.
597
+ cannot_update_email: You do not have access to update this user's email address. <br />Please contact a superuser if you need to perform this action.
592
598
  capture: Capture
593
599
  capture_events: Capture events
594
600
  card_code: Card Code
@@ -764,7 +770,6 @@ en:
764
770
  enable_mail_delivery: Enable Mail Delivery
765
771
  end: End
766
772
  ending_in: Ending in
767
- environment: Environment
768
773
  error: error
769
774
  errors:
770
775
  messages:
@@ -1228,6 +1233,10 @@ en:
1228
1233
  description: Apply a promotion to every nth order a user has completed.
1229
1234
  name: Nth Order
1230
1235
  form_text: "Apply this promotion on the users Nth order: "
1236
+ first_repeat_purchase_since:
1237
+ description: Available only to user who have not purchased in a while
1238
+ name: First Repeat Purchase Since
1239
+ form_text: "Apply this promotion to users whose last order was more than X days ago: "
1231
1240
  promotions: Promotions
1232
1241
  promotion_successfully_created: Promotion has been successfully created!
1233
1242
  promotion_total_changed_before_complete: "One or more of the promotions on your order have become ineligible and were removed. Please check the new order amounts and try again."