Class: LedgerEntry

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

Overview

== Schema Information

Table name: ledger_entries
Database name: primary

id :integer not null, primary key
amount :decimal(12, 2)
bank_date :date
company_amount :decimal(12, 2)
company_exchange_rate :float
consolidated_amount :decimal(12, 2)
consolidated_exchange_rate :float
currency :string(3) not null
description :string(255)
reconciled :boolean default(FALSE), not null
reconciled_at :datetime
created_at :datetime
updated_at :datetime
business_unit_id :integer
creator_id :integer
ledger_company_account_id :integer not null
ledger_detail_project_id :integer
ledger_transaction_id :integer not null
reconciled_by_id :integer
updater_id :integer

Indexes

by_buid_ltid (business_unit_id,ledger_transaction_id)
by_lcaid_buid_ltid (ledger_company_account_id,business_unit_id,ledger_transaction_id)
by_reconciled_and_account (reconciled,ledger_company_account_id)
idx_lt_id_lca_id (ledger_transaction_id,ledger_company_account_id)
index_ledger_entries_on_bank_date (bank_date)
lca_id_lt_id (ledger_company_account_id,ledger_transaction_id)
ldp_id_bu_id_lt_id (ledger_detail_project_id,business_unit_id,ledger_transaction_id)
lt_id_reconciled (ledger_transaction_id,reconciled)

Constant Summary collapse

LOCKED_ATTRIBUTES =

Columns that are immutable once a LedgerTransaction has been
posted — the Models::Auditable concern raises if any of these
change on a posted entry. Bank-rec fields are excluded so the
bank reconciliation flow can still flip #reconciled.

["ledger_transaction_id", "ledger_company_account_id", "currency", "amount", "company_exchange_rate", "company_amount",
"consolidated_exchange_rate", "consolidated_amount", "business_unit_id", "ledger_detail_project_id"]
BANK_REC_ATTRIBUTES =

Subset of attributes the bank-reconciliation UI is allowed to
mutate after a transaction is posted (see LOCKED_ATTRIBUTES).

["reconciled", "bank_date"]

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

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::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Class Method Details

.filter(params) ⇒ ActiveRecord::Relation<LedgerEntry>

Search scope for the GL entries report. Same shape as
LedgerTransaction.filter but joins-and-selects per-entry
columns so rows render straight from a single query.

Parameters:

  • params (Hash, ActionController::Parameters)

Returns:



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'app/models/ledger_entry.rb', line 181

