Class: LandedCost

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

Overview

== Schema Information

Table name: landed_costs
Database name: primary

id :integer not null, primary key
currency :string(255)
estimated :boolean default(FALSE)
quantity :integer
state :string(255)
total_landed_cost :decimal(11, 5)
total_weight :float
unit_landed_cost :decimal(11, 5)
unit_weight :float
created_at :datetime
updated_at :datetime
carrier_id :integer
creator_id :integer
purchase_order_id :integer
purchase_order_item_id :integer
shipment_receipt_id :integer
shipment_receipt_item_id :integer
updater_id :integer

Indexes

idx_purchase_order_id (purchase_order_id)
poi_id_state_estimated (purchase_order_item_id,state,estimated)
sr_id_state_estimated (shipment_receipt_id,state,estimated)
sri_id_estimated (shipment_receipt_item_id,estimated)
state_sri_id (state,shipment_receipt_item_id)

Constant Summary

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

#quantityObject (readonly)

validates_numericality_of :unit_landed_cost, :total_landed_cost, :greater_than_or_equal_to => 0

Validations:



51
# File 'app/models/landed_cost.rb', line 51

validates :quantity, numericality: { greater_than: 0 }

Class Method Details

.activeActiveRecord::Relation<LandedCost>

A relation of LandedCosts that are active. Active Record Scope

Returns:

See Also:



48
# File 'app/models/landed_cost.rb', line 48

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

.actualActiveRecord::Relation<LandedCost>

A relation of LandedCosts that are actual. Active Record Scope

Returns:

See Also:



47
# File 'app/models/landed_cost.rb', line 47

scope :actual, -> { where(state: 'created', estimated: false) }

.estimatedActiveRecord::Relation<LandedCost>

A relation of LandedCosts that are estimated. Active Record Scope

Returns:

See Also:



46
# File 'app/models/landed_cost.rb', line 46

scope :estimated, -> { where(state: 'created', estimated: true) }

Instance Method Details

#carrierSupplier

Returns:

See Also:



40
# File 'app/models/landed_cost.rb', line 40

belongs_to :carrier, class_name: 'Supplier', optional: true

#currency_symbolObject



190
191
192
# File 'app/models/landed_cost.rb', line 190

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

#item_ledger_entriesActiveRecord::Relation<ItemLedgerEntry>

Returns:

See Also:



41
# File 'app/models/landed_cost.rb', line 41

has_many :item_ledger_entries

#ledger_transactionsActiveRecord::Relation<LedgerTransaction>

Returns:

See Also:



42
# File 'app/models/landed_cost.rb', line 42

has_many :ledger_transactions

#purchase_orderPurchaseOrder



36
# File 'app/models/landed_cost.rb', line 36

belongs_to :purchase_order, optional: true

#purchase_order_itemPurchaseOrderItem



37
# File 'app/models/landed_cost.rb', line 37

belongs_to :purchase_order_item, optional: true

#reverse_ledgersObject



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'app/models/landed_cost.rb', line 67

