From 42da4439018c57a867ac54f0b112eef50f425bb2 Mon Sep 17 00:00:00 2001 From: Sebastian Castro Date: Thu, 7 Apr 2022 08:52:23 +0200 Subject: [PATCH] Report Refactor 3: Enterprise FeeSummary This one as actually not really been refactored, too much work to rewrite it so I just encapsulated the result within the new methods definitions --- .../reports/enterprise_fee_summaries_spec.rb | 162 -- ...terprise_fee_summary_report.rb => base.rb} | 51 +- .../enterprise_fee_summary_report_spec.rb | 1454 ++++++++--------- .../reports/enterprise_fee_summaries_spec.rb | 162 ++ 4 files changed, 910 insertions(+), 919 deletions(-) delete mode 100644 engines/order_management/spec/features/order_management/reports/enterprise_fee_summaries_spec.rb rename lib/reporting/reports/enterprise_fee_summary/{enterprise_fee_summary_report.rb => base.rb} (64%) create mode 100644 spec/system/admin/reports/enterprise_fee_summaries_spec.rb diff --git a/engines/order_management/spec/features/order_management/reports/enterprise_fee_summaries_spec.rb b/engines/order_management/spec/features/order_management/reports/enterprise_fee_summaries_spec.rb deleted file mode 100644 index a7dbc0c00e..0000000000 --- a/engines/order_management/spec/features/order_management/reports/enterprise_fee_summaries_spec.rb +++ /dev/null @@ -1,162 +0,0 @@ -# frozen_string_literal: true - -require "spec_helper" - -# feature "enterprise fee summaries", js: true do -# include AuthenticationHelper -# include WebHelper - -# let!(:distributor) { create(:distributor_enterprise) } -# let!(:other_distributor) { create(:distributor_enterprise) } - -# let!(:order_cycle) { create(:simple_order_cycle, coordinator: distributor) } -# let!(:other_order_cycle) { create(:simple_order_cycle, coordinator: other_distributor) } - -# before do -# login_as current_user -# end - -# describe "navigation" do -# context "when accessing the report as an superadmin" do -# let(:current_user) { create(:admin_user) } - -# it "shows link and allows access to the report" do -# visit spree.admin_reports_path -# click_on I18n.t("admin.reports.enterprise_fee_summary.name") -# expect(page).to have_button("Go") -# end -# end - -# context "when accessing the report as an admin" do -# let(:current_user) { distributor.owner } - -# it "shows link and allows access to the report" do -# visit spree.admin_reports_path -# click_on I18n.t("admin.reports.enterprise_fee_summary.name") -# expect(page).to have_button("Go") -# end -# end - -# context "when accessing the report as an enterprise user without sufficient permissions" do -# let(:current_user) { create(:user) } - -# it "does not allow access to the report" do -# visit spree.admin_reports_path -# expect(page).to have_no_link(I18n.t("admin.reports.enterprise_fee_summary.name")) -# visit main_app.new_order_management_reports_enterprise_fee_summary_path -# expect(page).to have_content(I18n.t("unauthorized")) -# end -# end -# end - -# describe "smoke test for filters" do -# before do -# visit main_app.new_order_management_reports_enterprise_fee_summary_path -# end - -# context "when logged in as admin" do -# let(:current_user) { create(:admin_user) } - -# it "shows all available options" do -# expect(page).to have_select "q_report_order_cycle_ids", with_options: [order_cycle.name] -# end -# end - -# context "when logged in as enterprise user" do -# let!(:order) do -# create(:completed_order_with_fees, order_cycle: order_cycle, -# distributor: distributor) -# end -# let(:current_user) { distributor.owner } - -# it "shows available options for the enterprise" do -# expect(page).to have_select "q_report_order_cycle_ids", options: [order_cycle.name] -# end -# end -# end - -# describe "csv downloads" do -# around do |example| -# with_empty_downloads_folder { example.run } -# end - -# describe "smoke test for generation of report based on permissions" do -# before do -# visit main_app.new_order_management_reports_enterprise_fee_summary_path -# end - -# context "when logged in as admin" do -# let!(:order) do -# create(:completed_order_with_fees, order_cycle: order_cycle, -# distributor: distributor) -# end -# let(:current_user) { create(:admin_user) } - -# it "generates file with data for all enterprises" do -# check I18n.t("filters.report_format_csv", scope: i18n_scope) -# click_on "Go" - -# expect(downloaded_filename).to include ".csv" -# expect(downloaded_content).to have_content(distributor.name) -# end -# end - -# context "when logged in as enterprise user" do -# let!(:order) do -# create(:completed_order_with_fees, order_cycle: order_cycle, -# distributor: distributor) -# end -# let!(:other_order) do -# create(:completed_order_with_fees, order_cycle: other_order_cycle, -# distributor: other_distributor) -# end -# let(:current_user) { distributor.owner } - -# it "generates file with data for the enterprise" do -# check I18n.t("filters.report_format_csv", scope: i18n_scope) -# click_on "Go" - -# expect(downloaded_filename).to include ".csv" -# csv_content = downloaded_content -# expect(csv_content).to have_content(distributor.name) -# expect(csv_content).not_to have_content(other_distributor.name) -# end -# end -# end - -# describe "smoke test for filtering report based on filters" do -# let!(:second_distributor) { create(:distributor_enterprise) } -# let!(:second_order_cycle) { create(:simple_order_cycle, coordinator: second_distributor) } - -# let!(:order) do -# create(:completed_order_with_fees, order_cycle: order_cycle, -# distributor: distributor) -# end -# let!(:second_order) do -# create(:completed_order_with_fees, order_cycle: second_order_cycle, -# distributor: second_distributor) -# end - -# let(:current_user) { create(:admin_user) } - -# before do -# visit main_app.new_order_management_reports_enterprise_fee_summary_path -# end - -# it "generates file with data for selected order cycle" do -# select order_cycle.name, from: "report_order_cycle_ids" -# check I18n.t("filters.report_format_csv", scope: i18n_scope) -# click_on "Go" - -# expect(downloaded_filename).to include ".csv" -# csv_content = downloaded_content -# expect(csv_content).to have_content(distributor.name) -# expect(csv_content).not_to have_content(second_distributor.name) -# end -# end -# end - -# def i18n_scope -# "order_management.reports.enterprise_fee_summaries" -# end -# end diff --git a/lib/reporting/reports/enterprise_fee_summary/enterprise_fee_summary_report.rb b/lib/reporting/reports/enterprise_fee_summary/base.rb similarity index 64% rename from lib/reporting/reports/enterprise_fee_summary/enterprise_fee_summary_report.rb rename to lib/reporting/reports/enterprise_fee_summary/base.rb index 69f443aca8..6ffa34a67a 100644 --- a/lib/reporting/reports/enterprise_fee_summary/enterprise_fee_summary_report.rb +++ b/lib/reporting/reports/enterprise_fee_summary/base.rb @@ -3,7 +3,7 @@ module Reporting module Reports module EnterpriseFeeSummary - class EnterpriseFeeSummaryReport < ReportObjectTemplate + class Base < ReportObjectTemplate attr_accessor :permissions, :parameters def initialize(user, params = {}) @@ -13,33 +13,29 @@ module Reporting p['start_at'] = p.delete('completed_at_gt') p['end_at'] = p.delete('completed_at_lt') end - @parameters = Reporting::Reports::EnterpriseFeeSummary::Parameters.new(p || {}) + @parameters = Parameters.new(p || {}) @parameters.validate! @permissions = Permissions.new(user) @parameters.authorize!(@permissions) end + def translate_header(key) + I18n.t("header.#{key}", scope: i18n_scope) + end + + def i18n_scope + "order_management.reports.enterprise_fee_summary.formats.csv" + end + def message I18n.t("spree.admin.reports.customer_names_message.customer_names_tip") end - def table_headers - data_row_attributes.map do |attribute| - header_label(attribute) - end + def query_result + enterprise_fee_type_total_list.sort end - def table_rows - enterprise_fee_type_total_list.sort.map do |data| - data_row_attributes.map do |attribute| - data.public_send(attribute) - end - end - end - - private - - def data_row_attributes + def data_attributes [ :fee_type, :enterprise_name, @@ -52,13 +48,15 @@ module Reporting ] end - def header_label(attribute) - I18n.t("header.#{attribute}", scope: i18n_scope) + # This report calculate data in a different way, so we just encapsulate the result + # in the columns method + def columns + data_attributes.map { |field| + [field.to_sym, proc { |data| data.public_send(field) }] + }.to_h end - def i18n_scope - "order_management.reports.enterprise_fee_summary.formats.csv" - end + private def enterprise_fees_by_customer if parameters.order_cycle_ids.empty? @@ -73,19 +71,12 @@ module Reporting summarizer = Summarizer.new(total_data) ReportData::EnterpriseFeeTypeTotal.new.tap do |total| - enterprise_fee_type_summarizer_to_total_attributes.each do |attribute| + data_attributes.each do |attribute| total.public_send("#{attribute}=", summarizer.public_send(attribute)) end end end end - - def enterprise_fee_type_summarizer_to_total_attributes - [ - :fee_type, :enterprise_name, :fee_name, :customer_name, :fee_placement, - :fee_calculated_on_transfer_through_name, :tax_category_name, :total_amount - ] - end end end end diff --git a/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb b/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb index c5db21860a..0487954d33 100644 --- a/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb +++ b/spec/lib/reports/enterprise_fee_summary/enterprise_fee_summary_report_spec.rb @@ -2,730 +2,730 @@ require "spec_helper" -# rubocop:disable Layout/LineLength -# describe Reporting::Reports::EnterpriseFeeSummary::EnterpriseFeeSummaryReport do -# let(:report_klass) { Reporting::Reports::EnterpriseFeeSummary } - -# # Basic data. -# let!(:shipping_method) do -# create(:shipping_method, :per_item, amount: 1, name: "Sample Shipping Method") -# end - -# let!(:payment_method) do -# create(:payment_method, :per_item, amount: 2, name: "Sample Payment Method") -# end - -# # Create enterprises. -# let!(:distributor) do -# create(:distributor_enterprise, name: "Sample Distributor").tap do |enterprise| -# payment_method.distributors << enterprise -# shipping_method.distributors << enterprise -# end -# end -# let!(:producer) { create(:supplier_enterprise, name: "Sample Producer") } -# let!(:coordinator) { create(:enterprise, name: "Sample Coordinator") } - -# # Add some fee noise. -# let!(:other_distributor_fee) { create(:enterprise_fee, :per_item, enterprise: distributor) } -# let!(:other_producer_fee) { create(:enterprise_fee, :per_item, enterprise: producer) } -# let!(:other_coordinator_fee) { create(:enterprise_fee, :per_item, enterprise: coordinator) } - -# # Set up other requirements for ordering. -# let!(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } -# let!(:product) { create(:product, tax_category: product_tax_category) } -# let!(:product_tax_category) { create(:tax_category, name: "Sample Product Tax") } -# let!(:variant) { prepare_variant } - -# # Create customers. -# let!(:customer) { create(:customer, first_name: "Sample", last_name: "Customer") } -# let!(:another_customer) { create(:customer, first_name: "Another", last_name: "Customer") } - -# # Setup up permissions and report. -# let!(:current_user) { create(:admin_user) } - -# let(:permissions) { report_klass::Permissions.new(current_user) } -# let(:parameters) { report_klass::Parameters.new } -# let(:service) { described_class.new(permissions, parameters) } - -# describe "grouping and sorting of entries" do -# let!(:order_cycle) do -# create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: order_cycle_fees) -# end - -# let!(:variant) do -# prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, -# outgoing_exchange_fees: variant_outgoing_exchange_fees) -# end - -# let!(:order_cycle_fees) do -# [ -# create(:enterprise_fee, :per_item, name: "Coordinator Fee 1", enterprise: coordinator, -# fee_type: "admin", amount: 512.0, -# tax_category: coordinator_tax_category), -# create(:enterprise_fee, :per_item, name: "Coordinator Fee 2", enterprise: coordinator, -# fee_type: "sales", amount: 1024.0, -# inherits_tax_category: true) -# ] -# end -# let!(:coordinator_tax_category) { create(:tax_category, name: "Sample Coordinator Tax") } - -# let!(:variant_incoming_exchange_fees) do -# [ -# create(:enterprise_fee, :per_item, name: "Producer Fee 1", enterprise: producer, -# fee_type: "sales", amount: 64.0, -# tax_category: producer_tax_category), -# create(:enterprise_fee, :per_item, name: "Producer Fee 2", enterprise: producer, -# fee_type: "sales", amount: 128.0, -# inherits_tax_category: true) -# ] -# end -# let!(:producer_tax_category) { create(:tax_category, name: "Sample Producer Tax") } - -# let!(:variant_outgoing_exchange_fees) do -# [ -# create(:enterprise_fee, :per_item, name: "Distributor Fee 1", enterprise: distributor, -# fee_type: "admin", amount: 4.0, -# tax_category: distributor_tax_category), -# create(:enterprise_fee, :per_item, name: "Distributor Fee 2", enterprise: distributor, -# fee_type: "sales", amount: 8.0, -# inherits_tax_category: true) -# ] -# end -# let!(:distributor_tax_category) { create(:tax_category, name: "Sample Distributor Tax") } - -# let!(:customer_order) { prepare_order(customer: customer) } -# let!(:customer_incomplete_order) { prepare_incomplete_order(customer: customer) } -# let!(:second_customer_order) { prepare_order(customer: customer) } -# let!(:other_customer_order) { prepare_order(customer: another_customer) } - -# it "groups and sorts entries correctly" do -# totals = service.list - -# expect(totals.length).to eq(16) - -# # Data is sorted by the following, in order: -# # * fee_type -# # * enterprise_name -# # * fee_name -# # * customer_name -# # * fee_placement -# # * fee_calculated_on_transfer_through_name -# # * tax_category_name -# # * total_amount - -# expected_result = [ -# ["Admin", "Sample Coordinator", "Coordinator Fee 1", "Another Customer", -# "Coordinator", "All", "Sample Coordinator Tax", "512.00"], -# ["Admin", "Sample Coordinator", "Coordinator Fee 1", "Sample Customer", -# "Coordinator", "All", "Sample Coordinator Tax", "1024.00"], -# ["Admin", "Sample Distributor", "Distributor Fee 1", "Another Customer", -# "Outgoing", "Sample Distributor", "Sample Distributor Tax", "4.00"], -# ["Admin", "Sample Distributor", "Distributor Fee 1", "Sample Customer", -# "Outgoing", "Sample Distributor", "Sample Distributor Tax", "8.00"], -# ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Another Customer", -# nil, nil, nil, "2.00"], -# ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", -# nil, nil, nil, "4.00"], -# ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Another Customer", -# "Coordinator", "All", "Various", "1024.00"], -# ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Sample Customer", -# "Coordinator", "All", "Various", "2048.00"], -# ["Sales", "Sample Distributor", "Distributor Fee 2", "Another Customer", -# "Outgoing", "Sample Distributor", "Sample Product Tax", "8.00"], -# ["Sales", "Sample Distributor", "Distributor Fee 2", "Sample Customer", -# "Outgoing", "Sample Distributor", "Sample Product Tax", "16.00"], -# ["Sales", "Sample Producer", "Producer Fee 1", "Another Customer", -# "Incoming", "Sample Producer", "Sample Producer Tax", "64.00"], -# ["Sales", "Sample Producer", "Producer Fee 1", "Sample Customer", -# "Incoming", "Sample Producer", "Sample Producer Tax", "128.00"], -# ["Sales", "Sample Producer", "Producer Fee 2", "Another Customer", -# "Incoming", "Sample Producer", "Sample Product Tax", "128.00"], -# ["Sales", "Sample Producer", "Producer Fee 2", "Sample Customer", -# "Incoming", "Sample Producer", "Sample Product Tax", "256.00"], -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Another Customer", -# nil, nil, "Platform Rate", "1.00"], -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "2.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end - -# describe "data exclusions" do -# describe "invalid adjustments (through 'eligible') like failed payments" do -# let!(:customer_order) { prepare_order(customer: customer) } - -# before do -# # Make the payment fail. See Spree::Payment#revoke_adjustment_eligibility. -# payment = customer_order.payments.first -# payment.state = "failed" -# payment.save! -# end - -# it "is included" do -# totals = service.list - -# expect(totals.length).to eq(1) - -# expected_result = [ -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "1.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end - -# describe "non-mandatory $0 adjustments (through 'eligible')" do -# let!(:variant) { prepare_variant(outgoing_exchange_fees: [enterprise_fee]) } - -# let!(:enterprise_fee) do -# create(:enterprise_fee, :per_item, name: "Sample Enterprise Fee", enterprise: distributor, -# fee_type: "admin", amount: 0) -# end - -# let!(:customer_order) { prepare_order(customer: customer) } - -# before do -# # Change "eligible" in enterprise fee adjustment to false. $0 adjustments that are not -# # mandatory are set to be ineligible, but there are no non-mandatory adjustments supported -# # by the report yet. -# adjustment = Spree::Adjustment.where(originator_type: "EnterpriseFee").first -# adjustment.eligible = false -# adjustment.save! -# end - -# it "is included" do -# totals = service.list - -# expect(totals.length).to eq(2) - -# expected_result = [ -# ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", -# nil, nil, nil, "2.00"], -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "1.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end - -# describe "$0 mandatory adjustments" do -# let!(:payment_method) do -# create(:payment_method, :per_item, amount: 0, name: "Sample Payment Method") -# end - -# let!(:customer_order) { prepare_order(customer: customer) } - -# it "is included" do -# totals = service.list - -# expect(totals.length).to eq(1) - -# expected_result = [ -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "1.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end -# end - -# describe "handling of more complex cases" do -# context "with non-sender fee for incoming exchange and non-receiver fee for outgoing" do -# let!(:variant) do -# prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, -# outgoing_exchange_fees: variant_outgoing_exchange_fees) -# end -# let!(:variant_incoming_exchange_fees) { [coordinator_fee, distributor_fee] } -# let!(:variant_outgoing_exchange_fees) { [producer_fee, coordinator_fee] } - -# let!(:producer_fee) do -# tax_category = create(:tax_category, name: "Sample Producer Tax") -# create(:enterprise_fee, :per_item, name: "Sample Producer Fee", enterprise: producer, -# fee_type: "sales", amount: 64.0, -# tax_category: tax_category) -# end -# let!(:coordinator_fee) do -# tax_category = create(:tax_category, name: "Sample Coordinator Tax") -# create(:enterprise_fee, :per_item, name: "Sample Coordinator Fee", enterprise: coordinator, -# fee_type: "admin", amount: 512.0, -# tax_category: tax_category) -# end -# let!(:distributor_fee) do -# tax_category = create(:tax_category, name: "Sample Distributor Tax") -# create(:enterprise_fee, :per_item, name: "Sample Distributor Fee", enterprise: distributor, -# fee_type: "admin", amount: 4.0, -# tax_category: tax_category) -# end - -# let!(:customer_order) { prepare_order(customer: customer) } - -# it "fetches data correctly" do -# totals = service.list - -# expect(totals.length).to eq(6) - -# expected_result = [ -# ["Admin", "Sample Coordinator", "Sample Coordinator Fee", "Sample Customer", -# "Incoming", "Sample Producer", "Sample Coordinator Tax", "512.00"], -# ["Admin", "Sample Coordinator", "Sample Coordinator Fee", "Sample Customer", -# "Outgoing", "Sample Distributor", "Sample Coordinator Tax", "512.00"], -# ["Admin", "Sample Distributor", "Sample Distributor Fee", "Sample Customer", -# "Incoming", "Sample Producer", "Sample Distributor Tax", "4.00"], -# ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", -# nil, nil, nil, "2.00"], -# ["Sales", "Sample Producer", "Sample Producer Fee", "Sample Customer", -# "Outgoing", "Sample Distributor", "Sample Producer Tax", "64.00"], -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "1.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end - -# context "with order-based enterprise fee calculator" do -# let!(:producer_fee) do -# tax_category = create(:tax_category, name: "Producer Tax A") -# create(:enterprise_fee, :flat_rate, name: "Producer Fee A", enterprise: producer, -# fee_type: "sales", tax_category: tax_category, -# amount: 10) -# end -# let!(:coordinator_fee) do -# tax_category = create(:tax_category, name: "Coordinator Tax A") -# create(:enterprise_fee, :flat_rate, name: "Coordinator Fee A", enterprise: coordinator, -# fee_type: "admin", tax_category: tax_category, -# amount: 15) -# end -# let!(:coordinator_fee_inheriting_product_tax_category) do -# create(:enterprise_fee, :flat_rate, name: "Coordinator Fee B", enterprise: coordinator, -# fee_type: "admin", inherits_tax_category: true, -# amount: 20) -# end -# let!(:coordinator_fee_without_tax) do -# create(:enterprise_fee, :flat_rate, name: "Coordinator Fee C", enterprise: coordinator, -# fee_type: "admin", inherits_tax_category: false, -# amount: 25) -# end -# let!(:distributor_fee) do -# create(:enterprise_fee, :flat_rate, name: "Distributor Fee A", enterprise: distributor, -# fee_type: "admin", inherits_tax_category: false, -# amount: 30) -# end - -# let!(:coordinator_fees) do -# [ -# coordinator_fee, -# coordinator_fee_inheriting_product_tax_category, -# coordinator_fee_without_tax -# ] -# end - -# let!(:order_cycle) do -# create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: coordinator_fees) -# end - -# let!(:variant_incoming_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } -# let!(:variant_outgoing_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } - -# let!(:variant) do -# prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, -# outgoing_exchange_fees: variant_outgoing_exchange_fees) -# end - -# let!(:customer_order) { prepare_order(customer: customer) } - -# it "fetches data correctly" do -# totals = service.list - -# expect(totals.length).to eq(11) - -# entire_orders_text = i18n_translate("fee_calculated_on_transfer_through_entire_orders", -# distributor: "Sample Distributor") -# various_tax_categories_text = i18n_translate("tax_category_various") - -# expected_result = [ -# ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", -# "Coordinator", "All", "Coordinator Tax A", "15.00"], -# ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", -# "Incoming", entire_orders_text, "Coordinator Tax A", "15.00"], -# ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", -# "Outgoing", entire_orders_text, "Coordinator Tax A", "15.00"], -# ["Admin", "Sample Coordinator", "Coordinator Fee B", "Sample Customer", -# "Coordinator", "All", various_tax_categories_text, "20.00"], -# ["Admin", "Sample Coordinator", "Coordinator Fee C", "Sample Customer", -# "Coordinator", "All", nil, "25.00"], -# ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", -# "Incoming", entire_orders_text, various_tax_categories_text, "30.00"], -# ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", -# "Outgoing", entire_orders_text, various_tax_categories_text, "30.00"], -# ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", -# nil, nil, nil, "2.00"], -# ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", -# "Incoming", entire_orders_text, "Producer Tax A", "10.00"], -# ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", -# "Outgoing", entire_orders_text, "Producer Tax A", "10.00"], -# ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", -# nil, nil, "Platform Rate", "1.00"] -# ] - -# expected_result.each_with_index do |expected_attributes, row_index| -# expect_total_attributes(totals[row_index], expected_attributes) -# end -# end -# end -# end - -# describe "filtering results based on permissions" do -# let!(:distributor_a) do -# create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end -# let!(:distributor_b) do -# create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end - -# let!(:order_cycle_a) { create(:simple_order_cycle, coordinator: coordinator) } -# let!(:order_cycle_b) { create(:simple_order_cycle, coordinator: coordinator) } - -# let!(:variant_a) { prepare_variant(distributor: distributor_a, order_cycle: order_cycle_a) } -# let!(:variant_b) { prepare_variant(distributor: distributor_b, order_cycle: order_cycle_b) } - -# let!(:order_a) { prepare_order(order_cycle: order_cycle_a, distributor: distributor_a) } -# let!(:order_b) { prepare_order(order_cycle: order_cycle_b, distributor: distributor_b) } - -# context "when admin" do -# let!(:current_user) { create(:admin_user) } - -# it "includes all order cycles" do -# totals = service.list - -# expect_total_matches(totals, 2, fee_type: "Shipment") -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") -# end -# end - -# context "when enterprise owner for distributor" do -# let!(:current_user) { distributor_a.owner } - -# it "does not include unrelated order cycles" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_type: "Shipment") -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") -# end -# end -# end - -# describe "filters entries correctly" do -# let(:parameters) { report_klass::Parameters.new(parameters_attributes) } - -# context "filtering by completion date" do -# let(:timestamp) { Time.zone.local(2018, 1, 5, 14, 30, 5) } - -# let!(:customer_a) { create(:customer, first_name: "Customer", last_name: "A") } -# let!(:customer_b) { create(:customer, first_name: "Customer", last_name: "B") } -# let!(:customer_c) { create(:customer, first_name: "Customer", last_name: "C") } - -# let!(:order_placed_before_timestamp) do -# prepare_order(customer: customer_a).tap do |order| -# order.update_column(:completed_at, timestamp - 1.second) -# end -# end - -# let!(:order_placed_during_timestamp) do -# prepare_order(customer: customer_b).tap do |order| -# order.update_column(:completed_at, timestamp) -# end -# end - -# let!(:order_placed_after_timestamp) do -# prepare_order(customer: customer_c).tap do |order| -# order.update_column(:completed_at, timestamp + 1.second) -# end -# end - -# context "on or after start_at" do -# let(:parameters_attributes) { { start_at: timestamp } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 0, fee_type: "Shipment", customer_name: "Customer A") -# expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer B") -# expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer C") -# end -# end - -# context "on or before end_at" do -# let(:parameters_attributes) { { end_at: timestamp } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer A") -# expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer B") -# expect_total_matches(totals, 0, fee_type: "Shipment", customer_name: "Customer C") -# end -# end -# end - -# describe "for specified shops" do -# let!(:distributor_a) do -# create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end -# let!(:distributor_b) do -# create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end -# let!(:distributor_c) do -# create(:distributor_enterprise, name: "Distributor C", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end - -# let!(:order_a) { prepare_order(distributor: distributor_a) } -# let!(:order_b) { prepare_order(distributor: distributor_b) } -# let!(:order_c) { prepare_order(distributor: distributor_c) } - -# let(:parameters_attributes) { { distributor_ids: [distributor_a.id, distributor_b.id] } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") -# expect_total_matches(totals, 0, fee_type: "Shipment", enterprise_name: "Distributor C") -# end -# end - -# describe "for specified suppliers" do -# let!(:producer_a) { create(:supplier_enterprise, name: "Producer A") } -# let!(:producer_b) { create(:supplier_enterprise, name: "Producer B") } -# let!(:producer_c) { create(:supplier_enterprise, name: "Producer C") } - -# let!(:fee_a) { create(:enterprise_fee, name: "Fee A", enterprise: producer_a, amount: 1) } -# let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: producer_b, amount: 1) } -# let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: producer_c, amount: 1) } - -# let!(:product_a) { create(:product, supplier: producer_a) } -# let!(:product_b) { create(:product, supplier: producer_b) } -# let!(:product_c) { create(:product, supplier: producer_c) } - -# let!(:variant_a) do -# prepare_variant(product: product_a, producer: producer_a, incoming_exchange_fees: [fee_a]) -# end -# let!(:variant_b) do -# prepare_variant(product: product_b, producer: producer_b, incoming_exchange_fees: [fee_b]) -# end -# let!(:variant_c) do -# prepare_variant(product: product_c, producer: producer_c, incoming_exchange_fees: [fee_c]) -# end - -# let!(:order_a) { prepare_order(variant: variant_a) } -# let!(:order_b) { prepare_order(variant: variant_b) } -# let!(:order_c) { prepare_order(variant: variant_c) } - -# let(:parameters_attributes) { { producer_ids: [producer_a.id, producer_b.id] } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_name: "Fee A", enterprise_name: "Producer A") -# expect_total_matches(totals, 1, fee_name: "Fee B", enterprise_name: "Producer B") -# expect_total_matches(totals, 0, fee_name: "Fee C", enterprise_name: "Producer C") -# end -# end - -# describe "for specified order cycles" do -# let!(:distributor_a) do -# create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end -# let!(:distributor_b) do -# create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end -# let!(:distributor_c) do -# create(:distributor_enterprise, name: "Distributor C", payment_methods: [payment_method], -# shipping_methods: [shipping_method]) -# end - -# let!(:order_cycle_a) { create(:simple_order_cycle, coordinator: coordinator) } -# let!(:order_cycle_b) { create(:simple_order_cycle, coordinator: coordinator) } -# let!(:order_cycle_c) { create(:simple_order_cycle, coordinator: coordinator) } - -# let!(:variant_a) { prepare_variant(distributor: distributor_a, order_cycle: order_cycle_a) } -# let!(:variant_b) { prepare_variant(distributor: distributor_b, order_cycle: order_cycle_b) } -# let!(:variant_c) { prepare_variant(distributor: distributor_c, order_cycle: order_cycle_c) } - -# let!(:order_a) { prepare_order(order_cycle: order_cycle_a, distributor: distributor_a) } -# let!(:order_b) { prepare_order(order_cycle: order_cycle_b, distributor: distributor_b) } -# let!(:order_c) { prepare_order(order_cycle: order_cycle_c, distributor: distributor_c) } - -# let(:parameters_attributes) { { order_cycle_ids: [order_cycle_a.id, order_cycle_b.id] } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") -# expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") -# expect_total_matches(totals, 0, fee_type: "Shipment", enterprise_name: "Distributor C") -# end -# end - -# describe "for specified enterprise fees" do -# let!(:fee_a) { create(:enterprise_fee, name: "Fee A", enterprise: distributor, amount: 1) } -# let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: distributor, amount: 1) } -# let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: distributor, amount: 1) } - -# let!(:variant) { prepare_variant(outgoing_exchange_fees: variant_outgoing_exchange_fees) } -# let!(:variant_outgoing_exchange_fees) { [fee_a, fee_b, fee_c] } - -# let!(:order) { prepare_order(variant: variant) } - -# let(:parameters_attributes) { { enterprise_fee_ids: [fee_a.id, fee_b.id] } } - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_name: "Fee A") -# expect_total_matches(totals, 1, fee_name: "Fee B") -# expect_total_matches(totals, 0, fee_name: "Fee C") -# end -# end - -# describe "for specified shipping methods" do -# let!(:shipping_method_a) do -# method = create(:shipping_method, name: "Shipping A", distributors: [distributor]) -# method.calculator.update_attribute(:preferred_amount, 1) -# method -# end -# let!(:shipping_method_b) do -# method = create(:shipping_method, name: "Shipping B", distributors: [distributor]) -# method.calculator.update_attribute(:preferred_amount, 1) -# method -# end -# let!(:shipping_method_c) do -# create(:shipping_method, name: "Shipping C", distributors: [distributor]) -# end - -# let!(:order_a) { prepare_order(shipping_method: shipping_method_a) } -# let!(:order_b) { prepare_order(shipping_method: shipping_method_b) } -# let!(:order_c) { prepare_order(shipping_method: shipping_method_c) } - -# let(:parameters_attributes) do -# { shipping_method_ids: [shipping_method_a.id, shipping_method_b.id] } -# end - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_name: "Shipping A") -# expect_total_matches(totals, 1, fee_name: "Shipping B") -# expect_total_matches(totals, 0, fee_name: "Shipping C") -# end -# end - -# describe "for specified payment methods" do -# let!(:payment_method_a) do -# method = create(:payment_method, name: "Payment A", distributors: [distributor]) -# method.calculator.update_attribute(:preferred_amount, 1) -# method -# end -# let!(:payment_method_b) do -# method = create(:payment_method, name: "Payment B", distributors: [distributor]) -# method.calculator.update_attribute(:preferred_amount, 1) -# method -# end -# let!(:payment_method_c) do -# create(:payment_method, name: "Payment C", distributors: [distributor]) -# end - -# let!(:order_a) { prepare_order(payment_method: payment_method_a) } -# let!(:order_b) { prepare_order(payment_method: payment_method_b) } -# let!(:order_c) { prepare_order(payment_method: payment_method_c) } - -# let(:parameters_attributes) do -# { payment_method_ids: [payment_method_a.id, payment_method_b.id] } -# end - -# it "filters entries" do -# totals = service.list - -# expect_total_matches(totals, 1, fee_name: "Payment A") -# expect_total_matches(totals, 1, fee_name: "Payment B") -# expect_total_matches(totals, 0, fee_name: "Payment C") -# end -# end -# end - -# # Helper methods for example group - -# def i18n_translate(translation_key, options = {}) -# I18n.t("order_management.reports.enterprise_fee_summary.#{translation_key}", **options) -# end - -# def expect_total_attributes(total, expected_attribute_list) -# actual_attribute_list = [total.fee_type, total.enterprise_name, total.fee_name, -# total.customer_name, total.fee_placement, -# total.fee_calculated_on_transfer_through_name, total.tax_category_name, -# total.total_amount] -# expect(actual_attribute_list).to eq(expected_attribute_list) -# end - -# def expect_total_matches(totals, count, attributes) -# expect(count_totals(totals, attributes)).to eq(count) -# end - -# def default_order_options -# { customer: customer, distributor: distributor, order_cycle: order_cycle, -# shipping_method: shipping_method, variant: variant } -# end - -# def prepare_incomplete_order(options = {}) -# target_options = default_order_options.merge(options) -# create(:order, :with_line_item, target_options) -# end - -# def prepare_order(options = {}) -# factory_trait_options = { payment_method: payment_method } -# target_options = default_order_options.merge(factory_trait_options).merge(options) -# create(:order, :with_line_item, :completed, target_options) -# end - -# def default_variant_options -# { product: product, producer: producer, is_master: false, coordinator: coordinator, -# distributor: distributor, order_cycle: order_cycle } -# end - -# def prepare_variant(options = {}) -# target_options = default_variant_options.merge(options) -# create(:variant, :with_order_cycle, target_options) -# end - -# def count_totals(totals, attributes) -# totals.count do |data| -# attributes.all? do |attribute_name, attribute_value| -# data.public_send(attribute_name) == attribute_value -# end -# end -# end -# end -# rubocop:enable Layout/LineLength +describe Reporting::Reports::EnterpriseFeeSummary::Base do + let(:report_module) { Reporting::Reports::EnterpriseFeeSummary } + + # Basic data. + let!(:shipping_method) do + create(:shipping_method, :per_item, amount: 1, name: "Sample Shipping Method") + end + + let!(:payment_method) do + create(:payment_method, :per_item, amount: 2, name: "Sample Payment Method") + end + + # Create enterprises. + let!(:distributor) do + create(:distributor_enterprise, name: "Sample Distributor").tap do |enterprise| + payment_method.distributors << enterprise + shipping_method.distributors << enterprise + end + end + let!(:producer) { create(:supplier_enterprise, name: "Sample Producer") } + let!(:coordinator) { create(:enterprise, name: "Sample Coordinator") } + + # Add some fee noise. + let!(:other_distributor_fee) { create(:enterprise_fee, :per_item, enterprise: distributor) } + let!(:other_producer_fee) { create(:enterprise_fee, :per_item, enterprise: producer) } + let!(:other_coordinator_fee) { create(:enterprise_fee, :per_item, enterprise: coordinator) } + + # Set up other requirements for ordering. + let!(:order_cycle) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:product) { create(:product, tax_category: product_tax_category) } + let!(:product_tax_category) { create(:tax_category, name: "Sample Product Tax") } + let!(:variant) { prepare_variant } + + # Create customers. + let!(:customer) { create(:customer, first_name: "Sample", last_name: "Customer") } + let!(:another_customer) { create(:customer, first_name: "Another", last_name: "Customer") } + + # Setup up permissions and report. + let!(:current_user) { create(:admin_user) } + let(:parameters) { report_module::Parameters.new } + let(:subject) { + report = described_class.new(current_user) + allow(report).to receive(:parameters).and_return(parameters) + report + } + + describe "grouping and sorting of entries" do + let!(:order_cycle) do + create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: order_cycle_fees) + end + + let!(:variant) do + prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, + outgoing_exchange_fees: variant_outgoing_exchange_fees) + end + + let!(:order_cycle_fees) do + [ + create(:enterprise_fee, :per_item, name: "Coordinator Fee 1", enterprise: coordinator, + fee_type: "admin", amount: 512.0, + tax_category: coordinator_tax_category), + create(:enterprise_fee, :per_item, name: "Coordinator Fee 2", enterprise: coordinator, + fee_type: "sales", amount: 1024.0, + inherits_tax_category: true) + ] + end + let!(:coordinator_tax_category) { create(:tax_category, name: "Sample Coordinator Tax") } + + let!(:variant_incoming_exchange_fees) do + [ + create(:enterprise_fee, :per_item, name: "Producer Fee 1", enterprise: producer, + fee_type: "sales", amount: 64.0, + tax_category: producer_tax_category), + create(:enterprise_fee, :per_item, name: "Producer Fee 2", enterprise: producer, + fee_type: "sales", amount: 128.0, + inherits_tax_category: true) + ] + end + let!(:producer_tax_category) { create(:tax_category, name: "Sample Producer Tax") } + + let!(:variant_outgoing_exchange_fees) do + [ + create(:enterprise_fee, :per_item, name: "Distributor Fee 1", enterprise: distributor, + fee_type: "admin", amount: 4.0, + tax_category: distributor_tax_category), + create(:enterprise_fee, :per_item, name: "Distributor Fee 2", enterprise: distributor, + fee_type: "sales", amount: 8.0, + inherits_tax_category: true) + ] + end + let!(:distributor_tax_category) { create(:tax_category, name: "Sample Distributor Tax") } + + let!(:customer_order) { prepare_order(customer: customer) } + let!(:customer_incomplete_order) { prepare_incomplete_order(customer: customer) } + let!(:second_customer_order) { prepare_order(customer: customer) } + let!(:other_customer_order) { prepare_order(customer: another_customer) } + + it "groups and sorts entries correctly" do + totals = subject.query_result + + expect(totals.length).to eq(16) + + # Data is sorted by the following, in order: + # * fee_type + # * enterprise_name + # * fee_name + # * customer_name + # * fee_placement + # * fee_calculated_on_transfer_through_name + # * tax_category_name + # * total_amount + + expected_result = [ + ["Admin", "Sample Coordinator", "Coordinator Fee 1", "Another Customer", + "Coordinator", "All", "Sample Coordinator Tax", "512.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee 1", "Sample Customer", + "Coordinator", "All", "Sample Coordinator Tax", "1024.00"], + ["Admin", "Sample Distributor", "Distributor Fee 1", "Another Customer", + "Outgoing", "Sample Distributor", "Sample Distributor Tax", "4.00"], + ["Admin", "Sample Distributor", "Distributor Fee 1", "Sample Customer", + "Outgoing", "Sample Distributor", "Sample Distributor Tax", "8.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Another Customer", + nil, nil, nil, "2.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "4.00"], + ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Another Customer", + "Coordinator", "All", "Various", "1024.00"], + ["Sales", "Sample Coordinator", "Coordinator Fee 2", "Sample Customer", + "Coordinator", "All", "Various", "2048.00"], + ["Sales", "Sample Distributor", "Distributor Fee 2", "Another Customer", + "Outgoing", "Sample Distributor", "Sample Product Tax", "8.00"], + ["Sales", "Sample Distributor", "Distributor Fee 2", "Sample Customer", + "Outgoing", "Sample Distributor", "Sample Product Tax", "16.00"], + ["Sales", "Sample Producer", "Producer Fee 1", "Another Customer", + "Incoming", "Sample Producer", "Sample Producer Tax", "64.00"], + ["Sales", "Sample Producer", "Producer Fee 1", "Sample Customer", + "Incoming", "Sample Producer", "Sample Producer Tax", "128.00"], + ["Sales", "Sample Producer", "Producer Fee 2", "Another Customer", + "Incoming", "Sample Producer", "Sample Product Tax", "128.00"], + ["Sales", "Sample Producer", "Producer Fee 2", "Sample Customer", + "Incoming", "Sample Producer", "Sample Product Tax", "256.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Another Customer", + nil, nil, "Platform Rate", "1.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "2.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + + describe "data exclusions" do + describe "invalid adjustments (through 'eligible') like failed payments" do + let!(:customer_order) { prepare_order(customer: customer) } + + before do + # Make the payment fail. See Spree::Payment#revoke_adjustment_eligibility. + payment = customer_order.payments.first + payment.state = "failed" + payment.save! + end + + it "is included" do + totals = subject.query_result + + expect(totals.length).to eq(1) + + expected_result = [ + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + + describe "non-mandatory $0 adjustments (through 'eligible')" do + let!(:variant) { prepare_variant(outgoing_exchange_fees: [enterprise_fee]) } + + let!(:enterprise_fee) do + create(:enterprise_fee, :per_item, name: "Sample Enterprise Fee", enterprise: distributor, + fee_type: "admin", amount: 0) + end + + let!(:customer_order) { prepare_order(customer: customer) } + + before do + # Change "eligible" in enterprise fee adjustment to false. $0 adjustments that are not + # mandatory are set to be ineligible, but there are no non-mandatory adjustments supported + # by the report yet. + adjustment = Spree::Adjustment.where(originator_type: "EnterpriseFee").first + adjustment.eligible = false + adjustment.save! + end + + it "is included" do + totals = subject.query_result + + expect(totals.length).to eq(2) + + expected_result = [ + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + + describe "$0 mandatory adjustments" do + let!(:payment_method) do + create(:payment_method, :per_item, amount: 0, name: "Sample Payment Method") + end + + let!(:customer_order) { prepare_order(customer: customer) } + + it "is included" do + totals = subject.query_result + + expect(totals.length).to eq(1) + + expected_result = [ + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + end + + describe "handling of more complex cases" do + context "with non-sender fee for incoming exchange and non-receiver fee for outgoing" do + let!(:variant) do + prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, + outgoing_exchange_fees: variant_outgoing_exchange_fees) + end + let!(:variant_incoming_exchange_fees) { [coordinator_fee, distributor_fee] } + let!(:variant_outgoing_exchange_fees) { [producer_fee, coordinator_fee] } + + let!(:producer_fee) do + tax_category = create(:tax_category, name: "Sample Producer Tax") + create(:enterprise_fee, :per_item, name: "Sample Producer Fee", enterprise: producer, + fee_type: "sales", amount: 64.0, + tax_category: tax_category) + end + let!(:coordinator_fee) do + tax_category = create(:tax_category, name: "Sample Coordinator Tax") + create(:enterprise_fee, :per_item, name: "Sample Coordinator Fee", enterprise: coordinator, + fee_type: "admin", amount: 512.0, + tax_category: tax_category) + end + let!(:distributor_fee) do + tax_category = create(:tax_category, name: "Sample Distributor Tax") + create(:enterprise_fee, :per_item, name: "Sample Distributor Fee", enterprise: distributor, + fee_type: "admin", amount: 4.0, + tax_category: tax_category) + end + + let!(:customer_order) { prepare_order(customer: customer) } + + it "fetches data correctly" do + totals = subject.query_result + + expect(totals.length).to eq(6) + + expected_result = [ + ["Admin", "Sample Coordinator", "Sample Coordinator Fee", "Sample Customer", + "Incoming", "Sample Producer", "Sample Coordinator Tax", "512.00"], + ["Admin", "Sample Coordinator", "Sample Coordinator Fee", "Sample Customer", + "Outgoing", "Sample Distributor", "Sample Coordinator Tax", "512.00"], + ["Admin", "Sample Distributor", "Sample Distributor Fee", "Sample Customer", + "Incoming", "Sample Producer", "Sample Distributor Tax", "4.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Sales", "Sample Producer", "Sample Producer Fee", "Sample Customer", + "Outgoing", "Sample Distributor", "Sample Producer Tax", "64.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + + context "with order-based enterprise fee calculator" do + let!(:producer_fee) do + tax_category = create(:tax_category, name: "Producer Tax A") + create(:enterprise_fee, :flat_rate, name: "Producer Fee A", enterprise: producer, + fee_type: "sales", tax_category: tax_category, + amount: 10) + end + let!(:coordinator_fee) do + tax_category = create(:tax_category, name: "Coordinator Tax A") + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee A", enterprise: coordinator, + fee_type: "admin", tax_category: tax_category, + amount: 15) + end + let!(:coordinator_fee_inheriting_product_tax_category) do + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee B", enterprise: coordinator, + fee_type: "admin", inherits_tax_category: true, + amount: 20) + end + let!(:coordinator_fee_without_tax) do + create(:enterprise_fee, :flat_rate, name: "Coordinator Fee C", enterprise: coordinator, + fee_type: "admin", inherits_tax_category: false, + amount: 25) + end + let!(:distributor_fee) do + create(:enterprise_fee, :flat_rate, name: "Distributor Fee A", enterprise: distributor, + fee_type: "admin", inherits_tax_category: false, + amount: 30) + end + + let!(:coordinator_fees) do + [ + coordinator_fee, + coordinator_fee_inheriting_product_tax_category, + coordinator_fee_without_tax + ] + end + + let!(:order_cycle) do + create(:simple_order_cycle, coordinator: coordinator, coordinator_fees: coordinator_fees) + end + + let!(:variant_incoming_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } + let!(:variant_outgoing_exchange_fees) { [producer_fee, coordinator_fee, distributor_fee] } + + let!(:variant) do + prepare_variant(incoming_exchange_fees: variant_incoming_exchange_fees, + outgoing_exchange_fees: variant_outgoing_exchange_fees) + end + + let!(:customer_order) { prepare_order(customer: customer) } + + it "fetches data correctly" do + totals = subject.query_result + + expect(totals.length).to eq(11) + + entire_orders_text = i18n_translate("fee_calculated_on_transfer_through_entire_orders", + distributor: "Sample Distributor") + various_tax_categories_text = i18n_translate("tax_category_various") + + expected_result = [ + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Coordinator", "All", "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Incoming", entire_orders_text, "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee A", "Sample Customer", + "Outgoing", entire_orders_text, "Coordinator Tax A", "15.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee B", "Sample Customer", + "Coordinator", "All", various_tax_categories_text, "20.00"], + ["Admin", "Sample Coordinator", "Coordinator Fee C", "Sample Customer", + "Coordinator", "All", nil, "25.00"], + ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", + "Incoming", entire_orders_text, various_tax_categories_text, "30.00"], + ["Admin", "Sample Distributor", "Distributor Fee A", "Sample Customer", + "Outgoing", entire_orders_text, various_tax_categories_text, "30.00"], + ["Payment Transaction", "Sample Distributor", "Sample Payment Method", "Sample Customer", + nil, nil, nil, "2.00"], + ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", + "Incoming", entire_orders_text, "Producer Tax A", "10.00"], + ["Sales", "Sample Producer", "Producer Fee A", "Sample Customer", + "Outgoing", entire_orders_text, "Producer Tax A", "10.00"], + ["Shipment", "Sample Distributor", "Sample Shipping Method", "Sample Customer", + nil, nil, "Platform Rate", "1.00"] + ] + + expected_result.each_with_index do |expected_attributes, row_index| + expect_total_attributes(totals[row_index], expected_attributes) + end + end + end + end + + describe "filtering results based on permissions" do + let!(:distributor_a) do + create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + let!(:distributor_b) do + create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + + let!(:order_cycle_a) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order_cycle_b) { create(:simple_order_cycle, coordinator: coordinator) } + + let!(:variant_a) { prepare_variant(distributor: distributor_a, order_cycle: order_cycle_a) } + let!(:variant_b) { prepare_variant(distributor: distributor_b, order_cycle: order_cycle_b) } + + let!(:order_a) { prepare_order(order_cycle: order_cycle_a, distributor: distributor_a) } + let!(:order_b) { prepare_order(order_cycle: order_cycle_b, distributor: distributor_b) } + + context "when admin" do + let!(:current_user) { create(:admin_user) } + + it "includes all order cycles" do + totals = subject.query_result + + expect_total_matches(totals, 2, fee_type: "Shipment") + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") + end + end + + context "when enterprise owner for distributor" do + let!(:current_user) { distributor_a.owner } + + it "does not include unrelated order cycles" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_type: "Shipment") + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") + end + end + end + + describe "filters entries correctly" do + let(:parameters) { report_module::Parameters.new(parameters_attributes) } + + context "filtering by completion date" do + let(:timestamp) { Time.zone.local(2018, 1, 5, 14, 30, 5) } + + let!(:customer_a) { create(:customer, first_name: "Customer", last_name: "A") } + let!(:customer_b) { create(:customer, first_name: "Customer", last_name: "B") } + let!(:customer_c) { create(:customer, first_name: "Customer", last_name: "C") } + + let!(:order_placed_before_timestamp) do + prepare_order(customer: customer_a).tap do |order| + order.update_column(:completed_at, timestamp - 1.second) + end + end + + let!(:order_placed_during_timestamp) do + prepare_order(customer: customer_b).tap do |order| + order.update_column(:completed_at, timestamp) + end + end + + let!(:order_placed_after_timestamp) do + prepare_order(customer: customer_c).tap do |order| + order.update_column(:completed_at, timestamp + 1.second) + end + end + + context "on or after start_at" do + let(:parameters_attributes) { { start_at: timestamp } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 0, fee_type: "Shipment", customer_name: "Customer A") + expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer B") + expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer C") + end + end + + context "on or before end_at" do + let(:parameters_attributes) { { end_at: timestamp } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer A") + expect_total_matches(totals, 1, fee_type: "Shipment", customer_name: "Customer B") + expect_total_matches(totals, 0, fee_type: "Shipment", customer_name: "Customer C") + end + end + end + + describe "for specified shops" do + let!(:distributor_a) do + create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + let!(:distributor_b) do + create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + let!(:distributor_c) do + create(:distributor_enterprise, name: "Distributor C", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + + let!(:order_a) { prepare_order(distributor: distributor_a) } + let!(:order_b) { prepare_order(distributor: distributor_b) } + let!(:order_c) { prepare_order(distributor: distributor_c) } + + let(:parameters_attributes) { { distributor_ids: [distributor_a.id, distributor_b.id] } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") + expect_total_matches(totals, 0, fee_type: "Shipment", enterprise_name: "Distributor C") + end + end + + describe "for specified suppliers" do + let!(:producer_a) { create(:supplier_enterprise, name: "Producer A") } + let!(:producer_b) { create(:supplier_enterprise, name: "Producer B") } + let!(:producer_c) { create(:supplier_enterprise, name: "Producer C") } + + let!(:fee_a) { create(:enterprise_fee, name: "Fee A", enterprise: producer_a, amount: 1) } + let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: producer_b, amount: 1) } + let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: producer_c, amount: 1) } + + let!(:product_a) { create(:product, supplier: producer_a) } + let!(:product_b) { create(:product, supplier: producer_b) } + let!(:product_c) { create(:product, supplier: producer_c) } + + let!(:variant_a) do + prepare_variant(product: product_a, producer: producer_a, incoming_exchange_fees: [fee_a]) + end + let!(:variant_b) do + prepare_variant(product: product_b, producer: producer_b, incoming_exchange_fees: [fee_b]) + end + let!(:variant_c) do + prepare_variant(product: product_c, producer: producer_c, incoming_exchange_fees: [fee_c]) + end + + let!(:order_a) { prepare_order(variant: variant_a) } + let!(:order_b) { prepare_order(variant: variant_b) } + let!(:order_c) { prepare_order(variant: variant_c) } + + let(:parameters_attributes) { { producer_ids: [producer_a.id, producer_b.id] } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_name: "Fee A", enterprise_name: "Producer A") + expect_total_matches(totals, 1, fee_name: "Fee B", enterprise_name: "Producer B") + expect_total_matches(totals, 0, fee_name: "Fee C", enterprise_name: "Producer C") + end + end + + describe "for specified order cycles" do + let!(:distributor_a) do + create(:distributor_enterprise, name: "Distributor A", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + let!(:distributor_b) do + create(:distributor_enterprise, name: "Distributor B", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + let!(:distributor_c) do + create(:distributor_enterprise, name: "Distributor C", payment_methods: [payment_method], + shipping_methods: [shipping_method]) + end + + let!(:order_cycle_a) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order_cycle_b) { create(:simple_order_cycle, coordinator: coordinator) } + let!(:order_cycle_c) { create(:simple_order_cycle, coordinator: coordinator) } + + let!(:variant_a) { prepare_variant(distributor: distributor_a, order_cycle: order_cycle_a) } + let!(:variant_b) { prepare_variant(distributor: distributor_b, order_cycle: order_cycle_b) } + let!(:variant_c) { prepare_variant(distributor: distributor_c, order_cycle: order_cycle_c) } + + let!(:order_a) { prepare_order(order_cycle: order_cycle_a, distributor: distributor_a) } + let!(:order_b) { prepare_order(order_cycle: order_cycle_b, distributor: distributor_b) } + let!(:order_c) { prepare_order(order_cycle: order_cycle_c, distributor: distributor_c) } + + let(:parameters_attributes) { { order_cycle_ids: [order_cycle_a.id, order_cycle_b.id] } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor A") + expect_total_matches(totals, 1, fee_type: "Shipment", enterprise_name: "Distributor B") + expect_total_matches(totals, 0, fee_type: "Shipment", enterprise_name: "Distributor C") + end + end + + describe "for specified enterprise fees" do + let!(:fee_a) { create(:enterprise_fee, name: "Fee A", enterprise: distributor, amount: 1) } + let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: distributor, amount: 1) } + let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: distributor, amount: 1) } + + let!(:variant) { prepare_variant(outgoing_exchange_fees: variant_outgoing_exchange_fees) } + let!(:variant_outgoing_exchange_fees) { [fee_a, fee_b, fee_c] } + + let!(:order) { prepare_order(variant: variant) } + + let(:parameters_attributes) { { enterprise_fee_ids: [fee_a.id, fee_b.id] } } + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_name: "Fee A") + expect_total_matches(totals, 1, fee_name: "Fee B") + expect_total_matches(totals, 0, fee_name: "Fee C") + end + end + + describe "for specified shipping methods" do + let!(:shipping_method_a) do + method = create(:shipping_method, name: "Shipping A", distributors: [distributor]) + method.calculator.update_attribute(:preferred_amount, 1) + method + end + let!(:shipping_method_b) do + method = create(:shipping_method, name: "Shipping B", distributors: [distributor]) + method.calculator.update_attribute(:preferred_amount, 1) + method + end + let!(:shipping_method_c) do + create(:shipping_method, name: "Shipping C", distributors: [distributor]) + end + + let!(:order_a) { prepare_order(shipping_method: shipping_method_a) } + let!(:order_b) { prepare_order(shipping_method: shipping_method_b) } + let!(:order_c) { prepare_order(shipping_method: shipping_method_c) } + + let(:parameters_attributes) do + { shipping_method_ids: [shipping_method_a.id, shipping_method_b.id] } + end + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_name: "Shipping A") + expect_total_matches(totals, 1, fee_name: "Shipping B") + expect_total_matches(totals, 0, fee_name: "Shipping C") + end + end + + describe "for specified payment methods" do + let!(:payment_method_a) do + method = create(:payment_method, name: "Payment A", distributors: [distributor]) + method.calculator.update_attribute(:preferred_amount, 1) + method + end + let!(:payment_method_b) do + method = create(:payment_method, name: "Payment B", distributors: [distributor]) + method.calculator.update_attribute(:preferred_amount, 1) + method + end + let!(:payment_method_c) do + create(:payment_method, name: "Payment C", distributors: [distributor]) + end + + let!(:order_a) { prepare_order(payment_method: payment_method_a) } + let!(:order_b) { prepare_order(payment_method: payment_method_b) } + let!(:order_c) { prepare_order(payment_method: payment_method_c) } + + let(:parameters_attributes) do + { payment_method_ids: [payment_method_a.id, payment_method_b.id] } + end + + it "filters entries" do + totals = subject.query_result + + expect_total_matches(totals, 1, fee_name: "Payment A") + expect_total_matches(totals, 1, fee_name: "Payment B") + expect_total_matches(totals, 0, fee_name: "Payment C") + end + end + end + + # Helper methods for example group + + def i18n_translate(translation_key, options = {}) + I18n.t("order_management.reports.enterprise_fee_summary.#{translation_key}", **options) + end + + def expect_total_attributes(total, expected_attribute_list) + actual_attribute_list = [total.fee_type, total.enterprise_name, total.fee_name, + total.customer_name, total.fee_placement, + total.fee_calculated_on_transfer_through_name, total.tax_category_name, + total.total_amount] + expect(actual_attribute_list).to eq(expected_attribute_list) + end + + def expect_total_matches(totals, count, attributes) + expect(count_totals(totals, attributes)).to eq(count) + end + + def default_order_options + { customer: customer, distributor: distributor, order_cycle: order_cycle, + shipping_method: shipping_method, variant: variant } + end + + def prepare_incomplete_order(options = {}) + target_options = default_order_options.merge(options) + create(:order, :with_line_item, target_options) + end + + def prepare_order(options = {}) + factory_trait_options = { payment_method: payment_method } + target_options = default_order_options.merge(factory_trait_options).merge(options) + create(:order, :with_line_item, :completed, target_options) + end + + def default_variant_options + { product: product, producer: producer, is_master: false, coordinator: coordinator, + distributor: distributor, order_cycle: order_cycle } + end + + def prepare_variant(options = {}) + target_options = default_variant_options.merge(options) + create(:variant, :with_order_cycle, target_options) + end + + def count_totals(totals, attributes) + totals.count do |data| + attributes.all? do |attribute_name, attribute_value| + data.public_send(attribute_name) == attribute_value + end + end + end +end diff --git a/spec/system/admin/reports/enterprise_fee_summaries_spec.rb b/spec/system/admin/reports/enterprise_fee_summaries_spec.rb new file mode 100644 index 0000000000..51bf9b41f2 --- /dev/null +++ b/spec/system/admin/reports/enterprise_fee_summaries_spec.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +require "spec_helper" + +feature "enterprise fee summaries", js: true do + include AuthenticationHelper + include WebHelper + + let!(:distributor) { create(:distributor_enterprise) } + let!(:other_distributor) { create(:distributor_enterprise) } + + let!(:order_cycle) { create(:simple_order_cycle, coordinator: distributor) } + let!(:other_order_cycle) { create(:simple_order_cycle, coordinator: other_distributor) } + + before do + login_as current_user + end + + describe "navigation" do + context "when accessing the report as an superadmin" do + let(:current_user) { create(:admin_user) } + + it "shows link and allows access to the report" do + visit spree.admin_reports_path + click_on I18n.t("admin.reports.enterprise_fee_summary.name") + expect(page).to have_button("Go") + end + end + + context "when accessing the report as an admin" do + let(:current_user) { distributor.owner } + + it "shows link and allows access to the report" do + visit spree.admin_reports_path + click_on I18n.t("admin.reports.enterprise_fee_summary.name") + expect(page).to have_button("Go") + end + end + + context "when accessing the report as an enterprise user without sufficient permissions" do + let(:current_user) { create(:user) } + + it "does not allow access to the report" do + visit spree.admin_reports_path + expect(page).to have_no_link(I18n.t("admin.reports.enterprise_fee_summary.name")) + visit main_app.admin_report_path(report_type: 'enterprise_fee_summary') + expect(page).to have_content(I18n.t("unauthorized")) + end + end + end + + describe "smoke test for filters" do + before do + visit main_app.admin_report_path(report_type: 'enterprise_fee_summary') + end + + context "when logged in as admin" do + let(:current_user) { create(:admin_user) } + + it "shows all available options" do + expect(page).to have_select "q_order_cycle_ids", with_options: [order_cycle.name] + end + end + + context "when logged in as enterprise user" do + let!(:order) do + create(:completed_order_with_fees, order_cycle: order_cycle, + distributor: distributor) + end + let(:current_user) { distributor.owner } + + it "shows available options for the enterprise" do + expect(page).to have_select "q_order_cycle_ids", options: [order_cycle.name] + end + end + end + + describe "csv downloads" do + around do |example| + with_empty_downloads_folder { example.run } + end + + describe "smoke test for generation of report based on permissions" do + before do + visit main_app.admin_report_path(report_type: 'enterprise_fee_summary') + end + + context "when logged in as admin" do + let!(:order) do + create(:completed_order_with_fees, order_cycle: order_cycle, + distributor: distributor) + end + let(:current_user) { create(:admin_user) } + + it "generates file with data for all enterprises" do + check I18n.t("filters.report_format_csv", scope: i18n_scope) + click_on "Go" + + expect(downloaded_filename).to include ".csv" + expect(downloaded_content).to have_content(distributor.name) + end + end + + context "when logged in as enterprise user" do + let!(:order) do + create(:completed_order_with_fees, order_cycle: order_cycle, + distributor: distributor) + end + let!(:other_order) do + create(:completed_order_with_fees, order_cycle: other_order_cycle, + distributor: other_distributor) + end + let(:current_user) { distributor.owner } + + it "generates file with data for the enterprise" do + check I18n.t("filters.report_format_csv", scope: i18n_scope) + click_on "Go" + + expect(downloaded_filename).to include ".csv" + csv_content = downloaded_content + expect(csv_content).to have_content(distributor.name) + expect(csv_content).not_to have_content(other_distributor.name) + end + end + end + + describe "smoke test for filtering report based on filters" do + let!(:second_distributor) { create(:distributor_enterprise) } + let!(:second_order_cycle) { create(:simple_order_cycle, coordinator: second_distributor) } + + let!(:order) do + create(:completed_order_with_fees, order_cycle: order_cycle, + distributor: distributor) + end + let!(:second_order) do + create(:completed_order_with_fees, order_cycle: second_order_cycle, + distributor: second_distributor) + end + + let(:current_user) { create(:admin_user) } + + before do + visit main_app.admin_report_path(report_type: 'enterprise_fee_summary') + end + + it "generates file with data for selected order cycle" do + select order_cycle.name, from: "report_order_cycle_ids" + check I18n.t("filters.report_format_csv", scope: i18n_scope) + click_on "Go" + + expect(downloaded_filename).to include ".csv" + csv_content = downloaded_content + expect(csv_content).to have_content(distributor.name) + expect(csv_content).not_to have_content(second_distributor.name) + end + end + end + + def i18n_scope + "order_management.reports.enterprise_fee_summaries" + end +end