Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/app/helpers/spree/store_helper.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Methods added to this helper will be available to all templates in the frontend.
module Spree
# Methods added to this helper will be available to all templates in the
# frontend.
module StoreHelper

# helper to determine if its appropriate to show the store menu
# @return [Boolean] true when it is appropriate to show the store menu
def store_menu?
%w{thank_you}.exclude? params[:action]
end
Expand Down
26 changes: 20 additions & 6 deletions core/app/models/spree/address.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,35 +30,42 @@ def self.default(user = nil, kind = "bill")
end
end

# Can modify an address if it's not been used in an order (but checkouts controller has finer control)
# def editable?
# new_record? || (shipments.empty? && checkouts.empty?)
# end

# @return [String] the full name on this address
def full_name
"#{firstname} #{lastname}".strip
end

# @return [String] a string representation of this state
def state_text
state.try(:abbr) || state.try(:name) || state_name
end

# @param other [Spree::Address, nil] the address we are comparing with
# @return [Boolean] true if this fields on this address match the fields on
# the other address
def same_as?(other)
return false if other.nil?
attributes.except('id', 'updated_at', 'created_at') == other.attributes.except('id', 'updated_at', 'created_at')
end

alias same_as same_as?

# @return [String] the full name on the address followed by the first line
# of the address
def to_s
"#{full_name}: #{address1}"
end

# @return [Spree::Address] a new address that is the same_as? this address
def clone
ActiveSupport::Deprecation.warn "Spree::Address.clone is deprecated and may be removed from future releases, Use Spree::Address.dup instead", caller
self.dup
end

# @note This compares the addresses based on only the fields that make up
# the logical "address" and excludes their order IDs. Use #same_as? to
# include the order IDs in the comparison
# @return [Boolean] true if the two addresses have the same address fields
def ==(other_address)
self_attrs = self.attributes
other_attrs = other_address.respond_to?(:attributes) ? other_address.attributes : {}
Expand All @@ -68,11 +75,13 @@ def ==(other_address)
self_attrs == other_attrs
end

# @return [Boolean] true if the order is missing all of the address fields
# are nil
def empty?
attributes.except('id', 'created_at', 'updated_at', 'order_id', 'country_id').all? { |_, v| v.nil? }
end

# Generates an ActiveMerchant compatible address hash
# @return [Hash] an ActiveMerchant compatible address hash
def active_merchant_hash
{
name: full_name,
Expand All @@ -86,10 +95,15 @@ def active_merchant_hash
}
end

# @todo Remove this from the public API if possible.
# @return [true] whether or not the address requires a phone number to be
# valid
def require_phone?
true
end

# @todo Remove this from the public API if possible.
# @return [true] whether or not the address requires a zipcode to be valid
def require_zipcode?
true
end
Expand Down
45 changes: 23 additions & 22 deletions core/app/models/spree/adjustment.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
# Adjustments represent a change to the +item_total+ of an Order. Each adjustment
# has an +amount+ that can be either positive or negative.
#
# Adjustments can be "opened" or "closed".
# Once an adjustment is closed, it will not be automatically updated.
#
# Boolean attributes:
#
# +mandatory+
#
# If this flag is set to true then it means the the charge is required and will not
# be removed from the order, even if the amount is zero. In other words a record
# will be created even if the amount is zero. This is useful for representing things
# such as shipping and tax charges where you may want to make it explicitly clear
# that no charge was made for such things.
#
# +eligible?+
#
# This boolean attributes stores whether this adjustment is currently eligible
# for its order. Only eligible adjustments count towards the order's adjustment
# total. This allows an adjustment to be preserved if it becomes ineligible so
# it might be reinstated.
module Spree
# Adjustments represent a change to the +item_total+ of an Order. Each
# adjustment has an +amount+ that can be either positive or negative.
#
# Adjustments can be "opened" or "closed". Once an adjustment is closed, it
# will not be automatically updated.
#
# == Boolean attributes
#
# 1. *mandatory*
#
# If this flag is set to true then it means the the charge is required and
# will not be removed from the order, even if the amount is zero. In other
# words a record will be created even if the amount is zero. This is
# useful for representing things such as shipping and tax charges where
# you may want to make it explicitly clear that no charge was made for
# such things.
#
# 2. *eligible?*
#
# This boolean attributes stores whether this adjustment is currently
# eligible for its order. Only eligible adjustments count towards the
# order's adjustment total. This allows an adjustment to be preserved if
# it becomes ineligible so it might be reinstated.
class Adjustment < Spree::Base
belongs_to :adjustable, polymorphic: true, touch: true
belongs_to :source, polymorphic: true
Expand Down
9 changes: 4 additions & 5 deletions core/app/models/spree/calculator/free_shipping.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# TODO: Deprecate this class.
# This calculator will be removed in future versions of Spree.
# The only case where it was used was for Free Shipping Promotions.
# There is now a Promotion Action which deals with these types of promotions instead.
module Spree
# @deprecated This calculator will be removed in future versions of Spree.
# The only case where it was used was for Free Shipping Promotions. There is
# now a Promotion Action which deals with these types of promotions instead.
class Calculator::FreeShipping < Calculator
def self.description
Spree.t(:free_shipping)
Expand All @@ -19,4 +18,4 @@ def compute(object)
order.ship_total
end
end
end
end
54 changes: 44 additions & 10 deletions core/app/models/spree/credit_card.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ class CreditCard < Spree::Base
jcb: /^(?:2131|1800|35\d{3})\d{11}$/
}

