mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-09 23:06:06 +00:00
Report Refactor 1: Products & Inventory
This commit is contained in:
committed by
Jean-Baptiste Bellet
parent
881a708ecf
commit
3d1b61ae6e
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]}"
|
||||
|
||||
@@ -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"})
|
||||
@@ -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)
|
||||
@@ -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|
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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 } }
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user