Class: Invoice
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- Invoice
- Includes:
- ExtensibleObjectHelper
- Defined in:
- app/models/invoice.rb
Direct Known Subclasses
Constant Summary collapse
- ACTIVITY_TOTAL_SQL =
This just ends up being useful in a couple places
'(IF(activities.cost_in_cents IS NULL, 0, activities.cost_in_cents)+IF(activities.tax_in_cents IS NULL, 0, activities.tax_in_cents))'
Class Method Summary collapse
Instance Method Summary collapse
- #amount(force_reload = false) ⇒ Object
- #amount_outstanding ⇒ Object
- #amount_paid(force_reload = false) ⇒ Object
- #authorized_for?(options) ⇒ Boolean
- #ensure_not_published_on_destroy ⇒ Object
- #ensure_not_published_on_update ⇒ Object
- #ensure_were_the_most_recent ⇒ Object
- #grand_total ⇒ Object
-
#initialize(*args) ⇒ Invoice
constructor
A new instance of Invoice.
- #invalid_if_published(collection_record = nil) ⇒ Object
- #is_most_recent_invoice? ⇒ Boolean
- #is_paid?(force_reload = false) ⇒ Boolean
- #long_name ⇒ Object
- #mark_invoice_payments ⇒ Object
- #name ⇒ Object
- #paid_on ⇒ Object
- #reattach_activities ⇒ Object
- #remove_invoice_payments ⇒ Object
- #sub_total ⇒ Object
- #taxes_total ⇒ Object
- #validate_on_update ⇒ Object
Methods included from ExtensibleObjectHelper
Constructor Details
#initialize(*args) ⇒ Invoice
34 35 36 37 38 |
# File 'app/models/invoice.rb', line 34 def initialize(*args) super(*args) end_of_last_month = Time.utc(*Time.now.to_a).last_month.end_of_month self.issued_on = end_of_last_month unless self.issued_on end |
Class Method Details
.find_with_totals(how_many = :all, options = {}) ⇒ Object
227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
# File 'app/models/invoice.rb', line 227 def self.find_with_totals( how_many = :all, = {} ) joins = [] joins << 'LEFT JOIN ('+ "SELECT invoices.id AS invoice_id, SUM(#{ACTIVITY_TOTAL_SQL}) AS total_in_cents"+ ' FROM invoices'+ ' LEFT JOIN activities ON activities.invoice_id = invoices.id'+ ' GROUP BY invoices.id'+ ') AS activities_total ON activities_total.invoice_id = invoices.id' joins << 'LEFT JOIN ('+ 'SELECT invoices.id AS invoice_id, SUM(invoice_payments.amount_in_cents) AS total_in_cents'+ ' FROM invoices'+ ' LEFT JOIN invoice_payments ON invoice_payments.invoice_id = invoices.id'+ ' GROUP BY invoices.id'+ ') AS invoices_total ON invoices_total.invoice_id = invoices.id' cast_amount = 'IF(activities_total.total_in_cents IS NULL, 0,activities_total.total_in_cents)' cast_amount_paid = 'IF(invoices_total.total_in_cents IS NULL, 0,invoices_total.total_in_cents)' Invoice.find( how_many, { :select => [ 'invoices.id', 'invoices.client_id', 'invoices.comments', 'invoices.issued_on', "#{cast_amount} AS amount_in_cents", "#{cast_amount_paid} AS amount_paid_in_cents", "#{cast_amount} - #{cast_amount_paid} AS amount_outstanding_in_cents" ].join(', '), :order => 'issued_on ASC', :joins => joins.join(' ') }.merge() ) end |
Instance Method Details
#amount(force_reload = false) ⇒ Object
132 133 134 135 136 |
# File 'app/models/invoice.rb', line 132 def amount( force_reload = false ) (attribute_present? :amount_in_cents and !force_reload) ? Money.new(read_attribute(:amount_in_cents).to_i) : process_total( :amount, ACTIVITY_TOTAL_SQL ) end |
#amount_outstanding ⇒ Object
223 224 225 |
# File 'app/models/invoice.rb', line 223 def amount_outstanding amount - amount_paid end |
#amount_paid(force_reload = false) ⇒ Object
215 216 217 218 219 220 221 |
# File 'app/models/invoice.rb', line 215 def amount_paid( force_reload = false ) Money.new( (attribute_present? :amount_paid_in_cents and !force_reload) ? read_attribute(:amount_paid_in_cents).to_i : InvoicePayment.sum( :amount_in_cents, :conditions => ['invoice_id = ?', id] ).to_i ) end |
#authorized_for?(options) ⇒ Boolean
264 265 266 267 268 269 270 271 |
# File 'app/models/invoice.rb', line 264 def () case [:action].to_s when /^(update|destroy)$/ (is_published and !is_most_recent_invoice?) ? false : true else true end end |
#ensure_not_published_on_destroy ⇒ Object
101 102 103 104 105 106 |
# File 'app/models/invoice.rb', line 101 def ensure_not_published_on_destroy if is_published and !changes.has_key? :is_published errors.add_to_base "Can't destroy a published invoice" return false end end |
#ensure_not_published_on_update ⇒ Object
108 109 110 |
# File 'app/models/invoice.rb', line 108 def ensure_not_published_on_update errors.add_to_base "Can't update a published invoice" if is_published and !changes.has_key? :is_published end |
#ensure_were_the_most_recent ⇒ Object
94 95 96 97 98 99 |
# File 'app/models/invoice.rb', line 94 def ensure_were_the_most_recent unless is_most_recent_invoice? errors.add_to_base "Can't destroy an invoice if its not the client's most recent invoice" return false end end |
#grand_total ⇒ Object
138 139 140 |
# File 'app/models/invoice.rb', line 138 def grand_total process_total :grand_total, ACTIVITY_TOTAL_SQL end |
#invalid_if_published(collection_record = nil) ⇒ Object
40 41 42 |
# File 'app/models/invoice.rb', line 40 def invalid_if_published(collection_record = nil) raise "Can't adjust an already-published invoice." if !new_record? and is_published end |
#is_most_recent_invoice? ⇒ Boolean
88 89 90 91 92 |
# File 'app/models/invoice.rb', line 88 def is_most_recent_invoice? newest_invoice = Invoice.find :first, :select => 'id', :order => 'issued_on DESC', :conditions => ['client_id = ?', client_id] (newest_invoice.nil? or newest_invoice.id == id) ? true : false end |
#is_paid?(force_reload = false) ⇒ Boolean
209 210 211 212 213 |
# File 'app/models/invoice.rb', line 209 def is_paid?( force_reload = false ) (attribute_present? :is_paid and !force_reload) ? (read_attribute(:is_paid).to_i == 1) : amount_outstanding.zero? end |
#long_name ⇒ Object
146 147 148 149 150 151 152 153 |
# File 'app/models/invoice.rb', line 146 def long_name "Invoice #%d (%s) - %s (%s)" % [ id, issued_on.strftime("%m/%d/%Y %I:%M %p"), client.company_name, ('$%.2f' % amount.to_s).gsub(/(\d)(?=\d{3}+(\.\d*)?$)/, '\1,') ] end |
#mark_invoice_payments ⇒ Object
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'app/models/invoice.rb', line 159 def mark_invoice_payments if changes.has_key? "is_published" remove_invoice_payments if is_published unallocated_payments = Payment.find_with_totals( :all, :conditions => [ 'client_id = ? AND (payments.amount_in_cents - IF(payments_total.amount_allocated_in_cents IS NULL, 0, payments_total.amount_allocated_in_cents) ) > ?', client_id, 0 ] ) current_client_balance = 0.0.to_money unallocated_payments.each { |pmnt| current_client_balance -= pmnt.amount_unallocated } invoice_balance = amount unallocated_payments.each do |unallocated_pmnt| break if invoice_balance == 0 or current_client_balance >= 0 payment_allocation = (unallocated_pmnt.amount_unallocated > invoice_balance) ? invoice_balance : unallocated_pmnt.amount_unallocated InvoicePayment.create! :invoice => self, :payment => unallocated_pmnt, :amount => payment_allocation invoice_balance -= payment_allocation current_client_balance += payment_allocation end end end end |
#name ⇒ Object
142 143 144 |
# File 'app/models/invoice.rb', line 142 def name '"%s" Invoice on %s' % [ (client) ? client.company_name : '(Unknown Client)', issued_on.strftime("%m/%d/%Y %I:%M %p") ] end |
#paid_on ⇒ Object
195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'app/models/invoice.rb', line 195 def paid_on raise StandardError unless is_paid? InvoicePayment.find( :first, :order => 'payments.paid_on DESC', :include => [:payment], :conditions => ['invoice_id = ?', id] ).payment.paid_on rescue nil end |
#reattach_activities ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'app/models/invoice.rb', line 44 def reattach_activities included_activity_types = activity_types.collect{ |a| a.label.downcase } unincluded_activity_types = ActivityType.find(:all).collect{ |a| a.label.downcase } - included_activity_types # First we NULL'ify (remove) existing attachments that no longer should be: nullify_conditions = [] nullify_parameters = [] # Conditions for occurance adjutments nullify_conditions << '(DATEDIFF(occurred_on, DATE(?)) > 0)' nullify_parameters << issued_on # For the ActivityType Adjustments: unless unincluded_activity_types.empty? nullify_conditions << '(%s)' % ( ['activity_type = ?'] * unincluded_activity_types.size).join(' OR ') nullify_parameters += unincluded_activity_types end Activity.update_all( 'invoice_id = NULL', [ ['invoice_id = ?', 'is_published = ?', ('(%s)' % nullify_conditions.join(' OR ')) ].join(' AND ') ]+ [id, true]+nullify_parameters ) unless new_record? # Now we attach the new records : update_where = [ ['invoice_id IS NULL'], ['is_published = ?', true], ['client_id = ?', client_id], ['DATEDIFF(occurred_on, DATE(?)) <= 0', issued_on], # Slightly more complicated, for the type includes: ( (included_activity_types.size > 0) ? [ '('+(['activity_type = ?'] * included_activity_types.size).join(' OR ')+')', included_activity_types ] : [ 'activity_type IS NULL' ] ) ] Activity.update_all( ['invoice_id = ?', id ], # This is what ActiveRecord actually expects... update_where.collect{|c| c[0]}.join(' AND ').to_a + update_where.reject{|c| c.length < 2 }.collect{|c| c[1]}.flatten ) end |
#remove_invoice_payments ⇒ Object
155 156 157 |
# File 'app/models/invoice.rb', line 155 def remove_invoice_payments InvoicePayment.destroy_all ['invoice_id = ?', id] end |
#sub_total ⇒ Object
128 129 130 |
# File 'app/models/invoice.rb', line 128 def sub_total process_total :sub_total, :cost_in_cents end |
#taxes_total ⇒ Object
124 125 126 |
# File 'app/models/invoice.rb', line 124 def taxes_total process_total :taxes_total, :tax_in_cents end |
#validate_on_update ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 |
# File 'app/models/invoice.rb', line 112 def validate_on_update errors.add :client, "can't be updated after creation" if changes.has_key? "client_id" errors.add_to_base( "Invoice can't be updated once published." ) if is_published and changes.reject{|k,v| k == 'is_published'}.length > 0 errors.add_to_base( "Invoice can't be unpublished, unless its the newest invoice in the client's queue." ) if changes.has_key?('is_published') and is_published_was and !is_most_recent_invoice? end |