浏览代码

Merge branch 'apply_rubocop' of SML/pos-boutique into master

Jose Miguel Ledon 7 年之前
父节点
当前提交
c11d8d77f0
共有 42 个文件被更改,包括 1271 次插入702 次删除
  1. 3 0
      app/assets/javascripts/promotions.coffee
  2. 3 0
      app/assets/stylesheets/promotions.scss
  3. 60 55
      app/controllers/application_controller.rb
  4. 74 0
      app/controllers/promotions_controller.rb
  5. 25 13
      app/controllers/sales_controller.rb
  6. 10 9
      app/controllers/users_controller.rb
  7. 2 0
      app/helpers/promotions_helper.rb
  8. 18 11
      app/models/ability.rb
  9. 26 11
      app/models/pre_sale.rb
  10. 11 3
      app/models/product.rb
  11. 63 0
      app/models/promotion.rb
  12. 4 4
      app/models/user.rb
  13. 43 44
      app/views/available_products/_form.html.erb
  14. 1 1
      app/views/available_products/initial_stock.html.erb
  15. 1 1
      app/views/available_products/stock.html.erb
  16. 1 1
      app/views/cash_outs/index.html.erb
  17. 5 3
      app/views/expenses/_expenses_for_manager.html.erb
  18. 1 1
      app/views/expenses/_form.html.erb
  19. 2 3
      app/views/expenses/new.html.erb
  20. 11 1
      app/views/layouts/application.html.erb
  21. 1 1
      app/views/pos_configs/index.html.erb
  22. 1 1
      app/views/pre_sales/_pre_sale.html.erb
  23. 262 0
      app/views/promotions/_form.html.erb
  24. 116 0
      app/views/promotions/index.html.erb
  25. 54 0
      app/views/promotions/new.html.erb
  26. 260 194
      app/views/sales/sales_per_month_report.html.erb
  27. 1 1
      app/views/transfers/_index_for_admin.html.erb
  28. 31 75
      app/views/transfers/_index_for_cashier.html.erb
  29. 6 6
      app/views/transfers/_index_for_manager.html.erb
  30. 8 8
      app/views/transfers/index.html.erb
  31. 12 24
      app/views/transfers/receipt.pdf.erb
  32. 5 7
      app/views/users/_form.html.erb
  33. 1 0
      config/initializers/inflections.rb
  34. 2 1
      config/locales/en.yml
  35. 78 213
      config/navigation.rb
  36. 3 0
      config/routes.rb
  37. 14 0
      db/migrate/20180816020521_create_promotions.rb
  38. 5 0
      db/migrate/20180816022444_add_promotion_id_to_pre_sale.rb
  39. 6 0
      db/migrate/20180816171401_add_unit_price_w_discount_to_pre_sale.rb
  40. 5 0
      db/migrate/20180816181027_add_sub_category_to_promotion.rb
  41. 5 0
      db/migrate/20180817233420_add_haggle_percent_to_pre_sale.rb
  42. 31 10
      db/schema.rb

+ 3 - 0
app/assets/javascripts/promotions.coffee

@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/

+ 3 - 0
app/assets/stylesheets/promotions.scss

@@ -0,0 +1,3 @@
+// Place all the styles related to the promotions controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/

+ 60 - 55
app/controllers/application_controller.rb

@@ -35,6 +35,19 @@ class ApplicationController < ActionController::Base
     render json: SpmxCounty.where("state_id = ?", params[:state_id])
   end
 
+  def find_products
+    products =
+      if params[:variants] == "true" && params[:query].include?(":")
+        attributes = query_for_variants(params[:query])
+        Product.name_sku_barcode_attribute_like(@product_name, attributes).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
+      elsif params[:variants] == "true"
+        Product.name_sku_barcode_like(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
+      else
+        Product.name_sku_barcode_like_sp(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes, :display_sku_name_attributes])
+      end
+    render json: products
+  end
+
   def find
     query = params[:query]
     if query.include? ':' # search with attributes
@@ -65,6 +78,34 @@ class ApplicationController < ActionController::Base
     render json: query.include?(":") ? Product.name_sku_barcode_attribute_like(product_name, attrs_query_string).limit(30).to_json(methods: [:small_img, :display_attributes]) : Product.name_sku_barcode_like(params[:query]).limit(30).to_json(methods: [:small_img, :display_attributes])
   end
 
+  def query_for_variants(query)
+    product_query =
+      if query.include? ':' # search with attributes
+        query_array = query.split(':')
+        @product_name = query_array[0]
+        query_array.shift # delete the name of the product from the array to iterate the attributes
+        attrs_query_string = ''
+        query_array.each do |attribute|
+          next if attribute.nil?
+          attr_type =
+            case attribute[0]
+            when 'c'
+              'colors'
+            when 't'
+              'sizes'
+            when 'e'
+              'styles'
+            end
+          attribute[0] = "" # delete the attribute type character
+          attrs_query_string.concat(" AND attributes_json::json->>'#{attr_type}' ilike '%#{attribute}%'")
+        end
+        attrs_query_string
+      else
+        query
+      end
+    product_query
+  end
+
   # para special_prices
   def find_sp
     query = params[:query]
@@ -82,75 +123,39 @@ class ApplicationController < ActionController::Base
       else
         Pointsale.find(current_user.pointsale_id).products
       end
-
-    if query.include? ':' # search with attributes
-      query_array = query.split(':')
-      product_name = query_array[0]
-      query_array.shift # delete the name of the product from the array to iterate the attributes
-      attrs_query_string = ''
-      query_array.each do |attribute|
-        next if attribute.nil?
-        attr_type =
-          case attribute[0]
-          when 'c'
-            'colors'
-          when 't'
-            'sizes'
-          when 'e'
-            'styles'
-          end
-        attribute[0] = "" # delete the attribute type character
-        attrs_query_string.concat(" AND attributes_json::json->>'#{attr_type}' ilike '%#{attribute}%'")
+    attributes = query_for_variants(params[:query])
+    consult =
+      if query.include? ':'
+        location.name_sku_barcode_attribute_like(@product_name, attributes)
+      else
+        location.name_sku_barcode_like(params[:query])
       end
-      consult = location.name_sku_barcode_attribute_like(product_name, attrs_query_string)
-    else
-      product_name = query
-      consult = location.name_sku_barcode_like(params[:query])
-    end
 
     render json: consult.where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
   end
 
-  # rubocop:disable Metrics/BlockNesting
   def find_from_stock_by_pointsale
     if params[:pointsale_id].present?
       id = params[:pointsale_id][2, params[:pointsale_id].length]
+      attributes = query_for_variants(params[:query])
       query = params[:query]
-      if query.include? ':' # search with attributes
-        query_array = query.split(':')
-        product_name = query_array[0]
-        query_array.shift # delete the name of the product from the array to iterate the attributes
-        attrs_query_string = ''
-        query_array.each do |attribute|
-          if attribute.present?
-            attr_type = case attribute[0]
-                        when 'c'
-                          'colors'
-                        when 't'
-                          'sizes'
-                        when 'e'
-                          'styles'
-                        end
-            attribute[0] = "" # delete the attribute type character
-            attrs_query_string.concat(" AND attributes_json::json->>'#{attr_type}' ilike '%#{attribute}%'")
-          else
-            next
-          end
+      location =
+        if params[:pointsale_id].first == "P"
+          Pointsale.find(id).products
+        else
+          Warehouse.find(id).products
         end
-      else
-        product_name = query
-      end
-
-      if params[:pointsale_id].first == 'P'
-        render json: query.include?(":") ? Pointsale.find(id).products.name_sku_barcode_attribute_like(product_name, attrs_query_string).where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes]) : Pointsale.find(id).products.name_sku_barcode_like(params[:query]).where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
-      else
-        render json: query.include?(":") ? Warehouse.find(id).products.name_sku_barcode_attribute_like(product_name, attrs_query_string).where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes]) : Warehouse.find(id).products.name_sku_barcode_like(params[:query]).where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
-      end
+      consult =
+        if query.include?(":")
+          location.name_sku_barcode_attribute_like(@product_name, attributes)
+        else
+          location.name_sku_barcode_like(params[:query])
+        end
+      render json: consult.where("stock > 0").limit(30).to_json(methods: [:small_img, :display_attributes])
     else
       render json: {}
     end
   end
-  # rubocop:enable Metrics/BlockNesting
 
   def get_subcategories
     render json: params[:category_id] != '0' ? Category.activos.where("parent_id = ?", params[:category_id]) : Category.activos.where('parent_id != 0')

+ 74 - 0
app/controllers/promotions_controller.rb

@@ -0,0 +1,74 @@
+class PromotionsController < ApplicationController
+  ##--- Abilities
+  load_and_authorize_resource
+
+  add_breadcrumb I18n.t("breadcrumbs." + controller_name), :promotions_path
+  add_breadcrumb "Nueva " + I18n.t("breadcrumbs." + controller_name).singularize, :new_promotion_path, only: :new
+
+  before_action :set_promotion, only: [:show, :destroy]
+
+  # GET /promotions
+  # GET /promotions.json
+  def index
+    @promotions = Promotion.all.order('id desc')
+    @promotions.map(&:verify_promotion)
+  end
+
+  # GET /promotions/1
+  # GET /promotions/1.json
+  def show; end
+
+  # GET /promotions/new
+  def new
+    @promotion = Promotion.new
+    @promotion.percent = nil
+  end
+
+  # POST /promotions
+  # POST /promotions.json
+  def create
+    @promotion = Promotion.new(promotion_params)
+    source =
+      if @promotion.category_id.present? || @promotion.subcategory_id.to_i != 0
+        "a la #{@promotion.subcategory_id.to_i != 0 ? 'sublínea' : 'línea' } #{@promotion.subcategory_id.to_i != 0 ? Category.find(@promotion.subcategory_id).category : @promotion.category.category}"
+      elsif @promotion.product.present?
+        "al producto #{@promotion.product.name}"
+      end
+    respond_to do |format|
+      if @promotion.save
+        message = "Promoción de descuento del #{@promotion.percent}% agregado #{source} del #{@promotion.start_date.strftime("%d/%m/%Y")} al #{@promotion.end_date.strftime("%d/%m/%Y")}"
+        @promotion.audit_comment = message
+        format.html { redirect_to promotions_path, notice: message }
+        format.json { render :show, status: :created, location: @promotion }
+      else
+        @filter = params[:filter]
+        format.html { render :new }
+        format.json { render json: @promotion.errors, status: :unprocessable_entity }
+      end
+    end
+  end
+
+  # DELETE /promotions/1
+  # DELETE /promotions/1.json
+  def destroy
+    message = "Descuento eliminado correctamente"
+    @promotion.audit_comment = message
+    @promotion.destroy
+    respond_to do |format|
+      format.html { redirect_to promotions_url, notice: message }
+      format.json { head :no_content }
+    end
+  end
+
+  private
+
+  # Use callbacks to share common setup or constraints between actions.
+  def set_promotion
+    @promotion = Promotion.find(params[:id])
+  end
+
+  # Never trust parameters from the scary internet, only allow the white list through.
+  def promotion_params
+    params.require(:promotion).permit(:start_date, :end_date, :percent, :product_id, :category_id, :subcategory_id, :status)
+  end
+end

+ 25 - 13
app/controllers/sales_controller.rb

@@ -328,21 +328,22 @@ class SalesController < ApplicationController
   def add_haggle
     @pre_sale = PreSale.find(params[:pre_sale])
     @haggle_percent = !current_user.pointsale.haggle_percent.blank? ? current_user.pointsale.haggle_percent : @pos_config.haggle_in_sale_percent
-    @suggested_haggle = (@haggle_percent.to_f / 100) * @pre_sale.unit_price
+    @suggested_haggle = (@haggle_percent.to_f / 100) * @pre_sale.unit_price_w_discount
   end
 
   def create_haggle
+    @pre_sale = PreSale.find(params[:pre_sale])
+    @haggle_percent = params[:haggle_percent].present? ? params[:haggle_percent].to_f : nil
+    @haggle_quantity = params[:haggle_quantity].present? ? params[:haggle_quantity].to_f : nil
+
+    if @haggle_percent.present?
+      @pre_sale.haggle_percent = @haggle_percent
+    elsif @haggle_quantity.present?
+      @pre_sale.haggle = @haggle_quantity
+    end
+    @pre_sale.get_totals
+
     respond_to do |format|
-      @pre_sale = PreSale.find(params[:pre_sale])
-      @haggle_percent = params[:haggle_percent].present? ? params[:haggle_percent].to_f : nil
-      @haggle_quantity = params[:haggle_quantity].present? ? params[:haggle_quantity].to_f : nil
-
-      if @haggle_percent.present?
-        @pre_sale.haggle = (@haggle_percent / 100) * @pre_sale.total
-      elsif @haggle_quantity.present?
-        @pre_sale.haggle = @haggle_quantity
-      end
-      @pre_sale.get_totals
       if @pre_sale.save
         format.js
       end
@@ -372,6 +373,15 @@ class SalesController < ApplicationController
     # end_date = DateTime.parse(params[:end_date])
     start_date = DateTime.parse(params[:start_date]).in_time_zone(Time.zone).beginning_of_day + 1.days
     end_date = DateTime.parse(params[:end_date]).in_time_zone(Time.zone).end_of_day + 1.days
+    category = params[:category]
+    subcategory = params[:subcategory]
+    ids =
+      if subcategory.present?
+        subcategory
+      elsif category.present?
+        category = Category.find(category)
+        [category.id, category.children.ids].flatten
+      end
     @cash_sales_total = 0
     @reserved_sales_total = 0
     @credit_sales_total = 0
@@ -379,12 +389,14 @@ class SalesController < ApplicationController
     if params[:pointsale_id].present?
       @pointsale = Pointsale.find(params[:pointsale_id])
       @incomes_in_period = @pointsale.cash_registers_moves.joins(:sale).where(move_type: '1', created_at: start_date..end_date)
-      @sales = @pointsale.sales.includes(:sales_details, :user, :seller).activas.where(created_at: start_date..end_date).order("id DESC")
+      @sales = @pointsale.sales.joins(:products, products: :categories).includes(:user, :seller).activas.where(created_at: start_date..end_date).order("id DESC")
     else
       @incomes_in_period = CashRegistersMove.joins(:sale).where(move_type: '1', created_at: start_date..end_date)
-      @sales = Sale.includes(:sales_details, :user, :seller).activas.where(created_at: start_date..end_date).order("id DESC")
+      @sales = Sale.joins(:products, products: :categories).includes(:user, :seller).activas.where(created_at: start_date..end_date).order("id DESC")
     end
 
