diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 657102531e..3f81b750d4 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -1139,7 +1139,7 @@ Style/OptionalBooleanParameter: - 'lib/open_food_network/order_cycle_management_report.rb' - 'lib/open_food_network/orders_and_fulfillment_report.rb' - 'lib/open_food_network/payments_report.rb' - - 'lib/open_food_network/products_and_inventory_report_base.rb' + - 'lib/open_food_network/products_and_inventory_report.rb' - 'lib/open_food_network/users_and_enterprises_report.rb' - 'lib/open_food_network/xero_invoices_report.rb' - 'lib/spree/core/controller_helpers/order.rb' diff --git a/app/controllers/spree/admin/reports_controller.rb b/app/controllers/spree/admin/reports_controller.rb index 2ef24f68e2..7c9d415dce 100644 --- a/app/controllers/spree/admin/reports_controller.rb +++ b/app/controllers/spree/admin/reports_controller.rb @@ -86,21 +86,7 @@ module Spree end def products_and_inventory - @report_types = report_types[:products_and_inventory] - @report = if params[:report_type] != 'lettuce_share' - OpenFoodNetwork::ProductsAndInventoryReport.new spree_current_user, - raw_params, - render_content? - else - OpenFoodNetwork::LettuceShareReport.new spree_current_user, - raw_params, - render_content? - end - - render_report @report.header, - @report.table, - params[:csv], - "products_and_inventory_#{timestamp}.csv" + render_report2 end def users_and_enterprises diff --git a/app/views/spree/admin/reports/_products_and_inventory_description.html.haml b/app/views/spree/admin/reports/_products_and_inventory_description.html.haml index f662298e3e..f65d13273e 100644 --- a/app/views/spree/admin/reports/_products_and_inventory_description.html.haml +++ b/app/views/spree/admin/reports/_products_and_inventory_description.html.haml @@ -1,4 +1,4 @@ %ul{style: "margin-left: 12pt"} - report_types.each do |report_type| %li - = link_to report_type[0], "#{products_and_inventory_admin_reports_url}?report_type=#{report_type[1]}" + = link_to report_type[0], "#{products_and_inventory_admin_reports_url}?report_subtype=#{report_type[1]}" diff --git a/app/views/spree/admin/reports/filters/_products_and_inventory.html.haml b/app/views/spree/admin/reports/filters/_products_and_inventory.html.haml new file mode 100644 index 0000000000..dbb56d76b7 --- /dev/null +++ b/app/views/spree/admin/reports/filters/_products_and_inventory.html.haml @@ -0,0 +1,20 @@ +.row + .alpha.two.columns= label_tag nil, t(:report_distributor) + .omega.fourteen.columns + = select_tag(:distributor_id, + options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), + {:include_blank => true, :class => "select2 fullwidth light"}) + +.row + .alpha.two.columns= label_tag nil, t(:report_customers_supplier) + .omega.fourteen.columns + = select_tag(:supplier_id, + options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), + {:include_blank => true, :class => "select2 fullwidth light"}) + +.row + .alpha.two.columns= label_tag nil, t(:report_order_cycle) + .omega.fourteen.columns + = select_tag(:order_cycle_id, + options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), + {:include_blank => true, :class => "select2 fullwidth light"}) diff --git a/app/views/spree/admin/reports/products_and_inventory.html.haml b/app/views/spree/admin/reports/products_and_inventory.html.haml deleted file mode 100644 index 73d31a6990..0000000000 --- a/app/views/spree/admin/reports/products_and_inventory.html.haml +++ /dev/null @@ -1,34 +0,0 @@ -= form_tag spree.products_and_inventory_admin_reports_url do |f| - %br - .row - .four.columns.alpha - = label_tag nil, t(:report_distributor) - = select_tag(:distributor_id, - options_from_collection_for_select(@distributors, :id, :name, params[:distributor_id]), - {:include_blank => true, :class => "select2 fullwidth"}) - - - .four.columns - = label_tag nil, t(:report_customers_supplier) - = select_tag(:supplier_id, - options_from_collection_for_select(@suppliers, :id, :name, params[:supplier_id]), - {:include_blank => true, :class => "select2 fullwidth"}) - - - .six.columns - = label_tag nil, t(:report_order_cycle) - = select_tag(:order_cycle_id, - options_for_select(report_order_cycle_options(@order_cycles), params[:order_cycle_id]), - {:include_blank => true, :class => "select2 fullwidth"}) - - = label_tag nil, t(:report_type) - %br - = select_tag(:report_type, options_for_select(@report_types, params[:report_type])) - - %br - %br - = check_box_tag :csv - = label_tag :csv, t(:report_customers_csv) - %br - = button t(:go) -= render "table", id: "listing_products", msg_option: t(:go) diff --git a/lib/open_food_network/lettuce_share_report.rb b/lib/open_food_network/lettuce_share_report.rb index 25868c9006..a98c26e6f1 100644 --- a/lib/open_food_network/lettuce_share_report.rb +++ b/lib/open_food_network/lettuce_share_report.rb @@ -1,11 +1,17 @@ # frozen_string_literal: true - -require 'open_food_network/products_and_inventory_report_base' require 'variant_units/option_value_namer' module OpenFoodNetwork - class LettuceShareReport < ProductsAndInventoryReportBase - def header + class LettuceShareReport + attr_reader :context + + delegate :variants, :render_table, to: :context + + def initialize(context) + @context = context + end + + def table_headers # NOTE: These are NOT to be translated, they need to be in this exact format to work with LettucShare [ "PRODUCT", @@ -21,8 +27,8 @@ module OpenFoodNetwork ] end - def table - return [] unless @render_table + def table_rows + return [] unless render_table variants.select(&:in_stock?) .map do |variant| diff --git a/lib/open_food_network/products_and_inventory_default_report.rb b/lib/open_food_network/products_and_inventory_default_report.rb new file mode 100644 index 0000000000..2400586c7b --- /dev/null +++ b/lib/open_food_network/products_and_inventory_default_report.rb @@ -0,0 +1,53 @@ +# frozen_string_literal: true + +module OpenFoodNetwork + class ProductsAndInventoryDefaultReport + attr_reader :context + + delegate :variants, :render_table, to: :context + + def initialize(context) + @context = context + end + + def table_headers + [ + I18n.t(:report_header_supplier), + I18n.t(:report_header_producer_suburb), + I18n.t(:report_header_product), + I18n.t(:report_header_product_properties), + I18n.t(:report_header_taxons), + I18n.t(:report_header_variant_value), + I18n.t(:report_header_price), + I18n.t(:report_header_group_buy_unit_quantity), + I18n.t(:report_header_amount), + I18n.t(:report_header_sku) + ] + end + + def table_rows + return [] unless render_table + + variants.map do |variant| + [ + variant.product.supplier.name, + variant.product.supplier.address.city, + variant.product.name, + variant.product.properties.map(&:name).join(", "), + variant.product.taxons.map(&:name).join(", "), + variant.full_name, + variant.price, + variant.product.group_buy_unit_size, + "", + sku_for(variant) + ] + end + end + + def sku_for(variant) + return variant.sku if variant.sku.present? + + variant.product.sku + end + end +end diff --git a/lib/open_food_network/products_and_inventory_report.rb b/lib/open_food_network/products_and_inventory_report.rb index df40f3aa4c..ae211ec4d0 100644 --- a/lib/open_food_network/products_and_inventory_report.rb +++ b/lib/open_food_network/products_and_inventory_report.rb @@ -1,47 +1,100 @@ # frozen_string_literal: true -require 'open_food_network/products_and_inventory_report_base' +require 'open_food_network/scope_variant_to_hub' +require 'open_food_network/products_and_inventory_default_report' +require 'open_food_network/lettuce_share_report' module OpenFoodNetwork - class ProductsAndInventoryReport < ProductsAndInventoryReportBase - def header - [ - I18n.t(:report_header_supplier), - I18n.t(:report_header_producer_suburb), - I18n.t(:report_header_product), - I18n.t(:report_header_product_properties), - I18n.t(:report_header_taxons), - I18n.t(:report_header_variant_value), - I18n.t(:report_header_price), - I18n.t(:report_header_group_buy_unit_quantity), - I18n.t(:report_header_amount), - I18n.t(:report_header_sku) - ] + class ProductsAndInventoryReport + attr_reader :params, :render_table + + delegate :table_rows, :table_headers, :rules, :columns, :sku_for, to: :report + + def initialize(user, params = {}, render_table = false) + @user = user + @params = params + @render_table = render_table end - def table - return [] unless @render_table + def report + @report ||= report_klass.new(self) + end - variants.map do |variant| - [ - variant.product.supplier.name, - variant.product.supplier.address.city, - variant.product.name, - variant.product.properties.map(&:name).join(", "), - variant.product.taxons.map(&:name).join(", "), - variant.full_name, - variant.price, - variant.product.group_buy_unit_size, - "", - sku_for(variant) - ] + def report_type + params[:report_subtype] + end + + def report_klass + if report_type == 'lettuce_share' + OpenFoodNetwork::LettuceShareReport + else + OpenFoodNetwork::ProductsAndInventoryDefaultReport end end - def sku_for(variant) - return variant.sku if variant.sku.present? + def permissions + @permissions ||= OpenFoodNetwork::Permissions.new(@user) + end - variant.product.sku + def visible_products + @visible_products ||= permissions.visible_products + end + + def variants + filter(child_variants) + end + + def child_variants + Spree::Variant. + where(is_master: false). + includes(option_values: :option_type). + joins(:product). + merge(visible_products). + order('spree_products.name') + end + + def filter(variants) + filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants + end + + # Using the `in_stock?` method allows overrides by distributors. + def filter_on_hand(variants) + if report_type == 'inventory' + variants.select(&:in_stock?) + else + variants + end + end + + def filter_to_supplier(variants) + if params[:supplier_id].to_i > 0 + variants.where("spree_products.supplier_id = ?", params[:supplier_id]) + else + variants + end + end + + def filter_to_distributor(variants) + if params[:distributor_id].to_i > 0 + distributor = Enterprise.find params[:distributor_id] + scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) + variants.in_distributor(distributor).each { |v| scoper.scope(v) } + else + variants + end + end + + def filter_to_order_cycle(variants) + if params[:order_cycle_id].to_i > 0 + order_cycle = OrderCycle.find params[:order_cycle_id] + variant_ids = Exchange.in_order_cycle(order_cycle). + joins("INNER JOIN exchange_variants ON exchanges.id = exchange_variants.exchange_id"). + select("DISTINCT exchange_variants.variant_id") + + variants.where("spree_variants.id IN (#{variant_ids.to_sql})") + else + variants + end end end end diff --git a/lib/open_food_network/products_and_inventory_report_base.rb b/lib/open_food_network/products_and_inventory_report_base.rb deleted file mode 100644 index 22cd561f36..0000000000 --- a/lib/open_food_network/products_and_inventory_report_base.rb +++ /dev/null @@ -1,80 +0,0 @@ -# frozen_string_literal: true - -require 'open_food_network/scope_variant_to_hub' - -module OpenFoodNetwork - class ProductsAndInventoryReportBase - attr_reader :params - - def initialize(user, params = {}, render_table = false) - @user = user - @params = params - @render_table = render_table - end - - def permissions - @permissions ||= OpenFoodNetwork::Permissions.new(@user) - end - - def visible_products - @visible_products ||= permissions.visible_products - end - - def variants - filter(child_variants) - end - - def child_variants - Spree::Variant. - where(is_master: false). - includes(option_values: :option_type). - joins(:product). - merge(visible_products). - order('spree_products.name') - end - - def filter(variants) - filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants - end - - # Using the `in_stock?` method allows overrides by distributors. - def filter_on_hand(variants) - if params[:report_type] == 'inventory' - variants.select(&:in_stock?) - else - variants - end - end - - def filter_to_supplier(variants) - if params[:supplier_id].to_i > 0 - variants.where("spree_products.supplier_id = ?", params[:supplier_id]) - else - variants - end - end - - def filter_to_distributor(variants) - if params[:distributor_id].to_i > 0 - distributor = Enterprise.find params[:distributor_id] - scoper = OpenFoodNetwork::ScopeVariantToHub.new(distributor) - variants.in_distributor(distributor).each { |v| scoper.scope(v) } - else - variants - end - end - - def filter_to_order_cycle(variants) - if params[:order_cycle_id].to_i > 0 - order_cycle = OrderCycle.find params[:order_cycle_id] - variant_ids = Exchange.in_order_cycle(order_cycle). - joins("INNER JOIN exchange_variants ON exchanges.id = exchange_variants.exchange_id"). - select("DISTINCT exchange_variants.variant_id") - - variants.where("spree_variants.id IN (#{variant_ids.to_sql})") - else - variants - end - end - end -end diff --git a/spec/controllers/spree/admin/reports_controller_spec.rb b/spec/controllers/spree/admin/reports_controller_spec.rb index ccdf76047f..063ce9d770 100644 --- a/spec/controllers/spree/admin/reports_controller_spec.rb +++ b/spec/controllers/spree/admin/reports_controller_spec.rb @@ -233,7 +233,7 @@ describe Spree::Admin::ReportsController, type: :controller do it "assigns report types" do spree_get :products_and_inventory - expect(assigns(:report_types)).to eq(subject.report_types[:products_and_inventory]) + expect(assigns(:report_subtypes)).to eq(subject.report_types[:products_and_inventory]) end it "creates a ProductAndInventoryReport" do @@ -242,8 +242,8 @@ describe Spree::Admin::ReportsController, type: :controller do { "test" => "foo", "controller" => "spree/admin/reports", "report" => {}, "action" => "products_and_inventory", "use_route" => "main_app" }, false) .and_return(report = double(:report)) - allow(report).to receive(:header).and_return [] - allow(report).to receive(:table).and_return [] + allow(report).to receive(:table_headers).and_return [] + allow(report).to receive(:table_rows).and_return [] spree_get :products_and_inventory, test: "foo" expect(assigns(:report)).to eq(report) end diff --git a/spec/lib/open_food_network/lettuce_share_report_spec.rb b/spec/lib/open_food_network/lettuce_share_report_spec.rb index 7f32110299..a8e7da5b95 100644 --- a/spec/lib/open_food_network/lettuce_share_report_spec.rb +++ b/spec/lib/open_food_network/lettuce_share_report_spec.rb @@ -2,12 +2,15 @@ require 'spec_helper' -require 'open_food_network/lettuce_share_report' +require 'open_food_network/products_and_inventory_report' module OpenFoodNetwork describe LettuceShareReport do let(:user) { create(:user) } - let(:report) { LettuceShareReport.new user, {}, true } + let(:base_report) { + ProductsAndInventoryReport.new(user, { report_subtype: 'lettuce_share' }, true) + } + let(:report) { base_report.report } let(:variant) { create(:variant) } describe "grower and method" do @@ -34,7 +37,7 @@ module OpenFoodNetwork describe "table" do it "handles no items" do - expect(report.table).to eq [] + expect(report.table_rows).to eq [] end describe "lists" do @@ -51,18 +54,18 @@ module OpenFoodNetwork } it "all items" do - allow(report).to receive(:child_variants) { - Spree::Variant.where(id: [variant, variant2, variant3]) - } - expect(report.table.count).to eq 3 + allow(base_report).to receive(:child_variants) { + Spree::Variant.where(id: [variant, variant2, variant3]) + } + expect(report.table_rows.count).to eq 3 end it "only available items" do variant.on_hand = 0 - allow(report).to receive(:child_variants) { - Spree::Variant.where(id: [variant, variant2, variant3, variant4]) - } - expect(report.table.count).to eq 3 + allow(base_report).to receive(:child_variants) { + Spree::Variant.where(id: [variant, variant2, variant3, variant4]) + } + expect(report.table_rows.count).to eq 3 end it "only available items considering overrides" do @@ -71,11 +74,13 @@ module OpenFoodNetwork # create the overrides variant2_override variant3_override - allow(report).to receive(:child_variants) { - Spree::Variant.where(id: [variant, variant2, variant3]) - } - allow(report).to receive(:params) { { distributor_id: hub.id } } - rows = report.table + allow(base_report).to receive(:child_variants) { + Spree::Variant.where(id: [variant, variant2, variant3]) + } + allow(base_report).to receive(:params) { + { distributor_id: hub.id, report_subtype: 'lettuce_share' } + } + rows = report.table_rows expect(rows.count).to eq 2 expect(rows.map{ |row| row[0] }).to include variant.product.name, variant2.product.name end diff --git a/spec/lib/open_food_network/products_and_inventory_default_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_default_report_spec.rb new file mode 100644 index 0000000000..2de38c53cc --- /dev/null +++ b/spec/lib/open_food_network/products_and_inventory_default_report_spec.rb @@ -0,0 +1,261 @@ +# frozen_string_literal: true + +require 'spec_helper' +require 'open_food_network/products_and_inventory_report' + +describe OpenFoodNetwork::ProductsAndInventoryDefaultReport do + context "As a site admin" do + let(:user) do + user = create(:user) + user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin') + user + end + subject do + OpenFoodNetwork::ProductsAndInventoryReport.new user, {}, true + end + + it "Should return headers" do + expect(subject.table_headers).to eq([ + "Supplier", + "Producer Suburb", + "Product", + "Product Properties", + "Taxons", + "Variant Value", + "Price", + "Group Buy Unit Quantity", + "Amount", + "SKU" + ]) + end + + it "should build a table from a list of variants" do + variant = double(:variant, sku: "sku", + full_name: "Variant Name", + count_on_hand: 10, + price: 100) + allow(variant).to receive_message_chain(:product, :supplier, :name).and_return("Supplier") + allow(variant).to receive_message_chain(:product, :supplier, :address, + :city).and_return("A city") + allow(variant).to receive_message_chain(:product, :name).and_return("Product Name") + allow(variant).to receive_message_chain(:product, + :properties).and_return [double(name: "property1"), + double(name: "property2")] + allow(variant).to receive_message_chain(:product, + :taxons).and_return [double(name: "taxon1"), + double(name: "taxon2")] + allow(variant).to receive_message_chain(:product, :group_buy_unit_size).and_return(21) + allow(subject).to receive(:variants).and_return [variant] + + expect(subject.table_rows).to eq([[ + "Supplier", + "A city", + "Product Name", + "property1, property2", + "taxon1, taxon2", + "Variant Name", + 100, + 21, + "", + "sku" + ]]) + end + + it "fetches variants for some params" do + expect(subject).to receive(:child_variants).and_return ["children"] + expect(subject).to receive(:filter).with(['children']).and_return ["filter_children"] + expect(subject.variants).to eq(["filter_children"]) + end + end + + context "As an enterprise user" do + let(:supplier) { create(:supplier_enterprise) } + let(:enterprise_user) do + user = create(:user) + user.enterprise_roles.create(enterprise: supplier) + user.spree_roles = [] + user.save! + user + end + + subject do + OpenFoodNetwork::ProductsAndInventoryReport.new enterprise_user, {}, true + end + + describe "fetching child variants" do + it "returns some variants" do + product1 = create(:simple_product, supplier: supplier) + variant1 = product1.variants.first + variant2 = create(:variant, product: product1) + + expect(subject.child_variants).to match_array [variant1, variant2] + end + + it "should only return variants managed by the user" do + product1 = create(:simple_product, supplier: create(:supplier_enterprise)) + product2 = create(:simple_product, supplier: supplier) + variant1 = product1.variants.first + variant2 = product2.variants.first + + expect(subject.child_variants).to eq([variant2]) + end + end + + describe "Filtering variants" do + let(:variants) { Spree::Variant.where(nil).joins(:product).where(is_master: false) } + + describe "based on report type" do + it "returns only variants on hand" do + product1 = create(:simple_product, supplier: supplier, on_hand: 99) + product2 = create(:simple_product, supplier: supplier, on_hand: 0) + + allow(subject).to receive(:params).and_return(report_subtype: 'inventory') + expect(subject.filter(variants)).to eq([product1.variants.first]) + end + end + it "filters to a specific supplier" do + supplier2 = create(:supplier_enterprise) + product1 = create(:simple_product, supplier: supplier) + product2 = create(:simple_product, supplier: supplier2) + + allow(subject).to receive(:params).and_return(supplier_id: supplier.id) + expect(subject.filter(variants)).to eq([product1.variants.first]) + end + it "filters to a specific distributor" do + distributor = create(:distributor_enterprise) + product1 = create(:simple_product, supplier: supplier) + product2 = create(:simple_product, supplier: supplier) + order_cycle = create(:simple_order_cycle, suppliers: [supplier], + distributors: [distributor], + variants: [product2.variants.first]) + + allow(subject).to receive(:params).and_return(distributor_id: distributor.id) + expect(subject.filter(variants)).to eq([product2.variants.first]) + end + + it "ignores variant overrides without filter" do + distributor = create(:distributor_enterprise) + product = create(:simple_product, supplier: supplier, price: 5) + variant = product.variants.first + order_cycle = create(:simple_order_cycle, suppliers: [supplier], + distributors: [distributor], + variants: [product.variants.first]) + create(:variant_override, hub: distributor, variant: variant, price: 2) + + result = subject.filter(variants) + + expect(result.first.price).to eq 5 + end + + it "considers variant overrides with distributor" do + distributor = create(:distributor_enterprise) + product = create(:simple_product, supplier: supplier, price: 5) + variant = product.variants.first + order_cycle = create(:simple_order_cycle, suppliers: [supplier], + distributors: [distributor], + variants: [product.variants.first]) + create(:variant_override, hub: distributor, variant: variant, price: 2) + + allow(subject).to receive(:params).and_return(distributor_id: distributor.id) + result = subject.filter(variants) + + expect(result.first.price).to eq 2 + end + + it "filters to a specific order cycle" do + distributor = create(:distributor_enterprise) + product1 = create(:simple_product, supplier: supplier) + product2 = create(:simple_product, supplier: supplier) + order_cycle = create(:simple_order_cycle, suppliers: [supplier], + distributors: [distributor], + variants: [product1.variants.first]) + + allow(subject).to receive(:params).and_return(order_cycle_id: order_cycle.id) + expect(subject.filter(variants)).to eq([product1.variants.first]) + end + + it "should do all the filters at once" do + # The following data ensures that this spec fails if any of the + # filters fail. It's testing the filters are not impacting each other. + distributor = create(:distributor_enterprise) + other_distributor = create(:distributor_enterprise) + other_supplier = create(:supplier_enterprise) + not_filtered_variant = create(:simple_product, supplier: supplier).variants.first + variant_filtered_by_order_cycle = create(:simple_product, + supplier: supplier).variants.first + variant_filtered_by_distributor = create(:simple_product, + supplier: supplier).variants.first + variant_filtered_by_supplier = create(:simple_product, + supplier: other_supplier).variants.first + variant_filtered_by_stock = create(:simple_product, supplier: supplier, + on_hand: 0).variants.first + + # This OC contains all products except the one that should be filtered + # by order cycle. We create a separate OC further down to proof that + # the product is passing all other filters. + order_cycle = create( + :simple_order_cycle, + suppliers: [supplier, other_supplier], + distributors: [distributor, other_distributor], + variants: [ + not_filtered_variant, + variant_filtered_by_distributor, + variant_filtered_by_supplier, + variant_filtered_by_stock + ] + ) + + # Remove the distribution of one product for one distributor but still + # sell it through the other distributor. + order_cycle.exchanges.outgoing.find_by(receiver_id: distributor.id). + exchange_variants. + find_by(variant_id: variant_filtered_by_distributor). + destroy + + # Make product available to be filtered later. See OC comment above. + create( + :simple_order_cycle, + suppliers: [supplier], + distributors: [distributor, other_distributor], + variants: [ + variant_filtered_by_order_cycle + ] + ) + + allow(subject).to receive(:params).and_return( + order_cycle_id: order_cycle.id, + supplier_id: supplier.id, + distributor_id: distributor.id, + report_subtype: 'inventory' + ) + + expect(subject.filter(variants)).to match_array [not_filtered_variant] + + # And it integrates with the ordering of the `variants` method. + expect(subject.variants).to match_array [not_filtered_variant] + end + end + + describe "fetching SKU for a variant" do + let(:variant) { create(:variant) } + let(:product) { variant.product } + + before { product.update_attribute(:sku, "Product SKU") } + + context "when the variant has an SKU set" do + before { variant.update_attribute(:sku, "Variant SKU") } + it "returns it" do + expect(subject.__send__(:sku_for, variant)).to eq "Variant SKU" + end + end + + context "when the variant has bo SKU set" do + before { variant.update_attribute(:sku, "") } + + it "returns the product's SKU" do + expect(subject.__send__(:sku_for, variant)).to eq "Product SKU" + end + end + end + end +end diff --git a/spec/lib/open_food_network/products_and_inventory_report_spec.rb b/spec/lib/open_food_network/products_and_inventory_report_spec.rb deleted file mode 100644 index 48a1ba27e1..0000000000 --- a/spec/lib/open_food_network/products_and_inventory_report_spec.rb +++ /dev/null @@ -1,259 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require 'open_food_network/products_and_inventory_report' - -module OpenFoodNetwork - describe ProductsAndInventoryReport do - context "As a site admin" do - let(:user) do - user = create(:user) - user.spree_roles << Spree::Role.find_or_create_by!(name: 'admin') - user - end - subject do - ProductsAndInventoryReport.new user, {}, true - end - - it "Should return headers" do - expect(subject.header).to eq([ - "Supplier", - "Producer Suburb", - "Product", - "Product Properties", - "Taxons", - "Variant Value", - "Price", - "Group Buy Unit Quantity", - "Amount", - "SKU" - ]) - end - - it "should build a table from a list of variants" do - variant = double(:variant, sku: "sku", - full_name: "Variant Name", - count_on_hand: 10, - price: 100) - allow(variant).to receive_message_chain(:product, :supplier, :name).and_return("Supplier") - allow(variant).to receive_message_chain(:product, :supplier, :address, - :city).and_return("A city") - allow(variant).to receive_message_chain(:product, :name).and_return("Product Name") - allow(variant).to receive_message_chain(:product, - :properties).and_return [double(name: "property1"), - double(name: "property2")] - allow(variant).to receive_message_chain(:product, - :taxons).and_return [double(name: "taxon1"), - double(name: "taxon2")] - allow(variant).to receive_message_chain(:product, :group_buy_unit_size).and_return(21) - allow(subject).to receive(:variants).and_return [variant] - - expect(subject.table).to eq([[ - "Supplier", - "A city", - "Product Name", - "property1, property2", - "taxon1, taxon2", - "Variant Name", - 100, - 21, - "", - "sku" - ]]) - end - - it "fetches variants for some params" do - expect(subject).to receive(:child_variants).and_return ["children"] - expect(subject).to receive(:filter).with(['children']).and_return ["filter_children"] - expect(subject.variants).to eq(["filter_children"]) - end - end - - context "As an enterprise user" do - let(:supplier) { create(:supplier_enterprise) } - let(:enterprise_user) do - user = create(:user) - user.enterprise_roles.create(enterprise: supplier) - user.spree_roles = [] - user.save! - user - end - - subject do - ProductsAndInventoryReport.new enterprise_user, {}, true - end - - describe "fetching child variants" do - it "returns some variants" do - product1 = create(:simple_product, supplier: supplier) - variant1 = product1.variants.first - variant2 = create(:variant, product: product1) - - expect(subject.child_variants).to match_array [variant1, variant2] - end - - it "should only return variants managed by the user" do - product1 = create(:simple_product, supplier: create(:supplier_enterprise)) - product2 = create(:simple_product, supplier: supplier) - variant1 = product1.variants.first - variant2 = product2.variants.first - - expect(subject.child_variants).to eq([variant2]) - end - end - - describe "Filtering variants" do - let(:variants) { Spree::Variant.where(nil).joins(:product).where(is_master: false) } - - describe "based on report type" do - it "returns only variants on hand" do - product1 = create(:simple_product, supplier: supplier, on_hand: 99) - product2 = create(:simple_product, supplier: supplier, on_hand: 0) - - allow(subject).to receive(:params).and_return(report_type: 'inventory') - expect(subject.filter(variants)).to eq([product1.variants.first]) - end - end - it "filters to a specific supplier" do - supplier2 = create(:supplier_enterprise) - product1 = create(:simple_product, supplier: supplier) - product2 = create(:simple_product, supplier: supplier2) - - allow(subject).to receive(:params).and_return(supplier_id: supplier.id) - expect(subject.filter(variants)).to eq([product1.variants.first]) - end - it "filters to a specific distributor" do - distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier: supplier) - product2 = create(:simple_product, supplier: supplier) - order_cycle = create(:simple_order_cycle, suppliers: [supplier], - distributors: [distributor], variants: [product2.variants.first]) - - allow(subject).to receive(:params).and_return(distributor_id: distributor.id) - expect(subject.filter(variants)).to eq([product2.variants.first]) - end - - it "ignores variant overrides without filter" do - distributor = create(:distributor_enterprise) - product = create(:simple_product, supplier: supplier, price: 5) - variant = product.variants.first - order_cycle = create(:simple_order_cycle, suppliers: [supplier], - distributors: [distributor], variants: [product.variants.first]) - create(:variant_override, hub: distributor, variant: variant, price: 2) - - result = subject.filter(variants) - - expect(result.first.price).to eq 5 - end - - it "considers variant overrides with distributor" do - distributor = create(:distributor_enterprise) - product = create(:simple_product, supplier: supplier, price: 5) - variant = product.variants.first - order_cycle = create(:simple_order_cycle, suppliers: [supplier], - distributors: [distributor], variants: [product.variants.first]) - create(:variant_override, hub: distributor, variant: variant, price: 2) - - allow(subject).to receive(:params).and_return(distributor_id: distributor.id) - result = subject.filter(variants) - - expect(result.first.price).to eq 2 - end - - it "filters to a specific order cycle" do - distributor = create(:distributor_enterprise) - product1 = create(:simple_product, supplier: supplier) - product2 = create(:simple_product, supplier: supplier) - order_cycle = create(:simple_order_cycle, suppliers: [supplier], - distributors: [distributor], variants: [product1.variants.first]) - - allow(subject).to receive(:params).and_return(order_cycle_id: order_cycle.id) - expect(subject.filter(variants)).to eq([product1.variants.first]) - end - - it "should do all the filters at once" do - # The following data ensures that this spec fails if any of the - # filters fail. It's testing the filters are not impacting each other. - distributor = create(:distributor_enterprise) - other_distributor = create(:distributor_enterprise) - other_supplier = create(:supplier_enterprise) - not_filtered_variant = create(:simple_product, supplier: supplier).variants.first - variant_filtered_by_order_cycle = create(:simple_product, - supplier: supplier).variants.first - variant_filtered_by_distributor = create(:simple_product, - supplier: supplier).variants.first - variant_filtered_by_supplier = create(:simple_product, - supplier: other_supplier).variants.first - variant_filtered_by_stock = create(:simple_product, supplier: supplier, - on_hand: 0).variants.first - - # This OC contains all products except the one that should be filtered - # by order cycle. We create a separate OC further down to proof that - # the product is passing all other filters. - order_cycle = create( - :simple_order_cycle, - suppliers: [supplier, other_supplier], - distributors: [distributor, other_distributor], - variants: [ - not_filtered_variant, - variant_filtered_by_distributor, - variant_filtered_by_supplier, - variant_filtered_by_stock - ] - ) - - # Remove the distribution of one product for one distributor but still - # sell it through the other distributor. - order_cycle.exchanges.outgoing.find_by(receiver_id: distributor.id). - exchange_variants. - find_by(variant_id: variant_filtered_by_distributor). - destroy - - # Make product available to be filtered later. See OC comment above. - create( - :simple_order_cycle, - suppliers: [supplier], - distributors: [distributor, other_distributor], - variants: [ - variant_filtered_by_order_cycle - ] - ) - - allow(subject).to receive(:params).and_return( - order_cycle_id: order_cycle.id, - supplier_id: supplier.id, - distributor_id: distributor.id, - report_type: 'inventory' - ) - - expect(subject.filter(variants)).to match_array [not_filtered_variant] - - # And it integrates with the ordering of the `variants` method. - expect(subject.variants).to match_array [not_filtered_variant] - end - end - - describe "fetching SKU for a variant" do - let(:variant) { create(:variant) } - let(:product) { variant.product } - - before { product.update_attribute(:sku, "Product SKU") } - - context "when the variant has an SKU set" do - before { variant.update_attribute(:sku, "Variant SKU") } - it "returns it" do - expect(subject.send(:sku_for, variant)).to eq "Variant SKU" - end - end - - context "when the variant has bo SKU set" do - before { variant.update_attribute(:sku, "") } - - it "returns the product's SKU" do - expect(subject.send(:sku_for, variant)).to eq "Product SKU" - end - end - end - end - end -end diff --git a/spec/system/admin/reports/packing_report_spec.rb b/spec/system/admin/reports/packing_report_spec.rb index 421144b223..efe64b1901 100644 --- a/spec/system/admin/reports/packing_report_spec.rb +++ b/spec/system/admin/reports/packing_report_spec.rb @@ -46,7 +46,7 @@ describe "Packing Reports", js: true do click_link "Pack By Customer" fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' - click_button 'Search' + click_button 'Go' rows = find("table.report__table").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } @@ -59,7 +59,7 @@ describe "Packing Reports", js: true do it "sorts alphabetically" do click_link "Pack By Customer" - click_button 'Search' + click_button 'Go' rows = find("table.report__table").all("tr") table = rows.map { |r| r.all("th,td").map { |c| c.text.strip }[3] } @@ -79,7 +79,7 @@ describe "Packing Reports", js: true do click_link "Pack By Supplier" fill_in 'q_completed_at_gt', with: '2013-04-25 13:00:00' fill_in 'q_completed_at_lt', with: '2013-04-25 16:00:00' - click_button 'Search' + click_button 'Go' rows = find("table.report__table").all("thead tr") table = rows.map { |r| r.all("th").map { |c| c.text.strip } } diff --git a/spec/system/admin/reports_spec.rb b/spec/system/admin/reports_spec.rb index d2064eae8f..2ee3e41bcf 100644 --- a/spec/system/admin/reports_spec.rb +++ b/spec/system/admin/reports_spec.rb @@ -94,7 +94,7 @@ describe ' it "orders and distributors report" do login_as_admin_and_visit spree.admin_reports_path click_link 'Orders And Distributors' - click_button 'Search' + click_button 'Go' expect(page).to have_content 'ORDER DATE' end @@ -102,7 +102,7 @@ describe ' it "payments reports" do login_as_admin_and_visit spree.admin_reports_path click_link 'Payment Reports' - click_button 'Search' + click_button 'Go' expect(page).to have_content 'PAYMENT STATE' end @@ -176,7 +176,7 @@ describe ' # When I filter to just one distributor select user1.enterprises.first.name, from: 'q_distributor_id_eq' - click_button 'Search' + click_button 'Go' # Then I should see the relevant order expect(page).to have_content order1.number.to_s @@ -243,7 +243,7 @@ describe ' pick_datetime "#q_completed_at_lt", datetime_end select 'Order Cycle Customer Totals', from: 'report_subtype' - click_button 'Search' + click_button 'Go' # Then I should see the rows for the first order but not the second expect(all('table.report__table tbody tr').count).to eq(4) # Two rows per order end