Class: LedgerTransactionsController

Inherits:
CrmController show all
Includes:
Controllers::Showable
Defined in:
app/controllers/ledger_transactions_controller.rb

Overview

== Schema Information

Table name: ledger_transactions

id :integer not null, primary key
transaction_number :integer not null
transaction_type :string(255) not null
transaction_date :date not null
description :string(255)
currency :string(3) not null
exchange_rate :float
creator_id :integer
updater_id :integer
created_at :datetime
updated_at :datetime
company_id :integer
invoice_id :integer
receipt_id :integer
shipment_receipt_id :integer
landed_cost_id :integer
voucher_id :integer
credit_memo_id :integer
outgoing_payment_id :integer
reversed_transaction_id :integer
supplier_id :integer

Constant Summary

Constants included from Controllers::ReferenceFindable

Controllers::ReferenceFindable::ID_EMBEDDED_PATTERNS

Constants included from Controllers::AnalyticsEvents

Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY

Constants included from Controllers::ErrorRendering

Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES

Instance Method Summary collapse

Methods included from Controllers::Showable

#perform_show, #show

Methods inherited from CrmController

#access_denied, #context_id, #context_object, #crm_home_path, #current_ability, #default_url_options, #download_temp, #get_tempfile_path_for_download, #init_status_job_collector, #initialize_crm_lazy_chunks, #persist_enqueued_status_jobs, #record_not_found, #redirect_to_job_or_fallback, #render_edit_action, #set_context, #set_download_path, #stash_file_for_temp_download, #sync_admin_presence_cookie, #touch_employee_last_seen

Methods inherited from ApplicationController

#account_impersonated?, #add_to_flash, #after_sign_in_path_for, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #skip_layout_for_turbo_frame?, #stamp_impersonation_context, #tab_frame_breakout_request?, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y

Methods included from Controllers::ReturnPathHandling

#check_for_return_path, #redirect_to_return_path_or_default

Methods included from Controllers::AnalyticsEvents

#consume_queued_analytics_events, #registration_lead_type, #track_event

Methods included from Controllers::DeviceDetection

#device_detector, #is_ie?

Methods included from Controllers::SubdomainDetection

#is_crm_request?, #is_www_request?, #json_request?

Methods included from Controllers::TurboSafeRedirect

#redirect_to

Methods included from Controllers::TrackingDetection

#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?

Methods included from Controllers::AcceleratedFileSending

#send_file_accelerated, #send_upload_accelerated

Methods included from Controllers::ErrorRendering

#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #render_unpermitted_parameters, #safe_referer_or_fallback

Methods included from Controllers::TurnstileVerification

#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!

Methods included from Controllers::CloudflareCaching

edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_edge_cache!, #skip_session

Methods included from Controllers::Webpackable

#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?

Methods included from Controllers::Localizable

#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale

Methods included from Controllers::Authenticable

#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak

Methods included from UrlsHelper

#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?

Instance Method Details

#business_unitsvoid

This method returns an undefined value.

Responds with the business units (id, company, identifier) as JSON for form selects.



300
301
302
303
304
305
# File 'app/controllers/ledger_transactions_controller.rb', line 300

def business_units
  @business_units = BusinessUnit.by_number
  respond_to do |format|
    format.json { render json: @business_units.map { |bu| { id: bu.id, company_id: bu.company_id, identifier: bu.number_and_name } } }
  end
end

#createObject

POST /ledger_transactions
POST /ledger_transactions.xml



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'app/controllers/ledger_transactions_controller.rb', line 137

def create
  LedgerTransaction.transaction do
    @ledger_transaction = LedgerTransaction.new(params[:ledger_transaction])

    respond_to do |format|
      if @ledger_transaction.save
        format.html { redirect_to(@ledger_transaction, info: 'Ledger transaction was successfully created.') }
        format.xml  { render xml: @ledger_transaction, status: :created, location: @ledger_transaction }
      else
        format.html { render action: 'new', status: :unprocessable_content }
        format.xml  { render xml: @ledger_transaction.errors, status: :unprocessable_content }
      end
    end
  end
end

#do_reversalvoid

This method returns an undefined value.

