| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426 |
- 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
- has_many :promotions
- 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 máximo de caracteres para el SKU 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 código 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.not(products: { status: 0 }).order(" products.status ASC, products.name ASC") }
- scope :activos, -> { where(status: 1).order("products.name ASC") }
- scope :activos_children, -> { activos.where(is_parent: false).order("products.name ASC") }
- scope :vigentes_parents, -> { vigentes.where("parent_id IS NULL") }
- scope :name_sku_barcode_like, ->(name) { activos.where("is_parent = ? and (name ilike ? or sku ilike ? or barcode ilike ?)", false, "%#{name}%", "%#{name}%", "%#{name}%") }
- scope :name_sku_barcode_attribute_like, ->(name, attributes_string) { activos.where("is_parent = ? and (name ilike ? or sku ilike ? or barcode ilike ?) #{attributes_string}", false, "%#{name}%", "%#{name}%", "%#{name}%") }
- # para special_prices
- scope :name_sku_barcode_like_sp, ->(name) { activos.where("is_parent = ? and (name ilike ? or sku ilike ? or barcode ilike ?)", true, "%#{name}%", "%#{name}%", "%#{name}%") }
- def name_with_sku
- sku.to_s + " - " + name.to_s
- end
- def get_promotion
- category_id = categories[0].parent.id
- category_array = [category_id, categories.ids].flatten
- product_ids = [id, parent_id]
- promos = Promotion.where("product_id IN (?) OR category_id IN (?)", product_ids, category_array).order("percent, id DESC").vigentes.first
- end
- def display_sku_name_attributes
- sku.to_s + " | " + name.to_s + " | " + display_attributes.to_s
- end
- def full_display
- show_name = name + "\n" + "SKU: " + sku + "\n"
- show_name += "\n" + display_attributes.to_s if parent_id.present?
- show_name += " Código de barras: " + barcode if parent_id.present? && barcode.present?
- show_name
- 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)
- available = get_available_in_pointsale(pointsale_id)
- if available.present? && available.price_sale.present?
- available.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
- # rubocop:disable Metrics/BlockLength
- def save_variants(current_user)
- Thread.new do
- 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
- if current_user.usertype == 'G'
- AvailableProduct.create(product_id: @products_variant.id, pointsale_id: current_user.pointsale_id, stock: 0)
- end
- end
- end
- end
- end
- # rubocop:enable Metrics/BlockLength
- 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
- # rubocop:disable Metrics/BlockLength
- def save_new_attributes(new_sizes, new_colors, new_styles)
- Thread.new do
- 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
- end
- # rubocop:enable Metrics/BlockLength
- def children
- Product.where("parent_id = ? and status != ? ", id, 0)
- 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? ? value.to_s : attributes.to_s + " " + value.to_s
- end
- end
- attributes.upcase
- 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" || user.usertype == "SS"
- 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 != ? and sales.date_sale between ? and ?", 1, 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 != ? and sales.date_sale between ? and ?', 1, beg_period, end_period).order('sum_quantity desc').limit(10).sum(:quantity)
- total_sold = user.pointsale.sales_details.joins(:sale).where('sales.status != ? and sales.date_sale between ? and ?', 1, 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.vigentes.where("parent_id = ?", 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
|