Class: OutgoingPayment

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/outgoing_payment.rb

Overview

== Schema Information

Table name: outgoing_payments
Database name: primary

id :integer not null, primary key
amount :decimal(10, 2)
approved_at :datetime
category :string(255)
check_state :string(255)
currency :string(255)
exchange_rate :float
payment_date :date
print_reminder_sent :boolean default(FALSE)
reference_number :string(255) not null
remark :text
reversal_date :date
review_request_sent :boolean default(FALSE)
state :string(255)
created_at :datetime
updated_at :datetime
approved_by_id :integer
bank_account_id :integer
company_id :integer
creator_id :integer
job_id :string(255)
mailing_address_id :integer
supplier_id :integer
updater_id :integer

Indexes

category_state (category,state)
index_outgoing_payments_on_amount (amount)
index_outgoing_payments_on_bank_account_id (bank_account_id)
index_outgoing_payments_on_check_state (check_state)
index_outgoing_payments_on_company_id (company_id)
index_outgoing_payments_on_currency (currency)
index_outgoing_payments_on_job_id (job_id)
index_outgoing_payments_on_mailing_address_id (mailing_address_id)
index_outgoing_payments_on_payment_date (payment_date)
index_outgoing_payments_on_reference_number (reference_number)
index_outgoing_payments_on_state (state)
index_outgoing_payments_on_supplier_id (supplier_id)

Constant Summary collapse

CATEGORIES =
%w[ach cash check debit_card e_billpay non_cash paypal wire_transfer].freeze
DEFAULT_BANK_ACCOUNTS =

default bank account per country and payment method, using => instead of : to force it to use strings for the keys

{
  1 => {
    'ach' => 1,
    'check' => 1,
    'debit_card' => 1,
    'e_billpay' => 1,
    'paypal' => 7,
    'wire_transfer' => 1
  },
  2 => {
    'ach' => 2,
    'check' => 2,
    'debit_card' => 2,
    'e_billpay' => 2,
    'paypal' => 8,
    'wire_transfer' => 2
  },
  3 => {},
  4 => {}
}.freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#amountObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#bank_account_idObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#categoryObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#company_idObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#credit_memo_idObject

Returns the value of attribute credit_memo_id.



111
112
113
# File 'app/models/outgoing_payment.rb', line 111

def credit_memo_id
  @credit_memo_id
end

#currencyObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#payment_dateObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#supplier_idObject (readonly)



86
# File 'app/models/outgoing_payment.rb', line 86

validates :company_id, :supplier_id, :bank_account_id, :category, :amount, :payment_date, :currency, presence: true

#supplier_typeObject

Returns the value of attribute supplier_type.



111
112
113
# File 'app/models/outgoing_payment.rb', line 111

def supplier_type
  @supplier_type
end

Class Method Details

.appliedActiveRecord::Relation<OutgoingPayment>

A relation of OutgoingPayments that are applied. Active Record Scope

Returns:

See Also:



109
# File 'app/models/outgoing_payment.rb', line 109

scope :applied, -> { where(state: 'applied') }

.billing_address(payee, outgoing_payment_items) ⇒ Object



298
299
300
301
302
303
304
# File 'app/models/outgoing_payment.rb', line 298

def self.billing_address(payee, outgoing_payment_items)
  billing_address = outgoing_payment_items&.first&.spiff_enrollment&.mailing_address

  billing_address ||= payee&.billing_address

  billing_address
end

.check_states_for_selectObject



210
211
212
# File 'app/models/outgoing_payment.rb', line 210

def self.check_states_for_select
  [['No Check', :no_check], ['Queued', :queued], ['Printed', :printed], ['Reprinted', :reprinted], ['Generated', :generated]]
end

.get_next_reference_numberObject



359
360
361
362
# File 'app/models/outgoing_payment.rb', line 359

def self.get_next_reference_number
  seq = OutgoingPayment.find_by_sql("SELECT nextval('payment_reference_numbers_seq') AS reference_number")
  seq[0].reference_number.to_s
end

.payment_count(company_id = nil, bank_account_id = nil, where_conditions = nil, where_not_conditions = nil) ⇒ Object



214
215
216
217
218
219
220
221
# File 'app/models/outgoing_payment.rb', line 214

def self.payment_count(company_id = nil,  = nil, where_conditions = nil, where_not_conditions = nil)
  p = OutgoingPayment.order('id')
  p = p.where(company_id:) unless company_id.nil?
  p = p.where(bank_account_id:) unless .nil?
  p = p.where(where_conditions) unless where_conditions.nil?
  p = p.where.not(where_not_conditions) unless where_not_conditions.nil?
  p.count
