Class: Spree::Order
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Spree::Order
- Includes:
- Checkout
- Defined in:
- app/models/spree/order.rb,
app/models/spree/order/checkout.rb
Defined Under Namespace
Modules: Checkout
Instance Attribute Summary collapse
-
#use_billing ⇒ Object
Returns the value of attribute use_billing.
Class Method Summary collapse
- .between(start_date, end_date) ⇒ Object
- .by_customer(customer) ⇒ Object
- .by_number(number) ⇒ Object
- .by_state(state) ⇒ Object
- .complete ⇒ Object
- .incomplete ⇒ Object
-
.register_update_hook(hook) ⇒ Object
Use this method in other gems that wish to register their own custom logic that should be called after Order#updat.
Instance Method Summary collapse
- #add_variant(variant, quantity = 1) ⇒ Object
- #allow_cancel? ⇒ Boolean
- #allow_resume? ⇒ Boolean
-
#amount ⇒ Object
For compatiblity with Calculator::PriceSack.
-
#associate_user!(user) ⇒ Object
Associates the specified user with the order.
- #available_payment_methods ⇒ Object
-
#available_shipping_methods(display_on = nil) ⇒ Object
Helper methods for checkout steps.
-
#backordered? ⇒ Boolean
Indicates whether there are any backordered InventoryUnits associated with the Order.
- #billing_firstname ⇒ Object
- #billing_lastname ⇒ Object
-
#checkout_allowed? ⇒ Boolean
Indicates whether or not the user is allowed to proceed to checkout.
-
#clear_adjustments! ⇒ Object
destroy any previous adjustments.
- #clone_billing_address ⇒ Object
- #completed? ⇒ Boolean
-
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process.
- #contains?(variant) ⇒ Boolean
-
#create_shipment! ⇒ Object
Creates a new shipment (adjustment is created by shipment model).
-
#create_tax_charge! ⇒ Object
Creates new tax charges if there are any applicable rates.
- #credit_cards ⇒ Object
- #deliver_order_confirmation_email ⇒ Object
- #display_total ⇒ Object
- #empty! ⇒ Object
-
#exclude_tax? ⇒ Boolean
Indicates whether tax should be backed out of the price calcualtions in cases where prices include tax but the customer is not required to pay taxes in that case.
-
#finalize! ⇒ Object
Finalizes an in progress order after checkout is complete.
- #find_line_item_by_variant(variant) ⇒ Object
-
#generate_order_number ⇒ Object
FIXME refactor this method and implement validation using validates_* utilities.
- #has_step?(step) ⇒ Boolean
- #insufficient_stock_lines ⇒ Object
-
#item_count ⇒ Object
Indicates the number of items in the order.
- #merge!(order) ⇒ Object
- #name ⇒ Object
- #outstanding_balance ⇒ Object
- #outstanding_balance? ⇒ Boolean
- #paid? ⇒ Boolean
- #payment ⇒ Object
- #payment_method ⇒ Object
-
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped.
-
#price_adjustment_totals ⇒ Object
Array of totals grouped by Adjustment#label.
-
#price_adjustments ⇒ Object
Array of adjustments that are inclusive in the variant price.
- #process_payments! ⇒ Object
- #products ⇒ Object
- #quantity_of(variant) ⇒ Object
- #rate_hash ⇒ Object
-
#remove_invalid_shipments! ⇒ Object
Clear shipment when transitioning to delivery step of checkout if the current shipping address is not eligible for the existing shipping method.
- #ship_total ⇒ Object
-
#shipment ⇒ Object
convenience method since many stores will not allow user to create multiple shipments.
- #tax_total ⇒ Object
-
#tax_zone ⇒ Object
Returns the relevant zone (if any) to be used for taxation purposes.
- #to_param ⇒ Object
-
#update! ⇒ Object
This is a multi-purpose method for processing logic related to changes in the Order.
Methods included from Checkout
Instance Attribute Details
#use_billing ⇒ Object
Returns the value of attribute use_billing.
61 62 63 |
# File 'app/models/spree/order.rb', line 61 def use_billing @use_billing end |
Class Method Details
.between(start_date, end_date) ⇒ Object
80 81 82 |
# File 'app/models/spree/order.rb', line 80 def self.between(start_date, end_date) where(:created_at => start_date..end_date) end |
.by_customer(customer) ⇒ Object
84 85 86 |
# File 'app/models/spree/order.rb', line 84 def self.by_customer(customer) joins(:user).where("#{Spree.user_class.table_name}.email" => customer) end |
.by_number(number) ⇒ Object
76 77 78 |
# File 'app/models/spree/order.rb', line 76 def self.by_number(number) where(:number => number) end |
.by_state(state) ⇒ Object
88 89 90 |
# File 'app/models/spree/order.rb', line 88 def self.by_state(state) where(:state => state) end |
.complete ⇒ Object
92 93 94 |
# File 'app/models/spree/order.rb', line 92 def self.complete where('completed_at IS NOT NULL') end |
.incomplete ⇒ Object
96 97 98 |
# File 'app/models/spree/order.rb', line 96 def self.incomplete where(:completed_at => nil) end |
.register_update_hook(hook) ⇒ Object
Use this method in other gems that wish to register their own custom logic that should be called after Order#updat
101 102 103 |
# File 'app/models/spree/order.rb', line 101 def self.register_update_hook(hook) self.update_hooks.add(hook) end |
Instance Method Details
#add_variant(variant, quantity = 1) ⇒ Object
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
# File 'app/models/spree/order.rb', line 242 def add_variant(variant, quantity = 1) current_item = find_line_item_by_variant(variant) if current_item current_item.quantity += quantity current_item.save else current_item = LineItem.new(:quantity => quantity) current_item.variant = variant current_item.price = variant.price self.line_items << current_item end self.reload current_item end |
#allow_cancel? ⇒ Boolean
231 232 233 234 |
# File 'app/models/spree/order.rb', line 231 def allow_cancel? return false unless completed? and state != 'canceled' %w{ready backorder pending}.include? shipment_state end |
#allow_resume? ⇒ Boolean
236 237 238 239 240 |
# File 'app/models/spree/order.rb', line 236 def allow_resume? # we shouldn't allow resume for legacy orders b/c we lack the information necessary to restore to a previous state return false if state_changes.empty? || state_changes.last.previous_state.nil? true end |
#amount ⇒ Object
For compatiblity with Calculator::PriceSack
106 107 108 |
# File 'app/models/spree/order.rb', line 106 def amount line_items.sum(&:amount) end |
#associate_user!(user) ⇒ Object
Associates the specified user with the order.
259 260 261 262 263 264 265 |
# File 'app/models/spree/order.rb', line 259 def associate_user!(user) self.user = user self.email = user.email # disable validations since they can cause issues when associating # an incomplete address during the address step save(:validate => false) end |
#available_payment_methods ⇒ Object
399 400 401 |
# File 'app/models/spree/order.rb', line 399 def available_payment_methods @available_payment_methods ||= PaymentMethod.available(:front_end) end |
#available_shipping_methods(display_on = nil) ⇒ Object
Helper methods for checkout steps
376 377 378 379 |
# File 'app/models/spree/order.rb', line 376 def available_shipping_methods(display_on = nil) return [] unless ship_address ShippingMethod.all_available(self, display_on) end |
#backordered? ⇒ Boolean
Indicates whether there are any backordered InventoryUnits associated with the Order.
145 146 147 148 |
# File 'app/models/spree/order.rb', line 145 def backordered? return false unless Spree::Config[:track_inventory_levels] inventory_units.backorder.present? end |
#billing_firstname ⇒ Object
419 420 421 |
# File 'app/models/spree/order.rb', line 419 def billing_firstname bill_address.try(:firstname) end |
#billing_lastname ⇒ Object
423 424 425 |
# File 'app/models/spree/order.rb', line 423 def billing_lastname bill_address.try(:lastname) end |
#checkout_allowed? ⇒ Boolean
Indicates whether or not the user is allowed to proceed to checkout. Currently this is implemented as a check for whether or not there is at least one LineItem in the Order. Feel free to override this logic in your own application if you require additional steps before allowing a checkout.
125 126 127 |
# File 'app/models/spree/order.rb', line 125 def checkout_allowed? line_items.count > 0 end |
#clear_adjustments! ⇒ Object
destroy any previous adjustments. Adjustments will be recalculated during order update.
449 450 451 452 |
# File 'app/models/spree/order.rb', line 449 def clear_adjustments! adjustments.tax.each(&:destroy) price_adjustments.each(&:destroy) end |
#clone_billing_address ⇒ Object
222 223 224 225 226 227 228 229 |
# File 'app/models/spree/order.rb', line 222 def clone_billing_address if bill_address and self.ship_address.nil? self.ship_address = bill_address.clone else self.ship_address.attributes = bill_address.attributes.except('id', 'updated_at', 'created_at') end true end |
#completed? ⇒ Boolean
118 119 120 |
# File 'app/models/spree/order.rb', line 118 def completed? !! completed_at end |
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process
135 136 137 |
# File 'app/models/spree/order.rb', line 135 def confirmation_required? payment_method && payment_method.payment_profiles_supported? end |
#contains?(variant) ⇒ Boolean
283 284 285 |
# File 'app/models/spree/order.rb', line 283 def contains?(variant) find_line_item_by_variant(variant).present? end |
#create_shipment! ⇒ Object
Creates a new shipment (adjustment is created by shipment model)
317 318 319 320 321 322 323 324 325 326 327 |
# File 'app/models/spree/order.rb', line 317 def create_shipment! shipping_method(true) if shipment.present? shipment.update_attributes!(:shipping_method => shipping_method) else self.shipments << Shipment.create!({ :order => self, :shipping_method => shipping_method, :address => self.ship_address, :inventory_units => self.inventory_units}, :without_protection => true) end end |
#create_tax_charge! ⇒ Object
Creates new tax charges if there are any applicable rates. If prices already include taxes then price adjustments are created instead.
312 313 314 |
# File 'app/models/spree/order.rb', line 312 def create_tax_charge! Spree::TaxRate.adjust(self) end |
#credit_cards ⇒ Object
343 344 345 346 |
# File 'app/models/spree/order.rb', line 343 def credit_cards credit_card_ids = payments.from_credit_card.map(&:source_id).uniq CreditCard.scoped(:conditions => { :id => credit_card_ids }) end |
#deliver_order_confirmation_email ⇒ Object
365 366 367 368 369 370 371 372 |
# File 'app/models/spree/order.rb', line 365 def deliver_order_confirmation_email begin OrderMailer.confirm_email(self).deliver rescue Exception => e logger.error("#{e.class.name}: #{e.}") logger.error(e.backtrace * "\n") end end |
#display_total ⇒ Object
110 111 112 |
# File 'app/models/spree/order.rb', line 110 def display_total Spree::Money.new(total) end |
#empty! ⇒ Object
442 443 444 445 |
# File 'app/models/spree/order.rb', line 442 def empty! line_items.destroy_all adjustments.destroy_all end |
#exclude_tax? ⇒ Boolean
Indicates whether tax should be backed out of the price calcualtions in cases where prices include tax but the customer is not required to pay taxes in that case.
159 160 161 162 |
# File 'app/models/spree/order.rb', line 159 def exclude_tax? return false unless Spree::Config[:prices_inc_tax] return tax_zone != Zone.default_tax end |
#finalize! ⇒ Object
Finalizes an in progress order after checkout is complete. Called after transition to complete state when payments will have been processed
350 351 352 353 354 355 356 357 358 359 360 361 362 363 |
# File 'app/models/spree/order.rb', line 350 def finalize! touch :completed_at InventoryUnit.assign_opening_inventory(self) # lock any optional adjustments (coupon promotions, etc.) adjustments.optional.each { |adjustment| adjustment.update_column('locked', true) } deliver_order_confirmation_email self.state_changes.create({ :previous_state => 'cart', :next_state => 'complete', :name => 'order' , :user_id => self.user_id }, :without_protection => true) end |
#find_line_item_by_variant(variant) ⇒ Object
292 293 294 |
# File 'app/models/spree/order.rb', line 292 def find_line_item_by_variant(variant) line_items.detect { |line_item| line_item.variant_id == variant.id } end |
#generate_order_number ⇒ Object
FIXME refactor this method and implement validation using validates_* utilities
268 269 270 271 272 273 274 275 276 |
# File 'app/models/spree/order.rb', line 268 def generate_order_number record = true while record random = "R#{Array.new(9){rand(9)}.join}" record = self.class.where(:number => random).first end self.number = random if self.number.blank? self.number end |
#has_step?(step) ⇒ Boolean
454 455 456 |
# File 'app/models/spree/order.rb', line 454 def has_step?(step) checkout_steps.include?(step) end |
#insufficient_stock_lines ⇒ Object
431 432 433 |
# File 'app/models/spree/order.rb', line 431 def insufficient_stock_lines line_items.select &:insufficient_stock? end |
#item_count ⇒ Object
Indicates the number of items in the order
140 141 142 |
# File 'app/models/spree/order.rb', line 140 def item_count line_items.sum(:quantity) end |
#merge!(order) ⇒ Object
435 436 437 438 439 440 |
# File 'app/models/spree/order.rb', line 435 def merge!(order) order.line_items.each do |line_item| self.add_variant(line_item.variant, line_item.quantity) end order.destroy end |
#name ⇒ Object
337 338 339 340 341 |
# File 'app/models/spree/order.rb', line 337 def name if (address = bill_address || ship_address) "#{address.firstname} #{address.lastname}" end end |
#outstanding_balance ⇒ Object
329 330 331 |
# File 'app/models/spree/order.rb', line 329 def outstanding_balance total - payment_total end |
#outstanding_balance? ⇒ Boolean
333 334 335 |
# File 'app/models/spree/order.rb', line 333 def outstanding_balance? self.outstanding_balance != 0 end |
#paid? ⇒ Boolean
391 392 393 |
# File 'app/models/spree/order.rb', line 391 def paid? payment_state == 'paid' end |
#payment ⇒ Object
395 396 397 |
# File 'app/models/spree/order.rb', line 395 def payment payments.first end |
#payment_method ⇒ Object
403 404 405 406 407 408 409 |
# File 'app/models/spree/order.rb', line 403 def payment_method if payment and payment.payment_method payment.payment_method else available_payment_methods.first end end |
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped
130 131 132 |
# File 'app/models/spree/order.rb', line 130 def payment_required? total.to_f > 0.0 end |
#price_adjustment_totals ⇒ Object
Array of totals grouped by Adjustment#label. Useful for displaying price adjustments on an invoice. For example, you can display tax breakout for cases where tax is included in price.
178 179 180 181 182 183 184 185 186 187 188 |
# File 'app/models/spree/order.rb', line 178 def price_adjustment_totals totals = {} price_adjustments.each do |adjustment| label = adjustment.label totals[label] ||= 0 totals[label] = totals[label] + adjustment.amount end totals end |
#price_adjustments ⇒ Object
Array of adjustments that are inclusive in the variant price. Useful for when prices include tax (ex. VAT) and you need to record the tax amount separately.
166 167 168 169 170 171 172 173 174 |
# File 'app/models/spree/order.rb', line 166 def price_adjustments adjustments = [] line_items.each do |line_item| adjustments.concat line_item.adjustments end adjustments end |
#process_payments! ⇒ Object
411 412 413 414 415 416 417 |
# File 'app/models/spree/order.rb', line 411 def process_payments! begin payments.each(&:process!) rescue Core::GatewayError !!Spree::Config[:allow_checkout_on_gateway_error] end end |
#products ⇒ Object
427 428 429 |
# File 'app/models/spree/order.rb', line 427 def products line_items.map { |li| li.variant.product } end |
#quantity_of(variant) ⇒ Object
287 288 289 290 |
# File 'app/models/spree/order.rb', line 287 def quantity_of(variant) line_item = find_line_item_by_variant(variant) line_item ? line_item.quantity : 0 end |
#rate_hash ⇒ Object
381 382 383 384 385 386 387 388 389 |
# File 'app/models/spree/order.rb', line 381 def rate_hash @rate_hash ||= available_shipping_methods(:front_end).collect do |ship_method| next unless cost = ship_method.calculator.compute(self) ShippingRate.new( :id => ship_method.id, :shipping_method => ship_method, :name => ship_method.name, :cost => cost) end.compact.sort_by { |r| r.cost } end |
#remove_invalid_shipments! ⇒ Object
Clear shipment when transitioning to delivery step of checkout if the current shipping address is not eligible for the existing shipping method
306 307 308 |
# File 'app/models/spree/order.rb', line 306 def remove_invalid_shipments! shipments.each { |s| s.destroy unless s.shipping_method.available_to_order?(self) } end |
#ship_total ⇒ Object
296 297 298 |
# File 'app/models/spree/order.rb', line 296 def ship_total adjustments.shipping.map(&:amount).sum end |
#shipment ⇒ Object
convenience method since many stores will not allow user to create multiple shipments
279 280 281 |
# File 'app/models/spree/order.rb', line 279 def shipment @shipment ||= shipments.last end |
#tax_total ⇒ Object
300 301 302 |
# File 'app/models/spree/order.rb', line 300 def tax_total adjustments.tax.map(&:amount).sum end |
#tax_zone ⇒ Object
Returns the relevant zone (if any) to be used for taxation purposes. Uses default tax zone unless there is a specific match
152 153 154 155 |
# File 'app/models/spree/order.rb', line 152 def tax_zone zone_address = Spree::Config[:tax_using_ship_address] ? ship_address : bill_address Zone.match(zone_address) || Zone.default_tax end |
#to_param ⇒ Object
114 115 116 |
# File 'app/models/spree/order.rb', line 114 def to_param number.to_s.to_url.upcase end |
#update! ⇒ Object
This is a multi-purpose method for processing logic related to changes in the Order. It is meant to be called from various observers so that the Order is aware of changes that affect totals and other values stored in the Order. This method should never do anything to the Order that results in a save call on the object (otherwise you will end up in an infinite recursion as the associations try to save and then in turn try to call update!
again.)
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'app/models/spree/order.rb', line 194 def update! update_totals update_payment_state # give each of the shipments a chance to update themselves shipments.each { |shipment| shipment.update!(self) }#(&:update!) update_shipment_state update_adjustments # update totals a second time in case updated adjustments have an effect on the total update_totals update_attributes_without_callbacks({ :payment_state => payment_state, :shipment_state => shipment_state, :item_total => item_total, :adjustment_total => adjustment_total, :payment_total => payment_total, :total => total }) #ensure checkout payment always matches order total if payment and payment.checkout? and payment.amount != total payment.update_attributes_without_callbacks(:amount => total) end update_hooks.each { |hook| self.send hook } end |