+    @sales = @sales.where(categories: { id: ids }) if category.present?
+
     @sales_quantity = @sales.size
     @prods_total = SalesDetail.where("sale_id IN (?)", @sales.pluck(:id)).sum(:quantity).round
     # @cash_sales_income = CashRegistersMove.activos.where("sale_id IN (?)", @sales.where(saletype: 1).pluck(:id)).where(created_at: start_date..end_date).sum(:quantity)

+ 10 - 9
app/controllers/users_controller.rb

@@ -44,16 +44,17 @@ class UsersController < ApplicationController
   def create
     @user = User.new(user_params)
     respond_to do |format|
-      message = "Usuario #{@user.userid}"
-      if @user.usertype == 'SS'
-        message += " con perfil SUPER ADMINISTRADOR creado."
-      elsif @user.usertype == "A"
-        message += " con perfil ADMINISTRADOR creado."
-      else
-        message += " creado y asignado al " + (@user.pointsale.present? ? "punto de venta #{@user.pointsale.name}" : "almacén #{@user.warehouse.name}")
-      end
-      @user.audit_comment = message
       if @user.save
+        message = "Usuario #{@user.userid}"
+        message +=
+          if @user.usertype == 'SS'
+            " con perfil SUPER ADMINISTRADOR creado."
+          elsif @user.usertype == "A"
+            " con perfil ADMINISTRADOR creado."
+          else
+            " creado y asignado al " + (@user.pointsale.present? ? "punto de venta #{@user.pointsale.name}" : "almacén #{@user.warehouse.name}")
+          end
+        @user.audit_comment = message
         format.html { redirect_to users_path, success: message }
         format.json { render :show, status: :created, location: @user }
       else

+ 2 - 0
app/helpers/promotions_helper.rb

@@ -0,0 +1,2 @@
+module PromotionsHelper
+end

+ 18 - 11
app/models/ability.rb

@@ -34,36 +34,43 @@ class Ability
 
     if user.usertype == "A" || user.usertype == "SS"
       # Cajas registradoras
-      can :read, [CashRegister, Purchase, PaymentMethod, ProductsReturn, CashOut]
+      can :read, [AvailableProduct, CashOut, CashRegister, PaymentMethod, ProductsReturn]
       # Categorias
-      can :manage, [Category, Customer, BillingInformation, Expensesconcept, Pointsale, Product, Supplier, Unit, Sale, PosConfig, Purchase, SpecialPrice, ProductWaste, Seller, Transfer, Expense, User, Warehouse, Commission, Sellerscommission]
+      can :manage, [BillingInformation, Category, Commission, Customer, Expense, Expensesconcept, Pointsale, PosConfig, Product, Promotion, ProductWaste, Purchase, Sale, Seller, Sellerscommission, SpecialPrice, Supplier, Transfer, Unit, User, Warehouse]
       can [:opened_cash_registers, :find_cash_outs_by_date, :print_receipt], CashOut
-      cannot [:create, :delete, :liquidate_reserve], Sale
-      cannot :create, ProductWaste
+      can :sales_reserved, Sale
+      cannot [:delete, :liquidate_reserve], Sale
+      cannot :create, [ProductWaste, Purchase, Sale]
+      cannot [:debtors, :customer_sales], Customer
     elsif user.usertype == "G"
       # Cajas registradoras
-      can :manage, [CashRegister, Purchase, Product, PrePurchase, Seller, Sale, Expense, ProductWaste, Transfer, OpenCashRegister, CashOut, Supplier, Customer, Credit, CreditPayment, Commission, Sellerscommission, ProductsReturn, Category]
+      can :manage, [CashOut, CashRegister, Category, Credit, CreditPayment, Commission, Customer, Expense, OpenCashRegister, PreTransfer, PrePurchase, Product, Promotion, ProductsReturn, ProductWaste, Purchase, Sale, Seller, Sellerscommission, Supplier, Transfer]
       # Categorias
-      can :read, [SpecialPrice, Expensesconcept, Credit, CreditPayment, Unit]
+      can :read, [AvailableProduct, Credit, CreditPayment, Expensesconcept, PosConfig, SpecialPrice, Unit]
       # Clientes
-      can :cru, [Customer, BillingInformation, Pointsale, User, Warehouse, Credit, CreditPayment, Commission, Sellerscommission]
+      can :cru, [BillingInformation, Commission, Credit, CreditPayment, Customer, Sellerscommission, User, Warehouse]
 
       cannot :opened_cash_registers, CashOut
+      cannot :sales_per_month_report, Sale
+      cannot :product_track, Product
 
     elsif user.usertype == "C"
       # Cajas registradoras
-      can :read, [Product, Pointsale, Customer, BillingInformation, Seller, SpecialPrice, Expensesconcept, Credit, CreditPayment]
+      can :read, [AvailableProduct, BillingInformation, Credit, CreditPayment, Customer, SpecialPrice, Product, Promotion]
       # ventas
       can :cru, [Credit, CreditPayment]
 
-      can :manage, [CashRegister, PreSale, OpenCashRegister, Sale, Customer, Credit, CreditPayment, CashOut, Expense, Transfer, ProductsReturn, ProductWaste]
+      can :manage, [CashOut, Credit, CreditPayment, CashRegister, Customer, Expense, OpenCashRegister, PreSale, PreTransfer, ProductsReturn, ProductWaste, Sale, Transfer]
 
       cannot :opened_cash_registers, CashOut
+      cannot :sales_per_month_report, Sale
+      cannot :index, [CashRegister, Product]
 
     elsif user.usertype == "S"
-      can :read, [CashRegister, Product, Pointsale, Customer, BillingInformation, Seller, SpecialPrice, Expensesconcept]
+      can :read, [AvailableProduct, BillingInformation, CashRegister, PosConfig, Product, Promotion]
+      can :manage, [PreTransfer, ProductWaste, Transfer]
 
-      can :manage, [Transfer, ProductWaste]
+      cannot :index, [CashRegister, Product]
     end
   end
 end

+ 26 - 11
app/models/pre_sale.rb

@@ -17,20 +17,35 @@ class PreSale < ActiveRecord::Base
   # before_save :get_totals
 
   def get_totals
+    promotion = product.get_promotion
     special_price = SpecialPrice.find_by(customer_id: customer_id, product_id: product_id)
-    unless special_price.blank?
-      self.special_price_id = special_price.id
-      discount_per_unit = special_price.get_unit_discount(unit_price)
-      self.discount = discount_per_unit * quantity
-    end
-
-    self.discount += haggle
-    self.amount = quantity * unit_price
+    discount_per_unit =
+      if promotion.present?
+        self.promotion_id = promotion.id
+        (promotion.percent / 100) * unit_price
+      elsif special_price.present?
+        self.special_price_id = special_price.id
+        special_price.get_unit_discount(unit_price)
+      else
+        0
+      end
+    self.unit_price_w_discount = (unit_price - discount_per_unit).round(2)
+    self.discount = (discount_per_unit * quantity).round(2)
+    self.amount = (quantity * unit_price_w_discount)
     self.tax = 0.0
-    if product.include_sale_tax == 1
+    if product.include_sale_tax?
       self.tax = ((PosConfig.first.tax_percent / 100) * amount).round(2)
     end
-    self.total = (amount - discount) + tax
-    return true
+    haggle_discount =
+      if haggle > 0
+        haggle.round(2)
+      elsif haggle_percent > 0
+        ((haggle_percent.to_f / 100) * amount).round(2)
+      else
+        0
+      end
+    self.amount -= haggle_discount if haggle_discount.present?
+    self.total = amount + tax
+    true
   end
 end

+ 11 - 3
app/models/product.rb

@@ -13,6 +13,7 @@ class Product < ActiveRecord::Base
   has_many :special_prices
   has_many :pre_transfers
   has_many :available_products
+  has_many :promotions
 
   enum status: [:erased, :active, :inactive]
 
@@ -27,13 +28,13 @@ class Product < ActiveRecord::Base
   ##--- 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}." },
+            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 codigo de barras ya fue utilizado, favor de especificar otro." }, allow_blank: true
+  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."
 
@@ -63,6 +64,13 @@ class Product < ActiveRecord::Base
     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
@@ -350,7 +358,7 @@ class Product < ActiveRecord::Base
   # rubocop:enable Metrics/BlockLength
 
   def children
-    Product.where("parent_id = ? and status != 0 ", id)
+    Product.where("parent_id = ? and status != ? ", id, 0)
   end
 
   def attributes_to_hash

+ 63 - 0
app/models/promotion.rb

@@ -0,0 +1,63 @@
+class Promotion < ActiveRecord::Base
+  ##--- Llevar registro de Actividad del usuario
+  audited
+
+  belongs_to :product
+  belongs_to :category
+  # belongs_to :subcategory_id, class_name: "Category"
+
+  enum status: [:inactive, :active]
+
+  validates_presence_of :start_date, message: "Debe indicar fecha de inicio de la promoción."
+  validates_presence_of :end_date, message: "Debe indicar fecha de término de la promoción."
+  validates_presence_of :percent, message: "Debe indicar el porcentaje de descuento de la promoción."
+  validates :percent, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 100 }
+  validate :start_end_date, on: [:create]
+
+  validate :start_date_greater_than_end_date, if: proc { |p| p.start_date.present? && p.end_date.present? }
+  validate :promotion_source
+  validate :check_for_duplicates
+
+  scope :activas, -> { where(status: 1) }
+  scope :vigentes, -> { where("? between start_date and end_date", Date.today) }
+
+  def verify_promotion
+    is_active = end_date >= Date.today
+    unless is_active
+      update_attribute(:status, :inactive)
+    end
+  end
+
+  def start_end_date
+    if end_date.present? && end_date < Date.today
+      errors.add(:end_date, "La fecha de fin debe ser mayor a hoy.")
+    end
+  end
+
+  def start_date_greater_than_end_date
+    if end_date < start_date
+      errors.add(:start_date, 'La fecha de inicio debe ser mayor a la fecha de término de la promoción.')
+    end
+  end
+
+  def promotion_source
+    if product_id.blank? && category_id.blank?
+      errors.add(:base, 'Seleccione una opción entre línea o producto.')
+    end
+  end
+
+  def check_for_duplicates
+    source =
+      if category_id.present?
+        "category_id = #{category_id}"
+      elsif subcategory_id.present?
+        "subcategory_id = #{subcategory_id}"
+      elsif product_id.present?
+        "product_id = #{product_id}"
+      end
+    if source
+      exists_already = Promotion.where(source).where("start_date < ? and end_date > ?", end_date, start_date).any?
+      errors.add(:base, 'Ya existe una promoción activa con los parámetros seleccionados') if exists_already
+    end
+  end
+end

+ 4 - 4
app/models/user.rb

@@ -28,10 +28,10 @@ class User < ActiveRecord::Base
   attr_accessor :skip_validations_from_pointsale
   attr_accessor :skip_validations_when_edit
   validates_presence_of :userid, message: "Debe especificar el nombre de usuario, es con el que el usuario iniciará sesión."
-  validates :password, presence: { message: "Debe especificar la contraseña nueva" },  length: { within: Devise.password_length, too_short: "La contraseña no puede ser menor a %{count}." }, unless: :skip_validations_when_edit
+  validates :password, presence: { message: "Debe especificar la contraseña nueva" }, length: { within: Devise.password_length, too_short: "La contraseña no puede ser menor a %{count}." }, unless: :skip_validations_when_edit
   validates :password_confirmation, presence: { message: "Debe confirmar la contraseña nueva" }, unless: :skip_validations_when_edit
   # validates_presence_of :pointsale_id, message: "Debe seleccionar el punto de venta al que pertenecera el usuario.", on: [:create, :update], unless: :skip_validations_from_pointsale
-  validates_presence_of :usertype, message: "Debe seleccionar el tipo de usuario, con este se determinara los permisos que tendrá dentro del sistema.", on: [:create, :update], unless: :skip_validations_from_pointsale
+  validates_presence_of :usertype, message: "Debe seleccionar el tipo de usuario, con este se determinará los permisos que tendrá dentro del sistema.", on: [:create, :update], unless: :skip_validations_from_pointsale
 
   validates :pointsale_id, presence: { message: "Debe seleccionar un almacén o un punto de venta al que pertenecerá el usuario." }, if: proc { |c| c.warehouse_id.blank? && c.usertype != 'A' }, on: [:create, :update], unless: :skip_validations_from_pointsale
   validates :warehouse_id, presence: { message: "Debe seleccionar un almacén o un punto de venta al que pertenecerá el usuario." }, if: proc { |c| c.pointsale_id.blank? && c.usertype != 'A' }, on: [:create, :update], unless: :skip_validations_from_pointsale
@@ -49,9 +49,9 @@ class User < ActiveRecord::Base
   end
 
   def warehouse_or_pointsale
-    if pointsale_id.present?
+    if !pointsale_id.blank?
       self.warehouse_id = nil
-    elsif warehouse_id.present?
+    elsif !warehouse_id.blank?
       self.pointsale_id = nil
     end
   end

+ 43 - 44
app/views/available_products/_form.html.erb

