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

[View source]

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)
[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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.

[View source]

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.

[View source]

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…

[View source]

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

[View source]

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…

[View source]

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