app/models/invoice.rb in erp_invoicing-4.0.0 vs app/models/invoice.rb in erp_invoicing-4.1.0
- old
+ new
@@ -20,124 +20,360 @@
class Invoice < ActiveRecord::Base
attr_protected :created_at, :updated_at
acts_as_document
-
- belongs_to :billing_account
- belongs_to :invoice_type
- belongs_to :invoice_payment_strategy_type
- belongs_to :balance_record, :class_name => "Money", :foreign_key => 'balance_id', :dependent => :destroy
- belongs_to :calculate_balance_strategy_type
- has_many :invoice_payment_term_sets, :dependent => :destroy
- has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
+ can_be_generated
+ has_tracked_status
+ tracks_created_by_updated_by
+
+ belongs_to :billing_account
+ belongs_to :invoice_type
+ belongs_to :invoice_payment_strategy_type
+ belongs_to :balance_record, :class_name => "Money", :foreign_key => 'balance_id', :dependent => :destroy
+ belongs_to :calculate_balance_strategy_type
+ has_many :invoice_payment_term_sets, :dependent => :destroy
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
def successful
- all.select{|item| item.financial_txn.has_captured_payment?}
+ all.select { |item| item.financial_txn.has_captured_payment? }
end
+
def pending
- all.select{|item| item.is_pending?}
+ all.select { |item| item.is_pending? }
end
end
- has_many :invoice_items, :dependent => :destroy do
+ has_many :invoice_items, :dependent => :destroy do
def by_date
order('created_at')
end
def unpaid
- select{|item| item.balance > 0 }
+ select { |item| item.balance > 0 }
end
end
- has_many :invoice_party_roles, :dependent => :destroy
- has_many :parties, :through => :invoice_party_roles
-
+ has_many :invoice_party_roles, :dependent => :destroy
+ has_many :parties, :through => :invoice_party_roles
+
alias :items :invoice_items
alias :type :invoice_type
alias :party_roles :invoice_party_roles
alias :payment_strategy :invoice_payment_strategy_type
- def has_payments?(status)
+ class << self
+
+ # Filter records
+ #
+ # @param filters [Hash] a hash of filters to be applied,
+ # @param statement [ActiveRecord::Relation] the query being built
+ # @return [ActiveRecord::Relation] the query being built
+ def apply_filters(filters, statement=nil)
+ if statement.nil?
+ statement = self
+ end
+
+ unless filters[:status].blank?
+ if filters[:status] == 'open'
+ statement = statement.open
+ end
+
+ if filters[:status] == 'closed'
+ statement = statement.closed
+ end
+ end
+
+ statement
+ end
+
+ # generate an invoice from a order_txn
+ # options include
+ # message - Message to display on Invoice
+ # invoice_date - Date of Invoice
+ # due_date - Due date of Invoice
+ # taxation - context for taxation
+ # {
+ # is_online_sale: true,
+ # origin_address: {
+ # state: "FL"
+ # },
+ # destination_address: {
+ # state: "FL"
+ # }
+ # }
+ #
+ def generate_from_order(order_txn, options={})
+ ActiveRecord::Base.connection.transaction do
+ invoice = Invoice.new
+
+ # create invoice
+ invoice.invoice_number = next_invoice_number
+ invoice.description = "Invoice for #{order_txn.order_number.to_s}"
+ invoice.message = options[:message]
+ invoice.invoice_date = options[:invoice_date]
+ invoice.due_date = options[:due_date]
+
+ invoice.save
+
+ invoice.current_status = 'invoice_statuses_open'
+
+ # add customer relationship
+ party = order_txn.find_party_by_role('order_roles_customer')
+ invoice.add_party_with_role_type(party, RoleType.customer)
+
+ dba_organization = options[:dba_organization] || order_txn.find_party_by_role(RoleType.iid('dba_org'))
+ invoice.add_party_with_role_type(dba_organization, RoleType.dba_org)
+
+ order_txn.order_line_items.each do |line_item|
+ invoice_item = InvoiceItem.new
+
+ invoice_item.invoice = invoice
+
+ charged_item = line_item.product_instance || line_item.product_offer ||line_item.product_type
+
+ invoice_item.item_description = charged_item.description
+ invoice_item.quantity = line_item.quantity
+ invoice_item.unit_price = line_item.sold_price
+ invoice_item.amount = (line_item.quantity * line_item.sold_price)
+ invoice_item.taxed = line_item.taxed?
+ invoice_item.biz_txn_acct_root = charged_item.try(:biz_txn_acct_root)
+ invoice_item.add_invoiced_record(charged_item)
+
+ invoice.invoice_items << invoice_item
+
+ invoice_item.save
+ end
+
+ # handles everything but shipping charge lines, multiple invoice items created from all iterations
+ order_txn.all_charge_lines.select { |charge_line| charge_line.charge_type && charge_line.charge_type.internal_identifier != 'shipping' }.each do |charge_line|
+ invoice_item = InvoiceItem.new
+
+ invoice_item.invoice = invoice
+ charged_item = charge_line.charged_item
+ invoice_item.item_description = charge_line.description
+
+ # set data based on charged item either a OrderTxn or OrderLineItem
+ if charged_item.is_a?(OrderLineItem)
+ invoice_item.quantity = charged_item.quantity
+ invoice_item.unit_price = charged_item.sold_price
+ invoice_item.amount = charged_item.sold_amount
+ invoice_item.add_invoiced_record(charged_item.line_item_record)
+ invoice_item.taxed = charged_item.taxed?
+ elsif charged_item.is_a?(OrderTxn)
+ invoice_item.quantity = 1
+ invoice_item.unit_price = charge_line.money.amount
+ invoice_item.amount = charge_line.money.amount
+ invoice_item.add_invoiced_record(charge_line)
+ end
+
+ invoice.invoice_items << invoice_item
+
+ invoice_item.save!
+ end
+
+ # handles shipping charge lines, one invoice item created from all iterations
+ shipping_charges = order_txn.all_charge_lines.select { |charge_line| charge_line.charge_type && charge_line.charge_type.internal_identifier == 'shipping' }
+ if shipping_charges.length > 0
+ shipping_invoice_item = InvoiceItem.new
+ shipping_charges.each do |charge_line|
+ shipping_invoice_item.item_description = charge_line.description
+ shipping_invoice_item.invoice = invoice
+ shipping_invoice_item.quantity = 1
+ shipping_invoice_item.amount = shipping_invoice_item.unit_price.nil? ? charge_line.money.amount : shipping_invoice_item.unit_price + charge_line.money.amount
+ shipping_invoice_item.unit_price = shipping_invoice_item.unit_price.nil? ? charge_line.money.amount : shipping_invoice_item.unit_price + charge_line.money.amount
+ shipping_invoice_item.taxed = charge_line.taxed?
+ shipping_invoice_item.add_invoiced_record(charge_line)
+
+ invoice.invoice_items << shipping_invoice_item
+ end
+ shipping_invoice_item.save
+ end
+
+ invoice.generated_by = order_txn
+
+ # calculate taxes
+ invoice.calculate_tax(options[:taxation])
+
+ invoice
+ end
+ end
+
+ def next_invoice_number
+ max_id = maximum('id')
+
+ current_invoice = where(Invoice.arel_table[:invoice_number].matches("%#{max_id}%")).first
+
+ if current_invoice
+ while current_invoice
+ max_id = max_id + 1
+ current_invoice = where(Invoice.arel_table[:invoice_number].matches("%#{max_id}%")).first
+ end
+ else
+ if max_id
+ max_id = max_id + 1
+ else
+ max_id = 1
+ end
+ end
+
+ "Inv-#{max_id}"
+ end
+
+ def open
+ Invoice.with_current_status(['invoice_statuses_open'])
+ end
+
+ def closed
+ Invoice.with_current_status(['invoice_statuses_closed'])
+ end
+
+ def hold
+ Invoice.with_current_status(['invoice_statuses_hold'])
+ end
+
+ def sent
+ Invoice.with_current_status(['invoice_statuses_sent'])
+ end
+ end
+
+ def has_invoice_items?
+ !self.items.empty?
+ end
+
+ def has_payments?(status=:all)
selected_payment_applications = self.get_payment_applications(status)
+
!(selected_payment_applications.nil? or selected_payment_applications.empty?)
end
def get_payment_applications(status=:all)
selected_payment_applications = case status.to_sym
- when :pending
- self.payment_applications.pending
- when :successful
- self.payment_applications.successful
- when :all
- self.payment_applications
- end
+ when :pending
+ self.payment_applications.pending
+ when :successful
+ self.payment_applications.successful
+ when :all
+ self.payment_applications
+ end
unless self.items.empty?
- selected_payment_applications = (selected_payment_applications | self.items.collect{|item| item.get_payment_applications(status)}).flatten! unless (self.items.collect{|item| item.get_payment_applications(status)}.empty?)
+ unless self.items.collect { |item| item.get_payment_applications(status) }.empty?
+ selected_payment_applications = (selected_payment_applications | self.items.collect { |item| item.get_payment_applications(status) }).flatten!
+ end
end
-
+
selected_payment_applications
end
- def calculate_balance
- unless self.calculate_balance_strategy_type.nil?
- case self.calculate_balance_strategy_type.internal_identifier
- when 'invoice_items_and_payments'
- (self.items.all.sum(&:total_amount) - self.total_payments)
- when 'payable_balances_and_payments'
- (self.payable_balances.all.sum(&:balance).amount - self.total_payments)
- when 'payments'
- (self.balance - self.total_payments)
- else
- self.balance
+ def sub_total
+ if items.empty?
+ if self.balance_record
+ self.balance_record.amount
+ else
+ 0
end
else
- self.balance
+ self.items.all.sum(&:sub_total).round(2)
end
end
+ def total_amount
+ if items.empty?
+ if self.balance_record
+ self.balance_record.amount
+ else
+ 0
+ end
+ else
+ self.items.all.sum(&:total_amount).round(2)
+ end
+ end
+
def balance
- self.balance_record.amount
+ if items.empty?
+ if self.balance_record
+ self.balance_record.amount
+ else
+ 0
+ end
+ else
+ self.items.all.sum(&:total_amount).round(2)
+ end
end
+ alias payment_due balance
+
def balance=(amount, currency=Currency.usd)
if self.balance_record
self.balance_record.amount = amount
else
self.balance_record = Money.create(:amount => amount, :currency => currency)
end
self.balance_record.save
end
- def payment_due
- self.items.all.sum(&:total_amount)
+ def total_payments
+ self.get_payment_applications(:successful).sum { |item| item.money.amount }
end
- def total_payments
- self.get_payment_applications(:successful).sum{|item| item.money.amount}
+ # calculates tax for each line item and save to sales_tax
+ def calculate_tax(ctx={})
+ tax = 0
+
+ self.invoice_items.each do |line_item|
+ tax += line_item.calculate_tax(ctx)
+ end
+
+ self.sales_tax = tax
+ self.save
+
+ tax
end
+ def calculate_balance
+ unless self.calculate_balance_strategy_type.nil?
+ case self.calculate_balance_strategy_type.internal_identifier
+ when 'invoice_items_and_payments'
+ (self.items.all.sum(&:total_amount) - self.total_payments).round(2)
+ when 'payable_balances_and_payments'
+ (self.payable_balances.all.sum(&:balance).amount - self.total_payments).round(2)
+ when 'payments'
+ (self.balance - self.total_payments).round(2)
+ else
+ self.balance
+ end
+ else
+ unless self.balance.nil?
+ if has_invoice_items?
+ (self.balance - self.total_payments).round(2)
+ else
+ self.balance.round(2)
+ end
+ end
+ end
+ end
+
def transactions
transactions = []
self.items.each do |item|
transactions << {
- :date => item.created_at,
- :description => item.item_description,
- :quantity => item.quantity,
- :amount => item.amount
+ :date => item.created_at,
+ :description => item.item_description,
+ :quantity => item.quantity,
+ :amount => item.amount
}
end
self.get_payment_applications(:successful).each do |item|
transactions << {
- :date => item.financial_txn.payments.last.created_at,
- :description => item.financial_txn.description,
- :quantity => 1,
- :amount => (0 - item.financial_txn.money.amount)
+ :date => item.financial_txn.payments.last.created_at,
+ :description => item.financial_txn.description,
+ :quantity => 1,
+ :amount => (0 - item.financial_txn.money.amount)
}
end
- transactions.sort_by{|item| [item[:date]]}
+ transactions.sort_by { |item| [item[:date]] }
end
def add_party_with_role_type(party, role_type)
self.invoice_party_roles << InvoicePartyRole.create(:party => party, :role_type => convert_role_type(role_type))
self.save
@@ -145,15 +381,31 @@
def find_parties_by_role_type(role_type)
self.invoice_party_roles.where('role_type_id = ?', convert_role_type(role_type).id).all.collect(&:party)
end
+ def find_party_by_role_type(role_type)
+ parties = find_parties_by_role_type(role_type)
+
+ unless parties.empty?
+ parties.first
+ end
+ end
+
+ def dba_organization
+ find_parties_by_role_type('dba_org').first
+ end
+
+ def to_data_hash
+ to_hash(only: [:id, :created_at, :updated_at, :description, :invoice_number, :invoice_date, :due_date])
+ end
+
private
def convert_role_type(role_type)
role_type = RoleType.iid(role_type) if role_type.is_a? String
raise "Role type does not exist" if role_type.nil?
role_type
end
-
+
end