ngelx/solidus_import_products

View on GitHub
app/services/solidus_import_products/create_variant.rb

Summary

Maintainability
A
3 hrs
Test Coverage
module SolidusImportProducts
  # CreateVariant
  # This method assumes that some form of checking has already been done to
  # make sure that we do actually want to create a variant.
  # It performs a similar task to a product, but it also must pick up on
  # size/color options
  class CreateVariant
    attr_accessor :product, :variant, :product_information, :logger

    include SolidusImportProducts::ImportHelper

    def self.call(options = {})
      new.call(options)
    end

    def call(args = { product: nil, product_information: nil })
      self.logger = SolidusImportProducts::Logger.instance
      self.product_information = args[:product_information]
      self.product = args[:product]
      return if product_information.nil?

      load_or_initialize_variant

      product_information.each do |field, value|
        if field == :variant_options
          value.each { |variant_field, variant_value| options(variant_field, variant_value) }
        elsif field == :attributes
          value.each { |attr_field, attr_value| variant.send("#{attr_field}=", attr_value) if variant.respond_to?("#{attr_field}=") }
        end
      end

      begin
        variant.save

        product_information[:variant_images].each do |filename|
          find_and_attach_image_to(variant, filename)
        end

        stock_items
        logger.log("Variant of SKU #{variant.sku} successfully imported.\n", :debug)
      rescue StandardError => e
        message = "A variant could not be imported - here is the information we have:\n"
        message += "#{product_information}, #{variant.errors.full_messages.join(', ')}\n"
        message += e.message.to_s
        logger.log(message, :error)
        raise SolidusImportProducts::Exception::VariantError, message
      end
      variant
    end

    private

    def load_or_initialize_variant
      self.variant = Spree::Variant.find_by(sku: product_information[:attributes][:sku])

      if variant
        if variant.product != product
          raise SolidusImportProducts::Exception::SkuError,
                "SKU #{product_information[:attributes][:sku]} should belongs to #{product.inspect} but was #{variant.product.inspect}"
        end
        product_information[:attributes].delete(:id)
      else
        self.variant = product.variants.new(sku: product_information[:attributes][:sku], id: product_information[:attributes][:id])
      end
    end

    def options(field, value)
      return unless value

      option_type = get_or_create_option_type(field)
      option_value = get_or_create_option_value(option_type, value)

      product.option_types << option_type unless product.option_types.include?(option_type)

      variant.option_values << option_value unless variant.option_values.include?(option_value)
    end

    def get_or_create_option_type(field)
      Spree::OptionType.where('name = :field or presentation = :field', field: field.to_s).first ||
        Spree::OptionType.create(name: field, presentation: field)
    end

    def get_or_create_option_value(option_type, value)
      option_type.option_values.where('name = :value or presentation = :value', value: value).first ||
        option_type.option_values.create(presentation: value, name: value)
    end

    def attach_image
      Spree::ProductImport.settings[:image_fields_variants].each do |field|

      end
    end

    def stock_items
      source_location = Spree::StockLocation.find_by(default: true)
      unless source_location
        logger.log('Seems that there are no SourceLocation set right?, so stock will not set.', :warn) if product_information[:attributes][:stock] || product_information[:attributes][:backorderable]
        return
      end
      logger.log("SourceLocation: #{source_location.inspect}", :debug)

      stock_item = variant.stock_items.where(stock_location_id: source_location.id).first_or_initialize

      stock_item.send('backorderable=', product_information[:attributes][:backorderable]) if product_information[:attributes].key?(:backorderable) && stock_item.respond_to?('backorderable=')

      stock_item.set_count_on_hand(product_information[:attributes][:stock]) if product_information[:attributes][:stock]
    end
  end
end