Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes #66 #74

Merged
merged 5 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 79 additions & 24 deletions app/helpers/articles_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,6 @@ def row_classes(article)
classes.join(' ')
end

def format_unit(unit_property, article)
unit_code = article.send(unit_property)
return article.unit if unit_code.nil?

ArticleUnitsLib.human_readable_unit(unit_code)
end

def format_supplier_order_unit(article)
format_unit(:supplier_order_unit, article)
end
Expand All @@ -34,23 +27,6 @@ def format_billing_unit(article)
format_unit(:billing_unit, article)
end

def format_unit_with_ratios(unit_property, article)
base = format_unit(unit_property, article)
unit_code = article.send(unit_property)
return base if ArticleUnitsLib.unit_is_si_convertible(unit_code)

relevant_units = [article.article_unit_ratios.map(&:unit)]
relevant_units << article.supplier_order_unit unless unit_property == :supplier_order_unit
first_si_convertible_unit = relevant_units
.flatten
.find { |unit| ArticleUnitsLib.unit_is_si_convertible(unit) }

return base if first_si_convertible_unit.nil?

quantity = article.convert_quantity(1, unit_code, first_si_convertible_unit)
"#{base} (#{number_with_precision(quantity, precision: 2, strip_insignificant_zeros: true)}\u00a0#{ArticleUnitsLib.units.to_h[first_si_convertible_unit][:symbol]})"
end

def format_supplier_order_unit_with_ratios(article)
format_unit_with_ratios(:supplier_order_unit, article)
end
Expand All @@ -77,4 +53,83 @@ def field_with_preset_value_and_errors(options)
safe_join(output)
end
end

private

def format_unit_with_ratios(unit_property, article_version, reference_unit = :group_order_unit)
base = format_unit(unit_property, article_version)

factorized_unit_str = get_factorized_unit_str(article_version, unit_property, reference_unit) unless reference_unit.nil?
return base if factorized_unit_str.nil?

"#{base} (#{factorized_unit_str})"
end

def format_unit(unit_property, article)
unit_code = article.send(unit_property)
return article.unit if unit_code.nil?

ArticleUnitsLib.human_readable_unit(unit_code)
end

def get_factorized_unit_str(article_version, unit_property, reference_unit)
unit_code = article_version.send(unit_property)
reference_unit_code = article_version.send(reference_unit) || article_version.supplier_order_unit
return nil if ArticleUnitsLib.unit_is_si_convertible(unit_code)

factors = [{
quantity: article_version.convert_quantity(1, unit_code, reference_unit_code),
code: reference_unit_code
}]

first_si_conversible_unit_after_reference_unit = get_first_si_conversible_unit(article_version, reference_unit_code) unless ArticleUnitsLib.unit_is_si_convertible(reference_unit_code)
unless first_si_conversible_unit_after_reference_unit.nil?
factors << {
quantity: article_version.convert_quantity(1, reference_unit_code, first_si_conversible_unit_after_reference_unit),
code: first_si_conversible_unit_after_reference_unit
}
end

return nil if factors.length == 1 && factors.first[:quantity] == 1 && factors.first[:code] == unit_code

format_unit_factors(factors)
end

def get_first_si_conversible_unit(article_version, after_unit)
relevant_units = [article_version.supplier_order_unit] + article_version.article_unit_ratios.map(&:unit)

unit_index = relevant_units.find_index { |unit| unit == after_unit }
return nil if unit_index.nil?

relevant_units[unit_index + 1..].find { |unit| ArticleUnitsLib.unit_is_si_convertible(unit) }
end

def format_unit_factors(factors)
factor_str_arr = factors.each_with_index.map do |factor, index|
is_last = index + 1 == factors.length
format_unit_factor(factor, is_last)
end

factor_str_arr
.compact
.join("#{Prawn::Text::NBSP}×#{Prawn::Text::NBSP}")
end

def format_unit_factor(factor, with_unit)
return nil if !with_unit && factor[:quantity] == 1

quantity_str = number_with_precision(factor[:quantity], precision: 2, strip_insignificant_zeros: true)
return quantity_str unless with_unit

unit_data = ArticleUnitsLib.units.to_h[factor[:code]]
is_si_conversible = ArticleUnitsLib.unit_is_si_convertible(factor[:code])
unit_label = is_si_conversible ? unit_data[:symbol] : unit_data[:name]
return unit_label if factor[:quantity] == 1

multiplier_str = '×' unless is_si_conversible

[quantity_str, multiplier_str, unit_label]
.compact
.join(Prawn::Text::NBSP)
end
end
125 changes: 125 additions & 0 deletions spec/helpers/articles_helper_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
require_relative '../spec_helper'

describe ArticlesHelper do
include described_class

let(:article) { create(:article) }
let(:article_version) { article.latest_article_version }

