From 24ab2e7fb0a3be206996bb954a22439dadfff458 Mon Sep 17 00:00:00 2001 From: Kristina Lim Date: Sun, 14 Oct 2018 17:35:28 +0800 Subject: [PATCH] Add filtering to enterprise fee summary --- .../enterprise_fee_summary/report_service.rb | 2 +- .../reports/enterprise_fee_summary/scope.rb | 43 ++- .../report_service_spec.rb | 301 +++++++++++++++++- 3 files changed, 329 insertions(+), 17 deletions(-) diff --git a/lib/order_management/reports/enterprise_fee_summary/report_service.rb b/lib/order_management/reports/enterprise_fee_summary/report_service.rb index 43f193e804..3b57be7b6c 100644 --- a/lib/order_management/reports/enterprise_fee_summary/report_service.rb +++ b/lib/order_management/reports/enterprise_fee_summary/report_service.rb @@ -17,7 +17,7 @@ module OrderManagement end def enterprise_fees_by_customer - Scope.new.all + Scope.new.apply_filters(parameters).result end def enterprise_fee_type_totals diff --git a/lib/order_management/reports/enterprise_fee_summary/scope.rb b/lib/order_management/reports/enterprise_fee_summary/scope.rb index 533536b3b4..b218c1f677 100644 --- a/lib/order_management/reports/enterprise_fee_summary/scope.rb +++ b/lib/order_management/reports/enterprise_fee_summary/scope.rb @@ -2,11 +2,22 @@ module OrderManagement module Reports module EnterpriseFeeSummary class Scope + attr_accessor :parameters + def initialize setup_default_scope end - def all + def apply_filters(params) + filter_by_date(params) + filter_by_distribution(params) + filter_by_fee(params) + + self + end + + def result + group_data.select_attributes @scope.all end @@ -275,6 +286,30 @@ module OrderManagement ) end + def filter_by_date(params) + filter_scope("spree_orders.completed_at >= ?", params.start_at) \ + if params.start_at.present? + filter_scope("spree_orders.completed_at <= ?", params.end_at) if params.end_at.present? + end + + def filter_by_distribution(params) + filter_scope(spree_orders: { distributor_id: params.distributor_ids }) \ + if params.distributor_ids.present? + filter_scope(spree_products: { supplier_id: params.producer_ids }) \ + if params.producer_ids.present? + filter_scope(spree_orders: { order_cycle_id: params.order_cycle_ids }) \ + if params.order_cycle_ids.present? + end + + def filter_by_fee(params) + filter_scope(enterprise_fees: { id: params.enterprise_fee_ids }) \ + if params.enterprise_fee_ids.present? + filter_scope(spree_shipping_methods: { id: params.shipping_method_ids }) \ + if params.shipping_method_ids.present? + filter_scope(spree_payment_methods: { id: params.payment_method_ids }) \ + if params.payment_method_ids.present? + end + def group_data chain_to_scope do group("enterprise_fees.id", "enterprises.id", "customers.id", "hubs.id", @@ -314,6 +349,12 @@ module OrderManagement joins(join_string) end end + + def filter_scope(*args) + chain_to_scope do + where(*args) + end + end end end end diff --git a/spec/lib/order_management/reports/enterprise_fee_summary/report_service_spec.rb b/spec/lib/order_management/reports/enterprise_fee_summary/report_service_spec.rb index 14a7b49929..94effc1f11 100644 --- a/spec/lib/order_management/reports/enterprise_fee_summary/report_service_spec.rb +++ b/spec/lib/order_management/reports/enterprise_fee_summary/report_service_spec.rb @@ -4,6 +4,8 @@ require "order_management/reports/enterprise_fee_summary/report_service" require "order_management/reports/enterprise_fee_summary/parameters" describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do + let(:report_klass) { OrderManagement::Reports::EnterpriseFeeSummary } + let!(:shipping_method) do create(:shipping_method, name: "Sample Shipping Method", calculator: per_item_calculator(1.0)) end @@ -75,9 +77,9 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do let!(:another_customer) { create(:customer, name: "Another Customer") } describe "grouping and sorting of entries" do - let!(:customer_order) { prepare_completed_order(customer: customer) } - let!(:second_customer_order) { prepare_completed_order(customer: customer) } - let!(:other_customer_order) { prepare_completed_order(customer: another_customer) } + let!(:customer_order) { prepare_order(customer: customer) } + let!(:second_customer_order) { prepare_order(customer: customer) } + let!(:other_customer_order) { prepare_order(customer: another_customer) } let(:parameters) { OrderManagement::Reports::EnterpriseFeeSummary::Parameters.new } let(:service) { described_class.new(parameters, nil) } @@ -138,6 +140,241 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end + describe "filters entries correctly" do + let(:parameters) { report_klass::Parameters.new(parameters_attributes) } + let(:service) { described_class.new(parameters, nil) } + + context "filtering by completion date" do + let(:timestamp) { Time.zone.local(2018, 1, 5, 14, 30, 5) } + + let!(:customer_a) { create(:customer, name: "Customer A") } + let!(:customer_b) { create(:customer, name: "Customer B") } + let!(:customer_c) { create(:customer, name: "Customer 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.enterprise_fee_type_totals.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.enterprise_fee_type_totals.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.enterprise_fee_type_totals.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) } + let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: producer_b) } + let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: producer_c) } + + 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.enterprise_fee_type_totals.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.enterprise_fee_type_totals.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) } + let!(:fee_b) { create(:enterprise_fee, name: "Fee B", enterprise: distributor) } + let!(:fee_c) { create(:enterprise_fee, name: "Fee C", enterprise: distributor) } + + let!(:variant) { prepare_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.enterprise_fee_type_totals.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 + create(:shipping_method, name: "Shipping A", distributors: [distributor]) + end + let!(:shipping_method_b) do + create(:shipping_method, name: "Shipping B", distributors: [distributor]) + 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.enterprise_fee_type_totals.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 + create(:payment_method, name: "Payment A", distributors: [distributor]) + end + let!(:payment_method_b) do + create(:payment_method, name: "Payment B", distributors: [distributor]) + 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.enterprise_fee_type_totals.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 expect_total_attributes(total, expected_attribute_list) @@ -148,6 +385,10 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do 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 prepare_tax_category(name) create(:tax_category, name: name) end @@ -157,7 +398,7 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do shipping_method: shipping_method, variant: variant } end - def prepare_order(options = {}) + def setup_order(options = {}) target = default_order_options.merge(options) create(:order, customer: target[:customer], distributor: target[:distributor], @@ -168,8 +409,8 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do end end - def prepare_completed_order(options = {}) - order = prepare_order(options) + def prepare_order(options = {}) + order = setup_order(options) complete_order(order, options) order.reload end @@ -182,16 +423,38 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do while !order.completed? do break unless order.next! end end - def prepare_variant(options = {}) - variant = create(:variant, product: product, is_master: false) - exchange = create(:exchange, incoming: true, order_cycle: order_cycle, sender: producer, - receiver: coordinator, variants: [variant]) - attach_enterprise_fees(exchange, options[:incoming_exchange_fees] || []) + def default_variant_options + { product: product, producer: producer, coordinator: coordinator, distributor: distributor, + order_cycle: order_cycle } + end - exchange = create(:exchange, incoming: false, order_cycle: order_cycle, sender: coordinator, - receiver: distributor, variants: [variant]) - attach_enterprise_fees(exchange, options[:outgoing_exchange_fees] || []) - variant + def prepare_variant(options = {}) + target = default_variant_options.merge(options) + + create(:variant, product: target[:product], is_master: false).tap do |variant| + exchange_options = { producer: target[:producer], coordinator: target[:coordinator], + distributor: target[:distributor], + incoming_exchange_fees: target[:incoming_exchange_fees], + outgoing_exchange_fees: target[:outgoing_exchange_fees] } + setup_exchanges(target[:order_cycle], variant, exchange_options) + end + end + + def setup_exchanges(order_cycle, variant, options) + setup_exchange(order_cycle, variant, true, sender: options[:producer], + receiver: options[:coordinator], + enterprise_fees: options[:incoming_exchange_fees]) + setup_exchange(order_cycle, variant, false, sender: options[:coordinator], + receiver: options[:distributor], + enterprise_fees: options[:outgoing_exchange_fees]) + end + + def setup_exchange(order_cycle, variant, incoming, options) + exchange_attributes = { order_cycle_id: order_cycle.id, incoming: incoming, + sender_id: options[:sender].id, receiver_id: options[:receiver].id } + exchange = Exchange.where(exchange_attributes).first || create(:exchange, exchange_attributes) + exchange.variants << variant + attach_enterprise_fees(exchange, options[:enterprise_fees] || []) end def attach_enterprise_fees(exchange, enterprise_fees) @@ -203,4 +466,12 @@ describe OrderManagement::Reports::EnterpriseFeeSummary::ReportService do def per_item_calculator(amount) Spree::Calculator::PerItem.new(preferred_amount: amount) 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