Class: Spree::Order
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Spree::Order
- Defined in:
- app/models/spree/order.rb
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.
- #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.
- #clone_billing_address ⇒ Object
- #completed? ⇒ Boolean
- #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.
- #creditcards ⇒ Object
- #deliver_order_confirmation_email ⇒ 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.
-
#generate_order_number ⇒ Object
FIXME refactor this method and implement validation using validates_* utilities.
- #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
- #restore_state ⇒ Object
- #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.
Instance Attribute Details
#use_billing ⇒ Object
Returns the value of attribute use_billing.
252 253 254 |
# File 'app/models/spree/order.rb', line 252 def use_billing @use_billing end |
Class Method Details
.between(start_date, end_date) ⇒ Object
105 106 107 |
# File 'app/models/spree/order.rb', line 105 def self.between(start_date, end_date) where(:created_at => start_date..end_date) end |
.by_customer(customer) ⇒ Object
109 110 111 |
# File 'app/models/spree/order.rb', line 109 def self.by_customer(customer) joins(:user).where("#{Spree::User.table_name}.email" => customer) end |
.by_number(number) ⇒ Object
101 102 103 |
# File 'app/models/spree/order.rb', line 101 def self.by_number(number) where(:number => number) end |
.by_state(state) ⇒ Object
113 114 115 |
# File 'app/models/spree/order.rb', line 113 def self.by_state(state) where(:state => state) end |
.complete ⇒ Object
117 118 119 |
# File 'app/models/spree/order.rb', line 117 def self.complete where('completed_at IS NOT NULL') end |
.incomplete ⇒ Object
121 122 123 |
# File 'app/models/spree/order.rb', line 121 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
126 127 128 |
# File 'app/models/spree/order.rb', line 126 def self.register_update_hook(hook) self.update_hooks.add(hook) end |
Instance Method Details
#add_variant(variant, quantity = 1) ⇒ Object
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
# File 'app/models/spree/order.rb', line 274 def add_variant(variant, quantity = 1) current_item = contains?(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 # populate line_items attributes for additional_fields entries # that have populate => [:line_item] Variant.additional_fields.select { |f| !f[:populate].nil? && f[:populate].include?(:line_item) }.each do |field| value = '' if field[:only].nil? || field[:only].include?(:variant) value = variant.send(field[:name].gsub(' ', '_').downcase) elsif field[:only].include?(:product) value = variant.product.send(field[:name].gsub(' ', '_').downcase) end current_item.update_attribute(field[:name].gsub(' ', '_').downcase, value) end self.reload current_item end |
#allow_cancel? ⇒ Boolean
263 264 265 266 |
# File 'app/models/spree/order.rb', line 263 def allow_cancel? return false unless completed? and state != 'canceled' %w{ready backorder pending}.include? shipment_state end |
#allow_resume? ⇒ Boolean
268 269 270 271 272 |
# File 'app/models/spree/order.rb', line 268 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
131 132 133 |
# File 'app/models/spree/order.rb', line 131 def amount line_items.map(&:amount).sum end |
#available_payment_methods ⇒ Object
434 435 436 |
# File 'app/models/spree/order.rb', line 434 def available_payment_methods @available_payment_methods ||= PaymentMethod.available(:front_end) end |
#available_shipping_methods(display_on = nil) ⇒ Object
Helper methods for checkout steps
411 412 413 414 |
# File 'app/models/spree/order.rb', line 411 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.
161 162 163 164 |
# File 'app/models/spree/order.rb', line 161 def backordered? return false unless Spree::Config[:track_inventory_levels] inventory_units.backorder.present? end |
#billing_firstname ⇒ Object
446 447 448 |
# File 'app/models/spree/order.rb', line 446 def billing_firstname bill_address.try(:firstname) end |
#billing_lastname ⇒ Object
450 451 452 |
# File 'app/models/spree/order.rb', line 450 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.
146 147 148 |
# File 'app/models/spree/order.rb', line 146 def checkout_allowed? line_items.count > 0 end |
#clone_billing_address ⇒ Object
254 255 256 257 258 259 260 261 |
# File 'app/models/spree/order.rb', line 254 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
139 140 141 |
# File 'app/models/spree/order.rb', line 139 def completed? !! completed_at end |
#contains?(variant) ⇒ Boolean
319 320 321 |
# File 'app/models/spree/order.rb', line 319 def contains?(variant) line_items.detect { |line_item| line_item.variant_id == variant.id } end |
#create_shipment! ⇒ Object
Creates a new shipment (adjustment is created by shipment model)
347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'app/models/spree/order.rb', line 347 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.
338 339 340 341 342 343 344 |
# File 'app/models/spree/order.rb', line 338 def create_tax_charge! # destroy any previous adjustments (eveything is recalculated from scratch) adjustments.tax.each(&:destroy) price_adjustments.each(&:destroy) TaxRate.match(self).each { |rate| rate.adjust(self) } end |
#creditcards ⇒ Object
374 375 376 377 |
# File 'app/models/spree/order.rb', line 374 def creditcards creditcard_ids = payments.from_creditcard.map(&:source_id).uniq Creditcard.scoped(:conditions => { :id => creditcard_ids }) end |
#deliver_order_confirmation_email ⇒ Object
400 401 402 403 404 405 406 407 |
# File 'app/models/spree/order.rb', line 400 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 |
#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.
175 176 177 178 |
# File 'app/models/spree/order.rb', line 175 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
385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
# File 'app/models/spree/order.rb', line 385 def finalize! update_attribute(:completed_at, Time.now) InventoryUnit.assign_opening_inventory(self) # lock any optional adjustments (coupon promotions, etc.) adjustments.optional.each { |adjustment| adjustment.update_attribute('locked', true) } deliver_order_confirmation_email self.state_changes.create({ :previous_state => 'cart', :next_state => 'complete', :name => 'order' , :user_id => (User.respond_to?(:current) && User.current.try(:id)) || self.user_id }, :without_protection => true) end |
#generate_order_number ⇒ Object
FIXME refactor this method and implement validation using validates_* utilities
304 305 306 307 308 309 310 311 312 |
# File 'app/models/spree/order.rb', line 304 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 |
#insufficient_stock_lines ⇒ Object
458 459 460 |
# File 'app/models/spree/order.rb', line 458 def insufficient_stock_lines line_items.select &:insufficient_stock? end |
#item_count ⇒ Object
Indicates the number of items in the order
156 157 158 |
# File 'app/models/spree/order.rb', line 156 def item_count line_items.map(&:quantity).sum end |
#merge!(order) ⇒ Object
462 463 464 465 466 467 |
# File 'app/models/spree/order.rb', line 462 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
368 369 370 371 372 |
# File 'app/models/spree/order.rb', line 368 def name if (address = bill_address || ship_address) "#{address.firstname} #{address.lastname}" end end |
#outstanding_balance ⇒ Object
360 361 362 |
# File 'app/models/spree/order.rb', line 360 def outstanding_balance total - payment_total end |
#outstanding_balance? ⇒ Boolean
364 365 366 |
# File 'app/models/spree/order.rb', line 364 def outstanding_balance? self.outstanding_balance != 0 end |
#paid? ⇒ Boolean
426 427 428 |
# File 'app/models/spree/order.rb', line 426 def paid? payment_state == 'paid' end |
#payment ⇒ Object
430 431 432 |
# File 'app/models/spree/order.rb', line 430 def payment payments.first end |
#payment_method ⇒ Object
438 439 440 441 442 443 444 |
# File 'app/models/spree/order.rb', line 438 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
151 152 153 |
# File 'app/models/spree/order.rb', line 151 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.
194 195 196 197 198 199 200 201 202 203 204 |
# File 'app/models/spree/order.rb', line 194 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.
182 183 184 185 186 187 188 189 190 |
# File 'app/models/spree/order.rb', line 182 def price_adjustments adjustments = [] line_items.each do |line_item| adjustments.concat line_item.adjustments end adjustments end |
#process_payments! ⇒ Object
379 380 381 |
# File 'app/models/spree/order.rb', line 379 def process_payments! ret = payments.each(&:process!) end |
#products ⇒ Object
454 455 456 |
# File 'app/models/spree/order.rb', line 454 def products line_items.map { |li| li.variant.product } end |
#quantity_of(variant) ⇒ Object
323 324 325 326 |
# File 'app/models/spree/order.rb', line 323 def quantity_of(variant) line_item = line_items.find { |line_item| line_item.variant_id == variant.id } line_item ? line_item.quantity : 0 end |
#rate_hash ⇒ Object
416 417 418 419 420 421 422 423 424 |
# File 'app/models/spree/order.rb', line 416 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 |
#restore_state ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 |
# File 'app/models/spree/order.rb', line 238 def restore_state # pop the resume event so we can see what the event before that was state_changes.pop if state_changes.last.name == 'resume' update_attribute('state', state_changes.last.previous_state) if paid? raise 'do something with inventory' #Spree::InventoryUnit.assign_opening_inventory(self) if inventory_units.empty? #shipment.inventory_units = inventory_units #shipment.ready! end end |
#ship_total ⇒ Object
328 329 330 |
# File 'app/models/spree/order.rb', line 328 def ship_total adjustments.shipping.map(&:amount).sum end |
#shipment ⇒ Object
convenience method since many stores will not allow user to create multiple shipments
315 316 317 |
# File 'app/models/spree/order.rb', line 315 def shipment @shipment ||= shipments.last end |
#tax_total ⇒ Object
332 333 334 |
# File 'app/models/spree/order.rb', line 332 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
168 169 170 171 |
# File 'app/models/spree/order.rb', line 168 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
135 136 137 |
# File 'app/models/spree/order.rb', line 135 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.)
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
# File 'app/models/spree/order.rb', line 210 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 |