Selaa lähdekoodia

printing labels

chemi ledon 7 vuotta sitten
vanhempi
commit
097131d472

+ 1 - 0
.gitignore

@@ -35,3 +35,4 @@ POS_variantes.pdf
 /public.sql
 log
 public/pdfs
+public/barcodes

+ 3 - 1
Gemfile

@@ -65,6 +65,7 @@ gem 'rails-jquery-autocomplete'
 gem 'font-awesome-sass', '~> 4.5.0'
 gem 'wicked_pdf'
 gem 'wkhtmltopdf-binary'
+gem 'pdfjs_viewer-rails' # pdf viewer
 gem 'twitter_bootstrap_wizard_rails'
 gem 'bootstrap-tagsinput-rails'
 gem 'acts-as-taggable-on', '~> 3.4' # then run rake acts_as_taggable_on_engine:install:migrations
@@ -73,7 +74,8 @@ gem 'will_paginate'
 gem "font-awesome-rails"
 gem 'carrierwave', '>= 1.0.0.rc', '< 2.0'
 gem 'mini_magick'
-
+gem 'barby'
+gem 'chunky_png'
 # gem 'ajax-datatables-rails', git: 'git://github.com/antillas21/ajax-datatables-rails.git', branch: 'master'
 # gem 'amcharts.rb'
 # Use ActiveModel has_secure_password

+ 1 - 0
app/assets/javascripts/application.js

@@ -61,6 +61,7 @@
 //= require backstretch/jquery.backstretch.min.js
 //= require plugins/login-5.js
 //= require jquery-barcodeListener.js
+//= require jquery-code-scanner.js
 //= require jquery.remotipart
 //= require accounting.min.js
 

+ 49 - 0
app/assets/javascripts/jquery-code-scanner.js

@@ -0,0 +1,49 @@
+(function ($) {
+    $.fn.codeScanner = function (options) {
+        var settings = $.extend({}, $.fn.codeScanner.defaults, options);
+
+        return this.each(function () {
+            var pressed = false;
+            var chars = [];
+            var $input = $(this);
+
+            $(window).keypress(function (e) {
+                var keycode = (e.which) ? e.which : e.keyCode;
+                if ((keycode >= 65 && keycode <= 90) ||
+                    (keycode >= 97 && keycode <= 122) ||
+                    (keycode >= 48 && keycode <= 57)
+                ) {
+                    chars.push(String.fromCharCode(e.which));
+                }
+                // console.log(e.which + ":" + chars.join("|"));
+                if (pressed == false) {
+                    setTimeout(function () {
+                        if (chars.length >= settings.minEntryChars) {
+                            var barcode = chars.join('');
+                            settings.onScan($input, barcode);
+                        }
+                        chars = [];
+                        pressed = false;
+                    }, settings.maxEntryTime);
+                }
+                pressed = true;
+            });
+
+            $(this).keypress(function (e) {
+                if (e.which === 13) {
+                    e.preventDefault();
+                }
+            });
+
+            return $(this);
+        });
+    };
+
+    $.fn.codeScanner.defaults = {
+        minEntryChars: 8,
+        maxEntryTime: 100,
+        onScan: function ($element, barcode) {
+            $element.val(barcode);
+        }
+    };
+})(jQuery);

+ 32 - 1
app/controllers/products_controller.rb

@@ -91,15 +91,19 @@ class ProductsController < ApplicationController
   # POST /products.json
   # rubocop:disable Metrics/BlockLength
   def create
-    @with_presentation = true
     @product = Product.new(product_params)
     if @product.presentation
       @product.is_parent = true
     end
     message = 'El producto ' + @product.sku + ' fue creado.'
     @product.audit_comment = message
+
     respond_to do |format|
       if @product.save
+        unless @product.is_parent?
+          @product.generate_barcode
+          @product.save
+        end
         @product.save_variants(current_user)
         ##--- Para cuando se agrega un producto desde purchase
         if params[:remoto] == "true"
@@ -287,6 +291,33 @@ class ProductsController < ApplicationController
     end
   end
 
+  def labels_list
+    @product = Product.find(params[:product_id])
+    if @product.presentation
+      @pointsales = @product.get_available_children(true)
+    end
+  end
+
+  def print_labels
+    @products = JSON.parse params[:products]
+    @pointsale_id = params[:pointsale_id]
+    @products.each do |obj|
+      obj['product'] = Product.find(obj['id'])
+      obj['price'] = @pointsale_id.present? ? obj['product'].get_price_sale(@pointsale_id) : obj['product'].price_sale
+    end
+
+    file_name = "etiquetas_#{Time.now.to_i}"
+    pdf = render_to_string pdf: file_name, template: "products/print_labels.pdf.erb", layout: 'labels.html.erb', locals: { objs: @products }, show_as_html: params.key?('debug'), page_width: '62mm', page_height: '300mm'
+    save_path = Rails.public_path.join("pdfs", "#{file_name}.pdf")
+    File.open(save_path, 'wb') do |file|
+      file << pdf
+    end
+
+    respond_to do |format|
+      format.js { render action: "print_labels", locals: { pdf_path: "pdfs/#{file_name}.pdf" } }
+    end
+  end
+
   private
 
   # Use callbacks to share common setup or constraints between actions.

+ 2 - 1
app/datatables/products_datatable.rb