end


336
337
338
339
340
341
342
343
344
345
# File 'app/models/outgoing_payment.rb', line 336

def self.print_all_checks(payment_ids, current_user)
  OutgoingPayment.transaction do
    pdfs = []
    payment_ids.each do |payment_id|
      payment = OutgoingPayment.find(payment_id)
      pdfs << payment.print_check(current_user)
    end
    return PdfTools.combine(pdfs, output_file_path: Upload.temp_location("combined_check_printout_#{Time.current.to_fs(:no_spaces)}.pdf"))
  end
end

.send_checks_pending_review_notificationObject



181
182
183
184
185
186
187
188
# File 'app/models/outgoing_payment.rb', line 181

def self.send_checks_pending_review_notification
  payments = where(category: 'check', state: 'applied', check_state: 'pending_review', review_request_sent: false)
  if payments.any?
    InternalMailer.checks_pending_review_notification(payments).deliver
    payments.each { |payment| payment.update(review_request_sent: true) }
  end
  true
end

.send_checks_ready_to_print_notificationObject



190
191
192
193
194
195
196
197
# File 'app/models/outgoing_payment.rb', line 190

def self.send_checks_ready_to_print_notification
  payments = where(category: 'check', state: 'applied', check_state: 'queued', print_reminder_sent: false)
  if payments.any?
    InternalMailer.checks_ready_to_print_notification(payments).deliver
    payments.each { |payment| payment.update(print_reminder_sent: true) }
  end
  true
end

.send_daily_unprinted_checks_digestObject



199
200
201
202
203
204
# File 'app/models/outgoing_payment.rb', line 199

def self.send_daily_unprinted_checks_digest
  payments = applied.where(category: 'check', check_state: %w[queued generated], job_id: nil)
             .includes(:company, :supplier, :outgoing_payment_items)
  InternalMailer.daily_unprinted_checks_digest(payments.to_a).deliver if payments.any?
  true
end

.states_for_selectObject



206
207
208
# File 'app/models/outgoing_payment.rb', line 206

def self.states_for_select
  OutgoingPayment.state_machines[:state].states.map { |s| [s.human_name.titleize, s.name] }.sort
end

Instance Method Details

#addressAddress

Returns:

See Also:



78
# File 'app/models/outgoing_payment.rb', line 78

belongs_to :address, foreign_key: :mailing_address_id, primary_key: :id, optional: true

#approved_byEmployee

Returns:

See Also:



77
# File 'app/models/outgoing_payment.rb', line 77

belongs_to :approved_by, class_name: 'Employee', optional: true

#background_jobObject



223
224
225
226
227
# File 'app/models/outgoing_payment.rb', line 223

def background_job
  return unless job_id

  BackgroundJobStatus.find(job_id)
end

#bank_accountBankAccount



76
# File 'app/models/outgoing_payment.rb', line 76

belongs_to :bank_account, optional: true

#build_outgoing_payment_items(voucher_items = {}, credit_memos = {}, receipts = {}) ⇒ Object



259
260
261
262
263
264
265
266
267
268
269
270
# File 'app/models/outgoing_payment.rb', line 259

def build_outgoing_payment_items(voucher_items = {}, credit_memos = {}, receipts = {})
  outgoing_payment_items.destroy_all
  voucher_items.each do |voucher_item_id, attrs|
    outgoing_payment_items.build(voucher_item_id:, amount: attrs['amount_to_pay'])
  end
  credit_memos.each do |credit_memo_id, attrs|
    outgoing_payment_items.build(credit_memo_id:, amount: attrs['amount_to_pay'])
  end
  receipts.each do |receipt_id, attrs|
    outgoing_payment_items.build(receipt_id:, amount: attrs['amount_to_pay'])
  end
end

#can_print_check?Boolean

Returns:

  • (Boolean)


177
178
179
# File 'app/models/outgoing_payment.rb', line 177

def can_print_check?
  category == 'check' and !voided? and !pending_review?
end

#checksActiveRecord::Relation<Check>

Returns:

  • (ActiveRecord::Relation<Check>)

See Also:



82
# File 'app/models/outgoing_payment.rb', line 82

has_many :checks

#companyCompany

Returns:

See Also:



74
# File 'app/models/outgoing_payment.rb', line 74

belongs_to :company, optional: true

#currency_symbolObject



249
250
251
252
253
# File 'app/models/outgoing_payment.rb', line 249

def currency_symbol
  Money::Currency.new(currency || 'USD').symbol
rescue Money::Currency::UnknownCurrency
  '$'
end

#editing_locked?Boolean