def self.filter(params)
  # ledger_entries = LedgerEntry.select("distinct(ledger_entries.*)").with_transaction_and_resource.order("ledger_transactions.transaction_date desc, ledger_transactions.transaction_number desc, ledger_entries.id desc")
  params[:transaction_type] = params[:transaction_type].map(&:presence).compact if params[:transaction_type].present?
  # params[:reconciled] = params[:reconciled].to_b if params[:reconciled].present?
  params[:payment_type] = params[:payment_type].map(&:presence).compact if params[:payment_type].present?
  params[:company_account_id] = (params[:company_account_id].map(&:presence).compact).map!(&:to_i) if params[:company_account_id].present?
  ledger_entries = LedgerEntry.left_outer_joins(ledger_transaction: %i[receipt outgoing_payment])
                              .select("ledger_entries.*,ledger_transactions.transaction_number,ledger_transactions.transaction_type,ledger_transactions.company_id,ledger_transactions.transaction_date,ledger_transactions.description,ledger_transactions.currency,outgoing_payments.category,receipts.category")
                              .order("ledger_transactions.transaction_date desc, ledger_transactions.transaction_number desc, ledger_entries.id desc")
  ledger_entries = ledger_entries.where(ledger_transactions: { transaction_type: params[:transaction_type] }) if params[:transaction_type].present?
  # ledger_entries = ledger_entries.where("ledger_entries.reconciled = ?", params[:reconciled]) if params[:reconciled] == false || params[:reconciled] == true
  ledger_entries = ledger_entries.where(OutgoingPayment[:category].in(params[:payment_type].collect { |pt| pt.downcase.underscore }).or(Receipt[:category].in(params[:payment_type]))) if params[:payment_type].present?
  if params[:transaction_date_gteq].present? or params[:transaction_date_lteq].present?
    start_date = params[:transaction_date_gteq].present? ? params[:transaction_date_gteq] : '2000-01-01'
    end_date = params[:transaction_date_lteq].present? ? params[:transaction_date_lteq] : Time.current.utc.to_date.to_fs(:db)
    ledger_entries = ledger_entries.where(ledger_transactions: { transaction_date: start_date.to_date..end_date.to_date })
  end
  if params[:bank_date_gteq].present? or params[:bank_date_lteq].present?
    bank_start_date = params[:bank_date_gteq].present? ? params[:bank_date_gteq] : '2000-01-01'
    bank_end_date = params[:bank_date_lteq].present? ? params[:bank_date_lteq] : Time.current.utc.to_date.to_fs(:db)
    ledger_entries = ledger_entries.where(bank_date: bank_start_date.to_date..bank_end_date.to_date)
  end
  if params[:reconciled_date_gteq].present? or params[:reconciled_date_lteq].present?
    reconciled_start_date = params[:reconciled_date_gteq].present? ? params[:reconciled_date_gteq] : '2000-01-01'
    reconciled_end_date = params[:reconciled_date_lteq].present? ? params[:reconciled_date_lteq] : Time.current.utc.to_date.to_fs(:db)
    ledger_entries = ledger_entries.where(reconciled_at: reconciled_start_date.to_date..reconciled_end_date.to_date)
  end
  ledger_entries = ledger_entries.where(ledger_company_account_id: params[:company_account_id]) if params[:company_account_id].present?
  # ledger_entries = ledger_entries.where("ledger_transactions.transaction_number = ?", params[:transaction_number]) unless params[:transaction_number].blank?
  # ledger_entries = ledger_entries.where("ledger_transactions.company_id in (?)", params[:company_id]) unless params[:company_id].blank?
  # ledger_entries = ledger_entries.where("ledger_entries.reconciled = ?", params[:reconciled]) unless params[:reconciled].blank?
  # ledger_entries = ledger_entries.where(:receipts => {:card_type => params[:card_type]}) unless params[:card_type].blank?
  # unless params[:description].blank?
  #   description = "%#{params[:description]}%"
  #   ledger_entries = ledger_entries.where("ledger_transactions.description like ? or ledger_entries.description like ?", description, description)
  # end
  # ledger_entries = ledger_entries.where("ledger_entries.amount = ?", BigDecimal(params[:amount])) unless params[:amount].blank?
  # ledger_entries = ledger_entries.where("ledger_transactions.currency in (?)", params[:currency]) unless params[:currency].blank?
  # ledger_entries = ledger_entries.where("ledger_entries.ledger_detail_project_id in (?)", params[:project_id]) unless params[:project_id].blank?
  # ledger_entries = ledger_entries.where("ledger_entries.business_unit_id in (?)", params[:business_unit_id]) unless params[:business_unit_id].blank?
  return ledger_entries
end

.get_materials_in_transit(company_id, start_date, end_date, po_number_ref, reconciled) ⇒ Array<Hash>

Materials-in-Transit reconciliation report. Returns one row
per entry posted to the "Materials in Transit" account, with
filters for date range, company, optional PO number(s), and
reconciliation flag.

Parameters:

  • company_id (Array<Integer>, String)
  • start_date (String, Date)
  • end_date (String, Date)
  • po_number_ref (String)

    whitespace/comma-separated PO numbers

  • reconciled (String, Boolean, nil)

Returns:

  • (Array<Hash>)


296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
# File 'app/models/ledger_entry.rb', line 296

