erp_invoicing 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/app/controllers/erp_invoicing/erp_app/organizer/bill_pay/accounts_controller.rb +98 -46
  2. data/app/controllers/erp_invoicing/erp_app/organizer/bill_pay/base_controller.rb +1 -1
  3. data/app/controllers/erp_invoicing/erp_app/shared/billing_accounts_controller.rb +111 -0
  4. data/app/controllers/erp_invoicing/erp_app/shared/files_controller.rb +52 -0
  5. data/app/controllers/erp_invoicing/erp_app/shared/invoices_controller.rb +208 -0
  6. data/app/controllers/erp_invoicing/sms_controller.rb +156 -0
  7. data/app/models/billing_account.rb +204 -0
  8. data/app/models/extensions/document.rb +5 -0
  9. data/app/models/extensions/financial_txn.rb +5 -0
  10. data/app/models/extensions/party.rb +10 -0
  11. data/app/models/invoice.rb +100 -7
  12. data/app/models/invoice_item.rb +35 -2
  13. data/app/models/invoice_payment_strategy_type.rb +5 -0
  14. data/app/models/invoice_payment_term.rb +4 -0
  15. data/app/models/invoice_payment_term_set.rb +6 -0
  16. data/app/models/invoice_payment_term_type.rb +3 -0
  17. data/app/models/payment_application.rb +6 -1
  18. data/app/models/recurring_payment.rb +40 -0
  19. data/config/routes.rb +8 -1
  20. data/db/data_migrations/20111121153349_create_bill_pay_organizer_application.rb +1 -1
  21. data/db/data_migrations/20120118181839_create_invoice_management_desktop_application.rb +26 -0
  22. data/db/data_migrations/20120229174322_add_billpay_widget.rb +28 -0
  23. data/db/migrate/20111121000000_invoicing_services.rb +170 -103
  24. data/db/migrate/20120228184317_add_text_to_pay_to_recurring_payments.rb +13 -0
  25. data/db/migrate/20120301155722_add_billing_date_to_billing_account.rb +13 -0
  26. data/lib/erp_invoicing/engine.rb +5 -0
  27. data/lib/erp_invoicing/version.rb +7 -1
  28. data/lib/erp_invoicing.rb +1 -0
  29. data/public/javascripts/erp_app/desktop/applications/invoice_management/billing_accounts_panel.js +80 -0
  30. data/public/javascripts/erp_app/desktop/applications/invoice_management/invoices_panel.js +48 -0
  31. data/public/javascripts/erp_app/desktop/applications/invoice_management/module.js +36 -0
  32. data/public/javascripts/erp_app/organizer/applications/bill_pay/base.js +88 -76
  33. data/public/javascripts/erp_app/organizer/applications/bill_pay/extensions.js +6 -97
  34. data/public/javascripts/erp_app/organizer/applications/bill_pay/make_payment_window.js +19 -13
  35. data/public/javascripts/erp_app/organizer/applications/bill_pay/payment_accounts_grid_panel.js +2 -1
  36. data/public/javascripts/erp_app/shared/add_invoice_window.js +220 -0
  37. data/public/javascripts/erp_app/shared/billing_accounts_grid_panel.js +318 -0
  38. data/public/javascripts/erp_app/shared/invoice_items_grid_panel.js +152 -0
  39. data/public/javascripts/erp_app/shared/invoices_grid_panel.js +363 -0
  40. data/public/javascripts/erp_app/shared/payments_grid_panel.js +74 -0
  41. data/spec/dummy/Rakefile +7 -0
  42. data/spec/dummy/app/assets/javascripts/application.js +9 -0
  43. data/spec/dummy/app/assets/stylesheets/application.css +7 -0
  44. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  45. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  46. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/spec/dummy/config/application.rb +50 -0
  48. data/spec/dummy/config/boot.rb +10 -0
  49. data/spec/dummy/config/database.yml +8 -0
  50. data/spec/dummy/config/environment.rb +5 -0
  51. data/spec/dummy/config/environments/spec.rb +27 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/inflections.rb +10 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  56. data/spec/dummy/config/initializers/session_store.rb +8 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +12 -0
  58. data/spec/dummy/config/locales/en.yml +5 -0
  59. data/spec/dummy/config/routes.rb +3 -0
  60. data/spec/dummy/config.ru +4 -0
  61. data/spec/dummy/public/404.html +26 -0
  62. data/spec/dummy/public/422.html +26 -0
  63. data/spec/dummy/public/500.html +26 -0
  64. data/spec/dummy/public/favicon.ico +0 -0
  65. data/spec/dummy/script/rails +6 -0
  66. data/spec/factories/basic.rb +9 -0
  67. data/spec/models/billing_account_spec.rb +78 -0
  68. data/spec/models/invoice_item_spec.rb +22 -0
  69. data/spec/models/invoice_spec.rb +58 -0
  70. data/spec/models/payment_application_spec.rb +17 -0
  71. data/spec/spec_helper.rb +60 -0
  72. metadata +148 -67
  73. data/app/widgets/bill_pay/base.rb +0 -230
  74. data/app/widgets/bill_pay/helpers/controller/bill_pay_controller_helper.rb +0 -3
  75. data/app/widgets/bill_pay/helpers/view/bill_pay_view_helper.rb +0 -3
  76. data/app/widgets/bill_pay/javascript/bill_pay.js +0 -11
  77. data/app/widgets/bill_pay/views/_menu.html.erb +0 -63
  78. data/app/widgets/bill_pay/views/_payment_history_table.html.erb +0 -115
  79. data/app/widgets/bill_pay/views/_statement_table.html.erb +0 -117
  80. data/app/widgets/bill_pay/views/account_home.html.erb +0 -10
  81. data/app/widgets/bill_pay/views/index.html.erb +0 -4
  82. data/app/widgets/bill_pay/views/make_payment.html.erb +0 -24
  83. data/app/widgets/bill_pay/views/payment_account_forms/bank_account.html.erb +0 -28
  84. data/app/widgets/bill_pay/views/payment_account_forms/credit_card.html.erb +0 -48
  85. data/app/widgets/bill_pay/views/payment_accounts.html.erb +0 -120
  86. data/app/widgets/bill_pay/views/payment_history.html.erb +0 -104
  87. data/app/widgets/bill_pay/views/pdf/layout.pdf.erb +0 -35
  88. data/app/widgets/bill_pay/views/statements.html.erb +0 -4
  89. data/public/javascripts/erp_app/organizer/applications/bill_pay/accounts_grid_panel.js +0 -182
