diff --git a/app/controllers/chargeback_assignment_controller.rb b/app/controllers/chargeback_assignment_controller.rb new file mode 100644 index 00000000000..8eeb8292703 --- /dev/null +++ b/app/controllers/chargeback_assignment_controller.rb @@ -0,0 +1,421 @@ +class ChargebackAssignmentController < ApplicationController + before_action :check_privileges + before_action :get_session_data + after_action :cleanup_action + after_action :set_session_data + + include Mixins::SavedReportPaging + include Mixins::GenericSessionMixin + include Mixins::BreadcrumbsMixin + + def self.table_name + @table_name ||= "chargeback_assignment" + end + + def tree_select + self.x_active_tree = params[:tree] if params[:tree] + self.x_node = params[:id] + get_node_info(x_node) + replace_right_cell + end + + def explorer + @breadcrumbs = [] + @explorer = true + build_accordions_and_trees + + @right_cell_text = _("All Assignments") + set_form_locals if @in_a_form + session[:changed] = false + + render :layout => "application" unless request.xml_http_request? + end + + def set_form_locals + @x_edit_buttons_locals = { + :action_url => 'cb_assign_update', + :no_cancel => true, + :multi_record => true + } + end + + # AJAX driven routine to check for changes in ANY field on the form + def cb_assign_field_changed + return unless load_edit("cbassign_edit__#{x_node}", "replace_cell__chargeback") + + cb_assign_get_form_vars + render :update do |page| + page << javascript_prologue + except = %i[cbshow_typ cbtag_cat cblabel_key] + changed = (@edit[:new].except(*except) != @edit[:current].except(*except)) + page.replace("cb_assignment_div", :partial => "cb_assignments") if params[:cbshow_typ] || params[:cbtag_cat] || params[:cblabel_key] + page << javascript_for_miq_button_visibility(changed) + end + end + + def cb_assign_update + if params[:button] == "reset" + get_node_info(x_node) + add_flash(_("All changes have been reset"), :warning) + replace_right_cell + else + return unless load_edit("cbassign_edit__#{x_node}", "replace_cell__chargeback") + + cb_assign_set_record_vars + rate_type = x_node.split('-').last + begin + ChargebackRate.set_assignments(rate_type, @edit[:set_assignments]) + rescue StandardError => bang + render_flash(_("Error during 'Rate assignments': %{error_message}") % {:error_message => bang.message}, :error) + else + add_flash(_("Rate Assignments saved")) + get_node_info(x_node) + replace_right_cell + end + end + end + + def title + @title = _("Chargeback Assignment") + end + + private ############################ + + def features + [ + { + :role => "chargeback_assignments", + :name => :cb_assignments, + :title => _("Assignments") + } + ].map { |hsh| ApplicationController::Feature.new_with_hash(hsh) } + end + + def get_node_info(node, show_list = true) + @show_list = show_list + node = valid_active_node(node) + if ["xx-Compute", "xx-Storage"].include?(node) + cb_assign_set_form_vars + @right_cell_text = case node + when "xx-Compute" then _("Compute Rate Assignments") + when "xx-Storage" then _("Storage Rate Assignments") + end + else + @right_cell_text = _("All Assignments") + end + {:view => @view, :pages => @pages} + end + + # Set record vars for save + def cb_assign_set_record_vars + @edit[:set_assignments] = [] + if @edit[:new][:cbshow_typ].ends_with?("-tags") + assigned_rates_from_all_categories = @edit[:cb_assign][:tags].values.reduce({}, :merge) + assigned_rates_from_all_categories.each_key do |id| + key = "#{@edit[:new][:cbshow_typ]}__#{id}" + next if @edit[:new][key].nil? || @edit[:new][key] == "nil" + + temp = { + :cb_rate => ChargebackRate.find(@edit[:new][key]), + :tag => [Classification.find(id)], + } + temp[:tag].push(@edit[:new][:cbshow_typ].split("-").first) + @edit[:set_assignments].push(temp) + end + elsif @edit[:new][:cbshow_typ].ends_with?("-labels") + @edit[:cb_assign][:docker_label_values_saved].each_key do |id| + key = "#{@edit[:new][:cbshow_typ]}__#{id}" + next if @edit[:new][key].nil? || @edit[:new][key] == "nil" + + temp = { + :cb_rate => ChargebackRate.find(@edit[:new][key]), + :label => [CustomAttribute.find(id)] + } + temp[:label].push(@edit[:new][:cbshow_typ].split("-").first) + @edit[:set_assignments].push(temp) + end + else + @edit[:cb_assign][:cis].each_key do |id| + key = "#{@edit[:new][:cbshow_typ]}__#{id}" + next if @edit[:new][key].nil? || @edit[:new][key] == "nil" + + temp = {:cb_rate => ChargebackRate.find(@edit[:new][key])} + model = if @edit[:new][:cbshow_typ] == "enterprise" + MiqEnterprise + elsif @edit[:new][:cbshow_typ] == "ems_container" + ExtManagementSystem + else + Object.const_get(@edit[:new][:cbshow_typ].camelize) rescue nil + end + + temp[:object] = model.find(id) unless model.nil? + @edit[:set_assignments].push(temp) + end + end + end + + # Set form variables for edit + def cb_assign_set_form_vars + @edit = { + :cb_rates => {}, + :cb_assign => {}, + } + ChargebackRate.all.each do |cbr| + if cbr.rate_type == x_node.split('-').last + @edit[:cb_rates][cbr.id.to_s] = cbr.description + end + end + @edit[:key] = "cbassign_edit__#{x_node}" + @edit[:new] = HashWithIndifferentAccess.new + @edit[:current] = HashWithIndifferentAccess.new + @edit[:current_assignment] = ChargebackRate.get_assignments(x_node.split('-').last) + unless @edit[:current_assignment].empty? + @edit[:new][:cbshow_typ] = case @edit[:current_assignment][0][:object] + when EmsCluster + "ems_cluster" + when ExtManagementSystem, ManageIQ::Providers::ContainerManager + "ext_management_system" + when MiqEnterprise + "enterprise" + when NilClass + if @edit[:current_assignment][0][:tag] + "#{@edit[:current_assignment][0][:tag][1]}-tags" + else + "#{@edit[:current_assignment][0][:label][1]}-labels" + end + else + @edit[:current_assignment][0][:object].class.name.downcase + end + end + if @edit[:new][:cbshow_typ]&.ends_with?("-tags") + get_categories_all + tag = @edit[:current_assignment][0][:tag][0] + if tag + @edit[:new][:cbtag_cat] = tag["parent_id"].to_s + get_tags_all + else + @edit[:current_assignment] = [] + end + elsif @edit[:new][:cbshow_typ]&.ends_with?("-labels") + get_docker_labels_all_keys + assigned_label = @edit[:current_assignment][0][:label][0] + if assigned_label + label = @edit[:cb_assign][:docker_label_keys].detect { |_key, value| value == assigned_label.name } + label ||= @edit[:cb_assign][:docker_label_default_keys].detect { |_key, value| value == assigned_label.name } + @edit[:new][:cblabel_key] = label.first + get_docker_labels_all_values(label.first) + else + @edit[:current_assignment] = [] + end + elsif @edit[:new][:cbshow_typ] + get_cis_all + end + + @edit[:current_assignment].each do |el| + if el[:object] + @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:object]["id"]}"] = el[:cb_rate]["id"].to_s + elsif el[:tag] + @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:tag][0]["id"]}"] = el[:cb_rate]["id"].to_s + elsif el[:label] + @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:label][0].id}"] = el[:cb_rate]["id"].to_s + end + end + + @edit[:current] = copy_hash(@edit[:new]) + session[:edit] = @edit + @in_a_form = true + end + + def get_categories_all + @edit[:cb_assign][:cats] = {} + Classification.categories.select { |c| c.show && !c.entries.empty? }.each do |c| + @edit[:cb_assign][:cats][c.id.to_s] = c.description + end + end + + def get_tags_all + @edit[:cb_assign][:tags] ||= {} + + Classification.all.each do |category| + @edit[:cb_assign][:tags][category.id] ||= {} + category.entries.each do |entry| + @edit[:cb_assign][:tags][category.id][entry.id.to_s] = entry.description + end + end + end + + DEFAULT_CHARGEBACK_LABELS = ["com.redhat.component"].freeze + + def get_docker_labels_all_keys + @edit[:cb_assign][:docker_label_keys] = {} + @edit[:cb_assign][:docker_label_default_keys] = {} + CustomAttribute.where(:section => "docker_labels").pluck(:id, :name).uniq(&:second).each do |label| + if DEFAULT_CHARGEBACK_LABELS.include?(label.second) + @edit[:cb_assign][:docker_label_default_keys][label.first.to_s] = label.second + else + @edit[:cb_assign][:docker_label_keys][label.first.to_s] = label.second + end + end + end + + def get_docker_labels_all_values(label_id) + @edit[:cb_assign][:docker_label_values] = {} + @edit[:cb_assign][:docker_label_values_saved] = {} + + CustomAttribute.where(:section => "docker_labels").pluck(:id, :value).each do |label| + @edit[:cb_assign][:docker_label_values_saved][label.first.to_s] = label.second + end + + return if label_id && label_id == 'null' || label_id.nil? + + label_name = CustomAttribute.find(label_id).name + + CustomAttribute.where(:section => "docker_labels", :name => label_name).pluck(:id, :value).uniq(&:second).each do |label| + @edit[:cb_assign][:docker_label_values][label.first.to_s] = label.second + end + end + + WHITELIST_INSTANCE_TYPE = %w[enterprise storage ext_management_system ems_cluster tenant ems_container].freeze + NOTHING_FORM_VALUE = "nil".freeze + + def get_cis_all + @edit[:cb_assign][:cis] = {} + klass = @edit[:new][:cbshow_typ] + return if klass == NOTHING_FORM_VALUE || klass.nil? # no rate was selected + unless WHITELIST_INSTANCE_TYPE.include?(klass) + raise ArgumentError, "Received: #{klass}, expected one of #{WHITELIST_INSTANCE_TYPE}" + end + + all_of_classtype = + if klass == "enterprise" + MiqEnterprise.all + elsif klass == "ext_management_system" + ExtManagementSystem.all + else + klass.classify.constantize.all + end + @edit[:cb_assign][:hierarchy] ||= {} + all_of_classtype.each do |instance| + @edit[:cb_assign][:cis][instance.id] = instance.name + if klass == "ems_cluster" + provider_name = instance.ext_management_system.name + @edit[:cb_assign][:cis][instance.id] = "#{provider_name}/#{instance.name}" + end + next unless klass == "tenant" && instance.root? + + @edit[:cb_assign][:hierarchy][instance.id] = {} + @edit[:cb_assign][:hierarchy][instance.id][:name] = instance.name + @edit[:cb_assign][:hierarchy][instance.id][:subtenant] = instance.build_tenant_tree + end + end + + def cb_assign_params_to_edit(cb_assign_key, tag_category_id = nil) + current_assingments = cb_assign_key == :tags ? @edit[:cb_assign][cb_assign_key].try(:[], tag_category_id) : @edit[:cb_assign][cb_assign_key] + + return unless current_assingments + + current_assingments.each_key do |id| + key = "#{@edit[:new][:cbshow_typ]}__#{id}" + @edit[:new][key] = params[key].to_s if params[key] + end + end + + # Get variables from edit form + def cb_assign_get_form_vars + @edit[:new][:cbshow_typ] = params[:cbshow_typ] if params[:cbshow_typ] + @edit[:new][:cbtag_cat] = nil if params[:cbshow_typ] # Reset categories pull down if assign to selection is changed + @edit[:new][:cbtag_cat] = params[:cbtag_cat].to_s if params[:cbtag_cat] + @edit[:new][:cblabel_key] = nil if params[:cbshow_typ] + @edit[:new][:cblabel_key] = params[:cblabel_key].to_s if params[:cblabel_key] + + if @edit[:new][:cbshow_typ].ends_with?("-tags") + get_categories_all + get_tags_all + elsif @edit[:new][:cbshow_typ].ends_with?("-labels") + get_docker_labels_all_keys + get_docker_labels_all_values(@edit[:new][:cblabel_key]) + else + get_cis_all + end + + cb_assign_params_to_edit(:cis) + cb_assign_params_to_edit(:tags, @edit[:new][:cbtag_cat].try(:to_i)) + cb_assign_params_to_edit(:docker_label_values) + end + + def replace_right_cell(options = {}) + @explorer = true + c_tb = build_toolbar(center_toolbar_filename) + + # Build a presenter to render the JS + presenter = ExplorerPresenter.new(:active_tree => x_active_tree) + + # FIXME + # if params[:action].ends_with?("_delete") + # page << "miqTreeActivateNodeSilently('#{x_active_tree.to_s}', '<%= x_node %>');" + # end + # presenter[:select_node] = x_node if params[:action].ends_with?("_delete") + presenter[:osf_node] = x_node + + # Assignments accordion + presenter.update(:main_div, r[:partial => "assignments_tabs"]) + + if @record || @in_a_form || + (@pages && (@items_per_page == ONE_MILLION || @pages[:items] == 0)) + if %w[Compute Storage].include?(x_node.split('-').last) + presenter.hide(:toolbar) + # incase it was hidden for summary screen, and incase there were no records on show_list + presenter.show(:paging_div, :form_buttons_div).remove_paging + locals = {:record_id => @edit[:rec_id]} + if x_active_tree == :cb_rates_tree + locals[:action_url] = 'cb_rate_edit' + else + locals.update( + :action_url => 'cb_assign_update', + :no_cancel => true, + :multi_record => true + ) + end + presenter.update(:form_buttons_div, r[:partial => 'layouts/x_edit_buttons', :locals => locals]) + else + # Added so buttons can be turned off even tho div is not being displayed it still pops up Abandon changes box when trying to change a node on tree after saving a record + presenter.hide(:buttons_on).show(:toolbar).hide(:paging_div) + presenter.hide(:form_buttons_div) if params[:button] + end + else + presenter.hide(:form_buttons_div) + if x_node == "root" + presenter.hide(:toolbar).remove_paging + end + presenter.show(:paging_div) + end + + presenter[:record_id] = determine_record_id_for_presenter + + presenter[:clear_gtl_list_grid] = @gtl_type && @gtl_type != 'list' + + presenter[:right_cell_text] = @right_cell_text + presenter.update(:breadcrumbs, r[:partial => 'layouts/breadcrumbs']) + + render :json => presenter.for_render + end + + def get_session_data + super + end + + def set_session_data + super + end + + def breadcrumbs_options + { + :breadcrumbs => [ + {:title => _("Overview")}, + {:title => _("Chargeback")}, + ], + } + end + + menu_section :chargeback +end diff --git a/app/controllers/chargeback_controller.rb b/app/controllers/chargeback_controller.rb deleted file mode 100644 index 3040af3bd08..00000000000 --- a/app/controllers/chargeback_controller.rb +++ /dev/null @@ -1,991 +0,0 @@ -class ChargebackController < ApplicationController - before_action :check_privileges - before_action :get_session_data - after_action :cleanup_action - after_action :set_session_data - - include Mixins::SavedReportPaging - include Mixins::GenericSessionMixin - include Mixins::BreadcrumbsMixin - - CB_X_BUTTON_ALLOWED_ACTIONS = { - 'chargeback_rates_copy' => :cb_rate_edit, - 'chargeback_rates_delete' => :cb_rates_delete, - 'chargeback_rates_edit' => :cb_rate_edit, - 'chargeback_rates_new' => :cb_rate_edit - }.freeze - - def x_button - generic_x_button(CB_X_BUTTON_ALLOWED_ACTIONS) - end - - def x_show - @explorer = true - if x_active_tree == :cb_rates_tree - @record = identify_record(params[:id], ChargebackRate) - nodeid = x_build_node_id(@record) - params[:id] = "xx-#{@record.rate_type}_#{nodeid}" - params[:tree] = x_active_tree.to_s - tree_select - end - end - - def accordion_select - self.x_active_accord = params[:id].sub(/_accord$/, '') - self.x_active_tree = "#{x_active_accord}_tree" - get_node_info(x_node) - replace_right_cell - end - - def tree_select - self.x_active_tree = params[:tree] if params[:tree] - self.x_node = params[:id] - get_node_info(x_node) - replace_right_cell - end - - def explorer - @breadcrumbs = [] - @explorer = true - build_accordions_and_trees - - @right_cell_text = case x_active_tree - when :cb_rates_tree then _("All Chargeback Rates") - when :cb_assignments_tree then _("All Assignments") - when :cb_reports_tree then _("All Saved Chargeback Reports") - end - set_form_locals if @in_a_form - session[:changed] = false - - render :layout => "application" unless request.xml_http_request? - end - - def set_form_locals - if x_active_tree == :cb_rates_tree - @x_edit_buttons_locals = {:action_url => 'cb_rate_edit'} - elsif x_active_tree == :cb_assignments_tree - @x_edit_buttons_locals = { - :action_url => 'cb_assign_update', - :no_cancel => true, - :multi_record => true - } - end - end - - # Show the main Schedules list view - def cb_rates_list - @gtl_type = "list" - @explorer = true - if params[:ppsetting] # User selected new per page value - @items_per_page = params[:ppsetting].to_i # Set the new per page value - @settings.store_path(:perpage, @gtl_type.to_sym, @items_per_page) # Set the per page setting for this gtl type - end - @sortcol = session[:rates_sortcol].nil? ? 0 : session[:rates_sortcol].to_i - @sortdir = session[:rates_sortdir].nil? ? "ASC" : session[:rates_sortdir] - - @view, @pages = get_view(ChargebackRate, :named_scope => [[:with_rate_type, x_node.split('-').last]]) # Get the records (into a view) and the paginator - - @current_page = @pages[:current] unless @pages.nil? # save the current page number - session[:rates_sortcol] = @sortcol - session[:rates_sortdir] = @sortdir - - update_gtl_div('cb_rates_list') if pagination_or_gtl_request? && @show_list - end - - def cb_rate_edit - assert_privileges(params[:pressed]) if params[:pressed] - case params[:button] - when "cancel" - if params[:id] - add_flash(_("Edit of Chargeback Rate \"%{name}\" was cancelled by the user") % {:name => session[:edit][:new][:description]}) - else - add_flash(_("Add of new Chargeback Rate was cancelled by the user")) - end - get_node_info(x_node) - @edit = session[:edit] = nil # clean out the saved info - session[:changed] = false - replace_right_cell - when "save", "add" - id = params[:button] == "save" ? params[:id] : "new" - return unless load_edit("cbrate_edit__#{id}", "replace_cell__chargeback") - - @rate = params[:button] == "add" ? ChargebackRate.new : ChargebackRate.find(params[:id]) - if @edit[:new][:description].nil? || @edit[:new][:description] == "" - render_flash(_("Description is required"), :error) - return - end - @rate.description = @edit[:new][:description] - @rate.rate_type = @edit[:new][:rate_type] if @edit[:new][:rate_type] - - cb_rate_set_record_vars - # Detect errors saving tiers - tiers_valid = @rate_tiers.all? { |tiers| tiers.all?(&:valid?) } - - @rate.chargeback_rate_details.replace(@rate_details) - @rate.chargeback_rate_details.each_with_index do |_detail, i| - @rate_details[i].save_tiers(@rate_tiers[i]) - end - - tiers_valid &&= @rate_details.all? { |rate_detail| rate_detail.errors.messages.blank? } - - if tiers_valid && @rate.save - if params[:button] == "add" - AuditEvent.success(build_created_audit(@rate, @edit)) - add_flash(_("Chargeback Rate \"%{name}\" was added") % {:name => @rate.description}) - else - AuditEvent.success(build_saved_audit(@rate, @edit)) - add_flash(_("Chargeback Rate \"%{name}\" was saved") % {:name => @rate.description}) - end - @edit = session[:edit] = nil # clean out the saved info - session[:changed] = @changed = false - get_node_info(x_node) - replace_right_cell(:replace_trees => [:cb_rates]) - else - @rate.errors.each do |field, msg| - add_flash("#{field.to_s.capitalize} #{msg}", :error) - end - @rate_details.each do |detail| - display_detail_errors(detail, detail.errors) - end - @rate_tiers.each_with_index do |tiers, detail_index| - tiers.each do |tier| - display_detail_errors(@rate_details[detail_index], tier.errors) - end - end - @changed = session[:changed] = (@edit[:new] != @edit[:current]) - javascript_flash - end - - when "reset", nil # displaying edit from for actions: new, edit or copy - @in_a_form = true - @_params[:id] ||= find_checked_items[0] - session[:changed] = params[:pressed] == 'chargeback_rates_copy' - - @rate = new_rate_edit? ? ChargebackRate.new : ChargebackRate.find(params[:id]) - @record = @rate - - if params[:pressed] == 'chargeback_rates_edit' && @rate.default? - render_flash(_("Default Chargeback Rate \"%{name}\" cannot be edited.") % {:name => @rate.description}, :error) - return - end - - cb_rate_set_form_vars - - add_flash(_("All changes have been reset"), :warning) if params[:button] == "reset" - - replace_right_cell - end - end - - # AJAX driven routine to check for changes in ANY field on the form - def cb_rate_form_field_changed - return unless load_edit("cbrate_edit__#{params[:id]}", "replace_cell__chargeback") - - cb_rate_get_form_vars - render :update do |page| - page << javascript_prologue - changed = (@edit[:new] != @edit[:current]) - # Update the new column with the code of the currency selected by the user - page.replace('chargeback_rate_currency', :partial => 'cb_rate_currency') - page << javascript_for_miq_button_visibility(changed) - end - end - - def cb_rate_show - @display = "main" - if @record.nil? - flash_to_session(_('Error: Record no longer exists in the database'), :error) - redirect_to(:action => 'cb_rates_list') - return - end - end - - # Delete all selected or single displayed action(s) - def cb_rates_delete - assert_privileges("chargeback_rates_delete") - rates = [] - if !params[:id] # showing a list - rates = find_checked_items - if rates.empty? - add_flash(_("No Chargeback Rates were selected for deletion"), :error) - end - else # showing 1 rate, delete it - cb_rate = ChargebackRate.find_by(:id => params[:id]) - self.x_node = x_node.split('_').first - if cb_rate.nil? - add_flash(_("Chargeback Rate no longer exists"), :error) - else - rates.push(params[:id]) - end - end - process_cb_rates(rates, 'destroy') if rates.present? - - cb_rates_list - @right_cell_text = _("%s Chargeback Rates") % {:typ => x_node.split('-').last} - replace_right_cell(:replace_trees => [:cb_rates]) - end - - # AJAX driven routine to check for changes in ANY field on the form - def cb_assign_field_changed - return unless load_edit("cbassign_edit__#{x_node}", "replace_cell__chargeback") - - cb_assign_get_form_vars - render :update do |page| - page << javascript_prologue - except = %i[cbshow_typ cbtag_cat cblabel_key] - changed = (@edit[:new].except(*except) != @edit[:current].except(*except)) - page.replace("cb_assignment_div", :partial => "cb_assignments") if params[:cbshow_typ] || params[:cbtag_cat] || params[:cblabel_key] - page << javascript_for_miq_button_visibility(changed) - end - end - - # Add a new tier at the end - def cb_tier_add - detail_index = params[:detail_index] - ii = detail_index.to_i - - @edit = session[:edit] - detail = @edit[:new][:details][ii] - - @edit[:new][:num_tiers][ii] = detail[:chargeback_tiers].to_a.length if detail[:chargeback_tiers] - @edit[:new][:num_tiers][ii] = 1 unless @edit[:new][:num_tiers][ii] || @edit[:new][:num_tiers][ii].zero? - @edit[:new][:num_tiers][ii] += 1 - - tier_index = @edit[:new][:num_tiers][ii] - 1 - tier_list = @edit[:new][:tiers][ii] - tier_list[tier_index] = {} - - tier = tier_list[tier_index] - tier[:start] = tier_list[tier_index - 1][:finish] - tier[:finish] = Float::INFINITY - tier[:fixed_rate] = 0.0 - tier[:variable_rate] = 0.0 - - code_currency = Currency.find_by(:id => detail[:currency]).code - add_row(detail_index, tier_index - 1, code_currency) - end - - # Remove the selected tier - def cb_tier_remove - @edit = session[:edit] - index = params[:index] - detail_index, tier_to_remove_index = index.split("-") - detail_index = detail_index.to_i - @edit[:new][:num_tiers][detail_index] = @edit[:new][:num_tiers][detail_index] - 1 - - # Delete tier record - @edit[:new][:tiers][detail_index].delete_at(tier_to_remove_index.to_i) - - @changed = session[:changed] = true - - render :update do |page| - page << javascript_prologue - page.replace_html("chargeback_rate_edit_form", :partial => "cb_rate_edit_table") - page << javascript_for_miq_button_visibility(@changed) - end - end - - def cb_assign_update - if params[:button] == "reset" - get_node_info(x_node) - add_flash(_("All changes have been reset"), :warning) - replace_right_cell - else - return unless load_edit("cbassign_edit__#{x_node}", "replace_cell__chargeback") - - cb_assign_set_record_vars - rate_type = x_node.split('-').last - begin - ChargebackRate.set_assignments(rate_type, @edit[:set_assignments]) - rescue StandardError => bang - render_flash(_("Error during 'Rate assignments': %{error_message}") % {:error_message => bang.message}, :error) - else - add_flash(_("Rate Assignments saved")) - get_node_info(x_node) - replace_right_cell - end - end - end - - def title - @title = _("Chargeback") - end - - private ############################ - - def features - [ - { - :role => "chargeback_reports", - :name => :cb_reports, - :title => _("Reports") - }, - { - :role => "chargeback_rates", - :name => :cb_rates, - :title => _("Rates") - }, - { - :role => "chargeback_assignments", - :name => :cb_assignments, - :title => _("Assignments") - } - ].map { |hsh| ApplicationController::Feature.new_with_hash(hsh) } - end - - # Build a Chargeback Reports explorer tree - def cb_rpts_build_tree - TreeBuilderChargebackReports.new("cb_reports_tree", @sb) - end - - def cb_rpts_show_saved_report - @sb[:last_savedreports_id] = parse_nodetype_and_id(params[:id]).last if params[:id] && params[:id] != "cb_reports_accord" - cb_rpts_fetch_saved_report(@sb[:last_savedreports_id]) - @sb[:parent_reports] = nil if @report.blank? - end - - def cb_rpts_fetch_saved_report(id) - rr = MiqReportResult.for_user(current_user).find(id.to_s.split('-').last) - if rr.nil? # Saved report no longer exists - @report = nil - return - end - @right_cell_text ||= _("Saved Chargeback Report [%{name}]") % {:name => rr.name} - if !current_user.miq_group_ids.include?(rr.miq_group_id) && !report_admin_user? - add_flash(_("Report is not authorized for the logged in user"), :error) - @saved_reports = cb_rpts_get_all_reps(id.split('-')[1]) - return - else - @report_result_id = session[:report_result_id] = rr.id - session[:report_result_runtime] = rr.last_run_on - if rr.status.downcase == "complete" - session[:rpt_task_id] = nil - if rr.valid_report_column? - if rr.contains_records? - @html = report_first_page(rr) # Get the first page of the results - if @report.graph.present? - @render_chart = true - @ght_type = "hybrid" - else - @ght_type = "tabular" - end - @report.extras ||= {} # Create extras hash - @report.extras[:to_html] ||= @html # Save the html report - else - add_flash(_("No records found for this report"), :warning) - end - else - @saved_reports = cb_rpts_get_all_reps(rr.miq_report_id.to_s) - rep = MiqReport.find(rr.miq_report_id) - if x_active_tree == :cb_reports_tree - self.x_node = "reports-#{rep.id}" - end - return - end - end - end - end - - def get_node_info(node, show_list = true) - @show_list = show_list - node = valid_active_node(node) - if x_active_tree == :cb_rates_tree - if node == "root" - @record = nil - @right_cell_text = _("All Chargeback Rates") - elsif ["xx-Compute", "xx-Storage"].include?(node) - @record = nil - @right_cell_text = case node - when "xx-Compute" then _("Compute Chargeback Rates") - when "xx-Storage" then _("Storage Chargeback Rates") - end - cb_rates_list - else - @record = ChargebackRate.find(parse_nodetype_and_id(node).last) - @sb[:action] = nil - @right_cell_text = case @record.rate_type - when "Compute" then _("Compute Chargeback Rate \"%{name}\"") % {:name => @record.description} - when "Storage" then _("Storage Chargeback Rate \"%{name}\"") % {:name => @record.description} - end - cb_rate_show - end - elsif x_active_tree == :cb_assignments_tree - if ["xx-Compute", "xx-Storage"].include?(node) - cb_assign_set_form_vars - @right_cell_text = case node - when "xx-Compute" then _("Compute Rate Assignments") - when "xx-Storage" then _("Storage Rate Assignments") - end - else - @right_cell_text = _("All Assignments") - end - elsif x_active_tree == :cb_reports_tree - @nodetype = node.split("-")[0] - nodes = x_node.split('_') - nodes_len = nodes.length - - # On the root node - if x_node == "root" - cb_rpt_build_folder_nodes - @right_cell_div = "reports_list_div" - @right_cell_text = _("All Saved Chargeback Reports") - elsif nodes_len == 2 - # On a saved report node - cb_rpts_show_saved_report - if @report - s = MiqReportResult.for_user(current_user).find(nodes.last.split('-').last) - - @right_cell_div = "reports_list_div" - @right_cell_text = _("Saved Chargeback Report \"%{last_run_on}\"") % {:last_run_on => format_timezone(s.last_run_on, Time.zone, "gtl")} - else - add_flash(_("Selected Saved Chargeback Report no longer exists"), :warning) - self.x_node = nodes[0..1].join("_") - cb_rpts_build_tree # Rebuild tree - end - # On a saved reports parent node - else - # saved reports under report node on saved report accordion - @saved_reports = cb_rpts_get_all_reps(nodes[0].split('-')[1]) - if @saved_reports.present? - @sb[:sel_saved_rep_id] = nodes[1] - @right_cell_div = "reports_list_div" - miq_report = MiqReport.for_user(current_user).find(@sb[:miq_report_id]) - @right_cell_text = _("Saved Chargeback Reports \"%{report_name}\"") % {:report_name => miq_report.name} - @sb[:parent_reports] = nil if @sb[:saved_reports].present? # setting it to nil so saved reports can be displayed, unless all saved reports were deleted - else - add_flash(_("Selected Chargeback Report no longer exists"), :warning) - self.x_node = nodes[0] - @saved_reports = nil - cb_rpts_build_tree # Rebuild tree - end - end - end - {:view => @view, :pages => @pages} - end - - def cb_rpt_build_folder_nodes - @parent_reports = {} - - MiqReportResult.with_saved_chargeback_reports.select_distinct_results.each_with_index do |sr, sr_idx| - @parent_reports[sr.miq_report.name] = "#{sr.miq_report_id}-#{sr_idx}" - end - end - - def cb_rpts_get_all_reps(nodeid) - return [] if nodeid.blank? - - @sb[:miq_report_id] = nodeid - miq_report = MiqReport.for_user(current_user).find(@sb[:miq_report_id]) - saved_reports = miq_report.miq_report_results.with_current_user_groups - .select("id, miq_report_id, name, last_run_on, report_source") - .order(:last_run_on => :desc) - - @sb[:tree_typ] = "reports" - @right_cell_text = _("Report \"%{report_name}\"") % {:report_name => miq_report.name} - saved_reports - end - - def cb_rates_build_tree - TreeBuilderChargebackRates.new("cb_rates_tree", @sb) - end - - # Common Schedule button handler routines - def process_cb_rates(rates, task) - process_elements(rates, ChargebackRate, task) - end - - # Set form variables for edit - def cb_rate_set_form_vars - @edit = {} - @edit[:new] = HashWithIndifferentAccess.new - @edit[:current] = HashWithIndifferentAccess.new - @edit[:new][:tiers] = [] - @edit[:new][:num_tiers] = [] - @edit[:new][:description] = @rate.description - @edit[:new][:rate_type] = @rate.rate_type || x_node.split('-').last - @edit[:new][:details] = [] - - tiers = [] - rate_details = @rate.chargeback_rate_details - rate_details = ChargebackRateDetail.default_rate_details_for(@edit[:new][:rate_type]) if new_rate_edit? - - # Select the currency of the first chargeback_rate_detail. All the chargeback_rate_details have the same currency - @edit[:new][:currency] = rate_details[0].detail_currency.id - @edit[:new][:code_currency] = "#{rate_details[0].detail_currency.symbol} [#{rate_details[0].detail_currency.full_name}]" - - rate_details.each_with_index do |detail, detail_index| - temp = detail.slice(*ChargebackRateDetail::FORM_ATTRIBUTES) - temp[:report_column_name] = Dictionary.gettext(detail.chargeable_field.metric_key, :type => :column, :notfound => :titleize) - temp[:group] = detail.chargeable_field.group - temp[:per_time] ||= "hourly" - - temp[:currency] = detail.detail_currency.id - - if detail.chargeable_field.detail_measure.present? - temp[:detail_measure] = {} - temp[:detail_measure][:measures] = detail.chargeable_field.detail_measure.measures - temp[:chargeback_rate_detail_measure_id] = detail.chargeable_field.detail_measure.id - end - - temp[:id] = params[:pressed] == 'chargeback_rates_copy' ? nil : detail.id - temp[:sub_metrics] = detail.sub_metrics - temp[:sub_metric_human] = detail.sub_metric_human - - tiers[detail_index] ||= [] - - detail.chargeback_tiers.each do |tier| - new_tier = tier.slice(*ChargebackTier::FORM_ATTRIBUTES) - new_tier[:id] = params[:pressed] == 'chargeback_rates_copy' ? nil : tier.id - new_tier[:chargeback_rate_detail_id] = params[:pressed] == 'chargeback_rates_copy' ? nil : detail.id - new_tier[:start] = new_tier[:start].to_f - new_tier[:finish] = ChargebackTier.to_float(new_tier[:finish]) - tiers[detail_index].push(new_tier) - end - - @edit[:new][:tiers][detail_index] = tiers[detail_index] - @edit[:new][:num_tiers][detail_index] = tiers[detail_index].size - @edit[:new][:details].push(temp) - end - - @edit[:new][:per_time_types] = ChargebackRateDetail::PER_TIME_TYPES.map { |x, y| [x, _(y)] }.to_h - - if params[:pressed] == 'chargeback_rates_copy' - @rate.id = nil - @edit[:new][:description] = "copy of #{@rate.description}" - end - - @edit[:rec_id] = @rate.id || nil - @edit[:key] = "cbrate_edit__#{@rate.id || "new"}" - @edit[:current] = copy_hash(@edit[:new]) - session[:edit] = @edit - end - - # Get variables from edit form - def cb_rate_get_form_vars - @edit[:new][:description] = params[:description] if params[:description] - if params[:currency] - @edit[:new][:currency] = params[:currency].to_i - rate_detail_currency = Currency.find(params[:currency]) - @edit[:new][:code_currency] = "#{rate_detail_currency.symbol} [#{rate_detail_currency.full_name}]" - end - @edit[:new][:details].each_with_index do |detail, detail_index| - %i[per_time per_unit sub_metric].each do |measure| - key = "#{measure}_#{detail_index}".to_sym - detail[measure] = params[key] if params[key] - end - # Add currencies to chargeback_controller.rb - detail[:currency] = params[:currency].to_i if params[:currency] - - # Save tiers into @edit - (0..@edit[:new][:num_tiers][detail_index].to_i - 1).each do |tier_index| - tier = @edit[:new][:tiers][detail_index][tier_index] || {} - %i[fixed_rate variable_rate start finish].each do |field| - key = "#{field}_#{detail_index}_#{tier_index}".to_sym - tier[field] = params[key] if params[key] - end - end - end - end - - def cb_rate_set_record_vars - @rate_details = [] - @rate_tiers = [] - @edit[:new][:details].each_with_index do |detail, detail_index| - rate_detail = detail[:id] ? ChargebackRateDetail.find(detail[:id]) : ChargebackRateDetail.new - rate_detail.attributes = detail.slice(*ChargebackRateDetail::FORM_ATTRIBUTES) - rate_detail.sub_metric = detail[:sub_metric] if rate_detail.sub_metric - rate_detail_edit = @edit[:new][:details][detail_index] - # C: Record the currency selected in the edit view, in my chargeback_rate_details table - rate_detail.chargeback_rate_detail_currency_id = rate_detail_edit[:currency] - rate_detail.chargeback_rate_detail_measure_id = rate_detail_edit[:chargeback_rate_detail_measure_id] - rate_detail.chargeback_rate_id = @rate.id - # Save tiers into @sb - rate_tiers = [] - @edit[:new][:tiers][detail_index].each do |tier| - rate_tier = tier[:id] ? ChargebackTier.find(tier[:id]) : ChargebackTier.new - tier[:start] = Float::INFINITY if tier[:start].blank? - tier[:finish] = Float::INFINITY if tier[:finish].blank? - rate_tier.attributes = tier.slice(*ChargebackTier::FORM_ATTRIBUTES) - rate_tier.chargeback_rate_detail_id = rate_detail.id - rate_tiers.push(rate_tier) - end - @rate_tiers[detail_index] = rate_tiers - @rate_details.push(rate_detail) - end - end - - # Set record vars for save - def cb_assign_set_record_vars - @edit[:set_assignments] = [] - if @edit[:new][:cbshow_typ].ends_with?("-tags") - assigned_rates_from_all_categories = @edit[:cb_assign][:tags].values.reduce({}, :merge) - assigned_rates_from_all_categories.each_key do |id| - key = "#{@edit[:new][:cbshow_typ]}__#{id}" - next if @edit[:new][key].nil? || @edit[:new][key] == "nil" - - temp = { - :cb_rate => ChargebackRate.find(@edit[:new][key]), - :tag => [Classification.find(id)], - } - temp[:tag].push(@edit[:new][:cbshow_typ].split("-").first) - @edit[:set_assignments].push(temp) - end - elsif @edit[:new][:cbshow_typ].ends_with?("-labels") - @edit[:cb_assign][:docker_label_values_saved].each_key do |id| - key = "#{@edit[:new][:cbshow_typ]}__#{id}" - next if @edit[:new][key].nil? || @edit[:new][key] == "nil" - - temp = { - :cb_rate => ChargebackRate.find(@edit[:new][key]), - :label => [CustomAttribute.find(id)] - } - temp[:label].push(@edit[:new][:cbshow_typ].split("-").first) - @edit[:set_assignments].push(temp) - end - else - @edit[:cb_assign][:cis].each_key do |id| - key = "#{@edit[:new][:cbshow_typ]}__#{id}" - next if @edit[:new][key].nil? || @edit[:new][key] == "nil" - - temp = {:cb_rate => ChargebackRate.find(@edit[:new][key])} - model = if @edit[:new][:cbshow_typ] == "enterprise" - MiqEnterprise - elsif @edit[:new][:cbshow_typ] == "ems_container" - ExtManagementSystem - else - Object.const_get(@edit[:new][:cbshow_typ].camelize) rescue nil - end - - temp[:object] = model.find(id) unless model.nil? - @edit[:set_assignments].push(temp) - end - end - end - - # Set form variables for edit - def cb_assign_set_form_vars - @edit = { - :cb_rates => {}, - :cb_assign => {}, - } - ChargebackRate.all.each do |cbr| - if cbr.rate_type == x_node.split('-').last - @edit[:cb_rates][cbr.id.to_s] = cbr.description - end - end - @edit[:key] = "cbassign_edit__#{x_node}" - @edit[:new] = HashWithIndifferentAccess.new - @edit[:current] = HashWithIndifferentAccess.new - @edit[:current_assignment] = ChargebackRate.get_assignments(x_node.split('-').last) - unless @edit[:current_assignment].empty? - @edit[:new][:cbshow_typ] = case @edit[:current_assignment][0][:object] - when EmsCluster - "ems_cluster" - when ExtManagementSystem, ManageIQ::Providers::ContainerManager - "ext_management_system" - when MiqEnterprise - "enterprise" - when NilClass - if @edit[:current_assignment][0][:tag] - "#{@edit[:current_assignment][0][:tag][1]}-tags" - else - "#{@edit[:current_assignment][0][:label][1]}-labels" - end - else - @edit[:current_assignment][0][:object].class.name.downcase - end - end - if @edit[:new][:cbshow_typ]&.ends_with?("-tags") - get_categories_all - tag = @edit[:current_assignment][0][:tag][0] - if tag - @edit[:new][:cbtag_cat] = tag["parent_id"].to_s - get_tags_all - else - @edit[:current_assignment] = [] - end - elsif @edit[:new][:cbshow_typ]&.ends_with?("-labels") - get_docker_labels_all_keys - assigned_label = @edit[:current_assignment][0][:label][0] - if assigned_label - label = @edit[:cb_assign][:docker_label_keys].detect { |_key, value| value == assigned_label.name } - label ||= @edit[:cb_assign][:docker_label_default_keys].detect { |_key, value| value == assigned_label.name } - @edit[:new][:cblabel_key] = label.first - get_docker_labels_all_values(label.first) - else - @edit[:current_assignment] = [] - end - elsif @edit[:new][:cbshow_typ] - get_cis_all - end - - @edit[:current_assignment].each do |el| - if el[:object] - @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:object]["id"]}"] = el[:cb_rate]["id"].to_s - elsif el[:tag] - @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:tag][0]["id"]}"] = el[:cb_rate]["id"].to_s - elsif el[:label] - @edit[:new]["#{@edit[:new][:cbshow_typ]}__#{el[:label][0].id}"] = el[:cb_rate]["id"].to_s - end - end - - @edit[:current] = copy_hash(@edit[:new]) - session[:edit] = @edit - @in_a_form = true - end - - def new_rate_edit? - params[:id] == 'new' || params[:pressed] == 'chargeback_rates_new' - end - - def get_categories_all - @edit[:cb_assign][:cats] = {} - Classification.categories.select { |c| c.show && !c.entries.empty? }.each do |c| - @edit[:cb_assign][:cats][c.id.to_s] = c.description - end - end - - def get_tags_all - @edit[:cb_assign][:tags] ||= {} - - Classification.all.each do |category| - @edit[:cb_assign][:tags][category.id] ||= {} - category.entries.each do |entry| - @edit[:cb_assign][:tags][category.id][entry.id.to_s] = entry.description - end - end - end - - DEFAULT_CHARGEBACK_LABELS = ["com.redhat.component"].freeze - - def get_docker_labels_all_keys - @edit[:cb_assign][:docker_label_keys] = {} - @edit[:cb_assign][:docker_label_default_keys] = {} - CustomAttribute.where(:section => "docker_labels").pluck(:id, :name).uniq(&:second).each do |label| - if DEFAULT_CHARGEBACK_LABELS.include?(label.second) - @edit[:cb_assign][:docker_label_default_keys][label.first.to_s] = label.second - else - @edit[:cb_assign][:docker_label_keys][label.first.to_s] = label.second - end - end - end - - def get_docker_labels_all_values(label_id) - @edit[:cb_assign][:docker_label_values] = {} - @edit[:cb_assign][:docker_label_values_saved] = {} - - CustomAttribute.where(:section => "docker_labels").pluck(:id, :value).each do |label| - @edit[:cb_assign][:docker_label_values_saved][label.first.to_s] = label.second - end - - return if label_id && label_id == 'null' || label_id.nil? - - label_name = CustomAttribute.find(label_id).name - - CustomAttribute.where(:section => "docker_labels", :name => label_name).pluck(:id, :value).uniq(&:second).each do |label| - @edit[:cb_assign][:docker_label_values][label.first.to_s] = label.second - end - end - - WHITELIST_INSTANCE_TYPE = %w[enterprise storage ext_management_system ems_cluster tenant ems_container].freeze - NOTHING_FORM_VALUE = "nil".freeze - - def get_cis_all - @edit[:cb_assign][:cis] = {} - klass = @edit[:new][:cbshow_typ] - return if klass == NOTHING_FORM_VALUE || klass.nil? # no rate was selected - unless WHITELIST_INSTANCE_TYPE.include?(klass) - raise ArgumentError, "Received: #{klass}, expected one of #{WHITELIST_INSTANCE_TYPE}" - end - - all_of_classtype = - if klass == "enterprise" - MiqEnterprise.all - elsif klass == "ext_management_system" - ExtManagementSystem.all - else - klass.classify.constantize.all - end - @edit[:cb_assign][:hierarchy] ||= {} - all_of_classtype.each do |instance| - @edit[:cb_assign][:cis][instance.id] = instance.name - if klass == "ems_cluster" - provider_name = instance.ext_management_system.name - @edit[:cb_assign][:cis][instance.id] = "#{provider_name}/#{instance.name}" - end - next unless klass == "tenant" && instance.root? - - @edit[:cb_assign][:hierarchy][instance.id] = {} - @edit[:cb_assign][:hierarchy][instance.id][:name] = instance.name - @edit[:cb_assign][:hierarchy][instance.id][:subtenant] = instance.build_tenant_tree - end - end - - def cb_assign_params_to_edit(cb_assign_key, tag_category_id = nil) - current_assingments = cb_assign_key == :tags ? @edit[:cb_assign][cb_assign_key].try(:[], tag_category_id) : @edit[:cb_assign][cb_assign_key] - - return unless current_assingments - - current_assingments.each_key do |id| - key = "#{@edit[:new][:cbshow_typ]}__#{id}" - @edit[:new][key] = params[key].to_s if params[key] - end - end - - # Get variables from edit form - def cb_assign_get_form_vars - @edit[:new][:cbshow_typ] = params[:cbshow_typ] if params[:cbshow_typ] - @edit[:new][:cbtag_cat] = nil if params[:cbshow_typ] # Reset categories pull down if assign to selection is changed - @edit[:new][:cbtag_cat] = params[:cbtag_cat].to_s if params[:cbtag_cat] - @edit[:new][:cblabel_key] = nil if params[:cbshow_typ] - @edit[:new][:cblabel_key] = params[:cblabel_key].to_s if params[:cblabel_key] - - if @edit[:new][:cbshow_typ].ends_with?("-tags") - get_categories_all - get_tags_all - elsif @edit[:new][:cbshow_typ].ends_with?("-labels") - get_docker_labels_all_keys - get_docker_labels_all_values(@edit[:new][:cblabel_key]) - else - get_cis_all - end - - cb_assign_params_to_edit(:cis) - cb_assign_params_to_edit(:tags, @edit[:new][:cbtag_cat].try(:to_i)) - cb_assign_params_to_edit(:docker_label_values) - end - - def replace_right_cell(options = {}) - replace_trees = Array(options[:replace_trees]) - replace_trees = @replace_trees if @replace_trees # get_node_info might set this - @explorer = true - c_tb = build_toolbar(center_toolbar_filename) - - # Build a presenter to render the JS - presenter = ExplorerPresenter.new(:active_tree => x_active_tree) - reload_trees_by_presenter(presenter, [cb_rates_build_tree]) if replace_trees.include?(:cb_rates) - - # FIXME - # if params[:action].ends_with?("_delete") - # page << "miqTreeActivateNodeSilently('#{x_active_tree.to_s}', '<%= x_node %>');" - # end - # presenter[:select_node] = x_node if params[:action].ends_with?("_delete") - presenter[:osf_node] = x_node - - case x_active_tree - when :cb_rates_tree - # Rates accordion - if c_tb.present? - presenter.reload_toolbars(:center => c_tb) - end - presenter.set_visibility(c_tb.present?, :toolbar) - presenter.update(:main_div, r[:partial => 'rates_tabs']) - when :cb_assignments_tree - # Assignments accordion - presenter.update(:main_div, r[:partial => "assignments_tabs"]) - when :cb_reports_tree - if c_tb.present? - presenter.reload_toolbars(:center => c_tb) - presenter.show(:toolbar) - else - presenter.hide(:toolbar) - end - presenter.update(:main_div, r[:partial => 'reports_list']) - if @html - presenter.update(:paging_div, r[:partial => 'layouts/saved_report_paging_bar', - :locals => @sb[:pages]]) - presenter.show(:paging_div) - else - presenter.hide(:paging_div) - end - end - - if @record || @in_a_form || - (@pages && (@items_per_page == ONE_MILLION || @pages[:items] == 0)) - if %w[chargeback_rates_copy chargeback_rates_edit chargeback_rates_new].include?(@sb[:action]) || - (x_active_tree == :cb_assignments_tree && %w[Compute Storage].include?(x_node.split('-').last)) - presenter.hide(:toolbar) - # incase it was hidden for summary screen, and incase there were no records on show_list - presenter.show(:paging_div, :form_buttons_div).remove_paging - locals = {:record_id => @edit[:rec_id]} - if x_active_tree == :cb_rates_tree - locals[:action_url] = 'cb_rate_edit' - else - locals.update( - :action_url => 'cb_assign_update', - :no_cancel => true, - :multi_record => true - ) - end - presenter.update(:form_buttons_div, r[:partial => 'layouts/x_edit_buttons', :locals => locals]) - else - # Added so buttons can be turned off even tho div is not being displayed it still pops up Abandon changes box when trying to change a node on tree after saving a record - presenter.hide(:buttons_on).show(:toolbar).hide(:paging_div) - presenter.hide(:form_buttons_div) if params[:button] - end - else - presenter.hide(:form_buttons_div) - if (x_active_tree == :cb_assignments_tree && x_node == "root") || - (x_active_tree == :cb_reports_tree && !@report) || - (x_active_tree == :cb_rates_tree && x_node == "root") - presenter.hide(:toolbar).remove_paging - end - presenter.show(:paging_div) - end - - presenter[:record_id] = determine_record_id_for_presenter - - presenter[:clear_gtl_list_grid] = @gtl_type && @gtl_type != 'list' - - presenter[:right_cell_text] = @right_cell_text - unless x_active_tree == :cb_assignments_tree - presenter[:lock_sidebar] = @in_a_form && @edit - end - - presenter.update(:breadcrumbs, r[:partial => 'layouts/breadcrumbs']) - - render :json => presenter.for_render - end - - def get_session_data - super - @current_page = session[:chargeback_current_page] - end - - def set_session_data - super - session[:chargeback_current_page] = @current_page - end - - def display_detail_errors(detail, errors) - errors.each { |field, msg| add_flash("'#{detail.chargeable_field.description}' #{field.to_s.humanize.downcase} #{msg}", :error) } - end - - def add_row(i, pos, code_currency) - locals = {:code_currency => code_currency} - render :update do |page| - page << javascript_prologue - # Update the first row to change the colspan - page.replace("rate_detail_row_#{i}_0", - :partial => "tier_first_row", - :locals => locals) - # Insert the new tier after the last one - page.insert_html(:after, - "rate_detail_row_#{i}_#{pos}", - :partial => "tier_row", - :locals => locals) - page << javascript_for_miq_button_visibility(true) - end - end - - def breadcrumbs_options - { - :breadcrumbs => [ - {:title => _("Overview")}, - {:title => _("Chargeback")}, - ], - } - end - - menu_section :vi -end diff --git a/app/controllers/chargeback_rate_controller.rb b/app/controllers/chargeback_rate_controller.rb new file mode 100644 index 00000000000..e1b3358080d --- /dev/null +++ b/app/controllers/chargeback_rate_controller.rb @@ -0,0 +1,515 @@ +class ChargebackRateController < ApplicationController + before_action :check_privileges + before_action :get_session_data + after_action :cleanup_action + after_action :set_session_data + + include Mixins::GenericSessionMixin + include Mixins::BreadcrumbsMixin + + CB_X_BUTTON_ALLOWED_ACTIONS = { + 'chargeback_rates_copy' => :cb_rate_edit, + 'chargeback_rates_delete' => :cb_rates_delete, + 'chargeback_rates_edit' => :cb_rate_edit, + 'chargeback_rates_new' => :cb_rate_edit + }.freeze + + def x_button + generic_x_button(CB_X_BUTTON_ALLOWED_ACTIONS) + end + + def x_show + @explorer = true + @record = identify_record(params[:id], ChargebackRate) + nodeid = x_build_node_id(@record) + params[:id] = "xx-#{@record.rate_type}_#{nodeid}" + params[:tree] = x_active_tree.to_s + tree_select + redirect_to :action => 'explorer' unless request.xml_http_request? # Ajax request means in explorer + end + + def tree_select + self.x_active_tree = :cb_rates_tree + self.x_node = params[:id] + get_node_info(x_node) + replace_right_cell if request.xml_http_request? # Ajax request means in explorer + end + + def explorer + @breadcrumbs = [] + @explorer = true + build_accordions_and_trees + + @right_cell_text ||= _("All Chargeback Rates") + set_form_locals if @in_a_form + session[:changed] = false + + render :layout => "application" unless request.xml_http_request? + end + + def set_form_locals + @x_edit_buttons_locals = {:action_url => 'cb_rate_edit'} + end + + # Show the main Schedules list view + def cb_rates_list + @gtl_type = "list" + @explorer = true + if params[:ppsetting] # User selected new per page value + @items_per_page = params[:ppsetting].to_i # Set the new per page value + @settings.store_path(:perpage, @gtl_type.to_sym, @items_per_page) # Set the per page setting for this gtl type + end + @sortcol = session[:rates_sortcol].nil? ? 0 : session[:rates_sortcol].to_i + @sortdir = session[:rates_sortdir].nil? ? "ASC" : session[:rates_sortdir] + + @view, @pages = get_view(ChargebackRate, :named_scope => [[:with_rate_type, x_node.split('-').last]]) # Get the records (into a view) and the paginator + + @current_page = @pages[:current] unless @pages.nil? # save the current page number + session[:rates_sortcol] = @sortcol + session[:rates_sortdir] = @sortdir + + update_gtl_div('cb_rates_list') if pagination_or_gtl_request? && @show_list + end + + def cb_rate_edit + assert_privileges(params[:pressed]) if params[:pressed] + case params[:button] + when "cancel" + if params[:id] + add_flash(_("Edit of Chargeback Rate \"%{name}\" was cancelled by the user") % {:name => session[:edit][:new][:description]}) + else + add_flash(_("Add of new Chargeback Rate was cancelled by the user")) + end + get_node_info(x_node) + @edit = session[:edit] = nil # clean out the saved info + session[:changed] = false + replace_right_cell + when "save", "add" + id = params[:button] == "save" ? params[:id] : "new" + return unless load_edit("cbrate_edit__#{id}", "replace_cell__chargeback_rate") + + @rate = params[:button] == "add" ? ChargebackRate.new : ChargebackRate.find(params[:id]) + if @edit[:new][:description].nil? || @edit[:new][:description] == "" + render_flash(_("Description is required"), :error) + return + end + @rate.description = @edit[:new][:description] + @rate.rate_type = @edit[:new][:rate_type] if @edit[:new][:rate_type] + + cb_rate_set_record_vars + # Detect errors saving tiers + tiers_valid = @rate_tiers.all? { |tiers| tiers.all?(&:valid?) } + + @rate.chargeback_rate_details.replace(@rate_details) + @rate.chargeback_rate_details.each_with_index do |_detail, i| + @rate_details[i].save_tiers(@rate_tiers[i]) + end + + tiers_valid &&= @rate_details.all? { |rate_detail| rate_detail.errors.messages.blank? } + + if tiers_valid && @rate.save + if params[:button] == "add" + AuditEvent.success(build_created_audit(@rate, @edit)) + add_flash(_("Chargeback Rate \"%{name}\" was added") % {:name => @rate.description}) + else + AuditEvent.success(build_saved_audit(@rate, @edit)) + add_flash(_("Chargeback Rate \"%{name}\" was saved") % {:name => @rate.description}) + end + @edit = session[:edit] = nil # clean out the saved info + session[:changed] = @changed = false + get_node_info(x_node) + replace_right_cell(:replace_trees => [:cb_rates]) + else + @rate.errors.each do |field, msg| + add_flash("#{field.to_s.capitalize} #{msg}", :error) + end + @rate_details.each do |detail| + display_detail_errors(detail, detail.errors) + end + @rate_tiers.each_with_index do |tiers, detail_index| + tiers.each do |tier| + display_detail_errors(@rate_details[detail_index], tier.errors) + end + end + @changed = session[:changed] = (@edit[:new] != @edit[:current]) + javascript_flash + end + + when "reset", nil # displaying edit from for actions: new, edit or copy + @in_a_form = true + @_params[:id] ||= find_checked_items[0] + session[:changed] = params[:pressed] == 'chargeback_rates_copy' + + @rate = new_rate_edit? ? ChargebackRate.new : ChargebackRate.find(params[:id]) + @record = @rate + + if params[:pressed] == 'chargeback_rates_edit' && @rate.default? + render_flash(_("Default Chargeback Rate \"%{name}\" cannot be edited.") % {:name => @rate.description}, :error) + return + end + + cb_rate_set_form_vars + + add_flash(_("All changes have been reset"), :warning) if params[:button] == "reset" + + replace_right_cell + end + end + + # AJAX driven routine to check for changes in ANY field on the form + def cb_rate_form_field_changed + return unless load_edit("cbrate_edit__#{params[:id]}", "replace_cell__chargeback") + + cb_rate_get_form_vars + render :update do |page| + page << javascript_prologue + changed = (@edit[:new] != @edit[:current]) + # Update the new column with the code of the currency selected by the user + page.replace('chargeback_rate_currency', :partial => 'cb_rate_currency') + page << javascript_for_miq_button_visibility(changed) + end + end + + def cb_rate_show + @display = "main" + if @record.nil? + flash_to_session(_('Error: Record no longer exists in the database'), :error) + redirect_to(:action => 'cb_rates_list') + return + end + end + + # Delete all selected or single displayed action(s) + def cb_rates_delete + assert_privileges("chargeback_rates_delete") + rates = [] + if !params[:id] # showing a list + rates = find_checked_items + if rates.empty? + add_flash(_("No Chargeback Rates were selected for deletion"), :error) + end + else # showing 1 rate, delete it + cb_rate = ChargebackRate.find_by(:id => params[:id]) + self.x_node = x_node.split('_').first + if cb_rate.nil? + add_flash(_("Chargeback Rate no longer exists"), :error) + else + rates.push(params[:id]) + end + end + process_cb_rates(rates, 'destroy') if rates.present? + + cb_rates_list + @right_cell_text = _("%s Chargeback Rates") % {:typ => x_node.split('-').last} + replace_right_cell(:replace_trees => [:cb_rates]) + end + + # Add a new tier at the end + def cb_tier_add + detail_index = params[:detail_index] + ii = detail_index.to_i + + @edit = session[:edit] + detail = @edit[:new][:details][ii] + + @edit[:new][:num_tiers][ii] = detail[:chargeback_tiers].to_a.length if detail[:chargeback_tiers] + @edit[:new][:num_tiers][ii] = 1 unless @edit[:new][:num_tiers][ii] || @edit[:new][:num_tiers][ii].zero? + @edit[:new][:num_tiers][ii] += 1 + + tier_index = @edit[:new][:num_tiers][ii] - 1 + tier_list = @edit[:new][:tiers][ii] + tier_list[tier_index] = {} + + tier = tier_list[tier_index] + tier[:start] = tier_list[tier_index - 1][:finish] + tier[:finish] = Float::INFINITY + tier[:fixed_rate] = 0.0 + tier[:variable_rate] = 0.0 + + code_currency = Currency.find_by(:id => detail[:currency]).code + add_row(detail_index, tier_index - 1, code_currency) + end + + # Remove the selected tier + def cb_tier_remove + @edit = session[:edit] + index = params[:index] + detail_index, tier_to_remove_index = index.split("-") + detail_index = detail_index.to_i + @edit[:new][:num_tiers][detail_index] = @edit[:new][:num_tiers][detail_index] - 1 + + # Delete tier record + @edit[:new][:tiers][detail_index].delete_at(tier_to_remove_index.to_i) + + @changed = session[:changed] = true + + render :update do |page| + page << javascript_prologue + page.replace_html("chargeback_rate_edit_form", :partial => "cb_rate_edit_table") + page << javascript_for_miq_button_visibility(@changed) + end + end + + def title + @title = _("Chargeback Rates") + end + + private ############################ + + def features + [ + { + :role => "chargeback_rates", + :name => :cb_rates, + :title => _("Rates") + } + ].map { |hsh| ApplicationController::Feature.new_with_hash(hsh) } + end + + def get_node_info(node, show_list = true) + @show_list = show_list + node = valid_active_node(node) + if node == "root" + @record = nil + @right_cell_text = _("All Chargeback Rates") + elsif ["xx-Compute", "xx-Storage"].include?(node) + @record = nil + @right_cell_text = case node + when "xx-Compute" then _("Compute Chargeback Rates") + when "xx-Storage" then _("Storage Chargeback Rates") + end + cb_rates_list + else + @record = ChargebackRate.find(parse_nodetype_and_id(node).last) + @sb[:action] = nil + @right_cell_text = case @record.rate_type + when "Compute" then _("Compute Chargeback Rate \"%{name}\"") % {:name => @record.description} + when "Storage" then _("Storage Chargeback Rate \"%{name}\"") % {:name => @record.description} + end + cb_rate_show + end + {:view => @view, :pages => @pages} + end + + def cb_rates_build_tree + TreeBuilderChargebackRates.new("cb_rates_tree", @sb) + end + + # Common Schedule button handler routines + def process_cb_rates(rates, task) + process_elements(rates, ChargebackRate, task) + end + + # Set form variables for edit + def cb_rate_set_form_vars + @edit = {} + @edit[:new] = HashWithIndifferentAccess.new + @edit[:current] = HashWithIndifferentAccess.new + @edit[:new][:tiers] = [] + @edit[:new][:num_tiers] = [] + @edit[:new][:description] = @rate.description + @edit[:new][:rate_type] = @rate.rate_type || x_node.split('-').last + @edit[:new][:details] = [] + + tiers = [] + rate_details = @rate.chargeback_rate_details + rate_details = ChargebackRateDetail.default_rate_details_for(@edit[:new][:rate_type]) if new_rate_edit? + + # Select the currency of the first chargeback_rate_detail. All the chargeback_rate_details have the same currency + @edit[:new][:currency] = rate_details[0].detail_currency.id + @edit[:new][:code_currency] = "#{rate_details[0].detail_currency.symbol} [#{rate_details[0].detail_currency.full_name}]" + + rate_details.each_with_index do |detail, detail_index| + temp = detail.slice(*ChargebackRateDetail::FORM_ATTRIBUTES) + temp[:report_column_name] = Dictionary.gettext(detail.chargeable_field.metric_key, :type => :column, :notfound => :titleize) + temp[:group] = detail.chargeable_field.group + temp[:per_time] ||= "hourly" + + temp[:currency] = detail.detail_currency.id + + if detail.chargeable_field.detail_measure.present? + temp[:detail_measure] = {} + temp[:detail_measure][:measures] = detail.chargeable_field.detail_measure.measures + temp[:chargeback_rate_detail_measure_id] = detail.chargeable_field.detail_measure.id + end + + temp[:id] = params[:pressed] == 'chargeback_rates_copy' ? nil : detail.id + temp[:sub_metrics] = detail.sub_metrics + temp[:sub_metric_human] = detail.sub_metric_human + + tiers[detail_index] ||= [] + + detail.chargeback_tiers.each do |tier| + new_tier = tier.slice(*ChargebackTier::FORM_ATTRIBUTES) + new_tier[:id] = params[:pressed] == 'chargeback_rates_copy' ? nil : tier.id + new_tier[:chargeback_rate_detail_id] = params[:pressed] == 'chargeback_rates_copy' ? nil : detail.id + new_tier[:start] = new_tier[:start].to_f + new_tier[:finish] = ChargebackTier.to_float(new_tier[:finish]) + tiers[detail_index].push(new_tier) + end + + @edit[:new][:tiers][detail_index] = tiers[detail_index] + @edit[:new][:num_tiers][detail_index] = tiers[detail_index].size + @edit[:new][:details].push(temp) + end + + @edit[:new][:per_time_types] = ChargebackRateDetail::PER_TIME_TYPES.map { |x, y| [x, _(y)] }.to_h + + if params[:pressed] == 'chargeback_rates_copy' + @rate.id = nil + @edit[:new][:description] = "copy of #{@rate.description}" + end + + @edit[:rec_id] = @rate.id || nil + @edit[:key] = "cbrate_edit__#{@rate.id || "new"}" + @edit[:current] = copy_hash(@edit[:new]) + session[:edit] = @edit + end + + # Get variables from edit form + def cb_rate_get_form_vars + @edit[:new][:description] = params[:description] if params[:description] + if params[:currency] + @edit[:new][:currency] = params[:currency].to_i + rate_detail_currency = Currency.find(params[:currency]) + @edit[:new][:code_currency] = "#{rate_detail_currency.symbol} [#{rate_detail_currency.full_name}]" + end + @edit[:new][:details].each_with_index do |detail, detail_index| + %i[per_time per_unit sub_metric].each do |measure| + key = "#{measure}_#{detail_index}".to_sym + detail[measure] = params[key] if params[key] + end + # Add currencies to chargeback_controller.rb + detail[:currency] = params[:currency].to_i if params[:currency] + + # Save tiers into @edit + (0..@edit[:new][:num_tiers][detail_index].to_i - 1).each do |tier_index| + tier = @edit[:new][:tiers][detail_index][tier_index] || {} + %i[fixed_rate variable_rate start finish].each do |field| + key = "#{field}_#{detail_index}_#{tier_index}".to_sym + tier[field] = params[key] if params[key] + end + end + end + end + + def cb_rate_set_record_vars + @rate_details = [] + @rate_tiers = [] + @edit[:new][:details].each_with_index do |detail, detail_index| + rate_detail = detail[:id] ? ChargebackRateDetail.find(detail[:id]) : ChargebackRateDetail.new + rate_detail.attributes = detail.slice(*ChargebackRateDetail::FORM_ATTRIBUTES) + rate_detail.sub_metric = detail[:sub_metric] if rate_detail.sub_metric + rate_detail_edit = @edit[:new][:details][detail_index] + # C: Record the currency selected in the edit view, in my chargeback_rate_details table + rate_detail.chargeback_rate_detail_currency_id = rate_detail_edit[:currency] + rate_detail.chargeback_rate_detail_measure_id = rate_detail_edit[:chargeback_rate_detail_measure_id] + rate_detail.chargeback_rate_id = @rate.id + # Save tiers into @sb + rate_tiers = [] + @edit[:new][:tiers][detail_index].each do |tier| + rate_tier = tier[:id] ? ChargebackTier.find(tier[:id]) : ChargebackTier.new + tier[:start] = Float::INFINITY if tier[:start].blank? + tier[:finish] = Float::INFINITY if tier[:finish].blank? + rate_tier.attributes = tier.slice(*ChargebackTier::FORM_ATTRIBUTES) + rate_tier.chargeback_rate_detail_id = rate_detail.id + rate_tiers.push(rate_tier) + end + @rate_tiers[detail_index] = rate_tiers + @rate_details.push(rate_detail) + end + end + + def new_rate_edit? + params[:id] == 'new' || params[:pressed] == 'chargeback_rates_new' + end + + def replace_right_cell(options = {}) + replace_trees = Array(options[:replace_trees]) + replace_trees = @replace_trees if @replace_trees # get_node_info might set this + @explorer = true + c_tb = build_toolbar(center_toolbar_filename) + + # Build a presenter to render the JS + presenter = ExplorerPresenter.new(:active_tree => x_active_tree) + reload_trees_by_presenter(presenter, [cb_rates_build_tree]) if replace_trees.include?(:cb_rates) + + # FIXME + # if params[:action].ends_with?("_delete") + # page << "miqTreeActivateNodeSilently('#{x_active_tree.to_s}', '<%= x_node %>');" + # end + # presenter[:select_node] = x_node if params[:action].ends_with?("_delete") + presenter[:osf_node] = x_node + + if c_tb.present? + presenter.reload_toolbars(:center => c_tb) + end + presenter.set_visibility(c_tb.present?, :toolbar) + presenter.update(:main_div, r[:partial => 'rates_tabs']) + + if @record || @in_a_form || + (@pages && (@items_per_page == ONE_MILLION || @pages[:items] == 0)) + if %w[chargeback_rates_copy chargeback_rates_edit chargeback_rates_new].include?(@sb[:action]) + presenter.hide(:toolbar) + # incase it was hidden for summary screen, and incase there were no records on show_list + presenter.show(:paging_div, :form_buttons_div).remove_paging + locals = {:record_id => @edit[:rec_id]} + locals[:action_url] = 'cb_rate_edit' + presenter.update(:form_buttons_div, r[:partial => 'layouts/x_edit_buttons', :locals => locals]) + else + # Added so buttons can be turned off even tho div is not being displayed it still pops up Abandon changes box when trying to change a node on tree after saving a record + presenter.hide(:buttons_on).show(:toolbar).hide(:paging_div) + presenter.hide(:form_buttons_div) if params[:button] + end + else + presenter.hide(:form_buttons_div) + if x_node == "root" + presenter.hide(:toolbar).remove_paging + end + presenter.show(:paging_div) + end + + presenter[:record_id] = determine_record_id_for_presenter + + presenter[:clear_gtl_list_grid] = @gtl_type && @gtl_type != 'list' + + presenter[:right_cell_text] = @right_cell_text + presenter[:lock_sidebar] = @in_a_form && @edit + + presenter.update(:breadcrumbs, r[:partial => 'layouts/breadcrumbs']) + + render :json => presenter.for_render + end + + def display_detail_errors(detail, errors) + errors.each { |field, msg| add_flash("'#{detail.chargeable_field.description}' #{field.to_s.humanize.downcase} #{msg}", :error) } + end + + def add_row(i, pos, code_currency) + locals = {:code_currency => code_currency} + render :update do |page| + page << javascript_prologue + # Update the first row to change the colspan + page.replace("rate_detail_row_#{i}_0", + :partial => "tier_first_row", + :locals => locals) + # Insert the new tier after the last one + page.insert_html(:after, + "rate_detail_row_#{i}_#{pos}", + :partial => "tier_row", + :locals => locals) + page << javascript_for_miq_button_visibility(true) + end + end + + def breadcrumbs_options + { + :breadcrumbs => [ + {:title => _("Overview")}, + {:title => _("Chargeback")}, + ], + } + end + + menu_section :chargeback +end diff --git a/app/controllers/chargeback_report_controller.rb b/app/controllers/chargeback_report_controller.rb new file mode 100644 index 00000000000..e9917447163 --- /dev/null +++ b/app/controllers/chargeback_report_controller.rb @@ -0,0 +1,239 @@ +class ChargebackReportController < ApplicationController + before_action :check_privileges + before_action :get_session_data + after_action :cleanup_action + after_action :set_session_data + + include Mixins::SavedReportPaging + include Mixins::GenericSessionMixin + include Mixins::BreadcrumbsMixin + + def self.table_name + @table_name ||= "chargeback_report" + end + + def x_show + @explorer = true + end + + def tree_select + self.x_active_tree = params[:tree] if params[:tree] + self.x_node = params[:id] + get_node_info(x_node) + replace_right_cell + end + + def explorer + @breadcrumbs = [] + @explorer = true + build_accordions_and_trees + + @right_cell_text = _("All Saved Chargeback Reports") + render :layout => "application" unless request.xml_http_request? + end + + def title + @title = _("Chargeback Reports") + end + + private ############################ + + def features + [ + { + :role => "chargeback_reports", + :name => :cb_reports, + :title => _("Reports") + } + ].map { |hsh| ApplicationController::Feature.new_with_hash(hsh) } + end + + # Build a Chargeback Reports explorer tree + def cb_rpts_build_tree + TreeBuilderChargebackReports.new("cb_reports_tree", @sb) + end + + def cb_rpts_show_saved_report + @sb[:last_savedreports_id] = parse_nodetype_and_id(params[:id]).last if params[:id] && params[:id] != "cb_reports_accord" + cb_rpts_fetch_saved_report(@sb[:last_savedreports_id]) + @sb[:parent_reports] = nil if @report.blank? + end + + def cb_rpts_fetch_saved_report(id) + rr = MiqReportResult.for_user(current_user).find(id.to_s.split('-').last) + if rr.nil? # Saved report no longer exists + @report = nil + return + end + @right_cell_text ||= _("Saved Chargeback Report [%{name}]") % {:name => rr.name} + if !current_user.miq_group_ids.include?(rr.miq_group_id) && !report_admin_user? + add_flash(_("Report is not authorized for the logged in user"), :error) + @saved_reports = cb_rpts_get_all_reps(id.split('-')[1]) + return + else + @report_result_id = session[:report_result_id] = rr.id + session[:report_result_runtime] = rr.last_run_on + if rr.status.downcase == "complete" + session[:rpt_task_id] = nil + if rr.valid_report_column? + if rr.contains_records? + @html = report_first_page(rr) # Get the first page of the results + if @report.graph.present? + @render_chart = true + @ght_type = "hybrid" + else + @ght_type = "tabular" + end + @report.extras ||= {} # Create extras hash + @report.extras[:to_html] ||= @html # Save the html report + else + add_flash(_("No records found for this report"), :warning) + end + else + @saved_reports = cb_rpts_get_all_reps(rr.miq_report_id.to_s) + rep = MiqReport.find(rr.miq_report_id) + if x_active_tree == :cb_reports_tree + self.x_node = "reports-#{rep.id}" + end + return + end + end + end + end + + def get_node_info(node, show_list = true) + @show_list = show_list + node = valid_active_node(node) + @nodetype = node.split("-")[0] + nodes = x_node.split('_') + nodes_len = nodes.length + + # On the root node + if x_node == "root" + cb_rpt_build_folder_nodes + @right_cell_div = "reports_list_div" + @right_cell_text = _("All Saved Chargeback Reports") + elsif nodes_len == 2 + # On a saved report node + cb_rpts_show_saved_report + if @report + s = MiqReportResult.for_user(current_user).find(nodes.last.split('-').last) + + @right_cell_div = "reports_list_div" + @right_cell_text = _("Saved Chargeback Report \"%{last_run_on}\"") % {:last_run_on => format_timezone(s.last_run_on, Time.zone, "gtl")} + else + add_flash(_("Selected Saved Chargeback Report no longer exists"), :warning) + self.x_node = nodes[0..1].join("_") + cb_rpts_build_tree # Rebuild tree + end + # On a saved reports parent node + else + # saved reports under report node on saved report accordion + @saved_reports = cb_rpts_get_all_reps(nodes[0].split('-')[1]) + if @saved_reports.present? + @sb[:sel_saved_rep_id] = nodes[1] + @right_cell_div = "reports_list_div" + miq_report = MiqReport.for_user(current_user).find(@sb[:miq_report_id]) + @right_cell_text = _("Saved Chargeback Reports \"%{report_name}\"") % {:report_name => miq_report.name} + @sb[:parent_reports] = nil if @sb[:saved_reports].present? # setting it to nil so saved reports can be displayed, unless all saved reports were deleted + else + add_flash(_("Selected Chargeback Report no longer exists"), :warning) + self.x_node = nodes[0] + @saved_reports = nil + cb_rpts_build_tree # Rebuild tree + end + end + {:view => @view, :pages => @pages} + end + + def cb_rpt_build_folder_nodes + @parent_reports = {} + + MiqReportResult.with_saved_chargeback_reports.select_distinct_results.each_with_index do |sr, sr_idx| + @parent_reports[sr.miq_report.name] = "#{sr.miq_report_id}-#{sr_idx}" + end + end + + def cb_rpts_get_all_reps(nodeid) + return [] if nodeid.blank? + + @sb[:miq_report_id] = nodeid + miq_report = MiqReport.for_user(current_user).find(@sb[:miq_report_id]) + saved_reports = miq_report.miq_report_results.with_current_user_groups + .select("id, miq_report_id, name, last_run_on, report_source") + .order(:last_run_on => :desc) + + @sb[:tree_typ] = "reports" + @right_cell_text = _("Report \"%{report_name}\"") % {:report_name => miq_report.name} + saved_reports + end + + + def replace_right_cell(options = {}) + @explorer = true + c_tb = build_toolbar(center_toolbar_filename) + + # Build a presenter to render the JS + presenter = ExplorerPresenter.new(:active_tree => x_active_tree) + + # FIXME + # if params[:action].ends_with?("_delete") + # page << "miqTreeActivateNodeSilently('#{x_active_tree.to_s}', '<%= x_node %>');" + # end + # presenter[:select_node] = x_node if params[:action].ends_with?("_delete") + presenter[:osf_node] = x_node + + + if c_tb.present? + presenter.reload_toolbars(:center => c_tb) + presenter.show(:toolbar) + else + presenter.hide(:toolbar) + end + presenter.update(:main_div, r[:partial => 'reports_list']) + if @html + presenter.update(:paging_div, r[:partial => 'layouts/saved_report_paging_bar', + :locals => @sb[:pages]]) + presenter.show(:paging_div) + else + presenter.hide(:paging_div) + end + + presenter.hide(:form_buttons_div) + if x_active_tree == :cb_reports_tree && !@report + presenter.hide(:toolbar).remove_paging + else + presenter.show(:paging_div) + end + + presenter[:record_id] = determine_record_id_for_presenter + + presenter[:clear_gtl_list_grid] = @gtl_type && @gtl_type != 'list' + + presenter[:right_cell_text] = @right_cell_text + presenter.update(:breadcrumbs, r[:partial => 'layouts/breadcrumbs']) + + render :json => presenter.for_render + end + + def get_session_data + super + @current_page = session[:chargeback_report_current_page] + end + + def set_session_data + super + session[:chargeback_report_current_page] = @current_page + end + + def breadcrumbs_options + { + :breadcrumbs => [ + {:title => _("Overview")}, + {:title => _("Chargeback")}, + ], + } + end + + menu_section :chargeback +end diff --git a/app/helpers/application_helper/toolbar_chooser.rb b/app/helpers/application_helper/toolbar_chooser.rb index 28da4b56b5b..2c35b535f64 100644 --- a/app/helpers/application_helper/toolbar_chooser.rb +++ b/app/helpers/application_helper/toolbar_chooser.rb @@ -98,8 +98,10 @@ def center_toolbar_filename_explorer center_toolbar_filename_containers elsif %i[sandt_tree svccat_tree stcat_tree svcs_tree ot_tree].include?(x_active_tree) center_toolbar_filename_services - elsif @layout == "chargeback" - center_toolbar_filename_chargeback + elsif @layout == "chargeback_rate" + center_toolbar_filename_chargeback_rate + elsif @layout == "chargeback_report" + center_toolbar_filename_chargeback_report elsif @layout == "miq_ae_tools" super_admin_user? ? "miq_ae_tools_simulate_center_tb" : nil elsif @layout == "miq_policy" @@ -210,15 +212,14 @@ def center_toolbar_filename_containers x_node == 'root' ? 'containers_center_tb' : 'container_center_tb' end - def center_toolbar_filename_chargeback - if @report && x_active_tree == :cb_reports_tree - return "chargeback_center_tb" - elsif x_active_tree == :cb_rates_tree && x_node != "root" - if %w[Compute Storage].include?(x_node.split('-').last) - return "chargebacks_center_tb" - else - return "chargeback_center_tb" - end + def center_toolbar_filename_chargeback_report + return "chargeback_center_tb" if @report + nil + end + + def center_toolbar_filename_chargeback_rate + if x_node != "root" + return %w[Compute Storage].include?(x_node.split('-').last) ? "chargebacks_center_tb" : "chargeback_center_tb" end nil end @@ -592,7 +593,9 @@ def inventory_group_center_tb end end - NO_GTL_VIEW_BUTTONS = %w[chargeback + NO_GTL_VIEW_BUTTONS = %w[chargeback_assignment + chargeback_rate + chargeback_report generic_object generic_object_definition miq_ae_class diff --git a/app/helpers/chargeback_helper.rb b/app/helpers/chargeback_rate_helper.rb similarity index 92% rename from app/helpers/chargeback_helper.rb rename to app/helpers/chargeback_rate_helper.rb index 4265d283bd3..11127e4cf44 100644 --- a/app/helpers/chargeback_helper.rb +++ b/app/helpers/chargeback_rate_helper.rb @@ -1,4 +1,4 @@ -module ChargebackHelper +module ChargebackRateHelper def rate_detail_group(rd_group) rd_groups = { 'cpu' => _('CPU'), diff --git a/app/presenters/menu/default_menu.rb b/app/presenters/menu/default_menu.rb index bb8d0a801d6..e2da087dd96 100644 --- a/app/presenters/menu/default_menu.rb +++ b/app/presenters/menu/default_menu.rb @@ -26,7 +26,7 @@ def overview_menu_section # Menu::Item.new('usage', N_('Usage'), 'usage', {:feature => 'usage'}, '/report/usage/'), # / Hiding usage for now - release 5.2 Settings.product.consumption ? consumption_menu_section : nil, Menu::Item.new('miq_capacity_utilization', N_('Utilization'), 'utilization', {:feature => 'utilization'}, '/utilization'), - Menu::Item.new('chargeback', N_('Chargeback'), 'chargeback', {:feature => 'chargeback', :any => true}, '/chargeback/explorer'), + chargeback_menu_section, Menu::Item.new('optimization', N_('Optimization'), 'optimization', {:feature => 'optimization'}, '/optimization'), ].compact) end @@ -46,6 +46,14 @@ def services_menu_section ]) end + def chargeback_menu_section + Menu::Section.new(:chargeback, N_("Chargeback"), 'fa fa-plus', [ + Menu::Item.new('chargeback_report', N_('Reports'), 'chargeback_reports', {:feature => 'chargeback_reports', :any => true}, '/chargeback_report/explorer'), + Menu::Item.new('chargeback_rate', N_('Rates'), 'chargeback_rates', {:feature => 'chargeback_rates', :any => true}, '/chargeback_rate/explorer'), + Menu::Item.new('chargeback_assignment', N_('Assignments'), 'chargeback_assignments', {:feature => 'chargeback_assignments'}, '/chargeback_assignment/explorer'), + ]) + end + def clouds_menu_section Menu::Section.new(:clo, N_("Clouds"), 'fa fa-plus', [ Menu::Item.new('ems_cloud', N_('Providers'), 'ems_cloud', {:feature => 'ems_cloud_show_list'}, '/ems_cloud/show_list'), diff --git a/app/views/chargeback/explorer.html.haml b/app/views/chargeback/explorer.html.haml deleted file mode 100644 index 918d6df98a3..00000000000 --- a/app/views/chargeback/explorer.html.haml +++ /dev/null @@ -1,11 +0,0 @@ --# These are the initial divs that will go inside center_cell_div -#main_div - - case x_active_tree - - when :cb_reports_tree - = render :partial => "reports_list" - - - when :cb_rates_tree - = render :partial => "rates_tabs" - - - when :cb_assignments_tree - = render :partial => "assignments_tabs" diff --git a/app/views/chargeback/_assignments_list.html.haml b/app/views/chargeback_assignment/_assignments_list.html.haml similarity index 100% rename from app/views/chargeback/_assignments_list.html.haml rename to app/views/chargeback_assignment/_assignments_list.html.haml diff --git a/app/views/chargeback/_assignments_tabs.html.haml b/app/views/chargeback_assignment/_assignments_tabs.html.haml similarity index 100% rename from app/views/chargeback/_assignments_tabs.html.haml rename to app/views/chargeback_assignment/_assignments_tabs.html.haml diff --git a/app/views/chargeback/_cb_assignments.html.haml b/app/views/chargeback_assignment/_cb_assignments.html.haml similarity index 100% rename from app/views/chargeback/_cb_assignments.html.haml rename to app/views/chargeback_assignment/_cb_assignments.html.haml diff --git a/app/views/chargeback_assignment/explorer.html.haml b/app/views/chargeback_assignment/explorer.html.haml new file mode 100644 index 00000000000..47a267e551d --- /dev/null +++ b/app/views/chargeback_assignment/explorer.html.haml @@ -0,0 +1,3 @@ +-# These are the initial divs that will go inside center_cell_div +#main_div + = render :partial => "assignments_tabs" diff --git a/app/views/chargeback/_cb_rate_currency.html.haml b/app/views/chargeback_rate/_cb_rate_currency.html.haml similarity index 100% rename from app/views/chargeback/_cb_rate_currency.html.haml rename to app/views/chargeback_rate/_cb_rate_currency.html.haml diff --git a/app/views/chargeback/_cb_rate_edit.html.haml b/app/views/chargeback_rate/_cb_rate_edit.html.haml similarity index 100% rename from app/views/chargeback/_cb_rate_edit.html.haml rename to app/views/chargeback_rate/_cb_rate_edit.html.haml diff --git a/app/views/chargeback/_cb_rate_edit_table.html.haml b/app/views/chargeback_rate/_cb_rate_edit_table.html.haml similarity index 100% rename from app/views/chargeback/_cb_rate_edit_table.html.haml rename to app/views/chargeback_rate/_cb_rate_edit_table.html.haml diff --git a/app/views/chargeback/_cb_rate_show.html.haml b/app/views/chargeback_rate/_cb_rate_show.html.haml similarity index 100% rename from app/views/chargeback/_cb_rate_show.html.haml rename to app/views/chargeback_rate/_cb_rate_show.html.haml diff --git a/app/views/chargeback/_cb_tier_edit_values.html.haml b/app/views/chargeback_rate/_cb_tier_edit_values.html.haml similarity index 100% rename from app/views/chargeback/_cb_tier_edit_values.html.haml rename to app/views/chargeback_rate/_cb_tier_edit_values.html.haml diff --git a/app/views/chargeback/_rates_list.html.haml b/app/views/chargeback_rate/_rates_list.html.haml similarity index 100% rename from app/views/chargeback/_rates_list.html.haml rename to app/views/chargeback_rate/_rates_list.html.haml diff --git a/app/views/chargeback/_rates_tabs.html.haml b/app/views/chargeback_rate/_rates_tabs.html.haml similarity index 100% rename from app/views/chargeback/_rates_tabs.html.haml rename to app/views/chargeback_rate/_rates_tabs.html.haml diff --git a/app/views/chargeback/_tier_first_row.haml b/app/views/chargeback_rate/_tier_first_row.haml similarity index 100% rename from app/views/chargeback/_tier_first_row.haml rename to app/views/chargeback_rate/_tier_first_row.haml diff --git a/app/views/chargeback/_tier_row.haml b/app/views/chargeback_rate/_tier_row.haml similarity index 100% rename from app/views/chargeback/_tier_row.haml rename to app/views/chargeback_rate/_tier_row.haml diff --git a/app/views/chargeback/cb_rates_list.html.haml b/app/views/chargeback_rate/cb_rates_list.html.haml similarity index 100% rename from app/views/chargeback/cb_rates_list.html.haml rename to app/views/chargeback_rate/cb_rates_list.html.haml diff --git a/app/views/chargeback_rate/explorer.html.haml b/app/views/chargeback_rate/explorer.html.haml new file mode 100644 index 00000000000..33632ec992a --- /dev/null +++ b/app/views/chargeback_rate/explorer.html.haml @@ -0,0 +1,3 @@ +-# These are the initial divs that will go inside center_cell_div +#main_div + = render :partial => "rates_tabs" diff --git a/app/views/chargeback_rate/x_show.html.haml b/app/views/chargeback_rate/x_show.html.haml new file mode 100644 index 00000000000..a14c20f0fab --- /dev/null +++ b/app/views/chargeback_rate/x_show.html.haml @@ -0,0 +1 @@ += render :partial => 'cb_rate_show' diff --git a/app/views/chargeback/_reports_list.html.haml b/app/views/chargeback_report/_reports_list.html.haml similarity index 100% rename from app/views/chargeback/_reports_list.html.haml rename to app/views/chargeback_report/_reports_list.html.haml diff --git a/app/views/chargeback_report/explorer.html.haml b/app/views/chargeback_report/explorer.html.haml new file mode 100644 index 00000000000..54da33dbeeb --- /dev/null +++ b/app/views/chargeback_report/explorer.html.haml @@ -0,0 +1,3 @@ +-# These are the initial divs that will go inside center_cell_div +#main_div + = render :partial => "reports_list" diff --git a/config/routes.rb b/config/routes.rb index bb266341733..89648d3a49e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -355,19 +355,24 @@ dialog_runner_post }, - :chargeback => { + :chargeback_assignment => { :get => %w( explorer - render_csv - render_pdf - render_txt - report_only ), :post => %w( - accordion_select explorer cb_assign_field_changed cb_assign_update + tree_select + ) + }, + :chargeback_rate => { + :get => %w( + explorer + x_show + ), + :post => %w( + explorer cb_rate_edit cb_rate_form_field_changed cb_rate_show @@ -375,10 +380,25 @@ cb_rates_list cb_tier_add cb_tier_remove + tree_select + x_button + x_show + ) + }, + + :chargeback_report => { + :get => %w( + explorer + render_csv + render_pdf + render_txt + report_only + ), + :post => %w( + explorer saved_report_paging tree_autoload tree_select - x_button x_show ) }, diff --git a/spec/controllers/chargeback_assignment_controller_spec.rb b/spec/controllers/chargeback_assignment_controller_spec.rb new file mode 100644 index 00000000000..8071fb4d678 --- /dev/null +++ b/spec/controllers/chargeback_assignment_controller_spec.rb @@ -0,0 +1,172 @@ +describe ChargebackAssignmentController do + before { stub_user(:features => :all) } + + context "returns current rate assignments or set them to blank if category/tag is deleted" do + let(:category) { FactoryBot.create(:classification) } + let(:tag) { FactoryBot.create(:classification, :parent_id => category.id) } + let(:entry) { FactoryBot.create(:classification, :parent_id => tag.id) } + + describe "#get_tags_all" do + before { entry } + + it "returns the classification entry record" do + controller.instance_variable_set(:@edit, :cb_assign => {:tags => {}}) + controller.send(:get_tags_all) + + result = {category.id => {tag.id.to_s => tag.description}, tag.id => { entry.id.to_s => entry.description}, entry.id => {}} + + expect(assigns(:edit)[:cb_assign][:tags]).to eq(result) + end + end + + describe "#cb_assign_set_form_vars" do + before do + cbr = FactoryBot.create(:chargeback_rate, :rate_type => "Storage") + ChargebackRate.set_assignments(:Storage, [{:cb_rate => cbr, :tag => [tag, "vm"]}]) + sandbox = {:active_tree => :cb_assignments_tree, :trees => {:cb_assignments_tree => {:active_node => 'xx-Storage'}}} + controller.instance_variable_set(:@sb, sandbox) + end + + it "returns tag for current assignments" do + controller.send(:cb_assign_set_form_vars) + expect(assigns(:edit)[:current_assignment][0][:tag][0]['parent_id']).to eq(category.id) + end + + it "returns empty array for current_assignment when tag/category is not found" do + tag.destroy + controller.send(:cb_assign_set_form_vars) + expect(assigns(:edit)[:current_assignment]).to eq([]) + end + end + + describe '#cb_assign_get_form_vars' do + before do + controller.params = {:cblabel_key => 'null'} + controller.instance_variable_set(:@edit, :new => {:cbshow_typ => '-labels'}, :cb_assign => {}) + end + + it "returns tag for current assignments" do + expect { controller.send(:cb_assign_get_form_vars) }.not_to raise_error + end + + it "initializes hash when data are no available(params[:cblabel_key] == null)" do + controller.send(:cb_assign_get_form_vars) + docker_label_values = controller.instance_variable_get(:@edit)[:cb_assign][:docker_label_values] + expect(docker_label_values).to eq({}) + end + + it "initializes hash when data are no available (params[:cblabel_key] == nil)" do + controller.params = {:cblabel_key => nil} + controller.send(:cb_assign_get_form_vars) + docker_label_values = controller.instance_variable_get(:@edit)[:cb_assign][:docker_label_values] + expect(docker_label_values).to eq({}) + end + end + end + + describe "#explorer" do + render_views + + it "can be rendered" do + EvmSpecHelper.create_guid_miq_server_zone + get :explorer + expect(response.status).to eq(200) + expect(response.body).to_not be_empty + end + end + + describe "#get_cis_all" do + let!(:storage) { FactoryBot.create(:storage) } + let!(:miq_enterprise) { FactoryBot.create(:miq_enterprise) } + + it "returns names of instances of enterprise" do + names_miqent = {} + MiqEnterprise.all.each do |instance| + names_miqent[instance.id] = instance.name + end + controller.instance_variable_set(:@edit, :new => {:cbshow_typ => "enterprise"}, :cb_assign => {}) + controller.send(:get_cis_all) + expect(assigns(:edit)[:cb_assign][:cis]).to eq(names_miqent) + end + + it "returns names of instances of storage" do + names_storage = {} + element = "storage" + element.classify.constantize.all.each do |instance| + names_storage[instance.id] = instance.name + end + controller.instance_variable_set(:@edit, :new => {:cbshow_typ => element}, :cb_assign => {}) + controller.send(:get_cis_all) + expect(assigns(:edit)[:cb_assign][:cis]).to eq(names_storage) + end + + it "returns a ArgumentError when element not in whitelist" do + controller.instance_variable_set(:@edit, :new => {:cbshow_typ => "None"}, :cb_assign => {}) + expect { controller.send(:get_cis_all) }.to raise_error(ArgumentError) + end + + it "doesn't names of instances when nothing is selected" do + controller.instance_variable_set(:@edit, + :new => {:cbshow_typ => described_class::NOTHING_FORM_VALUE}, :cb_assign => {}) + controller.send(:get_cis_all) + expect(assigns(:edit)[:cb_assign][:cis]).to eq({}) + end + end + + describe '#cb_assign_field_changed' do + let(:edit) do + { + :cb_assign => { + :cats => { + '1' => 'Category1', + '2' => 'Category2' + }, + :tags => { + 1 => { + '2' => 'Tag1', + '3' => 'Tag2', + '4' => 'Tag3' + }, + 2 => {} + } + }, + :current => { + :cbshow_typ => 'storage-tags', + :cbtag_cat => '1', + 'storage-tags__2' => '3' + }, + :new => { + :cbshow_typ => 'storage-tags', + :cbtag_cat => '1', + 'storage-tags__2' => '3' + } + } + end + + before do + allow(controller).to receive(:load_edit).and_return(true) + controller.instance_variable_set(:@edit, edit) + end + + context 'changing Tag Category for assignments' do + it 'hides buttons as no change has been made' do + post :cb_assign_field_changed, :params => {:cbtag_cat => '2'} + expect(response.body).to include("miqButtons('hide');") + end + end + + context 'changing Assigned To, for assignments' do + it 'hides buttons as no change has been made' do + post :cb_assign_field_changed, :params => {:cbshow_typ => 'storage'} + expect(response.body).to include("miqButtons('hide');") + end + end + + context 'changing item under Selections, for assignments' do + it 'shows buttons as a change in Selections has been made' do + post :cb_assign_field_changed, :params => {'storage-tags__3' => '4'} + expect(response.body).to include("miqButtons('show');") + end + end + end +end diff --git a/spec/controllers/chargeback_controller_spec.rb b/spec/controllers/chargeback_rate_controller_spec.rb similarity index 67% rename from spec/controllers/chargeback_controller_spec.rb rename to spec/controllers/chargeback_rate_controller_spec.rb index 4ffaa46017b..fc13aaf1a7e 100644 --- a/spec/controllers/chargeback_controller_spec.rb +++ b/spec/controllers/chargeback_rate_controller_spec.rb @@ -1,115 +1,6 @@ -describe ChargebackController do +describe ChargebackRateController do before { stub_user(:features => :all) } - context "returns current rate assignments or set them to blank if category/tag is deleted" do - let(:category) { FactoryBot.create(:classification) } - let(:tag) { FactoryBot.create(:classification, :parent_id => category.id) } - let(:entry) { FactoryBot.create(:classification, :parent_id => tag.id) } - - describe "#get_tags_all" do - before { entry } - - it "returns the classification entry record" do - controller.instance_variable_set(:@edit, :cb_assign => {:tags => {}}) - controller.send(:get_tags_all) - - result = {category.id => {tag.id.to_s => tag.description}, tag.id => { entry.id.to_s => entry.description}, entry.id => {}} - - expect(assigns(:edit)[:cb_assign][:tags]).to eq(result) - end - end - - describe "#cb_assign_set_form_vars" do - before do - cbr = FactoryBot.create(:chargeback_rate, :rate_type => "Storage") - ChargebackRate.set_assignments(:Storage, [{:cb_rate => cbr, :tag => [tag, "vm"]}]) - sandbox = {:active_tree => :cb_assignments_tree, :trees => {:cb_assignments_tree => {:active_node => 'xx-Storage'}}} - controller.instance_variable_set(:@sb, sandbox) - end - - it "returns tag for current assignments" do - controller.send(:cb_assign_set_form_vars) - expect(assigns(:edit)[:current_assignment][0][:tag][0]['parent_id']).to eq(category.id) - end - - it "returns empty array for current_assignment when tag/category is not found" do - tag.destroy - controller.send(:cb_assign_set_form_vars) - expect(assigns(:edit)[:current_assignment]).to eq([]) - end - end - - describe '#cb_assign_get_form_vars' do - before do - controller.params = {:cblabel_key => 'null'} - controller.instance_variable_set(:@edit, :new => {:cbshow_typ => '-labels'}, :cb_assign => {}) - end - - it "returns tag for current assignments" do - expect { controller.send(:cb_assign_get_form_vars) }.not_to raise_error - end - - it "initializes hash when data are no available(params[:cblabel_key] == null)" do - controller.send(:cb_assign_get_form_vars) - docker_label_values = controller.instance_variable_get(:@edit)[:cb_assign][:docker_label_values] - expect(docker_label_values).to eq({}) - end - - it "initializes hash when data are no available (params[:cblabel_key] == nil)" do - controller.params = {:cblabel_key => nil} - controller.send(:cb_assign_get_form_vars) - docker_label_values = controller.instance_variable_get(:@edit)[:cb_assign][:docker_label_values] - expect(docker_label_values).to eq({}) - end - end - end - - context "Saved chargeback rendering" do - it "Saved chargeback reports renders paginagion buttons correctly" do - report = FactoryBot.create(:miq_report_with_results, :miq_group => User.current_user.current_group) - report.extras = {:total_html_rows => 100} - rp_id = report.id - rr_id = report.miq_report_results[0].id - node = "xx-#{rp_id}-2_rr-#{rr_id}" - html = '' - 100.times do - html += '' - end - html += '
column 1column 2
col1 valuecol2 value
' - - allow(controller).to receive(:cb_rpts_show_saved_report) - expect(controller).to receive(:render) - controller.instance_variable_set(:@sb, - :active_tree => :cb_reports_tree, - :trees => {:cb_reports_tree => {:active_node => node}}) - controller.instance_variable_set(:@report, report) - controller.instance_variable_set(:@html, html) - controller.instance_variable_set(:@layout, "chargeback") - controller.params = {:id => node} - controller.send(:tree_select) - expect(response).to render_template('layouts/_saved_report_paging_bar') - expect(controller.send(:flash_errors?)).to be_falsey - expect(response.status).to eq(200) - end - - describe "#cb_rpt_build_folder_nodes" do - let!(:admin_user) { FactoryBot.create(:user_admin) } - let!(:chargeback_report) { FactoryBot.create(:miq_report_chargeback_with_results) } - - before { login_as admin_user } - - it "returns list of saved chargeback report results" do - controller.send(:cb_rpt_build_folder_nodes) - - parent_reports = controller.instance_variable_get(:@parent_reports) - - tree_id = "#{chargeback_report.id}-0" - expected_result = {chargeback_report.miq_report_results.first.miq_report.name => tree_id} - expect(parent_reports).to eq(expected_result) - end - end - end - describe "#explorer" do render_views @@ -149,44 +40,6 @@ end end - describe "#get_cis_all" do - let!(:storage) { FactoryBot.create(:storage) } - let!(:miq_enterprise) { FactoryBot.create(:miq_enterprise) } - - it "returns names of instances of enterprise" do - names_miqent = {} - MiqEnterprise.all.each do |instance| - names_miqent[instance.id] = instance.name - end - controller.instance_variable_set(:@edit, :new => {:cbshow_typ => "enterprise"}, :cb_assign => {}) - controller.send(:get_cis_all) - expect(assigns(:edit)[:cb_assign][:cis]).to eq(names_miqent) - end - - it "returns names of instances of storage" do - names_storage = {} - element = "storage" - element.classify.constantize.all.each do |instance| - names_storage[instance.id] = instance.name - end - controller.instance_variable_set(:@edit, :new => {:cbshow_typ => element}, :cb_assign => {}) - controller.send(:get_cis_all) - expect(assigns(:edit)[:cb_assign][:cis]).to eq(names_storage) - end - - it "returns a ArgumentError when element not in whitelist" do - controller.instance_variable_set(:@edit, :new => {:cbshow_typ => "None"}, :cb_assign => {}) - expect { controller.send(:get_cis_all) }.to raise_error(ArgumentError) - end - - it "doesn't names of instances when nothing is selected" do - controller.instance_variable_set(:@edit, - :new => {:cbshow_typ => described_class::NOTHING_FORM_VALUE}, :cb_assign => {}) - controller.send(:get_cis_all) - expect(assigns(:edit)[:cb_assign][:cis]).to eq({}) - end - end - context "chargeback rate form actions" do # indexing inputs regard to database # html inputs have names(and ids) in form "start_0_0", "finish_0_2" @@ -217,7 +70,7 @@ before do EvmSpecHelper.local_miq_server allow_any_instance_of(described_class).to receive(:center_toolbar_filename).and_return("chargeback_center_tb") - seed_session_trees('chargeback', :cb_rates_tree, "xx-Compute") + seed_session_trees('chargeback_rate', :cb_rates_tree, "xx-Compute") [ChargebackRateDetailMeasure, ChargeableField].each(&:seed) end @@ -240,8 +93,8 @@ def change_form_value(field, value, resource_action = nil) it "renders edit form with correct values" do post :x_button, :params => {:pressed => "chargeback_rates_edit", :id => chargeback_rate.id} - expect(response).to render_template(:partial => 'chargeback/_cb_rate_edit') - expect(response).to render_template(:partial => 'chargeback/_cb_rate_edit_table') + expect(response).to render_template(:partial => 'chargeback_rate/_cb_rate_edit') + expect(response).to render_template(:partial => 'chargeback_rate/_cb_rate_edit_table') main_content = JSON.parse(response.body)['updatePartials']['main_div'] expect_input(main_content, "description", "foo") @@ -259,7 +112,7 @@ def change_form_value(field, value, resource_action = nil) response_body = response.body.delete('\\').gsub('u003e', ">").gsub('u003c', "<") - expect(response).to render_template(:partial => 'chargeback/_cb_rate_edit_table') + expect(response).to render_template(:partial => 'chargeback_rate/_cb_rate_edit_table') expect_rendered_tiers(response_body, [{:start => "0.0", :finish => "20.0"}, {:start => "40.0", :finish => Float::INFINITY}]) @@ -589,36 +442,9 @@ def convert_chargeback_rate_to_hash(rate) end end - describe "#cb_rpts_fetch_saved_report" do - let(:current_user) { User.current_user } - let(:miq_task) { MiqTask.new(:name => "Generate Report result", :userid => current_user.userid) } - let(:miq_report_result) do - FactoryBot.create(:miq_chargeback_report_result, :miq_group => current_user.current_group, :miq_task => miq_task) - end - - let(:chargeback_report) { FactoryBot.create(:miq_report_chargeback, :miq_report_results => [miq_report_result]) } - - before do - miq_task.state_finished - miq_report_result.report = chargeback_report.to_hash.merge(:extras=> {:total_html_rows => 100}) - miq_report_result.save - controller.instance_variable_set(:@sb, {}) - controller.instance_variable_set(:@settings, :perpage => { :reports => 20 }) - end - - it "fetch existing report" do - controller.send(:cb_rpts_fetch_saved_report, miq_report_result.id) - - fetched_report = controller.instance_variable_get(:@report) - - expect(fetched_report).not_to be_nil - expect(fetched_report).to eq(chargeback_report) - end - end - describe "#replace_right_cell" do it "Can build the :cb_rates tree" do - seed_session_trees('chargeback', :cb_rates, 'root') + seed_session_trees('chargeback_rate', :cb_rates, 'root') session_to_sb expect(controller).to receive(:render) @@ -701,95 +527,4 @@ def convert_chargeback_rate_to_hash(rate) end end end - - context "GenericSessionMixin" do - let(:lastaction) { 'lastaction' } - let(:display) { 'display' } - let(:current_page) { 'current_page' } - - describe '#get_session_data' do - it "Sets variables correctly" do - allow(controller).to receive(:session).and_return(:chargeback_lastaction => lastaction, - :chargeback_display => display, - :chargeback_current_page => current_page) - controller.send(:get_session_data) - - expect(controller.instance_variable_get(:@title)).to eq("Chargeback") - expect(controller.instance_variable_get(:@layout)).to eq("chargeback") - expect(controller.instance_variable_get(:@lastaction)).to eq(lastaction) - expect(controller.instance_variable_get(:@display)).to eq(display) - expect(controller.instance_variable_get(:@current_page)).to eq(current_page) - end - end - - describe '#set_session_data' do - it "Sets session correctly" do - controller.instance_variable_set(:@lastaction, lastaction) - controller.instance_variable_set(:@display, display) - controller.instance_variable_set(:@current_page, current_page) - controller.send(:set_session_data) - - expect(controller.session[:chargeback_lastaction]).to eq(lastaction) - expect(controller.session[:chargeback_display]).to eq(display) - expect(controller.session[:chargeback_current_page]).to eq(current_page) - end - end - end - - describe '#cb_assign_field_changed' do - let(:edit) do - { - :cb_assign => { - :cats => { - '1' => 'Category1', - '2' => 'Category2' - }, - :tags => { - 1 => { - '2' => 'Tag1', - '3' => 'Tag2', - '4' => 'Tag3' - }, - 2 => {} - } - }, - :current => { - :cbshow_typ => 'storage-tags', - :cbtag_cat => '1', - 'storage-tags__2' => '3' - }, - :new => { - :cbshow_typ => 'storage-tags', - :cbtag_cat => '1', - 'storage-tags__2' => '3' - } - } - end - - before do - allow(controller).to receive(:load_edit).and_return(true) - controller.instance_variable_set(:@edit, edit) - end - - context 'changing Tag Category for assignments' do - it 'hides buttons as no change has been made' do - post :cb_assign_field_changed, :params => {:cbtag_cat => '2'} - expect(response.body).to include("miqButtons('hide');") - end - end - - context 'changing Assigned To, for assignments' do - it 'hides buttons as no change has been made' do - post :cb_assign_field_changed, :params => {:cbshow_typ => 'storage'} - expect(response.body).to include("miqButtons('hide');") - end - end - - context 'changing item under Selections, for assignments' do - it 'shows buttons as a change in Selections has been made' do - post :cb_assign_field_changed, :params => {'storage-tags__3' => '4'} - expect(response.body).to include("miqButtons('show');") - end - end - end end diff --git a/spec/controllers/chargeback_report_controller_spec.rb b/spec/controllers/chargeback_report_controller_spec.rb new file mode 100644 index 00000000000..afaa639334f --- /dev/null +++ b/spec/controllers/chargeback_report_controller_spec.rb @@ -0,0 +1,121 @@ +describe ChargebackReportController do + before { stub_user(:features => :all) } + + context "Saved chargeback rendering" do + it "Saved chargeback reports renders paginagion buttons correctly" do + report = FactoryBot.create(:miq_report_with_results, :miq_group => User.current_user.current_group) + report.extras = {:total_html_rows => 100} + rp_id = report.id + rr_id = report.miq_report_results[0].id + node = "xx-#{rp_id}-2_rr-#{rr_id}" + html = '' + 100.times do + html += '' + end + html += '
column 1column 2
col1 valuecol2 value
' + + allow(controller).to receive(:cb_rpts_show_saved_report) + expect(controller).to receive(:render) + controller.instance_variable_set(:@sb, + :active_tree => :cb_reports_tree, + :trees => {:cb_reports_tree => {:active_node => node}}) + controller.instance_variable_set(:@report, report) + controller.instance_variable_set(:@html, html) + controller.instance_variable_set(:@layout, "chargeback") + controller.params = {:id => node} + controller.send(:tree_select) + expect(response).to render_template('layouts/_saved_report_paging_bar') + expect(controller.send(:flash_errors?)).to be_falsey + expect(response.status).to eq(200) + end + + describe "#cb_rpt_build_folder_nodes" do + let!(:admin_user) { FactoryBot.create(:user_admin) } + let!(:chargeback_report) { FactoryBot.create(:miq_report_chargeback_with_results) } + + before { login_as admin_user } + + it "returns list of saved chargeback report results" do + controller.send(:cb_rpt_build_folder_nodes) + + parent_reports = controller.instance_variable_get(:@parent_reports) + + tree_id = "#{chargeback_report.id}-0" + expected_result = {chargeback_report.miq_report_results.first.miq_report.name => tree_id} + expect(parent_reports).to eq(expected_result) + end + end + end + + describe "#explorer" do + render_views + + it "can be rendered" do + EvmSpecHelper.create_guid_miq_server_zone + get :explorer + expect(response.status).to eq(200) + expect(response.body).to_not be_empty + end + end + + describe "#cb_rpts_fetch_saved_report" do + let(:current_user) { User.current_user } + let(:miq_task) { MiqTask.new(:name => "Generate Report result", :userid => current_user.userid) } + let(:miq_report_result) do + FactoryBot.create(:miq_chargeback_report_result, :miq_group => current_user.current_group, :miq_task => miq_task) + end + + let(:chargeback_report) { FactoryBot.create(:miq_report_chargeback, :miq_report_results => [miq_report_result]) } + + before do + miq_task.state_finished + miq_report_result.report = chargeback_report.to_hash.merge(:extras=> {:total_html_rows => 100}) + miq_report_result.save + controller.instance_variable_set(:@sb, {}) + controller.instance_variable_set(:@settings, :perpage => { :reports => 20 }) + end + + it "fetch existing report" do + controller.send(:cb_rpts_fetch_saved_report, miq_report_result.id) + + fetched_report = controller.instance_variable_get(:@report) + + expect(fetched_report).not_to be_nil + expect(fetched_report).to eq(chargeback_report) + end + end + + context "GenericSessionMixin" do + let(:lastaction) { 'lastaction' } + let(:display) { 'display' } + let(:current_page) { 'chargeback_report_current_page' } + + describe '#get_session_data' do + it "Sets variables correctly" do + allow(controller).to receive(:session).and_return(:chargeback_report_lastaction => lastaction, + :chargeback_report_display => display, + :chargeback_report_current_page => current_page) + controller.send(:get_session_data) + + expect(controller.instance_variable_get(:@title)).to eq("Chargeback Reports") + expect(controller.instance_variable_get(:@layout)).to eq("chargeback_report") + expect(controller.instance_variable_get(:@lastaction)).to eq(lastaction) + expect(controller.instance_variable_get(:@display)).to eq(display) + expect(controller.instance_variable_get(:@current_page)).to eq(current_page) + end + end + + describe '#set_session_data' do + it "Sets session correctly" do + controller.instance_variable_set(:@lastaction, lastaction) + controller.instance_variable_set(:@display, display) + controller.instance_variable_set(:@current_page, current_page) + controller.send(:set_session_data) + + expect(controller.session[:chargeback_report_lastaction]).to eq(lastaction) + expect(controller.session[:chargeback_report_display]).to eq(display) + expect(controller.session[:chargeback_report_current_page]).to eq(current_page) + end + end + end +end diff --git a/spec/helpers/application_helper/toolbar_chooser_spec.rb b/spec/helpers/application_helper/toolbar_chooser_spec.rb index 9b81b4d884f..3bfc41b7938 100644 --- a/spec/helpers/application_helper/toolbar_chooser_spec.rb +++ b/spec/helpers/application_helper/toolbar_chooser_spec.rb @@ -136,7 +136,9 @@ let(:explorer) { true } %w( - chargeback + chargeback_assignment + chargeback_rate + chargeback_report generic_object_definition miq_ae_class miq_ae_customization diff --git a/spec/routing/chargeback_assignment_routing_spec.rb b/spec/routing/chargeback_assignment_routing_spec.rb new file mode 100644 index 00000000000..4d6350a3fce --- /dev/null +++ b/spec/routing/chargeback_assignment_routing_spec.rb @@ -0,0 +1,33 @@ +describe 'routes for ChargebackAssignmentController' do + let(:controller_name) { 'chargeback_assignment' } + + describe '#cb_assign_field_changed' do + it 'routes with POST' do + expect(post("/#{controller_name}/cb_assign_field_changed")).to route_to( + "#{controller_name}#cb_assign_field_changed" + ) + end + end + + describe '#cb_assign_update' do + it 'routes with POST' do + expect(post("/#{controller_name}/cb_assign_update")).to route_to("#{controller_name}#cb_assign_update") + end + end + + describe '#explorer' do + it 'routes with GET' do + expect(get("/#{controller_name}/explorer")).to route_to("#{controller_name}#explorer") + end + + it 'routes with POST' do + expect(post("/#{controller_name}/explorer")).to route_to("#{controller_name}#explorer") + end + end + + describe '#index' do + it 'routes with GET' do + expect(get("/#{controller_name}")).to route_to("#{controller_name}#index") + end + end +end diff --git a/spec/routing/chargeback_routing_spec.rb b/spec/routing/chargeback_rate_routing_spec.rb similarity index 53% rename from spec/routing/chargeback_routing_spec.rb rename to spec/routing/chargeback_rate_routing_spec.rb index 0bfca4b34c1..c8489ed47a5 100644 --- a/spec/routing/chargeback_routing_spec.rb +++ b/spec/routing/chargeback_rate_routing_spec.rb @@ -1,25 +1,5 @@ -describe 'routes for ChargebackController' do - let(:controller_name) { 'chargeback' } - - describe '#accordion_select' do - it 'routes with POST' do - expect(post("/#{controller_name}/accordion_select")).to route_to("#{controller_name}#accordion_select") - end - end - - describe '#cb_assign_field_changed' do - it 'routes with POST' do - expect(post("/#{controller_name}/cb_assign_field_changed")).to route_to( - "#{controller_name}#cb_assign_field_changed" - ) - end - end - - describe '#cb_assign_update' do - it 'routes with POST' do - expect(post("/#{controller_name}/cb_assign_update")).to route_to("#{controller_name}#cb_assign_update") - end - end +describe 'routes for ChargebackRateController' do + let(:controller_name) { 'chargeback_rate' } describe '#cb_rate_edit' do it 'routes with POST' do @@ -69,30 +49,6 @@ end end - describe '#render_csv' do - it 'routes with GET' do - expect(get("/#{controller_name}/render_csv")).to route_to("#{controller_name}#render_csv") - end - end - - describe '#render_pdf' do - it 'routes with GET' do - expect(get("/#{controller_name}/render_pdf")).to route_to("#{controller_name}#render_pdf") - end - end - - describe '#render_txt' do - it 'routes with GET' do - expect(get("/#{controller_name}/render_txt")).to route_to("#{controller_name}#render_txt") - end - end - - describe '#report_only' do - it 'routes with GET' do - expect(get("/#{controller_name}/report_only")).to route_to("#{controller_name}#report_only") - end - end - describe '#x_show' do it 'routes with POST' do expect(post("/#{controller_name}/x_show")).to route_to("#{controller_name}#x_show") diff --git a/spec/routing/chargeback_report_routing_spec.rb b/spec/routing/chargeback_report_routing_spec.rb new file mode 100644 index 00000000000..d4f1a2200b8 --- /dev/null +++ b/spec/routing/chargeback_report_routing_spec.rb @@ -0,0 +1,49 @@ +describe 'routes for ChargebackReportController' do + let(:controller_name) { 'chargeback_report' } + + describe '#explorer' do + it 'routes with GET' do + expect(get("/#{controller_name}/explorer")).to route_to("#{controller_name}#explorer") + end + + it 'routes with POST' do + expect(post("/#{controller_name}/explorer")).to route_to("#{controller_name}#explorer") + end + end + + describe '#index' do + it 'routes with GET' do + expect(get("/#{controller_name}")).to route_to("#{controller_name}#index") + end + end + + describe '#render_csv' do + it 'routes with GET' do + expect(get("/#{controller_name}/render_csv")).to route_to("#{controller_name}#render_csv") + end + end + + describe '#render_pdf' do + it 'routes with GET' do + expect(get("/#{controller_name}/render_pdf")).to route_to("#{controller_name}#render_pdf") + end + end + + describe '#render_txt' do + it 'routes with GET' do + expect(get("/#{controller_name}/render_txt")).to route_to("#{controller_name}#render_txt") + end + end + + describe '#report_only' do + it 'routes with GET' do + expect(get("/#{controller_name}/report_only")).to route_to("#{controller_name}#report_only") + end + end + + describe '#x_show' do + it 'routes with POST' do + expect(post("/#{controller_name}/x_show")).to route_to("#{controller_name}#x_show") + end + end +end