@@ -1,7 +1,7 @@
 class ProductsDatatable
   delegate :params, :link_to, :number_to_currency, :image_tag,:fa_icon,
     :products_path, :edit_product_path, :product_update_status_path, :product_prices_path,
-    :available_products_path, :available_product_edit_price_path, :product_edit_variants_path,
+    :available_products_path, :available_product_edit_price_path, :product_edit_variants_path, :product_labels_path,
     to: :@view
   def initialize(view, user)
     @view = view
@@ -110,6 +110,7 @@ private
       links += link_to(fa_icon('edit'), edit_product_path(product), { class: 'btn btn-icon-only btn-primary filtros', title: "Modificar producto" })
       links += link_to(fa_icon('copy'), product_edit_variants_path(product.id), { class: 'btn btn-icon-only btn-info filtros', title: "Modificar variantes del producto" }) if product.presentation
       links += link_to(fa_icon('list-alt'), product_prices_path(product), { remote: true, class: 'btn btn-icon-only green-dark', title: "Precios en punto de venta" })
+      links += link_to(fa_icon('ticket'), product_labels_path(product), { remote: true, class: 'btn btn-icon-only purple', title: "Imprimir etiquetas" })
       links += (link_to(fa_icon('toggle-off'), product_update_status_path(product), class: 'btn btn-icon-only default', title: "Desactivar producto", data: { confirm: '¿Está seguro de desactivar el producto?', method: 'post' })) if product.active?
       links += (link_to(fa_icon('toggle-on'), product_update_status_path(product), class: 'btn btn-icon-only green-jungle', title: "Activar producto", data: { confirm: '¿Está seguro de activar el producto?', method: 'post' })) if product.inactive?
       links += (link_to(fa_icon('trash-o'), product, class: 'btn btn-icon-only btn-danger', title: "Eliminar producto", data: { confirm: '¿Esta seguro de eliminar el producto?', method: :delete })) if product.can_be_deleted?

+ 3 - 0
app/helpers/application_helper.rb

@@ -8,4 +8,7 @@ module ApplicationHelper
     end
   end
 
+  def wicked_pdf_barcode_image(img, options={})
+    image_tag "file://#{Rails.root.join('public', 'barcodes', img)}", options
+  end
 end

+ 20 - 0
app/models/product.rb

@@ -1,3 +1,7 @@
+require 'barby'
+require 'barby/barcode/code_39'
+require 'barby/outputter/png_outputter'
+
 class Product < ActiveRecord::Base
   ##--- Asociaciones
   belongs_to :unit
@@ -207,6 +211,11 @@ class Product < ActiveRecord::Base
 
           @products_variant.attributes_json = attributes_json.to_json
           @products_variant.save
+
+          @products_variant.generate_barcode
+
+          @products_variant.save
+
           if current_user.usertype == 'G'
             AvailableProduct.create(product_id: @products_variant.id, pointsale_id: current_user.pointsale_id, stock: 0)
           end
@@ -423,4 +432,15 @@ class Product < ActiveRecord::Base
       AvailableProduct.where("product_id IN (?)", children)
     end
   end
+
+  def generate_barcode
+    if barcode.blank?
+      barcode_generated = format('%07d', id)
+      self.barcode = barcode_generated
+      save_path = Rails.public_path.join('barcodes', "#{barcode_generated}.png")
+      File.open(save_path, 'wb'){ |f|
+        f.write Barby::Code39.new(barcode_generated).to_png(:height => 30, :margin => 5)
+      }
+    end
+  end
 end

+ 30 - 0
app/views/layouts/labels.html.erb

@@ -0,0 +1,30 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset='utf-8' />
+    <style type="text/css">
+      @media print {
+        .pdf-body {
+          width: 62mm;
+          height: auto;
+        }
+      }
+      body {
+        width: 58mm;
+        height: auto;
+        font-family: 'OpenSansRegular';
+        font-size: 11px;
+      }
+      .text-center {
+        text-align: center;
+      }
+
+      .text-right {
+        text-align: right;
+      }
+    </style>
+  </head>
+  <body>
+    <%= yield %>
+  </body>
+</html>

+ 303 - 270
app/views/products/_form.html.erb

@@ -1,275 +1,297 @@
 <!-- BEGIN FORM-->