@@ -57,19 +57,19 @@
   }
 
   function updateStock() {
-  var min = parseInt($('#min_stock_general').val());
-  var max = parseInt($('#max_stock_general').val());
-  // checar por campos vacios
-  if(!min || !max ) {
+    var min = parseInt($('#min_stock_general').val());
+    var max = parseInt($('#max_stock_general').val());
+    // checar por campos vacios
+    if(!min || !max ) {
       toastr["error"]("Error, No pueden quedar vacios los campos de mínimo y máximo");
-  } else {
-    //maximo sea menor al minimo
-    if( max < min) {
-      toastr["error"]("Error, El máximo debe de ser mayor al mínimo");
-      //almenos 1 producto seleccionado
-    } else if(rows_selected.length == 0) {
-      toastr["error"]("Error, Se debe seleccionar al menos un producto para actualizar");
     } else {
+      //maximo sea menor al minimo
+      if( max < min) {
+        toastr["error"]("Error, El máximo debe de ser mayor al mínimo");
+        //almenos 1 producto seleccionado
+      } else if(rows_selected.length == 0) {
+        toastr["error"]("Error, Se debe seleccionar al menos un producto para actualizar");
+      } else {
         $.ajax({
           type: "PUT",
           url: "/update_stock",
@@ -84,7 +84,6 @@
             var rows = $(".stock-table").dataTable().fnGetNodes();
             $.each(rows, function(i, row) {
               var id = $(this).attr('id').substring($(this).attr('id').indexOf('_') + 1, $(this).attr('id').length);
-
               if($.inArray(id, rows_selected) != -1) {
                 minInput = $(row).find("td:eq(6)")
                 minInput.text(min);
@@ -92,49 +91,49 @@
                 maxInput.text(max);
               }
             });
-              $('#select-all').click();
-              $('#min_stock_general').val("");
-              $('#max_stock_general').val("");
-              toastr["success"]("Stock mínimo y máximo actualizado.");
+            $('#select-all').click();
+            $('#min_stock_general').val("");
+            $('#max_stock_general').val("");
+            toastr["success"]("Stock mínimo y máximo actualizado.");
           }
         });
+      }
     }
   }
-  }
 
   function initialStock() {
-  var initialStock = parseInt($('#initial_stock_input').val());
-  if (initialStock || initialStock == 0) {
-    if(rows_selected.length > 0) {
-      $.ajax({
-        type: "PUT",
-        url: "/set_initial_stock",
-        dataType: "json",
-        data: {
-          ids: rows_selected,
-          stock: initialStock
-        },
-        success: function(xhr, status, error) {
-          var table = $('.stock-table').dataTable();
-          var rows = $(".stock-table").dataTable().fnGetNodes();
-          $.each(rows, function(i, row) {
-            var id = $(this).attr('id').substring($(this).attr('id').indexOf('_') + 1, $(this).attr('id').length);
+    var initialStock = parseInt($('#initial_stock_input').val());
+    if (initialStock || initialStock == 0) {
+      if(rows_selected.length > 0) {
+        $.ajax({
+          type: "PUT",
+          url: "/set_initial_stock",
+          dataType: "json",
+          data: {
+            ids: rows_selected,
+            stock: initialStock
+          },
+          success: function(xhr, status, error) {
+            var table = $('.stock-table').dataTable();
+            var rows = $(".stock-table").dataTable().fnGetNodes();
+            $.each(rows, function(i, row) {
+              var id = $(this).attr('id').substring($(this).attr('id').indexOf('_') + 1, $(this).attr('id').length);
 
-            if($.inArray(id, rows_selected) != -1) {
-              initialStockInput = $(row).find("td:eq(6)");
-              initialStockInput.text(initialStock);
-            }
-          });
+              if($.inArray(id, rows_selected) != -1) {
+                initialStockInput = $(row).find("td:eq(6)");
+                initialStockInput.text(initialStock);
+              }
+            });
             $('#select-all').click();
             $('#initial_stock').val("");
             toastr["success"]("Stock inicial actualizado.");
-        }
-      });
+          }
+        });
+      } else {
+        toastr["error"]("Error, Se debe seleccionar al menos un producto para actualizar");
+      }
     } else {
-      toastr["error"]("Error, Se debe seleccionar al menos un producto para actualizar");
+      toastr["error"]("Error, NO puede quedar vacío el campo de stock inicial");
     }
-  } else {
-    toastr["error"]("Error, NO puede quedar vacio el campo de stock inicial");
-  }
   }
 </script>

+ 1 - 1
app/views/available_products/initial_stock.html.erb

@@ -33,7 +33,7 @@
                   <h4 class="form-section"> Lista de productos</h4>
                   <div class="portlet-body">
                     <div class="row">
-                      <% unless current_user.usertype == "G" %>
+                      <% unless current_user.usertype == "G" || current_user.usertype == "S" %>
                         <%= label_tag :pointsale, "Puntos de venta", class: "control-label col-md-1 col-sm-2" %>
                         <div class="col-md-3 col-sm-4">
                           <%= select_tag "pointsale", options_from_collection_for_select(@pointsales, :id, :name), class: "form-control input-medium" %>

+ 1 - 1
app/views/available_products/stock.html.erb

@@ -34,7 +34,7 @@
                   <h4 class="form-section"> Lista de productos</h4>
                   <div class="portlet-body">
                     <div class="row">
-                      <% unless current_user.usertype == "G" %>
+                      <% unless current_user.usertype == "G" || current_user.usertype == "S" %>
                         <%= label_tag :pointsale, "Puntos de venta", class: "control-label col-md-1 col-sm-2" %>
                         <div class="col-md-3 col-sm-4">
                           <%= select_tag "pointsale", options_from_collection_for_select(@pointsales, :id, :name), class: "form-control input-medium" %>

+ 1 - 1
app/views/cash_outs/index.html.erb

@@ -44,7 +44,7 @@
                     <span class="caption-subject bold uppercase">Lista de cortes de caja</span>
                   </div>
                    <div class="actions">
-                    <% if can? :create, OpenCashRegister %>
+                    <% if can? :create, OpenCashRegister && !session[:open_cash_register_id].present? %>
                       <%= link_to new_open_cash_register_path, remote: true, class: 'btn btn bold green margin-top pull-right', title: "Abrir caja registradora" do %> Abrir caja <i class="fa fa-plus"></i><% end %> <br>
                     <% end %>
                     <% if can? :get_open_cash_registers, CashOut %>

+ 5 - 3
app/views/expenses/_expenses_for_manager.html.erb

@@ -19,7 +19,7 @@
       <td><%= expense.expense_code %></td>
       <td><%= expense.expensesconcept.name %></td>
       <td><%= expense.open_cash_register.cash_register.name %></td>
-      <td><%= l(expense.created_at, :format => '%d/%B/%Y') %> </td>
+      <td><%= l(expense.created_at, format: '%d/%B/%Y') %> </td>
       <td><%= number_to_currency(expense.quantity, precision: 2) %> </td>
       <td><%= expense.observations %> </td>
       <td>
@@ -33,8 +33,10 @@
         <% end %>
       </td>
       <td class="text-center">
-        <%= expense.active? %>
-          <%= link_to expense, method: :delete, class: "btn btn-icon-only btn-danger", title: "Cancelar egreso", data: { confirm: '¿Está seguro que desea cancelar el egreso?' } do %> <i class="fa fa-ban"></i><% end %>
+        <% if expense.active? %>
+          <%= link_to expense, method: :delete, class: "btn btn-icon-only btn-danger", title: "Cancelar egreso", data: { confirm: '¿Está seguro que desea cancelar el egreso?' } do %>
+            <i class="fa fa-ban"></i>
+          <% end %>
         <% end %>
       </td>
     </tr>

+ 1 - 1
app/views/expenses/_form.html.erb

@@ -2,7 +2,7 @@
   <div class="portlet-body form">
     <% if @expense.errors.any? %>
       <div class="alert alert-danger">
-        <strong>Tiene <%= pluralize(@expense.errors.count, "error") %> no se puede guardar el gasto</strong><br>
+        <strong>Tiene <%= pluralize(@expense.errors.count, "error") %> no se puede guardar el egreso</strong><br>
       </div>
     <% end %>
     <div class="form-body">

+ 2 - 3
app/views/expenses/new.html.erb

@@ -17,7 +17,7 @@
 		<!-- BEGIN PAGE CONTENT BODY -->
 		<div class="page-content">
 			<div class="container-fluid">
-				<%= link_to  expenses_path, {:class=>"btn blue-hoki pull-right margin-bottom-10"} do %>  <i class="fa fa-angle-left "></i> Regresar
+				<%= link_to expenses_path, {:class=>"btn blue-hoki pull-right margin-bottom-10"} do %>  <i class="fa fa-angle-left "></i> Regresar
 				<% end %>
 				<!-- BEGIN PAGE BREADCRUMBS -->
 				<ul class="page-breadcrumb breadcrumb">
@@ -39,7 +39,7 @@
 								<%= render 'form' %>
 							</div>
 						</div>
-					</div>                       
+					</div>
 				</div>
 				<!-- END PAGE CONTENT INNER -->
 			</div>
@@ -49,4 +49,3 @@
 	</div>
 	<!-- END CONTENT -->
 </div>
-<!-- END CONTAINER -->

+ 11 - 1
app/views/layouts/application.html.erb

@@ -41,7 +41,17 @@
 						</div>
 						<!-- END LOGO -->
 						<!-- BEGIN TOP NAVIGATION MENU -->
-						<label style="margin-top: 25px;font-size: 14px;"> Módulo - <%= usertype(current_user) %><br><small><%= current_user.pointsale_id.present? ? "Punto de venta - #{current_user.pointsale.name}" : "" %></small></label>
+						<label style="margin-top: 25px;font-size: 14px;">
+							Módulo - <%= usertype(current_user) %>
+							<br>
+							<small>
+								<% if current_user.pointsale_id.present? %>
+									<%= "Punto de venta - #{current_user.pointsale.name}" %>
+								<% elsif current_user.warehouse_id.present? %>
+									<%= "Almacén - #{current_user.warehouse.name}" %>
+								<% end %>
+							</small>
+						</label>
 						<a href="javascript:;" class="menu-toggler"></a>
 						<div class="top-menu">
 							<ul class="nav navbar-nav pull-right">

+ 1 - 1
app/views/pos_configs/index.html.erb

@@ -31,7 +31,7 @@
                 <div class="portlet-title">
                   <div class="caption">
                     <i class="fa fa-fw fa-cog"></i>
-                    <span class="caption-subject bold uppercase">Párametros generales para los puntos de venta</span>
+                    <span class="caption-subject bold uppercase">Pametros generales para los puntos de venta</span>
                   </div>
                 </div>
                 <%= render 'form' %>

+ 1 - 1
app/views/pre_sales/_pre_sale.html.erb

@@ -19,7 +19,7 @@
   <td><%= pre_sale.discount %></td>
   <td><%= pre_sale.total %></td>
   <td style="width:5%">
-    <% if @pos_config.enable_haggle? && pre_sale.haggle == 0 %>
+    <% if @pos_config.enable_haggle? && pre_sale.haggle.zero? && pre_sale.haggle_percent.zero? %>
       <%= link_to add_sale_haggle_path(pre_sale), :remote => true, :class => "btn btn-icon-only btn-primary hagglebutton", :title=>"Agregar regate" do %>
         <i class="fa fa-tag"></i>
       <% end %>

+ 262 - 0
app/views/promotions/_form.html.erb

@@ -0,0 +1,262 @@
+<%= form_for(@promotion, html: { class: "form-horizontal", id: "promotion_form" }) do |f| %>
+  <div class="portlet-body form">
+    <% if @promotion.errors.any? %>
+      <div class="alert alert-danger">
+        <strong>Tiene <%= pluralize(@promotion.errors.count, "error") %> no se puede guardar la promoción</strong><br>
+        <% @promotion.errors.values.each do |message| %>
+          <%= message.first.to_s %><br>
+           <!--  $("#error_explanation_move").append($("<li />").html("< %= message.first.to_s %>")); -->
+        <% end %>
+      </div>
+    <% end %>
+    <div class="form-body">
+      <div class="alert alert-danger hidden" id="error_explanation_for_promotion"></div>
+      <div class="row">
+        <div class="col-md-12">
+          <!-- fechas -->
+          <div class="form-group">
+            <%= label_tag :begin_date, "Fecha", { class: "col-md-1 control-label" } do %> Del
+              <span class="required">*</span>
+            <% end %>
+            <div class="col-sm-2">
+              <div class='input-group date' id='begin_date'>
+                <!-- <input id="start" type='text' class="form-control"/> -->
+                <%= f.text_field :start_date, { id: 'start', class: 'form-control' } %>
+                <span class="input-group-addon">
+                  <span class="glyphicon glyphicon-calendar"></span>
+                </span>
+                <!-- < %= f.hidden_field :start_date, {id: 'start_date_input'} %> -->
+              </div>
+            </div>
+            <%= label_tag :end_date, "Fecha", { class: "col-md-1 control-label" } do %> Al
+              <span class="required">*</span>
+            <% end %>
+            <div class="col-sm-2">
+              <div class='input-group date' id='end_date'>
+                <!-- <input id="end" type='text' class="form-control"/> -->
+                <%= f.text_field :end_date, { id: 'end', class: 'form-control' } %>
+                <span class="input-group-addon">
+                  <span class="glyphicon glyphicon-calendar"></span>
+                </span>
+                <!-- < %= f.hidden_field :end_date, {id: 'end_date_input'} %> -->
+              </div>
+            </div>
+          </div>
+          <!-- filtro -->
+          <div class="form-group">
+            <div class="col-md-8" style="padding-top:5px">
+              <div class="radio-list">
+                <label class="radio-inline" style="margin-left:10px">
+                  <input type="radio" name="filter" id="category" value="category">Todos los productos de una línea
+                </label>
+                <label class="radio-inline" style="margin-left:37px">
+                  <input type="radio" name="filter" id="products" value="products" checked>Producto específico
+                </label>
+              </div>
+            </div>
+          </div>
+          <div class="form-group">
+            <%= f.label :percent, "", { class: "col-md-2 control-label" } do %> Porcentaje de descuento <span class="required">*</span> <% end %>
+            <div class="col-md-1">
+              <div class="input-group">
+                <%= f.text_field :percent, { class: "form-control mask_decimal input-xsmall", value: 1 } %>
+                <span class="input-group-addon"> % </span>
+              </div>
+            </div>
+          </div>
+          <div class="form-group" id="variants_div">
+            <%= label_tag :variants, "", { class: "col-md-3 control-label" } do %> ¿Buscar por variantes? <span class="required">*</span> <% end %>
+            <div class="col-md-3">
+              <%= check_box_tag(:include_variants, true, true, {
+                class: "make-switch",
+                  data: {
+                    on_color: "success",
+                    off_color: "danger",
+                    on_text: "Si",
+                    off_text: "No"
+                  }
+                }) %>
+            </div>
+          </div>
+          <div class="form-group">
+            <div class="well col-md-6">
+              <!-- linea -->
+              <div class="form-group hidden" id="category_div">
+                <%= f.label :category_id, "", { class: "col-md-3 control-label" } do %> Línea de productos
+                  <span class="required">*</span>
+                <% end %>
+                <div class="col-md-8 select2-bootstrap-prepend">
+                  <%= f.collection_select(:category_id, Category.activos_padre, :id, :category, {}, class: "form-control select2") %>
+                  <span class="help-block">Se agregarán todos los productos de la línea a la tabla.</span>
+                </div>
+              </div>
+              <div class="form-group hidden" id="subcategory_div">
+                <%= f.label :subcategory_id, "Sublínea", { class: "col-md-3 col-sm-3 control-label" } %>
+                <div class="col-md-8 select2-bootstrap-prepend col-sm-4">
+                  <%= f.collection_select(:subcategory_id, {}, :id, :category, {}, class: "form-control select2") %>
+                </div>
+              </div>
+              <!-- producto -->
+              <div class="form-group" id="products_div">
+                <label class="control-label col-md-3" for="typeahead"> Producto
+                  <span class="required">*</span>
+                </label>
+                <div class="input-groupll col-md-8">
+                  <input class="form-control" type="text" id="typeahead" data-option-url="/find_products/%QUERY/VAR">
+                  <span class="help-block">Se puede buscar por No. de parte o Nombre de producto </span>
+                  <%= f.hidden_field :product_id, { id: 'product_input' } %>
+                  <br>
+                  <span id="product_name"><% if @promotion.product.present? %><i>Producto seleccionado: <%= @promotion.product.display_sku_name_attributes %></i><% end %></span>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  <!-- acciones del form -->
+    <div class="form-actions">
+      <div class="row">
+        <div class="col-md-9">
+          <!-- < %= f.submit 'Guardar', { class: "btn green" } %> -->
+          <button type="button" class="btn green" onclick="addPromotion()" id="submit_promotion">Guardar</button>
+          <%= link_to 'Cancelar', promotions_path, { class: "btn default" } %>
+        </div>
+      </div>
+    </div>
+  </div>
+<% end %>
+
+<script type="text/javascript">
+  var selectedProduct;
+  // inicialmente dejar en blanco los selects
+  $(document).on("page:change", function() {
+    <% unless @filter.present? && @filter == "category" %>
+      $("#promotion_category_id").select2("val", "");
+      $("#promotion_subcategory_id").select2("val", "");
+    <% end %>
+  });
+
+  $('input[name="include_variants"]').on('switchChange.bootstrapSwitch', function(event, state) {
+    $(this).val(state);
+  });
+
+  $('#begin_date').datetimepicker({
+    icons: {
+      date: "fa fa-calendar"
+    },
+    format: "DD/MM/YYYY"
+  });
+
+  $('#end_date').datetimepicker({
+    icons: {
+      date: "fa fa-calendar"
+    },
+    format: "DD/MM/YYYY"
+  });
+
+  var bloodhound = new Bloodhound({
+    datumTokenizer: function (d) {
+      return Bloodhound.tokenizers.whitespace(d.value);
+    },
+    queryTokenizer: Bloodhound.tokenizers.whitespace,
+    remote: {
+      url: $('#typeahead').data('option-url'),
+      wildcard: '%QUERY',
+      replace: function(url, uriEncodedQuery) {
+        variants = $("#include_variants").val();
+        return url.replace('VAR', variants).replace('%QUERY', uriEncodedQuery)
+      }
+    }
+  });
+  bloodhound.initialize();
+
+  $('#typeahead').typeahead(
+    {
+      minLength: 3
+    },
+    {
+      displayKey: 'name',
+      source: bloodhound.ttAdapter(),
+      limit: Infinity,
+      templates: {
+        empty: [
+          '<div class="empty-message">',
+            'No se encontró ningún producto. Favor de verificar',
+          '</div>'
+        ].join('\n'),
+        suggestion: function(data) {
+          return '<div class="media">' +
+                  '<div class="pull-left">' +
+                    '<div class="media-object">' +
+                      '<img src="'+ data.small_img +'" width="50" height="50"/>' +
+                    '</div>' +
+                  '</div>' +
+                  '<div class="media-body">' +
+                    '<h4 class="media-heading"><strong>'+ data.sku +'</strong> | '+ data.name +'</h4>' +
+                    '<p>'+ data.barcode +'</p>' +
+                    '<p>'+ data.description +'</p>' +
+                    '<p>'+ data.display_attributes +'</p>' +
+                  '</div>' +
+                '</div>'
+        }
+      }
+    }
+  );
+
+  // this is the event that is fired when a user clicks on a suggestion
+  $('#typeahead').bind('typeahead:selected', function(event, datum, name) {
+    console.log(selectedProduct)
+    selectedProduct = datum;
+    $('#product_input').val(selectedProduct.id);
+    $("#product_name").html("<i>Producto seleccionado: "+selectedProduct.display_sku_name_attributes+"</i>")
+  });
+
+  $('input[type=radio][name=filter]').change(function() {
+    switch(this.value) {
+      case 'category':
+        $('#category_div').removeClass('hidden');
+        $("#subcategory_div").removeClass("hidden");
+        $('#products_div').addClass('hidden');
+        $("#variants_div").addClass('hidden');
+
+        $('#product_input').val('');
+        break;
+      case 'products':
+        $('#category_div').addClass('hidden');
+        $("#subcategory_div").addClass("hidden");
+        $('#products_div').removeClass('hidden');
+        $("#variants_div").removeClass('hidden');
+
+        $("#promotion_category_id").select2("val", "");
+        break;
+    }
+    $('.select2').select2();
+  });
+
+  $('#promotion_category_id').on('change', function() {
+    if($(this).val()) {
+      $.ajax({
+        type: "get",
+        url: '/getcategories/' + $(this).val(),
+        dataType: 'json',
+        success: function(data) {
+          $('#promotion_subcategory_id').empty();
+          if(data.length > 0) {
+            $('#promotion_subcategory_id').attr('disabled', false);
+            $('#promotion_subcategory_id').append("<option selected value='0'>Seleccione</option>")
+          } else {
+            $('#promotion_subcategory_id').attr('disabled', true);
+          }
+          for(i in data){
+            $('#promotion_subcategory_id').append("<option value='" + data[i].id +"'>" + data[i].category + "</option>")
+          }
+        }
+      });
+    }
+  });
+
+  function addPromotion() {
+    $('#promotion_form').submit();
+  }
+</script>

+ 116 - 0
app/views/promotions/index.html.erb

@@ -0,0 +1,116 @@
+<!-- BEGIN CONTAINER -->
+<div class="page-container">
+  <!-- BEGIN CONTENT -->
+  <div class="page-content-wrapper">
+    <!-- BEGIN CONTENT BODY -->
+    <!-- BEGIN PAGE HEAD-->
+    <div class="page-head">
+      <div class="container-fluid">
+        <!-- BEGIN PAGE TITLE -->
+        <div class="page-title">
+          <h1>Promociones</h1>
+        </div>
+        <!-- END PAGE TITLE -->
+      </div>
+    </div>
+    <!-- END PAGE HEAD-->
+    <!-- BEGIN PAGE CONTENT BODY -->
+    <div class="page-content">
+      <div class="container-fluid">
+        <!-- BEGIN PAGE BREADCRUMBS -->
+        <ul class="page-breadcrumb breadcrumb">
+          <%= render_breadcrumbs :tag => :li, :separator => ' <i class="fa fa-circle"></i> ' %>
+        </ul>
+        <!-- END PAGE BREADCRUMBS -->
+        <!-- BEGIN PAGE CONTENT INNER -->
+        <div class="page-content-inner">
+          <div id="notice">
+          <% if success %>
+            <div class="alert alert-success">
+              <p><%= success %></p>
+            </div>
+          <% elsif warning %>
+            <div class="alert alert-warning">
+              <p><%= warning %></p>
+            </div>
+          <% end %>
+          </div>
+          <div class="row">
+            <div class="col-md-12">
+              <div class="portlet light">
+                <div class="portlet-title">
+                  <div class="caption">
+                    <i class="fa fa-list"></i>
+                    <span class="caption-subject bold uppercase">Lista de promociones</span>
+                  </div>
+                  <div class="actions">
+                    <% if can? :create, Promotion %>
+                      <%= link_to new_promotion_path, { class: "btn bold green pull-right" } do %>
+                        Nueva promoción <i class="fa fa-plus"></i>
+                      <% end %>
+                    <% end %>
+                  </div>
+                </div>
+                <div class="portlet-body">
+                  <table class="table table-striped table-bordered table-hover tableadvanced">
+                    <thead>
+                      <tr>
+                        <th>#</th>
+                        <th>Periodo</th>
+                        <th>Descuento</th>
+                        <th>Línea / Sublínea</th>
+                        <th>Producto</th>
+                        <th>Status</th>
+                        <th>Acciones</th>
+                      </tr>
+                    </thead>
+                    <tbody>
+                      <% @promotions.each_with_index do |promotion, key| %>
+                        <tr>
+                          <td><%= key + 1 %></td>
+                          <td>
+                            Del <strong><%= l(promotion.start_date, format: '%d/%B/%Y') %></strong> al <strong><%= l(promotion.end_date, format: '%d/%B/%Y') %></strong>
+                          </td>
+                          <td> <%= promotion.percent %>%</td>
+                          <td>
+                            <%=
+                              if promotion.subcategory_id.present?
+                                Category.find(promotion.subcategory_id).category
+                              elsif promotion.category_id.present?
+                                promotion.category.category
+                              else
+                                ""
+                              end %>
+                          </td>
+                          <td> <%= promotion.product.present? ? promotion.product.full_display : '' %> </td>
+                          <td class="text-center">
+                            <% if promotion.active? %>
+                              <span class="label label-success">Vigente</span>
+                            <% else %>
+                              <span class="label label-danger">NO vigente</span>
+                            <% end %>
+                          </td>
+                          <td>
+                            <% if can? :destroy, Promotion %>
+                              <%= link_to promotion, method: :delete, class: "btn btn-icon-only btn-danger", title: "Eliminar promoción", data: { confirm: '¿Está seguro de eliminar la promoción?' } do %>
+                                <i class="fa fa-trash-o"></i>
+                              <% end %>
+                            <% end %>
+                          </td>
+                        </tr>
+                      <% end %>
+                    </tbody>
+                  </table>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- END PAGE CONTENT INNER -->
+      </div>
+    </div>
+    <!-- END PAGE CONTENT BODY -->
+    <!-- END CONTENT BODY -->
+  </div>
+  <!-- END CONTENT -->
+</div>

+ 54 - 0
app/views/promotions/new.html.erb

@@ -0,0 +1,54 @@
+<!-- BEGIN CONTAINER -->
+<div class="page-container">
+  <!-- BEGIN CONTENT -->
+  <div class="page-content-wrapper">
+    <!-- BEGIN CONTENT BODY -->
+    <!-- BEGIN PAGE HEAD-->
+    <div class="page-head">
+      <div class="container-fluid">
+        <!-- BEGIN PAGE TITLE -->
+        <div class="page-title">
+          <h1> Promociones</h1>
+        </div>
+        <!-- END PAGE TITLE -->
+      </div>
+    </div>
+    <!-- END PAGE HEAD-->
+    <!-- BEGIN PAGE CONTENT BODY -->
+    <div class="page-content">
+      <div class="container-fluid">
+        <%= link_to promotions_path, { class: "btn blue-hoki pull-right margin-bottom-10" } do %>
+          <i class="fa fa-angle-left "></i>
+          Regresar
+        <% end %>
+        <!-- BEGIN PAGE BREADCRUMBS -->
+        <ul class="page-breadcrumb breadcrumb">
+          <%= render_breadcrumbs :tag => :li, :separator => ' <i class="fa fa-circle"></i> ' %>
+        </ul>
+        <!-- END PAGE BREADCRUMBS -->
+        <!-- BEGIN PAGE CONTENT INNER -->
+        <div class="page-content-inner">
+          <div id="notice"><%= notice %></div>
+          <div class="row">
+            <div class="col-md-12">
+              <div class="portlet light ">
+                <div class="portlet-title">
+                  <div class="caption">
+                    <i class="fa fa-plus font-green"></i>
+                    <span class="caption-subject font-green bold uppercase">Nueva promoción</span>
+                    <span class="caption-helper"></span>
+                  </div>
+                </div>
+                <%= render 'form' %>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- END PAGE CONTENT INNER -->
+      </div>
+    </div>
+    <!-- END PAGE CONTENT BODY -->
+    <!-- END CONTENT BODY -->
+  </div>
+  <!-- END CONTENT -->
+</div>

+ 260 - 194
app/views/sales/sales_per_month_report.html.erb

@@ -1,229 +1,295 @@
-  <!-- BEGIN CONTAINER -->
-  <div class="page-container">
-    <!-- BEGIN CONTENT -->
-    <div class="page-content-wrapper">
-      <!-- BEGIN CONTENT BODY -->
-      <!-- BEGIN PAGE HEAD-->
-      <div class="page-head">
-        <div class="container-fluid">
-          <!-- BEGIN PAGE TITLE -->
-          <div class="page-title">
-            <h1>Reporte ventas e ingresos por mes </h1>
-          </div>
-          <!-- END PAGE TITLE -->
+<!-- BEGIN CONTAINER -->
+<div class="page-container">
+  <!-- BEGIN CONTENT -->
+  <div class="page-content-wrapper">
+    <!-- BEGIN CONTENT BODY -->
+    <!-- BEGIN PAGE HEAD-->
+    <div class="page-head">
+      <div class="container-fluid">
+        <!-- BEGIN PAGE TITLE -->
+        <div class="page-title">
+          <h1>Reporte de ventas e ingresos por mes </h1>
         </div>
+        <!-- END PAGE TITLE -->
       </div>
-      <!-- END PAGE HEAD-->
-      <!-- BEGIN PAGE CONTENT BODY -->
-      <div class="page-content">
-        <div class="container-fluid">
-          <!-- BEGIN PAGE BREADCRUMBS -->
-          <ul class="page-breadcrumb breadcrumb">
-            <%= render_breadcrumbs :tag => :li, :separator => ' <i class="fa fa-circle"></i> ' %>
-          </ul>
-          <!-- END PAGE BREADCRUMBS -->
-          <div class="search-page search-content-3">
-            <div class="row">
-              <div class="col-md-12">
-                <div class="search-filter bordered">
-                <!-- <h4 class=" uppercase form-section"> Búsqueda de producto </h4> <hr> -->
-                  <div class="form-group">
-                    <div class="row">
-                      <div class="col-md-3">
-                        <div class="search-label uppercase">Punto de venta</div>
-                        <div class="col-md-12">
-                          <%= select_tag "pointsale", options_from_collection_for_select(Pointsale.vigentes, :id, :name), :include_blank => "Todos", class: "form-control  input-medium" %>
-                        </div>
+    </div>
+    <!-- END PAGE HEAD-->
+    <!-- BEGIN PAGE CONTENT BODY -->
+    <div class="page-content">
+      <div class="container-fluid">
+        <!-- BEGIN PAGE BREADCRUMBS -->
+        <ul class="page-breadcrumb breadcrumb">
+          <%= render_breadcrumbs :tag => :li, :separator => ' <i class="fa fa-circle"></i> ' %>
+        </ul>
+        <!-- END PAGE BREADCRUMBS -->
+        <div class="search-page search-content-3">
+          <div class="row">
+            <div class="col-md-12">
+              <div class="search-filter bordered">
+              <!-- <h4 class=" uppercase form-section"> Búsqueda de producto </h4> <hr> -->
+                <div class="form-group">
+                  <div class="row">
+                    <div class="col-md-3 col-sm-4">
+                      <div class="search-label uppercase">Punto de venta</div>
+                      <div class="col-md-12">
+                        <%= select_tag "pointsale", options_from_collection_for_select(Pointsale.vigentes, :id, :name), include_blank: "Todos", class: "form-control  input-medium" %>
                       </div>
-                      <div class="col-md-3">
-                        <div class="search-label uppercase"> Del </div>
-                        <div class="col-md-6">
-                          <div class="input-group input-medium date date-picker" data-date-format="dd/mm/yyyy">
-                            <input id="start" type='text' class="form-control" value="<%= l(Date.today.beginning_of_month, format: '%d/%m/%Y') %>" />
-                            <span class="input-group-addon">
-                              <span class="glyphicon glyphicon-calendar"></span>
-                            </span>
-                          </div>
+                    </div>
+                    <div class="col-md-3 col-sm-4">
+                      <div class="search-label uppercase"> Del </div>
+                      <div class="col-md-6">
+                        <div class="input-group input-medium date date-picker" data-date-format="dd/mm/yyyy">
+                          <input id="start" type='text' class="form-control" value="<%= l(Date.today.beginning_of_month, format: '%d/%m/%Y') %>" />
+                          <span class="input-group-addon">
+                            <span class="glyphicon glyphicon-calendar"></span>
+                          </span>
                         </div>
                       </div>
-                      <div class="col-md-3">
-                        <div class="search-label uppercase"> Al </div>
-                        <div class="col-md-6">
-                          <div class="input-group input-medium date date-picker" data-date-format="dd/mm/yyyy">
-                            <input id="end" type='text' class="form-control" value="<%= l(Date.today.end_of_month, format: '%d/%m/%Y') %>" />
-                            <span class="input-group-addon">
-                              <span class="glyphicon glyphicon-calendar"></span>
-                            </span>
-                          </div>
+                    </div>
+                    <div class="col-md-3 col-sm-4">
+                      <div class="search-label uppercase"> Al </div>
+                      <div class="col-md-6">
+                        <div class="input-group input-medium date date-picker" data-date-format="dd/mm/yyyy">
+                          <input id="end" type='text' class="form-control" value="<%= l(Date.today.end_of_month, format: '%d/%m/%Y') %>" />
+                          <span class="input-group-addon">
+                            <span class="glyphicon glyphicon-calendar"></span>
+                          </span>
                         </div>
                       </div>
-                      <div class="col-md-3" style="margin-top: 20px">
-                        <button class="btn green bold btn-block" onclick="findSalesByPeriod()" style="margin:0px">Filtrar <i class="m-icon-swapright m-icon-white"></i> </button>
-                      </div>
                     </div>
                   </div>
                 </div>
-              </div>
-            </div>
-          </div>
-          <!-- BEGIN PAGE CONTENT INNER -->
-          <div class="page-content-inner">
-            <div class="row ">
-              <div class="portlet light">
-                <div class="portlet-title">
-                  <div class="caption">
-                    <span class="caption-subject font-blue-dark bold uppercase">Ventas e ingresos</span>
-                  </div>
-                </div>
-                <div class="portlet-body form" id="sales_content">
-                  <div class="product_track_search">
-                    <div class="row">
+                <div class="form-group">
+                  <div class="row">
+                    <div class="col-md-3 col-sm-4">
+                      <!-- categoria -->
+                      <div class="search-label uppercase">Línea</div>
                       <div class="col-md-12">
-                        <div class="note note-success hidden" id="note_info">
-                          <h4 class="block">Detalle de ventas e ingresos del periodo: <strong><span id="start_date_display"></span></strong> al <strong><span id="end_date_display"></span></strong> en <strong><span id="pointsale_display"></span></strong></h4>
-                        </div>
-                        <div class="col-xs-offset-1 col-xs-5">
-                          <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                            <div class="font-grey-mint font-sm">NÚMERO DE VENTAS</div>
-                            <div class="uppercase font-hg font-blue-sharp" id="sales_quantity">0</div>
-                          </div>
-                        </div>
-                        <div class="col-xs-offset-1 col-xs-5">
-                          <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                            <div class="font-grey-mint font-sm">PRODUCTOS VENDIDOS</div>
-                            <div class="uppercase font-hg font-blue-sharp" id="prods_total">0</div>
-                          </div>
+                        <div class="select2-bootstrap-prepend">
+                          <%= select_tag "category", options_from_collection_for_select(Category.activos_padre, :id, :category), include_blank: "Todas",  class: "form-control select2-allow-clear-todas", onchange: 'getSubCategories($(this).val());'%>
                         </div>
                       </div>
                     </div>
-
-                    <div class="row" style="margin-top:20px">
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Total vendido contado </div>
-                          <div class="uppercase font-hg font-blue-sharp" id="cash_sales_total"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Total vendido apartados</div>
-                          <div class="uppercase font-hg font-blue-sharp" id="reserved_sales_total"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Total vendido crédito </div>
-                          <div class="uppercase font-hg font-blue-sharp" id="credit_sales_total"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-xs-3">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">TOTAL VENDIDO</div>
-                          <div class="uppercase font-hg font-green" id="sales_total">$0.00</div>
+                    <div class="col-md-3 col-sm-4">
+                      <!-- sub- categoria -->
+                      <div class="search-label uppercase">Sublínea</div>
+                      <div class="col-md-12">
+                        <div class="select2-bootstrap-prepend">
+                          <%= collection_select("", :subcategory, ({}), :id, :category, options = { include_blank: "Todas" }, html_options = { class: 'form-control select2-allow-clear-todas input-medium' }) %>
                         </div>
                       </div>
                     </div>
-                    <div class="row" style="margin-top:20px">
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Ingresos por contado y devoluciones </div>
-                          <div class="uppercase font-hg font-blue-sharp" id="cash_sales_income"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Ingresos por apartados</div>
-                          <div class="uppercase font-hg font-blue-sharp" id="reserved_sales_income"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-md-3 col-sm-3 col-xs-6">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">Ingresos por ventas a crédito </div>
-                          <div class="uppercase font-hg font-blue-sharp" id="credit_sales_income"> $0.00</div>
-                        </div>
-                      </div>
-                      <div class="col-xs-3">
-                        <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
-                          <div class="font-grey-mint font-sm">TOTAL DE INGRESOS</div>
-                          <div class="uppercase font-hg font-green" id="sales_income">$0.00</div>
-                        </div>
+                    <div class="col-md-3 col-sm-4" style="margin-top: 20px">
+                      <button class="btn green bold btn-block" onclick="findSalesByPeriod()" style="margin:0px">Filtrar <i class="m-icon-swapright m-icon-white"></i> </button>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        <!-- BEGIN PAGE CONTENT INNER -->
+        <div class="page-content-inner">
+          <div class="portlet light">
+            <div class="portlet-title">
+              <div class="caption">
+                <span class="caption-subject font-blue-dark bold uppercase">Ventas e ingresos</span>
+              </div>
+            </div>
+            <div class="portlet-body form" id="sales_content">
+              <div class="product_track_search">
+                <div class="row">
+                  <div class="col-md-12">
+                    <div class="note note-success hidden" id="note_info">
+                      <h4 class="block">Detalle de ventas e ingresos del periodo: <strong><span id="start_date_display"></span></strong> al <strong><span id="end_date_display"></span></strong> en <strong><span id="pointsale_display"></span></strong></h4>
+                    </div>
+                    <div class="col-xs-offset-1 col-xs-5">
+                      <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                        <div class="font-grey-mint font-sm">NÚMERO DE VENTAS</div>
+                        <div class="uppercase font-hg font-blue-sharp" id="sales_quantity">0</div>
                       </div>
                     </div>
-                    <br>
-                    <div class="row">
-                      <div class="col-md-12">
-                        <h4 class="form-section">Desglose de ventas del periodo</h4>
-                        <table class="table table-striped table-bordered table-hover tableadvanced" id="sales_table">
-                          <thead>
-                            <tr>
-                              <th>Fecha</th>
-                              <th>Folio</th>
-                              <th>Punto de venta</th>
-                              <th>Vendido por</th>
-                              <th>Productos</th>
-                              <th>Tipo</th>
-                              <th>Status</th>
-                              <th>subtotal</th>
-                              <th>IVA</th>
-                              <th>Descto.</th>
-                              <th>Total</th>
-                            </tr>
-                          </thead>
-                          <tbody>
-                          </tbody>
-                        </table>
+                    <div class="col-xs-offset-1 col-xs-5">
+                      <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                        <div class="font-grey-mint font-sm">PRODUCTOS VENDIDOS</div>
+                        <div class="uppercase font-hg font-blue-sharp" id="prods_total">0</div>
                       </div>
                     </div>
                   </div>
                 </div>
+
+                <div class="row" style="margin-top:20px">
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Total vendido contado </div>
+                      <div class="uppercase font-hg font-blue-sharp" id="cash_sales_total"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Total vendido apartados</div>
+                      <div class="uppercase font-hg font-blue-sharp" id="reserved_sales_total"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Total vendido crédito </div>
+                      <div class="uppercase font-hg font-blue-sharp" id="credit_sales_total"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-xs-3">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">TOTAL VENDIDO</div>
+                      <div class="uppercase font-hg font-green" id="sales_total">$0.00</div>
+                    </div>
+                  </div>
+                </div>
+                <div class="row" style="margin-top:20px">
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Ingresos por contado y devoluciones </div>
+                      <div class="uppercase font-hg font-blue-sharp" id="cash_sales_income"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Ingresos por apartados</div>
+                      <div class="uppercase font-hg font-blue-sharp" id="reserved_sales_income"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-md-3 col-sm-3 col-xs-6">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">Ingresos por ventas a crédito </div>
+                      <div class="uppercase font-hg font-blue-sharp" id="credit_sales_income"> $0.00</div>
+                    </div>
+                  </div>
+                  <div class="col-xs-3">
+                    <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                      <div class="font-grey-mint font-sm">TOTAL DE INGRESOS</div>
+                      <div class="uppercase font-hg font-green" id="sales_income">$0.00</div>
+                    </div>
+                  </div>
+                </div>
+                <br>
+                <div class="row">
+                  <div class="col-md-12">
+                    <h4 class="form-section">Desglose de ventas del periodo</h4>
+                    <table class="table table-striped table-bordered table-hover tableadvanced" id="sales_table">
+                      <thead>
+                        <tr>
+                          <th>Fecha</th>
+                          <th>Folio</th>
+                          <th>Punto de venta</th>
+                          <th>Vendido por</th>
+                          <th>Productos</th>
+                          <th>Tipo</th>
+                          <th>Status</th>
+                          <th>subtotal</th>
+                          <th>IVA</th>
+                          <th>Descto.</th>
+                          <th>Total</th>
+                        </tr>
+                      </thead>
+                      <tbody>
+                      </tbody>
+                    </table>
+                  </div>
+                </div>
               </div>
             </div>
           </div>
-          <!-- END PAGE CONTENT INNER -->
         </div>
+        <!-- END PAGE CONTENT INNER -->
       </div>
-      <!-- END PAGE CONTENT BODY -->
-      <!-- END CONTENT BODY -->
     </div>
-    <!-- END CONTENT -->
+    <!-- END PAGE CONTENT BODY -->
+    <!-- END CONTENT BODY -->
   </div>
-  <!-- END CONTAINER -->
-  <script type="text/javascript">
-    var rows_selected = [];
+  <!-- END CONTENT -->
+</div>
+<!-- END CONTAINER -->
+<script type="text/javascript">
+  var rows_selected = [];
 
-    //buscar ventas por periodo de tiempo
-    function findSalesByPeriod() {
-      var pointsale = $('#pointsale').val();
-      var start = moment($("#start").val(), "DD-MM-YYYY").format('YYYY-MM-DD');
-      var end = moment($("#end").val(), "DD-MM-YYYY").format('YYYY-MM-DD');
-      // var start = moment($("#start").val(), "DD-MM-YYYY").startOf('Day').format('YYYY-MM-DD H:mm:ss');
-      // var end = moment($("#end").val(), "DD-MM-YYYY").endOf('Day').format('YYYY-MM-DD H:mm:ss');
+  //buscar ventas por periodo de tiempo
+  function findSalesByPeriod() {
+    var pointsale = $('#pointsale').val();
+    var start = moment($("#start").val(), "DD-MM-YYYY").format('YYYY-MM-DD');
+    var end = moment($("#end").val(), "DD-MM-YYYY").format('YYYY-MM-DD');
+    var category = $("#category option:selected").val();
+    var subcategory = $("#_subcategory option:selected").val();
+    // var start = moment($("#start").val(), "DD-MM-YYYY").startOf('Day').format('YYYY-MM-DD H:mm:ss');
+    // var end = moment($("#end").val(), "DD-MM-YYYY").endOf('Day').format('YYYY-MM-DD H:mm:ss');
 
-      if ($("#start").val() && $("#end").val()) {
-        App.blockUI({
-          target: $("#sales_content"),
-          animate: true
-        });
+    if ($("#start").val() && $("#end").val()) {
+      App.blockUI({
+        target: $("#sales_content"),
+        animate: true
+      });
 
-        $.ajax({
-          type: "get",
-          url:  '/sales_per_month/',
-          data: {
-            start_date: start,
-            end_date: end,
-            pointsale_id: pointsale
-          },
-          dataType: 'script',
-          success: function(data) {
-            App.unblockUI($("#sales_content"));
-            $('#start_date_display').html(moment($("#start").val(), "DD-MM-YYYY").format('DD/MM/YYYY'));
-            $('#end_date_display').html(moment($("#end").val(), "DD-MM-YYYY").format('DD/MM/YYYY'));
-            $('#pointsale_display').html(pointsale ? $('#pointsale').find("option:selected").text() : 'TODOS los puntos de venta');
-            $('#note_info').removeClass('hidden');
-          }
-        });
-      } else {
-        toastr["error"]("Es necesario seleccionar fechas.");
+      $.ajax({
+        type: "get",
+        url: '/sales_per_month/',
+        data: {
+          start_date: start,
+          end_date: end,
+          pointsale_id: pointsale,
+          category: category,
+          subcategory: subcategory
+        },
+        dataType: 'script',
+        success: function(data) {
+          App.unblockUI($("#sales_content"));
+          $('#start_date_display').html(moment($("#start").val(), "DD-MM-YYYY").format('DD/MM/YYYY'));
+          $('#end_date_display').html(moment($("#end").val(), "DD-MM-YYYY").format('DD/MM/YYYY'));
+          $('#pointsale_display').html(pointsale ? $('#pointsale').find("option:selected").text() : 'TODOS los puntos de venta');
+          $('#note_info').removeClass('hidden');
+        },
+        error: function(x) {
+          App.unblockUI($("#sales_content"));
+          $('#note_info').addClass('hidden');
+          toastr["error"]("Ocurrió un problema al filtrar.");
+        }
+      });
+    } else {
+      toastr["error"]("Es necesario seleccionar fechas.");
+    }
+  }
+
+  function getSubCategories(value) {
+    $('#_subcategory').html('');
+    $('#_subcategory').select2("destroy").select2({
+      placeholder: 'Seleccione',
+      "language": {
+        "noResults": function(){
+          return "No se encontraron coincidencias";
+        }
       }
+    });
+    $('#_subcategory').select2('val', null);
+    if(value) {
+      $.ajax({
+        type: "get",
+        url: '/getcategories/' + value,
+        dataType: 'json',
+        success: function(data) {
+          if(data.length > 0) {
+            $('#_subcategory').append('<option></option>')
+            for(i in data) {
+              $('#_subcategory').append("<option value='" + data[i].id + "'>" + data[i].category + "</option>")
+            }
+            $('#_subcategory').select2({
+              allowClear: true,
+              placeholder: 'Todas',
+              "language": {
+                "noResults": function(){
+                  return "No se encontraron coincidencias";
+                }
+              }
+            });
+          }
+        }
+      });
     }
-  </script>
+  }
+</script>

+ 1 - 1
app/views/transfers/_index_for_admin.html.erb

@@ -26,7 +26,7 @@
             <td>
               <%= transfer.destiny_is_pointsale? ? Pointsale.find(transfer.destiny_id).name : Warehouse.find(transfer.destiny_id).name %>
             </td>
-            <td> <%= transfer.transfer_details.count %> </td>
+            <td> <%= transfer.transfer_details.sum(:quantity) %> </td>
             <td class="text-center"><%= transfer_status(transfer) %></td>
             <td class="text-center"><%= transfer_details(transfer) %></td>
             <td class="text-center">

+ 31 - 75
app/views/transfers/_index_for_cashier.html.erb

@@ -2,11 +2,11 @@
   <div class="col-md-2 col-sm-3 col-xs-3">
     <ul class="nav nav-tabs tabs-left">
       <li class="active">
-          <a href="#recibidos" data-toggle="tab"> Recibidos </a>
+        <a href="#recibidos" data-toggle="tab"> Recibidos </a>
       </li>
       <li>
-          <a href="#enviados" data-toggle="tab"> Enviados </a>
-      </li>                    
+        <a href="#enviados" data-toggle="tab"> Enviados </a>
+      </li>
     </ul>
   </div>
   <div class="col-md-10 col-sm-9 col-xs-9">
@@ -22,59 +22,37 @@
                     <th>Origen</th>
                     <th>Productos</th>
                     <th>Status</th>
-                    <th>Status recepción</th>                       
+                    <th>Status recepción</th>
                     <th>Acciones</th>
                   </tr>
                 </thead>
                 <tbody>
                   <% @received.each_with_index do |transfer, key| %>
                     <tr>
-                      <td> <%= key +1 %> </td>
-                      <td> <%= l(transfer.transfer_date, :format => '%d/%B/%Y') %> </td>
-                      <td> <%= transfer.origin_is_pointsale == 1 ? Pointsale.find(transfer.origin_id).name : Warehouse.find(transfer.origin_id).name %>  </td>
-                      <td> <%= transfer.products.count %> </td>
-                      <td>
-                          <% case transfer.status %>
-                            <% when "cancelled" %>
-                              <span class="label label-danger"> CANCELADO </span>
-                            <% when "pending" %>
-                              <span class="label bg-yellow-crusta"> PENDIENTE DE RECEPCIÓN </span>
-                            <% when "received" %>
-                              <span class="label label-success"> RECIBIDO </span>
-                          <% end %>
-                      </td>
-                      <td class="text-center">
-                        <% if transfer.transfer_details.where(:has_looses => 1).count > 0 && 
-                          transfer.transfer_details.where(:has_surplus => 1).count == 0 %>
-                          <span class="label label-warning"> CON PERDIDAS </span>
-                        <% elsif transfer.transfer_details.where(:has_surplus => 1).count > 0 &&
-                          transfer.transfer_details.where(:has_looses => 1).count == 0 %>
-                          <span class="label label-warning"> CON EXCEDENTE </span>
-                        <% elsif transfer.transfer_details.where(:has_surplus => 1).count > 0  && 
-                        transfer.transfer_details.where(:has_looses => 1).count > 0 %>
-                          <span class="label label-danger"> CON INCONSISTENCIAS </span>
-                        <% elsif transfer.received? %>
-                          <i class='fa fa-check fa-2 font-green'></i> 
-                        <% end %>
-                      </td>                       
+                      <td><%= key + 1 %> </td>
+                      <td><%= l(transfer.transfer_date, format: '%d/%B/%Y') %> </td>
+                      <td><%= transfer.origin_is_pointsale == 1 ? Pointsale.find(transfer.origin_id).name : Warehouse.find(transfer.origin_id).name %>  </td>
+                      <td><%= transfer.transfer_details.sum(:quantity) %> </td>
+                      <td><%= transfer_status(transfer) %></td>
+                      <td class="text-center"><%= transfer_details(transfer) %></td>
                       <td>
-                        <%= link_to transfer, {:class=>"btn btn-icon-only default", :title=>"Ver traspaso"} do %>
+                        <%= link_to transfer, { class: "btn btn-icon-only default", title: "Ver traspaso" } do %>
                           <i class="fa fa-search"></i>
                         <% end %>
-                        <%= link_to print_transfer_receipt_path(transfer.id, format: 'pdf'), {:class=>"btn btn-icon-only default", :target => "blank"} do %>
+                        <%= link_to print_transfer_receipt_path(transfer.id, format: 'pdf'), { class: "btn btn-icon-only default", target: "blank" } do %>
                           <i class="fa fa-print"></i>
-                        <% end %>                        
-                        <% if transfer.status =="pending" %>
-                          <%= link_to verify_transfer_path(transfer), {:class=>"btn btn-icon-only green-meadow", :title=>"Verificar traspaso"} do %>
+                        <% end %>
+                        <% if transfer.pending? %>
+                          <%= link_to verify_transfer_path(transfer), { class: "btn btn-icon-only green-meadow", title: "Verificar traspaso" } do %>
                             <i class="fa fa-check-square-o"></i>
-                          <% end %>                              
+                          <% end %>
                         <% end %>
                       </td>
                     </tr>
-                  <% end %>            
+                  <% end %>
                 </tbody>
-              </table>                            
-            </div>                                  
+              </table>
+            </div>
           </div>
         </div>
         <div class="tab-pane fade" id="enviados">
@@ -88,54 +66,32 @@
                     <th>Destino</th>
                     <th>Productos</th>
                     <th>Status</th>
-                    <th>Status recepción</th>                    
+                    <th>Status<br>recepción</th>
                     <th>Acciones</th>
                   </tr>
                 </thead>
                 <tbody>
                   <% @sent.each_with_index do |transfer, key| %>
                     <tr>
-                      <td> <%= key +1 %> </td>
-                      <td> <%= l(transfer.transfer_date, :format => '%d/%B/%Y') %> </td>
-                      <td> <%= transfer.destiny_is_pointsale == 1 ? Pointsale.find(transfer.destiny_id).name : Warehouse.find(transfer.destiny_id).name %>  </td>
-                      <td> <%= transfer.products.count %> </td>
-                      <td>
-                          <% case transfer.status %>
-                            <% when "cancelled" %>
-                              <span class="label label-danger"> CANCELADO </span>
-                            <% when "pending" %>
-                              <span class="label bg-yellow-crusta"> PENDIENTE DE RECEPCIÓN </span>
-                            <% when "received" %>
-                              <span class="label label-success"> RECIBIDO </span>
-                          <% end %>
-                      </td>
-                      <td class="text-center">
-                        <% if transfer.transfer_details.where(:has_looses => 1).count > 0 && 
-                          transfer.transfer_details.where(:has_surplus => 1).count == 0 %>
-                          <span class="label label-warning"> CON PERDIDAS </span>
-                        <% elsif transfer.transfer_details.where(:has_surplus => 1).count > 0 &&
-                          transfer.transfer_details.where(:has_looses => 1).count == 0 %>
-                          <span class="label label-warning"> CON EXCEDENTE </span>
-                        <% elsif transfer.transfer_details.where(:has_surplus => 1).count > 0  && 
-                        transfer.transfer_details.where(:has_looses => 1).count > 0 %>
-                          <span class="label label-danger"> CON INCONSISTENCIAS </span>
-                        <% elsif transfer.received? %>
-                          <i class='fa fa-check fa-2 font-green'></i>                           
-                        <% end %>
-                      </td>                      
+                      <td> <%= key + 1 %> </td>
+                      <td> <%= l(transfer.transfer_date, format: '%d/%B/%Y') %> </td>
+                      <td> <%= transfer.destiny_is_pointsale == 1 ? Pointsale.find(transfer.destiny_id).name : Warehouse.find(transfer.destiny_id).name %> </td>
+                      <td> <%= transfer.transfer_details.sum(:quantity) %> </td>
+                      <td><%= transfer_status(transfer) %></td>
+                      <td class="text-center"><%= transfer_details(transfer) %></td>
                       <td>
-                        <%= link_to transfer, {:class=>"btn btn-icon-only default", :title=>"Ver traspaso"} do %>
+                        <%= link_to transfer, { class: "btn btn-icon-only default", title: "Ver traspaso" } do %>
                           <i class="fa fa-search"></i>
                         <% end %>
-                        <%= link_to print_transfer_receipt_path(transfer.id, format: 'pdf'), {:class=>"btn btn-icon-only default", :target => "blank"} do %>
+                        <%= link_to print_transfer_receipt_path(transfer.id, format: 'pdf'), { class: "btn btn-icon-only default", target: "blank" } do %>
                           <i class="fa fa-print"></i>
                         <% end %>
                       </td>
                     </tr>
-                  <% end %>            
+                  <% end %>
                 </tbody>
-              </table>                            
-            </div>                                  
+              </table>
+            </div>
           </div>
         </div>
      </div>

+ 6 - 6
app/views/transfers/_index_for_manager.html.erb

@@ -2,10 +2,10 @@
   <div class="col-md-2 col-sm-3 col-xs-3">
     <ul class="nav nav-tabs tabs-left">
       <li class="active">
-          <a href="#recibidos" data-toggle="tab"> Recibidos </a>
+        <a href="#recibidos" data-toggle="tab"> Recibidos </a>
       </li>
       <li>
-          <a href="#enviados" data-toggle="tab"> Enviados </a>
+        <a href="#enviados" data-toggle="tab"> Enviados </a>
       </li>
     </ul>
   </div>
@@ -29,10 +29,10 @@
                 <tbody>
                   <% @received.each_with_index do |transfer, key| %>
                     <tr>
-                      <td> <%= key +1 %> </td>
-                      <td> <%= l(transfer.transfer_date, :format => '%d/%B/%Y') %> </td>
+                      <td> <%= key + 1 %> </td>
+                      <td> <%= l(transfer.transfer_date, format: '%d/%B/%Y') %> </td>
                       <td> <%= transfer.origin_is_pointsale == 1 ? Pointsale.find(transfer.origin_id).name : Warehouse.find(transfer.origin_id).name %>  </td>
-                      <td> <%= transfer.transfer_details.count %> </td>
+                      <td> <%= transfer.transfer_details.sum(:quantity) %> </td>
                       <td><%= transfer_status(transfer) %> </td>
                       <td class="text-center"><%= transfer_details(transfer) %></td>
                       <td>
@@ -76,7 +76,7 @@
                       <td> <%= key + 1 %> </td>
                       <td> <%= l(transfer.transfer_date, format: '%d/%B/%Y') %> </td>
                       <td> <%= transfer.destiny_is_pointsale == 1 ? Pointsale.find(transfer.destiny_id).name : Warehouse.find(transfer.destiny_id).name %></td>
-                      <td> <%= transfer.products.count %></td>
+                      <td> <%= transfer.transfer_details.sum(:quantity) %></td>
                       <td><%= transfer_status(transfer) %></td>
                       <td class="text-center"><%= transfer_details(transfer) %></td>
                       <td>

+ 8 - 8
app/views/transfers/index.html.erb

@@ -43,18 +43,18 @@
                     <span class="caption-subject bold uppercase">Lista de traspasos</span>
                   </div>
                   <div class="actions">
-                    <%= link_to new_transfer_path, {:class=>"btn bold green pull-right filtros"} do %> Nuevo traspaso <i class="fa fa-plus"></i>
+                    <%= link_to new_transfer_path, { class: "btn bold green pull-right filtros" } do %> Nuevo traspaso <i class="fa fa-plus"></i>
                     <% end %>
                   </div>
                 </div>
                 <div class="portlet-body">
-                    <% if current_user.usertype == "A" || current_user.usertype == "SS" %>
-                      <%= render partial: 'transfers/index_for_admin' %>
-                    <% elsif current_user.usertype == "C" %>
-                      <%= render partial: 'transfers/index_for_cashier' %>
-                    <% else %>
-                      <%= render partial: 'transfers/index_for_manager' %>
-                    <% end %>
+                  <% if current_user.usertype == "A" || current_user.usertype == "SS" %>
+                    <%= render partial: 'transfers/index_for_admin' %>
+                  <% elsif current_user.usertype == "C" %>
+                    <%= render partial: 'transfers/index_for_cashier' %>
+                  <% else %>
+                    <%= render partial: 'transfers/index_for_manager' %>
+                  <% end %>
                 </div>
               </div>
             </div>

+ 12 - 24
app/views/transfers/receipt.pdf.erb

@@ -1,5 +1,5 @@
 <div class='pdf-body'>
-	<h3 style="margin-top:20px" class="text-center">Traspaso de <strong><%= origin %></strong> a <strong><%= destiny %></strong></h3> 
+	<h3 style="margin-top:20px" class="text-center">Traspaso de <strong><%= origin %></strong> a <strong><%= destiny %></strong></h3>
 
 	<div class="resume" style="border-bottom:1px solid black;">
 		<h4 style="margin-top:5px;margin-bottom:5px">Información general</h4>
@@ -11,17 +11,10 @@
 			<%= transfer.user.first_name %><br>
 
 		<strong>Productos:</strong>&nbsp;&nbsp;
-			<%= transfer.products.count %><br>
+			<%= transfer.transfer_details.sum(:quantity) %><br>
 
 		<strong>Status:</strong>&nbsp;&nbsp;
-	        <% case transfer.status %>
-	          <% when "cancelled" %>
-	            CANCELADO <br>	
-	          <% when "pending" %>
-	            PENDIENTE <br>	
-	          <% when "received" %>
-	            RECIBIDO <br>	
-	        <% end %>
+	    <%= transfer_status(transfer) %>
 
 		<% if transfer.received? %>
 			<strong>Status de recepción:</strong>&nbsp;&nbsp;
@@ -32,12 +25,12 @@
 
 			<strong>Recibió:</strong>&nbsp;&nbsp;
 				<%= transfer.received_by.first_name %>
-							
+
 			<% if transfer.observations.present? %>
 				<br> <strong>Observaciones:</strong>&nbsp;&nbsp;
-					<%= transfer.observations %>			
+					<%= transfer.observations %>
 			<% end %>
-		<% end %>		
+		<% end %>
 	</div>
 
 	<div class="products">
@@ -57,8 +50,8 @@
 						</td>
 					</tr>
 					<tr>
-						<td style="text-align:center"> 
-							<%= detail.quantity.round %>  
+						<td style="text-align:center">
+							<%= detail.quantity.round %>
 						</td>
 						<td style="text-align:center">
 							<%= detail.adjustment if transfer.received? %>
@@ -69,15 +62,10 @@
 							<% elsif detail.has_surplus? %>
 								CON EXCEDENTE
 							<% end %>
-						</td>						
-					</tr>				
-				<% end %>			
+						</td>
+					</tr>
+				<% end %>
 			</tbody>
 		</table>
-	</div>		
-
+	</div>
 </div>
-
-
-
-

+ 5 - 7
app/views/users/_form.html.erb

@@ -62,14 +62,12 @@
 									<%= f.hidden_field :pointsale_id, value: (current_user.usertype == 'G' ? current_user.pointsale_id : @user.pointsale_id), id: 'pointsale_id' %>
 								</div>
 							</div>
-							<% if current_user.usertype == "A" || current_user.usertype == "SS" %>
-								<div class="form-group hidden" id="warehouse_div">
-									<%= f.label :warehouse_id, "", { class: "col-md-3 control-label" } do %> Almacén <span class="required">*</span> <% end %>
-									<div class="col-md-4">
-										<%= f.collection_select :warehouse_id, Warehouse.activos, :id, :name, { prompt: "Seleccione" }, { class: "form-control select2" } %>
-									</div>
+							<div class="form-group hidden" id="warehouse_div">
+								<%= f.label :warehouse_id, "", { class: "col-md-3 control-label" } do %> Almacén <span class="required">*</span> <% end %>
+								<div class="col-md-4">
+									<%= f.collection_select :warehouse_id, Warehouse.activos, :id, :name, { prompt: "Seleccione" }, { class: "form-control select2" } %>
 								</div>
-							<% end %>
+							</div>
 							<div class="form-group">
 								<%= f.label :userid, "Nombre de usuario", { class: "col-md-3 control-label" } do %> Usuario <span class="required">*</span> <% end %>
 								<div class="col-md-4">

+ 1 - 0
config/initializers/inflections.rb

@@ -14,5 +14,6 @@
 ActiveSupport::Inflector.inflections do |inflect|
   # inflect.acronym 'RESTful'
   inflect.irregular "error", "errores"
+  inflect.irregular "promoción", "promociones"
 end
 

+ 2 - 1
config/locales/en.yml

@@ -279,9 +279,10 @@ en:
     warehouses: Almacenes
     commissions: Comisiones
     products_returns: Devoluciones
+    promotions: Promociones
     # user:
     #   pwd: Cambio de contraseña
   dictionary:
     sizes: Talla
-    colors: Color 
+    colors: Color
     styles: Estilo

+ 78 - 213
config/navigation.rb

@@ -41,258 +41,123 @@ SimpleNavigation.register_renderer bootstrap_breadcrumbs: SimpleNavigationn::Ren
 SimpleNavigation::Configuration.run do |navigation|
   navigation.renderer = SimpleNavigationRenderers::Bootstrap3
   navigation.items do |primary|
-    if current_user.usertype == 'A' || current_user.usertype == "SS"
-      # clientes
+    # clientes
+    if can? :read, Customer
       primary.item :customers, { icon: "fa fa-fw fa-smile-o", text: "Clientes" }, customers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
         sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_customer, 'Nuevo Cliente', new_customer_path
         sub_nav.item :list_customers, 'Lista de clientes', customers_path
         sub_nav.item :divider_after_list_customers, '#', divider: true
-        sub_nav.item :special_prices, 'Descuentos especiales por cliente', new_special_price_path
+        sub_nav.item :debtor_customers, 'Clientes deudores', debtors_path if (can? :debtors, Customer)
+        sub_nav.item :special_prices, 'Descuentos especiales por cliente', new_special_price_path if (can? :create, SpecialPrice)
       end
-      # proveedores
-      primary.item :suppliers, { icon: "fa fa-fw fa-suitcase", text: "Proveedores" }, suppliers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_supplier, 'Nuevo Proveedor', new_supplier_path
-        sub_nav.item :list_suppliers, 'Lista de proveedores', suppliers_path
-      end
-      # productos
-      primary.item :products, { icon: "fa fa-fw fa-cubes", text: "Productos" }, products_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
+    end
+    # productos
+    primary.item :products, { icon: "fa fa-fw fa-cubes", text: "Productos" }, products_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
+      sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
+      if can? :read, Unit
         sub_nav.item :units, 'Unidades de medida', units_path
         sub_nav.item :divider_before_categories, '#', divider: true
-        sub_nav.item :new_category, 'Nueva línea de producto', new_category_path
+      end
+      if can? :read, Category
         sub_nav.item :list_categories, 'Lista de líneas de producto', categories_path
         sub_nav.item :divider_before_products, '#', divider: true
-        sub_nav.item :new_product, 'Nuevo Producto', new_product_path
-        sub_nav.item :list_products, 'Lista de productos', products_path
-        sub_nav.item :stock_by_pointsale, 'Existencias', stock_by_pointsale_path
-        sub_nav.item :list_waste_of_products, 'Lista de mermas de productos', product_wastes_path
+      end
+      sub_nav.item :list_products, 'Lista de productos', products_path if (can? :index, Product)
+      sub_nav.item :stock_by_pointsale, 'Existencias', stock_by_pointsale_path if (can? :read, AvailableProduct)
+      sub_nav.item :list_waste_of_products, 'Lista de mermas de productos', product_wastes_path if (can? :read, ProductWaste)
+      if can? :read, Purchase
         sub_nav.item :divider_before_purchases, '#', divider: true
-        sub_nav.item :new_product, 'Nueva Compra', new_purchase_path
         sub_nav.item :list_products, 'Lista de compras', purchases_path
+      end
+      if can? :read, Supplier
+        sub_nav.item :divider_before_purchases, '#', divider: true
+        sub_nav.item :list_suppliers, 'Lista de proveedores', suppliers_path
+      end
+      if (can? :product_track, Product)
         sub_nav.item :divider_before_purchases, '#', divider: true
         sub_nav.item :product_track, 'Seguimiento de productos', product_track_path
       end
-      # puntos de venta y caja registradora
-      primary.item :pointsales, { icon: "fa fa-fw fa-cart-plus", text: "Puntos de venta" }, pointsales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_pointsale, 'Nuevo Punto de venta', new_pointsale_path
+    end
+    # puntos de venta y caja registradora
+    primary.item :pointsales, { icon: "fa fa-fw fa-cart-plus", text: "Puntos de venta" }, pointsales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
+      sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
+      if can? :read, Pointsale
         sub_nav.item :list_pointsales, 'Lista de puntos de venta', pointsales_path
         sub_nav.item :divider_before_pointsales, '#', divider: true
-        sub_nav.item :list_cash_registers, 'Lista de cajas', cash_registers_path
-        sub_nav.item :list_cash_outs, 'Lista de cortes de caja', cash_outs_path
+      end
+      sub_nav.item :list_cash_registers, 'Lista de cajas', cash_registers_path if (can? :index, CashRegister)
+      sub_nav.item :list_cash_outs, 'Lista de cortes de caja', cash_outs_path if (can? :read, CashOut)
+      sub_nav.item :divider_before_pointsales, '#', divider: true
+      sub_nav.item :list_promotions, 'Promociones', promotions_path if (can? :read, Promotion)
+      if (can? :read, Seller)
         sub_nav.item :divider_before_sellers, '#', divider: true
         sub_nav.item :list_sellers, 'Lista de vendedores', sellers_path
+      end
+      if can? :read, Transfer
         sub_nav.item :divider_after_sellers, '#', divider: true
-        sub_nav.item :new_transfer, 'Nuevo traspaso', new_transfer_path
         sub_nav.item :list_transfers, 'Lista de traspasos', transfers_path
+      end
+      if can? :read, Warehouse
         sub_nav.item :divider_after_list_users, '#', divider: true
         sub_nav.item :list_warehouse, 'Almacenes', warehouses_path
       end
-      # ventas
+    end
+    # ventas
+    if can? :read, Sale
       primary.item :sales, { icon: "fa fa-fw fa-money", text: "Ventas" }, sales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
         sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
         sub_nav.item :list_sales, 'Lista de ventas', sales_path
-        sub_nav.item :divider_after_list_sales, '#', divider: true
-        sub_nav.item :list_sales_reserved, 'Lista de apartados', sales_reserved_path
-        sub_nav.item :commission, "Lista de comisiones", commissions_path
+        if can? :sales_reserved, Sale
+          sub_nav.item :list_sales_reserved, 'Lista de apartados', sales_reserved_path
+        end
+        if can? :read, ProductsReturn
+          sub_nav.item :list_products_return, 'Lista de devoluciones', products_returns_path
+        end
+        if can? :manage, Commission
+          sub_nav.item :divider_after_list_returns, '#', divider: true
+          sub_nav.item :commission, 'Lista de comisiones', commissions_path
+        end
       end
-      # egresos
+    end
+    # egresos
+    if can? :read, Expense
       primary.item :expensesconcepts, { icon: "fa fa-fw fa-dollar", text: "Egresos" }, expensesconcepts_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
         sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_expensesconcept, 'Nuevo Concepto de egreso', new_expensesconcept_path
-        sub_nav.item :list_expensesconcepts, 'Lista de conceptos de egreso', expensesconcepts_path
-        sub_nav.item :divider_after_list_expensesconcepts, '#', divider: true
-        sub_nav.item :new_expense, 'Nuevo egreso', new_expense_path
+        if can? :read, Expensesconcept
+          sub_nav.item :list_expensesconcepts, 'Lista de conceptos de egreso', expensesconcepts_path
+          sub_nav.item :divider_after_list_expensesconcepts, '#', divider: true
+        end
         sub_nav.item :list_expenses, 'Lista de egresos', expenses_path
       end
-      # reportes
+    end
+    # reportes
+    if current_user.usertype == "A" || current_user.usertype == "SS"
       primary.item :reports, { icon: "fa fa-bar-chart", text: "Reportes" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
         sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
         sub_nav.item :sales_per_month, 'Ventas por mes', sales_per_month_report_path
         sub_nav.item :minmax_report, 'Mínimos y Máximos', min_max_path
       end
-      # configuracion
-      primary.item :config, { icon: "fa fa-fw fa-cog", text: "Configuración" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :pos_configs, 'Configurar parámetros generales', pos_configs_path
-        sub_nav.item :divider_after_list_transfers, '#', divider: true
-        sub_nav.item :list_products_initial_stock, 'Stock inicial de productos', products_initial_stock_path
-        sub_nav.item :list_products_stock, 'Stock mínimo y máximo de productos', products_stock_path
-        sub_nav.item :divider_after_list_transfers, '#', divider: true
-        sub_nav.item :list_users, 'Usuarios del sistema', users_path
-      end
-      # soporte
-      primary.item :support, { icon: "fa fa-fw fa-group", text: "Soporte" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: "dropdown-menu pull-left" }
-        sub_nav.item :send_info, "Soporte técnico", contact_support_path
-        sub_nav.item :updates, "Actualizaciones", system_updates_path
-      end
     end
-
-    if current_user.usertype == 'G'
-      primary.item :home, { icon: "fa fa-fw fa-home", text: "Inicio" }, root_path, class: 'menu-dropdown classic-menu-dropdown'
-      # clientes
-      primary.item :customers, { icon: "fa fa-fw fa-smile-o", text: "Clientes" }, customers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_customer, 'Nuevo Cliente', new_customer_path
-        sub_nav.item :list_customers, 'Lista de clientes', customers_path
-        sub_nav.item :debtor_customers, 'Clientes deudores', debtors_path
-      end
-      # proveedores
-      primary.item :suppliers, { icon: "fa fa-fw fa-suitcase", text: "Proveedores" }, suppliers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_supplier, 'Nuevo Proveedor', new_supplier_path
-        sub_nav.item :list_suppliers, 'Lista de proveedores', suppliers_path
-      end
-      # productos
-      primary.item :products, { icon: "fa fa-fw fa-cubes", text: "Productos" }, products_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :units, 'Unidades de medida', units_path
-        sub_nav.item :divider_before_categories, '#', divider: true
-        sub_nav.item :new_category, 'Nueva línea de producto', new_category_path
-        sub_nav.item :list_categories, 'Lista de líneas de producto', categories_path
-        sub_nav.item :divider_before_products, '#', divider: true
-        sub_nav.item :new_product, 'Nuevo Producto', new_product_path
-        sub_nav.item :list_products, 'Lista de productos', products_path
-        sub_nav.item :divider_after_list_products, '#', divider: true
-        sub_nav.item :stock_by_pointsale, 'Existencias', stock_by_pointsale_path
-        sub_nav.item :divider_after_stock_by_pointsale, '#', divider: true
-        sub_nav.item :list_waste, 'Nueva merma de productos', new_product_waste_path
-        sub_nav.item :list_waste_of_products, 'Lista de mermas de productos', product_wastes_path
-        sub_nav.item :divider_before_purchases, '#', divider: true
-        sub_nav.item :new_purchase, 'Nueva Compra', new_purchase_path
-        sub_nav.item :purchases_list, 'Lista de compras', purchases_path
-      end
-      # ventas
-      primary.item :sales, { icon: "fa fa-fw fa-money", text: "Ventas" }, sales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_sale, 'Nueva venta', new_sale_path
-        sub_nav.item :list_sales, 'Lista de ventas', sales_path
-        sub_nav.item :divider_after_list_sales, '#', divider: true
-        sub_nav.item :list_sales_reserved, 'Lista de apartados', sales_reserved_path
-        sub_nav.item :divider_after_list_sales_reserved, '#', divider: true
-        sub_nav.item :commission, "Lista de comisiones", commissions_path
-        sub_nav.item :divider_after_list_comissions, '#', divider: true
-        sub_nav.item :list_products_return, 'Lista de devoluciones', products_returns_path
-      end
-      # punto de venta, caja registradora y vendedores
-      primary.item :pointsales, { icon: "fa fa-fw fa-cart-plus", text: "Puntos de venta" }, pointsales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :list_products, 'Lista de cajas ', cash_registers_path
-        sub_nav.item :divider_after_list_products, '#', divider: true
-        sub_nav.item :list_cash_outs, 'Lista de cortes de caja', cash_outs_path
-        sub_nav.item :divider_after_list_cash_outs, '#', divider: true
-        sub_nav.item :list_sellers, 'Lista de vendedores', sellers_path
-        sub_nav.item :divider_after_list_sellers, '#', divider: true
-        sub_nav.item :new_transfer, 'Nuevo traspaso', new_transfer_path
-        sub_nav.item :list_transfers, 'Lista de traspasos', transfers_path
-        sub_nav.item :divider_after_list_transfers, '#', divider: true
-        sub_nav.item :list_warehouse, 'Almacenes', warehouses_path
-      end
-      # coneptos de egresos
-      primary.item :expensesconcepts, { icon: "fa fa-fw fa-dollar", text: "Egresos" }, expensesconcepts_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_expensesconcept, 'Nuevo egreso', new_expense_path
-        sub_nav.item :list_expensesconcepts, 'Lista de egresos', expenses_path
-        sub_nav.item :divider_after_expenses, '#', divider: true
-        sub_nav.item :list_expensesconcepts, 'Lista de conceptos de egreso', expensesconcepts_path
-      end
-      # configuracion
-      primary.item :config, { icon: "fa fa-fw fa-cog", text: "Configuración" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :list_products_initial_stock, 'Stock inicial de productos', products_initial_stock_path(pointsale_id: current_user.pointsale_id)
-        sub_nav.item :list_products_stock, 'Stock mínimo y máximo de productos', products_stock_path(pointsale_id: current_user.pointsale_id)
-        sub_nav.item :divider_after_list_transfers, '#', divider: true
-        sub_nav.item :list_users, 'Usuarios del sistema', users_path
-      end
-      # soporte
-      primary.item :support, { icon: "fa fa-fw fa-group", text: "Soporte" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: "dropdown-menu pull-left" }
-        sub_nav.item :send_info, "Soporte técnico", contact_support_path
-        sub_nav.item :updates, "Actualizaciones", system_updates_path
-      end
+    # soporte
+    primary.item :support, { icon: "fa fa-fw fa-group", text: "Soporte" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
+      sub_nav.dom_attributes = { class: "dropdown-menu pull-left" }
+      sub_nav.item :send_info, "Soporte técnico", contact_support_path
+      sub_nav.item :updates, "Actualizaciones", system_updates_path
     end
-
-    if current_user.usertype == 'C'
-      primary.item :home, { icon: "fa fa-fw fa-home", text: "Inicio" }, root_path, class: 'menu-dropdown classic-menu-dropdown'
-      # clientes
-      primary.item :customers, { icon: "fa fa-fw fa-smile-o", text: "Clientes" }, customers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
+    # configuracion general
+    if can? :read, PosConfig
+      primary.item :pos_config, { icon: "fa fa-fw fa-cog", text: "Configuración" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
         sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_customer, 'Nuevo Cliente', new_customer_path
-        sub_nav.item :list_customers, 'Lista de clientes', customers_path
-        sub_nav.item :divider_after_list_customers, '#', divider: true
-        sub_nav.item :debtor_customers, 'Clientes deudores', debtors_path
-      end
-
-      # productos
-      primary.item :products, { icon: "fa fa-fw fa-cubes", text: "Productos" }, products_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.item :stock_by_pointsale, 'Existencias', stock_by_pointsale_path
-        sub_nav.item :divider_after_stock_by_pointsale, '#', divider: true
-        sub_nav.item :list_waste, 'Nueva merma de productos', new_product_waste_path
-        sub_nav.item :list_waste_of_products, 'Lista de mermas de productos', product_wastes_path
-      end
-      # ventas
-      primary.item :sales, { icon: "fa fa-fw fa-money", text: "Ventas" }, sales_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_sale, 'Nueva venta', new_sale_path
-        sub_nav.item :list_sales, 'Lista de ventas', sales_path
-        sub_nav.item :divider_after_list_sales, '#', divider: true
-        sub_nav.item :list_sales_reserved, 'Lista de apartados', sales_reserved_path
-        sub_nav.item :divider_after_list_comissions, '#', divider: true
-        sub_nav.item :list_products_return, 'Lista de devoluciones', products_returns_path
-      end
-      # caja y cortes de caja
-      primary.item :cash_register, { icon: "fa fa-shopping-cart", text: "Caja" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :cash_out, 'Corte de caja', new_cash_out_path
-        # sub_nav.item :list_cash_outs, 'Lista de cortes de caja', cash_outs_path
-      end
-      # conceptos de egresos
-      primary.item :expenses, { icon: "fa fa-fw fa-dollar", text: "Egresos" }, expenses_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_expense, 'Nuevo egreso', new_expense_path
-        sub_nav.item :list_expenses, 'Lista de egresos', expenses_path
-      end
-      # conceptos de egresos
-      primary.item :transfers, { icon: "fa fa-fw fa-dollar", text: "Traspasos" }, transfers_path, class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: 'dropdown-menu pull-left' }
-        sub_nav.item :new_transfer, 'Nuevo traspaso', new_transfer_path
-        sub_nav.item :list_expenses, 'Lista de traspasos', transfers_path
-      end
-      # soporte
-      primary.item :support, { icon: "fa fa-fw fa-group", text: "Soporte" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: "dropdown-menu pull-left" }
-        sub_nav.item :send_info, "Soporte técnico", contact_support_path
-        sub_nav.item :updates, "Actualizaciones", system_updates_path
-      end
-    end
-
-    if current_user.usertype == 'S'
-      primary.item :home, { icon: "fa fa-fw fa-home", text: "Inicio" }, root_path, class: 'menu-dropdown classic-menu-dropdown'
-      # existencias
-      primary.item :products, { icon: "fa fa-fw fa-cubes", text: "Existencias" }, stock_by_pointsale_path, class: 'menu-dropdown classic-menu-dropdown'
-      # traspasos
-      primary.item :transfers, { icon: "fa fa-fw fa-exchange", text: "Traspasos" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.item :new_transfer, 'Nuevo traspaso', new_transfer_path
-        sub_nav.item :list_transfers, 'Lista de traspasos', transfers_path
-      end
-      # mermas
-      primary.item :product_wastes, { icon: "fa fa-fw fa-arrow-down", text: "Mermas" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.item :list_waste, 'Nueva merma de productos', new_product_waste_path
-        sub_nav.item :list_waste_of_products, 'Lista de mermas de productos', product_wastes_path
-      end
-      # inventario
-      primary.item :stock, { icon: "fa fa-fw fa-database", text: "Inventario" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.item :initial_stock, 'Inventario inicial', products_initial_stock_path(current_user.warehouse_id)
-        sub_nav.item :stock_adjustment, 'Ajuste de inventario', products_stock_path(current_user.warehouse_id)
-      end
-      # soporte
-      primary.item :support, { icon: "fa fa-fw fa-group", text: "Soporte" }, '#', class: 'menu-dropdown classic-menu-dropdown' do |sub_nav|
-        sub_nav.dom_attributes = { class: "dropdown-menu pull-left" }
-        sub_nav.item :send_info, "Soporte técnico", contact_support_path
-        sub_nav.item :updates, "Actualizaciones", system_updates_path
+        if can? :manage, PosConfig
+          sub_nav.item :pos_configs, 'Configurar parámetros generales', pos_configs_path
+          sub_nav.item :divider_after_pos_config, '#', divider: true
+        end
+        sub_nav.item :list_products_initial_stock, 'Stock inicial de productos', products_initial_stock_path
+        sub_nav.item :list_products_stock, 'Stock mínimo y máximo de productos', products_stock_path
+        if can? :read, User
+          sub_nav.item :divider_after_products_stock, '#', divider: true
+          sub_nav.item :list_users, 'Usuarios del sistema', users_path
+        end
       end
     end
 

+ 3 - 0
config/routes.rb

@@ -101,6 +101,7 @@ Rails.application.routes.draw do
   end
   get 'validate_unique_barcode/:barcode' => 'products#validate_unique_barcode', defaults: { format: 'js' }
   get 'product_track' => 'products#product_track', :as => "product_track"
+  get 'find_products/:query/:variants' => 'application#find_products', as: "find_products", format: :json
 
   resources :available_products, except: [:edit, :update] do
     get 'edit_price' => 'available_products#edit_price'
@@ -255,5 +256,7 @@ Rails.application.routes.draw do
   ## soporte
   get "contact_support" => "supports#contact_support"
   get "system_updates" => "supports#system_updates"
+
+  resources :promotions, except: [:show, :update, :edit]
 end
 # rubocop:enable Metrics/BlockLength

+ 14 - 0
db/migrate/20180816020521_create_promotions.rb

@@ -0,0 +1,14 @@
+class CreatePromotions < ActiveRecord::Migration
+  def change
+    create_table :promotions do |t|
+      t.date :start_date, null: false
+      t.date :end_date, null: false
+      t.decimal :percent, default: 0
+      t.belongs_to :product, index: true
+      t.belongs_to :category, index: true
+      t.belongs_to :user, index: true
+      t.integer :status, null: false, default: 1
+      t.timestamps null: false
+    end
+  end
+end

+ 5 - 0
db/migrate/20180816022444_add_promotion_id_to_pre_sale.rb

@@ -0,0 +1,5 @@
+class AddPromotionIdToPreSale < ActiveRecord::Migration
+  def change
+    add_column :pre_sales, :promotion_id, :integer, index: true, null: true unless column_exists? :pre_sales, :promotion_id
+  end
+end

+ 6 - 0
db/migrate/20180816171401_add_unit_price_w_discount_to_pre_sale.rb

@@ -0,0 +1,6 @@
+class AddUnitPriceWDiscountToPreSale < ActiveRecord::Migration
+  def change
+    add_column :pre_sales, :unit_price_w_discount, :decimal, precision: 10, scale: 2, null: true
+    add_column :sales_details, :unit_price_w_discount, :decimal, precision: 10, scale: 2, null: true
+  end
+end

+ 5 - 0
db/migrate/20180816181027_add_sub_category_to_promotion.rb

@@ -0,0 +1,5 @@
+class AddSubCategoryToPromotion < ActiveRecord::Migration
+  def change
+    add_column :promotions, :subcategory_id, :integer, null: true
+  end
+end

+ 5 - 0
db/migrate/20180817233420_add_haggle_percent_to_pre_sale.rb

@@ -0,0 +1,5 @@
+class AddHagglePercentToPreSale < ActiveRecord::Migration
+  def change
+    add_column :pre_sales, :haggle_percent, :integer, default: 0, null: true
+  end
+end

+ 31 - 10
db/schema.rb

@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20180727152745) do
+ActiveRecord::Schema.define(version: 20180817233420) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -383,6 +383,9 @@ ActiveRecord::Schema.define(version: 20180727152745) do
     t.integer  "special_price_id"
     t.decimal  "unit_price",                      precision: 10, scale: 2
     t.decimal  "haggle",                          precision: 10, scale: 2, default: 0.0
+    t.integer  "promotion_id"
+    t.decimal  "unit_price_w_discount",           precision: 10, scale: 2
+    t.integer  "haggle_percent",                                           default: 0
   end
 
   create_table "pre_transfers", force: :cascade do |t|
@@ -502,6 +505,23 @@ ActiveRecord::Schema.define(version: 20180727152745) do
 
   add_index "products_variants", ["product_id"], name: "index_products_variants_on_product_id", using: :btree
 
+  create_table "promotions", force: :cascade do |t|
+    t.date     "start_date",                   null: false
+    t.date     "end_date",                     null: false
+    t.decimal  "percent",        default: 0.0
+    t.integer  "product_id"
+    t.integer  "category_id"
+    t.integer  "user_id"
+    t.integer  "status",         default: 1,   null: false
+    t.datetime "created_at",                   null: false
+    t.datetime "updated_at",                   null: false
+    t.integer  "subcategory_id"
+  end
+
+  add_index "promotions", ["category_id"], name: "index_promotions_on_category_id", using: :btree
+  add_index "promotions", ["product_id"], name: "index_promotions_on_product_id", using: :btree
+  add_index "promotions", ["user_id"], name: "index_promotions_on_user_id", using: :btree
+
   create_table "purchase_details", force: :cascade do |t|
     t.integer  "purchase_id"
     t.integer  "product_id"
@@ -561,16 +581,17 @@ ActiveRecord::Schema.define(version: 20180727152745) do
   create_table "sales_details", force: :cascade do |t|
     t.integer  "sale_id"
     t.integer  "product_id"
-    t.decimal  "quantity",                                                null: false
-    t.decimal  "amount",           precision: 10, scale: 2, default: 0.0, null: false
-    t.decimal  "tax",              precision: 10, scale: 2, default: 0.0, null: false
-    t.decimal  "discount",         precision: 10, scale: 2, default: 0.0, null: false
-    t.decimal  "total",            precision: 10, scale: 2, default: 0.0, null: false
-    t.integer  "status",                                    default: 1,   null: false
-    t.datetime "created_at",                                              null: false
-    t.datetime "updated_at",                                              null: false
+    t.decimal  "quantity",                                                     null: false
+    t.decimal  "amount",                precision: 10, scale: 2, default: 0.0, null: false
+    t.decimal  "tax",                   precision: 10, scale: 2, default: 0.0, null: false
+    t.decimal  "discount",              precision: 10, scale: 2, default: 0.0, null: false
+    t.decimal  "total",                 precision: 10, scale: 2, default: 0.0, null: false
+    t.integer  "status",                                         default: 1,   null: false
+    t.datetime "created_at",                                                   null: false
+    t.datetime "updated_at",                                                   null: false
     t.integer  "special_price_id"
-    t.decimal  "unit_price",       precision: 10, scale: 2
+    t.decimal  "unit_price",            precision: 10, scale: 2
+    t.decimal  "unit_price_w_discount", precision: 10, scale: 2
   end
 
   add_index "sales_details", ["product_id"], name: "index_sales_details_on_product_id", using: :btree