Reverses the ledger transaction as of params[:reversal_date]. Re-renders the form
with HTTP 422 when the date is missing or the reversal fails.



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'app/controllers/ledger_transactions_controller.rb', line 216

def do_reversal
  @ledger_transaction = LedgerTransaction.find(params[:id])

  if @ledger_transaction.transaction_reversal.present?
    redirect_to @ledger_transaction, error: 'Ledger Transaction has already been reversed.'
  elsif params[:reversal_date].blank?
    flash.now[:error] = 'You must specify a reversal date.'
    render :reverse, status: :unprocessable_content
  else
    begin
      @ledger_transaction.reverse(params[:reversal_date])
      redirect_to @ledger_transaction, info: 'Ledger Transaction successfully voided.'
    rescue StandardError => e
      flash.now[:error] = "Unable to void transaction, message : #{e}."
      render :reverse, status: :unprocessable_content
    end
  end
end

#do_review_reconcilevoid

This method returns an undefined value.

Saves the reviewed reconciliation journal transaction and, when marked reconciled,
flags the selected MIT/RNV ledger entries. Re-renders the review page with HTTP 422
on validation failure.



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'app/controllers/ledger_transactions_controller.rb', line 263

def do_review_reconcile
  LedgerTransaction.transaction do
    mit_selected = params[:mit_ids].present? ? LedgerEntry.where(id: params[:mit_ids].map(&:to_i)) : []
    rnv_selected = params[:rnv_ids].present? ? LedgerEntry.where(id: params[:rnv_ids].map(&:to_i)) : []
    @ledger_transaction = LedgerTransaction.new(params[:ledger_transaction])
    reconciled_count = @ledger_transaction.ledger_entries.filter_map(&:reconciled).uniq.count
    reconciled = @ledger_transaction.ledger_entries.filter_map(&:reconciled).uniq.first

    if reconciled_count > 1
      flash.now[:error] = 'Only one entry cannot be checked as reconciled. Please make sure that the entries are checked or unchecked in the reconciled column to save them correctly. '
      repopulate_review_reconcile_state
      render :review_reconcile, status: :unprocessable_content
    elsif @ledger_transaction.save
      notice = ["<p>Ledger Transaction <b>#{@ledger_transaction.id}</b> was successfully created.</p>"]
      if reconciled == true
        if mit_selected.present?
          mit_selected.update_all(reconciled: true)
          notice << "<p>Materials in Transit ids (#{mit_selected.map(&:id).join(',')}) were successfully reconciled.</p>"
        end
        if rnv_selected.present?
          rnv_selected.update_all(reconciled: true)
          notice << "<p>Receipts Not Vouchered ids (#{rnv_selected.map(&:id).join(',')}) were successfully reconciled.</p>"
        end
      end

      flash[:info] = notice.join
      redirect_to po_reconcile_ledger_entries_path({ po_reconcile_params: params[:review_reconcile_save] })
    else
      flash.now[:error] = @ledger_transaction.errors.messages.values
      repopulate_review_reconcile_state
      render :review_reconcile, status: :unprocessable_content
    end
  end
end

#download_sample_csvvoid

This method returns an undefined value.

Serves the sample CSV template for the spreadsheet import flow.



124
125
126
127
# File 'app/controllers/ledger_transactions_controller.rb', line 124

def download_sample_csv
  file_path = Rails.root.join('data/import_templates/gl_sample.csv')
  send_file_accelerated(file_path, download: true, preserve_source: true)
end

#editObject

GET /ledger_transactions/1/edit



130
131
132
133
# File 'app/controllers/ledger_transactions_controller.rb', line 130

def edit
  @ledger_transaction = LedgerTransaction.includes(:ledger_entries).find(params[:id])
  @ledger_transaction.set_company_and_override
end

#indexObject

GET /ledger_transactions
GET /ledger_transactions.xml



34
35
36
37
38
39
40
41
# File 'app/controllers/ledger_transactions_controller.rb', line 34

def index
  @pagy, @ledger_transactions = pagy(LedgerTransaction.includes([:company, { ledger_entries: [:ledger_detail_project, { ledger_company_account: %i[ledger_detail_account company] }] }]).order('ledger_transactions.created_at desc'))

  respond_to do |format|
    format.html # index.html.erb
    format.xml  { render xml: @ledger_transactions }
  end
