erp_invoicing 4.0.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7e365f81c6acd92a870c00bb151777b8933d7958
4
- data.tar.gz: 88c2affeaafa500990ecf8860884795884ee8e5c
3
+ metadata.gz: a500054d5b3b67fbb83faf39fd66c8fd9a397206
4
+ data.tar.gz: d40fe04522ecad5ee7a39d5d7b446e09a43570e8
5
5
  SHA512:
6
- metadata.gz: d2fcce87ce55c3727b525fc20ebbdff65b0457d6b6f38912f5fa70203862c832ce372b0373342915d22e0349875716bf715fdda56ec36e0fb5e8a5d2aa0fa160
7
- data.tar.gz: a8e691f4e4c3a213b89da8048fdc68dd97ba9043670fe917dc5c27e751c9af69d79d98bae6be5714645b403bc31e1bb8efefc349ecde2562838da47fda18b3ad
6
+ metadata.gz: 7fab6ace54f591e9655f1885e1bf3ea7d02490a40dfddf6d38dae03c7fd45e7658af79c30759ca389ec05343c54c4f8ba00251bd3bfa31789eb5bc6dd4676041
7
+ data.tar.gz: 8f2e5b22081f3aa2eaf343317f14e9bb4c518b3787d9acc3c4a0f693fbe407dbcc2cfc950ea799b63f1dc5cc3faff1eb5da87b66cb5b6574548474c5528b885c
@@ -0,0 +1,26 @@
1
+ module Api
2
+ module V1
3
+ class InvoicesController < BaseController
4
+
5
+ def index
6
+ query_filter = params[:query_filter].blank? ? {} : JSON.parse(params[:query_filter]).symbolize_keys
7
+
8
+ invoices = Invoice.apply_filters(query_filter)
9
+
10
+ # scope by dba organization
11
+ invoices = invoices.joins("inner join invoice_party_roles as invoice_party_reln on
12
+ (invoice_party_reln.invoice_id = invoices.id
13
+ and
14
+ invoice_party_reln.party_id in (#{current_user.party.dba_organization.id})
15
+ and
16
+ invoice_party_reln.role_type_id = #{RoleType.iid('dba_org').id}
17
+ )")
18
+
19
+ total_count = invoices.count
20
+
21
+ render json: {total_count: total_count, invoices: invoices.collect{|invoice| invoice.to_data_hash} }
22
+ end
23
+
24
+ end # InvoicesController
25
+ end # V1
26
+ end # Api
@@ -2,6 +2,7 @@ class BillingAccount < ActiveRecord::Base
2
2
  attr_protected :created_at, :updated_at
3
3
 
4
4
  acts_as_financial_txn_account
5
+ has_payment_applications
5
6
 
6
7
  belongs_to :calculate_balance_strategy_type
7
8
  has_many :invoices, :dependent => :destroy do
@@ -13,14 +14,7 @@ class BillingAccount < ActiveRecord::Base
13
14
  all.sum(&:balance)
14
15
  end
15
16
  end
16
- has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
17
- def successful
18
- all.select{|item| item.financial_txn.has_captured_payment?}
19
- end
20
- def pending
21
- all.select{|item| item.is_pending?}
22
- end
23
- end
17
+
24
18
  has_one :recurring_payment, :dependent => :destroy
25
19
 
26
20
  def self.find_by_account_number(account_number)
@@ -32,11 +26,6 @@ class BillingAccount < ActiveRecord::Base
32
26
  !self.recurring_payment.nil? and self.recurring_payment.enabled
33
27
  end
34
28
 
35
- def has_payments?(status)
36
- selected_payment_applications = self.get_payment_applications(status)
37
- !(selected_payment_applications.nil? or selected_payment_applications.empty?)
38
- end
39
-
40
29
  def all_documents
41
30
  (self.invoices.collect(&:document) | self.documents).flatten
42
31
  end
@@ -83,14 +72,6 @@ class BillingAccount < ActiveRecord::Base
83
72
  outstanding_balance_amt == 0 ? 0 : outstanding_balance_amt.round(2)
84
73
  end
85
74
 
86
- def total_pending_payments
87
- self.payment_applications.pending.sum{|item| item.money.amount}
88
- end
89
-
90
- def total_payments
91
- self.payment_applications.successful.sum{|item| item.money.amount}
92
- end
93
-
94
75
  #payment due is determined by last invoice
95
76
  def payment_due
96
77
  if !self.calculate_balance_strategy_type.nil? and self.calculate_balance_strategy_type.iid == 'invoices_and_payments' and !self.invoices.empty?
@@ -0,0 +1,5 @@
1
+ BizTxnAcctRoot.class_eval do
2
+
3
+ has_many :invoice_items
4
+
5
+ end
@@ -0,0 +1,5 @@
1
+ ChargeLine.class_eval do
2
+
3
+ has_payment_applications
4
+
5
+ end
@@ -0,0 +1,15 @@
1
+ OrderTxn.class_eval do
2
+
3
+ def has_generated_invoice?
4
+ (Invoice.items_generated_by(self).count != 0)
5
+ end
6
+
7
+ def generated_invoice
8
+ Invoice.items_generated_by(self).first
9
+ end
10
+
11
+ def has_payments?(status=:all)
12
+ (has_generated_invoice? && Invoice.items_generated_by(self).first.has_payments?(status))
13
+ end
14
+
15
+ end
@@ -22,81 +22,284 @@ class Invoice < ActiveRecord::Base
22
22
  attr_protected :created_at, :updated_at
23
23
 
24
24
  acts_as_document
25
-
26
- belongs_to :billing_account
27
- belongs_to :invoice_type
28
- belongs_to :invoice_payment_strategy_type
29
- belongs_to :balance_record, :class_name => "Money", :foreign_key => 'balance_id', :dependent => :destroy
30
- belongs_to :calculate_balance_strategy_type
31
- has_many :invoice_payment_term_sets, :dependent => :destroy
32
- has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
25
+ can_be_generated
26
+ has_tracked_status
27
+ tracks_created_by_updated_by
28
+
29
+ belongs_to :billing_account
30
+ belongs_to :invoice_type
31
+ belongs_to :invoice_payment_strategy_type
32
+ belongs_to :balance_record, :class_name => "Money", :foreign_key => 'balance_id', :dependent => :destroy
33
+ belongs_to :calculate_balance_strategy_type
34
+ has_many :invoice_payment_term_sets, :dependent => :destroy
35
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
33
36
  def successful
34
- all.select{|item| item.financial_txn.has_captured_payment?}
37
+ all.select { |item| item.financial_txn.has_captured_payment? }
35
38
  end
39
+
36
40
  def pending
37
- all.select{|item| item.is_pending?}
41
+ all.select { |item| item.is_pending? }
38
42
  end
39
43
  end
40
- has_many :invoice_items, :dependent => :destroy do
44
+ has_many :invoice_items, :dependent => :destroy do
41
45
  def by_date
42
46
  order('created_at')
43
47
  end
44
48
 
45
49
  def unpaid
46
- select{|item| item.balance > 0 }
50
+ select { |item| item.balance > 0 }
47
51
  end
48
52
  end
49
- has_many :invoice_party_roles, :dependent => :destroy
50
- has_many :parties, :through => :invoice_party_roles
51
-
53
+ has_many :invoice_party_roles, :dependent => :destroy
54
+ has_many :parties, :through => :invoice_party_roles
55
+
52
56
  alias :items :invoice_items
53
57
  alias :type :invoice_type
54
58
  alias :party_roles :invoice_party_roles
55
59
  alias :payment_strategy :invoice_payment_strategy_type
56
60
 
57
- def has_payments?(status)
61
+ class << self
62
+
63
+ # Filter records
64
+ #
65
+ # @param filters [Hash] a hash of filters to be applied,
66
+ # @param statement [ActiveRecord::Relation] the query being built
67
+ # @return [ActiveRecord::Relation] the query being built
68
+ def apply_filters(filters, statement=nil)
69
+ if statement.nil?
70
+ statement = self
71
+ end
72
+
73
+ unless filters[:status].blank?
74
+ if filters[:status] == 'open'
75
+ statement = statement.open
76
+ end
77
+
78
+ if filters[:status] == 'closed'
79
+ statement = statement.closed
80
+ end
81
+ end
82
+
83
+ statement
84
+ end
85
+
86
+ # generate an invoice from a order_txn
87
+ # options include
88
+ # message - Message to display on Invoice
89
+ # invoice_date - Date of Invoice
90
+ # due_date - Due date of Invoice
91
+ # taxation - context for taxation
92
+ # {
93
+ # is_online_sale: true,
94
+ # origin_address: {
95
+ # state: "FL"
96
+ # },
97
+ # destination_address: {
98
+ # state: "FL"
99
+ # }
100
+ # }
101
+ #
102
+ def generate_from_order(order_txn, options={})
103
+ ActiveRecord::Base.connection.transaction do
104
+ invoice = Invoice.new
105
+
106
+ # create invoice
107
+ invoice.invoice_number = next_invoice_number
108
+ invoice.description = "Invoice for #{order_txn.order_number.to_s}"
109
+ invoice.message = options[:message]
110
+ invoice.invoice_date = options[:invoice_date]
111
+ invoice.due_date = options[:due_date]
112
+
113
+ invoice.save
114
+
115
+ invoice.current_status = 'invoice_statuses_open'
116
+
117
+ # add customer relationship
118
+ party = order_txn.find_party_by_role('order_roles_customer')
119
+ invoice.add_party_with_role_type(party, RoleType.customer)
120
+
121
+ dba_organization = options[:dba_organization] || order_txn.find_party_by_role(RoleType.iid('dba_org'))
122
+ invoice.add_party_with_role_type(dba_organization, RoleType.dba_org)
123
+
124
+ order_txn.order_line_items.each do |line_item|
125
+ invoice_item = InvoiceItem.new
126
+
127
+ invoice_item.invoice = invoice
128
+
129
+ charged_item = line_item.product_instance || line_item.product_offer ||line_item.product_type
130
+
131
+ invoice_item.item_description = charged_item.description
132
+ invoice_item.quantity = line_item.quantity
133
+ invoice_item.unit_price = line_item.sold_price
134
+ invoice_item.amount = (line_item.quantity * line_item.sold_price)
135
+ invoice_item.taxed = line_item.taxed?
136
+ invoice_item.biz_txn_acct_root = charged_item.try(:biz_txn_acct_root)
137
+ invoice_item.add_invoiced_record(charged_item)
138
+
139
+ invoice.invoice_items << invoice_item
140
+
141
+ invoice_item.save
142
+ end
143
+
144
+ # handles everything but shipping charge lines, multiple invoice items created from all iterations
145
+ order_txn.all_charge_lines.select { |charge_line| charge_line.charge_type && charge_line.charge_type.internal_identifier != 'shipping' }.each do |charge_line|
146
+ invoice_item = InvoiceItem.new
147
+
148
+ invoice_item.invoice = invoice
149
+ charged_item = charge_line.charged_item
150
+ invoice_item.item_description = charge_line.description
151
+
152
+ # set data based on charged item either a OrderTxn or OrderLineItem
153
+ if charged_item.is_a?(OrderLineItem)
154
+ invoice_item.quantity = charged_item.quantity
155
+ invoice_item.unit_price = charged_item.sold_price
156
+ invoice_item.amount = charged_item.sold_amount
157
+ invoice_item.add_invoiced_record(charged_item.line_item_record)
158
+ invoice_item.taxed = charged_item.taxed?
159
+ elsif charged_item.is_a?(OrderTxn)
160
+ invoice_item.quantity = 1
161
+ invoice_item.unit_price = charge_line.money.amount
162
+ invoice_item.amount = charge_line.money.amount
163
+ invoice_item.add_invoiced_record(charge_line)
164
+ end
165
+
166
+ invoice.invoice_items << invoice_item
167
+
168
+ invoice_item.save!
169
+ end
170
+
171
+ # handles shipping charge lines, one invoice item created from all iterations
172
+ shipping_charges = order_txn.all_charge_lines.select { |charge_line| charge_line.charge_type && charge_line.charge_type.internal_identifier == 'shipping' }
173
+ if shipping_charges.length > 0
174
+ shipping_invoice_item = InvoiceItem.new
175
+ shipping_charges.each do |charge_line|
176
+ shipping_invoice_item.item_description = charge_line.description
177
+ shipping_invoice_item.invoice = invoice
178
+ shipping_invoice_item.quantity = 1
179
+ shipping_invoice_item.amount = shipping_invoice_item.unit_price.nil? ? charge_line.money.amount : shipping_invoice_item.unit_price + charge_line.money.amount
180
+ shipping_invoice_item.unit_price = shipping_invoice_item.unit_price.nil? ? charge_line.money.amount : shipping_invoice_item.unit_price + charge_line.money.amount
181
+ shipping_invoice_item.taxed = charge_line.taxed?
182
+ shipping_invoice_item.add_invoiced_record(charge_line)
183
+
184
+ invoice.invoice_items << shipping_invoice_item
185
+ end
186
+ shipping_invoice_item.save
187
+ end
188
+
189
+ invoice.generated_by = order_txn
190
+
191
+ # calculate taxes
192
+ invoice.calculate_tax(options[:taxation])
193
+
194
+ invoice
195
+ end
196
+ end
197
+
198
+ def next_invoice_number
199
+ max_id = maximum('id')
200
+
201
+ current_invoice = where(Invoice.arel_table[:invoice_number].matches("%#{max_id}%")).first
202
+
203
+ if current_invoice
204
+ while current_invoice
205
+ max_id = max_id + 1
206
+ current_invoice = where(Invoice.arel_table[:invoice_number].matches("%#{max_id}%")).first
207
+ end
208
+ else
209
+ if max_id
210
+ max_id = max_id + 1
211
+ else
212
+ max_id = 1
213
+ end
214
+ end
215
+
216
+ "Inv-#{max_id}"
217
+ end
218
+
219
+ def open
220
+ Invoice.with_current_status(['invoice_statuses_open'])
221
+ end
222
+
223
+ def closed
224
+ Invoice.with_current_status(['invoice_statuses_closed'])
225
+ end
226
+
227
+ def hold
228
+ Invoice.with_current_status(['invoice_statuses_hold'])
229
+ end
230
+
231
+ def sent
232
+ Invoice.with_current_status(['invoice_statuses_sent'])
233
+ end
234
+ end
235
+
236
+ def has_invoice_items?
237
+ !self.items.empty?
238
+ end
239
+
240
+ def has_payments?(status=:all)
58
241
  selected_payment_applications = self.get_payment_applications(status)
242
+
59
243
  !(selected_payment_applications.nil? or selected_payment_applications.empty?)
60
244
  end
61
245
 
62
246
  def get_payment_applications(status=:all)
63
247
  selected_payment_applications = case status.to_sym
64
- when :pending
65
- self.payment_applications.pending
66
- when :successful
67
- self.payment_applications.successful
68
- when :all
69
- self.payment_applications
70
- end
248
+ when :pending
249
+ self.payment_applications.pending
250
+ when :successful
251
+ self.payment_applications.successful
252
+ when :all
253
+ self.payment_applications
254
+ end
71
255
 
72
256
  unless self.items.empty?
73
- selected_payment_applications = (selected_payment_applications | self.items.collect{|item| item.get_payment_applications(status)}).flatten! unless (self.items.collect{|item| item.get_payment_applications(status)}.empty?)
257
+ unless self.items.collect { |item| item.get_payment_applications(status) }.empty?
258
+ selected_payment_applications = (selected_payment_applications | self.items.collect { |item| item.get_payment_applications(status) }).flatten!
259
+ end
74
260
  end
75
-
261
+
76
262
  selected_payment_applications
77
263
  end
78
264
 
79
- def calculate_balance
80
- unless self.calculate_balance_strategy_type.nil?
81
- case self.calculate_balance_strategy_type.internal_identifier
82
- when 'invoice_items_and_payments'
83
- (self.items.all.sum(&:total_amount) - self.total_payments)
84
- when 'payable_balances_and_payments'
85
- (self.payable_balances.all.sum(&:balance).amount - self.total_payments)
86
- when 'payments'
87
- (self.balance - self.total_payments)
88
- else
89
- self.balance
265
+ def sub_total
266
+ if items.empty?
267
+ if self.balance_record
268
+ self.balance_record.amount
269
+ else
270
+ 0
271
+ end
272
+ else
273
+ self.items.all.sum(&:sub_total).round(2)
274
+ end
275
+ end
276
+
277
+ def total_amount
278
+ if items.empty?
279
+ if self.balance_record
280
+ self.balance_record.amount
281
+ else
282
+ 0
90
283
  end
91
284
  else
92
- self.balance
285
+ self.items.all.sum(&:total_amount).round(2)
93
286
  end
94
287
  end
95
288
 
96
289
  def balance
97
- self.balance_record.amount
290
+ if items.empty?
291
+ if self.balance_record
292
+ self.balance_record.amount
293
+ else
294
+ 0
295
+ end
296
+ else
297
+ self.items.all.sum(&:total_amount).round(2)
298
+ end
98
299
  end
99
300
 
301
+ alias payment_due balance
302
+
100
303
  def balance=(amount, currency=Currency.usd)
101
304
  if self.balance_record
102
305
  self.balance_record.amount = amount
@@ -106,12 +309,45 @@ class Invoice < ActiveRecord::Base
106
309
  self.balance_record.save
107
310
  end
108
311
 
109
- def payment_due
110
- self.items.all.sum(&:total_amount)
312
+ def total_payments
313
+ self.get_payment_applications(:successful).sum { |item| item.money.amount }
111
314
  end
112
315
 
113
- def total_payments
114
- self.get_payment_applications(:successful).sum{|item| item.money.amount}
316
+ # calculates tax for each line item and save to sales_tax
317
+ def calculate_tax(ctx={})
318
+ tax = 0
319
+
320
+ self.invoice_items.each do |line_item|
321
+ tax += line_item.calculate_tax(ctx)
322
+ end
323
+
324
+ self.sales_tax = tax
325
+ self.save
326
+
327
+ tax
328
+ end
329
+
330
+ def calculate_balance
331
+ unless self.calculate_balance_strategy_type.nil?
332
+ case self.calculate_balance_strategy_type.internal_identifier
333
+ when 'invoice_items_and_payments'
334
+ (self.items.all.sum(&:total_amount) - self.total_payments).round(2)
335
+ when 'payable_balances_and_payments'
336
+ (self.payable_balances.all.sum(&:balance).amount - self.total_payments).round(2)
337
+ when 'payments'
338
+ (self.balance - self.total_payments).round(2)
339
+ else
340
+ self.balance
341
+ end
342
+ else
343
+ unless self.balance.nil?
344
+ if has_invoice_items?
345
+ (self.balance - self.total_payments).round(2)
346
+ else
347
+ self.balance.round(2)
348
+ end
349
+ end
350
+ end
115
351
  end
116
352
 
117
353
  def transactions
@@ -119,23 +355,23 @@ class Invoice < ActiveRecord::Base
119
355
 
120
356
  self.items.each do |item|
121
357
  transactions << {
122
- :date => item.created_at,
123
- :description => item.item_description,
124
- :quantity => item.quantity,
125
- :amount => item.amount
358
+ :date => item.created_at,
359
+ :description => item.item_description,
360
+ :quantity => item.quantity,
361
+ :amount => item.amount
126
362
  }
127
363
  end
128
364
 
129
365
  self.get_payment_applications(:successful).each do |item|
130
366
  transactions << {
131
- :date => item.financial_txn.payments.last.created_at,
132
- :description => item.financial_txn.description,
133
- :quantity => 1,
134
- :amount => (0 - item.financial_txn.money.amount)
367
+ :date => item.financial_txn.payments.last.created_at,
368
+ :description => item.financial_txn.description,
369
+ :quantity => 1,
370
+ :amount => (0 - item.financial_txn.money.amount)
135
371
  }
136
372
  end
137
373
 
138
- transactions.sort_by{|item| [item[:date]]}
374
+ transactions.sort_by { |item| [item[:date]] }
139
375
  end
140
376
 
141
377
  def add_party_with_role_type(party, role_type)
@@ -147,6 +383,22 @@ class Invoice < ActiveRecord::Base
147
383
  self.invoice_party_roles.where('role_type_id = ?', convert_role_type(role_type).id).all.collect(&:party)
148
384
  end
149
385
 
386
+ def find_party_by_role_type(role_type)
387
+ parties = find_parties_by_role_type(role_type)
388
+
389
+ unless parties.empty?
390
+ parties.first
391
+ end
392
+ end
393
+
394
+ def dba_organization
395
+ find_parties_by_role_type('dba_org').first
396
+ end
397
+
398
+ def to_data_hash
399
+ to_hash(only: [:id, :created_at, :updated_at, :description, :invoice_number, :invoice_date, :due_date])
400
+ end
401
+
150
402
  private
151
403
 
152
404
  def convert_role_type(role_type)
@@ -155,5 +407,5 @@ class Invoice < ActiveRecord::Base
155
407
 
156
408
  role_type
157
409
  end
158
-
410
+
159
411
  end
@@ -1,47 +1,106 @@
1
+ #### Table Definition ###########################
2
+ # create_table :invoice_items do |t|
3
+ # #foreign keys
4
+ # t.references :invoice
5
+ # t.references :invoice_item_type
6
+ #
7
+ # #non-key columns
8
+ # t.integer :item_seq_id
9
+ # t.string :item_description
10
+ # t.decimal :unit_price, :precision => 8, :scale => 2
11
+ # t.boolean :taxed
12
+ # t.decimal :sales_tax, :precision => 8, :scale => 2
13
+ # t.decimal :quantity, :precision => 8, :scale => 2
14
+ # t.decimal :amount, :precision => 8, :scale => 2
15
+ #
16
+ # t.timestamps
17
+ # end
18
+
19
+ # add_index :invoice_items, :invoice_id, :name => 'invoice_id_idx'
20
+ # add_index :invoice_items, :invoice_item_type_id, :name => 'invoice_item_type_id_idx'
21
+ #################################################
22
+
1
23
  class InvoiceItem < ActiveRecord::Base
2
24
  attr_protected :created_at, :updated_at
3
25
 
4
- belongs_to :agreement
5
- belongs_to :agreement_item_type
6
- belongs_to :invoiceable_item, :polymorphic => true
26
+ has_payment_applications
27
+ tracks_created_by_updated_by
28
+
29
+ belongs_to :invoice
30
+ belongs_to :agreement
31
+ belongs_to :invoice_item_type
32
+ belongs_to :biz_txn_acct_root
33
+
34
+ has_many :invoiced_records, :dependent => :destroy
35
+ has_many :sales_tax_lines, as: :taxed_record, dependent: :destroy
36
+
37
+ alias :gl_account :biz_txn_acct_root
38
+
39
+ def taxed?
40
+ self.taxed
41
+ end
7
42
 
8
- #This line of code connects the invoice to a polymorphic payment application.
9
- #The effect of this is to allow payments to be "applied_to" invoices
10
- has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
11
- def pending
12
- all.select{|item| item.is_pending?}
43
+ def total_amount
44
+ if taxed?
45
+ (sub_total + (self.sales_tax || 0)).round(2)
46
+ else
47
+ sub_total
13
48
  end
14
- def successful
15
- all.select{|item| item.financial_txn.has_captured_payment?}
49
+ end
50
+
51
+ def sub_total
52
+ _amount = self.amount
53
+
54
+ if _amount.blank?
55
+ _amount = (self.unit_price * self.quantity)
16
56
  end
57
+
58
+ _amount.round(2)
17
59
  end
18
60
 
19
- def has_payments?(status)
20
- selected_payment_applications = self.get_payment_applications(status)
21
- !(selected_payment_applications.nil? or selected_payment_applications.empty?)
61
+ def balance
62
+ (self.total_amount - self.total_payments).round(2)
22
63
  end
23
-
24
- def get_payment_applications(status=:all)
25
- case status.to_sym
26
- when :pending
27
- self.payment_applications.pending
28
- when :successful
29
- self.payment_applications.successful
30
- when :all
31
- self.payment_applications
64
+
65
+ # calculates tax and save to sales_tax
66
+ def calculate_tax(ctx={})
67
+ tax = 0
68
+
69
+ # see if anything is taxed
70
+ if invoiced_records.collect { |item| item.taxed? }.include?(true)
71
+ taxation = ErpOrders::Taxation.new
72
+
73
+ tax += taxation.calculate_tax(self,
74
+ ctx.merge({
75
+ amount: (self.unit_price * (self.quantity || 1))
76
+ }))
32
77
  end
78
+
79
+ tax
33
80
  end
34
81
 
35
- def total_amount
36
- (self.amount * self.quantity)
82
+ def add_invoiced_record(record)
83
+ invoiced_records << InvoicedRecord.new(invoiceable_item: record)
37
84
  end
38
85
 
39
- def total_payments
40
- self.get_payment_applications(:successful).sum{|item| item.money.amount}
86
+ def to_s
87
+ item_description
41
88
  end
42
89
 
43
- def balance
44
- self.total_amount - self.total_payments
90
+ def product_descriptions
91
+ # return an array of product descriptions for this invoice item
92
+ descriptions = []
93
+ invoiced_records.each do |invoiced_record|
94
+ descriptions << invoiced_record.invoiceable_item.description
95
+ end
96
+ if descriptions.count == 0
97
+ descriptions << "No Product Description"
98
+ end
99
+ descriptions
100
+ end
101
+
102
+ def to_label
103
+ to_s
45
104
  end
46
105
 
47
106
  end
@@ -6,10 +6,10 @@
6
6
  # t.string :external_id_source
7
7
  # t.references :invoice
8
8
  # t.references :party
9
-
9
+ #
10
10
  # t.timestamps
11
11
  # end
12
-
12
+ #
13
13
  # add_index :invoice_party_roles, :invoice_id, :name => 'invoice_party_invoice_id_idx'
14
14
  # add_index :invoice_party_roles, :party_id, :name => 'invoice_party_party_id_idx'
15
15
  #################################################
@@ -0,0 +1,19 @@
1
+ class InvoicedRecord < ActiveRecord::Base
2
+ attr_protected :created_at, :updated_at
3
+
4
+ belongs_to :invoice_item
5
+ belongs_to :invoiceable_item, :polymorphic => true
6
+
7
+ # Weather or not this item is taxed
8
+ #
9
+ def taxed?
10
+ if invoiceable_item.respond_to?(:taxed?)
11
+ invoiceable_item.taxed?
12
+ elsif invoiceable_item.respond_to?(:taxable?)
13
+ invoiceable_item.taxable?
14
+ else
15
+ false
16
+ end
17
+ end
18
+
19
+ end
@@ -18,6 +18,8 @@
18
18
  class PaymentApplication < ActiveRecord::Base
19
19
  attr_protected :created_at, :updated_at
20
20
 
21
+ tracks_created_by_updated_by
22
+
21
23
  belongs_to :financial_txn
22
24
  belongs_to :payment_applied_to, :polymorphic => true
23
25
  belongs_to :money, :foreign_key => 'applied_money_amount_id', :dependent => :destroy
data/config/routes.rb CHANGED
@@ -1,13 +1,14 @@
1
- ErpInvoicing::Engine.routes.draw do
1
+ Rails.application.routes.draw do
2
+
3
+ namespace :api do
4
+ namespace :v1 do
2
5
 
3
- match '/sms/receive_response' => "sms#receive_response"
6
+ resources :invoices, :defaults => {:format => 'json'}
4
7
 
5
- match '/erp_app/organizer/bill_pay/base(/:action)' => "erp_app/organizer/bill_pay/base#index"
6
- match '/erp_app/organizer/bill_pay/accounts(/:action)' => "erp_app/organizer/bill_pay/accounts#index"
8
+ end # v1
9
+ end # api
7
10
 
8
- #shared
9
- match '/erp_app/shared/billing_accounts(/:action(/:id))' => "erp_app/shared/billing_accounts"
10
- match '/erp_app/shared/invoices(/:action(/:id))' => "erp_app/shared/invoices"
11
- match '/erp_app/shared/invoices/files/:invoice_id(/:action)' => "erp_app/shared/files"
12
-
11
+ end
12
+
13
+ ErpInvoicing::Engine.routes.draw do
13
14
  end
@@ -0,0 +1,19 @@
1
+ class AddInvoiceTrackedStatuses
2
+
3
+ def self.up
4
+ invoice_statuses = TrackedStatusType.find_or_create('invoice_statuses', 'Invoice Statuses')
5
+
6
+ TrackedStatusType.find_or_create('invoice_statuses_open', 'Open', invoice_statuses)
7
+ TrackedStatusType.find_or_create('invoice_statuses_hold', 'Hold', invoice_statuses)
8
+ TrackedStatusType.find_or_create('invoice_statuses_sent', 'Sent', invoice_statuses)
9
+ TrackedStatusType.find_or_create('invoice_statuses_closed', 'Closed', invoice_statuses)
10
+ end
11
+
12
+ def self.down
13
+ status = TrackedStatusType.where(internal_identifier: 'invoice_statuses').first
14
+ if status
15
+ status.destroy
16
+ end
17
+ end
18
+
19
+ end
@@ -70,25 +70,36 @@ class InvoicingServices < ActiveRecord::Migration
70
70
  #foreign keys
71
71
  t.references :invoice
72
72
  t.references :invoice_item_type
73
-
74
- #these columns support the polymporphic relationship to invoice-able items
75
- t.references :invoiceable_item, :polymorphic => true
76
73
 
77
74
  #non-key columns
78
75
  t.integer :item_seq_id
79
76
  t.string :item_description
80
- t.decimal :sales_tax, :precision => 8, :scale => 2
81
77
  t.decimal :quantity, :precision => 8, :scale => 2
82
78
  t.decimal :amount, :precision => 8, :scale => 2
79
+ t.boolean :taxable
80
+ t.decimal :tax_rate, :precision => 8, :scale => 2
81
+ t.decimal :unit_price, :precision => 8, :scale => 2
83
82
 
84
83
  t.timestamps
85
84
  end
86
85
 
87
- add_index :invoice_items, [:invoiceable_item_id, :invoiceable_item_type], :name => 'invoiceable_item_idx'
88
86
  add_index :invoice_items, :invoice_id, :name => 'invoice_id_idx'
89
87
  add_index :invoice_items, :invoice_item_type_id, :name => 'invoice_item_type_id_idx'
90
88
  end
91
-
89
+
90
+
91
+ # Create invoiced records
92
+ unless table_exists?(:invoiced_records)
93
+ create_table :invoiced_records do |t|
94
+ t.references :invoice_item
95
+ t.references :invoiceable_item, :polymorphic => true
96
+
97
+ t.timestamps
98
+ end
99
+
100
+ add_index :invoiced_records, [:invoiceable_item_id, :invoiceable_item_type], :name => 'invoiced_records_invoiceable_item_idx'
101
+ end
102
+
92
103
  # Create invoice item types
93
104
  unless table_exists?(:invoice_item_types)
94
105
  create_table :invoice_item_types do |t|
@@ -265,7 +276,8 @@ class InvoicingServices < ActiveRecord::Migration
265
276
  :invoice_items, :invoice_item_types, :invoice_party_roles,
266
277
  :billing_accounts, :billing_contact_mechanisms,
267
278
  :payment_applications, :payment_party_roles, :recurring_payments,
268
- :invoice_payment_term_sets,:invoice_payment_terms,:invoice_payment_term_types, :calculate_balance_strategy_types
279
+ :invoice_payment_term_sets,:invoice_payment_terms,:invoice_payment_term_types,
280
+ :calculate_balance_strategy_types, :invoiced_records
269
281
  ].each do |tbl|
270
282
  if table_exists?(tbl)
271
283
  drop_table tbl
@@ -0,0 +1,9 @@
1
+ class ChangeInvoiceItemDescriptionToText < ActiveRecord::Migration
2
+ def up
3
+ change_column :invoice_items, :item_description, :text
4
+ end
5
+
6
+ def down
7
+ change_column :invoice_items, :item_description, :string
8
+ end
9
+ end
@@ -0,0 +1,50 @@
1
+ class UpdateTaxationForInvoices < ActiveRecord::Migration
2
+ def up
3
+
4
+ if column_exists? :invoice_items, :tax_rate
5
+ remove_column :invoice_items, :tax_rate
6
+ end
7
+
8
+ unless column_exists? :invoice_items, :sales_tax
9
+ add_column :invoice_items, :sales_tax, :decimal, precision: 8, scale: 2
10
+ end
11
+
12
+ unless column_exists? :invoice_items, :taxed
13
+ add_column :invoice_items, :taxed, :boolean
14
+ end
15
+
16
+ if column_exists? :invoice_items, :taxable
17
+ remove_column :invoice_items, :taxable
18
+ end
19
+
20
+ unless column_exists? :invoices, :sales_tax
21
+ add_column :invoices, :sales_tax, :decimal, precision: 8, scale: 2
22
+ end
23
+
24
+ end
25
+
26
+ def down
27
+
28
+ unless column_exists? :invoice_items, :tax_rate
29
+ add_column :invoice_items, :tax_rate, :decimal, precision: 8, scale: 2
30
+ end
31
+
32
+ if column_exists? :invoice_items, :sales_tax
33
+ remove_column :invoice_items, :sales_tax
34
+ end
35
+
36
+ if column_exists? :invoice_items, :taxed
37
+ remove_column :invoice_items, :taxed
38
+ end
39
+
40
+ unless column_exists? :invoice_items, :taxable
41
+ add_column :invoice_items, :taxable, :boolean
42
+ end
43
+
44
+ if column_exists? :invoices, :sales_tax
45
+ remove_column :invoices, :sales_tax
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,15 @@
1
+ class AddBizTxnAcctRootIdToInvoiceItem < ActiveRecord::Migration
2
+ def up
3
+ unless column_exists? :invoice_items, :biz_txn_acct_root_id
4
+ add_column :invoice_items, :biz_txn_acct_root_id, :integer
5
+
6
+ add_index :invoice_items, :biz_txn_acct_root_id
7
+ end
8
+ end
9
+
10
+ def down
11
+ if column_exists? :invoice_items, :biz_txn_acct_root_id
12
+ remove_column :invoice_items, :biz_txn_acct_root_id
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ class AddCreatedByUpdatedByToErpInvoicing < ActiveRecord::Migration
2
+ def up
3
+ %w{invoices invoice_items payment_applications}.each do |table|
4
+
5
+ unless column_exists? table.to_sym, :created_by_party_id
6
+ add_column table.to_sym, :created_by_party_id, :integer
7
+
8
+ add_index table.to_sym, :created_by_party_id, name: "#{table}_created_by_pty_idx"
9
+ end
10
+
11
+ unless column_exists? table.to_sym, :updated_by_party_id
12
+ add_column table.to_sym, :updated_by_party_id, :integer
13
+
14
+ add_index table.to_sym, :updated_by_party_id, name: "#{table}_updated_by_pty_idx"
15
+ end
16
+
17
+ end
18
+
19
+ end
20
+
21
+ def down
22
+ %w{invoices invoice_items payment_applications}.each do |table|
23
+
24
+ if column_exists? table.to_sym, :created_by_party_id
25
+ remove_column table.to_sym, :created_by_party_id
26
+ end
27
+
28
+ if column_exists? table.to_sym, :updated_by_party_id
29
+ remove_column table.to_sym, :updated_by_party_id
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ end
data/lib/erp_invoicing.rb CHANGED
@@ -2,6 +2,7 @@ require 'erp_base_erp_svcs'
2
2
  require 'erp_app'
3
3
  require 'knitkit'
4
4
 
5
+ require 'erp_invoicing/extensions/active_record/has_payment_applications'
5
6
  require "erp_invoicing/version"
6
7
  require "erp_invoicing/engine"
7
8
 
@@ -2,8 +2,8 @@ module ErpInvoicing
2
2
  class Engine < Rails::Engine
3
3
  isolate_namespace ErpInvoicing
4
4
 
5
- initializer "erp_invoicing.merge_public" do |app|
6
- app.middleware.insert_before Rack::Runtime, ::ActionDispatch::Static, "#{root}/public"
5
+ ActiveSupport.on_load(:active_record) do
6
+ include ErpInvoicing::Extensions::ActiveRecord::HasPaymentApplications
7
7
  end
8
8
 
9
9
  ErpBaseErpSvcs.register_as_compass_ae_engine(config, self)
@@ -0,0 +1,62 @@
1
+ module ErpInvoicing
2
+ module Extensions
3
+ module ActiveRecord
4
+ module HasPaymentApplications
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ module ClassMethods
10
+ def has_payment_applications
11
+ extend HasPaymentApplications::SingletonMethods
12
+ include HasPaymentApplications::InstanceMethods
13
+
14
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
15
+ def pending
16
+ all.select{|item| item.is_pending?}
17
+ end
18
+
19
+ def successful
20
+ all.select{|item| item.financial_txn.has_captured_payment?}
21
+ end
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ module SingletonMethods
29
+ end
30
+
31
+ module InstanceMethods
32
+
33
+ def get_payment_applications(status=:all)
34
+ case status.to_sym
35
+ when :pending
36
+ payment_applications.pending
37
+ when :successful
38
+ payment_applications.successful
39
+ when :all
40
+ payment_applications
41
+ end
42
+ end
43
+
44
+ def has_payments?(status=:all)
45
+ selected_payment_applications = self.get_payment_applications(status)
46
+ !(selected_payment_applications.nil? or selected_payment_applications.empty?)
47
+ end
48
+
49
+ def total_payments
50
+ self.get_payment_applications(:successful).sum { |item| item.money.amount }
51
+ end
52
+
53
+ def total_pending_payments
54
+ self.payment_applications.pending.sum{|item| item.money.amount}
55
+ end
56
+
57
+ end
58
+
59
+ end #End HasPaymentApplications module
60
+ end #End ActiveRecord module
61
+ end #End Extensions module
62
+ end #End ErpInvoicing module
@@ -1,7 +1,7 @@
1
1
  module ErpInvoicing
2
2
  module VERSION #:nodoc:
3
3
  MAJOR = 4
4
- MINOR = 0
4
+ MINOR = 1
5
5
  TINY = 0
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: erp_invoicing
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.0.0
4
+ version: 4.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Koloski, Russell Holmes
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-11-19 00:00:00.000000000 Z
11
+ date: 2016-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erp_work_effort
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '4.0'
19
+ version: '4.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '4.0'
26
+ version: '4.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: erp_commerce
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '4.0'
33
+ version: '4.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '4.0'
40
+ version: '4.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: knitkit
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '3.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '3.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: erp_dev_svcs
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '4.0'
61
+ version: '4.1'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '4.0'
68
+ version: '4.1'
69
69
  description: ErpInvoicing adds models and services to the CompassAE core to handle
70
70
  invoicing and billing functions. It includes extensions to the core ERP classes
71
71
  for accounts and things that are 'billable' (products, work efforts), and additional
@@ -81,11 +81,15 @@ files:
81
81
  - GPL-3-LICENSE
82
82
  - README.rdoc
83
83
  - Rakefile
84
+ - app/controllers/api/v1/invoices_controller.rb
84
85
  - app/models/billing_account.rb
85
86
  - app/models/calculate_balance_strategy_type.rb
87
+ - app/models/extensions/biz_txn_acct_root.rb
88
+ - app/models/extensions/charge_line.rb
86
89
  - app/models/extensions/document.rb
87
90
  - app/models/extensions/financial_txn.rb
88
91
  - app/models/extensions/financial_txn_account.rb
92
+ - app/models/extensions/order_txn.rb
89
93
  - app/models/extensions/party.rb
90
94
  - app/models/extensions/product_instance.rb
91
95
  - app/models/extensions/product_type.rb
@@ -99,13 +103,20 @@ files:
99
103
  - app/models/invoice_payment_term_set.rb
100
104
  - app/models/invoice_payment_term_type.rb
101
105
  - app/models/invoice_type.rb
106
+ - app/models/invoiced_record.rb
102
107
  - app/models/payment_application.rb
103
108
  - app/models/recurring_payment.rb
104
109
  - config/routes.rb
105
110
  - db/data_migrations/20120605154637_add_calculate_balance_strategy_types.rb
111
+ - db/data_migrations/20160117135959_add_invoice_tracked_statuses.rb
106
112
  - db/migrate/20111121000000_invoicing_services.rb
113
+ - db/migrate/20150424193506_change_invoice_item_description_to_text.rb
114
+ - db/migrate/20150622170437_update_taxation_for_invoices.rb
115
+ - db/migrate/20151217185838_add_biz_txn_acct_root_id_to_invoice_item.rb
116
+ - db/migrate/20160310163059_add_created_by_updated_by_to_erp_invoicing.rb
107
117
  - lib/erp_invoicing.rb
108
118
  - lib/erp_invoicing/engine.rb
119
+ - lib/erp_invoicing/extensions/active_record/has_payment_applications.rb
109
120
  - lib/erp_invoicing/version.rb
110
121
  - lib/tasks/erp_invoicing_tasks.rake
111
122
  - spec/dummy/Rakefile
@@ -159,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
159
170
  version: '0'
160
171
  requirements: []
161
172
  rubyforge_project:
162
- rubygems_version: 2.2.2
173
+ rubygems_version: 2.4.8
163
174
  signing_key:
164
175
  specification_version: 4
165
176
  summary: ErpInvoicing adds models and services to the CompassAE core to handle invoicing