Class: Receipt

Overview

== Schema Information

Table name: receipts
Database name: primary

id :integer not null, primary key
amount :decimal(8, 2)
approved_for_refund :boolean
auth_code :string(255)
card_type :string(255)
category :string(255)
currency :string(255)
email :string
exchange_rate :float
exported :boolean default(FALSE), not null
gl_date :date
hold_for_review :boolean default(FALSE), not null
jde_number :string(255)
receipt_date :date
reference :string(255)
remark :string(255)
state :string(255)
created_at :datetime
updated_at :datetime
authorization_id :integer
bank_account_id :integer
company_id :integer
creator_id :integer
customer_id :integer
deleter_id :integer
edi_transaction_id :string
order_id :integer
payment_id :integer
updater_id :integer

Indexes

idx_customer_id_receipt_date (customer_id,receipt_date)
index_receipts_on_bank_account_id (bank_account_id)
index_receipts_on_company_id (company_id)
index_receipts_on_edi_transaction_id (edi_transaction_id) UNIQUE
index_receipts_on_hold_for_review (hold_for_review)
index_receipts_on_payment_id (payment_id)
index_receipts_on_state (state)
receipts_authorization_id_index (authorization_id)

Foreign Keys

fk_rails_... (authorization_id => legacy_authorizations.id)
fk_rails_... (bank_account_id => bank_accounts.id)
fk_rails_... (company_id => companies.id)
fk_rails_... (customer_id => parties.id)

Defined Under Namespace

Classes: SubmitToTaxjar

Constant Summary collapse

CATEGORIES =

Categories.

['ACH', 'Cash', 'Check', 'Credit Card', 'Echeck', 'Wire Transfer', 'Amazon Pay', 'Non-Cash', 'PayPal', 'Payment Link'].freeze
CARD_TYPES =

Recognised card types.

%w[american_express discover master visa vault].freeze

Constants included from ReceiptValidations

ReceiptValidations::WYUS_COMPANY_ID

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

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 ReceiptFinancials

#credit_applied_to_summary, #detail_document_balances, #funds_assigned, #no_funds_applied?, #unapplied_funds

Methods included from ReceiptPayment

#authorization_code, #category_for_authorization_type, #credit_card?, #default_cc_options, #echeck?, #payment_description

Methods included from ReceiptDisplay

#card_name, #currency_symbol, #customer_name, #customer_type, #is_not_for_a_customer?, #reference_summary, #store, #to_s

Methods included from ReceiptTransmission

#billing_address, #determine_email, #first_document, #notification_emails, #primary_transmission_contact, #send_email, #transmission_contact_points

Methods included from ReceiptSpreadsheetIo

create_receipts_from_xlsx, edit_receipts_from_xlsx, validate_data_create_receipts_from_xlsx, validate_data_edit_receipts_from_xlsx

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 Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#amountObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#bank_account_idObject (readonly)



83
# File 'app/models/receipt.rb', line 83

validates :bank_account_id, presence: { unless: proc { |r| r.category == 'Non-Cash' } }

#categoryObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#company_idObject (readonly)



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

validates :amount, :company_id, :customer_id, numericality: true

#currencyObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#customer_idObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#customer_name=(value) ⇒ Object (writeonly)

Form fields - getters are defined below to derive from customer



120
121
122
# File 'app/models/receipt.rb', line 120

def customer_name=(value)
  @customer_name = value
end

#customer_type=(value) ⇒ Object (writeonly)

Form fields - getters are defined below to derive from customer



120
121
122
# File 'app/models/receipt.rb', line 120

def customer_type=(value)
  @customer_type = value
end

#gl_dateObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#new_gl_dateObject



85
# File 'app/models/receipt.rb', line 85

validates :new_gl_date, presence: { on: :update, unless: proc { |r| r.state_changed? or r.approved_for_refund? or r.only_remarks_changed? } }

#process_cc_paymentObject

Returns the value of attribute process_cc_payment.



119
120
121
# File 'app/models/receipt.rb', line 119

def process_cc_payment
  @process_cc_payment
end

#receipt_dateObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

#stateObject (readonly)



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

validates :customer_id, :category, :amount, :currency, :gl_date, :receipt_date, :state, presence: true

Class Method Details

.available_to_applyActiveRecord::Relation<Receipt>

A relation of Receipts that are available to apply. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Receipt>)

See Also:



115
# File 'app/models/receipt.rb', line 115

scope :available_to_apply, -> { where(state: %w[unapplied partially_applied], approved_for_refund: true) }

.for_company_idActiveRecord::Relation<Receipt>

A relation of Receipts that are for company id. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Receipt>)

See Also:



117
# File 'app/models/receipt.rb', line 117

scope :for_company_id, ->(company_id) { where(company_id: company_id) }

.fully_appliedActiveRecord::Relation<Receipt>

A relation of Receipts that are fully applied. Active Record Scope

Returns:

  • (ActiveRecord::Relation<Receipt>)

See Also:



116
# File 'app/models/receipt.rb', line 116

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

.states_for_selectArray<Array(String, Symbol)>

[label, value] pairs of every workflow state for select inputs.

Returns:

  • (Array<Array(String, Symbol)>)


228
229
230
# File 'app/models/receipt.rb', line 228

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

Instance Method Details

#bank_accountBankAccount



73
# File 'app/models/receipt.rb', line 73

belongs_to :bank_account, optional: true

#communicationsActiveRecord::Relation<Communication>

Returns:

See Also:



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