end

#newObject

GET /ledger_transactions/new
GET /ledger_transactions/new.xml



45
46
47
48
49
50
51
52
53
# File 'app/controllers/ledger_transactions_controller.rb', line 45

def new
  @ledger_transaction = LedgerTransaction.new

  2.times { @ledger_transaction.ledger_entries.build }
  respond_to do |format|
    format.html # new.html.erb
    format.xml  { render xml: @ledger_transaction }
  end
end

#new_from_spreadsheetvoid

This method returns an undefined value.

Renders the upload form for creating a ledger transaction from a CSV/XLSX spreadsheet.



63
# File 'app/controllers/ledger_transactions_controller.rb', line 63

def new_from_spreadsheet; end

#preview_spreadsheetObject

GET /ledger_transactions/preview_spreadsheet

Re-parse the session-stashed upload into an unsaved
LedgerTransaction and render the standard new form pre-filled
with the parsed entries for the user to review before saving.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'app/controllers/ledger_transactions_controller.rb', line 100

def preview_spreadsheet
  pending = session[:pending_ledger_upload]
  if pending.blank?
    redirect_to new_from_spreadsheet_ledger_transactions_path,
                alert: 'No pending upload found. Please upload a file first.'
    return
  end

  Tempfile.create(['ledger_upload', File.extname(pending['filename'].to_s)]) do |tmp|
    tmp.binmode
    tmp.write(Base64.strict_decode64(pending['data']))
    tmp.flush
    @ledger_transaction = LedgerTransaction.build_from_spreadsheet(
      tmp.path,
      original_filename: pending['filename'],
      encoding: pending['encoding']
    )
  end

  render :new
end

#reversevoid

This method returns an undefined value.

Renders the reversal form for the ledger transaction.



209
210
211
# File 'app/controllers/ledger_transactions_controller.rb', line 209

def reverse
  @ledger_transaction = LedgerTransaction.find(params[:id])
end

#review_reconcilevoid

This method returns an undefined value.

Renders the review page for an unbalanced PO reconciliation journal entry
(materials-in-transit vs receipts-not-vouchered, plus the difference account).



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'app/controllers/ledger_transactions_controller.rb', line 238

def review_reconcile
  @company_id = params[:po_reconcile_params][:company_id].to_i
  @company_criteria = params[:company_criteria]
  lca_mit = LedgerCompanyAccount.where(company_id: @company_id, ledger_detail_account_id: LedgerAccount.find_by(number: 1415).id).first
  lca_rnv = LedgerCompanyAccount.where(company_id: @company_id, ledger_detail_account_id: LedgerAccount.find_by(number: 4120).id).first
  lca_diff = LedgerCompanyAccount.where(company_id: @company_id, ledger_detail_account_id: LedgerAccount.find_by(number: 9505).id).first
  @mit_gl_date_gteq = params[:po_reconcile_params][:mit_gl_date_gteq]
  @mit_gl_date_lteq = params[:po_reconcile_params][:mit_gl_date_lteq]
  @rnv_gl_date_gteq = params[:po_reconcile_params][:rnv_gl_date_gteq]
  @rnv_gl_date_lteq = params[:po_reconcile_params][:rnv_gl_date_lteq]
  @po_number_ref = params[:po_reconcile_params][:po_number_ref]
  @reconciled = params[:po_reconcile_params][:reconciled]
  @mit_ids = params[:mit_ids]
  @rnv_ids = params[:rnv_ids]
  @reconciliation_date = params[:po_reconcile_params][:reconciliation_date].present? ? params[:po_reconcile_params][:reconciliation_date].to_date : Date.current
  @ledger_transaction = LedgerTransaction.new(company_id: @company_id, transaction_type: 'JOURNAL_ENTRY', transaction_date: @reconciliation_date, currency: params[:tr_currency])
  @ledger_transaction.ledger_entries << LedgerEntry.new(ledger_company_account: lca_rnv, amount: params[:rnv_total].to_f, business_unit: BusinessUnit.find(params[:bu_id]), description: params[:description])
  @ledger_transaction.ledger_entries << LedgerEntry.new(ledger_company_account: lca_mit, amount: params[:mit_total].to_f, business_unit: BusinessUnit.find(params[:bu_id]), description: params[:description])
  @ledger_transaction.ledger_entries << LedgerEntry.new(ledger_company_account: lca_diff, business_unit: BusinessUnit.find(params[:bu_id]), description: params[:description])