-<%= form_for(@product, html: { class: "form-horizontal" }) do |f| %>
-  <div class="portlet-body form" id="products_form">
-    <% if @product.errors.any? %>
-      <div class="alert alert-danger">
-        <strong>Tiene <%= pluralize(@product.errors.count, "error") %> no se puede guardar el producto</strong><br>
-      </div>
-    <% end %>
-    <div class="form-body">
-      <input type="text" class="hidden" value="<%= @product.id %>" id="idproduct">
-      <h4 class="form-section">Información general</h4>
-      <div class="row">
-        <div class="col-md-7">
-          <%= hidden_field_tag :gain_margin, @pos_config.gain_margin %>
-          <%= hidden_field_tag :tax_percent, @pos_config.tax_percent %>
-          <div class="form-group">
-            <%= f.label :sku, { class: "col-md-3 col-sm-2 control-label" } do %>SKU <span class="required">*</span>
-            <% end %>
-            <div class="col-md-9 col-sm-4">
-              <div class="input-icon">
-                <i class="fa fa-tag"></i>
-                <%= f.text_field :sku, { class: "form-control input-medium", readonly: true } %>
-              </div>
-              <span class="help-block">El SKU se genera automáticamente en base a la línea y sublínea seleccionadas. </span>
-            </div>
-          </div>
-          <div class="form-group">
-            <%= f.label :barcode, { class: "col-md-3 col-sm-2 control-label" } do %> Código de barras <span class="required">*</span> <% end %>
-            <div class="col-md-9 col-sm-4">
-              <div class="input-icon">
-                <i class="fa fa-barcode"></i>
-                <%= f.text_field :barcode, { class: "form-control input-medium", readonly: true } %>
-              </div>
-              <span class="help-block">Para indicar el código de barras, debe escanearlo. </span>
-            </div>
-          </div>
-          <div class="form-group">
-            <%= f.label :name, "Producto", { class: "col-md-3 col-sm-2 control-label" } do %>Nombre <span class="required">*</span> <% end %>
-            <div class="col-md-9 col-sm-4">
-              <%= f.text_field :name, { class: "form-control input-large" } %>
-            </div>
-          </div>
-          <div class="form-group">
-            <%= f.label :description, "Descripción", { class: "col-md-3 col-sm-2 control-label" } %>
-            <div class="col-md-9 col-sm-4">
-              <%= f.text_area :description, { class: "form-control input-large" } %>
-            </div>
-          </div>
-          <div class="form-group">
-            <%= f.label :unit_id, "Unidad de medida", { class: "col-md-3 col-sm-2 control-label" } do %>Unidad de medida <span class="required">*</span> <% end %>
-            <div class="col-md-9 col-sm-4">
-              <%= f.collection_select :unit_id, Unit.vigentes, :id, :unit, { prompt: "Seleccione" }, { class: "form-control input-medium" } %>
-            </div>
-          </div>
-        </div>
-        <div class="col-md-5">
-          <div class="form-group">
-            <%= f.label :img_product, "Imagen producto", { class: "col-md-5 col-sm-2 control-label" } %>
-            <div class="col-md-7 col-sm-4">
-              <div class="fileinput fileinput-new" data-provides="fileinput">
-                <div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
-                  <% if @product.img_product? %>
-                    <%= image_tag @product.img_product %>
-                  <% else %>
-                    <%= image_tag "no-image.png" %>
-                  <% end %>
-                </div>
-                <div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px;"> </div>
-                <div>
-                  <span class="btn default btn-file">
-                    <span class="fileinput-new"> Seleccione imagen </span>
-                    <span class="fileinput-exists"> Cambiar </span>
-                    <%= f.file_field :img_product, { class: "default" } %>
-                    <%= f.hidden_field :img_product_cache %>
-                  </span>
-                  <a href="javascript:;" class="btn red fileinput-exists" data-dismiss="fileinput"> Borrar </a>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-      <h4 class="form-section">Información del producto</h4>
-      <div class="row">
-        <div class="form-group ">
-          <%= f.label :inventory, "¿Se maneja inventario de este producto?", { class: "col-md-3 col-sm-3 control-label" } do %>¿Este producto tendrá inventario? <span class="required">*</span> <% end %>
-          <div class="col-md-9 col-sm-2">
-            <%= f.check_box(:inventory,
-              {
-                class: "make-switch",
-                data: {
-                  on_color: "success",
-                  off_color: "danger",
-                  on_text: "Si",
-                  off_text: "No"
-                }
-              }, "true","false"
-            ) %>
-          </div>
-        </div>
-        <div class="form-group">
-          <%
-            @category = nil
-            @subcategory = nil;
-            @prompt = 'Seleccione'
-            @disabled = true
-            if @product.categories[0].present? && @product.categories[0].parent_id == 0
-              @category = @product.categories[0]
-              @prompt = ''
-            elsif @product.categories[0].present?
-              @category = Category.find(@product.categories[0].parent_id)
-              @subcategory = @product.categories[0]
-              @disabled = false
-            end
-          %>
-          <%= label_tag :categorias, "Líneas de producto", { class: "col-md-3 col-sm-3 control-label" } do %>Líneas de producto <span class="required">*</span> <% end %>
-          <div class="col-md-4 col-sm-4">
-            <%= select_tag 'categorias', options_from_collection_for_select(Category.activos_padre, 'id', 'category', :selected => (@category.id unless @category.nil?)), { :include_blank => "Seleccione", :class => 'form-control' } %>
-          </div>
-        </div>
-        <div class="form-group">
-          <%= f.label :category_ids, "Sublinea del producto", { class: "col-md-3 col-sm-3 control-label" }   %>
-          <div class="col-md-4 col-sm-4">
-            <%= f.collection_select(:category_ids, @category.nil? ? {} : Category.where(:parent_id => @category.id), :id, :category , options = { :include_blank => @prompt, :selected => (@subcategory.id unless @subcategory.nil?) }, :class => "form-control", :disabled => @disabled) %>
-            <%= f.hidden_field :category_ids, { :id => 'subcategory_hidden' } %>
-          </div>
-        </div>
-        <div class="form-group">
-          <%= f.label :include_purchase_tax, "¿Incluir IVA en compra?", { class: "col-md-3 col-sm-3 control-label" } do %> ¿Incluir IVA en compra? <span class="required">*</span> <% end %>
-          <div class="col-md-9 col-sm-2">
-            <%= f.check_box(:include_purchase_tax,
-              {
-                class: "make-switch",
-                data: {
-                  on_color: "success",
-                  off_color: "danger",
-                  on_text: "Si",
-                  off_text: "No"
-                }
-              }, "1","0"
-            ) %>
-          </div>
-        </div>
-        <div class="form-group">
-          <%= f.label :include_sale_tax, "¿Incluir IVA en venta?", { class: "col-md-3 col-sm-3 control-label" } do %> ¿Incluir IVA en venta? <span class="required">*</span> <% end %>
-          <div class="col-md-9 col-sm-2">
-            <%= f.check_box(:include_sale_tax,
-              {
-                class: "make-switch",
-                data: {
-                  on_color: "success",
-                  off_color: "danger",
-                  on_text: "Si",
-                  off_text: "No"
-                }
-              }, "1","0"
-            ) %>
-          </div>
-        </div>
-        <div class="form-group">
-          <%= f.label :is_in_dollars, "¿Se compra en dólares?", { class: "col-md-3 col-sm-3 control-label" } do %> ¿Se compra en dólares? <span class="required">*</span> <% end %>
-          <div class="col-md-9 col-sm-2">
-            <%= f.check_box(:is_in_dollars,
-              {
-                class: "make-switch",
-                data: {
-                  on_color: "success",
-                  off_color: "danger",
-                  on_text: "Si",
-                  off_text: "No"
-                }
-              }, "true","false"
-            ) %>
-          </div>
-        </div>
-        <div class="form-group inventory">
-          <%= f.label :price_base_dollars, "Precio de compra neto", { class: "col-md-3 col-sm-3 control-label" } %>
-          <div class="col-md-3 col-sm-3">
-            <div class="input-group <%= (@product.persisted? && @product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_usd_div">
-              <span class="input-group-addon"> $ </span>
-              <%= f.text_field :price_base_dollars, { class: "form-control mask_decimal" } %>
-              <span class="input-group-addon"> USD </span>
-            </div>
-            <div class="input-group <%= (@product.persisted? && !@product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_mxn_div">
-              <span class="input-group-addon"> $ </span>
-              <%= f.text_field :price_base, { class: "form-control mask_decimal" } %>
-              <span class="input-group-addon"> MXN </span>
-            </div>
-          </div>
-        </div>
-        <div class="form-group inventory">
-          <%= f.label :price_sale, "Precio de venta base", { class: "col-md-3 col-sm-3 control-label" } do %> Precio de venta base
-            <span class="required">*</span>
-          <% end %>
-          <div class="col-md-3 col-sm-3">
-            <div class="input-group">
-              <span class="input-group-addon"> $ </span>
-              <%= f.text_field :price_sale, { class: "form-control mask_decimal" } %>
-              <span class="input-group-addon"> MXN </span>
-            </div>
-          </div>
-          <div class="col-md-6 col-sm-6">
-            <span class="help-block">El precio de venta base será calculado de acuerdo al porcentaje de utilidad especificado, sólo en caso de especificar el precio de compra neto.</span>
-          </div>
-        </div>
-        <div class="form-group">
-          <%= f.label :presentation, "¿El producto tiene variantes?", { class: "col-md-3 col-sm-3 control-label" } do %>¿El producto tiene variantes?
-            <span class="required">*</span>
-          <% end %>
-          <div class="col-md-9 col-sm-2">
-            <%= f.check_box(:presentation,
-              {
-                class: "make-switch",
-                data: {
-                  on_color: "success",
-                  off_color: "danger",
-                  on_text: "Si",
-                  off_text: "No"
-                },
-                readonly: @with_presentation
-              }, "true", "false"
-            ) %>
-          </div>
-        </div>
-      </div>
-      <h4 class="form-section presentaciones">Variantes</h4>
-      <div class="row presentaciones">
-        <% if @product.persisted? %>
-        <div class="col-md-offset-2 col-md-9 col-sm-offset-2 col-sm-9">
-          <%= render 'products_children' %>
-        </div>
-        <% else %>
-        <div class="col-md-9 col-md-offset-3">
-          <div class="alert alert-warning hidden">
-            Son <strong id="variantes"></strong> variantes del producto.
-          </div>
-        </div>
-        <div class="form-group ">
-          <%= f.label :size_list, "Tallas", { class: "col-md-3 control-label" } %>
-          <div class="col-md-9">
-            <%= f.collection_select(:size_list, @product.size_list, :to_s, :to_s, { include_blank: false }, { class: "form-control input-medium", 'data-role'=>'tagsinput', multiple: true }) %>
-            <span class="help-block">Ingrese las diferentes tallas, separándolas con coma (,) </span>
-          </div>
-        </div>
-        <div class="form-group ">
-          <%= f.label :color_list, "Colores", { class: "col-md-3 control-label" } %>
-          <div class="col-md-9">
-            <%= f.collection_select(:color_list, @product.color_list, :to_s, :to_s, { include_blank: false }, { class: "form-control input-medium", 'data-role'=>'tagsinput', multiple: true }) %>
-            <span class="help-block">Ingrese los diferentes colores, separándolos con coma (,) </span>
-          </div>
-        </div>
-        <div class="form-group ">
-          <%= f.label :style_list, "Estilos", { class: "col-md-3 control-label" } %>
-          <div class="col-md-9">
-            <%= f.collection_select(:style_list, @product.style_list, :to_s, :to_s, { include_blank: false }, { class: "form-control input-medium", 'data-role'=>'tagsinput', multiple: true }) %>
-            <span class="help-block">Ingrese los diferentes estilos, separándolos con coma (,) </span>
-          </div>
-        </div>
-        <%= hidden_field_tag :variants %>
-        <% end %>
-      </div>
-    </div>
-    <div class="form-actions">
-      <div class="row">
-        <div class="col-md-offset-3 col-md-9">
-          <%= f.submit 'Guardar', { class: "btn green" } %>
-          <%= link_to 'Cancelar', products_path(filter: @filter, current_page: @current_page), { class: "btn default" } %>
-        </div>
-      </div>
-    </div>
-  </div>
+<%= form_for(@product, :html => {:class=>"form-horizontal"}) do |f| %>
+	<div class="portlet-body form" id="products_form">
+		<% if @product.errors.any? %>
+			<div class="alert alert-danger">
+				<strong>Tiene <%= pluralize(@product.errors.count, "error") %> no se puede guardar el producto</strong><br>
+			</div>
+		<% end %>
+		<div class="form-body">
+			<input type="text" class="hidden" value="<%= @product.id %>" id="idproduct">
+			<h4 class="form-section">Información general</h4>
+			<div class="row">
+				<div class="col-md-7">
+					<%= hidden_field_tag :gain_margin, @pos_config.gain_margin %>
+					<%= hidden_field_tag :tax_percent, @pos_config.tax_percent %>
+					<div class="form-group">
+						<%= f.label :sku, {:class=>"col-md-3 control-label"} do %>SKU <span class="required">*</span>
+						<% end %>
+						<div class="col-md-9">
+							<div class="input-icon">
+								<i class="fa fa-tag"></i>
+								<%= f.text_field :sku, {:class=>"form-control input-medium", :readonly => true } %>
+							</div>
+							<span class="help-block">El SKU se genera automaticamente en base a la linea y sublinea seleccionadas. </span>
+						</div>
+					</div>
+					<div class="form-group">
+						<%= f.label :barcode, {:class=>"col-md-3 control-label"} do %> Codigo de barras <span class="required">*</span>
+						<% end %>
+						<div class="col-md-9">
+							<div class="input-icon">
+								<i class="fa fa-barcode"></i>
+								<%= f.text_field :barcode, {:class=>"form-control input-medium", :readonly => true } %>
+							</div>
+							<span class="help-block">Si el codigo de barras ya existe escaneelo, si desea generarlo clickee el switch. </span>
+						</div>
+					</div>
+					<div class="form-group">
+						<%= f.label :name, "Producto", {:class=>"col-md-3 control-label"} do %>Nombre
+							<span class="required">*</span>
+						<% end %>
+						<div class="col-md-9">
+							<%= f.text_field :name, {:class=>"form-control"} %>
+						</div>
+					</div>
+					<div class="form-group">
+						<%= f.label :description, "Descripción", {:class=>"col-md-3 control-label"} %>
+						<div class="col-md-9">
+							<%= f.text_area :description, {:class=>"form-control"} %>
+						</div>
+					</div>
+					<div class="form-group">
+						<%= f.label :unit_id, "Unidad de medida", {:class=>"col-md-3 control-label"} do %>Unidad de medida <span class="required">*</span>
+						<% end %>
+						<div class="col-md-9">
+							<%= f.collection_select :unit_id, Unit.vigentes, :id, :unit, {:prompt => "Seleccione"}, {:class => "form-control input-medium"}   %>
+						</div>
+					</div>
+				</div>
+				<div class="col-md-5">
+					<div class="form-group">
+						<%= f.label :img_product, "Imagen producto", {:class=>"col-md-5 control-label"} %>
+						<div class="col-md-7">
+							<div class="fileinput fileinput-new" data-provides="fileinput">
+								<div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
+									<% if @product.img_product? %>
+										<%= image_tag @product.img_product %>
+									<% else %>
+										<%= image_tag "no-image.png" %>
+									<% end %>
+								</div>
+								<div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px;"> </div>
+								<div>
+									<span class="btn default btn-file">
+										<span class="fileinput-new"> Seleccione imagen </span>
+										<span class="fileinput-exists"> Cambiar </span>
+										<%= f.file_field :img_product, {:class=>"default"} %>
+										<%= f.hidden_field :img_product_cache %>
+									</span>
+									<a href="javascript:;" class="btn red fileinput-exists" data-dismiss="fileinput"> Borrar </a>
+								</div>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+			<h4 class="form-section">Información del producto</h4>
+			<div class="row">
+				<div class="form-group ">
+					<%= f.label :inventory, "¿Se maneja inventario de este producto?", {:class=>"col-md-3 control-label"} do %>¿Este producto tendrá inventario?
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-9">
+						<%= f.check_box(:inventory,
+							{
+								class: "make-switch",
+								data: {
+									on_color: "success",
+									off_color: "danger",
+									on_text: "Si",
+									off_text: "No"
+								}
+							}, "true","false"
+						) %>
+					</div>
+				</div>
+
+				<div class="form-group">
+					<%
+						@category = nil
+						@subcategory = nil;
+						@prompt = 'Seleccione'
+						@disabled = true
+						if @product.categories[0].present? && @product.categories[0].parent_id == 0
+							@category = @product.categories[0]
+							@prompt = ''
+						elsif @product.categories[0].present?
+							@category = Category.find(@product.categories[0].parent_id)
+							@subcategory = @product.categories[0]
+							@disabled = false
+						end
+					%>
+					<%= label_tag :categorias, "Líneas de producto", {:class=>"col-md-3 control-label"} do %>Líneas de producto
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-4">
+							<%= select_tag 'categorias',
+								options_from_collection_for_select(Category.activos_padre, 'id', 'category',
+									:selected => (@category.id unless @category.nil?)),
+									{:include_blank => "Seleccione", :class => 'form-control'} %>
+					</div>
+				</div>
+				<div class="form-group">
+					<%= f.label :category_ids, "Sublinea del producto", {:class=>"col-md-3 control-label"}   %>
+					<div class="col-md-4">
+						<%= f.collection_select(:category_ids, @category.nil? ? {} : Category.where(:parent_id => @category.id), :id, :category , options ={:include_blank => @prompt, :selected => (@subcategory.id unless @subcategory.nil?) }, :class => "form-control", :disabled => @disabled) %>
+						<%= f.hidden_field :category_ids, {:id => 'subcategory_hidden'}%>
+					</div>
+				</div>
+				<div class="form-group">
+					<%= f.label :include_purchase_tax, "¿Incluir IVA en compra?", {:class=>"col-md-3 control-label"} do %> ¿Incluir IVA en compra?
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-9">
+						<%= f.check_box(:include_purchase_tax,
+							{
+								class: "make-switch",
+								data: {
+									on_color: "success",
+									off_color: "danger",
+									on_text: "Si",
+									off_text: "No"
+								}
+							}, "1","0"
+						) %>
+					</div>
+				</div>
+				<div class="form-group">
+					<%= f.label :include_sale_tax, "¿Incluir IVA en venta?", {:class=>"col-md-3 control-label"} do %> ¿Incluir IVA en venta?
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-9">
+						<%= f.check_box(:include_sale_tax,
+							{
+								class: "make-switch",
+								data: {
+									on_color: "success",
+									off_color: "danger",
+									on_text: "Si",
+									off_text: "No"
+								}
+							}, "1","0"
+						) %>
+					</div>
+				</div>
+				<div class="form-group">
+					<%= f.label :is_in_dollars, "¿Se compra en dolares?", {:class=>"col-md-3 control-label"} do %> ¿Se compra en dolares?
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-9">
+						<%= f.check_box(:is_in_dollars,
+							{
+								class: "make-switch",
+								data: {
+									on_color: "success",
+									off_color: "danger",
+									on_text: "Si",
+									off_text: "No"
+								}
+							}, "true","false"
+						) %>
+					</div>
+				</div>
+				<div class="form-group inventory">
+					<%= f.label :price_base_dollars, "Precio de compra neto", {:class=>"col-md-3 control-label"} %>
+					<div class="col-md-3">
+
+						<div class="input-group <%=(@product.persisted? && @product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_usd_div">
+							<span class="input-group-addon"> $ </span>
+							<%= f.text_field :price_base_dollars, {:class=>"form-control mask_decimal"} %>
+							<span class="input-group-addon"> USD </span>
+						</div>
+
+						<div class="input-group <%=(@product.persisted? && !@product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_mxn_div">
+							<span class="input-group-addon"> $ </span>
+							<%= f.text_field :price_base, {:class=>"form-control mask_decimal"} %>
+							<span class="input-group-addon"> MXN </span>
+						</div>
+
+					</div>
+				</div>
+				<div class="form-group inventory">
+					<%= f.label :price_sale, "Precio de venta base", {:class=>"col-md-3 control-label"} do %> Precio de venta base
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-3">
+						<div class="input-group">
+							<span class="input-group-addon"> $ </span>
+							<%= f.text_field :price_sale, {:class=>"form-control mask_decimal"} %>
+							<span class="input-group-addon"> MXN </span>
+						</div>
+					</div>
+					<div class="col-md-6">
+						<span class="help-block">El precio de venta base será calculado de acuerdo al porcentaje de utilidad especificado, solo en en caso de especificar el precio de compra neto.</span>
+					</div>
+				</div>
+				<div class="form-group">
+					<%= f.label :presentation, "¿El producto tiene variantes?", {:class=>"col-md-3 control-label"} do %>¿El producto tiene variantes?
+						<span class="required">*</span>
+					<% end %>
+					<div class="col-md-9">
+						<%= f.check_box(:presentation,
+							{
+								class: "make-switch",
+								data: {
+									on_color: "success",
+									off_color: "danger",
+									on_text: "Si",
+									off_text: "No"
+								},
+								readonly: @with_presentation
+							}, "true","false"
+						) %>
+					</div>
+				</div>
+			</div>
+			<h4 class="form-section presentaciones">Variantes</h4>
+			<div class="row presentaciones">
+				<% if @product.persisted? %>
+				<div class="col-md-9 col-md-offset-3">
+					<%= render 'products_children' %>
+				</div>
+				<% else %>
+				<div class="col-md-9 col-md-offset-3">
+					<div class="alert alert-warning hidden">
+						Son <strong id="variantes"></strong> variantes del producto.
+					</div>
+				</div>
+				<div class="form-group ">
+					<%= f.label :size_list, "Tallas", {:class=>"col-md-3 control-label"} %>
+					<div class="col-md-9">
+						<%= f.collection_select(:size_list, @product.size_list, :to_s, :to_s, { :include_blank => false }, {:class=>"form-control input-medium", 'data-role'=>'tagsinput', :multiple => true} ) %>
+						<span class="help-block">Ingrese las diferentes tallas, separandolas con coma (,). </span>
+					</div>
+				</div>
+				<div class="form-group ">
+					<%= f.label :color_list, "Colores", {:class=>"col-md-3 control-label"} %>
+					<div class="col-md-9">
+						<%= f.collection_select(:color_list, @product.color_list, :to_s, :to_s, { :include_blank => false }, {:class=>"form-control input-medium", 'data-role'=>'tagsinput', :multiple => true} ) %>
+						<span class="help-block">Ingrese los diferentes colores, separandolos con coma (,). </span>
+					</div>
+				</div>
+				<div class="form-group ">
+					<%= f.label :style_list, "Estilos", {:class=>"col-md-3 control-label"} %>
+					<div class="col-md-9">
+						<%= f.collection_select(:style_list, @product.style_list, :to_s, :to_s, { :include_blank => false }, {:class=>"form-control input-medium", 'data-role'=>'tagsinput', :multiple => true} ) %>
+						<span class="help-block">Ingrese los diferentes estilos, separandolos con coma (,). </span>
+					</div>
+				</div>
+				<%= hidden_field_tag :variants %>
+				<% end %>
+			</div>
+		</div>
+		<div class="form-actions">
+			<div class="row">
+				<div class="col-md-offset-3 col-md-9">
+					<%= f.submit 'Guardar', {:class=>"btn green"} %>
+					<%= link_to 'Cancelar', products_path(:filter => @filter, :current_page => @current_page), {:class=>"btn default"} %>
+				</div>
+			</div>
+		</div>
+	</div>
 <% end %>
