Class: AccountingDashboardController
- Inherits:
-
CrmController
- Object
- ActionController::Base
- ApplicationController
- CrmController
- AccountingDashboardController
- Includes:
- Controllers::RequireTurboFrame
- Defined in:
- app/controllers/accounting_dashboard_controller.rb
Overview
Controller: accounting dashboard.
Constant Summary collapse
- TAB_ACTIONS =
Tab fragments never render standalone — non-frame hits redirect to the
transmissions dashboard with the tab preselected. %i[tab_email_or_fax tab_portal tab_mail tab_mixed tab_no_channel_set tab_replacement_invoice tab_missing_edi_810].freeze
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
-
#acknowledge_missing_edi_810 ⇒ Object
POST /accounting/acknowledge_missing_edi_810 — mass-mark the selected EDI-810-missing invoices as transmitted (effectively "we know about it, stop bothering us").
-
#bulk_regenerate_edi_810 ⇒ Object
POST /accounting/bulk_regenerate_edi_810 — re-fire the EDI-810 creation pipeline for every selected invoice via Edi::InvoiceEventProcessor.
-
#index ⇒ Object
GET /accounting — landing page for the accounting team's daily workflow.
-
#tab_count ⇒ Object
GET /accounting/tab_count — count-only endpoint behind each tab badge in the dashboard.
-
#tab_email_or_fax ⇒ Object
GET /accounting/tab_email_or_fax — turbo-frame body for the Email/Fax tab.
-
#tab_mail ⇒ Object
GET /accounting/tab_mail — turbo-frame body for the Mail tab (customers who require physical printed-and-mailed documents).
-
#tab_missing_edi_810 ⇒ Object
GET /accounting/tab_missing_edi_810 — turbo-frame body for the "EDI 810 missing" tab (invoices that should have transmitted as an EDI 810 but didn't).
-
#tab_mixed ⇒ Object
GET /accounting/tab_mixed — turbo-frame body for the Mixed tab (customers whose contact-points span more than one transmission channel, requiring per-doc handling).
-
#tab_no_channel_set ⇒ Object
GET /accounting/tab_no_channel_set — turbo-frame body for docs whose customer has no transmission channel configured at all.
-
#tab_portal ⇒ Object
GET /accounting/tab_portal — turbo-frame body for the Portal tab (customers whose docs are picked up via partner portal, e.g. SPS).
-
#tab_replacement_invoice ⇒ Object
GET /accounting/tab_replacement_invoice — turbo-frame body for the "replacement invoice" tab.
-
#transmissions ⇒ Object
GET /accounting/transmissions — combined queue view of every outbound accounting document (invoice, credit memo) waiting on transmission.
-
#transmit_accounting_documents ⇒ Object
POST /accounting/transmit_accounting_documents — bulk action on the documents the user checked off in the transmissions list.
Methods included from Controllers::RequireTurboFrame
require_in_pane_tab_frames, require_turbo_frame
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
Methods included from Controllers::SubdomainDetection
#is_crm_request?, #is_www_request?, #json_request?
Methods included from Controllers::TurboSafeRedirect
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
#acknowledge_missing_edi_810 ⇒ Object
POST /accounting/acknowledge_missing_edi_810 — mass-mark the
selected EDI-810-missing invoices as transmitted (effectively
"we know about it, stop bothering us"). Each invoice fires its
transmit! state-machine event when it's still in a
transmittable state.
169 170 171 172 173 174 175 176 177 178 179 180 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 169 def acknowledge_missing_edi_810 invoice_ids = params[:invoice_ids].to_a.map(&:to_i).reject(&:zero?) if invoice_ids.empty? flash[:warning] = 'No invoices selected.' else Invoice.transaction do Invoice.where(id: invoice_ids).find_each { |invoice| invoice.transmit! if invoice.can_transmit? } end flash[:info] = "#{invoice_ids.size} invoice(s) marked as ignored and removed from this list." end redirect_to accounting_transmissions_path(since: params[:since], tab: 'missing_edi_810') end |
#bulk_regenerate_edi_810 ⇒ Object
POST /accounting/bulk_regenerate_edi_810 — re-fire the EDI-810
creation pipeline for every selected invoice via
Edi::InvoiceEventProcessor. Bulk equivalent of the per-invoice
InvoicesController#regenerate_edi_documents action.
186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 186 def bulk_regenerate_edi_810 :update, Invoice invoice_ids = params[:invoice_ids].to_a.map(&:to_i).reject(&:zero?) if invoice_ids.empty? flash[:warning] = 'No invoices selected.' else processor = Edi::InvoiceEventProcessor.new Invoice.where(id: invoice_ids).find_each { |invoice| processor.process_creation(invoice) } flash[:info] = "EDI documents queued for regeneration on #{invoice_ids.size} invoice(s)." end redirect_to accounting_transmissions_path(since: params[:since], tab: 'missing_edi_810') end |
#index ⇒ Object
GET /accounting — landing page for the accounting team's daily
workflow. The page itself is mostly turbo-frame slots that fetch
the per-tab content lazily.
14 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 14 def index; end |
#tab_count ⇒ Object
GET /accounting/tab_count — count-only endpoint behind each tab
badge in the dashboard. Returns just the integer count for the
requested tab, applying the same filter the corresponding tab
action would apply.
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/controllers/accounting_dashboard_controller.rb', line 140 def tab_count q = params[:q].to_h @tab = params[:tab].to_s @count = case @tab when 'email_or_fax' ViewAccountingTransmission.ransack(q.merge('transmission_type_in' => ['Email/Fax'])).result.count when 'portal' ViewAccountingTransmission.ransack(q.merge('transmission_type_in' => ['Portal'])).result.count when 'mail' ViewAccountingTransmission.ransack(q.merge('transmission_type_in' => ['Mail'])).result.count when 'mixed' ViewAccountingTransmission.ransack(q.merge('transmission_type_not_in' => ['Email/Fax', 'Portal', 'Mail', 'No Channel Set'])).result.count when 'no_channel_set' ViewAccountingTransmission.ransack(q.merge('transmission_type_in' => ['No Channel Set'])).result.count when 'replacement_invoice' ViewAccountingTransmission.ransack(q.merge('doc_type_in' => ['INV'])).result.count when 'missing_edi_810' Invoice.missing_edi_810.where(invoices: { created_at: 90.days.ago.. }).count else 0 end render layout: false end |
#tab_email_or_fax ⇒ Object
GET /accounting/tab_email_or_fax — turbo-frame body for the
Email/Fax tab. Pulls every doc whose customer transmits via email
or fax, including the related Communications so the per-row
"transmission error" tooltip doesn't N+1.
70 71 72 73 74 75 76 77 78 79 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 70 def tab_email_or_fax @email_fax_query = ViewAccountingTransmission.ransack(params[:q]) @email_fax_query.sorts = 'document_date asc' if @email_fax_query.sorts.empty? @email_fax_documents = @email_fax_query.result .except(:order) .order("#{@email_fax_query.sorts.first.name} #{@email_fax_query.sorts.first.dir} NULLS LAST") .load preload_unsent_communications(@email_fax_documents) render 'tab_email_or_fax', layout: should_render_layout? end |
#tab_mail ⇒ Object
GET /accounting/tab_mail — turbo-frame body for the Mail tab
(customers who require physical printed-and-mailed documents).
89 90 91 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 89 def tab_mail render 'tab_mail', layout: should_render_layout? end |
#tab_missing_edi_810 ⇒ Object
GET /accounting/tab_missing_edi_810 — turbo-frame body for the
"EDI 810 missing" tab (invoices that should have transmitted as an
EDI 810 but didn't). Defaults to the last 90 days but takes a
since param to expand the window.
126 127 128 129 130 131 132 133 134 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 126 def tab_missing_edi_810 @since = params[:since].present? ? Date.parse(params[:since]) : 90.days.ago.to_date @invoices = Invoice.missing_edi_810 .where(invoices: { created_at: @since.. }) .includes(:order, :customer) .order(created_at: :desc) .load render 'tab_missing_edi_810', layout: should_render_layout? end |
#tab_mixed ⇒ Object
GET /accounting/tab_mixed — turbo-frame body for the Mixed tab
(customers whose contact-points span more than one transmission
channel, requiring per-doc handling).
96 97 98 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 96 def tab_mixed render 'tab_mixed', layout: should_render_layout? end |
#tab_no_channel_set ⇒ Object
GET /accounting/tab_no_channel_set — turbo-frame body for docs
whose customer has no transmission channel configured at all.
These need a setup task before they can move forward.
103 104 105 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 103 def tab_no_channel_set render 'tab_no_channel_set', layout: should_render_layout? end |
#tab_portal ⇒ Object
GET /accounting/tab_portal — turbo-frame body for the Portal tab
(customers whose docs are picked up via partner portal, e.g. SPS).
83 84 85 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 83 def tab_portal render 'tab_portal', layout: should_render_layout? end |
#tab_replacement_invoice ⇒ Object
GET /accounting/tab_replacement_invoice — turbo-frame body for
the "replacement invoice" tab. Shows full-RMA-funded INV docs
whose RMA is still awaiting return after 15+ days, so accounting
can chase the customer for the return or write it off.
111 112 113 114 115 116 117 118 119 120 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 111 def tab_replacement_invoice @replacement_invoice_query = ViewAccountingTransmission.ransack(params[:q]) @replacement_invoice_query.sorts = 'document_date asc' if @replacement_invoice_query.sorts.empty? @replacement_invoice_documents = @replacement_invoice_query.result .except(:order) .order("#{@replacement_invoice_query.sorts.first.name} #{@replacement_invoice_query.sorts.first.dir} NULLS LAST") .load preload_unsent_communications(@replacement_invoice_documents) render 'tab_replacement_invoice', layout: should_render_layout? end |
#transmissions ⇒ Object
GET /accounting/transmissions — combined queue view of every
outbound accounting document (invoice, credit memo) waiting on
transmission. Each tab is rendered separately by the tab_*
actions; this action just sets up the shared search struct.
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 20 def transmissions params[:q] ||= {} # @tabs = { # email: {title: 'Email/Fax', query: {transmission_type_in: ['Email/Fax'], rma_funding_not_eq: 'full'}}, # portal: {title: 'Portal', query: {transmission_type_in: ['Portal'], rma_funding_not_eq: 'full'}}, # mail: {title: 'Mail', query: {transmission_type_in: ['Mail'], rma_funding_not_eq: 'full'}}, # mixed: {title: 'Mixed', query: {transmission_type_not_in: ['Email/Fax','Portal','Mail','No Channel Set'], rma_funding_not_eq: 'full'}}, # none: {title: 'No Channel Set', query: {transmission_type_in: ['No Channel Set'], rma_funding_not_eq: 'full'}}, # rma: {title: 'Replacement Invoice', query: {doc_type_in: ['INV'], rma_funding_eq: 'full', state_eq: 'unpaid', rma_state_eq: 'awaiting_return', document_date_lteq: 15.days.ago}} # } # @tabs.each {|tab, options| options[:count] = ViewAccountingTransmission.ransack(params[:q].merge(options[:query])).result.count} # @selected_tab = (params[:tab] || :email).to_sym @q = OpenStruct.new(params[:q]) end |
#transmit_accounting_documents ⇒ Object
POST /accounting/transmit_accounting_documents — bulk action on
the documents the user checked off in the transmissions list.
Three submit buttons land here: Transmit actually sends them,
Mark As Transmitted flips their state without sending,
Print produces a single combined PDF and exposes it via
session[:download_path] so the next page picks it up.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'app/controllers/accounting_dashboard_controller.rb', line 43 def transmit_accounting_documents redirect_to(accounting_transmissions_path(q: parsed_transmission_query, tab: params[:tab]), alert: 'You must select one or more documents') and return if params[:documents].nil? transmitter = AccountingDocumentTransmitter.new(params[:documents]) case params[:commit] when 'Transmit' result = transmitter.transmit flash[:info] = result[:success_message] flash[:warning] = result[:error_message] when 'Mark As Transmitted' transmitter.mark_transmitted flash[:info] = 'Selected documents have been marked as transmitted.' when 'Print' upload = transmitter.print_docs if upload.nil? flash[:error] = 'Unable to print documents' else session[:download_path] = upload_url(upload.id) end end redirect_to accounting_transmissions_path(q: parsed_transmission_query, tab: params[:tab]) end |