provide unit price values in front end shop

This commit is contained in:
Andy Brett
2021-02-24 21:26:14 -08:00
parent b12293d1fb
commit 39fc0707c3
6 changed files with 296 additions and 36 deletions

View File

@@ -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

View File

@@ -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)

View 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

View 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

View 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

View 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