# Sets the expiry date on this credit card.
#
# @param expiry [String] the desired new expiry date in one of the
# following formats: "mm/yy", "mm / yyyy", "mmyy", "mmyyyy"
def expiry=(expiry)
return unless expiry.present?

Expand All @@ -51,13 +55,20 @@ def expiry=(expiry)
self[:month] = self[:month].to_i if self[:month]
end

# Sets the credit card number, removing any non-numeric characters.
#
# @param num [String] the desired credit card number
def number=(num)
@number = num.gsub(/[^0-9]/, '') rescue nil
end

# cc_type is set by jquery.payment, which helpfully provides different
# types from Active Merchant. Converting them is necessary.
# Sets the credit card type, converting it to the preferred internal
# representation from jquery.payment's representation when appropriate.
#
# @param type [String] the desired credit card type
def cc_type=(type)
# cc_type is set by jquery.payment, which helpfully provides different
# types from Active Merchant. Converting them is necessary.
self[:cc_type] = case type
when 'mastercard', 'maestro' then 'master'
when 'amex' then 'american_express'
Expand All @@ -67,61 +78,84 @@ def cc_type=(type)
end
end

# Sets the last digits field based on the assigned credit card number.
def set_last_digits
number.to_s.gsub!(/\s/,'')
verification_value.to_s.gsub!(/\s/,'')
self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
end

# @return [String] the credit card type if it can be determined from the
# number, otherwise the empty string
def try_type_from_number
numbers = number.delete(' ') if number
CARD_TYPES.find{|type, pattern| return type.to_s if numbers =~ pattern}.to_s
end

# @return [Boolean] true when a verification value is present
def verification_value?
verification_value.present?
end

# Show the card number, with all but last 4 numbers replace with "X". (XXXX-XXXX-XXXX-4338)
# @return [String] the card number, with all but last 4 numbers replace
# with "X", as in "XXXX-XXXX-XXXX-4338"
def display_number
"XXXX-XXXX-XXXX-#{last_digits}"
end

# @return [Array<String>] the actions available on this credit card
def actions
%w{capture void credit}
end

# Indicates whether its possible to capture the payment
# @param payment [Spree::Payment] the payment we want to know if can be captured
# @return [Boolean] true when the payment is in the pending or checkout states
def can_capture?(payment)
payment.pending? || payment.checkout?
end

# Indicates whether its possible to void the payment.
# @param payment [Spree::Payment] the payment we want to know if can be voided
# @return [Boolean] true when the payment is not failed or voided
def can_void?(payment)
!payment.failed? && !payment.void?
end

# Indicates whether its possible to credit the payment. Note that most gateways require that the
# payment be settled first which generally happens within 12-24 hours of the transaction.
# Indicates whether its possible to credit the payment. Note that most
# gateways require that the payment be settled first which generally
# happens within 12-24 hours of the transaction.
#
# @param payment [Spree::Payment] the payment we want to know if can be credited
# @return [Boolean] true when the payment is completed and can be credited
def can_credit?(payment)
payment.completed? && payment.credit_allowed > 0
end

# @return [Boolean] true when there is a gateway customer or payment
# profile id present
def has_payment_profile?
gateway_customer_profile_id.present? || gateway_payment_profile_id.present?
end

# ActiveMerchant needs first_name/last_name because we pass it a Spree::CreditCard and it calls those methods on it.
# Looking at the ActiveMerchant source code we should probably be calling #to_active_merchant before passing
# the object to ActiveMerchant but this should do for now.
# @note ActiveMerchant needs first_name/last_name because we pass it a
# Spree::CreditCard and it calls those methods on it.
# @todo We should probably be calling #to_active_merchant before passing
# the object to ActiveMerchant.
# @return [String] the first name on this credit card
def first_name
name.to_s.split(/[[:space:]]/, 2)[0]
end

