mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-13 23:37:47 +00:00
provide unit price values in front end shop
This commit is contained in:
@@ -41,10 +41,10 @@ class Api::VariantSerializer < ActiveModel::Serializer
|
||||
end
|
||||
|
||||
def unit_price_price
|
||||
(rand * 10).round(2)
|
||||
price_with_fees / VariantUnits::UnitPrices.new(object).unit_price_denominator
|
||||
end
|
||||
|
||||
def unit_price_unit
|
||||
rand > 0.5 ? "item" : "kg"
|
||||
VariantUnits::UnitPrices.new(object).unit_price_unit
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,40 +62,7 @@ module VariantUnits
|
||||
end
|
||||
|
||||
def scale_for_unit_value
|
||||
units = {
|
||||
'weight' => {
|
||||
1.0 => { 'name' => 'g', 'system' => 'metric' },
|
||||
28.35 => { 'name' => 'oz', 'system' => 'imperial' },
|
||||
453.6 => { 'name' => 'lb', 'system' => 'imperial' },
|
||||
1000.0 => { 'name' => 'kg', 'system' => 'metric' },
|
||||
1_000_000.0 => { 'name' => 'T', 'system' => 'metric' }
|
||||
},
|
||||
'volume' => {
|
||||
0.001 => { 'name' => 'mL', 'system' => 'metric' },
|
||||
1.0 => { 'name' => 'L', 'system' => 'metric' },
|
||||
1000.0 => { 'name' => 'kL', 'system' => 'metric' }
|
||||
}
|
||||
}
|
||||
|
||||
scales = units[@variant.product.variant_unit]
|
||||
product_scale = @variant.product.variant_unit_scale
|
||||
product_scale_system = scales[product_scale.to_f]['system']
|
||||
|
||||
largest_unit = find_largest_unit(scales, product_scale_system)
|
||||
[largest_unit[0], largest_unit[1]["name"]]
|
||||
end
|
||||
|
||||
# Find the largest available and compatible unit where unit_value comes
|
||||
# to >= 1 when expressed in it.
|
||||
# If there is none available where this is true, use the smallest available unit.
|
||||
def find_largest_unit(scales, product_scale_system)
|
||||
largest_unit = scales.select { |scale, unit_info|
|
||||
unit_info['system'] == product_scale_system &&
|
||||
@variant.unit_value / scale >= 1
|
||||
}.max
|
||||
return scales.first if largest_unit.nil?
|
||||
|
||||
largest_unit
|
||||
VariantUnits::WeightsAndMeasures.new(@variant).scale_for_unit_value
|
||||
end
|
||||
|
||||
def pluralize(unit_name, count)
|
||||
|
||||
37
app/services/variant_units/unit_prices.rb
Normal file
37
app/services/variant_units/unit_prices.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module VariantUnits
|
||||
class UnitPrices
|
||||
def initialize(variant)
|
||||
@variant = variant
|
||||
@product = variant.product
|
||||
end
|
||||
|
||||
def unit_price_denominator
|
||||
return @variant.unit_value if @product&.variant_unit == "items"
|
||||
|
||||
case unit_price_unit
|
||||
when "lb"
|
||||
@variant.unit_value / 453.6
|
||||
when "kg"
|
||||
@variant.unit_value / 1000
|
||||
else # Liters
|
||||
@variant.unit_value
|
||||
end
|
||||
end
|
||||
|
||||
def unit_price_unit
|
||||
return "lb" if VariantUnits::WeightsAndMeasures.new(@variant).
|
||||
system_of_measurement == "imperial"
|
||||
|
||||
case @product&.variant_unit
|
||||
when "weight"
|
||||
"kg"
|
||||
when "volume"
|
||||
"L"
|
||||
else
|
||||
@product.variant_unit_name.presence || "item"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
60
app/services/variant_units/weights_and_measures.rb
Normal file
60
app/services/variant_units/weights_and_measures.rb
Normal file
@@ -0,0 +1,60 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module VariantUnits
|
||||
class WeightsAndMeasures
|
||||
def initialize(variant)
|
||||
@variant = variant
|
||||
@units = UNITS
|
||||
end
|
||||
|
||||
def scale_for_unit_value
|
||||
largest_unit = find_largest_unit(scales_for_variant_unit, system_of_measurement)
|
||||
return [nil, nil] unless largest_unit
|
||||
|
||||
[largest_unit[0], largest_unit[1]["name"]]
|
||||
end
|
||||
|
||||
def system_of_measurement
|
||||
scales = scales_for_variant_unit
|
||||
return "custom" unless product_scale = @variant.product.variant_unit_scale
|
||||
|
||||
scales[product_scale.to_f]['system']
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
UNITS = {
|
||||
'weight' => {
|
||||
1.0 => { 'name' => 'g', 'system' => 'metric' },
|
||||
28.35 => { 'name' => 'oz', 'system' => 'imperial' },
|
||||
453.6 => { 'name' => 'lb', 'system' => 'imperial' },
|
||||
1000.0 => { 'name' => 'kg', 'system' => 'metric' },
|
||||
1_000_000.0 => { 'name' => 'T', 'system' => 'metric' }
|
||||
},
|
||||
'volume' => {
|
||||
0.001 => { 'name' => 'mL', 'system' => 'metric' },
|
||||
1.0 => { 'name' => 'L', 'system' => 'metric' },
|
||||
1000.0 => { 'name' => 'kL', 'system' => 'metric' }
|
||||
}
|
||||
}.freeze
|
||||
|
||||
def scales_for_variant_unit
|
||||
@units[@variant.product.variant_unit]
|
||||
end
|
||||
|
||||
# Find the largest available and compatible unit where unit_value comes
|
||||
# to >= 1 when expressed in it.
|
||||
# If there is none available where this is true, use the smallest available unit.
|
||||
def find_largest_unit(scales, product_scale_system)
|
||||
return nil unless scales
|
||||
|
||||
largest_unit = scales.select { |scale, unit_info|
|
||||
unit_info['system'] == product_scale_system &&
|
||||
@variant.unit_value / scale >= 1
|
||||
}.max
|
||||
return scales.first if largest_unit.nil?
|
||||
|
||||
largest_unit
|
||||
end
|
||||
end
|
||||
end
|
||||
107
spec/services/variant_units/unit_prices_spec.rb
Normal file
107
spec/services/variant_units/unit_prices_spec.rb
Normal file
@@ -0,0 +1,107 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module VariantUnits
|
||||
describe UnitPrices do
|
||||
subject { UnitPrices.new(variant) }
|
||||
let(:variant) { Spree::Variant.new }
|
||||
let(:product) { double }
|
||||
|
||||
before do
|
||||
allow(variant).to receive(:product) { product }
|
||||
end
|
||||
|
||||
describe "#unit_price_unit" do
|
||||
context "metric" do
|
||||
before do
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
end
|
||||
|
||||
it "returns kg for weight" do
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
expect(subject.unit_price_unit).to eq("kg")
|
||||
end
|
||||
|
||||
it "returns L for volume" do
|
||||
allow(product).to receive(:variant_unit) { "volume" }
|
||||
expect(subject.unit_price_unit).to eq("L")
|
||||
end
|
||||
end
|
||||
|
||||
context "imperial" do
|
||||
it "returns lbs" do
|
||||
allow(product).to receive(:variant_unit_scale) { 453.6 }
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
expect(subject.unit_price_unit).to eq("lb")
|
||||
end
|
||||
end
|
||||
|
||||
context "items" do
|
||||
it "returns items if no unit is specified" do
|
||||
allow(product).to receive(:variant_unit_name) { nil }
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
expect(subject.unit_price_unit).to eq("item")
|
||||
end
|
||||
|
||||
it "returns the unit if a unit is specified" do
|
||||
allow(product).to receive(:variant_unit_name) { "bunch" }
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
expect(subject.unit_price_unit).to eq("bunch")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#unit_price_denominator" do
|
||||
context "metric" do
|
||||
it "returns 0.5 for a 500g variant" do
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
variant.unit_value = 500
|
||||
expect(subject.unit_price_denominator).to eq(0.5)
|
||||
end
|
||||
|
||||
it "returns 2 for a 2kg variant" do
|
||||
allow(product).to receive(:variant_unit_scale) { 1000 }
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
variant.unit_value = 2000
|
||||
expect(subject.unit_price_denominator).to eq(2)
|
||||
end
|
||||
|
||||
it "returns 0.5 for a 500mL variant" do
|
||||
allow(product).to receive(:variant_unit_scale) { 0.001 }
|
||||
allow(product).to receive(:variant_unit) { "volume" }
|
||||
variant.unit_value = 0.5
|
||||
expect(subject.unit_price_denominator).to eq(0.5)
|
||||
end
|
||||
end
|
||||
|
||||
context "imperial" do
|
||||
it "returns 2 for a 2 pound variant" do
|
||||
allow(product).to receive(:variant_unit_scale) { 453.6 }
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
variant.unit_value = 2*453.6
|
||||
expect(subject.unit_price_denominator).to eq(2)
|
||||
end
|
||||
end
|
||||
|
||||
context "items" do
|
||||
it "returns 1 if no unit is specified" do
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
variant.unit_value = 1
|
||||
expect(subject.unit_price_denominator).to eq(1)
|
||||
end
|
||||
|
||||
it "returns 2 for multi-item units" do
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
variant.unit_value = 2
|
||||
expect(subject.unit_price_denominator).to eq(2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
89
spec/services/variant_units/weights_and_measures_spec.rb
Normal file
89
spec/services/variant_units/weights_and_measures_spec.rb
Normal file
@@ -0,0 +1,89 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
module VariantUnits
|
||||
describe WeightsAndMeasures do
|
||||
subject { WeightsAndMeasures.new(variant) }
|
||||
let(:variant) { Spree::Variant.new }
|
||||
let(:product) { double }
|
||||
|
||||
before do
|
||||
allow(variant).to receive(:product) { product }
|
||||
end
|
||||
|
||||
describe "#system_of_measurement" do
|
||||
context "weight" do
|
||||
before do
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
end
|
||||
|
||||
it "when scale is for a metric unit" do
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
expect(subject.system_of_measurement).to eq("metric")
|
||||
end
|
||||
|
||||
it "when scale is for an imperial unit" do
|
||||
allow(product).to receive(:variant_unit_scale) { 28.35 }
|
||||
expect(subject.system_of_measurement).to eq("imperial")
|
||||
end
|
||||
end
|
||||
|
||||
context "volume" do
|
||||
it "when scale is for a metric unit" do
|
||||
allow(product).to receive(:variant_unit) { "volume" }
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
expect(subject.system_of_measurement).to eq("metric")
|
||||
end
|
||||
end
|
||||
|
||||
context "items" do
|
||||
it "when scale is for items" do
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
expect(subject.system_of_measurement).to eq("custom")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#scale_for_unit_value" do
|
||||
context "weight" do
|
||||
before do
|
||||
allow(product).to receive(:variant_unit) { "weight" }
|
||||
end
|
||||
|
||||
context "metric" do
|
||||
it "for a unit value that should display in grams" do
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
allow(variant).to receive(:unit_value) { 500 }
|
||||
expect(subject.scale_for_unit_value).to eq([1.0, "g"])
|
||||
end
|
||||
|
||||
it "for a unit value that should display in kg" do
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
allow(variant).to receive(:unit_value) { 1500 }
|
||||
expect(subject.scale_for_unit_value).to eq([1000.0, "kg"])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "volume" do
|
||||
it "for a unit value that should display in L" do
|
||||
allow(product).to receive(:variant_unit) { "volume" }
|
||||
allow(product).to receive(:variant_unit_scale) { 1.0 }
|
||||
allow(variant).to receive(:unit_value) { 1500 }
|
||||
expect(subject.scale_for_unit_value).to eq([1000, "kL"])
|
||||
end
|
||||
end
|
||||
|
||||
context "items" do
|
||||
it "when scale is for items" do
|
||||
allow(product).to receive(:variant_unit) { "items" }
|
||||
allow(product).to receive(:variant_unit_scale) { nil }
|
||||
allow(variant).to receive(:unit_value) { 4 }
|
||||
expect(subject.scale_for_unit_value).to eq([nil, nil])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user