+
 <script type="text/javascript">
   var timeout = null;
   var regex = /[a,e,i,o,u]/gi;
@@ -299,6 +321,17 @@
     });
   });
 
+		<% if @product.barcode.blank? %>
+			// $('#product_barcode').codeScanner({       // SE ESTA TRIGGEREANDO AL ESCRIBIR TEXTO
+			// 	maxEntryTime: 500, // milliseconds
+			// 	minEntryChars: 5,  // characters
+			// 	onScan: function ($element, code) {
+			// 		$('#product_barcode').val(code);
+			// 		validateBarcode(code);
+			// 	}
+			// });
+		<% end %>
+
   $('#product_size_list').on('change', function(event) { getVariants(); });
   $('#product_color_list').on('change', function(event) { getVariants(); });
   $('#product_style_list').on('change', function(event) { getVariants(); });

+ 132 - 0
app/views/products/_labels_list.html.erb

@@ -0,0 +1,132 @@
+<div class="row form-horizontal">
+    <div class="col-md-12">
+        <h4><%= @product.name %></h4>
+
+        <% if @product.presentation? && @pointsales.present? %>
+            <div class="form-group">
+                <label class="col-md-3 control-label">Punto de venta</label>
+                <div class="col-md-9">
+                    <%= select_tag :pointsale_id, options_from_collection_for_select(@pointsales, :pointsale_id, :name), :include_blank => "Seleccione punto de venta",  class: "form-control select2 first_input" %>
+                    <span class="help-block">Las etiquetas se imprimirán con el precio de venta del punto de venta seleccionado</span>
+                </div>
+                <br>
+            </div>
+        <% elsif !@product.presentation? && @product.pointsales.present? %>
+            <div class="form-group">
+                <label class="col-md-3 control-label">Punto de venta</label>
+                <div class="col-md-9">
+                    <%= select_tag :pointsale_id, options_from_collection_for_select(@product.pointsales, :id, :name), :include_blank => "Seleccione punto de venta",  class: "form-control select2 first_input" %>
+                    <span class="help-block">Las etiquetas se imprimirán con el precio de venta del punto de venta seleccionado</span>
+                </div>
+                <br>
+            </div>
+        <% else %>
+            <div class="alert alert-info">
+               El producto no está asignado a un punto de venta, se imprimirán las etiquetas con el precio de venta base.
+            </div>
+        <% end %>
+
+        <% if @product.presentation? %>
+            <table id="variant_table_for_labels" class="table table-striped table-bordered table-advance table-hover">
+                <thead>
+                    <tr>
+                        <th>Variante</th>
+                        <th>Cantidad a imprimir</th>
+                    </tr>
+                </thead>
+                <tbody>
+                    <% @product.children.each do |variant| %>
+                        <tr id="row_<%= variant.id %>">
+                            <td>
+                                <%= variant.display_attributes%>
+                            </td>
+                            <td>
+                                <input type="number" min="0" class="form-control input-small" id="variant_<%= variant.id %>" value="0" pattern="^[0-9]*[1-9][0-9]*$" title="Cantidad de etiquetas a imprimir">
+                            </td>
+                        </tr>
+                    <% end %>
+                </tbody>
+            </table>
+        <% else %>
+            <div class="form-group" style="margin-bottom:20px">
+                <label class="col-md-3 control-label">Cantidad a imprimir</label>
+                <div class="col-md-3" style="padding-top:10px">
+                    <input type="number" id="quantity_to_print" value="0" class="form-control first_input">
+                    <%= hidden_field_tag 'product_id', @product.id %>
+                </div>
+            </div>
+        <% end %>
+    </div>
+</div>
+
+<div class="form-actions">
+    <div class="row">
+        <div class="col-md-9">
+            <button class="btn green" type="button" onclick="printLabels($(this))">Imprimir</button>
+            <button class="btn default" type="button" onclick="cerrarDialog()">Cancelar</button>
+        </div>
+    </div>
+</div>
+
+<script type="text/javascript">
+
+    $(document).ready(function(){
+        App.init();
+    });
+
+	function printLabels(button) {
+        try {
+            button.attr('disabled', true);
+            var pointsale_id = "";
+            var products = [];
+            if($('#pointsale_id').length) {
+                // está asignado a un P.V sacar pointsale_id
+                pointsale_id = $('#pointsale_id').val();
+                if(!pointsale_id) { throw "Selecciona punto de venta"; }
+            }
+
+            if($('#quantity_to_print').length) {
+                // es producto sin variantes
+                var product_id = $('#product_id').val();
+                var obj = { id: product_id, print: parseFloat($("#quantity_to_print").val()) || 0 };
+                products.push(obj);
+            } else {
+                // tiene variantes
+                $('#variant_table_for_labels tbody tr').each(function(row) {
+                    var idText = $(this).attr('id');
+                    var product_id = idText.substring(idText.lastIndexOf('_') + 1, idText.length);
+                    var obj = { id: product_id, print: parseFloat($("#variant_" + product_id).val()) || 0 };
+                    products.push(obj);
+                });
+            }
+
+            var checkForZeroVal = products.some(function(p){
+                return p.print > 0;
+            });
+
+            if(!checkForZeroVal) { throw "Indica la cantidad a imprimir."; }
+
+            $.ajax({
+                type: "POST",
+                url: "print_labels",
+                dataType: "script",
+                data: {
+                    products: JSON.stringify(products),
+                    pointsale_id: pointsale_id
+                },
+                success: function(xhr, status, error) {
+                    button.attr('disabled', false);
+                    toastr["success"]("Etiquetas impresas correctamente.");
+                    $('#dialog').modal('toggle');
+                }
+            });
+        } catch(err) {
+            button.attr('disabled', false);
+            toastr["error"](err);
+        }
+	}
+
+    function cerrarDialog() {
+        $('#dialog').modal('toggle');
+    }
+</script>