Returns:

  • (Boolean)


245
246
247
# File 'app/models/outgoing_payment.rb', line 245

def editing_locked?
  !draft?
end

#generate_checkObject



272
273
274
275
276
277
278
279
280
281
282
# File 'app/models/outgoing_payment.rb', line 272

def generate_check
  OutgoingPayment.transaction do
    outgoing_payment_items.group_by(&:payee).each do |payee, outgoing_payment_items|
      check_number = .get_next_check_number
      address = self.address.nil? ? OutgoingPayment.billing_address(payee, outgoing_payment_items).try(:full_address, false, ', ') : self.address.try(:full_address, false, ', ')
      check = Check.create!(outgoing_payment: self, payee: payee.full_name, check_number:, bank_account:, address:, date: payment_date, amount: outgoing_payment_items.sum(&:amount))
      generate_check_pdf(check)
    end
    trigger_check_generated
  end
end

#generate_check_pdf(check) ⇒ Object



284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'app/models/outgoing_payment.rb', line 284

def generate_check_pdf(check)
  result = Pdf::Document::Check.new(check:, outgoing_payment: self).call
  path = Upload.temp_location(check.file_name)
  File.open(path, 'wb') do |file|
    file.write result.pdf
    file.flush
    file.fsync
  end
  check.uploads.where(category: 'check').destroy_all
  upload = Upload.uploadify(path, 'check', check, check.file_name)
  check.uploads.reload
  upload
end

#has_active_background_job?Boolean

Returns:

  • (Boolean)


233
234
235
# File 'app/models/outgoing_payment.rb', line 233

def has_active_background_job?
  background_job&.active?
end

#ledger_entriesActiveRecord::Relation<LedgerEntry>

Returns:

See Also:



81
# File 'app/models/outgoing_payment.rb', line 81

has_many :ledger_entries, through: :ledger_transactions

#ledger_transactionsActiveRecord::Relation<LedgerTransaction>

Returns:

See Also:



80
# File 'app/models/outgoing_payment.rb', line 80

has_many :ledger_transactions

#outgoing_payment_itemsActiveRecord::Relation<OutgoingPaymentItem>

Returns:

See Also:



79
# File 'app/models/outgoing_payment.rb', line 79

has_many :outgoing_payment_items, inverse_of: :outgoing_payment, dependent: :destroy

#payeeObject



229
230
231
# File 'app/models/outgoing_payment.rb', line 229

def payee
  outgoing_payment_items.empty? ? nil : outgoing_payment_items.first.payee
end

#payment_items_applied?Boolean

Returns:

  • (Boolean)


241
242
243
# File 'app/models/outgoing_payment.rb', line 241

def payment_items_applied?
  outgoing_payment_items.sum(:amount) == amount
end


306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
# File 'app/models/outgoing_payment.rb', line 306

def print_check(current_user, regenerate_pdf: false)
  OutgoingPayment.transaction do
    # generate the checks if we don't already have them
    if checks.empty?
      generate_check
    elsif regenerate_pdf
      checks.each do |check|
        Rails.logger.info "Regenerating PDF for outgoing_payment id #{id}, check id #{check.id}"
        generate_check_pdf(check)
      end
    end
    printed_checks = checks.reload # forcing a reload here, in case new checks were generated
    pdfs = printed_checks.map { |c| c.uploads.first }
    check_numbers = printed_checks.collect(&:check_number)

    printed_checks.each { |c| c.record_print(current_user) }

    trigger_check_printed if printed_checks.any?

    # If more than one check generated, combine them
    if pdfs.length > 1
      # Combine
      check_temp_path = Upload.temp_location("check_#{check_numbers.join('_')}.pdf")
      PdfTools.combine(pdfs, output_file_path: check_temp_path)
    else
      pdfs.first
    end
  end
end

#reverse(date) ⇒ Object



347
348
349
350
351
352
353
354
355
# File 'app/models/outgoing_payment.rb', line 347

def reverse(date)
  raise 'Reversal date required' if date.nil?

  OutgoingPayment.transaction do
    self.reversal_date = date
    self.state_event = 'void'
    save!
  end
end

#supplierParty

Returns:

See Also:



75
# File 'app/models/outgoing_payment.rb', line 75

belongs_to :supplier, class_name: 'Party', inverse_of: :outgoing_payments, optional: true

#supplier_nameObject



255
256
257
# File 'app/models/outgoing_payment.rb', line 255

def supplier_name
  supplier.try(:full_name)
end

#to_sObject



173
174
175
# File 'app/models/outgoing_payment.rb', line 173

def to_s
  "OutgoingPayment ##{reference_number}"
end