app/models/client.rb in brisk-bills-0.6.0 vs app/models/client.rb in brisk-bills-0.7.0

- old
+ new

@@ -21,13 +21,15 @@ end def uninvoiced_activities_balance( force_reload = false ) (attribute_present? :uninvoiced_activities_balance_in_cents and !force_reload) ? Money.new(read_attribute(:uninvoiced_activities_balance_in_cents).to_i) : - (Activity.sum( Invoice::ACTIVITY_TOTAL_SQL, :conditions => ['client_id = ? AND is_published = ? AND invoice_id IS NULL',id, true] ) or 0.0) + (Activity.sum( Invoice::ACTIVITY_TOTAL_SQL, :conditions => ['client_id = ? AND is_published = ? AND invoice_id IS NULL',id, true] ) or 0) end + # THis is the client's outstanding balance. This value is calculated based off the total invoices amount - total payments amount. And is + # Not determined based on invoice/payment assignments def balance( force_reload = false ) Money.new( (attribute_present? :balance_in_cents and !force_reload) ? read_attribute(:balance_in_cents).to_i : Client.find( @@ -75,19 +77,107 @@ end def mailing_address ret = [] - %w(name address1 address2).each do |f| - val = send(f.to_sym) and ( ret <<val if val.length ) + %w( name address1 address2 ).each do |f| + val = send(f.to_sym) and ( ret << val if val.length ) end ret << '%s%s %s' % [ (city.nil? or city.length == 0) ? '' : "#{city}, ", state, zip ] ret + end + + # Returns an array of unsaved InvoicePayment objects, with unset payment_ids, and 'recommended' amounts. + # If the provided amount exactly equals an outstanding invoice's amount, we return a InvoicePayment for the oldest such matching invoice. + # Otherwise, we start applying the amount to invoices in ascending order by issued_date. + def recommend_invoice_assignments_for(amount) + amount = amount.to_money + + invs = unpaid_invoices( + :all, + # Using this order forces the closest-amount match to be above anything else, followed by date sorting + :order => '(amount_outstanding_in_cents = %d) DESC, issued_on ASC, created_at ASC' % amount.cents + ) + + unassigned_outstanding = invs.inject(Money.new(0)){|total, inv| total + inv.amount_outstanding} + + invs.collect{ |inv| + if amount > 0 and unassigned_outstanding > 0 + assignment = (amount >= inv.amount_outstanding) ? + inv.amount_outstanding : + amount + + unassigned_outstanding -= assignment + amount -= assignment + + InvoicePayment.new :invoice => inv, :amount => assignment if assignment > 0 + end + }.compact + end + + # Returns an array of unsaved InvoicePayment objects, with unset invoice_ids, and 'recommended' amounts. + # If the provided amount exactly equals an payment's unalloated amount, we return a InvoicePayment for the oldest such matching payment. + # Otherwise, we start applying the amount to payments in ascending order by issued_date. + def recommend_payment_assignments_for(amount, verbose_inclusion = false) + amount = amount.to_money + + pymnts = unassigned_payments( + :all, + # Using this order forces the closest-amount match to be above anything else, followed by date sorting + :order => '(amount_unallocated_in_cents = %d) DESC, paid_on ASC, created_at ASC' % amount.cents + ) + + current_client_balance = pymnts.inject(Money.new(0)){|total, pmnt| total - pmnt.amount_unallocated} + + pymnts.collect{ |unallocated_pmnt| + if amount > 0 or current_client_balance < 0 + assignment = (unallocated_pmnt.amount_unallocated > amount) ? + amount : + unallocated_pmnt.amount_unallocated + + current_client_balance += assignment + amount -= assignment + + InvoicePayment.new :payment => unallocated_pmnt, :amount => assignment if assignment > 0 + end + }.compact + end + + # Returns all the client's invoices for which the allocated payments is less than the invoice amount. Perhaps this should be a has_many, + # But since we're using the find_with_totals that would get complicated... + def unpaid_invoices( how_many = :all, options = {} ) + Invoice.find_with_totals( + how_many, + {:conditions => [ + [ + 'client_id = ?', + 'is_published = ?', + 'IF(activities_total.total_in_cents IS NULL, 0,activities_total.total_in_cents) - '+ + 'IF(invoices_total.total_in_cents IS NULL, 0,invoices_total.total_in_cents) > ?' + ].join(' AND '), + id, true, 0 + ]}.merge(options.reject{|k,v| k == :conditions}) + ) + end + + # Returns all the client's payments for which the invoice allocation is less than the payment amount. Perhaps this should be a has_many, + # But since we're using the find_with_totals that would get complicated... + def unassigned_payments( how_many = :all, options = {} ) + Payment.find_with_totals( + how_many, + {:conditions => [ + [ + 'client_id = ?', + '(payments.amount_in_cents - IF(payments_total.amount_allocated_in_cents IS NULL, 0, payments_total.amount_allocated_in_cents) ) > ?' + ].join(' AND '), + id, 0 + ]}.merge(options.reject{|k,v| k == :conditions}) + ) end end \ No newline at end of file