# @note ActiveMerchant needs first_name/last_name because we pass it a
# Spree::CreditCard and it calls those methods on it.
# @todo We should probably be calling #to_active_merchant before passing
# the object to ActiveMerchant.
# @return [String] the last name on this credit card
def last_name
name.to_s.split(/[[:space:]]/, 2)[1]
end

# @return [ActiveMerchant::Billing::CreditCard] an ActiveMerchant credit
# card that represents this credit card
def to_active_merchant
ActiveMerchant::Billing::CreditCard.new(
:number => number,
Expand Down
2 changes: 1 addition & 1 deletion core/app/models/spree/gateway/bogus_simple.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Bogus Gateway that doesn't support payment profiles
module Spree
# Bogus Gateway that doesn't support payment profiles.
class Gateway::BogusSimple < Gateway::Bogus

def payment_profiles_supported?
Expand Down
34 changes: 27 additions & 7 deletions core/app/models/spree/inventory_unit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,24 @@ class InventoryUnit < Spree::Base
end
end

# This was refactored from a simpler query because the previous implementation
# led to issues once users tried to modify the objects returned. That's due
# to ActiveRecord `joins(shipment: :stock_location)` only returning readonly
# objects
#
# Returns an array of backordered inventory units as per a given stock item
# @param stock_item [Spree::StockItem] the stock item of the desired
# inventory units
# @return [Array<Spree::InventoryUnit>] an array of backordered inventory
# units for the given stock item
def self.backordered_for_stock_item(stock_item)
# This was refactored from a simpler query because the previous
# implementation led to issues once users tried to modify the objects
# returned. That's due to ActiveRecord `joins(shipment: :stock_location)`
# only returning readonly objects
backordered_per_variant(stock_item).select do |unit|
unit.shipment.stock_location == stock_item.stock_location
end
end

# Updates the given inventory units to not be pending.
#
# @param inventory_units [<Spree::InventoryUnit>] the inventory to be
# finalized
def self.finalize_units!(inventory_units)
inventory_units.map do |iu|
iu.update_columns(
Expand All @@ -73,28 +79,42 @@ def self.finalize_units!(inventory_units)
end
end

# @return [Spree::StockItem] the first stock item from this shipment's
# stock location that is associated with this inventory unit's variant
def find_stock_item
Spree::StockItem.where(stock_location_id: shipment.stock_location_id,
variant_id: variant_id).first
end

# Remove variant default_scope `deleted_at: nil`
# @note This returns the variant regardless of whether it was soft
# deleted.
# @return [Spree::Variant] this inventory unit's variant.
def variant
Spree::Variant.unscoped { super }
end

# @return [Spree::ReturnItem] a valid return item for this inventory unit
# if one exists, or a new one if one does not
def current_or_new_return_item
Spree::ReturnItem.from_inventory_unit(self)
end

# @return [BigDecimal] the portion of the additional tax on the line item
# this inventory unit belongs to that is associated with this individual
# inventory unit
def additional_tax_total
line_item.additional_tax_total * percentage_of_line_item
end

# @return [BigDecimal] the portion of the included tax on the line item
# this inventory unit belongs to that is associated with this
# individual inventory unit
def included_tax_total
line_item.included_tax_total * percentage_of_line_item
end

# @return [Boolean] true if this inventory unit has any return items
# which have requested exchanges
def exchange_requested?
return_items.not_expired.any?(&:exchange_requested?)
end
Expand Down
5 changes: 4 additions & 1 deletion core/app/models/spree/legacy_user.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# Default implementation of User. This class is intended to be modified by extensions (ex. spree_auth_devise)
module Spree
# Default implementation of User.
#
# @note This class is intended to be modified by extensions (ex.
# spree_auth_devise)
class LegacyUser < Spree::Base
include UserAddress
include UserPaymentSource
Expand Down
7 changes: 4 additions & 3 deletions core/app/models/spree/order_promotion.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Spree::OrderPromotion represents the relationship between:
# 1. A promotion that a user attempted to apply to their order
# 2. The specific code that they used
module Spree
# Spree::OrderPromotion represents the relationship between:
#
# 1. A promotion that a user attempted to apply to their order
# 2. The specific code that they used
class OrderPromotion < ActiveRecord::Base
self.table_name = 'spree_orders_promotions'

Expand Down
Loading