Browse Source

Fixed error in combinations generated when modifing variants

Dalia Carlon 9 years ago
parent
commit
e737ec8782
1 changed files with 397 additions and 409 deletions
  1. 397 409
      app/models/product.rb

+ 397 - 409
app/models/product.rb

@@ -1,411 +1,399 @@
 class Product < ActiveRecord::Base
-	##--- Asociaciones
-	belongs_to :unit
-	has_and_belongs_to_many :categories
-
-	has_and_belongs_to_many :pointsales,  :join_table => :available_products
-
-	has_many :sales_details
-	has_many :purchase_details
-	has_many :inventories_moves
-	has_many :pre_sales
-	has_many :pre_purchases
-	has_many :special_prices
-	has_many :pre_transfers
-	has_many :available_products
-
-	enum status: [:erased, :active, :inactive]
-
-	acts_as_taggable_on :sizes, :colors, :styles
-	accepts_nested_attributes_for :available_products
-
-	audited
-
-	attr_accessor :skip_sku_validation
-	mount_uploader :img_product, ImageUploader
-
-	##--- Validaciones previas de guardar
-	validates :sku, presence: { message: "Debe capturar el SKU del producto." },
-		length: { maximum: 30, too_long: "El maximo de caracteres debe ser %{count}." },
-		uniqueness: { message: "El SKU ya fue utilizado, favor de especificar otro." }, unless: :skip_sku_validation
-	validates_presence_of :name, message: "Debe capturar el nombre del producto."
-	validates_presence_of :unit_id, message: "Debe seleccionar la unidad de medida correspondiente al producto."
-	validates_presence_of :category_ids, message: "Debe elegir por lo menos una línea de producto relacionada al producto."
-	validates :barcode, uniqueness: { message: "El codigo de barras ya fue utilizado, favor de especificar otro." }, :allow_blank => true
-	#has_attached_file :img_product, :styles => { :medium => "300x300>", :thumb => "100x100>", :small =>  "70x70>" }, :default_url => "/images/:style/missing.png"
-	#validates_attachment_content_type :img_product, :content_type => /\Aimage\/.*\Z/
-	validates_presence_of :name, message: "Debe capturar el nombre del producto."
-	validates_presence_of :price_sale, message: "Debe capturar el precio de venta del producto."
-
-	def valid_categories
-		self.categories.count > 0
-	end
-
-	def small_img
-		if img_product?
-        "#{self.img_product.url(:medium)}"
-	    else
-	    	img = "/images/original/missing.png"
-	    end
-	end
-
-	##--- Tipo de vistas / consultas
-	scope :vigentes, -> { where( "status != 0").order(" status ASC, name ASC") }
-	scope :activos, -> { where( "status = 1").order(" name ASC") }
-	scope :activos_children, -> { where( "status = 1 and is_parent = false ").order("name ASC") }
-	scope :vigentes_parents, -> { where( "status != 0 and parent_id IS NULL ").order(" status ASC, name ASC") }
-	scope :name_sku_barcode_like, -> (name) { where("status = 1 and is_parent = false and (name ilike ? or sku ilike ? or barcode ilike ?)", "%#{name}%", "%#{name}%", "%#{name}%" ).order("name")}
-	scope :name_sku_barcode_attribute_like, -> (name, attribute) { where("status = 1 and is_parent = false and (name ilike ? or sku ilike ? or barcode ilike ?) and attributes_json ilike ?", "%#{name}%", "%#{name}%", "%#{name}%", "%#{attribute}%" ).order("name")}
-	#para special_prices
-	scope :name_sku_barcode_like_sp, -> (name) { where("status = 1 and is_parent = true and (name ilike ? or sku ilike ? or barcode ilike ?)", "%#{name}%", "%#{name}%", "%#{name}%" ).order("name")}
-
-	def name_with_sku
-		"#{sku} - #{name}"
-	end
-
-	def display_sku_name_attributes
-		"#{sku} | #{name} | #{display_attributes}"
-	end
-
-	def stock_in_pointsale(pointsale_id)
-		stock = 0;
-		#checar si hay existencias en los almacenes.
-		warehouses_stock = WarehouseStock.where(:product_id => self.id)
-		warehouses_stock.each do |warehouse|
-			#solamente hacerlo cuando pointsale id sea nil porque es el index de producto
-			stock += warehouse.stock if pointsale_id == 0
-		end
-		#existencias en puntos de venta.
-		availables = AvailableProduct.where(:product_id => self.id)
-		availables.each do |available|
-			if pointsale_id == available.pointsale_id
-				stock = available.stock
-			elsif pointsale_id == 0
-				stock +=  available.stock
-			end
-		end
-		return stock
-	end
-
-	def can_be_deleted?
-		if self.is_parent
-			children_ids = Product.where(:parent_id => self.id).pluck(:id)
-			in_available = AvailableProduct.where("product_id IN (?) and stock > 0", children_ids).any?
-			in_warehouse = WarehouseStock.where("product_id IN (?) and stock > 0", children_ids).any?
-			# si available es true es que NO se puede eliminar
-			return (in_available == true || in_warehouse == true) ? false : true
-		else
-			in_warehouse = WarehouseStock.where("product_id = #{self.id} and stock > 0").any?
-			in_available = AvailableProduct.where("product_id = #{self.id} and stock > 0").any?
-			# si available es true es que NO se puede eliminar
-			return (in_available == true || in_warehouse == true) ? false : true
-		end
-	end
-
-	def last_sale(pointsale_id)
-		unless pointsale_id.nil?
-			last_time = Pointsale.find(pointsale_id).sales_details.where(:product_id => self.id).last
-			return last_time
-		end
-	end
-
-	def available_in_pointsale?(pointsale_id)
-		if pointsales.exists?(:id => pointsale_id)
-			true
-		else
-			false
-		end
-	end
-
-	def get_available_in_pointsale(pointsale_id)
-		AvailableProduct.find_by(:pointsale_id => pointsale_id, :product_id => self.id)
-	end
-
-	def get_price_sale(pointsale_id)
-		if pointsale_id != 0 && available_in_pointsale?(pointsale_id) && !get_available_in_pointsale(pointsale_id).price_sale.nil?
-			get_available_in_pointsale(pointsale_id).price_sale
-		else
-			price_sale
-		end
-	end
-
-	def pointsales_prices
-		AvailableProduct.where("product_id = ? and price_sale IS NOT NULL", self.id)
-	end
-
-	def variants_attributes
-		ActsAsTaggableOn::Tagging.where(:taggable_id => self.id, :taggable_type => 'Product').distinct(:context).select(:context)
-	end
-
-	def get_combinations(combinations, attributes)
-		if self.presentation
-
-			##--- crear el array de los arrays de atributos
-
-			if self.size_list.count > 0
-				attributes << self.size_list
-			end
-			if self.color_list.count > 0
-				attributes << self.color_list
-			end
-			if self.style_list.count > 0
-				attributes << self.style_list
-			end
-
-			##--- verificar que atributos tenga mas de una categoria
-			if attributes.count > 1
-				##--- Making combinations from arrays
-		  		first_array, *rest_of_arrays = attributes
-				combinations = first_array.product(*rest_of_arrays)
-			else
-				attributes[0].each do |attribute|
-					combinations << attribute
-				end
-			end
-
-			return combinations
-		end
-
-	end
-
-	def save_variants
-		combinations = Array.new
-		attributes = Array.new
-		combinations = self.get_combinations(combinations, attributes)
-
-		##--- recorrer combinaciones para crear las variantes de productos
-		unless combinations.nil?
-			combinations.each_with_index do |combination, index|
-				@products_variant = Product.new
-				@products_variant = self.dup
-				@products_variant.parent_id = self.id
-				@products_variant.is_parent = false
-				@products_variant.sku = self.sku + "#{index + 1}" + "A"
-				@products_variant.category_ids = self.category_ids
-				attributes_json = {}
-
-				if combination.is_a?(Array)
-					combination.each do |attrib|
-						attributes_json = @products_variant.assign_attributes_to_variant(attrib, self.id, attributes_json)
-					end
-				else
-					attributes_json = @products_variant.assign_attributes_to_variant(combination, self.id, attributes_json)
-				end
-
-				@products_variant.attributes_json = attributes_json.to_json
-				@products_variant.save
-
-			end
-		end
-
-	end
-
-	def assign_attributes_to_variant(attri, parent_id, attributes_json)
-		attri_id = ActsAsTaggableOn::Tag.where("lower(name) = lower(?)", attri).select(:id).first
-		get_context = ActsAsTaggableOn::Tagging.where(:tag_id => attri_id, :taggable_id => parent_id, :taggable_type => 'Product').select(:context).first
-		context = get_context.context
-
-		if self.id != parent_id
-			if context == "sizes"
-				self.size_list = attri.to_s
-			elsif context == "colors"
-				self.color_list = attri.to_s
-			elsif context == "styles"
-				self.style_list = attri.to_s
-			end
-		end
-		attributes_json[context] = attri.to_s
-		return attributes_json
-	end
-
-	def update_attributes_to_variants(new_sizes, new_colors, new_styles)
-		unless new_sizes.nil?
-			(JSON.parse new_sizes).each do |s|
-				sizes.each do |p|
-					if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
-						self.size_list.remove(p.name.to_s)
-						variants = children.tagged_with(p.name.to_s, :on => :sizes, :any => true)
-						variants.each do |v|
-							v.size_list.remove(p.name.to_s)
-							v.size_list.add(s["name"].to_s)
-							v.save(:validate => false)
-						end
-						self.size_list.add(s["name"].to_s)
-					end
-				end
-			end
-		end
-		unless new_colors.nil?
-			(JSON.parse new_colors).each do |s|
-				colors.each do |p|
-					if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
-						self.color_list.remove(p.name.to_s)
-						variants = children.tagged_with(p.name.to_s, :on => :colors, :any => true)
-						variants.each do |v|
-							v.color_list.remove(p.name.to_s)
-							v.color_list.add(s["name"].to_s)
-							v.save(:validate => false)
-						end
-						self.color_list.add(s["name"].to_s)
-					end
-				end
-			end
-		end
-		unless new_styles.nil?
-			(JSON.parse new_styles).each do |s|
-				styles.each do |p|
-					if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
-						self.style_list.remove(p.name.to_s)
-						variants = children.tagged_with(p.name.to_s, :on => :styles, :any => true)
-						variants.each do |v|
-							v.style_list.remove(p.name.to_s)
-							v.style_list.add(s["name"].to_s)
-							v.save(:validate => false)
-						end
-						self.style_list.add(s["name"].to_s)
-					end
-				end
-			end
-		end
-
-		if self.save(:validate => false)
-			self.children.each do |variant|
-				attributes_json = {}
-
-				attributes_json = variant.assign_attributes_to_variant(variant.size_list, self.id, attributes_json) unless variant.size_list.count == 0
-				attributes_json = variant.assign_attributes_to_variant(variant.color_list, self.id, attributes_json) unless variant.color_list.count == 0
-				attributes_json = variant.assign_attributes_to_variant(variant.style_list, self.id, attributes_json) unless variant.style_list.count == 0
-				variant.attributes_json = attributes_json.to_json
-				variant.save(:validate => false)
-			end
-		end
-
-	end
-
-	def save_new_attributes(new_sizes, new_colors, new_styles)
-		combinations = Array.new
-		attributes = Array.new
-
-		unless new_sizes.nil?
-			new_sizes.each do |s|
-				self.size_list.add(s.to_s)
-				self.save(:validate => false)
-			end
-		end
-		unless new_colors.nil?
-			new_colors.each do |s|
-				self.color_list.add(s.to_s)
-				self.save(:validate => false)
-			end
-		end
-		unless new_styles.nil?
-			new_styles.each do |s|
-				self.style_list.add(s.to_s)
-				self.save(:validate => false)
-			end
-		end
-
-		combinations = self.get_combinations(combinations, attributes)
-
-		# self.save(:validate => false)
-		combinations.each_with_index do |combination, index|
-			attributes = {}
-			if combination.is_a?(Array)
-				combination.each do |c|
-					attributes = self.assign_attributes_to_variant(c, self.id, attributes)
-				end
-			else
-				attributes = self.assign_attributes_to_variant(combination, self.id, attributes)
-			end
-
-			if self.children.where("attributes_json = ?", attributes.to_json).select(:id).first.nil?
-				@products_variant = Product.new
-				@products_variant = self.dup
-				@products_variant.parent_id = self.id
-				@products_variant.is_parent = false
-				@products_variant.sku = self.sku + "#{index + 1}" + "A"
-				@products_variant.category_ids = self.category_ids
-				attributes_json = {}
-
-				if combination.is_a?(Array)
-					combination.each do |attrib|
-						attributes_json = @products_variant.assign_attributes_to_variant(attrib, self.id, attributes_json)
-					end
-				else
-					attributes_json = @products_variant.assign_attributes_to_variant(combination, self.id, attributes_json)
-				end
-
-				@products_variant.attributes_json = attributes_json.to_json
-				@products_variant.save
-			end
-		end
-
-	end
-
-	def children
-		Product.where("parent_id = ? and status != 0 ", self.id)
-	end
-
-	def attributes_to_hash
-		JSON.parse self.attributes_json.gsub('=>', ':')
-	end
-
-	def display_attributes
-		attributes = ""
-		if !self.attributes_json.nil?
-			self.attributes_to_hash.each do |attr, value|
-				description = I18n.t("dictionary." + attr) + ": #{value}"
-				attributes = attributes.blank? ? "#{description}" : "#{attributes}, #{description}"
-			end
-		end
-		return attributes
-	end
-
-	def display_attributes_receipt
-		attributes = ""
-		if !self.attributes_json.nil?
-			self.attributes_to_hash.each do |attr, value|
-				attributes = attributes.blank? ? "#{value}" : "#{attributes} #{value}"
-			end
-		end
-		return attributes
-	end
-
-	def self.most_selled_products(period, user)
-		#se envia al user porque no se puede acceder al current, esto es, para que el gerente
-		# vea los mas vendidos de su punto de venta.
-		all_products = Array.new
-		if period == 'week'
-	      	beg_period = Date.current.beginning_of_week
-	      	end_period = Date.current.end_of_week
-		elsif period == 'month'
-		    beg_period = Date.current.beginning_of_month
-		    end_period = Date.current.end_of_month
-		end
-
-		if user.usertype == 'A'
-			quantities_top_products = SalesDetail.activos.joins(:sale, :product).where("sales.date_sale between ? and ?", beg_period, end_period).group('products.name').order('sum_quantity desc').limit(10).sum(:quantity)
-			total_top_products = SalesDetail.activos.joins(:sale).where('sales.date_sale between ? and ?', beg_period, end_period).order('sum_quantity desc').limit(10).sum(:quantity)
-			total_sold = SalesDetail.activos.joins(:sale).where('sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
-		elsif user.usertype == 'G'
-			quantities_top_products = user.pointsale.sales_details.joins(:sale, :product).where("sales.status != 1 and sales.date_sale between ? and ?", beg_period, end_period).group('products.name').order('sum_quantity desc').limit(10).sum(:quantity)
-			total_top_products = user.pointsale.sales_details.joins(:sale).where('sales.status != 1 and sales.date_sale between ? and ?', beg_period, end_period).order('sum_quantity desc').limit(10).sum(:quantity)
-			total_sold = user.pointsale.sales_details.joins(:sale).where('sales.status != 1 and sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
-		end
-
-		others = total_sold - total_top_products
-		if others > 0
-			quantities_top_products.merge!({'Otros' => others})
-		end
-		return quantities_top_products
-	end
-
-	def get_available_children(just_pointsales)
-		children = Product.where("parent_id = ? and status != 0 ", self.id).pluck(:id)
-		if just_pointsales
-			AvailableProduct.where("product_id IN (?)", children).joins(:pointsale).select(:pointsale_id, :name).distinct(:pointsale_id)
-		else
-			AvailableProduct.where("product_id IN (?)", children)
-		end
-	end
+  ##--- Asociaciones
+  belongs_to :unit
+  has_and_belongs_to_many :categories
+
+  has_and_belongs_to_many :pointsales, join_table: :available_products
+
+  has_many :sales_details
+  has_many :purchase_details
+  has_many :inventories_moves
+  has_many :pre_sales
+  has_many :pre_purchases
+  has_many :special_prices
+  has_many :pre_transfers
+  has_many :available_products
+
+  enum status: [:erased, :active, :inactive]
+
+  acts_as_taggable_on :sizes, :colors, :styles
+  accepts_nested_attributes_for :available_products
+
+  audited
+
+  attr_accessor :skip_sku_validation
+  mount_uploader :img_product, ImageUploader
+
+  ##--- Validaciones previas de guardar
+  validates :sku,
+            presence: { message: "Debe capturar el SKU del producto." },
+            length: { maximum: 30, too_long: "El maximo de caracteres debe ser %{count}." },
+            uniqueness: { message: "El SKU ya fue utilizado, favor de especificar otro." },
+            unless: :skip_sku_validation
+  validates_presence_of :name, message: "Debe capturar el nombre del producto."
+  validates_presence_of :unit_id, message: "Debe seleccionar la unidad de medida correspondiente al producto."
+  validates_presence_of :category_ids, message: "Debe elegir por lo menos una línea de producto relacionada al producto."
+  validates :barcode, uniqueness: { message: "El codigo de barras ya fue utilizado, favor de especificar otro." }, allow_blank: true
+  validates_presence_of :name, message: "Debe capturar el nombre del producto."
+  validates_presence_of :price_sale, message: "Debe capturar el precio de venta del producto."
+
+  def valid_categories
+    categories.count > 0
+  end
+
+  def small_img
+    if img_product?
+      img_product.url(:medium).to_s
+    else
+      img = "/images/original/missing.png"
+    end
+  end
+
+  ##--- Tipo de vistas / consultas
+  scope :vigentes, -> { where("status != 0").order(" status ASC, name ASC") }
+  scope :activos, -> { where("status = 1").order(" name ASC") }
+  scope :activos_children, -> { where("status = 1 and is_parent = false ").order("name ASC") }
+  scope :vigentes_parents, -> { where("status != 0 and parent_id IS NULL ").order(" status ASC, name ASC") }
+  scope :name_sku_barcode_like, ->(name) { where("status = 1 and is_parent = false and (name ilike ? or sku ilike ? or barcode ilike ?)", "%#{name}%", "%#{name}%", "%#{name}%").order("name") }
+  scope :name_sku_barcode_attribute_like, ->(name, attribute) { where("status = 1 and is_parent = false and (name ilike ? or sku ilike ? or barcode ilike ?) and attributes_json ilike ?", "%#{name}%", "%#{name}%", "%#{name}%", "%#{attribute}%").order("name") }
+  # para special_prices
+  scope :name_sku_barcode_like_sp, ->(name) { where("status = 1 and is_parent = true and (name ilike ? or sku ilike ? or barcode ilike ?)", "%#{name}%", "%#{name}%", "%#{name}%").order("name") }
+
+  def name_with_sku
+    sku.to_s + " - " + name.to_s
+  end
+
+  def display_sku_name_attributes
+    sku.to_s + " | " + name.to_s + " | " + display_attributes.to_s
+  end
+
+  def stock_in_pointsale(pointsale_id)
+    stock = 0
+    # checar si hay existencias en los almacenes.
+    warehouses_stock = WarehouseStock.where(product_id: id)
+    warehouses_stock.each do |warehouse|
+      # solamente hacerlo cuando pointsale id sea nil porque es el index de producto
+      stock += warehouse.stock if pointsale_id.zero?
+    end
+    # existencias en puntos de venta.
+    availables = AvailableProduct.where(product_id: id)
+    availables.each do |available|
+      if pointsale_id == available.pointsale_id
+        stock = available.stock
+      elsif pointsale_id.zero?
+        stock +=  available.stock
+      end
+    end
+    stock
+  end
+
+  def can_be_deleted?
+    if is_parent
+      children_ids = Product.where(parent_id: id).pluck(:id)
+      in_available = AvailableProduct.where("product_id IN (?) and stock > 0", children_ids).any?
+      in_warehouse = WarehouseStock.where("product_id IN (?) and stock > 0", children_ids).any?
+    else
+      in_warehouse = WarehouseStock.where("product_id = #{id} and stock > 0").any?
+      in_available = AvailableProduct.where("product_id = #{id} and stock > 0").any?
+    end
+    in_available == true || in_warehouse == true ? false : true
+  end
+
+  def last_sale(pointsale_id)
+    unless pointsale_id.nil?
+      last_time = Pointsale.find(pointsale_id).sales_details.where(product_id: id).last
+    end
+  end
+
+  def available_in_pointsale?(pointsale_id)
+    if pointsales.exists?(id: pointsale_id)
+      true
+    else
+      false
+    end
+  end
+
+  def get_available_in_pointsale(pointsale_id)
+    AvailableProduct.find_by(pointsale_id: pointsale_id, product_id: id)
+  end
+
+  def get_price_sale(pointsale_id)
+    if pointsale_id != 0 && available_in_pointsale?(pointsale_id) && !get_available_in_pointsale(pointsale_id).price_sale.nil?
+      get_available_in_pointsale(pointsale_id).price_sale
+    else
+      price_sale
+    end
+  end
+
+  def pointsales_prices
+    AvailableProduct.where("product_id = ? and price_sale IS NOT NULL", id)
+  end
+
+  def variants_attributes
+    ActsAsTaggableOn::Tagging.where(taggable_id: id, taggable_type: 'Product').distinct(:context).select(:context)
+  end
+
+  def get_combinations(combinations, attributes)
+    if presentation
+
+      ##--- crear el array de los arrays de atributos
+
+      if size_list.count > 0
+        attributes << size_list
+      end
+      if color_list.count > 0
+        attributes << color_list
+      end
+      if style_list.count > 0
+        attributes << style_list
+      end
+
+      ##--- verificar que atributos tenga mas de una categoria
+      if attributes.count > 1
+        ##--- Making combinations from arrays
+        first_array, *rest_of_arrays = attributes
+        combinations = first_array.product(*rest_of_arrays)
+      else
+        attributes[0].each do |attribute|
+          combinations << attribute
+        end
+      end
+
+      combinations
+    end
+  end
+
+  def save_variants
+    combinations = Array.new
+    attributes = Array.new
+    combinations = get_combinations(combinations, attributes)
+
+    ##--- recorrer combinaciones para crear las variantes de productos
+    unless combinations.nil?
+      combinations.each_with_index do |combination, index|
+        @products_variant = Product.new
+        @products_variant = dup
+        @products_variant.parent_id = id
+        @products_variant.is_parent = false
+        @products_variant.sku = sku + (index + 1).to_s + "A"
+        @products_variant.category_ids = category_ids
+        attributes_json = {}
+
+        if combination.is_a?(Array)
+          combination.each do |attrib|
+            attributes_json = @products_variant.assign_attributes_to_variant(attrib, id, attributes_json)
+          end
+        else
+          attributes_json = @products_variant.assign_attributes_to_variant(combination, id, attributes_json)
+        end
+
+        @products_variant.attributes_json = attributes_json.to_json
+        @products_variant.save
+      end
+    end
+  end
+
+  def assign_attributes_to_variant(attri, parent_id, attributes_json)
+    attri_id = ActsAsTaggableOn::Tag.where("lower(name) = lower(?)", attri).select(:id).first
+    get_context = ActsAsTaggableOn::Tagging.where(tag_id: attri_id, taggable_id: parent_id, taggable_type: 'Product').select(:context).first
+    context = get_context.context
+
+    if id != parent_id
+      if context == "sizes"
+        self.size_list = attri.to_s
+      elsif context == "colors"
+        self.color_list = attri.to_s
+      elsif context == "styles"
+        self.style_list = attri.to_s
+      end
+    end
+    attributes_json[context] = attri.to_s
+    attributes_json
+  end
+
+  def update_attributes_to_variants(new_sizes, new_colors, new_styles)
+    unless new_sizes.nil?
+      (JSON.parse new_sizes).each do |s|
+        sizes.each do |p|
+          next unless p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          # if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          size_list.remove(p.name.to_s)
+          variants = children.tagged_with(p.name.to_s, on: :sizes, any: true)
+          variants.each do |v|
+            v.size_list.remove(p.name.to_s)
+            v.size_list.add(s["name"].to_s)
+            v.save(validate: false)
+          end
+          size_list.add(s["name"].to_s)
+        end
+      end
+    end
+    unless new_colors.nil?
+      (JSON.parse new_colors).each do |s|
+        colors.each do |p|
+          next unless p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          # if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          color_list.remove(p.name.to_s)
+          variants = children.tagged_with(p.name.to_s, on: :colors, any: true)
+          variants.each do |v|
+            v.color_list.remove(p.name.to_s)
+            v.color_list.add(s["name"].to_s)
+            v.save(validate: false)
+          end
+          color_list.add(s["name"].to_s)
+        end
+      end
+    end
+    unless new_styles.nil?
+      (JSON.parse new_styles).each do |s|
+        styles.each do |p|
+          next unless p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          # if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
+          style_list.remove(p.name.to_s)
+          variants = children.tagged_with(p.name.to_s, on: :styles, any: true)
+          variants.each do |v|
+            v.style_list.remove(p.name.to_s)
+            v.style_list.add(s["name"].to_s)
+            v.save(validate: false)
+          end
+          style_list.add(s["name"].to_s)
+        end
+      end
+    end
+
+    if save(validate: false)
+      children.each do |variant|
+        attributes_json = {}
+
+        attributes_json = variant.assign_attributes_to_variant(variant.size_list, id, attributes_json) unless variant.size_list.count.zero?
+        attributes_json = variant.assign_attributes_to_variant(variant.color_list, id, attributes_json) unless variant.color_list.count.zero?
+        attributes_json = variant.assign_attributes_to_variant(variant.style_list, id, attributes_json) unless variant.style_list.count.zero?
+        variant.attributes_json = attributes_json.to_json
+        variant.save(validate: false)
+      end
+    end
+  end
+
+  def save_new_attributes(new_sizes, new_colors, new_styles)
+    combinations = Array.new
+    attributes = Array.new
+
+    unless new_sizes.nil?
+      new_sizes.each do |s|
+        size_list.add(s.to_s)
+        save(validate: false)
+      end
+    end
+    unless new_colors.nil?
+      new_colors.each do |s|
+        color_list.add(s.to_s)
+        save(validate: false)
+      end
+    end
+    unless new_styles.nil?
+      new_styles.each do |s|
+        style_list.add(s.to_s)
+        save(validate: false)
+      end
+    end
+
+    combinations = get_combinations(combinations, attributes)
+    # self.save(:validate: false)
+    combinations.each_with_index do |combination, index|
+      attributes = {}
+      if combination.is_a?(Array)
+        combination.each do |c|
+          attributes = assign_attributes_to_variant(c, id, attributes)
+        end
+      else
+        attributes = assign_attributes_to_variant(combination, id, attributes)
+      end
+
+      next unless children.where("attributes_json = ?", attributes.to_json).select(:id).first.nil?
+      # if children.where("attributes_json = ?", attributes.to_json).select(:id).first.nil?
+      @products_variant = Product.new
+      @products_variant = dup
+      @products_variant.parent_id = id
+      @products_variant.is_parent = false
+      @products_variant.sku = sku + (index + 1).to_s + "A"
+      @products_variant.category_ids = category_ids
+      attrs_json = {}
+      if combination.is_a?(Array)
+        combination.each do |attrib|
+          attrs_json = @products_variant.assign_attributes_to_variant(attrib, id, attrs_json)
+        end
+      else
+        attrs_json = @products_variant.assign_attributes_to_variant(combination, id, attrs_json)
+      end
+      @products_variant.attributes_json = attributes.to_json
+      @products_variant.save(validate: false)
+    end
+  end
+
+  def children
+    Product.where("parent_id = ? and status != 0 ", id)
+  end
+
+  def attributes_to_hash
+    JSON.parse attributes_json.gsub('=>', ':')
+  end
+
+  def display_attributes
+    attributes = ""
+    unless attributes_json.nil?
+      attributes_to_hash.each do |attr_, value|
+        description = I18n.t("dictionary." + attr_) + ": #{value}"
+        attributes = attributes.blank? ? description.to_s : attributes.to_s + ", " + description.to_s
+      end
+    end
+    attributes
+  end
+
+  def display_attributes_receipt
+    attributes = ""
+    unless attributes_json.nil?
+      attributes_to_hash.each do |_attr_, value|
+        attributes = attributes.blank? ? values.to_s : attributes.to_s + value.to_s
+      end
+    end
+    attributes
+  end
+
+  def self.most_selled_products(period, user)
+    # se envia al user porque no se puede acceder al current, esto es, para que el gerente
+    # vea los mas vendidos de su punto de venta.
+    all_products = Array.new
+    if period == 'week'
+      beg_period = Date.current.beginning_of_week
+      end_period = Date.current.end_of_week
+    elsif period == 'month'
+      beg_period = Date.current.beginning_of_month
+      end_period = Date.current.end_of_month
+    end
+
+    if user.usertype == 'A'
+      quantities_top_products = SalesDetail.activos.joins(:sale, :product).where("sales.date_sale between ? and ?", beg_period, end_period).group('products.name').order('sum_quantity desc').limit(10).sum(:quantity)
+      total_top_products = SalesDetail.activos.joins(:sale).where('sales.date_sale between ? and ?', beg_period, end_period).order('sum_quantity desc').limit(10).sum(:quantity)
+      total_sold = SalesDetail.activos.joins(:sale).where('sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
+    elsif user.usertype == 'G'
+      quantities_top_products = user.pointsale.sales_details.joins(:sale, :product).where("sales.status != 1 and sales.date_sale between ? and ?", beg_period, end_period).group('products.name').order('sum_quantity desc').limit(10).sum(:quantity)
+      total_top_products = user.pointsale.sales_details.joins(:sale).where('sales.status != 1 and sales.date_sale between ? and ?', beg_period, end_period).order('sum_quantity desc').limit(10).sum(:quantity)
+      total_sold = user.pointsale.sales_details.joins(:sale).where('sales.status != 1 and sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
+    end
+
+    others = total_sold - total_top_products
+    if others > 0
+      quantities_top_products[:Otros] = others
+    end
+    quantities_top_products
+  end
+
+  def get_available_children(just_pointsales)
+    children = Product.where("parent_id = ? and status != 0 ", id).pluck(:id)
+    if just_pointsales
+      AvailableProduct.where("product_id IN (?)", children).joins(:pointsale).select(:pointsale_id, :name).distinct(:pointsale_id)
+    else
+      AvailableProduct.where("product_id IN (?)", children)
+    end
+  end
 end