end

#updateObject

PUT /ledger_transactions/1
PUT /ledger_transactions/1.xml



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
# File 'app/controllers/ledger_transactions_controller.rb', line 155

def update
  @ledger_transaction = LedgerTransaction.find(params[:id])

  respond_to do |format|
    if @ledger_transaction.update(params[:ledger_transaction])
      format.html { redirect_to(@ledger_transaction, info: 'Ledger transaction was successfully updated.') }
      format.xml  { head :ok }
    else
      format.html { render action: 'edit', status: :unprocessable_content }
      format.xml  { render xml: @ledger_transaction.errors, status: :unprocessable_content }
    end
  end
rescue ActiveRecord::StatementInvalid => e
  # The DB-level append-only guard (enforce_ledger_transaction_append_only)
  # rejects edits to a ledger transaction in a closed accounting period — it
  # must be reversed in an open period instead. That surfaces as a
  # CheckViolation out of #update; show it instead of 500ing (AppSignal #6052).
  # Re-raise anything that isn't the closed-period guard so genuine DB errors
  # still surface. Mirrors the void / do_reversal rescues in this controller.
  raise unless e.message.include?('append-only')

  @ledger_transaction.reload
  message = 'This ledger transaction is in a closed accounting period and cannot be edited. ' \
            'Reverse it in an open period instead.'
  respond_to do |format|
    format.html do
      flash.now[:error] = message
      render(action: 'edit', status: :unprocessable_content)
    end
    format.xml { render(xml: { error: message }, status: :unprocessable_content) }
  end
end

#upload_spreadsheetObject

POST /ledger_transactions/upload_spreadsheet

Stash the uploaded file in the session and 303-redirect to the
preview action. POST -> Redirect -> GET keeps the response semantics
honest (parsing succeeded -> 303; preview renders 200) and lets
Turbo Drive follow the redirect natively.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'app/controllers/ledger_transactions_controller.rb', line 71

def upload_spreadsheet
  upload_param = params[:upload] || {}
  uploaded_file = upload_param[:ledger_file].presence || upload_param[:ledger_csv].presence

  unless uploaded_file.is_a?(ActionDispatch::Http::UploadedFile)
    flash.now[:error] = 'No file was uploaded. Please select a CSV or XLSX file.'
    render :new_from_spreadsheet, status: :unprocessable_content
    return
  end

  if uploaded_file.size > MAX_LEDGER_UPLOAD_BYTES
    flash.now[:error] = "File too large. Please keep uploads under #{MAX_LEDGER_UPLOAD_BYTES / 1.kilobyte} KB."
    render :new_from_spreadsheet, status: :unprocessable_content
    return
  end

  session[:pending_ledger_upload] = {
    'data' => Base64.strict_encode64(uploaded_file.read),
    'filename' => uploaded_file.original_filename,
    'encoding' => upload_param[:encoding].presence
  }
  redirect_to preview_spreadsheet_ledger_transactions_path, status: :see_other
end

#voidvoid

This method returns an undefined value.

Voids the ledger transaction by reversing it as of its transaction date.
Redirects with an error when the transaction has already been reversed.



191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'app/controllers/ledger_transactions_controller.rb', line 191

def void
  @ledger_transaction = LedgerTransaction.find(params[:id])

  if @ledger_transaction.transaction_reversal.present?
    redirect_to @ledger_transaction, error: 'Ledger Transaction has already been reversed.'
  else
    begin
      @ledger_transaction.reverse(@ledger_transaction.transaction_date)
      redirect_to @ledger_transaction, info: 'Ledger Transaction successfully voided.'
    rescue StandardError => e
      flash[:error] = "Unable to void transaction, message : #{e}."
      redirect_to @ledger_transaction
    end
  end
end