describe 'formatting supplier order unit' do
def test_with_article_data(supplier_order_unit: nil, group_order_unit: nil, ratios: nil)
setup_article_version(supplier_order_unit: supplier_order_unit, group_order_unit: group_order_unit, ratios: ratios)
format_supplier_order_unit_with_ratios(article_version).gsub(Prawn::Text::NBSP, ' ')
end

describe 'without ratios' do
it 'formats SI conversible unit without ratios' do
result = test_with_article_data(supplier_order_unit: 'KGM')
expect(result).to eq('kg')
end

it 'formats non SI conversible unit' do
result = test_with_article_data(supplier_order_unit: 'XPK')
expect(result).to eq('Package')
end
end

describe 'with ratios' do
it 'formats ratio to group order unit' do
result = test_with_article_data(
supplier_order_unit: 'XPK',
ratios: [[250, 'GRM']],
group_order_unit: 'GRM'
)
expect(result).to eq('Package (250 g)')
end

it 'formats ratio to first SI ratio if group order unit equals or defaults to supplier order unit' do
result = test_with_article_data(
supplier_order_unit: 'XPK',
ratios: [[250, 'GRM']],
group_order_unit: nil
)
expect(result).to eq('Package (250 g)')
end

it 'formats ratio to group order unit with multiple ratios' do
result = test_with_article_data(
supplier_order_unit: 'XCR',
ratios: [[20.148, 'KGM'], [20, 'XBO'], [10, 'LTR']],
group_order_unit: 'XBO'
)
expect(result).to eq('Crate (20 × 0.5 l)')
end

it 'formats ratio from group order unit to first SI ratio if group order unit equals or defaults to supplier order unit' do
result = test_with_article_data(
supplier_order_unit: 'XPX',
ratios: [[100, 'XPC'], [400, 'XPK'], [4000, 'X6H'], [200_000, 'GRM']],
group_order_unit: 'XPK'
)
expect(result).to eq('Pallet (400 × 500 g)')
end

it 'formats ratio from group order unit to first SI ratio' do
result = test_with_article_data(
supplier_order_unit: 'XPX',
ratios: [[100, 'XPC'], [400, 'XPK'], [4000, 'X6H'], [200_000, 'GRM']],
group_order_unit: 'KGM'
)
expect(result).to eq('Pallet (200 kg)')
end
end
end

describe 'formatting group order unit' do
def test_with_article_data(supplier_order_unit: nil, group_order_unit: nil, ratios: nil)
setup_article_version(supplier_order_unit: supplier_order_unit, group_order_unit: group_order_unit, ratios: ratios)
format_group_order_unit_with_ratios(article_version).gsub(Prawn::Text::NBSP, ' ')
end

describe 'without ratios' do
it 'formats SI conversible unit without ratios' do
result = test_with_article_data(supplier_order_unit: 'KGM', group_order_unit: 'KGM')
expect(result).to eq('kg')
end

it 'formats non SI conversible unit' do
result = test_with_article_data(supplier_order_unit: 'XPK', group_order_unit: 'XPK')
expect(result).to eq('Package')
end
end

describe 'with ratios' do
it 'formats group order unit without ratios if group order unit is SI conversible' do
result = test_with_article_data(
supplier_order_unit: 'XPK',
ratios: [[250, 'GRM']],
group_order_unit: 'GRM'
)
expect(result).to eq('g')
end

it 'formats group order unit with ratio to first SI unit if group order unit is not SI conversible' do
result = test_with_article_data(
supplier_order_unit: 'XCR',
ratios: [[20.148, 'KGM'], [20, 'XBO'], [10, 'LTR']],
group_order_unit: 'XBO'
)
expect(result).to eq('Bottle (0.5 l)')
end
end
end
end

private

def setup_article_version(supplier_order_unit:, ratios:, group_order_unit:)
article_version.supplier_order_unit = supplier_order_unit unless supplier_order_unit.nil?
unless ratios.nil?
article_version.article_unit_ratios = ratios.each_with_index.map do |ratio_data, index|
ArticleUnitRatio.create(sort: index, quantity: ratio_data[0], unit: ratio_data[1])
end
end
article_version.group_order_unit = group_order_unit unless group_order_unit.nil?
end
4 changes: 2 additions & 2 deletions spec/integration/articles_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@
end

describe 'can update existing article' do
let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g') }
let!(:article) { create(:article, supplier: supplier, name: 'Foobar', order_number: 1, unit: '250 g', group_order_unit: nil) }

it do
find('input[type="submit"]').click
Expand Down Expand Up @@ -198,7 +198,7 @@
end

describe 'can convert units when updating' do
let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g') }
let!(:article) { create(:article, supplier: supplier, order_number: 1, unit: '250 g', group_order_unit: nil) }

it do
check('articles_convert_units')
Expand Down
Loading