Report Refactor 3: Products & Inventory

This commit is contained in:
Sebastian Castro
2022-04-06 11:06:40 +02:00
committed by Jean-Baptiste Bellet
parent 71aca960ee
commit b3e1ffe9e2
9 changed files with 117 additions and 177 deletions

View File

@@ -190,7 +190,7 @@ Layout/LineLength:
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/packing/packing_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_default_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/lib/reports/xero_invoices_report_spec.rb'
- 'spec/lib/stripe/authorize_response_patcher_spec.rb'
@@ -646,7 +646,7 @@ Metrics/ModuleLength:
- 'spec/lib/reports/order_grouper_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/customer_totals_report_spec.rb'
- 'spec/lib/reports/orders_and_fulfillment/orders_and_fulfillment_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_default_report_spec.rb'
- 'spec/lib/reports/products_and_inventory_report_spec.rb'
- 'spec/lib/reports/users_and_enterprises_report_spec.rb'
- 'spec/models/spree/adjustment_spec.rb'
- 'spec/models/spree/credit_card_spec.rb'

View File

@@ -0,0 +1,13 @@
# frozen_string_literal: true
module Reporting
module Reports
module ProductsAndInventory
class AllProducts < Base
def filter_on_hand(variants)
variants # do not filter
end
end
end
end
end

View File

@@ -5,37 +5,32 @@ require 'open_food_network/scope_variant_to_hub'
module Reporting
module Reports
module ProductsAndInventory
class ProductsAndInventoryReport < ReportObjectTemplate
delegate :table_rows, :table_headers, :rules, :columns, :sku_for, to: :report
def report
@report ||= report_klass.new(self)
end
def report_type
params[:report_subtype]
end
def report_klass
if report_type == 'lettuce_share'
LettuceShareReport
else
ProductsAndInventoryDefaultReport
end
end
def permissions
@permissions ||= OpenFoodNetwork::Permissions.new(@user)
end
def visible_products
@visible_products ||= permissions.visible_products
end
def variants
class Base < ReportObjectTemplate
def query_result
filter(child_variants)
end
# rubocop:disable Metrics/AbcSize
def columns
{
supplier: proc { |variant| variant.product.supplier.name },
producer_suburb: proc { |variant| variant.product.supplier.address.city },
product: proc { |variant| variant.product.name },
product_properties: proc { |v| v.product.properties.map(&:name).join(", ") },
taxons: proc { |variant| variant.product.taxons.map(&:name).join(", ") },
variant_value: proc { |variant| variant.full_name },
price: proc { |variant| variant.price },
group_buy_unit_quantity: proc { |variant| variant.product.group_buy_unit_size },
amount: proc { |_variant| "" },
sku: proc { |variant| variant.sku.presence || variant.product.sku },
}
end
# rubocop:enable Metrics/AbcSize
def filter(variants)
filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants
end
def child_variants
Spree::Variant.
where(is_master: false).
@@ -45,17 +40,23 @@ module Reporting
order('spree_products.name')
end
def filter(variants)
filter_on_hand filter_to_distributor filter_to_order_cycle filter_to_supplier variants
private
def report_type
params[:report_subtype]
end
def visible_products
@visible_products ||= permissions.visible_products
end
def permissions
@permissions ||= OpenFoodNetwork::Permissions.new(@user)
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
variants.select(&:in_stock?)
end
def filter_to_supplier(variants)

View File

@@ -0,0 +1,10 @@
# frozen_string_literal: true
module Reporting
module Reports
module ProductsAndInventory
class Inventory < Base
end
end
end
end

View File

@@ -1,59 +1,39 @@
# frozen_string_literal: true
# require 'variant_units/option_value_namer'
module Reporting
module Reports
module ProductsAndInventory
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",
"Description",
"Qty",
"Pack Size",
"Unit",
"Unit Price",
"Total",
"GST incl.",
"Grower and growing method",
"Taxon"
]
end
def table_rows
variants.select(&:in_stock?)
.map do |variant|
[
variant.product.name,
variant.full_name,
'',
VariantUnits::OptionValueNamer.new(variant).value,
VariantUnits::OptionValueNamer.new(variant).unit,
variant.price,
'',
gst(variant),
grower_and_method(variant),
variant.product.primary_taxon.name
]
end
end
def rules
[]
class LettuceShare < Base
# NOTE: These are NOT to be translated, they need to be in this exact format
# to work with LettucShare
def custom_headers
{
product: "PRODUCT",
description: "Description",
quantity: "Qty",
pack_size: "Pack Size",
unit: "Unit",
unit_price: "Unit Price",
total: "Total",
gst: "GST incl.",
grower: "Grower and growing method",
taxon: "Taxon"
}
end
def columns
{}
{
product: proc { |variant| variant.product.name },
description: proc { |variant| variant.full_name },
quantity: proc { |_variant| '' },
pack_size: proc { |variant| VariantUnits::OptionValueNamer.new(variant).value },
unit: proc { |variant| VariantUnits::OptionValueNamer.new(variant).unit },
unit_price: proc { |variant| variant.price },
total: proc { |_variant| '' },
gst: proc { |variant| gst(variant) },
grower: proc { |variant| grower_and_method(variant) },
taxon: proc { |variant| variant.product.primary_taxon.name }
}
end
private

