Module: Invoicing::LedgerItem::ClassMethods
- Defined in:
- lib/invoicing/ledger_item.rb
Instance Method Summary collapse
-
#account_summaries(self_id, options = {}) ⇒ Object
Returns a summary account status for all customers or suppliers with which a particular party has dealings.
-
#account_summary(self_id, other_id = nil, options = {}) ⇒ Object
Returns a summary of the customer or supplier account between two parties identified by
self_id
(the party from whose perspective the account is seen, ‘you’) andother_id
(‘them’, your supplier/customer). -
#debit_when_sent_by_self ⇒ Object
Returns
true
if this type of ledger item should be recorded as a debit when the party viewing the account is the sender of the document, and recorded as a credit when the party viewing the account is the recipient. -
#is_credit_note ⇒ Object
Returns
true
if this type of ledger item is acredit_note
subtype, andfalse
otherwise. -
#is_invoice ⇒ Object
Returns
true
if this type of ledger item is ainvoice
subtype, andfalse
otherwise. -
#is_payment ⇒ Object
Returns
true
if this type of ledger item is apayment
subtype, andfalse
otherwise. -
#sender_recipient_name_map(*sender_recipient_ids) ⇒ Object
Takes an array of IDs like those used in
sender_id
andrecipient_id
, and returns a hash which maps each of these IDs (typecast to integer) to the:name
field of the hash returned bysender_details
orrecipient_details
for that ID.
Instance Method Details
#account_summaries(self_id, options = {}) ⇒ Object
Returns a summary account status for all customers or suppliers with which a particular party has dealings. Takes into account all closed
invoices/credit notes and all cleared
payments which have self_id
as their sender_id
or recipient_id
. Returns a hash whose keys are the other party of each account (i.e. the value of sender_id
or recipient_id
which is not self_id
, as an integer), and whose values are again hashes, of the same form as returned by account_summary
(summary
objects as documented on account_summary
):
LedgerItem.account_summaries(1)
# => { 2 => { :USD => summary, :EUR => summary },
# 3 => { :EUR => summary } }
If you want to further restrict the ledger items taken into account in this calculation (e.g. include only data from a particular quarter) you can call this method within an ActiveRecord scope:
q3_2008 = ['issue_date >= ? AND issue_date < ?', DateTime.parse('2008-07-01'), DateTime.parse('2008-10-01')]
LedgerItem.scoped(:conditions => q3_2008).account_summaries(1)
Also accepts options:
:with_status
-
List of ledger item status strings; only ledger items whose status is one of these will be taken into account. Default:
["closed", "cleared"]
.
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 |
# File 'lib/invoicing/ledger_item.rb', line 678 def account_summaries(self_id, ={}) info = ledger_item_class_info ext = Invoicing::ConnectionAdapterExt scope = scope(:find) debit_classes = select_matching_subclasses(:debit_when_sent_by_self, true, self.table_name, self.inheritance_column).map{|c| c.name} credit_classes = select_matching_subclasses(:debit_when_sent_by_self, false, self.table_name, self.inheritance_column).map{|c| c.name} debit_when_sent = merge_conditions({info.method(:sender_id) => self_id, info.method(:type) => debit_classes}) debit_when_received = merge_conditions({info.method(:recipient_id) => self_id, info.method(:type) => credit_classes}) credit_when_sent = merge_conditions({info.method(:sender_id) => self_id, info.method(:type) => credit_classes}) credit_when_received = merge_conditions({info.method(:recipient_id) => self_id, info.method(:type) => debit_classes}) cols = {} [:total_amount, :sender_id, :recipient_id, :status, :currency].each do |col| cols[col] = connection.quote_column_name(info.method(col)) end sender_is_self = merge_conditions({info.method(:sender_id) => self_id}) recipient_is_self = merge_conditions({info.method(:recipient_id) => self_id}) other_id_column = ext.conditional_function(sender_is_self, cols[:recipient_id], cols[:sender_id]) accept_status = sanitize_sql_hash_for_conditions(info.method(:status) => ([:with_status] || %w(closed cleared))) filter_conditions = "#{accept_status} AND (#{sender_is_self} OR #{recipient_is_self})" sql = "SELECT #{other_id_column} AS other_id, #{cols[:currency]} AS currency, " + "SUM(#{ext.conditional_function(debit_when_sent, cols[:total_amount], 0)}) AS sales, " + "SUM(#{ext.conditional_function(debit_when_received, cols[:total_amount], 0)}) AS purchase_payments, " + "SUM(#{ext.conditional_function(credit_when_sent, cols[:total_amount], 0)}) AS sale_receipts, " + "SUM(#{ext.conditional_function(credit_when_received, cols[:total_amount], 0)}) AS purchases " + "FROM #{(scope && scope[:from]) || quoted_table_name} " # Structure borrowed from ActiveRecord::Base.construct_finder_sql add_joins!(sql, nil, scope) add_conditions!(sql, filter_conditions, scope) sql << " GROUP BY other_id, currency" add_order!(sql, nil, scope) add_limit!(sql, {}, scope) add_lock!(sql, {}, scope) rows = connection.select_all(sql) results = {} rows.each do |row| row.symbolize_keys! other_id = row[:other_id].to_i currency = row[:currency].to_sym summary = {:balance => BigDecimal('0'), :currency => currency} {:sales => 1, :purchases => -1, :sale_receipts => -1, :purchase_payments => 1}.each_pair do |field, factor| summary[field] = BigDecimal(row[field]) summary[:balance] += BigDecimal(factor.to_s) * summary[field] end results[other_id] ||= {} results[other_id][currency] = AccountSummary.new summary end results end |
#account_summary(self_id, other_id = nil, options = {}) ⇒ Object
Returns a summary of the customer or supplier account between two parties identified by self_id
(the party from whose perspective the account is seen, ‘you’) and other_id
(‘them’, your supplier/customer). The return value is a hash with ISO 4217 currency codes as keys (as symbols), and summary objects as values. An account using only one currency will have only one entry in the hash, but more complex accounts may have several.
The summary object has the following methods:
currency => symbol # Same as the key of this hash entry
sales => BigDecimal(...) # Sum of sales (invoices sent by self_id)
purchases => BigDecimal(...) # Sum of purchases (invoices received by self_id)
sale_receipts => BigDecimal(...) # Sum of payments received from customer
purchase_payments => BigDecimal(...) # Sum of payments made to supplier
balance => BigDecimal(...) # sales - purchases - sale_receipts + purchase_payments
The :balance
fields indicate any outstanding money owed on the account: the value is positive if they owe you money, and negative if you owe them money.
In addition, acts_as_currency_value
is set on the numeric fields, so you can use its convenience methods such as summary.sales_formatted
.
If other_id
is nil
, this method aggregates the accounts of self_id
with all other parties.
Also accepts options:
:with_status
-
List of ledger item status strings; only ledger items whose status is one of these will be taken into account. Default:
["closed", "cleared"]
.
626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 |
# File 'lib/invoicing/ledger_item.rb', line 626 def account_summary(self_id, other_id=nil, ={}) info = ledger_item_class_info self_id = self_id.to_i other_id = [nil, ''].include?(other_id) ? nil : other_id.to_i if other_id.nil? result = {} # Sum over all others, grouped by currency account_summaries(self_id, ).each_pair do |other_id, hash| hash.each_pair do |currency, summary| if result[currency] result[currency] += summary else result[currency] = summary end end end result else conditions = {info.method(:sender_id) => [self_id, other_id], info.method(:recipient_id) => [self_id, other_id]} with_scope(:find => {:conditions => conditions}) do account_summaries(self_id, )[other_id] || {} end end end |
#debit_when_sent_by_self ⇒ Object
Returns true
if this type of ledger item should be recorded as a debit when the party viewing the account is the sender of the document, and recorded as a credit when the party viewing the account is the recipient. Returns false
if those roles are reversed. This method implements default behaviour for invoices, credit notes and payments (see Invoicing::LedgerItem#debit?
); if you define custom ledger item subtypes (other than invoice
, credit_note
and payment
), you should override this method accordingly in those subclasses.
573 574 575 576 577 578 579 580 |
# File 'lib/invoicing/ledger_item.rb', line 573 def debit_when_sent_by_self case ledger_item_class_info.subtype when :invoice then true when :credit_note then true when :payment then false else nil end end |
#is_credit_note ⇒ Object
Returns true
if this type of ledger item is a credit_note
subtype, and false
otherwise.
588 589 590 |
# File 'lib/invoicing/ledger_item.rb', line 588 def is_credit_note ledger_item_class_info.subtype == :credit_note end |
#is_invoice ⇒ Object
Returns true
if this type of ledger item is a invoice
subtype, and false
otherwise.
583 584 585 |
# File 'lib/invoicing/ledger_item.rb', line 583 def is_invoice ledger_item_class_info.subtype == :invoice end |
#is_payment ⇒ Object
Returns true
if this type of ledger item is a payment
subtype, and false
otherwise.
593 594 595 |
# File 'lib/invoicing/ledger_item.rb', line 593 def is_payment ledger_item_class_info.subtype == :payment end |
#sender_recipient_name_map(*sender_recipient_ids) ⇒ Object
Takes an array of IDs like those used in sender_id
and recipient_id
, and returns a hash which maps each of these IDs (typecast to integer) to the :name
field of the hash returned by sender_details
or recipient_details
for that ID. This is useful as it allows LedgerItem
to use human-readable names for people or organisations in its output, without depending on a particular implementation of the model objects used to store those entities.
LedgerItem.sender_recipient_name_map [2, 4]
=> {2 => "Fast Flowers Ltd.", 4 => "Speedy Motors"}
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
# File 'lib/invoicing/ledger_item.rb', line 748 def sender_recipient_name_map(*sender_recipient_ids) sender_recipient_ids = sender_recipient_ids.flatten.map &:to_i sender_recipient_to_ledger_item_ids = {} result_map = {} info = ledger_item_class_info # Find the most recent occurrence of each ID, first in the sender_id column, then in recipient_id [:sender_id, :recipient_id].each do |column| column = info.method(column) quoted_column = connection.quote_column_name(column) sql = "SELECT MAX(#{primary_key}) AS id, #{quoted_column} AS ref FROM #{quoted_table_name} WHERE " sql << merge_conditions({column => sender_recipient_ids}) sql << " GROUP BY #{quoted_column}" ActiveRecord::Base.connection.select_all(sql).each do |row| sender_recipient_to_ledger_item_ids[row['ref'].to_i] = row['id'].to_i end sender_recipient_ids -= sender_recipient_to_ledger_item_ids.keys end # Load all the ledger items needed to get one representative of each name find(sender_recipient_to_ledger_item_ids.values.uniq).each do |ledger_item| sender_id = info.get(ledger_item, :sender_id) recipient_id = info.get(ledger_item, :recipient_id) if sender_recipient_to_ledger_item_ids.include? sender_id details = info.get(ledger_item, :sender_details) result_map[sender_id] = details[:name] end if sender_recipient_to_ledger_item_ids.include? recipient_id details = info.get(ledger_item, :recipient_details) result_map[recipient_id] = details[:name] end end result_map end |