product.rb 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  1. class Product < ActiveRecord::Base
  2. ##--- Asociaciones
  3. belongs_to :unit
  4. has_and_belongs_to_many :categories
  5. has_and_belongs_to_many :pointsales, :join_table => :available_products
  6. has_many :sales_details
  7. has_many :purchase_details
  8. has_many :inventories_moves
  9. has_many :pre_sales
  10. has_many :pre_purchases
  11. has_many :special_prices
  12. has_many :pre_transfers
  13. has_many :available_products
  14. enum status: [:erased, :active, :inactive]
  15. acts_as_taggable_on :sizes, :colors, :styles
  16. accepts_nested_attributes_for :available_products
  17. audited
  18. attr_accessor :skip_sku_validation
  19. mount_uploader :img_product, ImageUploader
  20. ##--- Validaciones previas de guardar
  21. validates :sku, presence: { message: "Debe capturar el SKU del producto." },
  22. length: { maximum: 30, too_long: "El maximo de caracteres debe ser %{count}." },
  23. uniqueness: { message: "El SKU ya fue utilizado, favor de especificar otro." }, unless: :skip_sku_validation
  24. validates_presence_of :name, message: "Debe capturar el nombre del producto."
  25. validates_presence_of :unit_id, message: "Debe seleccionar la unidad de medida correspondiente al producto."
  26. validates_presence_of :category_ids, message: "Debe elegir por lo menos una línea de producto relacionada al producto."
  27. validates :barcode, uniqueness: { message: "El codigo de barras ya fue utilizado, favor de especificar otro." }, :allow_blank => true
  28. #has_attached_file :img_product, :styles => { :medium => "300x300>", :thumb => "100x100>", :small => "70x70>" }, :default_url => "/images/:style/missing.png"
  29. #validates_attachment_content_type :img_product, :content_type => /\Aimage\/.*\Z/
  30. validates_presence_of :name, message: "Debe capturar el nombre del producto."
  31. validates_presence_of :price_sale, message: "Debe capturar el precio de venta del producto."
  32. def valid_categories
  33. self.categories.count > 0
  34. end
  35. def small_img
  36. if img_product?
  37. "#{self.img_product.url(:medium)}"
  38. else
  39. img = "/images/original/missing.png"
  40. end
  41. end
  42. ##--- Tipo de vistas / consultas
  43. scope :vigentes, -> { where( "status != 0").order(" status ASC, name ASC") }
  44. scope :activos, -> { where( "status = 1").order(" name ASC") }
  45. scope :activos_children, -> { where( "status = 1 and is_parent = false ").order("name ASC") }
  46. scope :vigentes_parents, -> { where( "status != 0 and parent_id IS NULL ").order(" status ASC, name ASC") }
  47. 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")}
  48. 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")}
  49. #para special_prices
  50. 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")}
  51. def name_with_sku
  52. "#{sku} - #{name}"
  53. end
  54. def display_sku_name_attributes
  55. "#{sku} | #{name} | #{display_attributes}"
  56. end
  57. def stock_in_pointsale(pointsale_id)
  58. stock = 0;
  59. #checar si hay existencias en los almacenes.
  60. warehouses_stock = WarehouseStock.where(:product_id => self.id)
  61. warehouses_stock.each do |warehouse|
  62. #solamente hacerlo cuando pointsale id sea nil porque es el index de producto
  63. stock += warehouse.stock if pointsale_id == 0
  64. end
  65. #existencias en puntos de venta.
  66. availables = AvailableProduct.where(:product_id => self.id)
  67. availables.each do |available|
  68. if pointsale_id == available.pointsale_id
  69. stock = available.stock
  70. elsif pointsale_id == 0
  71. stock += available.stock
  72. end
  73. end
  74. return stock
  75. end
  76. def can_be_deleted?
  77. if self.is_parent
  78. children_ids = Product.where(:parent_id => self.id).pluck(:id)
  79. in_available = AvailableProduct.where("product_id IN (?) and stock > 0", children_ids).any?
  80. in_warehouse = WarehouseStock.where("product_id IN (?) and stock > 0", children_ids).any?
  81. # si available es true es que NO se puede eliminar
  82. return (in_available == true || in_warehouse == true) ? false : true
  83. else
  84. in_warehouse = WarehouseStock.where("product_id = #{self.id} and stock > 0").any?
  85. in_available = AvailableProduct.where("product_id = #{self.id} and stock > 0").any?
  86. # si available es true es que NO se puede eliminar
  87. return (in_available == true || in_warehouse == true) ? false : true
  88. end
  89. end
  90. def last_sale(pointsale_id)
  91. unless pointsale_id.nil?
  92. last_time = Pointsale.find(pointsale_id).sales_details.where(:product_id => self.id).last
  93. return last_time
  94. end
  95. end
  96. def available_in_pointsale?(pointsale_id)
  97. if pointsales.exists?(:id => pointsale_id)
  98. true
  99. else
  100. false
  101. end
  102. end
  103. def get_available_in_pointsale(pointsale_id)
  104. AvailableProduct.find_by(:pointsale_id => pointsale_id, :product_id => self.id)
  105. end
  106. def get_price_sale(pointsale_id)
  107. if pointsale_id != 0 && available_in_pointsale?(pointsale_id) && !get_available_in_pointsale(pointsale_id).price_sale.nil?
  108. get_available_in_pointsale(pointsale_id).price_sale
  109. else
  110. price_sale
  111. end
  112. end
  113. def pointsales_prices
  114. AvailableProduct.where("product_id = ? and price_sale IS NOT NULL", self.id)
  115. end
  116. def variants_attributes
  117. ActsAsTaggableOn::Tagging.where(:taggable_id => self.id, :taggable_type => 'Product').distinct(:context).select(:context)
  118. end
  119. def get_combinations(combinations, attributes)
  120. if self.presentation
  121. ##--- crear el array de los arrays de atributos
  122. if self.size_list.count > 0
  123. attributes << self.size_list
  124. end
  125. if self.color_list.count > 0
  126. attributes << self.color_list
  127. end
  128. if self.style_list.count > 0
  129. attributes << self.style_list
  130. end
  131. ##--- verificar que atributos tenga mas de una categoria
  132. if attributes.count > 1
  133. ##--- Making combinations from arrays
  134. first_array, *rest_of_arrays = attributes
  135. combinations = first_array.product(*rest_of_arrays)
  136. else
  137. attributes[0].each do |attribute|
  138. combinations << attribute
  139. end
  140. end
  141. return combinations
  142. end
  143. end
  144. def save_variants
  145. combinations = Array.new
  146. attributes = Array.new
  147. combinations = self.get_combinations(combinations, attributes)
  148. ##--- recorrer combinaciones para crear las variantes de productos
  149. unless combinations.nil?
  150. combinations.each_with_index do |combination, index|
  151. @products_variant = Product.new
  152. @products_variant = self.dup
  153. @products_variant.parent_id = self.id
  154. @products_variant.is_parent = false
  155. @products_variant.sku = self.sku + "#{index + 1}" + "A"
  156. @products_variant.category_ids = self.category_ids
  157. attributes_json = {}
  158. if combination.is_a?(Array)
  159. combination.each do |attrib|
  160. attributes_json = @products_variant.assign_attributes_to_variant(attrib, self.id, attributes_json)
  161. end
  162. else
  163. attributes_json = @products_variant.assign_attributes_to_variant(combination, self.id, attributes_json)
  164. end
  165. @products_variant.attributes_json = attributes_json.to_json
  166. @products_variant.save
  167. end
  168. end
  169. end
  170. def assign_attributes_to_variant(attri, parent_id, attributes_json)
  171. attri_id = ActsAsTaggableOn::Tag.where("lower(name) = lower(?)", attri).select(:id).first
  172. get_context = ActsAsTaggableOn::Tagging.where(:tag_id => attri_id, :taggable_id => parent_id, :taggable_type => 'Product').select(:context).first
  173. context = get_context.context
  174. if self.id != parent_id
  175. if context == "sizes"
  176. self.size_list = attri.to_s
  177. elsif context == "colors"
  178. self.color_list = attri.to_s
  179. elsif context == "styles"
  180. self.style_list = attri.to_s
  181. end
  182. end
  183. attributes_json[context] = attri.to_s
  184. return attributes_json
  185. end
  186. def update_attributes_to_variants(new_sizes, new_colors, new_styles)
  187. unless new_sizes.nil?
  188. (JSON.parse new_sizes).each do |s|
  189. sizes.each do |p|
  190. if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
  191. self.size_list.remove(p.name.to_s)
  192. variants = children.tagged_with(p.name.to_s, :on => :sizes, :any => true)
  193. variants.each do |v|
  194. v.size_list.remove(p.name.to_s)
  195. v.size_list.add(s["name"].to_s)
  196. v.save(:validate => false)
  197. end
  198. self.size_list.add(s["name"].to_s)
  199. end
  200. end
  201. end
  202. end
  203. unless new_colors.nil?
  204. (JSON.parse new_colors).each do |s|
  205. colors.each do |p|
  206. if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
  207. self.color_list.remove(p.name.to_s)
  208. variants = children.tagged_with(p.name.to_s, :on => :colors, :any => true)
  209. variants.each do |v|
  210. v.color_list.remove(p.name.to_s)
  211. v.color_list.add(s["name"].to_s)
  212. v.save(:validate => false)
  213. end
  214. self.color_list.add(s["name"].to_s)
  215. end
  216. end
  217. end
  218. end
  219. unless new_styles.nil?
  220. (JSON.parse new_styles).each do |s|
  221. styles.each do |p|
  222. if p.id.to_s == s["id"].to_s && p.name.to_s != s["name"].to_s
  223. self.style_list.remove(p.name.to_s)
  224. variants = children.tagged_with(p.name.to_s, :on => :styles, :any => true)
  225. variants.each do |v|
  226. v.style_list.remove(p.name.to_s)
  227. v.style_list.add(s["name"].to_s)
  228. v.save(:validate => false)
  229. end
  230. self.style_list.add(s["name"].to_s)
  231. end
  232. end
  233. end
  234. end
  235. if self.save(:validate => false)
  236. self.children.each do |variant|
  237. attributes_json = {}
  238. attributes_json = variant.assign_attributes_to_variant(variant.size_list, self.id, attributes_json) unless variant.size_list.count == 0
  239. attributes_json = variant.assign_attributes_to_variant(variant.color_list, self.id, attributes_json) unless variant.color_list.count == 0
  240. attributes_json = variant.assign_attributes_to_variant(variant.style_list, self.id, attributes_json) unless variant.style_list.count == 0
  241. variant.attributes_json = attributes_json.to_json
  242. variant.save(:validate => false)
  243. end
  244. end
  245. end
  246. def save_new_attributes(new_sizes, new_colors, new_styles)
  247. combinations = Array.new
  248. attributes = Array.new
  249. unless new_sizes.nil?
  250. new_sizes.each do |s|
  251. self.size_list.add(s.to_s)
  252. self.save(:validate => false)
  253. end
  254. end
  255. unless new_colors.nil?
  256. new_colors.each do |s|
  257. self.color_list.add(s.to_s)
  258. self.save(:validate => false)
  259. end
  260. end
  261. unless new_styles.nil?
  262. new_styles.each do |s|
  263. self.style_list.add(s.to_s)
  264. self.save(:validate => false)
  265. end
  266. end
  267. combinations = self.get_combinations(combinations, attributes)
  268. # self.save(:validate => false)
  269. combinations.each_with_index do |combination, index|
  270. attributes = {}
  271. if combination.is_a?(Array)
  272. combination.each do |c|
  273. attributes = self.assign_attributes_to_variant(c, self.id, attributes)
  274. end
  275. else
  276. attributes = self.assign_attributes_to_variant(combination, self.id, attributes)
  277. end
  278. if self.children.where("attributes_json = ?", attributes.to_json).select(:id).first.nil?
  279. @products_variant = Product.new
  280. @products_variant = self.dup
  281. @products_variant.parent_id = self.id
  282. @products_variant.is_parent = false
  283. @products_variant.sku = self.sku + "#{index + 1}" + "A"
  284. @products_variant.category_ids = self.category_ids
  285. attributes_json = {}
  286. if combination.is_a?(Array)
  287. combination.each do |attrib|
  288. attributes_json = @products_variant.assign_attributes_to_variant(attrib, self.id, attributes_json)
  289. end
  290. else
  291. attributes_json = @products_variant.assign_attributes_to_variant(combination, self.id, attributes_json)
  292. end
  293. @products_variant.attributes_json = attributes_json.to_json
  294. @products_variant.save
  295. end
  296. end
  297. end
  298. def children
  299. Product.where("parent_id = ? and status != 0 ", self.id)
  300. end
  301. def attributes_to_hash
  302. JSON.parse self.attributes_json.gsub('=>', ':')
  303. end
  304. def display_attributes
  305. attributes = ""
  306. if !self.attributes_json.nil?
  307. self.attributes_to_hash.each do |attr, value|
  308. description = I18n.t("dictionary." + attr) + ": #{value}"
  309. attributes = attributes.blank? ? "#{description}" : "#{attributes}, #{description}"
  310. end
  311. end
  312. return attributes
  313. end
  314. def display_attributes_receipt
  315. attributes = ""
  316. if !self.attributes_json.nil?
  317. self.attributes_to_hash.each do |attr, value|
  318. attributes = attributes.blank? ? "#{value}" : "#{attributes} #{value}"
  319. end
  320. end
  321. return attributes
  322. end
  323. def self.most_selled_products(period, user)
  324. #se envia al user porque no se puede acceder al current, esto es, para que el gerente
  325. # vea los mas vendidos de su punto de venta.
  326. all_products = Array.new
  327. if period == 'week'
  328. beg_period = Date.current.beginning_of_week
  329. end_period = Date.current.end_of_week
  330. elsif period == 'month'
  331. beg_period = Date.current.beginning_of_month
  332. end_period = Date.current.end_of_month
  333. end
  334. if user.usertype == 'A'
  335. 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)
  336. 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)
  337. total_sold = SalesDetail.activos.joins(:sale).where('sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
  338. elsif user.usertype == 'G'
  339. 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)
  340. 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)
  341. total_sold = user.pointsale.sales_details.joins(:sale).where('sales.status != 1 and sales.date_sale between ? and ?', beg_period, end_period).sum(:quantity)
  342. end
  343. others = total_sold - total_top_products
  344. if others > 0
  345. quantities_top_products.merge!({'Otros' => others})
  346. end
  347. return quantities_top_products
  348. end
  349. def get_available_children(just_pointsales)
  350. children = Product.where("parent_id = ? and status != 0 ", self.id).pluck(:id)
  351. if just_pointsales
  352. AvailableProduct.where("product_id IN (?)", children).joins(:pointsale).select(:pointsale_id, :name).distinct(:pointsale_id)
  353. else
  354. AvailableProduct.where("product_id IN (?)", children)
  355. end
  356. end
  357. end