_form.html.erb 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463
  1. <!-- BEGIN FORM-->
  2. <%= form_for(@product, :html => {:class=>"form-horizontal"}) do |f| %>
  3. <div class="portlet-body form" id="products_form">
  4. <% if @product.errors.any? %>
  5. <div class="alert alert-danger">
  6. <strong>Tiene <%= pluralize(@product.errors.count, "error") %> no se puede guardar el producto</strong><br>
  7. </div>
  8. <% end %>
  9. <div class="form-body">
  10. <input type="text" class="hidden" value="<%= @product.id %>" id="idproduct">
  11. <h4 class="form-section">Información general</h4>
  12. <div class="row">
  13. <div class="col-md-7">
  14. <%= hidden_field_tag :gain_margin, @pos_config.gain_margin %>
  15. <%= hidden_field_tag :tax_percent, @pos_config.tax_percent %>
  16. <div class="form-group">
  17. <%= f.label :sku, {:class=>"col-md-3 control-label"} do %>SKU <span class="required">*</span>
  18. <% end %>
  19. <div class="col-md-9">
  20. <div class="input-icon">
  21. <i class="fa fa-tag"></i>
  22. <%= f.text_field :sku, {:class=>"form-control input-medium", :readonly => true } %>
  23. </div>
  24. <span class="help-block">El SKU se genera automaticamente en base a la linea y sublinea seleccionadas. </span>
  25. </div>
  26. </div>
  27. <div class="form-group">
  28. <%= f.label :barcode, {:class=>"col-md-3 control-label"} do %> Codigo de barras <span class="required">*</span>
  29. <% end %>
  30. <div class="col-md-9">
  31. <div class="input-icon">
  32. <i class="fa fa-barcode"></i>
  33. <%= f.text_field :barcode, {:class=>"form-control input-medium", :readonly => true } %>
  34. </div>
  35. <span class="help-block">Si el codigo de barras ya existe escaneelo, si no, se generará automaticamente. </span>
  36. </div>
  37. </div>
  38. <div class="form-group">
  39. <%= f.label :name, "Producto", {:class=>"col-md-3 control-label"} do %>Nombre
  40. <span class="required">*</span>
  41. <% end %>
  42. <div class="col-md-9">
  43. <%= f.text_field :name, {:class=>"form-control"} %>
  44. </div>
  45. </div>
  46. <div class="form-group">
  47. <%= f.label :description, "Descripción", {:class=>"col-md-3 control-label"} %>
  48. <div class="col-md-9">
  49. <%= f.text_area :description, {:class=>"form-control"} %>
  50. </div>
  51. </div>
  52. <div class="form-group">
  53. <%= f.label :unit_id, "Unidad de medida", {:class=>"col-md-3 control-label"} do %>Unidad de medida <span class="required">*</span>
  54. <% end %>
  55. <div class="col-md-9">
  56. <%= f.collection_select :unit_id, Unit.vigentes, :id, :unit, {:prompt => "Seleccione"}, {:class => "form-control input-medium"} %>
  57. </div>
  58. </div>
  59. </div>
  60. <div class="col-md-5">
  61. <div class="form-group">
  62. <%= f.label :img_product, "Imagen producto", {:class=>"col-md-5 control-label"} %>
  63. <div class="col-md-7">
  64. <div class="fileinput fileinput-new" data-provides="fileinput">
  65. <div class="fileinput-new thumbnail" style="width: 200px; height: 150px;">
  66. <% if @product.img_product? %>
  67. <%= image_tag @product.img_product %>
  68. <% else %>
  69. <%= image_tag "no-image.png" %>
  70. <% end %>
  71. </div>
  72. <div class="fileinput-preview fileinput-exists thumbnail" style="max-width: 200px; max-height: 150px;"> </div>
  73. <div>
  74. <span class="btn default btn-file">
  75. <span class="fileinput-new"> Seleccione imagen </span>
  76. <span class="fileinput-exists"> Cambiar </span>
  77. <%= f.file_field :img_product, {:class=>"default"} %>
  78. <%= f.hidden_field :img_product_cache %>
  79. </span>
  80. <a href="javascript:;" class="btn red fileinput-exists" data-dismiss="fileinput"> Borrar </a>
  81. </div>
  82. </div>
  83. </div>
  84. </div>
  85. </div>
  86. </div>
  87. <h4 class="form-section">Información del producto</h4>
  88. <div class="row">
  89. <div class="form-group ">
  90. <%= f.label :inventory, "¿Se maneja inventario de este producto?", {:class=>"col-md-3 control-label"} do %>¿Este producto tendrá inventario?
  91. <span class="required">*</span>
  92. <% end %>
  93. <div class="col-md-9">
  94. <%= f.check_box(:inventory,
  95. {
  96. class: "make-switch",
  97. data: {
  98. on_color: "success",
  99. off_color: "danger",
  100. on_text: "Si",
  101. off_text: "No"
  102. }
  103. }, "true","false"
  104. ) %>
  105. </div>
  106. </div>
  107. <%
  108. @category = nil
  109. @subcategory = nil
  110. @prompt = 'Seleccione'
  111. @disabled = true
  112. if @product.category.present? && @product.category.parent_id == 0
  113. @category = @product.category
  114. @disabled = false
  115. elsif @product.category.present? && @product.category.parent_id != 0
  116. @category = Category.find(@product.category.parent_id)
  117. @subcategory = @product.category
  118. @disabled = false
  119. end
  120. %>
  121. <div class="form-group">
  122. <%= f.label :category_id, "Líneas de producto", { class: "col-md-3 control-label" } do %>Líneas de producto
  123. <span class="required">*</span>
  124. <% end %>
  125. <div class="col-md-4">
  126. <%= f.collection_select(:category_id, Category.activos_padre, :id, :category, options = { include_blank: "Seleccione", selected: (@category.present? ? @category.id : @prompt ) }, class: "form-control select2", onchange: "getSubCategories($(this).val())") %>
  127. </div>
  128. </div>
  129. <div class="form-group">
  130. <%= f.label :sub_category_id, "Sublíneas de producto", { class: "col-md-3 control-label" } do %>Sublínea de producto
  131. <span class="required">*</span>
  132. <% end %>
  133. <div class="col-md-4">
  134. <%= collection_select("", :sub_category_id, (@category.present? ? Category.activos.where(parent_id: @category.id) : {}), :id, :category, options = { prompt: "Seleccione", selected: (@subcategory.id if @subcategory.present?) }, html_options = { class: 'form-control select2 select2-allow-clear-todas' }) %>
  135. <%= hidden_field_tag :subcategory_hidden, (@subcategory.id if @subcategory.present?) %>
  136. </div>
  137. </div>
  138. <div class="form-group">
  139. <%= f.label :include_purchase_tax, "¿Incluir IVA en compra?", {:class=>"col-md-3 control-label"} do %> ¿Incluir IVA en compra?
  140. <span class="required">*</span>
  141. <% end %>
  142. <div class="col-md-9">
  143. <%= f.check_box(:include_purchase_tax,
  144. {
  145. class: "make-switch",
  146. data: {
  147. on_color: "success",
  148. off_color: "danger",
  149. on_text: "Si",
  150. off_text: "No"
  151. }
  152. }, "1","0"
  153. ) %>
  154. </div>
  155. </div>
  156. <div class="form-group">
  157. <%= f.label :include_sale_tax, "¿Incluir IVA en venta?", {:class=>"col-md-3 control-label"} do %> ¿Incluir IVA en venta?
  158. <span class="required">*</span>
  159. <% end %>
  160. <div class="col-md-9">
  161. <%= f.check_box(:include_sale_tax,
  162. {
  163. class: "make-switch",
  164. data: {
  165. on_color: "success",
  166. off_color: "danger",
  167. on_text: "Si",
  168. off_text: "No"
  169. }
  170. }, "1","0"
  171. ) %>
  172. </div>
  173. </div>
  174. <div class="form-group">
  175. <%= f.label :is_in_dollars, "¿Se compra en dolares?", {:class=>"col-md-3 control-label"} do %> ¿Se compra en dolares?
  176. <span class="required">*</span>
  177. <% end %>
  178. <div class="col-md-9">
  179. <%= f.check_box(:is_in_dollars,
  180. {
  181. class: "make-switch",
  182. data: {
  183. on_color: "success",
  184. off_color: "danger",
  185. on_text: "Si",
  186. off_text: "No"
  187. }
  188. }, "true","false"
  189. ) %>
  190. </div>
  191. </div>
  192. <div class="form-group inventory">
  193. <%= f.label :price_base_dollars, "Precio de compra neto", {:class=>"col-md-3 control-label"} %>
  194. <div class="col-md-3">
  195. <div class="input-group <%=(@product.persisted? && @product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_usd_div">
  196. <span class="input-group-addon"> $ </span>
  197. <%= f.text_field :price_base_dollars, {:class=>"form-control mask_decimal"} %>
  198. <span class="input-group-addon"> USD </span>
  199. </div>
  200. <div class="input-group <%=(@product.persisted? && !@product.is_in_dollars?) ? '' : 'hidden' %>" id="price_base_mxn_div">
  201. <span class="input-group-addon"> $ </span>
  202. <%= f.text_field :price_base, {:class=>"form-control mask_decimal"} %>
  203. <span class="input-group-addon"> MXN </span>
  204. </div>
  205. </div>
  206. </div>
  207. <div class="form-group inventory">
  208. <%= f.label :price_sale, "Precio de venta base", {:class=>"col-md-3 control-label"} do %> Precio de venta base
  209. <span class="required">*</span>
  210. <% end %>
  211. <div class="col-md-3">
  212. <div class="input-group">
  213. <span class="input-group-addon"> $ </span>
  214. <%= f.text_field :price_sale, {:class=>"form-control mask_decimal"} %>
  215. <span class="input-group-addon"> MXN </span>
  216. </div>
  217. </div>
  218. <div class="col-md-6">
  219. <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>
  220. </div>
  221. </div>
  222. <div class="form-group">
  223. <%= f.label :presentation, "¿El producto tiene variantes?", {:class=>"col-md-3 control-label"} do %>¿El producto tiene variantes?
  224. <span class="required">*</span>
  225. <% end %>
  226. <div class="col-md-9">
  227. <%= f.check_box(:presentation,
  228. {
  229. class: "make-switch",
  230. data: {
  231. on_color: "success",
  232. off_color: "danger",
  233. on_text: "Si",
  234. off_text: "No"
  235. },
  236. readonly: @with_presentation
  237. }, "true","false"
  238. ) %>
  239. </div>
  240. </div>
  241. </div>
  242. <h4 class="form-section presentaciones">Variantes</h4>
  243. <div class="row presentaciones">
  244. <% if @product.persisted? %>
  245. <div class="col-md-9 col-md-offset-3">
  246. <%= render 'products_children' %>
  247. </div>
  248. <% else %>
  249. <div class="col-md-9 col-md-offset-3">
  250. <div class="alert alert-warning hidden">
  251. Son <strong id="variantes"></strong> variantes del producto.
  252. </div>
  253. </div>
  254. <div class="form-group ">
  255. <%= f.label :size_list, "Tallas", {:class=>"col-md-3 control-label"} %>
  256. <div class="col-md-9">
  257. <%= 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} ) %>
  258. <span class="help-block">Ingrese las diferentes tallas, separandolas con coma (,). </span>
  259. </div>
  260. </div>
  261. <div class="form-group ">
  262. <%= f.label :color_list, "Colores", {:class=>"col-md-3 control-label"} %>
  263. <div class="col-md-9">
  264. <%= 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} ) %>
  265. <span class="help-block">Ingrese los diferentes colores, separandolos con coma (,). </span>
  266. </div>
  267. </div>
  268. <div class="form-group ">
  269. <%= f.label :style_list, "Estilos", {:class=>"col-md-3 control-label"} %>
  270. <div class="col-md-9">
  271. <%= 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} ) %>
  272. <span class="help-block">Ingrese los diferentes estilos, separandolos con coma (,). </span>
  273. </div>
  274. </div>
  275. <%= hidden_field_tag :variants %>
  276. <% end %>
  277. </div>
  278. </div>
  279. <div class="form-actions">
  280. <div class="row">
  281. <div class="col-md-offset-3 col-md-9">
  282. <%= f.submit 'Guardar', {:class=>"btn green"} %>
  283. <%= link_to 'Cancelar', products_path(:filter => @filter, :current_page => @current_page), {:class=>"btn default"} %>
  284. </div>
  285. </div>
  286. </div>
  287. </div>
  288. <% end %>
  289. <script type="text/javascript">
  290. var timeout = null;
  291. var regex = /[a,e,i,o,u]/gi;
  292. var skuEdit = "";
  293. $(document).on('page:change', function() {
  294. App.init();
  295. $('.fileinput').fileinput();
  296. presentaciones();
  297. // < % if @product.children.present? %>
  298. getVariants();
  299. // < % end %>
  300. showPriceBaseInput(<%= @product.is_in_dollars? %>);
  301. $(document).on("cut copy paste",'form#new_product .presentaciones input[type=text]', function(e) {
  302. // evitar que se pueda copiar y pegar en los inputs de los tags de variantes
  303. e.preventDefault();
  304. });
  305. });
  306. <% if @product.barcode.blank? %>
  307. $(document).scannerDetection({
  308. timeBeforeScanTest: 200, // wait for the next character for upto 200ms
  309. startChar: [120], // Prefix character for the cabled scanner (OPL6845R)
  310. endChar: [13], // be sure the scan is complete if key 13 (enter) is detected
  311. avgTimeByChar: 40, // it's not a barcode if a character takes longer than 40ms
  312. onComplete: function(barcode, qty){
  313. $('#product_barcode').val(barcode);
  314. validateBarcode(barcode);
  315. }
  316. });
  317. <% end %>
  318. $('#product_size_list').on('change', function(event) { getVariants(); });
  319. $('#product_color_list').on('change', function(event) { getVariants(); });
  320. $('#product_style_list').on('change', function(event) { getVariants(); });
  321. function presentaciones(){
  322. if ($('#product_presentation').is(':checked')){
  323. $('.presentaciones').removeClass('hidden');
  324. } else {
  325. $('.presentaciones').addClass('hidden');
  326. }
  327. }
  328. $('#product_presentation').on('switchChange.bootstrapSwitch', function(event, state) {
  329. presentaciones();
  330. });
  331. function getVariants(){
  332. sizes = $('#product_size_list').tagsinput('items');
  333. colors = $('#product_color_list').tagsinput('items');
  334. styles = $('#product_style_list').tagsinput('items');
  335. variantes = (sizes.length > 0 ? sizes.length : 1)
  336. * (colors.length > 0 ? colors.length : 1 )
  337. * (styles.length > 0 ? styles.length : 1);
  338. $('#variants').html(variantes);
  339. $('.alert-warning').removeClass('hidden');
  340. $('#variantes').html(variantes);
  341. }
  342. function getSubCategories(value) {
  343. $('#_sub_category_id').html('');
  344. $('#_sub_category_id').select2("destroy").select2({ placeholder: 'Seleccione'});
  345. $('#_sub_category_id').select2('val', null);
  346. if($('#product_category_id').val()) {
  347. $.ajax({
  348. type: "get",
  349. url: '/getcategories/' + value,
  350. dataType: 'json',
  351. success: function(data) {
  352. if(data.length > 0) {
  353. $('#_sub_category_id').append('<option></option>')
  354. for(i in data) {
  355. $('#_sub_category_id').append("<option value='" + data[i].id + "'>" + data[i].category + "</option>")
  356. }
  357. $('#_sub_category_id').select2({ placeholder: 'Seleccione'});
  358. $('#_sub_category_id').attr('disabled', false);
  359. } else {
  360. $('#_sub_category_id').attr('disabled', true);
  361. $('#subcategory_hidden').val($('#product_category_id').val());
  362. }
  363. fillSKU();
  364. }
  365. });
  366. }
  367. }
  368. $('#_sub_category_id').on('change', function() {
  369. $('#subcategory_hidden').val($(this).val());
  370. fillSKU();
  371. });
  372. $('#product_price_base').on('input', function() {
  373. getSalePrice($(this));
  374. });
  375. $('#product_is_in_dollars').on('switchChange.bootstrapSwitch', function(event, state) {
  376. showPriceBaseInput(state);
  377. });
  378. function getSalePrice(input){
  379. clearTimeout(timeout);
  380. timeout = setTimeout(function () {
  381. var gainMargin = parseFloat($('#gain_margin').val());
  382. var purchasePrice = parseFloat(input.val());
  383. var gain = (gainMargin / 100) * purchasePrice
  384. $('#product_price_sale').val( (purchasePrice + gain).toFixed(2) );
  385. }, 500);
  386. }
  387. function getProductId() {
  388. $('#product_category_id').attr('disabled', true);
  389. var category_id = $('#_sub_category_id').val() ? $('#_sub_category_id').val() : $('#product_category_id').val();
  390. $.ajax({
  391. type: "get",
  392. url: '/get_max_product_id',
  393. dataType: 'json',
  394. data: {
  395. category_id: category_id
  396. },
  397. success: function(data) {
  398. $('#product_category_id').attr('disabled', false);
  399. var sku = $('#product_sku').val();
  400. $('#product_sku').val(sku + "-" + data);
  401. }
  402. });
  403. }
  404. function fillSKU() {
  405. var category = $('#product_category_id option:selected').text().substring(0,3).toUpperCase();
  406. $('#product_sku').val(category);
  407. if($('#_sub_category_id').val()) {
  408. var subcategory = $('#_sub_category_id option:selected').text();
  409. if(subcategory) {
  410. $('#product_sku').val(category + '-' + subcategory.replace(regex, '').substring(0,3).toUpperCase());
  411. }
  412. }
  413. getProductId();
  414. }
  415. function validateBarcode(barcode) {
  416. $.ajax({
  417. type: "get",
  418. url: '/validate_unique_barcode/' + barcode,
  419. dataType: 'script',
  420. success: function(data) {
  421. }
  422. });
  423. }
  424. function showPriceBaseInput(state) {
  425. //true es en dolares, false en pesos
  426. if (state) {
  427. $('#price_base_usd_div').removeClass('hidden');
  428. $('#price_base_mxn_div').addClass('hidden');
  429. $('#product_price_base').val('');
  430. <% unless @product.persisted? %>
  431. $('#product_price_sale').val('');
  432. <% end %>
  433. } else {
  434. $('#price_base_mxn_div').removeClass('hidden');
  435. $('#price_base_usd_div').addClass('hidden');
  436. $('#product_price_base_dollars').val('');
  437. }
  438. }
  439. </script>