View File

@@ -1,63 +0,0 @@
# frozen_string_literal: true
module Reporting
module Reports
module ProductsAndInventory
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
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 rules
[]
end
def columns
{}
end
def sku_for(variant)
return variant.sku if variant.sku.present?
variant.product.sku
end
end
end
end
end

View File

@@ -246,7 +246,7 @@ describe Admin::ReportsController, type: :controller do
end
it "creates a ProductAndInventoryReport" do
allow(Reporting::Reports::ProductsAndInventory::ProductsAndInventoryReport).to receive(:new)
allow(Reporting::Reports::ProductsAndInventory::Base).to receive(:new)
.and_return(report = double(:report))
allow(report).to receive(:table_headers).and_return []
allow(report).to receive(:table_rows).and_return []

View File

@@ -5,12 +5,9 @@ require 'spec_helper'
module Reporting
module Reports
module ProductsAndInventory
describe LettuceShareReport do
describe LettuceShare do
let(:user) { create(:user) }
let(:base_report) {
ProductsAndInventoryReport.new(user, { report_subtype: 'lettuce_share' })
}
let(:report) { base_report.report }
let(:report) { LettuceShare.new(user) }
let(:variant) { create(:variant) }
describe "grower and method" do
@@ -54,18 +51,17 @@ module Reporting
}
it "all items" do
allow(base_report).to receive(:child_variants) {
Spree::Variant.where(id: [variant, variant2, variant3])
}
allow(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(base_report).to receive(:child_variants) {
Spree::Variant.where(id: [variant, variant2, variant3,
variant4])
}
allow(report).to receive(:child_variants) {
Spree::Variant.where(id: [variant, variant2, variant3, variant4])
}
expect(report.table_rows.count).to eq 3
end
@@ -75,10 +71,10 @@ module Reporting
# create the overrides
variant2_override
variant3_override
allow(base_report).to receive(:child_variants) {
Spree::Variant.where(id: [variant, variant2, variant3])
}
allow(base_report).to receive(:params) {
allow(report).to receive(:child_variants) {
Spree::Variant.where(id: [variant, variant2, variant3])
}
allow(report).to receive(:params) {
{ distributor_id: hub.id, report_subtype: 'lettuce_share' }
}
rows = report.table_rows

View File

@@ -5,7 +5,7 @@ require 'spec_helper'
module Reporting
module Reports
module ProductsAndInventory
describe ProductsAndInventoryDefaultReport do
describe Base do
context "As a site admin" do
let(:user) do
user = create(:user)
@@ -13,7 +13,7 @@ module Reporting
user
end
subject do
ProductsAndInventoryReport.new user, {}
Base.new user, {}
end
it "Should return headers" do
@@ -48,7 +48,7 @@ module Reporting
: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]
allow(subject).to receive(:query_result).and_return [variant]
expect(subject.table_rows).to eq([[
"Supplier",
@@ -67,7 +67,7 @@ module Reporting
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"])
expect(subject.query_result).to eq(["filter_children"])
end
end
@@ -82,7 +82,7 @@ module Reporting
end
subject do
ProductsAndInventoryReport.new enterprise_user, {}
Base.new enterprise_user, {}
end
describe "fetching child variants" do
@@ -112,7 +112,7 @@ module Reporting
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')
subject = Inventory.new enterprise_user
expect(subject.filter(variants)).to eq([product1.variants.first])
end
end
@@ -225,17 +225,17 @@ module Reporting
]
)
subject = Inventory.new enterprise_user
allow(subject).to receive(:params).and_return(
order_cycle_id: order_cycle.id,
supplier_id: supplier.id,
distributor_id: distributor.id,
report_subtype: 'inventory'
distributor_id: distributor.id
)
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]
expect(subject.query_result).to match_array [not_filtered_variant]
end
end
@@ -243,12 +243,15 @@ module Reporting
let(:variant) { create(:variant) }
let(:product) { variant.product }
before { product.update_attribute(:sku, "Product SKU") }
before {
product.update_attribute(:sku, "Product SKU")
allow(subject).to receive(:query_result).and_return([variant])
}
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"
expect(subject.rows.first.sku).to eq "Variant SKU"
end
end
@@ -256,7 +259,7 @@ module Reporting
before { variant.update_attribute(:sku, "") }
it "returns the product's SKU" do
expect(subject.__send__(:sku_for, variant)).to eq "Product SKU"
expect(subject.rows.first.sku).to eq "Product SKU"
end
end
end