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