has_many :communications, -> { order(:id).reverse_order }, as: :resource, dependent: :nullify, inverse_of: :resource

#companyCompany

Returns:

See Also:



72
# File 'app/models/receipt.rb', line 72

belongs_to :company

#create_receipt_details(invoice, amount) ⇒ Object

Append a single Invoice-category ReceiptDetail for amount, push
the receipt's GL date forward to whichever of (current gl_date,
invoice gl_date) is later, and persist. Errors are reported to
AppSignal rather than raised so callers (e.g. EDI auto-apply) can
continue.

Parameters:

  • invoice (Invoice)
  • amount (BigDecimal, Numeric)


199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'app/models/receipt.rb', line 199

def create_receipt_details(invoice, amount)
  # Catch-up callers (Invoice#create_receipts_for_captured_payments) re-attempt
  # detail creation after the invoice may have been settled by another receipt
  # in the meantime. If the requested `amount` no longer fits in the invoice
  # balance, the `applied_amount_not_greater_than_invoice_balance` validation
  # would reject the save. Skip silently — no detail to create — instead of
  # reporting an AppSignal error for benign already-paid races (AppSignal #4120).
  if invoice.present? && amount.present? && invoice.balance.present? && amount > invoice.balance
    Rails.logger.info "[Receipt##{id}] skipping create_receipt_details: amount #{amount} exceeds invoice ##{invoice.id} balance #{invoice.balance}"
    return
  end

  effective_gl_date = [gl_date, invoice&.gl_date].compact.max
  self.new_gl_date = effective_gl_date
  receipt_details << ReceiptDetail.new(category: 'Invoice', invoice: invoice, amount: amount, gl_date: effective_gl_date)
  begin
    save!
  rescue StandardError => e
    ErrorReporting.error e, receipt_id: id, message: "Receipt details not created correctly. Receipt ID #{id}"
  end
end

#customerParty

Returns:

See Also:



71
# File 'app/models/receipt.rb', line 71

belongs_to :customer, class_name: 'Party', inverse_of: :receipts, optional: true

#edi_communication_logsActiveRecord::Relation<EdiCommunicationLog>

Returns:

See Also:



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

has_many :edi_communication_logs, through: :edi_documents

#edi_documentsActiveRecord::Relation<EdiDocument>

Returns:

See Also:



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

has_many :edi_documents, dependent: :destroy

#evaluate_taxjar_submissionObject

Re-run TaxJar submission evaluation on every active GL-Account line.
Fired from after_commit; details decide individually whether to
post or void to TaxJar.



153
154
155
# File 'app/models/receipt.rb', line 153

def evaluate_taxjar_submission
  receipt_details.non_voided.gl_accounts.each(&:evaluate_taxjar_submission)
end

#has_linked_payment_items?Boolean

Returns:

  • (Boolean)


221
222
223
# File 'app/models/receipt.rb', line 221

def has_linked_payment_items?
  outgoing_payment_items.not_voided.any?
end

#hold_for_review?Boolean

Returns:

  • (Boolean)


238
239
240
# File 'app/models/receipt.rb', line 238

def hold_for_review?
  hold_for_review
end

#ledger_transactionsActiveRecord::Relation<LedgerTransaction>

Returns:

See Also:



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

has_many :ledger_transactions, dependent: :destroy

#only_remarks_changed?Boolean

Returns:

  • (Boolean)


157
158
159
160
# File 'app/models/receipt.rb', line 157

def only_remarks_changed?
  all_changes = changes.keys.concat(receipt_details.map { |rd| rd.changes.keys }).flatten
  all_changes.any? and all_changes.all?('remark')
end

#outgoing_payment_itemsActiveRecord::Relation<OutgoingPaymentItem>

Returns:

See Also:



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

has_many :outgoing_payment_items, inverse_of: :receipt, dependent: :destroy

#paymentPayment

Returns:

See Also:



70
# File 'app/models/receipt.rb', line 70

belongs_to :payment, optional: true

#receipt_detailsActiveRecord::Relation<ReceiptDetail>

Returns:

See Also:



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

has_many :receipt_details, inverse_of: :receipt, dependent: :destroy

#regenerate_invoice_pdfObject

Re-render every linked Invoice PDF asynchronously so the new
payment appears on the customer-facing document.



234
235
236
# File 'app/models/receipt.rb', line 234

def regenerate_invoice_pdf
  receipt_details.where.not(invoice_id: nil).find_each { |rd| InvoicePdfGenerationWorker.perform_async(rd.invoice_id) }
end

#void_receipt(reversal_dates, use_original_date: false, use_specific_date: nil) ⇒ Object

reversal_dates: a hash of transaction id containing date to use
use_original_date: use the ledger transaction original date
use_specific_date: use a specific date



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
# File 'app/models/receipt.rb', line 165

def void_receipt(reversal_dates, use_original_date: false, use_specific_date: nil)
  Receipt.transaction do
    # void the receipt details
    receipt_details.each(&:void!)

    # reverse the ledger transactions
    ledger_transactions.each do |lt|
      date_to_use = use_specific_date
      date_to_use ||= lt.transaction_date if use_original_date
      date_to_use ||= reversal_dates[lt.id.to_s]
      lt.reverse(date_to_use)
    end

    # unoffset the credit memos and unpay the receipts
    receipt_details.each do |rd|
      if (cm = rd.credit_memo.presence) && !cm.funds_fully_offset?
        cm.unoffset!
      end
      rd.invoice.presence&.unpay
    end

    # void the receipt
    trigger_void!
  end
end