From f65852d994d8fc7af2ddada13688224b33062d64 Mon Sep 17 00:00:00 2001 From: Matt-Yorkley <9029026+Matt-Yorkley@users.noreply.github.com> Date: Wed, 6 Sep 2023 18:42:24 +0100 Subject: [PATCH] Migrate data to fix records with missing unit_presentation --- ...06165200_migrate_imported_variants_data.rb | 181 ++++++++++++++++++ db/schema.rb | 2 +- 2 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20230906165200_migrate_imported_variants_data.rb diff --git a/db/migrate/20230906165200_migrate_imported_variants_data.rb b/db/migrate/20230906165200_migrate_imported_variants_data.rb new file mode 100644 index 00000000000..42796d44a70 --- /dev/null +++ b/db/migrate/20230906165200_migrate_imported_variants_data.rb @@ -0,0 +1,181 @@ +class MigrateImportedVariantsData < ActiveRecord::Migration[7.0] + class OptionValueNamer + # nameable can be either a Spree::LineItem or a Spree::Variant + def initialize(nameable = nil) + @nameable = nameable + end + + def name + value, unit = option_value_value_unit + separator = value_scaled? ? '' : ' ' + + name_fields = [] + name_fields << "#{value}#{separator}#{unit}" if value.present? && unit.present? + name_fields << @nameable.unit_description if @nameable.unit_description.present? + name_fields.join ' ' + end + + def value + value, = option_value_value_unit + value + end + + def unit + _, unit = option_value_value_unit + unit + end + + private + + def value_scaled? + @nameable.product.variant_unit_scale.present? + end + + def option_value_value_unit + if @nameable.unit_value.present? && @nameable.product&.persisted? + if %w(weight volume).include? @nameable.product.variant_unit + value, unit_name = option_value_value_unit_scaled + else + value = @nameable.unit_value + unit_name = pluralize(@nameable.product.variant_unit_name, value) + end + + value = value.to_i if value == value.to_i + + else + value = unit_name = nil + end + + [value, unit_name] + end + + def option_value_value_unit_scaled + unit_scale, unit_name = scale_for_unit_value + + value = (@nameable.unit_value / unit_scale).to_d.truncate(2) + + [value, unit_name] + end + + def scale_for_unit_value + WeightsAndMeasures.new(@nameable).scale_for_unit_value + end + + def pluralize(unit_name, count) + OpenFoodNetwork::I18nInflections.pluralize(unit_name, count) + end + end + + module VariantAndLineItemNaming + def options_text + return unit_presentation unless variant_unit == "weight" + return display_as if has_attribute?(:display_as) && display_as.present? + return variant.display_as if variant_display_as? + + unit_presentation + end + + def variant_display_as? + respond_to?(:variant) && variant.present? && + variant.respond_to?(:display_as) && variant.display_as.present? + end + + def product_and_full_name + return product.name if full_name.blank? + return "#{product.name} - #{full_name}" unless full_name.start_with?(product.name) + + full_name + end + + # Used like "product.name - full_name", preferably using product_and_full_name method above. + # This returns, for a product with name "Bread": + # Bread - 1kg # if display_name blank + # Bread - Spelt Sourdough, 1kg # if display_name is "Spelt Sourdough, 1kg" + # Bread - 1kg Spelt Sourdough # if unit_to_display is "1kg Spelt Sourdough" + # if display_name is "Spelt Sourdough" and unit_to_display is "1kg" + # Bread - Spelt Sourdough (1kg) + def full_name + return unit_to_display if display_name.blank? + return display_name if display_name.downcase.include? unit_to_display.downcase + return unit_to_display if unit_to_display.downcase.include? display_name.downcase + + "#{display_name} (#{unit_to_display})" + end + + def name_to_display + return product.name if display_name.blank? + + display_name + end + + def unit_to_display + return display_as if has_attribute?(:display_as) && display_as.present? + return variant.display_as if variant_display_as? + + options_text.to_s + end + + def assign_units + assign_attributes(unit_value_attributes) + end + + def update_units + update_columns(unit_value_attributes) + end + + def unit_value_attributes + units = { unit_presentation: option_value_name } + units.merge!(variant_unit: product.variant_unit) if has_attribute?(:variant_unit) + units + end + + def weight_from_unit_value + (unit_value || 0) / 1000 if product.variant_unit == 'weight' + end + + private + + def option_value_name + return display_as if has_attribute?(:display_as) && display_as.present? + + OptionValueNamer.new(self).name + end + end + + class Variant < ActiveRecord::Base + include VariantAndLineItemNaming + + belongs_to :product + + self.table_name = "spree_variants" + end + + class Product < ActiveRecord::Base + has_many :variants + + self.table_name = "spree_products" + end + + def up + migrate_variant_unit + + Variant.where(unit_presentation: "").where.not(import_date: nil).each do |variant| + variant.update_columns( + variant.unit_value_attributes.merge(updated_at: Time.zone.now) + ) + end + end + + private + + def migrate_variant_unit + ActiveRecord::Base.connection.execute(<<-SQL + UPDATE spree_variants + SET variant_unit = spree_products.variant_unit + FROM spree_products + WHERE spree_variants.product_id = spree_products.id + AND spree_variants.variant_unit IS NULL + SQL + ) + end +end diff --git a/db/schema.rb b/db/schema.rb index 0abe6927454..cef7875e9fd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_08_09_201542) do +ActiveRecord::Schema[7.0].define(version: 2023_09_06_165200) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" enable_extension "plpgsql"