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 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 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 end