From 5be0082171f026d289cb43368cc42756637660d0 Mon Sep 17 00:00:00 2001 From: lentschi Date: Fri, 12 Jul 2024 19:00:05 +0200 Subject: [PATCH] Fixes #66 (#74) * Fixes #66 * Fixes broken integration tests * chore: refactor extract articles_helper article_version test setup function * chore: make helper functions private * On #66 Fixes https://github.com/foodcoopsat/foodsoft_hackathon/pull/74/files/b078cfaa87d334dba8eaafff5b2be152f293c448#r1670426999 --------- Co-authored-by: Philipp Rothmann --- app/helpers/articles_helper.rb | 103 +++++++++++++++++----- spec/helpers/articles_helper_spec.rb | 125 +++++++++++++++++++++++++++ spec/integration/articles_spec.rb | 4 +- 3 files changed, 206 insertions(+), 26 deletions(-) create mode 100644 spec/helpers/articles_helper_spec.rb diff --git a/app/helpers/articles_helper.rb b/app/helpers/articles_helper.rb index b9de5b1d..116b41ae 100644 --- a/app/helpers/articles_helper.rb +++ b/app/helpers/articles_helper.rb @@ -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 @@ -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 @@ -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 diff --git a/spec/helpers/articles_helper_spec.rb b/spec/helpers/articles_helper_spec.rb new file mode 100644 index 00000000..e7a39125 --- /dev/null +++ b/spec/helpers/articles_helper_spec.rb @@ -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 diff --git a/spec/integration/articles_spec.rb b/spec/integration/articles_spec.rb index cdf60c4e..ad9cb8d5 100644 --- a/spec/integration/articles_spec.rb +++ b/spec/integration/articles_spec.rb @@ -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 @@ -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')