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

Constants included from Www::SeoHelper

Www::SeoHelper::AWARDS, Www::SeoHelper::CA_ADDRESS, Www::SeoHelper::CA_BUSINESS_HOURS, Www::SeoHelper::CA_CONTACT_POINT, Www::SeoHelper::CA_CURRENCIES, Www::SeoHelper::CA_DESCRIPTION, Www::SeoHelper::CA_FOUNDING_DATE, Www::SeoHelper::CA_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::CA_LEGAL_NAME, Www::SeoHelper::CA_LOCAL_BUSINESS, Www::SeoHelper::CA_ONLINE_STORE, Www::SeoHelper::CA_RETURN_POLICY, Www::SeoHelper::CA_SALES_DEPARTMENT, Www::SeoHelper::CA_SERVICE_AREA, Www::SeoHelper::CA_URL, Www::SeoHelper::CA_VAT_ID, Www::SeoHelper::CA_WAREHOUSE_DEPARTMENT, Www::SeoHelper::CA_WAREHOUSE_HOURS, Www::SeoHelper::COMPANY_EMAIL, Www::SeoHelper::COMPANY_LOGO, Www::SeoHelper::COMPANY_NAME, Www::SeoHelper::COMPANY_SLOGAN, Www::SeoHelper::EXPERTISE, Www::SeoHelper::FAX_NUMBER, Www::SeoHelper::GS1_COMPANY_PREFIX, Www::SeoHelper::ISO6523_CODE, Www::SeoHelper::PAYMENT_METHODS, Www::SeoHelper::PHONE_NUMBER, Www::SeoHelper::PRIMARY_NAICS, Www::SeoHelper::REFUND_TYPE, Www::SeoHelper::RETURN_FEES, Www::SeoHelper::RETURN_METHOD, Www::SeoHelper::RETURN_POLICY_CATEGORY, Www::SeoHelper::SECONDARY_NAICS, Www::SeoHelper::SOCIAL_PROFILES, Www::SeoHelper::US_ADDRESS, Www::SeoHelper::US_BUSINESS_HOURS, Www::SeoHelper::US_CONTACT_POINT, Www::SeoHelper::US_CURRENCIES, Www::SeoHelper::US_DESCRIPTION, Www::SeoHelper::US_FOUNDING_DATE, Www::SeoHelper::US_GLOBAL_LOCATION_NUMBER, Www::SeoHelper::US_IMAGE, Www::SeoHelper::US_LEGAL_NAME, Www::SeoHelper::US_LOCAL_BUSINESS, Www::SeoHelper::US_ONLINE_STORE, Www::SeoHelper::US_RETURN_POLICY, Www::SeoHelper::US_SALES_DEPARTMENT, Www::SeoHelper::US_SERVICE_AREA, Www::SeoHelper::US_TAX_ID, Www::SeoHelper::US_URL, Www::SeoHelper::US_WAREHOUSE_DEPARTMENT, Www::SeoHelper::US_WAREHOUSE_HOURS

Constants included from IconHelper

IconHelper::CUSTOM_ICON_MAP, IconHelper::CUSTOM_SVG_DIR, IconHelper::DEFAULT_FAMILY

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

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?, #stamp_impersonation_context, #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, #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 ApplicationHelper

#better_number_to_currency, #check_force_logout, #check_or_cross, #check_or_times, #embedded_tab_frame_id, #error_messages, #general_disclaimer_on_product_installation_and_local_codes, #gridjs_from_html_table, #gridjs_table, #is_wy_ip, #line_break, #parent_layout, #pass_or_fail, #render_error_messages_list, #render_video_card, #resolved_auth_form_turbo_frame, #return_path_or, #safe_css_color, #set_return_path_if_present, #set_section_if_present, #tab_frame_id, #to_underscore, #track_page?, #turbo_section_wrapper, #turbo_tabs_request?, #url_on_same_domain_as_request, #widget_index_daily_focus_index_path, #working_hours?, #yes_or_no, #yes_or_no_highlighted, #yes_or_no_with_check_or_cross, #youtube_video

Methods included from UppyUploaderHelper

#file_uploader, #image_uploader, #large_file_uploader_s3, #lead_sketch_uploader, #rma_image_uploader, #rma_image_uploader_s3, #uppy_uploader, #video_uploader

Methods included from Www::ImagesHelper

#image_asset_tag, #image_asset_url

Methods included from Www::SeoHelper

#add_page_schema, #add_webpage_schema, #canada?, #company_social_links, #ensure_context_json, #json_ld_script_tag, #local_business_schema, #online_store_id, #online_store_schema, #page_main_entity, #page_main_entity_json, #render_auto_collection_page_schema, #render_collection_page_schema, #render_local_business_schema, #render_online_store_schema, #render_page_schemas, #render_page_video_schemas, #render_webpage_schema, #render_webpage_schema_with_collections, #usa?

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?

Methods included from IconHelper

#account_nav_icon, #fa_icon, #star_rating_html

Instance Method Details

#business_unitsObject



281
282
283
284
285
286
# File 'app/controllers/ledger_transactions_controller.rb', line 281

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



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

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_reversalObject



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'app/controllers/ledger_transactions_controller.rb', line 206

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_reconcileObject



246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'app/controllers/ledger_transactions_controller.rb', line 246

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_csvObject



122
123
124
125
# File 'app/controllers/ledger_transactions_controller.rb', line 122

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



128
129
130
131
# File 'app/controllers/ledger_transactions_controller.rb', line 128

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



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

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



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

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_spreadsheetObject



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

#reverseObject



202
203
204
# File 'app/controllers/ledger_transactions_controller.rb', line 202

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

#review_reconcileObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'app/controllers/ledger_transactions_controller.rb', line 225

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



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

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

#voidObject



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'app/controllers/ledger_transactions_controller.rb', line 186

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