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, currency = nil) ⇒ 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.
- #awaiting_returns? ⇒ Boolean
-
#backordered? ⇒ Boolean
Indicates whether there are any backordered InventoryUnits associated with the Order.
- #billing_firstname ⇒ Object
- #billing_lastname ⇒ Object
- #can_ship? ⇒ Boolean
-
#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
- #currency ⇒ Object
- #deliver_order_confirmation_email ⇒ Object
- #display_adjustment_total ⇒ Object
- #display_item_total ⇒ Object
- #display_outstanding_balance ⇒ 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
-
#has_unprocessed_payments? ⇒ Boolean
Used by the checkout state machine to check for unprocessed payments The Order should be only be able to proceed to complete if there are unprocessed payments and there is payment required.
- #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_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped.
- #pending_payments ⇒ Object
-
#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.
- #state_changed(name) ⇒ Object
- #tax_total ⇒ Object
-
#tax_zone ⇒ Object
Returns the relevant zone (if any) to be used for taxation purposes.
- #to_param ⇒ Object
- #update! ⇒ Object
- #update_totals ⇒ Object
- #updater ⇒ Object
- #variants ⇒ Object
Methods included from Checkout
Instance Attribute Details
#use_billing ⇒ Object
Returns the value of attribute use_billing.
76 77 78 |
# File 'app/models/spree/order.rb', line 76 def use_billing @use_billing end |
Class Method Details
.between(start_date, end_date) ⇒ Object
95 96 97 |
# File 'app/models/spree/order.rb', line 95 def self.between(start_date, end_date) where(:created_at => start_date..end_date) end |
.by_customer(customer) ⇒ Object
99 100 101 |
# File 'app/models/spree/order.rb', line 99 def self.by_customer(customer) joins(:user).where("#{Spree.user_class.table_name}.email" => customer) end |
.by_number(number) ⇒ Object
91 92 93 |
# File 'app/models/spree/order.rb', line 91 def self.by_number(number) where(:number => number) end |
.by_state(state) ⇒ Object
103 104 105 |
# File 'app/models/spree/order.rb', line 103 def self.by_state(state) where(:state => state) end |
.complete ⇒ Object
107 108 109 |
# File 'app/models/spree/order.rb', line 107 def self.complete where('completed_at IS NOT NULL') end |
.incomplete ⇒ Object
111 112 113 |
# File 'app/models/spree/order.rb', line 111 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
116 117 118 |
# File 'app/models/spree/order.rb', line 116 def self.register_update_hook(hook) self.update_hooks.add(hook) end |
Instance Method Details
#add_variant(variant, quantity = 1, currency = nil) ⇒ Object
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'app/models/spree/order.rb', line 265 def add_variant(variant, quantity = 1, currency = nil) current_item = find_line_item_by_variant(variant) if current_item current_item.quantity += quantity current_item.currency = currency unless currency.nil? current_item.save else current_item = LineItem.new(:quantity => quantity) current_item.variant = variant if currency current_item.currency = currency unless currency.nil? current_item.price = variant.price_in(currency).amount else current_item.price = variant.price end self.line_items << current_item end self.reload current_item end |
#allow_cancel? ⇒ Boolean
250 251 252 253 |
# File 'app/models/spree/order.rb', line 250 def allow_cancel? return false unless completed? and state != 'canceled' shipment_state.nil? || %w{ready backorder pending}.include?(shipment_state) end |
#allow_resume? ⇒ Boolean
255 256 257 258 259 |
# File 'app/models/spree/order.rb', line 255 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
121 122 123 |
# File 'app/models/spree/order.rb', line 121 def amount line_items.sum(&:amount) end |
#associate_user!(user) ⇒ Object
Associates the specified user with the order.
288 289 290 291 292 293 294 |
# File 'app/models/spree/order.rb', line 288 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
438 439 440 |
# File 'app/models/spree/order.rb', line 438 def available_payment_methods @available_payment_methods ||= PaymentMethod.available(:front_end) end |
#available_shipping_methods(display_on = nil) ⇒ Object
Helper methods for checkout steps
418 419 420 421 |
# File 'app/models/spree/order.rb', line 418 def available_shipping_methods(display_on = nil) return [] unless ship_address ShippingMethod.all_available(self, display_on) end |
#awaiting_returns? ⇒ Boolean
261 262 263 |
# File 'app/models/spree/order.rb', line 261 def awaiting_returns? .any? { || . } end |
#backordered? ⇒ Boolean
Indicates whether there are any backordered InventoryUnits associated with the Order.
189 190 191 192 |
# File 'app/models/spree/order.rb', line 189 def backordered? return false unless Spree::Config[:track_inventory_levels] inventory_units.backordered.present? end |
#billing_firstname ⇒ Object
462 463 464 |
# File 'app/models/spree/order.rb', line 462 def billing_firstname bill_address.try(:firstname) end |
#billing_lastname ⇒ Object
466 467 468 |
# File 'app/models/spree/order.rb', line 466 def billing_lastname bill_address.try(:lastname) end |
#can_ship? ⇒ Boolean
372 373 374 |
# File 'app/models/spree/order.rb', line 372 def can_ship? self.complete? || self.resumed? || self.awaiting_return? || self.returned? 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.
156 157 158 |
# File 'app/models/spree/order.rb', line 156 def checkout_allowed? line_items.count > 0 end |
#clear_adjustments! ⇒ Object
destroy any previous adjustments. Adjustments will be recalculated during order update.
506 507 508 509 |
# File 'app/models/spree/order.rb', line 506 def clear_adjustments! adjustments.tax.each(&:destroy) price_adjustments.each(&:destroy) end |
#clone_billing_address ⇒ Object
241 242 243 244 245 246 247 248 |
# File 'app/models/spree/order.rb', line 241 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
149 150 151 |
# File 'app/models/spree/order.rb', line 149 def completed? !! completed_at end |
#confirmation_required? ⇒ Boolean
If true, causes the confirmation step to happen during the checkout process
167 168 169 |
# File 'app/models/spree/order.rb', line 167 def confirmation_required? payments.map(&:payment_method).any?(&:payment_profiles_supported?) end |
#contains?(variant) ⇒ Boolean
312 313 314 |
# File 'app/models/spree/order.rb', line 312 def contains?(variant) find_line_item_by_variant(variant).present? end |
#create_shipment! ⇒ Object
Creates a new shipment (adjustment is created by shipment model)
346 347 348 349 350 351 352 353 354 355 356 |
# File 'app/models/spree/order.rb', line 346 def create_shipment! shipping_method(true) if shipment.present? shipment.update_attributes!({:shipping_method => shipping_method, :inventory_units => self.inventory_units}, :without_protection => true) 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.
341 342 343 |
# File 'app/models/spree/order.rb', line 341 def create_tax_charge! Spree::TaxRate.adjust(self) end |
#credit_cards ⇒ Object
376 377 378 379 |
# File 'app/models/spree/order.rb', line 376 def credit_cards credit_card_ids = payments.from_credit_card.pluck(:source_id).uniq CreditCard.scoped(:conditions => { :id => credit_card_ids }) end |
#currency ⇒ Object
125 126 127 |
# File 'app/models/spree/order.rb', line 125 def currency self[:currency] || Spree::Config[:currency] end |
#deliver_order_confirmation_email ⇒ Object
407 408 409 410 411 412 413 414 |
# File 'app/models/spree/order.rb', line 407 def deliver_order_confirmation_email begin OrderMailer.confirm_email(self.id).deliver rescue Exception => e logger.error("#{e.class.name}: #{e.}") logger.error(e.backtrace * "\n") end end |
#display_adjustment_total ⇒ Object
137 138 139 |
# File 'app/models/spree/order.rb', line 137 def display_adjustment_total Spree::Money.new(adjustment_total, { :currency => currency }) end |
#display_item_total ⇒ Object
133 134 135 |
# File 'app/models/spree/order.rb', line 133 def display_item_total Spree::Money.new(item_total, { :currency => currency }) end |
#display_outstanding_balance ⇒ Object
129 130 131 |
# File 'app/models/spree/order.rb', line 129 def display_outstanding_balance Spree::Money.new(outstanding_balance, { :currency => currency }) end |
#display_total ⇒ Object
141 142 143 |
# File 'app/models/spree/order.rb', line 141 def display_total Spree::Money.new(total, { :currency => currency }) end |
#empty! ⇒ Object
499 500 501 502 |
# File 'app/models/spree/order.rb', line 499 def empty! adjustments.destroy_all line_items.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.
203 204 205 206 |
# File 'app/models/spree/order.rb', line 203 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
383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
# File 'app/models/spree/order.rb', line 383 def finalize! touch :completed_at InventoryUnit.assign_opening_inventory(self) # lock all adjustments (coupon promotions, etc.) adjustments.each { |adjustment| adjustment.update_column('locked', true) } # update payment and shipment(s) states, and save updater.update_payment_state shipments.reload.each { |shipment| shipment.update!(self) } updater.update_shipment_state save updater.run_hooks 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
321 322 323 |
# File 'app/models/spree/order.rb', line 321 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
297 298 299 300 301 302 303 304 305 |
# File 'app/models/spree/order.rb', line 297 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
511 512 513 |
# File 'app/models/spree/order.rb', line 511 def has_step?(step) checkout_steps.include?(step) end |
#has_unprocessed_payments? ⇒ Boolean
Used by the checkout state machine to check for unprocessed payments The Order should be only be able to proceed to complete if there are unprocessed payments and there is payment required.
The reason for this is directly before an order transitions to complete, all of the order’s payments have ‘process!` called on it (look in order/checkout.rb). If payment is required and there’s no payments which haven’t already been tried, then the order cannot be paid for and therefore should not be able to become complete.
179 180 181 |
# File 'app/models/spree/order.rb', line 179 def has_unprocessed_payments? payments.with_state('checkout').reload.exists? end |
#insufficient_stock_lines ⇒ Object
478 479 480 |
# File 'app/models/spree/order.rb', line 478 def insufficient_stock_lines line_items.select &:insufficient_stock? end |
#item_count ⇒ Object
Indicates the number of items in the order
184 185 186 |
# File 'app/models/spree/order.rb', line 184 def item_count line_items.sum(:quantity) end |
#merge!(order) ⇒ Object
482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'app/models/spree/order.rb', line 482 def merge!(order) order.line_items.each do |line_item| next unless line_item.currency == currency current_line_item = self.line_items.find_by_variant_id(line_item.variant_id) if current_line_item current_line_item.quantity += line_item.quantity current_line_item.save else line_item.order_id = self.id line_item.save end end # So that the destroy doesn't take out line items which may have been re-assigned order.line_items.reload order.destroy end |
#name ⇒ Object
366 367 368 369 370 |
# File 'app/models/spree/order.rb', line 366 def name if (address = bill_address || ship_address) "#{address.firstname} #{address.lastname}" end end |
#outstanding_balance ⇒ Object
358 359 360 |
# File 'app/models/spree/order.rb', line 358 def outstanding_balance total - payment_total end |
#outstanding_balance? ⇒ Boolean
362 363 364 |
# File 'app/models/spree/order.rb', line 362 def outstanding_balance? self.outstanding_balance != 0 end |
#paid? ⇒ Boolean
434 435 436 |
# File 'app/models/spree/order.rb', line 434 def paid? payment_state == 'paid' end |
#payment_required? ⇒ Boolean
Is this a free order in which case the payment step should be skipped
161 162 163 164 |
# File 'app/models/spree/order.rb', line 161 def payment_required? update_totals total.to_f > 0.0 end |
#pending_payments ⇒ Object
442 443 444 |
# File 'app/models/spree/order.rb', line 442 def pending_payments payments.select {|p| p.state == "checkout"} 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.
222 223 224 225 226 227 |
# File 'app/models/spree/order.rb', line 222 def price_adjustment_totals Hash[price_adjustments.group_by(&:label).map do |label, adjustments| total = adjustments.sum(&:amount) [label, Spree::Money.new(total, { :currency => currency })] end] 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.
210 211 212 213 214 215 216 217 218 |
# File 'app/models/spree/order.rb', line 210 def price_adjustments adjustments = [] line_items.each do |line_item| adjustments.concat line_item.adjustments end adjustments end |
#process_payments! ⇒ Object
446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
# File 'app/models/spree/order.rb', line 446 def process_payments! begin pending_payments.each do |payment| break if payment_total >= total payment.process! if payment.completed? self.payment_total += payment.amount end end rescue Core::GatewayError !!Spree::Config[:allow_checkout_on_gateway_error] end end |
#products ⇒ Object
470 471 472 |
# File 'app/models/spree/order.rb', line 470 def products line_items.map { |li| li.variant.product } end |
#quantity_of(variant) ⇒ Object
316 317 318 319 |
# File 'app/models/spree/order.rb', line 316 def quantity_of(variant) line_item = find_line_item_by_variant(variant) line_item ? line_item.quantity : 0 end |
#rate_hash ⇒ Object
423 424 425 426 427 428 429 430 431 432 |
# File 'app/models/spree/order.rb', line 423 def rate_hash @rate_hash ||= available_shipping_methods.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, :currency => currency) 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
335 336 337 |
# File 'app/models/spree/order.rb', line 335 def remove_invalid_shipments! shipments.each { |s| s.destroy unless s.shipping_method.available_to_order?(self) } end |
#ship_total ⇒ Object
325 326 327 |
# File 'app/models/spree/order.rb', line 325 def ship_total adjustments.shipping.map(&:amount).sum end |
#shipment ⇒ Object
convenience method since many stores will not allow user to create multiple shipments
308 309 310 |
# File 'app/models/spree/order.rb', line 308 def shipment @shipment ||= shipments.last end |
#state_changed(name) ⇒ Object
515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 |
# File 'app/models/spree/order.rb', line 515 def state_changed(name) state = "#{name}_state" if persisted? old_state = self.send("#{state}_was") new_state = self.send(state) if old_state != new_state self.state_changes.create({ :previous_state => old_state, :next_state => self.send(state), :name => name, :user_id => self.user_id }, :without_protection => true) end end end |
#tax_total ⇒ Object
329 330 331 |
# File 'app/models/spree/order.rb', line 329 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
196 197 198 199 |
# File 'app/models/spree/order.rb', line 196 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
145 146 147 |
# File 'app/models/spree/order.rb', line 145 def to_param number.to_s.to_url.upcase end |
#update! ⇒ Object
233 234 235 |
# File 'app/models/spree/order.rb', line 233 def update! updater.update end |
#update_totals ⇒ Object
237 238 239 |
# File 'app/models/spree/order.rb', line 237 def update_totals updater.update_totals end |
#updater ⇒ Object
229 230 231 |
# File 'app/models/spree/order.rb', line 229 def updater @updater ||= OrderUpdater.new(self) end |
#variants ⇒ Object
474 475 476 |
# File 'app/models/spree/order.rb', line 474 def variants line_items.map(&:variant) end |