Parcourir la source

Min Max report

Jacqueline Maldonado il y a 7 ans
Parent
commit
85ec4afb75

+ 17 - 0
app/assets/javascripts/config.js

@@ -1568,6 +1568,23 @@ $(document).on("page:change", function() {
 			});
 		}
 
+		if($('.select2-allow-clear-todas').get(0)){
+			$('select.select2-allow-clear-todas').select2({
+				allowClear: true,
+				placeholder: "Todas",
+				width: null,
+				placeholder: "Seleccione",
+				"language": {
+				   "noResults": function(){
+					   return "No se encontraron coincidencias";
+				   }
+				},
+				escapeMarkup: function (markup) {
+					return markup;
+				}
+			});
+		}
+
 		if($('#form').get(0) ){
 				FormValidation.init();
 		}

+ 3 - 0
app/assets/javascripts/reports.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/reports.scss

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

+ 30 - 0
app/controllers/reports_controller.rb

@@ -0,0 +1,30 @@
+class ReportsController < ApplicationController
+  ##--- Breadcrum_rails
+  add_breadcrumb I18n.t("breadcrumbs." + controller_name), :reports_path
+  add_breadcrumb "Reporte de mínimos y máximos", :min_max_path, only: :min_max
+
+  def min_max
+    @pointsales = Pointsale.activos
+    respond_to do |format|
+      if params[:pointsale_id].present?
+        @products = AvailableProduct.joins(:product, :categories).activos.where(pointsale_id: params[:pointsale_id]).where("available_products.stock <= available_products.stock_min and available_products.stock_max > ?", 0)
+        @products = get_products_category(@products, params[:category], params[:subcategory]) if params[:category].present?
+        format.js
+      else
+        @products = AvailableProduct.where(pointsale_id: @pointsales.first.id).where("available_products.stock <= available_products.stock_min and available_products.stock_min > ?", 0)
+        format.html
+      end
+    end
+  end
+
+  def get_products_category(products, category, subcategory)
+    ids =
+      if subcategory.present?
+        subcategory
+      else
+        category = Category.find(category)
+        [category.id, category.children.ids].flatten
+      end
+    products.where(categories: { id: ids })
+  end
+end

+ 6 - 0
app/helpers/reports_helper.rb

@@ -0,0 +1,6 @@
+module ReportsHelper
+  def calculate_restock(available)
+    stock = (available.stock_max - available.stock)
+    stock
+  end
+end

+ 8 - 1
app/models/product.rb

@@ -50,7 +50,7 @@ class Product < ActiveRecord::Base
   end
 
   ##--- Tipo de vistas / consultas
-  scope :vigentes, -> { where.not(status: 0).order(" products.status ASC, products.name ASC") }
+  scope :vigentes, -> { where.not(products: { status: 0 }).order(" products.status ASC, products.name ASC") }
   scope :activos, -> { where(status: 1).order("products.name ASC") }
   scope :activos_children, -> { activos.where(is_parent: false).order("products.name ASC") }
   scope :vigentes_parents, -> { vigentes.where("parent_id IS NULL") }
@@ -67,6 +67,13 @@ class Product < ActiveRecord::Base
     sku.to_s + " | " + name.to_s + " | " + display_attributes.to_s
   end
 
+  def full_display
+    show_name = name + "\n" + "SKU: " + sku + "\n"
+    show_name += "\n" + display_attributes.to_s if parent_id.present?
+    show_name += " Código de barras: " + barcode if parent_id.present? && barcode.present?
+    show_name
+  end
+
   def stock_in_pointsale(pointsale_id)
     stock = 0
     # checar si hay existencias en los almacenes.

+ 10 - 0
app/views/reports/_min_max.html.erb

@@ -0,0 +1,10 @@
+<% category = available.product.categories[0] %>
+<tr>
+  <td><%= key += 1 %></td>
+  <td><%= available.product.full_display %></td>
+  <td><%= number_to_currency(available.product.price_base, precision: 2) %></td>
+  <td><%= category.parent_id.zero? ? category.category : category.parent.category %></td>
+  <td><%= category.category %></td>
+  <td class="success"><%= quantity = calculate_restock(available) %></td>
+  <td><%= number_to_currency((quantity * available.product.price_base), precision: 2) %></td>
+</tr>

+ 153 - 0
app/views/reports/min_max.html.erb