+ 9 - 0
app/views/products/labels_list.js.erb

@@ -0,0 +1,9 @@
+$('#dialog h3.modal-title').html("<i class='fa fa-ticket'></i> Imprimir etiquetas");
+$("#dialog .modal-body-content").html("<%= escape_javascript(render 'labels_list') %>");
+$("#dialog").modal("show");
+
+
+// Set focus to the first element
+$('#dialog').on('shown.bs.modal', function () {
+	  $('.first_input').focus();
+  });

+ 1 - 0
app/views/products/print_labels.js.erb

@@ -0,0 +1 @@
+window.open('<%= publicroot.reduced_path(file: "#{pdf_path}") %>', '_blank', '');

+ 10 - 0
app/views/products/print_labels.pdf.erb

@@ -0,0 +1,10 @@
+<div class='pdf-body'>
+    <% @products.each do |obj| %>
+        <% (1..obj['print']).each do |i| %>
+            <span><%= obj['product'].name %></span>
+            <%= wicked_pdf_barcode_image("#{obj['product'].barcode}.png", options = { width: 45, height: 30 }) %>
+            <span><%= obj['product'].display_attributes %></span>
+            <span><%= obj['price'] %></span>
+        <% end %>
+    <% end %>
+</div>

+ 4 - 0
config/routes.rb