def self.get_materials_in_transit(company_id, start_date, end_date, po_number_ref, reconciled)
  conn = ActiveRecord::Base.lease_connection
  accounts = company_id.empty? ? LedgerCompanyAccount.where(name: 'Materials in Transit').pluck(:id).join(',') : LedgerCompanyAccount.where(name: 'Materials in Transit').where(company_id: company_id).first.id
   = company_id.empty? ? " where ledger_company_account_id in (#{accounts}) " : " where ledger_company_account_id = #{accounts} "
  if start_date.present? && end_date.present?
    dates_sql = " and ltr.transaction_date between #{conn.quote(start_date)} and #{conn.quote(end_date)} "
  elsif start_date.present? && end_date.empty?
    dates_sql = " and ltr.transaction_date >= #{conn.quote(start_date)} "
  end
  po_numbers = po_number_ref.strip.split(/[\s,]+/).map(&:strip).reject(&:blank?)
  description_sql = po_numbers.present? ? " and le.description similar to #{conn.quote("%(#{po_numbers.join('|')})%")} " : " "
  reconciled_sql = reconciled.present? ?  (reconciled.to_bool == true ? ' and reconciled = true ' : ' and reconciled = false ') : ' '
  sql = <<-SQL
    select ltr.id as ledger_transaction_id,ltr.transaction_number,lca.company_id,c.short_name,le.id as ledger_entry_id,le.ledger_company_account_id,acc.number as account,
    ltr.transaction_type as type,ltr.transaction_date as gl_date,le.currency,le.amount,le.company_amount,le.company_exchange_rate as exchange_rate,le.description,le.reconciled as vcr_reconciled
    from ledger_accounts acc
    inner join ledger_company_accounts lca on acc.id = lca.ledger_detail_account_id
    inner join ledger_entries le on le.ledger_company_account_id = lca.id
    inner join ledger_transactions ltr on le.ledger_transaction_id = ltr.id
    inner join companies c on lca.company_id = c.id
    #{}
    #{dates_sql}
    #{description_sql}
    #{reconciled_sql}
  SQL

  materials_in_transit = conn.execute(sql).to_a.map(&:symbolize_keys)
end

.get_receipts_not_vouchered(company_id, start_date, end_date, po_number_ref, reconciled) ⇒ Array<Hash>

Receipts-Not-Vouchered reconciliation report. Walks
shipment-receipt and landed-cost rows, joins them back to GL
entries on the "Receipts Not Vouchered" account, and tries to
pair them by amount + PO. The eight hard-coded lt.id → po_id
mappings handle a backfill of historical mis-postings.

Parameters:

  • company_id (Array<Integer>, String)
  • start_date (String, Date)
  • end_date (String, Date)
  • po_number_ref (String)
  • reconciled (String, Boolean, nil)

Returns:

  • (Array<Hash>)


337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'app/models/ledger_entry.rb', line 337

def self.get_receipts_not_vouchered(company_id, start_date, end_date, po_number_ref, reconciled)
  conn = ActiveRecord::Base.lease_connection
  accounts = company_id.empty? ? LedgerCompanyAccount.where(name: 'Receipts Not Vouchered').pluck(:id).join(',') : LedgerCompanyAccount.where(name: 'Receipts Not Vouchered').where(company_id: company_id).first.id
   = company_id.empty? ? " where ledger_company_account_id in (#{accounts}) " : " where ledger_company_account_id = #{accounts} "
  if start_date.present? && end_date.present?
    dates_sql = " and ltr.transaction_date between #{conn.quote(start_date)} and #{conn.quote(end_date)} "
  elsif start_date.present? && end_date.empty?
    dates_sql = " and ltr.transaction_date >= #{conn.quote(start_date)} "
  end
  po_numbers = po_number_ref.strip.split(/[\s,]+/).map(&:strip).reject(&:blank?)
  description_sql = po_numbers.present? ? " and (po.po_numbers similar to #{conn.quote("%(#{po_numbers.join('|')})%")} or le.description similar to #{conn.quote("%(#{po_numbers.join('|')})%")}) " : " "
  reconciled_sql = if reconciled.present?
                     reconciled.to_bool == true ? ' and reconciled = true ' : ' and reconciled = false '
                   else
                     ' '
                   end
  lca_ids = LedgerCompanyAccount.where(name: 'Receipts Not Vouchered').pluck(:id)

  sql = <<-SQL
    with receipts_tb as (
        select shipment_receipt_id,purchase_order_id,array_agg(distinct po.reference_number)::varchar as po_numbers,abs(sum(sri.total_cost)) as total_cost
        from shipment_receipt_items sri
        inner join purchase_orders po on sri.purchase_order_id = po.id
        group by shipment_receipt_id,purchase_order_id
    ), r_transactions_tb as (
        select lt.id as ledger_transaction_id,shipment_receipt_id,abs(sum(amount)) as amount
        from ledger_transactions lt
        inner join ledger_entries le on lt.id = le.ledger_transaction_id and ledger_company_account_id in (#{lca_ids.join(',')})
        where shipment_receipt_id is not null
        group by lt.id,shipment_receipt_id
    ), landed_cost_tb as (
        select lc.id as landed_cost_id,purchase_order_id,array_agg(distinct po.reference_number)::varchar as po_numbers,abs(round(sum(lc.total_landed_cost),2)) as total_cost
        from landed_costs lc
        inner join purchase_orders po on lc.purchase_order_id = po.id
        group by lc.id,purchase_order_id
    ), lc_transactions_tb as (
        select lt.id as ledger_transaction_id,landed_cost_id,abs(sum(amount)) as amount
        from ledger_transactions lt
        inner join ledger_entries le on lt.id = le.ledger_transaction_id and ledger_company_account_id in (#{lca_ids.join(',')})
        where landed_cost_id is not null
        group by lt.id,landed_cost_id
    ), pot_same_amount as (
        select ledger_transaction_id,count(*)
        from receipts_tb rt
        inner join r_transactions_tb tt on rt.shipment_receipt_id = tt.shipment_receipt_id and rt.total_cost = tt.amount
        group by ledger_transaction_id
        having count(*) > 1
    ), lt_same_amount as (
        select lt.id as ledger_transaction_id,shipment_receipt_id,abs(sum(amount)) as amount,
                case when lt.id = 44932 then 522
                when lt.id = 44933 then 527
                when lt.id = 513103 then 5686
                when lt.id = 513104 then 5687
                when lt.id = 618809 then 6513
                when lt.id = 618810 then 6779
                when lt.id = 619478 then 6642
                when lt.id = 619479 then 6685 else null end as purchase_order_id
        from ledger_transactions lt
        inner join ledger_entries le on lt.id = le.ledger_transaction_id and ledger_company_account_id in (#{lca_ids.join(',')})
        where lt.id in (select ledger_transaction_id from pot_same_amount)
        group by lt.id,shipment_receipt_id
    ), purchase_orders_transactions as (
        select ledger_transaction_id,po_numbers
        from receipts_tb rt
        inner join r_transactions_tb tt on rt.shipment_receipt_id = tt.shipment_receipt_id and rt.total_cost = tt.amount
        where ledger_transaction_id not in (select ledger_transaction_id from pot_same_amount)
        union all
        select ledger_transaction_id,po_numbers
        from lt_same_amount lsa
        inner join receipts_tb rt on lsa.shipment_receipt_id = rt.shipment_receipt_id and lsa.purchase_order_id = rt.purchase_order_id
        union all
        select ledger_transaction_id,po_numbers
        from landed_cost_tb lct
        left join lc_transactions_tb ltt on lct.landed_cost_id = ltt.landed_cost_id and lct.total_cost = ltt.amount
    )
    select ltr.id as ledger_transaction_id,ltr.transaction_number,lca.company_id,c.short_name,le.id as ledger_entry_id,le.ledger_company_account_id,acc.number as account,
    ltr.transaction_type as type,ltr.transaction_date as gl_date,le.currency,ltr.exchange_rate,le.amount,le.company_amount,po.po_numbers,le.description,le.reconciled as po_reconciled
    from ledger_accounts acc
    inner join ledger_company_accounts lca on acc.id = lca.ledger_detail_account_id
    inner join ledger_entries le on le.ledger_company_account_id = lca.id
    inner join ledger_transactions ltr on le.ledger_transaction_id = ltr.id
    inner join companies c on lca.company_id = c.id
    left join purchase_orders_transactions po on po.ledger_transaction_id = ltr.id
    #{}
    #{dates_sql}
    #{description_sql}
    #{reconciled_sql}
  SQL

  receipts_not_vouchered = conn.execute(sql).to_a.map(&:symbolize_keys)
end

.with_transaction_and_resourceActiveRecord::Relation<LedgerEntry>

A relation of LedgerEntries that are with transaction and resource. Active Record Scope

Returns:

See Also:



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

scope :with_transaction_and_resource, -> { includes([{:ledger_transaction => [:item_ledger_entries, :company, {:invoice => :customer}, {:receipt => :customer}, :shipment_receipt, :landed_cost, {:voucher => :supplier}, {:credit_memo => :customer}, {:outgoing_payment => [:supplier, :checks]}], :ledger_company_account => [:ledger_detail_account, :company]}, :ledger_detail_project, :business_unit]) }

Instance Method Details

#bank_rec_date_not_closedvoid

This method returns an undefined value.

Validation: block changing BANK_REC_ATTRIBUTES when the new
bank_date falls inside a closed BANK_REC period. Catches
the inverse case of #bank_rec_edit_allowed (newly setting a
closed date rather than touching a previously-closed one).



275
276
277
278
279
280
281
282
283
# File 'app/models/ledger_entry.rb', line 275

def bank_rec_date_not_closed
  lt = self.ledger_transaction
  unless lt.nil? or lt.company_id.nil? or self.bank_date.nil?
    if self.changed? and self.changes.keys.any? {|attr| BANK_REC_ATTRIBUTES.include?(attr)}
      lcls = LedgerClosingPeriod.company_in_all_or(lt.company_id).transaction_type_in("BANK_REC").where(close_to: self.bank_date..)
      errors.add :base, "Bank reconciliation is closed up to #{lcls.first.close_to.to_fs(:crm_default)} for #{lt.company.short_name}." if lcls.any?
    end
  end
end

#bank_rec_edit_allowedvoid

This method returns an undefined value.

Validation: prevent un-reconciling or moving the bank-rec date
of an entry whose old bank_date is already inside a closed
BANK_REC period — accountants can't retroactively re-open
closed reconciliations.



258
259
260
261
262
263
264
265
266
267
# File 'app/models/ledger_entry.rb', line 258

def bank_rec_edit_allowed
  lt = self.ledger_transaction
  unless lt.nil? or lt.company_id.nil? or self.bank_date_was.nil?
    if (self.reconciled_changed? and self.reconciled_was == true) or self.bank_date_changed?
      # it had previously been reconciled, so check the books are not closed for bank recs on the old date
      lcls = LedgerClosingPeriod.company_in_all_or(lt.company_id).transaction_type_in("BANK_REC").where(close_to: (self.bank_date_was || self.bank_date)..)
      errors.add :base, "Bank reconciliation details cannot be changed as they have already been closed up to #{lcls.first.close_to.to_fs(:crm_default)} for #{lt.company.short_name}." if lcls.any?
    end
  end
end

#business_unitBusinessUnit

Business unit this entry is attributed to.



60
# File 'app/models/ledger_entry.rb', line 60

belongs_to :business_unit, optional: true

#business_unit_belongs_to_same_companyvoid

This method returns an undefined value.

Validation: a BusinessUnit tag has to belong to the same
Company as the entry's account, otherwise BU rollups would
leak across companies.



169
170
171
172
173
# File 'app/models/ledger_entry.rb', line 169

def business_unit_belongs_to_same_company
  if business_unit_id.present? and .present? and business_unit.company_id != .company_id
    errors.add(:business_unit_id, "must belong to same company as the chosen account")
  end
end

#currency_symbolString

ISO currency symbol ($, , C$) for the entry's currency.

Returns:

  • (String)


81
82
83
# File 'app/models/ledger_entry.rb', line 81

def currency_symbol
  Money::Currency.new(currency).symbol
end

#edit_allowedvoid

This method returns an undefined value.

Validation: same closing-period check as #period_not_closed
but only fires when one of LOCKED_ATTRIBUTES is being
changed (so untouched re-saves still pass).



242
243
244
245
246
247
248
249
250
# File 'app/models/ledger_entry.rb', line 242

def edit_allowed
  lt = self.ledger_transaction
  unless lt.nil? or lt.company_id.nil? or lt.transaction_type.nil? or lt.transaction_date.nil?
    if self.changed? and self.changes.keys.any? {|attr| LOCKED_ATTRIBUTES.include?(attr)}
      lcls = LedgerClosingPeriod.company_in_all_or(lt.company_id).transaction_type_in_all_or(lt.transaction_type).where(close_to: lt.transaction_date..)
      errors.add :base, "Period is closed for #{lt.transaction_type} for #{lt.company.short_name} on #{lcls.first.close_to.to_fs(:crm_default)}." if lcls.any?
    end
  end
end

#ledger_company_accountLedgerCompanyAccount

GL account this entry posts to.



54
# File 'app/models/ledger_entry.rb', line 54

belongs_to :ledger_company_account, :inverse_of => :ledger_entries, optional: true

#ledger_detail_projectLedgerDetailProject

Project this entry is attributed to.



58
# File 'app/models/ledger_entry.rb', line 58

belongs_to :ledger_detail_project, :inverse_of => :ledger_entries, optional: true

#ledger_transactionLedgerTransaction

Transaction this entry belongs to.



56
# File 'app/models/ledger_entry.rb', line 56

belongs_to :ledger_transaction, :inverse_of => :ledger_entries, optional: true

#period_not_closedvoid

This method returns an undefined value.

Validation: block creating a new entry whose transaction date
falls inside a LedgerClosingPeriod for the company /
transaction type combination.



229
230
231
232
233
234
235
# File 'app/models/ledger_entry.rb', line 229

def period_not_closed
  lt = self.ledger_transaction
  unless lt.nil? or lt.company_id.nil? or lt.transaction_type.nil? or lt.transaction_date.nil?
    lcls = LedgerClosingPeriod.company_in_all_or(lt.company_id).transaction_type_in_all_or(lt.transaction_type).where(close_to: lt.transaction_date..)
    errors.add :base, "Period is closed for #{lt.transaction_type} for #{lt.company.short_name} up to #{lcls.first.close_to.to_fs(:crm_default)}." if lcls.any?
  end
end

#reconciled_byEmployee

Employee who reconciled this entry during bank reconciliation.

Returns:

See Also:



62
# File 'app/models/ledger_entry.rb', line 62

belongs_to :reconciled_by, :class_name => "Employee", optional: true

#requires_business_unit?Boolean

Whether the linked LedgerDetailAccount flags entries as
business-unit-tagged (P&L accounts) rather than free-form
(balance-sheet accounts).

Returns:

  • (Boolean)


90
91
92
# File 'app/models/ledger_entry.rb', line 90

def requires_business_unit?
  .present? && .&.requires_business_unit?
end

#set_company_amountvoid

This method returns an undefined value.

Compute company_amount and company_exchange_rate, converting
amount from the entry currency into the owning Company's
functional currency. Re-runs whenever amount, account, or
transaction date / rate changes; otherwise just patches drift
if amount × rate no longer equals the stored amount.



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'app/models/ledger_entry.rb', line 112

def set_company_amount
  unless self.currency.nil? or self.amount.nil? or self..nil?
    if self.new_record? or self.company_amount.nil? or self. or self.amount_changed? or (self.ledger_transaction and (self.ledger_transaction.transaction_date_changed? or self.ledger_transaction.exchange_rate_changed?))
      if self.currency != self..company.currency
        # the currency of the entry is different to the owning company's currency
        # need to set the exchange rate and record the company amount
        exchange_rate = self.ledger_transaction.exchange_rate || ExchangeRate.get_exchange_rate(self.currency, self..company.currency, self.ledger_transaction.transaction_date)
        self.company_exchange_rate = exchange_rate
        self.company_amount = (self.amount * self.company_exchange_rate).round(2)
      else
        # it's the same so we just copy the amounts and set the exchange rate as 1
        self.company_exchange_rate = 1.0
        self.company_amount = self.amount
      end
    elsif self.amount.present? and self.company_exchange_rate.present? and self.company_amount != self.amount * self.company_exchange_rate
      self.company_amount = (self.amount * self.company_exchange_rate).round(2)
    end
  end
end

#set_consolidated_amountvoid

This method returns an undefined value.

Compute consolidated_amount — the entry projected into
CONSOLIDATED_CURRENCY (USD) for group reporting. Reuses the
company-amount conversion when the owning company is already
in USD; otherwise pulls a fresh ExchangeRate for the
transaction date.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'app/models/ledger_entry.rb', line 139

def set_consolidated_amount
  unless self.currency.nil? or self.amount.nil?
    if self.new_record? or self.consolidated_amount.nil? or self. or self.amount_changed? or (self.ledger_transaction and self.ledger_transaction.transaction_date_changed?)
      if self.currency != CONSOLIDATED_CURRENCY && self..company.currency == CONSOLIDATED_CURRENCY && self.company_amount != nil
        # the currency of the entry is different to the consolidated currency, but the linked account is the same currency
        # so we can just copy the company amount if it's already
        self.consolidated_exchange_rate = self.company_exchange_rate
        self.consolidated_amount = self.company_amount
      elsif self.currency != CONSOLIDATED_CURRENCY
        # the currency of the entry is different to the owning company's currency
        # need to set the exchange rate and record the company amount
        exchange_rate = ExchangeRate.get_exchange_rate(self.currency, CONSOLIDATED_CURRENCY, self.ledger_transaction.transaction_date)
        self.consolidated_exchange_rate = exchange_rate
        self.consolidated_amount = (self.amount * self.consolidated_exchange_rate).round(2)
      else
        # it's the same so we just copy the amounts and set the exchange rate as 1
        self.consolidated_exchange_rate = 1.0
        self.consolidated_amount = self.amount
      end
    elsif self.amount.present? and self.consolidated_exchange_rate.present? and self.consolidated_amount != self.amount * self.consolidated_exchange_rate
      self.consolidated_amount = (self.amount * self.consolidated_exchange_rate).round(2)
    end
  end
end

#set_currencyvoid

This method returns an undefined value.

Inherit currency from the parent LedgerTransaction on create
(or when the transaction's currency is changed mid-edit).
Suppresses NoMethodError when the entry has no transaction yet.



99
100
101
102
103
# File 'app/models/ledger_entry.rb', line 99

def set_currency
  if self.new_record? or (self.ledger_transaction and self.ledger_transaction.currency_changed?)
    self.currency = self.ledger_transaction.currency rescue nil
  end
end