def reverse_ledgers
  transaction do
     = LedgerCompanyAccount.(purchase_order.company_id, RECEIPTS_NOT_VOUCHERED_ACCOUNT)
     = LedgerCompanyAccount.(purchase_order.company_id, FREIGHT_ON_PURCHASING_ACCOUNT)
    business_unit = purchase_order.store.company.default_business_unit
     = LedgerCompanyAccount.(purchase_order.company_id, purchase_order.store.)
     = LedgerCompanyAccount.(purchase_order.company_id, purchase_order.store.)

    raise 'Unable to find Purchased Finished Goods company account' if .nil?
    raise 'Unable to find Services-Pending Fulfillment Debit company account' if .nil?
    raise 'Unable to find Receipts Not Vouchered company account' if .nil?
    raise 'Unable to find Freight on Purchasing company account' if .nil?
    raise 'Unable to find default business unit' if business_unit.nil?

     = if purchase_order_item.item.present? and purchase_order_item.item.tax_class == 'svc'
                       
                     else
                       
                     end

    existing_ledger_transaction = ledger_transactions.first
    existing_ledger_entry = existing_ledger_transaction&.ledger_entries&.first

    # use 2-decimal adjusted total to mirror forward posting and avoid rounding residue
    total_landed_cost_adjusted = total_landed_cost.round(2)

    # for all POs need to
    # + Purchased Finished Goods (1410)
    # - Receipts Not Vouchered (4120)
    # or if we don't have the total qty in stock any more
    # + Freight on Purchasing (6261)
    # Use the original entry's company_exchange_rate on the transaction so that
    # the LedgerEntry#set_company_amount callback (which runs after clear_exchange_rates
    # wipes entry-level rates) picks up the original rate instead of looking up a new
    # rate for the reversal date. This keeps the ledger entries consistent with the
    # item ledger entry, which also uses the original rate.
    original_exchange_rate = existing_ledger_entry&.company_exchange_rate || existing_ledger_transaction.exchange_rate

    transaction = LedgerTransaction.new(transaction_type: 'PO_LANDED_COST',
                                        transaction_date: Date.current,
                                        currency: currency,
                                        company: purchase_order.company,
                                        landed_cost: self,
                                        exchange_rate: original_exchange_rate)

    transaction.ledger_entries << LedgerEntry.new(ledger_company_account_id: .id, currency: currency, amount: total_landed_cost_adjusted, company_exchange_rate: existing_ledger_entry&.company_exchange_rate,
consolidated_exchange_rate: existing_ledger_entry&.consolidated_exchange_rate)

    store_item = StoreItem.where(store_id: purchase_order.store_id, item_id: purchase_order_item.item_id, location: 'AVAILABLE').first

    existing_item_ledger_entry = item_ledger_entries.first
    qty_originally_applied = existing_item_ledger_entry.nil? ? 0 : existing_item_ledger_entry.quantity_eval
    qty_to_gl = quantity - qty_originally_applied
    qty_to_apply = qty_originally_applied
    qty_currently_in_stock = store_item.qty_on_hand
    running_total = total_landed_cost_adjusted

    if qty_to_gl > 0
      # the amount we originally added to the g/l needs to go back to the g/l
      if qty_to_gl == quantity
        amount = total_landed_cost_adjusted
      else
        calculated_amount = (qty_to_gl * unit_landed_cost).round(2)
        amount = calculated_amount > running_total ? running_total : calculated_amount
      end
      transaction.ledger_entries << LedgerEntry.new(ledger_company_account_id: .id, currency: currency, amount: -amount, business_unit_id: business_unit.id,
company_exchange_rate: existing_ledger_entry&.company_exchange_rate, consolidated_exchange_rate: existing_ledger_entry&.consolidated_exchange_rate)
      running_total -= amount
    end

    # need to check we have the number of items we originally processed in stock still
    if qty_to_apply > qty_currently_in_stock
      remainder = qty_to_apply - qty_currently_in_stock
      qty_to_apply = qty_currently_in_stock
      if remainder > 0
        # we don't have it all in stock, so remainder needs to be added to the g/l too
        calculated_amount = (remainder * unit_landed_cost).round(2)
        amount = calculated_amount > running_total ? running_total : calculated_amount
        transaction.ledger_entries << LedgerEntry.new(ledger_company_account_id: .id, currency: currency, amount: -amount, business_unit_id: business_unit.id,
company_exchange_rate: existing_ledger_entry&.company_exchange_rate, consolidated_exchange_rate: existing_ledger_entry&.consolidated_exchange_rate)
        running_total -= amount
      end
    end

    if qty_to_apply > 0
      transaction.ledger_entries << LedgerEntry.new(ledger_company_account_id: .id, currency: currency, amount: -running_total, company_exchange_rate: existing_ledger_entry&.company_exchange_rate,
consolidated_exchange_rate: existing_ledger_entry&.consolidated_exchange_rate)
    elsif running_total != 0
      # No inventory re-application: ensure we zero out any rounding residue by balancing to Freight on Purchasing
      transaction.ledger_entries << LedgerEntry.new(ledger_company_account_id: .id, currency: currency, amount: -running_total, business_unit_id: business_unit.id,
company_exchange_rate: existing_ledger_entry&.company_exchange_rate, consolidated_exchange_rate: existing_ledger_entry&.consolidated_exchange_rate)
    end

    transaction.save!

    if currency == purchase_order.company.currency
      currency_to_use = currency
      unit_cost_to_use = unit_landed_cost
      total_cost_to_use = unit_landed_cost * -qty_to_apply
    else
      # currencies don't match so we need to convert to company currency, use original exchange rate
      rate = existing_ledger_entry&.company_exchange_rate
      currency_to_use = purchase_order.company.currency
      unit_cost_to_use = unit_landed_cost * rate
      total_cost_to_use = (unit_landed_cost * -qty_to_apply) * rate
    end

    # when a landed cost is voided we need to reverse the item ledger entry
    ItemLedgerEntry.create!(category: 'PO_LANDED_COST',
                           store: purchase_order.store,
                           item: purchase_order_item.item,
                           gl_date: Date.current,
                           quantity: 0,
                           quantity_eval: -qty_to_apply,
                           currency: currency_to_use,
                           unit_cost: unit_cost_to_use,
                           total_cost: total_cost_to_use,
                           landed_cost: self,
                           location: shipment_receipt.location,
                           ledger_transaction: transaction)
  end
end

#shipment_receiptShipmentReceipt



39
# File 'app/models/landed_cost.rb', line 39

belongs_to :shipment_receipt, optional: true

#shipment_receipt_itemShipmentReceiptItem



38
# File 'app/models/landed_cost.rb', line 38

belongs_to :shipment_receipt_item, optional: true