app/models/statement.rb in artfully_ose-1.0.0.rc4 vs app/models/statement.rb in artfully_ose-1.1.0.rc1
- old
+ new
@@ -1,25 +1,192 @@
+###
+#
+# Statement uses items from a show to calculate things like gross, net, and
+# cut the data up into various sections. The reason we don't use tickets is:
+#
+# * Item is the table of record for revenue
+# * a Ticket can have many items (refunds, exchanges, etc...) It's awkward (and db intensive)
+# to get the sold_item for a ticket
+#
+# The downside is that this approach requires a bit of hoop jumping in Row (see if, elsif, else block)
+# which is indeed ugly.
+#
+###
class Statement
+ include Ext::Due
+
attr_accessor :datetime,
:tickets_sold,
:tickets_comped,
:potential_revenue,
:gross_revenue,
:processing,
- :net_revenue
+ :net_revenue,
+ :cc_net,
+ :settled,
+ :payment_method_rows,
+ :order_location_rows,
+ :ticket_type_rows,
+ :discount_rows
- def self.for_show(show, organization)
- if show.nil? || organization.nil?
+ def self.for_show(show, imported=false)
+ if show.nil?
return new
end
new.tap do |statement|
+
+ # Some of this overlaps with Ticket::Glance. Consider refactoring to combine the two.
statement.datetime = show.datetime_local_to_event
statement.tickets_sold = show.tickets.select{|t| t.sold?}.size
statement.tickets_comped = show.tickets.select{|t| t.comped?}.size
statement.potential_revenue = show.tickets.inject(0) { |total_price, ticket| total_price += ticket.price }
statement.gross_revenue = show.items.inject(0) { |gross, item| gross += item.price }
statement.net_revenue = show.items.inject(0) { |net, item| net += item.net }
statement.processing = statement.gross_revenue - statement.net_revenue
+
+ statement.calculate_due(show, imported)
+
+ #
+ # PAYMENT METHOD
+ #
+ payment_method_hash = show.items.group_by { |item| item.order.payment_method }
+ statement.payment_method_rows = {}
+
+ # Initialize with the three common payment types
+ statement.payment_method_rows[::CreditCardPayment.payment_method.downcase] = PaymentTypeRow.new(::CreditCardPayment.payment_method)
+ statement.payment_method_rows[::CashPayment.payment_method.downcase] = PaymentTypeRow.new(::CashPayment.payment_method)
+ statement.payment_method_rows[::CompPayment.payment_method.downcase] = PaymentTypeRow.new(::CompPayment.payment_method)
+
+ payment_method_hash.each do |payment_method, items|
+ payment_method = (payment_method.try(:downcase) || "")
+ row = statement.payment_method_rows[payment_method] || PaymentTypeRow.new(payment_method)
+ items.each {|item| row << item}
+ statement.payment_method_rows[payment_method] = row
+ end
+
+ #
+ # ORDER LOCATION
+ #
+ order_location_hash = show.items.group_by do |item|
+ item.order.original_order.location
+ end
+
+ statement.order_location_rows = {}
+ statement.order_location_rows[::WebOrder.location] = OrderLocationRow.new(::WebOrder.location)
+ statement.order_location_rows[BoxOffice::Order.location] = OrderLocationRow.new(BoxOffice::Order.location)
+
+ order_location_hash.each do |order_location, items|
+ row = statement.order_location_rows[order_location] || OrderLocationRow.new(order_location)
+ items.each {|item| row << item}
+ statement.order_location_rows[order_location] = row
+ end
+
+ statement.build_discount_rows(show.items)
+ statement.build_ticket_type_rows(show, show.items)
+ end
+ end
+
+ #
+ # TODO: These are super-related to the procs in class Slices. Get these two on the same page and DRY it up
+ #
+ def build_ticket_type_rows(show, items)
+ self.ticket_type_rows = {}
+
+ show.chart.sections.each do |section|
+ self.ticket_type_rows[section.name] = TicketTypeRow.new(section.name)
+ end
+
+ items.each do |item|
+ row = self.ticket_type_rows[item.product.section.name] || TicketTypeRow.new(item.product.section.name)
+ row << item
+ self.ticket_type_rows[item.product.section.name] = row
+ end
+ end
+
+ def build_discount_rows(items)
+ self.discount_rows = {}
+ items.each do |item|
+ unless item.discount.nil?
+ row = self.discount_rows[item.discount.code] || DiscountRow.new(item.discount.code, item.discount.to_s)
+ row << item
+ row.discount += (item.original_price - item.price)
+ self.discount_rows[item.discount.code] = row
+ end
+ end
+ end
+
+ module Row
+ attr_accessor :tickets,
+ :gross,
+ :processing,
+ :net
+
+ def<<(item)
+ if item.refund?
+ self.tickets = self.tickets - 1
+ elsif item.exchanged? || item.return?
+ #Noop
+ else
+ self.tickets = self.tickets + 1
+ end
+
+ self.gross = self.gross + item.price
+ self.processing = self.processing + (item.realized_price - item.net)
+ self.net = self.net + item.net
+ end
+ end
+
+ class TicketTypeRow
+ include Row
+ attr_accessor :ticket_type
+
+ def initialize(ticket_type)
+ self.ticket_type = ticket_type
+ self.tickets = 0
+ self.gross = 0
+ self.processing = 0
+ self.net = 0
+ end
+ end
+
+ class DiscountRow
+ include Row
+ attr_accessor :discount_code, :type, :discount
+
+ def initialize(discount_code, type)
+ self.discount_code = discount_code
+ self.type = type
+ self.tickets = 0
+ self.gross = 0
+ self.processing = 0
+ self.net = 0
+ self.discount = 0
+ end
+ end
+
+ class OrderLocationRow
+ include Row
+ attr_accessor :order_location
+
+ def initialize(order_location)
+ self.order_location = order_location
+ self.tickets = 0
+ self.gross = 0
+ self.processing = 0
+ self.net = 0
+ end
+ end
+
+ class PaymentTypeRow
+ include Row
+ attr_accessor :payment_method
+
+ def initialize(payment_method)
+ self.payment_method = payment_method
+ self.tickets = 0
+ self.gross = 0
+ self.processing = 0
+ self.net = 0
end
end
end
\ No newline at end of file