@@ -0,0 +1,153 @@
+<div class="page-container">
+  <div class="page-content-wrapper">
+    <div class="page-head">
+      <div class="container-fluid">
+        <div class="page-title">
+          <h1>Reporte de mínimos y máximos por punto de venta</h1>
+        </div>
+      </div>
+    </div>
+    <div class="page-content">
+      <div class="container-fluid">
+        <ul class="page-breadcrumb breadcrumb">
+
+        </ul>
+        <div class="page-content-inner">
+          <div id="notice"><%= notice %></div>
+          <div class="portlet light hidden-print">
+            <div class="portlet-body">
+              <div class="row">
+                <div class="col-md-12">
+                  <div class="booking-search">
+                    <%= form_tag(min_max_path, method: "get", remote: true, id: "minmax_form") do %>
+                      <div class="row form-group">
+                        <!-- punto de venta -->
+                        <%= label :pointsale_id, "", { class: "col-md-1 col-sm-2 control-label" } do %>Punto de venta<% end %>
+                        <div class="col-md-4 col-sm-5 select2-bootstrap-prepend">
+                          <%= select_tag :pointsale_id, options_from_collection_for_select(@pointsales, :id, :name, selected: @pointsales.first), class: "form-control select2" %>
+                        </div>
+                      </div>
+                      <div class="row form-group">
+                        <!-- categoria -->
+                        <%= label :category, "", { class: "col-md-1 col-sm-1 control-label" } do %>Línea <% end %>
+                        <div class="col-md-4 col-sm-4 select2-bootstrap-prepend">
+                          <%= select_tag :category, options_for_select(Category.activos_padre.collect{ |p| [ "#{p.category} - #{p.description}", p.id ] }), include_blank: "Todas", class: "form-control select2-allow-clear-todas input-medium", onchange: 'getSubCategories($(this).val())' %>
+                        </div>
+                        <!-- sub- categoria -->
+                        <%= label :subcategory, "", { class: "col-md-1 col-sm-1 control-label" } do %>Sublínea <% end %>
+                        <div class="col-md-4 col-sm-4 select2-bootstrap-prepend">
+                          <div class="select2-bootstrap-prepend">
+                            <%= collection_select("", :subcategory, ({}), :id, :category_and_description, options = { include_blank: "Todas" }, html_options = { class: 'form-control select2-allow-clear-todas input-medium' }) %>
+                          </div>
+                        </div>
+                        <div class="col-md-2 col-sm-1">
+                          <button type="button" onclick="applyFilter()" class="btn blue">Buscar</button>
+                        </div>
+                      </div>
+                    <% end %>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+          <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 productos</span>
+              </div>
+            </div>
+            <div class="portlet-body ">
+              <div id="error_explanation"></div>
+              <%= hidden_field_tag 'title_for_print', "Reporte de mínimos y máximos por punto de venta" %>
+              <!-- lista de productos -->
+              <table class="table table-hover table-bordered table-striped tableadvancedprintable" id="minmax_table">
+                <thead>
+                  <th>#</th>
+                  <th width="25%">Producto</th>
+                  <th>Precio<br>unitario</th>
+                  <th width="25%">Línea</th>
+                  <th>Sublínea</th>
+                  <th class="success">Cantidad recomendada<br> a comprar</th>
+                  <th>Total</th>
+                </thead>
+                <tbody>
+                  <% total = 0 %>
+                  <% @products.each_with_index do |available, key| %>
+                    <% category = available.product.categories[0] %>
+                    <tr>
+                      <td><%= key += 1 %></td>
+                      <td><%= available.product.full_display %></td>
+                      <td><%= number_to_currency(available.product.price_base, precision: 2) %></td>
+                      <td><%= category.parent_id.zero? ? category.category : category.parent.category %></td>
+                      <td><%= category.category %></td>
+                      <td class="success"><%= quantity = calculate_restock(available) %></td>
+                      <td><%= number_to_currency((quantity * available.product.price_base), precision: 2) %></td>
+                    </tr>
+                    <% total += (quantity * available.product.price_base) %>
+                  <% end %>
+                  <%= hidden_field_tag :total, total %>
+                </tbody>
+              </table>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col-md-offset-4 col-md-3">
+              <div class="text-center well" id="container_total_prods" style="margin-bottom: 0px">
+                <div class="font-grey-mint font-sm">Total </div>
+                <div class="uppercase font-hg font-blue-sharp" id="total_prods"></div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</div>
+
+<script type="text/javascript">
+  $(document).ready(function(){
+    $('#total_prods').html(accounting.formatMoney($("#total").val()) + ' MXN');
+  });
+
+  function applyFilter() {
+    $("#minmax_form").submit();
+  }
+
+  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>

+ 14 - 0
app/views/reports/min_max.js.erb

@@ -0,0 +1,14 @@
+var table = $('#minmax_table').DataTable();
+$('#minmax_table').dataTable().fnClearTable();
+total = 0
+<% @products.each_with_index do |available, key| %>
+  total += <%= available.product.price_base * (available.stock_max - available.stock) %>
+  var row = $('<%= j render partial: "min_max", locals: { available: available, key: key } %>');
+  table.row.add(row).draw();
+<% end %>
+total = parseFloat(total).toFixed(2);
+$("#total").val(total);
+total = "$ " + total + " MXN";
+$("#total_prods").html(total);
+// $('#total_prods').html(accounting.formatMoney($("#total").val()) + ' MXN');
+// $('#total_prods').html(accounting.formatMoney(total + "MXN");

+ 1 - 0
config/navigation.rb

@@ -111,6 +111,7 @@ SimpleNavigation::Configuration.run do |navigation|
       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|

+ 3 - 0
config/routes.rb

@@ -245,5 +245,8 @@ Rails.application.routes.draw do
 
   get "products_by_category_pointsale/:pointsale_id/:category_id" => "application#products_by_category_pointsale", :as => "products_by_category_pointsale", :format => :json
   get "verify_transfer/:transfer_id" => "transfers#verify_transfer", :as => "verify_transfer"
+
+  ## reports
+  get "min_max" => "reports#min_max"
 end
 # rubocop:enable Metrics/BlockLength