@@ -17,6 +17,8 @@ Rails.application.routes.draw do
   match "/404", to: "errors#not_found", via: :all
   match "/500", to: "errors#internal_server_error", via: :all
 
+  mount PdfjsViewer::Rails::Engine => "/", as: 'publicroot'
+
   devise_scope :user do
     get "login", to: "devise/sessions#new"
     get "logout", to: "devise/sessions#destroy"
@@ -95,12 +97,14 @@ Rails.application.routes.draw do
     patch 'update_from_purchase' => 'products#update_from_purchase', :constraints => ->(req) { req.xhr? }
     post 'update_status' => 'products#update_status', :as => "update_status"
     get 'prices' => 'products#list_prices'
+    get 'labels' => 'products#labels_list'
     get 'variantsprices/:pointsale_id' => 'products#list_prices_variants', :format => :json
     get 'edit_variants' => 'products#edit_variants'
     post 'update_variants' => 'products#update_variants'
   end
   get 'validate_unique_barcode/:barcode' => 'products#validate_unique_barcode', defaults: { format: 'js' }
   get 'product_track' => 'products#product_track', :as => "product_track"
+  post 'print_labels' => 'products#print_labels', :format => :js, :as => "print_labels"
   get 'find_products/:query/:variants' => 'application#find_products', as: "find_products", format: :json
 
   resources :available_products, except: [:edit, :update] do

BIN
erd.pdf