app/models/orders/order.rb in artfully_ose-1.0.0.rc4 vs app/models/orders/order.rb in artfully_ose-1.1.0.rc1

- old
+ new

@@ -1,41 +1,87 @@ #Subclasses (and their type) should speak to the *location* or *nature* of the order, not the contents of the items # WebOrder, BoxOfficeOrder for example. NOT DonationOrder, since orders may contain multiple different item types class Order < ActiveRecord::Base include ActionView::Helpers::NumberHelper include ActionView::Helpers::TextHelper - include ArtfullyOseHelper include Ext::Integrations::Order + include OhNoes::Destroy + include ArtfullyOseHelper #This is a lambda used to by the items to calculate their net attr_accessor :per_item_processing_charge + attr_accessible :person_id, :organization_id, :person, :organization, :details + belongs_to :person belongs_to :organization + belongs_to :import belongs_to :parent, :class_name => "Order", :foreign_key => "parent_id" + belongs_to :gateway_transaction, :primary_key => :transaction_id, :foreign_key => :transaction_id has_many :children, :class_name => "Order", :foreign_key => "parent_id" - has_many :items - has_many :actions, :foreign_key => "subject_id" + has_many :items, :dependent => :destroy + has_many :actions, :foreign_key => "subject_id", :dependent => :destroy attr_accessor :skip_actions set_watch_for :created_at, :local_to => :organization set_watch_for :created_at, :local_to => :self, :as => :admins validates_presence_of :person_id validates_presence_of :organization_id + # Both of these are handle_asynchronously after_create :create_purchase_action, :unless => :skip_actions after_create :create_donation_actions, :unless => :skip_actions + after_create :sell_tickets + default_scope :order => 'orders.created_at DESC' scope :before, lambda { |time| where("orders.created_at < ?", time) } scope :after, lambda { |time| where("orders.created_at > ?", time) } scope :imported, where("fa_id IS NOT NULL") scope :not_imported, where("fa_id IS NULL") + scope :csv_imported, where("import_id IS NOT NULL") + scope :csv_not_imported, where("import_id IS NULL") scope :artfully, where("transaction_id IS NOT NULL") + searchable do + text :details, :id, :type, :location, :transaction_id, :payment_method, :special_instructions + + [:first_name, :last_name, :email].each do |person_field| + text person_field do + person.send(person_field) unless person.nil? + end + end + + text :organization_id do + organization.id + end + + text :organization_name do + organization.name + end + + text :event_name do + items.map{ |item| item.show.event.name unless item.show.nil? } + end + + string :details, :id, :type, :location, :transaction_id, :payment_method, :special_instructions + string :organization_id do + organization.id + end + + string :organization_name do + organization.name + end + + string :event_name, :multiple => true do + items.map{ |item| item.show.event.name unless item.show.nil? } + end + end + include Ext::DelayedIndexing + def self.in_range(start, stop, organization_id = nil) query = after(start).before(stop).includes(:items, :person, :organization).order("created_at DESC") if organization_id.present? query.where('organization_id = ?', organization_id) else @@ -46,27 +92,31 @@ def artfully? !transaction_id.nil? end def location + self.class.location + end + + def self.location "" end def total all_items.inject(0) {|sum, item| sum + item.total_price.to_i } end def nongift_amount all_items.inject(0) {|sum, item| sum + item.nongift_amount.to_i } end - - def tickets - items.select(&:ticket?) + + def destroyable? + ( (type.eql? "ApplicationOrder") || (type.eql? "ImportedOrder") ) && !is_fafs? && !artfully? && has_single_donation? end - - def donations - items.select(&:donation?) + + def editable? + ( (type.eql? "ApplicationOrder") || (type.eql? "ImportedOrder") ) && !is_fafs? && !artfully? && has_single_donation? end def for_organization(org) self.organization = org end @@ -74,16 +124,16 @@ def <<(products) self.items << Array.wrap(products).collect { |product| Item.for(product, @per_item_processing_charge) } end def payment - AthenaPayment.new(:transaction_id => transaction_id) + CreditCardPayment.new(:transaction_id => transaction_id) end - def record_exchange! - items.each do |item| - item.to_exchange! + def record_exchange!(exchanged_items) + items.each_with_index do |item, index| + item.to_exchange! exchanged_items[index] end end def all_items merge_and_sort_items @@ -91,26 +141,45 @@ def all_tickets all_items.select(&:ticket?) end + #TODO: Undupe these methods + def tickets + items.select(&:ticket?) + end + def all_donations all_items.select(&:donation?) end + def donations + items.select(&:donation?) + end + #End dupes + + def has_single_donation? + (donations.size == 1) && tickets.empty? + end + def settleable_donations all_donations.reject(&:modified?) end def refundable_items + return [] unless Payment.create(payment_method).refundable? items.select(&:refundable?) end def exchangeable_items items.select(&:exchangeable?) end + def returnable_items + items.select { |i| i.returnable? and i.comped? and not i.refundable? } + end + def num_tickets all_tickets.size end def has_ticket? @@ -120,15 +189,26 @@ def has_donation? items.select(&:donation?).present? end def sum_donations - all_donations.collect{|item| item.price.to_i}.sum + all_donations.collect{|item| item.total_price.to_i}.sum end + # + # Will return an array of all discount codes on all items on this order + # + def discounts_used + items.map{|i| i.discount.try(:code)}.reject(&:blank?).uniq + end + def ticket_details - pluralize(num_tickets, "ticket") + " to " + all_tickets.first.show.event.name + discount_string = "" + unless discounts_used.empty? + discount_string = ", used #{'discount'.pluralize(discounts_used.length)} " + discounts_used.join(",") + end + Ticket.to_sentence(self.tickets.map(&:product)) + discount_string end def to_comp! items.each do |item| item.to_comp! @@ -139,68 +219,101 @@ !fa_id.nil? end def donation_details if is_fafs? - o = Organization.find(organization_id) - "#{number_as_cents sum_donations} donation via Fractured Atlas for the benefit of #{o.fiscally_sponsored_project.name}" + "#{number_as_cents sum_donations} donation made through Fractured Atlas" else "#{number_as_cents sum_donations} donation" end end - - def returnable_items - items.select { |i| i.returnable? and not i.refundable? } - end def ticket_summary summary = TicketSummary.new items.select(&:ticket?).each do |item| summary << item.product end summary end def credit? - payment_method.eql? 'Credit card' + payment_method.eql? CreditCardPayment.payment_method end + + def cash? + payment_method.eql? CashPayment.payment_method + end + + def original_order + if self.parent.nil? + return self + else + return self.parent.original_order + end + end + + # + # If this order has no transaction_id, run up the parent chain until we hit one + # This is needed for exchanges that ultimately need to be refunded + # + def transaction_id + read_attribute(:transaction_id) || self.parent.try(:transaction_id) + end + def sell_tickets + all_tickets.each do |item| + item.product.sell_to(self.person, self.created_at) + end + end + def time_zone "Eastern Time (US & Canada)" end - private + def contact_email + items.try(:first).try(:show).try(:event).try(:contact_email) + end - #this used to do more. Now it only does this - def merge_and_sort_items - items + def create_donation_actions + items.select(&:donation?).collect do |item| + action = GiveAction.new + action.person = person + action.subject = self + action.organization_id = organization.id + action.details = donation_details + action.occurred_at = created_at + action.subtype = "Monetary" + action.save! + action end + end + handle_asynchronously :create_donation_actions - def create_purchase_action - unless all_tickets.empty? - action = GetAction.new - action.person = person - action.subject = self - action.organization_id = organization.id - action.details = ticket_details - action.occurred_at = created_at - action.subtype = "Purchase" + def create_purchase_action + unless all_tickets.empty? + action = purchase_action_class.new + action.person = person + action.subject = self + action.organization = organization + action.details = ticket_details + action.occurred_at = created_at - action.save! - action - end + #Weird, but Rails can't initialize these so the subtype is hardcoded in the model + action.subtype = action.subtype + action.import = self.import if self.import + action.save! + action end + end + handle_asynchronously :create_purchase_action - def create_donation_actions - items.select(&:donation?).collect do |item| - action = GiveAction.new - action.person = person - action.subject = self - action.organization_id = organization.id - action.details = donation_details - action.occurred_at = created_at - action.subtype = "Donation" - action.save! - action - end + def purchase_action_class + GetAction + end + + private + + #this used to do more. Now it only does this + def merge_and_sort_items + items end end