Class: Client

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
IsActiveModelHelper
Defined in:
app/models/client.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from IsActiveModelHelper

append_features, #initialize

Class Method Details

.find_invoiceable_clients_at(occurred_at) ⇒ Object

This is mostly used by the batch create feature. Here, we return an array of clients, which have approved, unassigned activity which occurred before the specified_date



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'app/models/client.rb', line 192

def self.find_invoiceable_clients_at(occurred_at)
  find(
    :all,
    :select     => 'DISTINCT `clients`.*',
    :joins      => 'LEFT JOIN `activities` ON `clients`.id = `activities`.client_id',
    :conditions => [
      [
        '`activities`.occurred_on <= ?',
        '`activities`.is_published = ?',
        '`activities`.invoice_id IS NULL'
      ].join(" AND "),
      occurred_at, 
      true
    ],
    :order => ["`clients`.company_name ASC","`clients`.id ASC"].join(",")
  )
end

Instance Method Details

#authorized_for?(options) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
79
80
81
82
83
84
# File 'app/models/client.rb', line 73

def authorized_for?(options)
  return true unless options.try(:[],:action)
  
  case options[:action].to_sym
    when :delete
      [Invoice, Payment, Activity].each{ |k| return false if k.count(:all, :conditions => ['client_id = ?', id] ) > 0 }

      true
    else
      true
  end
end

#balance(force_reload = false) ⇒ Object

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



33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'app/models/client.rb', line 33

def balance( force_reload = false )
  Money.new(
    (attribute_present? :balance_in_cents and !force_reload) ?
      read_attribute(:balance_in_cents).to_i :
      Client.find(
      :first, 
      :select => [
        'id',
        'company_name',
        '(
         IF(charges.charges_sum_in_cents IS NULL, 0,charges.charges_sum_in_cents) - 
         IF(deposits.payment_sum_in_cents IS NULL, 0, deposits.payment_sum_in_cents)
        ) AS sum_difference_in_cents'
      ].join(', '),
      :joins => [
        "LEFT JOIN(
          SELECT invoices.client_id, SUM(#{Invoice::ACTIVITY_TOTAL_SQL}) AS charges_sum_in_cents 
            FROM invoices 
            LEFT JOIN activities ON activities.invoice_id = invoices.id 
            GROUP BY invoices.client_id
        ) AS charges ON charges.client_id = clients.id",
        
        'LEFT JOIN (
          SELECT payments.client_id, SUM(payments.amount_in_cents) AS payment_sum_in_cents
            FROM payments 
            GROUP BY payments.client_id
        ) AS deposits ON deposits.client_id = clients.id '
      ].join(' '),
      :conditions => [ 'clients.id = ?', id]
      ).sum_difference_in_cents.to_i
  ) unless id.nil?
end

#ensure_not_referenced_on_destroyObject



66
67
68
69
70
71
# File 'app/models/client.rb', line 66

def ensure_not_referenced_on_destroy
  unless authorized_for?(:action => :delete)
    errors.add_to_base "Can't destroy a referenced employee"
    return false
  end
end

#mailing_addressObject



86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'app/models/client.rb', line 86

def mailing_address
  ret = []
  
  %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

#nameObject



19
20
21
# File 'app/models/client.rb', line 19

def name
  self.company_name
end

#recommend_invoice_assignments_for(amount) ⇒ Object

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.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/models/client.rb', line 105

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

#recommend_payment_assignments_for(amount) ⇒ Object

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.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'app/models/client.rb', line 133

def recommend_payment_assignments_for(amount)
  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

#unassigned_payments(how_many = :all, options = {}) ⇒ Object

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…



177
178
179
180
181
182
183
184
185
186
187
188
# File 'app/models/client.rb', line 177

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

#uninvoiced_activities_balance(force_reload = false) ⇒ Object



23
24
25
26
27
28
29
# File 'app/models/client.rb', line 23

def uninvoiced_activities_balance( force_reload = false )
  Money.new(
    (attribute_present? :uninvoiced_activities_balance_in_cents and !force_reload) ?
      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] ).to_i
  )
end

#unpaid_invoices(how_many = :all, options = {}) ⇒ Object

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…



160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'app/models/client.rb', line 160

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