@@ -0,0 +1,156 @@
1
+ module ErpInvoicing
2
+ class SmsController < ::ActionController::Base
3
+ before_filter :allow_by_ip
4
+
5
+ # Receive SMS callback from clickatell
6
+ # Example: If you provide this URL http://www.yourdomain.com/erp_invoicing/sms/receive_response then we will do a POST or GET as follows:
7
+ # https://www.yourdomain.com/sms/receive_response?api_id=12345&from=279991235642&to=27123456789&
8
+ # timestamp=2008-08-0609:43:50&text=Hereisthe%20messagetext&charset=ISO-8859-1&udh=&moMsgId=b2aee337abd962489b123fda9c3480fa
9
+ def receive_response
10
+ message_text = params[:text]
11
+ moMsgId = params[:moMsgId]
12
+ to_number = params[:to]
13
+ from_number = params[:from]
14
+
15
+ if message_text.blank? or to_number.blank? or from_number.blank?
16
+ Rails.logger.error 'ErpInvoicing::SmsController#receive_response called with insufficient data'
17
+ render_false and return
18
+ render :json => {:success => false} and return
19
+ end
20
+
21
+ # find the comm event sent within past 10mins where that incoming message is a reponse to
22
+ cmm_evt = CommunicationEvent.find_by_sql("SELECT * FROM communication_events
23
+ JOIN phone_numbers from_phone ON from_contact_mechanism_id=from_phone.id
24
+ JOIN phone_numbers to_phone ON to_contact_mechanism_id=to_phone.id
25
+ WHERE from_contact_mechanism_type = 'PhoneNumber'
26
+ AND from_contact_mechanism_type='PhoneNumber'
27
+ AND from_phone.phone_number = '#{to_number.to_s}'
28
+ AND (to_phone.phone_number = '#{from_number.to_s}' OR to_phone.phone_number = '#{from_number[1..from_number.length]}')
29
+ AND communication_events.created_at > '#{SMS_TIME_WINDOW.minutes.ago.to_s}'
30
+ ORDER BY communication_events.created_at DESC").first
31
+
32
+ unless cmm_evt.nil?
33
+ if message_text.downcase.include?('yespay') or message_text.downcase.include?('yes pay')
34
+ billing_account = BillingAccount.find(cmm_evt.case_id)
35
+ payment_due = billing_account.payment_due
36
+
37
+ # TODO: scrape for amount and compare with payment_due on account?
38
+
39
+ if billing_account.has_outstanding_balance?
40
+ success = submit_payment(cmm_evt, billing_account) if billing_account.has_outstanding_balance?
41
+ else
42
+ Rails.logger.error 'ErpInvoicing::SmsController#receive_response called: skipping payment NO OUTSTANDING BALANCE TO PAY'
43
+ end
44
+ to_party = cmm_evt.from_party
45
+
46
+ # log communication event
47
+ new_cmm_evt = CommunicationEvent.new
48
+ new_cmm_evt.short_description = 'SMS Response'
49
+ new_cmm_evt.comm_evt_purpose_types << CommEvtPurposeType.find_by_internal_identifier('sms_response')
50
+ new_cmm_evt.to_role = RoleType.find_by_internal_identifier('application')
51
+ new_cmm_evt.to_party = to_party
52
+ new_cmm_evt.to_contact_mechanism = to_party.default_phone_number
53
+ new_cmm_evt.from_contact_mechanism = cmm_evt.to_party.billing_phone_number
54
+ new_cmm_evt.from_role = RoleType.find_by_internal_identifier('customer')
55
+ new_cmm_evt.from_party = cmm_evt.to_party
56
+ new_cmm_evt.case_id = cmm_evt.case_id
57
+ new_cmm_evt.notes = "From Number: #{from_number}, To Number: #{to_number}, Message: #{message_text}, Payment: #{success}"
58
+ new_cmm_evt.external_identifier = moMsgId
59
+ new_cmm_evt.save
60
+
61
+ if success
62
+ # send successful payment notification
63
+ clikatell = ErpTechSvcs::SmsWrapper::Clickatell.new
64
+ clikatell.send_message(from_number, SMS_SUCCESS_NOTIFICATION.gsub('payment_due', payment_due.to_s), :mo => 1, :from => to_number)
65
+ render_true and return
66
+ else
67
+ render_false and return
68
+ end
69
+ end
70
+ else
71
+ Rails.logger.error "ErpInvoicing::SmsController#receive_ ation event #{moMsgId}"
72
+ render :json => {:success => false} and return
73
+ end
74
+ end
75
+
76
+ protected
77
+ def submit_payment(cmm_evt, billing_account)
78
+ party = cmm_evt.to_party
79
+
80
+ @error_message = nil
81
+ @message = nil
82
+ @payment_accounts = party.payment_accounts
83
+
84
+ root_account = BizTxnAcctRoot.find(@payment_accounts.first)
85
+ @payment_account = root_account.account
86
+ @amount = billing_account.payment_due
87
+ @payment_date = Date.today
88
+
89
+ money = Money.create(
90
+ :amount => @amount.to_f,
91
+ :description => "Clicktopay Payment Applied",
92
+ :currency => Currency.usd
93
+ )
94
+ financial_txn = FinancialTxn.new(:apply_date => @payment_date)
95
+ financial_txn.description = "Clicktopay Payment Applied"
96
+ financial_txn.money = money
97
+ financial_txn.account = root_account
98
+ financial_txn.save
99
+
100
+ PaymentApplication.create(
101
+ :financial_txn => financial_txn,
102
+ :payment_applied_to => billing_account,
103
+ :money => money
104
+ )
105
+
106
+ if financial_txn.apply_date == Date.today
107
+ case @payment_account.class.to_s
108
+ when "BankAccount"
109
+ financial_txn.txn_type = BizTxnType.ach_sale
110
+ financial_txn.save
111
+ result = @payment_account.purchase(financial_txn, ErpCommerce::Config.active_merchant_gateway_wrapper)
112
+ if !result[:payment].nil? and result[:payment].success
113
+ @authorization_code = result[:payment].authorization_code
114
+ else
115
+ @message = result[:message]
116
+ end
117
+ when "CreditCardAccount"
118
+ financial_txn.txn_type = BizTxnType.cc_sale
119
+ financial_txn.save
120
+ result = @payment_account.purchase(financial_txn, '123', ErpCommerce::Config.active_merchant_gateway_wrapper)
121
+ if !result[:payment].nil? and result[:payment].success
122
+ @authorization_code = result[:payment].authorization_code
123
+ else
124
+ @message = result[:message]
125
+ end
126
+ end
127
+ end
128
+
129
+ if @error_message.nil?
130
+ Rails.logger.info 'ErpInvoicing::SmsController#submit_payment called: payment successful'
131
+ else
132
+ Rails.logger.error 'ErpInvoicing::SmsController#submit_payment called: payment failed'
133
+ end
134
+ end
135
+
136
+
137
+ def render_false
138
+ render :json => {:success => false}
139
+ end
140
+
141
+ def render_true
142
+ render :json => {:success => true}
143
+ end
144
+
145
+ def allow_by_ip
146
+ Rails.logger.info "ErpInvoicing::SmsController#allow_by_ip called: request from #{request.remote_ip}"
147
+ if request.remote_ip != SMS_SERVICE_IP
148
+ redirect_to '/'
149
+ flash.now[:notice] = "Access denied!"
150
+ return false
151
+ end
152
+ end
153
+
154
+
155
+ end
156
+ end
@@ -0,0 +1,204 @@
1
+ class BillingAccount < ActiveRecord::Base
2
+ has_relational_dynamic_attributes
3
+ acts_as_financial_txn_account
4
+
5
+
6
+ has_many :invoices, :dependent => :destroy do
7
+ def by_invoice_date
8
+ order('invoice_date desc')
9
+ end
10
+
11
+ def balance
12
+ all.sum(&:balance)
13
+ end
14
+ end
15
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
16
+ def successful
17
+ all.select{|item| item.financial_txn.has_captured_payment?}
18
+ end
19
+ def pending
20
+ all.select{|item| item.is_pending?}
21
+ end
22
+ end
23
+ has_one :recurring_payment, :dependent => :destroy
24
+
25
+ def has_recurring_payment_enabled?
26
+ !self.recurring_payment.nil? and self.recurring_payment.enabled
27
+ end
28
+
29
+ def has_payments?(status)
30
+ selected_payment_applications = self.get_payment_applications(status)
31
+ !(selected_payment_applications.nil? or selected_payment_applications.empty?)
32
+ end
33
+
34
+ def get_payment_applications(status=:all)
35
+ selected_payment_applications = case status.to_sym
36
+ when :pending
37
+ self.payment_applications.pending
38
+ when :successful
39
+ self.payment_applications.successful
40
+ when :all
41
+ self.payment_applications
42
+ end
43
+
44
+ unless self.invoices.empty?
45
+ selected_payment_applications = (selected_payment_applications | self.invoices.collect{|item| item.get_payment_applications(status)}).flatten! unless (self.invoices.collect{|item| item.get_payment_applications(status)}.empty?)
46
+ end
47
+
48
+ selected_payment_applications
49
+ end
50
+
51
+ def has_outstanding_balance?
52
+ (outstanding_balance > 0)
53
+ end
54
+
55
+ def outstanding_balance
56
+ (balance - total_pending_payments)
57
+ end
58
+
59
+ def total_pending_payments
60
+ self.payment_applications.pending.sum{|item| item.money.amount}
61
+ end
62
+
63
+ def total_payments
64
+ self.payment_applications.successful.sum{|item| item.money.amount}
65
+ end
66
+
67
+ #payment due is determined by last invoice
68
+ def payment_due
69
+ if self.calculate_balance and !self.invoices.empty?
70
+ self.invoices.by_invoice_date.last.payment_due
71
+ else
72
+ self.financial_txn_account.payment_due.amount
73
+ end
74
+ end
75
+
76
+ def payment_due=(amount, currency=Currency.usd)
77
+ currency = Currency.usd
78
+ if amount.is_a?(Array)
79
+ currency = amount.last
80
+ amount = amount.first
81
+ end
82
+ if self.financial_txn_account.payment_due
83
+ self.financial_txn_account.payment_due.amount = amount
84
+ else
85
+ self.financial_txn_account.payment_due = Money.create(:amount => amount, :currency => currency)
86
+ end
87
+ self.financial_txn_account.payment_due.save
88
+ end
89
+
90
+ def billing_date
91
+ unless self.invoices.empty?
92
+ current_invoice.invoice_date
93
+ else
94
+ self.attributes['billing_date']
95
+ end
96
+ end
97
+
98
+ #override due_date for invoice.invoice_date
99
+ def due_date
100
+ unless self.invoices.empty?
101
+ current_invoice.due_date
102
+ else
103
+ self.financial_txn_account.due_date
104
+ end
105
+ end
106
+
107
+ #override balance_date for today if calculate_balance is set to true
108
+ def balance_date
109
+ if self.calculate_balance
110
+ Date.today
111
+ else
112
+ unless self.invoices.empty?
113
+ current_invoice.invoice_date
114
+ else
115
+ self.financial_txn_account.balance_date
116
+ end
117
+ end
118
+ end
119
+
120
+ #override balance to use invoices is calculate_balance is set to true
121
+ def balance
122
+ if self.calculate_balance
123
+ self.invoices.balance
124
+ else
125
+ (self.financial_txn_account.balance.amount - self.total_payments)
126
+ end
127
+ end
128
+
129
+ def balance=(amount, currency=Currency.usd)
130
+ if amount.is_a?(Array)
131
+ currency = amount.last
132
+ amount = amount.first
133
+ end
134
+ if self.financial_txn_account.balance
135
+ self.financial_txn_account.balance.amount = amount
136
+ else
137
+ self.financial_txn_account.balance = Money.create(:amount => amount, :currency => currency)
138
+ end
139
+ self.financial_txn_account.balance.save
140
+ end
141
+
142
+ def current_invoice
143
+ self.invoices.by_invoice_date.last
144
+ end
145
+
146
+ def send_sms_notification
147
+ primary_party = self.find_parties_by_role('primary').first
148
+ from_party = Party.find_by_description('Compass AE')
149
+ from_number = from_party.default_phone_number
150
+ to_number = primary_party.billing_phone_number
151
+
152
+ # prevent multiple sms notifications being sent within the time window
153
+ previous_cmm_evt = CommunicationEvent.find_by_sql("SELECT * FROM communication_events
154
+ JOIN phone_numbers from_phone ON from_contact_mechanism_id=from_phone.id
155
+ JOIN phone_numbers to_phone ON to_contact_mechanism_id=to_phone.id
156
+ WHERE from_contact_mechanism_type = 'PhoneNumber'
157
+ AND from_contact_mechanism_type='PhoneNumber'
158
+ AND from_phone.phone_number = '#{from_number.phone_number.to_s}'
159
+ AND (to_phone.phone_number = '#{to_number.phone_number.to_s}' OR to_phone.phone_number = '#{to_number.phone_number[1..to_number.phone_number.length]}')
160
+ AND communication_events.created_at > '#{SMS_TIME_WINDOW.minutes.ago.to_s}'
161
+ ORDER BY communication_events.created_at DESC").first
162
+
163
+ Rails.logger.info 'not sending sms notification, one has already been sent within time window' if !previous_cmm_evt.nil?
164
+
165
+ unless primary_party.billing_phone_number.nil? or !previous_cmm_evt.nil?
166
+ message = SMS_NOTIFICATION_MESSAGE.gsub('payment_due',self.payment_due.to_s)
167
+
168
+
169
+ # get cmm event purpose type
170
+ sms_purpose = CommEvtPurposeType.find_by_internal_identifier('sms_notification')
171
+
172
+ # create cmm event
173
+ cmm_evt = CommunicationEvent.new
174
+ cmm_evt.short_description = 'SMS Notification'
175
+ cmm_evt.from_role = RoleType.find_by_internal_identifier('application')
176
+ cmm_evt.from_party = from_party
177
+ cmm_evt.from_contact_mechanism = from_number
178
+ cmm_evt.comm_evt_purpose_types << sms_purpose
179
+ cmm_evt.to_contact_mechanism = to_number
180
+ cmm_evt.to_role = RoleType.find_by_internal_identifier('customer')
181
+ cmm_evt.to_party = primary_party
182
+ cmm_evt.case_id = self.id
183
+ cmm_evt.notes = "From Number: #{from_number}, To Number: #{to_number}, Message: #{message}"
184
+
185
+ clikatell = ErpTechSvcs::SmsWrapper::Clickatell.new
186
+ cmm_evt.external_identifier = clikatell.send_message(to_number.phone_number, message, :mo => 1, :from => from_number.phone_number)
187
+
188
+ unless cmm_evt.external_identifier.nil?
189
+ cmm_evt.save
190
+ return true
191
+ else
192
+ return false
193
+ end
194
+ end
195
+ end
196
+
197
+ def send_email_notification
198
+ primary_party = self.find_parties_by_role('primary').first
199
+ unless primary_party.billing_email_address.nil?
200
+ #send email
201
+ end
202
+ end
203
+
204
+ end
@@ -0,0 +1,5 @@
1
+ Document.class_eval do
2
+ has_many_polymorphic :documented_models,
3
+ :through => :valid_documents,
4
+ :models => [:billing_account]
5
+ end
@@ -0,0 +1,5 @@
1
+ FinancialTxn.class_eval do
2
+
3
+ has_many :payment_applications, :dependent => :destroy
4
+
5
+ end
@@ -0,0 +1,10 @@
1
+ Party.class_eval do
2
+
3
+ has_many :invoice_party_roles, :dependent => :destroy
4
+ has_many :invoices, :through => :invoice_party_roles
5
+
6
+ def billing_accounts
7
+ self.biz_txn_acct_roots.where('biz_txn_acct_type = ?', 'FinancialTxnAccount').collect(&:account).collect(&:financial_account)
8
+ end
9
+
10
+ end
@@ -1,16 +1,109 @@
1
1
  class Invoice < ActiveRecord::Base
2
+ acts_as_document
2
3
 
4
+ belongs_to :billing_account
3
5
  belongs_to :invoice_type
4
- has_many :invoice_items
5
- has_many :invoice_party_roles
6
- has_many :parties, :through => :invoice_party_roles
7
-
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
6
+ belongs_to :invoice_payment_strategy_type
7
+ has_many :invoice_payment_term_sets, :dependent => :destroy
8
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
9
+ def successful
10
+ all.select{|item| item.financial_txn.has_captured_payment?}
11
+ end
12
+ def pending
13
+ all.select{|item| item.is_pending?}
14
+ end
15
+ end
16
+ has_many :invoice_items, :dependent => :destroy do
17
+ def by_date
18
+ order('created_at')
19
+ end
11
20
 
21
+ def unpaid
22
+ select{|item| item.balance > 0 }
23
+ end
24
+ end
25
+ has_many :invoice_party_roles, :dependent => :destroy
26
+ has_many :parties, :through => :invoice_party_roles
27
+
12
28
  alias :items :invoice_items
13
29
  alias :type :invoice_type
14
30
  alias :party_roles :invoice_party_roles
31
+ alias :payment_strategy :invoice_payment_strategy_type
32
+
33
+ def has_payments?(status)
34
+ selected_payment_applications = self.get_payment_applications(status)
35
+ !(selected_payment_applications.nil? or selected_payment_applications.empty?)
36
+ end
37
+
38
+ def get_payment_applications(status=:all)
39
+ selected_payment_applications = case status.to_sym
40
+ when :pending
41
+ self.payment_applications.pending
42
+ when :successful
43
+ self.payment_applications.successful
44
+ when :all
45
+ self.payment_applications
46
+ end
47
+
48
+ unless self.items.empty?
49
+ 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?)
50
+ end
51
+
52
+ selected_payment_applications
53
+ end
54
+
55
+ def balance
56
+ (self.payment_due - self.total_payments)
57
+ end
58
+
59
+ def payment_due
60
+ self.items.all.sum(&:total_amount)
61
+ end
62
+
63
+ def total_payments
64
+ self.get_payment_applications(:successful).sum{|item| item.money.amount}
65
+ end
66
+
67
+ def transactions
68
+ transactions = []
69
+
70
+ self.items.each do |item|
71
+ transactions << {
72
+ :date => item.created_at,
73
+ :description => item.item_description,
74
+ :quantity => item.quantity,
75
+ :amount => item.amount
76
+ }
77
+ end
78
+
79
+ self.get_payment_applications(:successful).each do |item|
80
+ transactions << {
81
+ :date => item.financial_txn.payments.last.created_at,
82
+ :description => item.financial_txn.description,
83
+ :quantity => 1,
84
+ :amount => (0 - item.financial_txn.money.amount)
85
+ }
86
+ end
87
+
88
+ transactions.sort_by{|item| [item[:date]]}
89
+ end
90
+
91
+ def add_party_with_role_type(party, role_type)
92
+ self.invoice_party_roles << InvoicePartyRole.create(:party => party, :role_type => convert_role_type(role_type))
93
+ self.save
94
+ end
95
+
96
+ def find_parties_by_role_type(role_type)
97
+ self.invoice_party_roles.where('role_type_id = ?', convert_role_type(role_type).id).all.collect(&:party)
98
+ end
99
+
100
+ private
101
+
102
+ def convert_role_type(role_type)
103
+ role_type = RoleType.iid(role_type) if role_type.is_a? String
104
+ raise "Role type does not exist" if role_type.nil?
105
+
106
+ role_type
107
+ end
15
108
 
16
109
  end
@@ -2,12 +2,45 @@ class InvoiceItem < ActiveRecord::Base
2
2
 
3
3
  belongs_to :agreement
4
4
  belongs_to :agreement_item_type
5
-
6
5
  belongs_to :invoiceable_item, :polymorphic => true
7
6
 
8
7
  #This line of code connects the invoice to a polymorphic payment application.
9
8
  #The effect of this is to allow payments to be "applied_to" invoices
10
- has_many :payment_applications, :as => :payment_applied_to
9
+ has_many :payment_applications, :as => :payment_applied_to, :dependent => :destroy do
10
+ def pending
11
+ all.select{|item| item.is_pending?}
12
+ end
13
+ def successful
14
+ all.select{|item| item.financial_txn.has_captured_payment?}
15
+ end
16
+ end
11
17
 
18
+ def has_payments?(status)
19
+ selected_payment_applications = self.get_payment_applications(status)
20
+ !(selected_payment_applications.nil? or selected_payment_applications.empty?)
21
+ end
22
+
23
+ def get_payment_applications(status=:all)
24
+ case status.to_sym
25
+ when :pending
26
+ self.payment_applications.pending
27
+ when :successful
28
+ self.payment_applications.successful
29
+ when :all
30
+ self.payment_applications
31
+ end
32
+ end
33
+
34
+ def total_amount
35
+ (self.amount * self.quantity)
36
+ end
37
+
38
+ def total_payments
39
+ self.get_payment_applications(:successful).sum{|item| item.money.amount}
40
+ end
41
+
42
+ def balance
43
+ self.total_amount - self.total_payments
44
+ end
12
45
 
13
46
  end
@@ -0,0 +1,5 @@
1
+ class InvoicePaymentStrategyType < ActiveRecord::Base
2
+ acts_as_erp_type
3
+
4
+ has_many :invoices
5
+ end
@@ -0,0 +1,4 @@
1
+ class InvoicePaymentTerm < ActiveRecord::Base
2
+ has_one :invoice_payment_term_type
3
+ belongs_to :invoice_payment_term_set
4
+ end
@@ -0,0 +1,6 @@
1
+ class InvoicePaymentTermSet < ActiveRecord::Base
2
+ belongs_to :invoice
3
+ has_many :invoice_payment_terms
4
+
5
+ alias :payment_terms :invoice_payment_terms
6
+ end
@@ -0,0 +1,3 @@
1
+ class InvoicePaymentTermType < ActiveRecord::Base
2
+ has_many :invoice_payment_terms
3
+ end
@@ -1,6 +1,11 @@
1
1
  class PaymentApplication < ActiveRecord::Base
2
2
 
3
- belongs_to :payment
3
+ belongs_to :financial_txn, :dependent => :destroy
4
4
  belongs_to :payment_applied_to, :polymorphic => true
5
+ belongs_to :money, :foreign_key => 'applied_money_amount_id', :dependent => :destroy
6
+
7
+ def is_pending?
8
+ (self.financial_txn.is_scheduled? or self.financial_txn.is_pending?) unless self.financial_txn.nil?
9
+ end
5
10
 
6
11
  end
@@ -0,0 +1,40 @@
1
+ class RecurringPayment < ActiveRecord::Base
2
+ belongs_to :billing_account
3
+ belongs_to :payment_account, :polymorphic => true
4
+
5
+ def schedule_payment(date)
6
+ unless self.payment_account.nil?
7
+ if self.billing_account.has_outstanding_balance?
8
+ payment_amount = self.billing_account.outstanding_balance
9
+ if payment_amount < self.pay_up_to_amount
10
+
11
+ money = Money.create(
12
+ :amount => payment_amount.to_f,
13
+ :description => "AutoPayment",
14
+ :currency => Currency.usd
15
+ )
16
+ financial_txn = FinancialTxn.create(
17
+ :apply_date => date,
18
+ :money => money
19
+ )
20
+ financial_txn.description = "AutoPayment"
21
+ financial_txn.account = self.payment_account.account_root
22
+ financial_txn.save
23
+
24
+ PaymentApplication.create(
25
+ :financial_txn => financial_txn,
26
+ :payment_applied_to => self.billing_account,
27
+ :money => money,
28
+ :comment => "AutoPayment"
29
+ )
30
+
31
+ #make sure the payment is below the pay_up_to_amount
32
+ else
33
+ #notify payment greater than amount in autopay
34
+ end
35
+ end#make sure the account has an outstanding balance
36
+ end#make sure it has a payment account
37
+
38
+ end
39
+
40
+ end
data/config/routes.rb CHANGED
@@ -1,6 +1,13 @@
1
1
  ErpInvoicing::Engine.routes.draw do
2
2
 
3
+ match '/sms/receive_response' => "sms#receive_response"
4
+
3
5
  match '/erp_app/organizer/bill_pay/base(/:action)' => "erp_app/organizer/bill_pay/base#index"
4
6
  match '/erp_app/organizer/bill_pay/accounts(/:action)' => "erp_app/organizer/bill_pay/accounts#index"
5
7
 
6
- end
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
+
13
+ end
@@ -2,7 +2,7 @@ class CreateBillPayOrganizerApplication
2
2
  def self.up
3
3
  OrganizerApplication.create(
4
4
  :description => 'Bill Pay',
5
- :icon => 'icon-credit_cards',
5
+ :icon => 'icon-creditcards',
6
6
  :javascript_class_name => 'Compass.ErpApp.Organizer.Applications.BillPay.Base',
7
7
  :internal_identifier => 'bill_pay'
8
8
  )
@@ -0,0 +1,26 @@
1
+ class CreateInvoiceManagementDesktopApplication
2
+ def self.up
3
+ app = DesktopApplication.create(
4
+ :description => 'Invoice Management',
5
+ :icon => 'icon-creditcards',
6
+ :javascript_class_name => 'Compass.ErpApp.Desktop.Applications.InvoiceManagement',
7
+ :internal_identifier => 'invoice_management',
8
+ :shortcut_id => 'invoice_management-win'
9
+ )
10
+
11
+ app.save
12
+
13
+ pt1 = PreferenceType.iid('desktop_shortcut')
14
+ pt1.preferenced_records << app
15
+ pt1.save
16
+
17
+ pt2 = PreferenceType.iid('autoload_application')
18
+ pt2.preferenced_records << app
19
+ pt2.save
20
+
21
+ end
22
+
23
+ def self.down
24
+ DesktopApplication.destroy_all(['internal_identifier = ?','invoice_management'])
25
+ end
26
+ end