class ArticleUnitsLib DEFAULT_PIECE_UNIT_CODES = %w[PTN STC XCU XCN XSH X43 XST XOK XVA XBX XBH XBE XCX XBJ XUN XOS XDH XBA XFI XBO XBQ XFB XFT XJR XGR XOW X8B XCV XWA XEI XJT XGY XJY XBD XCR XAI XPA XBK XBI XOV XNT XPK XPC XPX X5M XPR XEC X6H X44 XBR XCW XBT XSA XBM XSX XDN XAE XSC XLU X5L XPP XPU XBG XP2 XCK XPT XGI XTU] DEFAULT_METRIC_SCALAR_UNIT_CODES = %w[KGM HGM DJ GRM LTR DLT CLT MLT] DEFAULT_IMPERIAL_SCALAR_UNIT_CODES = %w[LBR ONZ GLL QTI PTI] @un_ece_20_units = YAML.safe_load(ERB.new(File.read(File.expand_path( 'config/units-of-measure/un-ece-20-remastered.yml', Rails.root ))).result) @un_ece_21_units = YAML.safe_load(ERB.new(File.read(File.expand_path('config/units-of-measure/un-ece-21.yml', Rails.root))).result) def self.untranslated_units return @untranslated_units unless @untranslated_units.nil? options = {} @un_ece_20_units.each do |unit| code = unit['CommonCode'] base_unit = unit['conversion']['base_units'].nil? ? nil : unit['conversion']['base_units'][0] options[code] = { name: unit['Name'], description: unit['Description'], baseUnit: base_unit, conversionFactor: unit['conversion']['factor'], symbol: unit['Symbol'] } end @un_ece_21_units.each do |unit| code = 'X' + unit['Code'] name = unit['Name'] name[0] = name[0].downcase options[code] = { name: name, description: unit['Description'], baseUnit: nil, conversionFactor: nil, symbol: unit['Symbol'] } end options.each do |code, option| option[:translation_available] = !ArticleUnitsLib.get_translated_name_for_code(code, default_nil: true).nil? end @untranslated_units = options end def self.unit_translations @unit_translations = {} if @unit_translations.nil? unit_translations_cached_in_current_locale = @unit_translations[I18n.locale] return unit_translations_cached_in_current_locale unless unit_translations_cached_in_current_locale.nil? @unit_translations[I18n.locale] = YAML.safe_load(ERB.new(File.read(File.expand_path( "config/units-of-measure/locales/unece_#{I18n.locale}.yml", Rails.root ))).result) || {} end def self.units @units = {} if @units.nil? units_cached_in_current_locale = @units[I18n.locale] return units_cached_in_current_locale unless units_cached_in_current_locale.nil? @units[I18n.locale] = untranslated_units.to_h do |code, untranslated_unit| translated_name = ArticleUnitsLib.get_translated_name_for_code(code, default_nil: true) unit = untranslated_unit.clone unit[:name] = translated_name || unit[:name] unit[:untranslated] = translated_name.nil? unit[:symbol] = ArticleUnitsLib.get_translated_symbol_for_code(code) unit[:aliases] = ArticleUnitsLib.get_translated_aliases_for_code(code) [code, unit] end end def self.unit_is_si_convertible(code) !units[code]&.dig(:conversionFactor).nil? end def self.human_readable_unit(unit_code) unit = units.to_h[unit_code] unit[:symbol] || unit[:name] end def self.get_translated_name_for_code(code, default_nil: false) return nil if code.blank? unit_translations&.dig('unece_units', code, 'name') || (default_nil ? nil : untranslated_units[code][:name]) end def self.get_translated_symbol_for_code(code) return nil if code.blank? unit_translations&.dig('unece_units', code, 'symbols', 0) || untranslated_units[code][:symbol] end def self.get_translated_aliases_for_code(code) return nil if code.blank? unit_translations&.dig('unece_units', code, 'aliases') end def self.get_code_for_unit_name(name) return nil if name.blank? translation = unit_translations&.dig('unece_units')&.find do |_code, translations| translations['name'] == name end return translation[0] unless translation.nil? matching_unit = units.find do |_code, unit| unit[:name] == name end matching_unit[0] end def self.convert_old_unit(old_compound_unit_str, unit_quantity) return nil if old_compound_unit_str.nil? md = old_compound_unit_str.match(/([0-9]*)x(.*)/) old_compound_unit_str = md[2] if !md.nil? && md[1].to_f == unit_quantity md = old_compound_unit_str.match(%r{^\s*([0-9][0-9,./]*)?\s*([A-Za-z\u00C0-\u017F.]+)\s*$}) return nil if md.nil? unit = get_unit_from_old_str(md[2]) return nil if unit.nil? quantity = get_quantity_from_old_str(md[1]) if quantity == 1 && unit_quantity == 1 { supplier_order_unit: unit, first_ratio: nil, group_order_granularity: 1.0, group_order_unit: unit } else supplier_order_unit = unit.starts_with?('X') && unit != 'XPK' ? 'XPK' : 'XPP' { supplier_order_unit: supplier_order_unit, first_ratio: { quantity: quantity * unit_quantity, unit: unit }, group_order_granularity: unit_quantity > 1 ? quantity : 1.0, group_order_unit: unit_quantity > 1 ? unit : supplier_order_unit } end end def self.get_quantity_from_old_str(quantity_str) return 1 if quantity_str.nil? quantity_str = quantity_str .gsub(',', '.') .gsub(' ', '') division_parts = quantity_str.split('/').map(&:to_f) if division_parts.length == 2 division_parts[0] / division_parts[1] else quantity_str.to_f end end def self.get_unit_from_old_str(old_unit_str) unit_str = old_unit_str.strip.downcase units = ArticleUnitsLib.untranslated_units .sort { |a, b| sort_by_translation_state(a[1], b[1]) } matching_unit_arr = units.select { |key, unit| matches_unit(key, unit, unit_str) } .to_a return nil if matching_unit_arr.empty? matching_unit_arr[0][0] end def self.sort_by_translation_state(unit_a, unit_b) return -1 if unit_a[:translation_available] && !unit_b[:translation_available] return 1 if unit_b[:translation_available] && !unit_a[:translation_available] 0 end def self.matches_unit(unit_code, unit, unit_str) return true if unit[:symbol] == unit_str translation_data = unit_translations&.dig('unece_units', unit_code) return true if translation_data&.dig('symbols')&.include?(unit_str) name = translation_data&.dig('name')&.downcase return true if !name.nil? && name == unit_str aliases = translation_data&.dig('aliases')&.map { |a| a.strip.downcase } !aliases.nil? && aliases.any? { |a| a == unit_str || "#{a}." == unit_str } end end