deisy há 6 anos atrás
commit
7290371c56

+ 2 - 0
__init__.py

@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+import controllers

BIN
__init__.pyc


+ 22 - 0
__openerp__.py

@@ -0,0 +1,22 @@
+# -*- coding: utf-8 -*-
+{
+    'name': "Eiru Reports BACO",
+    'author': "Eiru",
+    'category': 'Reports',
+    'version': '0.1',
+    'depends': [
+        'base',
+        'account',
+        'eiru_assets',
+        'eiru_reports',
+    ],
+    'qweb': [
+        'static/src/xml/*.xml',
+        'static/src/reports/*.xml'
+    ],
+    'data': [
+        'templates.xml',
+        'views/actions.xml',
+        'views/menus.xml',
+    ],
+}

+ 2 - 0
controllers.py

@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+from openerp import http

BIN
controllers.pyc


BIN
static/description/icon.png


+ 10 - 0
static/src/css/custom.css

@@ -0,0 +1,10 @@
+.openerp_webclient_container {
+    height: 100% !important;
+    height: calc(100% - 45px) !important;
+    overflow: auto !important;
+  }
+
+.hover:hover{
+  cursor: -webkit-grab;
+  cursor: grab;
+}

+ 17 - 0
static/src/js/main.js

@@ -0,0 +1,17 @@
+openerp.eiru_reports_baco = function(instance) {
+  "use strict";
+
+  var reporting = instance.eiru_reports_baco;
+
+  reporting_base(instance, reporting);
+
+  try {
+    report_product_ranking_baco(reporting);
+  } catch (e) {
+    // ignorar error
+  }
+
+  // lista de funcionarios
+  instance.web.client_actions.add('eiru_reports_baco.product_ranking_baco_action', 'instance.eiru_reports_baco.ReportProductRankingBacoWidget');
+
+}

+ 22 - 0
static/src/js/reporting_base.js

@@ -0,0 +1,22 @@
+function reporting_base (instance, widget) {
+    "use strict";
+
+    widget.Base = instance.Widget.extend({
+
+        position: 0,
+
+        init: function (parent, position) {
+            this._super(parent);
+            this.position = position || this.position;
+        },
+        start: function () {
+            
+        },
+        getPosition: function () {
+            return this.position;
+        },
+        setPosition: function (position) {
+            this.position = position;
+        }
+    });
+}

+ 1702 - 0
static/src/js/reports/report_product_ranking_baco.js

@@ -0,0 +1,1702 @@
+function report_product_ranking_baco(reporting) {
+  "use strict";
+
+  var model = openerp;
+
+  reporting.ReportProductRankingBacoWidget = reporting.Base.extend({
+    template: 'ReportProductRankingBaco',
+    rowsData: [],
+    content: [],
+    modules: ['product_brand', 'point_of_sale'],
+
+    events: {
+      'click #toolbar > button': 'clickOnAction',
+      'click #generate': 'fetchGenerate',
+      'click-row.bs.table #table': 'clickAnalysisDetail',
+      'change #current-company': 'updateSelections',
+      'change #current-attribute': 'updateAttributeSelections',
+      'change #current-store': 'updateJournalSelections',
+      'change #current-period': 'updatePeriodSelections',
+      'change #current-date': 'ShowDateRange',
+    },
+
+    init: function(parent) {
+      this._super(parent);
+    },
+
+    start: function() {
+      var date = new model.eiru_reports.ReportDatePickerWidget(self);
+      date.fecthFecha();
+      this.fetchInitial();
+    },
+
+    valorNull: function(dato) {
+      var valor = "";
+      if (dato) {
+        valor = dato;
+      }
+      return valor;
+    },
+
+    checkModel: function(model) {
+      var self = this;
+      return _.filter(self.IrModuleModule, function(item) {
+        return item.name === model
+      });
+    },
+
+    ShowDateRange: function() {
+      var self = this;
+      var date = self.$el.find('#current-date').val();
+      if (date == 'range') {
+        self.$el.find('.datepicker').css('display', 'block');
+      }
+      if (date != 'range') {
+        self.$el.find('.datepicker').css('display', 'none');
+      }
+
+    },
+
+    clickAnalysisDetail: function(e, row, $element, field) {
+      if (field == 'product') {
+        this.do_action({
+          name: "Producto",
+          type: 'ir.actions.act_window',
+          res_model: "product.product",
+          views: [
+            [false, 'form']
+          ],
+          target: 'new',
+          domain: [
+            ['id', '=', row.id]
+          ],
+          context: {},
+          flags: {
+            'form': {
+              'action_buttons': false,
+              'options': {
+                'mode': 'view'
+              }
+            }
+          },
+          res_id: row.id,
+        });
+      }
+      e.stopImmediatePropagation();
+    },
+
+    fetchInitial: function() {
+      var self = this;
+      self.fecthIrModuleModule().then(function(IrModuleModule) {
+        return IrModuleModule;
+      }).then(function(IrModuleModule) {
+        self.IrModuleModule = IrModuleModule;
+        return self.fetchResUser();
+      }).then(function(ResUser) {
+        self.ResUser = ResUser;
+        self.fecthCheckType();
+        return self.fetchResCompany();
+      }).then(function(ResCompany) {
+        self.ResCompany = ResCompany;
+        if (ResCompany.length > 1) {
+          self.$el.find('#current-company').append('<option value="9999999">Todas las empresas</option>');
+          _.each(ResCompany, function(item) {
+            self.$el.find('#current-company').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.company').css('display', 'none');
+        }
+        return self.fetchResStore();
+      }).then(function(ResStore) {
+        self.ResStore = ResStore;
+        if (ResStore.length > 1) {
+          self.$el.find('#current-store').append('<option value="9999999">Todas las sucursales</option>');
+          _.each(ResStore, function(item) {
+            self.$el.find('#current-store').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.store').css('display', 'none');
+        }
+        return self.fetchAccountJournal();
+      }).then(function(AccountJournal) {
+        self.AccountJournal = AccountJournal;
+        if (AccountJournal.length > 1) {
+          self.$el.find('#current-journal').append('<option value="9999999">Todas las facturas</option>');
+          _.each(AccountJournal, function(item) {
+            self.$el.find('#current-journal').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.journal').css('display', 'none');
+        }
+        return self.fetchAccountPeriod();
+      }).then(function(AccountPeriod) {
+        self.AccountPeriod = AccountPeriod;
+        self.$el.find('#current-period').append('<option value="9999999">Todos los periodos</option>');
+        _.each(AccountPeriod, function(item) {
+          self.$el.find('#current-period').append('<option value="' + item.id + '">' + item.name + '</option>');
+        });
+        return self.fetchProductCategory();
+      }).then(function(ProductCategory) {
+        self.ProductCategory = ProductCategory;
+        self.$el.find('#current-category').append('<option value="9999999">Todas las categorias</option>');
+        _.each(ProductCategory, function(item) {
+          self.$el.find('#current-category').append('<option value="' + item.id + '">' + item.complete_name + '</option>');
+        });
+        return self.fetchProductBrand();
+      }).then(function(ProductBrand) {
+        self.ProductBrand = ProductBrand;
+        if (ProductBrand.length > 0) {
+          self.$el.find('#current-brand').append('<option value="9999999">Todas las marcas</option>');
+          _.each(ProductBrand, function(item) {
+            self.$el.find('#current-brand').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.brand').css('display', 'none');
+        }
+        return self.fetchProductAttribute();
+      }).then(function(ProductAttribute) {
+        self.ProductAttribute = ProductAttribute;
+        if (ProductAttribute.length > 0) {
+          self.$el.find('#current-attribute').append('<option value="9999999">Todos los atributos</option>');
+          _.each(ProductAttribute, function(item) {
+            self.$el.find('#current-attribute').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.attribute').css('display', 'none');
+        }
+        return self.fetchProductAttributeValue();
+      }).then(function(ProductAttributeValue) {
+        self.ProductAttributeValue = ProductAttributeValue;
+        if (ProductAttributeValue.length > 0) {
+          self.$el.find('#current-attribute-value').append('<option value="9999999">Todos los valores de atributos</option>');
+          _.each(ProductAttributeValue, function(item) {
+            self.$el.find('#current-attribute-value').append('<option value="' + item.id + '">' + item.name + '</option>');
+          });
+        } else {
+          self.$el.find('.attribute-value').css('display', 'none');
+        }
+        return self.fetchResCurrency();
+      }).then(function(ResCurrency) {
+        self.ResCurrency = ResCurrency;
+      });
+      self.$el.find('#generate').css('display', 'inline');
+      return;
+    },
+
+    fetchGenerate: function() {
+      var self = this;
+      self.$el.find('.search-form').block({
+        message: null,
+        overlayCSS: {
+          backgroundColor: '#FAFAFA'
+        }
+      });
+      self.$el.find('.report-form').block({
+        message: null,
+        overlayCSS: {
+          backgroundColor: '#FAFAFA'
+        }
+      });
+
+      this.fetchAccountInvoice().then(function(AccountInvoice) {
+        return AccountInvoice;
+      }).then(function(AccountInvoice) {
+        self.AccountInvoice = AccountInvoice;
+        return self.fetchAccountInvoiceLine();
+      }).then(function(AccountInvoiceLine) {
+        self.AccountInvoiceLine = AccountInvoiceLine;
+        return self.fetchPosOrder();
+      }).then(function(PosOrder) {
+        self.PosOrder = PosOrder;
+        return self.fetchPosOrderLine();
+      }).then(function(PosOrderLine) {
+        self.PosOrderLine = PosOrderLine;
+        return self.fetchProductProduct();
+      }).then(function(ProductProduct) {
+        self.ProductProduct = ProductProduct;
+        return self.BuildTable();
+      });
+    },
+
+    /*=====================================================================
+        IR MODULE
+    =====================================================================*/
+    fecthIrModuleModule: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['name', 'id'];
+      var domain = [
+        ['state', '=', 'installed'],
+        ['name', 'in', self.modules]
+      ];
+      var IrModuleModule = new model.web.Model('ir.module.module');
+      IrModuleModule.query(fields).filter(domain).all().then(function(results) {
+        defer.resolve(results);
+      })
+      return defer;
+    },
+
+    /*=====================================================================
+        Check type
+    =====================================================================*/
+    fecthCheckType: function() {
+      var self = this;
+      var modules = self.checkModel('point_of_sale');
+      if (modules.length == 0) {
+        self.$el.find('.type').css('display', 'none');
+      }
+    },
+
+    /*=====================================================================
+        USER
+    =====================================================================*/
+    fetchResUser: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['id', 'name', 'store_id'];
+      var domain = [
+        ['id', '=', self.session.uid]
+      ];
+      var ResUser = new model.web.Model('res.users');
+      ResUser.query(fields).filter(domain).all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        RES COMPANY
+    ====================================================================*/
+    fetchResCompany: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var currency = new model.web.Model('res.company');
+      var field = ['id', 'name', 'currency_id', 'logo'];
+      currency.query(field).filter().all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        RES STORE
+    ====================================================================*/
+    fetchResStore: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var field = ['id', 'name', 'company_id'];
+      var ResStore = new model.web.Model('res.store');
+      ResStore.query(field).all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        ACCOUNT JOURNAL
+    ====================================================================*/
+    fetchAccountJournal: function() {
+      var self = this;
+      var domain = [
+        ['active', '=', true],
+        ['type', '=', 'sale']
+      ];
+      var AccountJournal = new model.web.Model('account.journal');
+      return AccountJournal.call('getAccountJournal', [domain], {
+        context: new model.web.CompoundContext()
+      });
+    },
+
+    /*=====================================================================
+        ACCOUNT PERIOD
+    =====================================================================*/
+    fetchAccountPeriod: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var domain = [
+        ['special', '=', false]
+      ];
+      var field = ['id', 'name', 'date_start', 'date_stop', 'company_id'];
+      var AccountPeriod = new model.web.Model('account.period');
+      AccountPeriod.query(field).filter(domain).all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        PRODUCT CATEGORY
+    ====================================================================*/
+    fetchProductCategory: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['id', 'name', 'parent_id', 'complete_name','child_id'];
+      var ProductCategory = new model.web.Model('product.category');
+      ProductCategory.query(fields).filter().all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        PRODUCT BRAND
+    ====================================================================*/
+    fetchProductBrand: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var modules = self.checkModel('product_brand');
+      if (modules.length > 0) {
+        var fields = ['id', 'name'];
+        var ProductBrand = new model.web.Model('product.brand');
+        ProductBrand.query(fields).filter().all().then(function(results) {
+          defer.resolve(results);
+        });
+        return defer;
+      } else {
+        var ProductBrand = [];
+        return ProductBrand;
+      }
+    },
+
+    /*====================================================================
+        PRODUCT ATTRIBUTE
+    ====================================================================*/
+    fetchProductAttribute: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['id', 'name'];
+      var ProductAttribute = new model.web.Model('product.attribute');
+      ProductAttribute.query(fields).filter().all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        PRODUCT ATTRIBUTE VALUE
+    ====================================================================*/
+    fetchProductAttributeValue: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['id', 'name', 'attribute_id'];
+      var ProductAttributeValue = new model.web.Model('product.attribute.value');
+      ProductAttributeValue.query(fields).filter().all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        RES CURRENCY
+    ====================================================================*/
+    fetchResCurrency: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var fields = ['id', 'name', 'symbol', 'rate_silent', 'base', 'decimal_separator', 'decimal_places', 'thousands_separator', 'symbol_position'];
+      var domain = [
+        ['active', '=', true]
+      ];
+      var ResCurrency = new model.web.Model('res.currency');
+      ResCurrency.query(fields).filter(domain).all().then(function(results) {
+        defer.resolve(results);
+      });
+      return defer;
+    },
+
+    /*====================================================================
+        ACCOUNT INVOICE
+    ====================================================================*/
+    fetchAccountInvoice: function() {
+      var self = this;
+      var store = self.$el.find('#current-store').val();
+      var period = self.$el.find('#current-period').val();
+      var company = self.$el.find('#current-company').val();
+      var journal = self.$el.find('#current-journal').val();
+      var date = self.$el.find('#current-date').val();
+      var desde = self.$el.find('#from').val();
+      var hasta = self.$el.find('#to').val();
+
+      if (store && store != 9999999) {
+        var journal_ids = _.map(_.filter(self.AccountJournal, function(item) {
+          return item.store_ids[0] == store;
+        }), function(map) {
+          return map.id;
+        });
+      } else {
+        var journal_ids = _.flatten(_.map(self.AccountJournal, function(item) {
+          return item.id;
+        }));
+      }
+
+      var domain = [
+        ['state', 'in', ['open', 'paid']],
+        ['type', '=', 'out_invoice'],
+        ['journal_id', 'in', journal_ids],
+      ];
+
+      if (company && company != 9999999) {
+        domain.push(['company_id', '=', parseInt(company)]);
+      }
+
+      if (journal && journal != 9999999) {
+        domain.push(['journal_id', '=', parseInt(journal)]);
+      }
+
+      if (period && period != 9999999) {
+        domain.push(['period_id', '=', parseInt(period)]);
+      }
+
+      if (date && date != 9999999) {
+
+        if (desde) {
+          var date = desde.split('/')
+          date = (date[2] + "-" + date[1] + "-" + date[0]);
+          domain.push(['date_invoice', '>=', date]);
+        }
+
+        if (hasta) {
+          var date = hasta.split('/')
+          date = (date[2] + "-" + date[1] + "-" + date[0]);
+          domain.push(['date_invoice', '<=', date]);
+        }
+
+        if (date == 'today') {
+          var today = moment().format('YYYY-MM-DD');
+          domain.push(['date_invoice', '=', today]);
+        }
+
+        if (date == 'yesterday') {
+          var yesterday = moment().add(-1, 'days').format('YYYY-MM-DD');
+          domain.push(['date_invoice', '=', yesterday]);
+        }
+
+        if (date == 'currentMonth') {
+          var currentMonth = moment().format('YYYY-MM');
+          domain.push(['date_invoice', 'like', currentMonth]);
+        }
+
+        if (date == 'lastMonth') {
+          var lastMonth = moment().add(-1, 'months').format('YYYY-MM');
+          domain.push(['date_invoice', 'like', lastMonth]);
+        }
+      }
+
+      var AccountInvoice = new model.web.Model('account.invoice');
+      return AccountInvoice.call('getAccountInvoice', [domain], {
+        context: new model.web.CompoundContext()
+      });
+    },
+
+    /*====================================================================
+        ACCOUNT INVOICE LINE
+    ====================================================================*/
+    fetchAccountInvoiceLine: function() {
+      var self = this;
+      var invoice_ids = _.flatten(_.map(self.AccountInvoice, function(item) {
+        return item.id;
+      }));
+      var domain = [
+        ['invoice_id', 'in', invoice_ids],
+      ];
+      var AccountInvoiceLine = new model.web.Model('account.invoice.line');
+      return AccountInvoiceLine.call('getAccountInvoiceLine', [domain], {
+        context: new model.web.CompoundContext()
+      });
+    },
+
+    /*====================================================================
+        POS ORDER
+    ====================================================================*/
+    /*
+        TO DO
+        **************************************************
+        Mejorar los filtros de fechas en el modelo POS ORDER - Actualmente
+        es necesario filtrar al momento de hacer la consulta para reducir
+        la carga de la consulta y la segunda vez que se filtra es para
+        analizar los datos con la zona horaria.
+    */
+    fetchPosOrder: function() {
+      var self = this;
+      var defer = $.Deferred();
+      var type = $('#current-type').val();
+      var modules = self.checkModel('point_of_sale');
+      if (type && modules.length > 0) {
+        if (type != 'sale') {
+          var store = self.$el.find('#current-store').val();
+          var company = self.$el.find('#current-company').val();
+          var journal = self.$el.find('#current-journal').val();
+          var date = self.$el.find('#current-date').val();
+          var desde = self.$el.find('#from').val();
+          var hasta = self.$el.find('#to').val();
+          if (store && store != 9999999) {
+            var journal_ids = _.map(_.filter(self.AccountJournal, function(item) {
+              return item.store_ids[0] == store;
+            }), function(map) {
+              return map.id;
+            });
+          } else {
+            var journal_ids = _.flatten(_.map(self.AccountJournal, function(item) {
+              return item.id;
+            }));
+          }
+          var domain = [
+            ['state', 'not in', ['draft', 'cancel']],
+            ['sale_journal', 'in', journal_ids],
+          ];
+          if (company && company != 9999999) {
+            domain.push(['company_id', '=', parseInt(company)]);
+          }
+          if (journal && journal != 9999999) {
+            domain.push(['sale_journal', '=', parseInt(journal)]);
+          }
+
+          if (date && date != 9999999) {
+            if (date == 'range') {
+              if (desde) {
+                var date = desde.split('/')
+                date = (date[2] + "-" + date[1] + "-" + date[0]);
+                domain.push(['date_order', '>=', date]);
+              }
+            }
+            if (date == 'today') {
+              var today = moment().format('YYYY-MM-DD 00:00:00');
+              domain.push(['date_order', '>=', today]);
+            }
+            if (date == 'yesterday') {
+              var yesterday = moment().add(-1, 'days').format('YYYY-MM-DD 00:00:00');
+              domain.push(['date_order', '>=', yesterday]);
+            }
+            if (date == 'currentMonth') {
+              var currentMonth = moment().format('YYYY-MM');
+              domain.push(['date_order', 'like', currentMonth]);
+            }
+            if (date == 'lastMonth') {
+              var lastMonth = moment().add(-1, 'months').format('YYYY-MM');
+              domain.push(['date_order', 'like', lastMonth]);
+            }
+          }
+          var PosOrder = new model.web.Model('account.invoice');
+          return PosOrder.call('getPosOrder', [domain], {
+            context: new model.web.CompoundContext()
+          });
+        }
+      } else {
+        var PosOrder = [];
+        return PosOrder;
+      }
+    },
+
+    /*====================================================================
+        POS ORDER LINE
+    ====================================================================*/
+    fetchPosOrderLine: function() {
+      var self = this;
+      var order_ids = _.flatten(_.map(self.PosOrder, function(item) {
+        return item.id;
+      }));
+      var domain = [
+        ['order_id', 'in', order_ids],
+      ];
+      var modules = self.checkModel('point_of_sale');
+      if (modules.length > 0) {
+        var PosOrderLine = new model.web.Model('account.invoice.line');
+
+        /*==============================================================================
+            La funcion para obtener los datos de PosOrderLine esta ubicado en la clase
+            AccountInvoiceLine.
+        ==============================================================================*/
+
+        var PosOrderLine = new model.web.Model('account.invoice.line');
+        return PosOrderLine.call('getPosOrderLine', [domain], {
+          context: new model.web.CompoundContext()
+        });
+
+      } else {
+        var PosOrderLine = [];
+        return PosOrderLine;
+      }
+    },
+
+    /*====================================================================
+        PRODUCT PRODUCT
+    ====================================================================*/
+    fetchProductProduct: function() {
+      var self = this;
+      var categ_ids = [];
+      var brand = self.$el.find('#current-brand').val();
+      var category = self.$el.find('#current-category').val();
+
+      /*
+      =================================================
+          BUSCAR EN ACCOUNT INVOICE LINE
+      =================================================
+      */
+      var product_ids = _.flatten(_.map(self.AccountInvoiceLine, function(item) {
+        return item.product_id[0];
+      }));
+      var modules = self.checkModel('point_of_sale');
+      if (modules.length > 0) {
+        /*
+        =================================================
+            BUSCAR EN POS ORDER LINE
+        =================================================
+        */
+        var product_order_ids = _.flatten(_.map(self.PosOrderLine, function(item) {
+          return item.product_id[0];
+        }));
+        product_ids = product_ids.concat(product_order_ids);
+      }
+
+      var domain = [
+        ['id', 'in', product_ids]
+      ];
+
+      /*
+      =================================================
+          OBTENER CATEGORIAS EN CASCADA
+      =================================================
+      */
+      if (category) {
+        // Nivel 0, nivel 1
+        var category_ids = _.map(_.filter(self.ProductCategory, function(item) {
+          return item.id == category || item.parent_id[0] == category;
+        }), function(map) {
+          return map.id;
+        });
+
+        // Nivel 0, nivel 1, nivel 2
+        var category_children_ids = _.map(_.filter(self.ProductCategory, function(item) {
+          return _.contains(category_ids, item.parent_id[0]) || item.id == category;
+        }), function(map) {
+          return map.id;
+        });
+
+        // Nivel 0, nivel 1, nivel 2, nivel 3
+        var category_grandchildren_ids = _.map(_.filter(self.ProductCategory, function(item) {
+          return _.contains(category_children_ids, item.parent_id[0]) || item.id == category;
+        }), function(map) {
+          return map.id;
+        });
+
+        // Nivel 0, nivel 1, nivel 2, nivel 3, nivel 4
+        categ_ids = _.map(_.filter(self.ProductCategory, function(item) {
+          return _.contains(category_grandchildren_ids, item.parent_id[0]) || item.id == category;
+        }), function(map) {
+          return map.id;
+        });
+      };
+
+      /*
+      =================================================
+          FILTRAR MARCA
+      =================================================
+      */
+      if (brand && brand != 9999999) {
+        domain.push(['product_brand_id', '=', parseInt(brand)]);
+      }
+
+      /*
+      =================================================
+          FILTRAR CATEGORIA
+      =================================================
+      */
+      if (category && category != 9999999) {
+        domain.push(['categ_id', 'in', categ_ids]);
+      }
+
+      var ProductProduct = new model.web.Model('product.product');
+      return ProductProduct.call('getProductProduct', [domain], {
+        context: new model.web.CompoundContext()
+      });
+    },
+
+    /*====================================================================
+        UPDATE SELECTIONS
+    ====================================================================*/
+    updateSelections: function() {
+      var self = this;
+      var company = self.$el.find('#current-company').val();
+      if (company != 9999999) {
+        /*===================
+            STORE SELECTION
+        ===================*/
+        var store = self.$el.find('#current-store').empty();
+        self.$el.find('#current-store').append('<option value="9999999">Todas las sucursales</option>');
+        _.each(self.ResStore, function(item) {
+          if (parseFloat(company) == item.company_id[0]) {
+            self.$el.find('#current-store').append('<option value="' + item.id + '">' + item.name + '</option>');
+          }
+        });
+        /*====================
+            PERIOD SELECTION
+        ====================*/
+        var period = self.$el.find('#current-period').empty();
+        self.$el.find('#current-period').append('<option value="9999999">Todos los periodos</option>');
+        _.each(self.AccountPeriod, function(item) {
+          if (parseFloat(company) == item.company_id[0]) {
+            self.$el.find('#current-period').append('<option value="' + item.id + '">' + item.name + '</option>');
+          }
+        });
+      } else {
+        /*===================
+            STORE SELECTION
+        ===================*/
+        var store = self.$el.find('#current-store').empty();
+        self.$el.find('#current-store').append('<option value="9999999">Todas las sucursales</option>');
+        _.each(self.ResStore, function(item) {
+          self.$el.find('#current-store').append('<option value="' + item.id + '">' + item.name + '</option>');
+        });
+        /*====================
+            PERIOD SELECTION
+        ====================*/
+        var period = self.$el.find('#current-period').empty();
+        self.$el.find('#current-period').append('<option value="9999999">Todas los periodos</option>');
+        _.each(self.AccountPeriod, function(item) {
+          self.$el.find('#current-period').append('<option value="' + item.id + '">' + item.name + '</option>');
+        });
+      }
+    },
+
+    updateAttributeSelections: function() {
+      var self = this;
+      var attribute = self.$el.find('#current-attribute').val();
+      if (attribute != 9999999) {
+        /*=============================
+            ATTRIBUTE VALUE SELECTION
+        =============================*/
+        var attribute_value = self.$el.find('#current-attribute-value').empty();
+        self.$el.find('#current-attribute-value').append('<option value="9999999">Todos los valores de atributos</option>');
+        _.each(self.ProductAttributeValue, function(item) {
+          if (parseFloat(attribute) == item.attribute_id[0]) {
+            self.$el.find('#current-attribute-value').append('<option value="' + item.id + '">' + item.name + '</option>');
+          }
+        });
+      } else {
+        /*=============================
+            ATTRIBUTE VALUE SELECTION
+        =============================*/
+        var attribute_value = self.$el.find('#current-attribute-value').empty();
+        self.$el.find('#current-attribute-value').append('<option value="9999999">Todos los valores de atributos</option>');
+        _.each(self.ProductAttributeValue, function(item) {
+          self.$el.find('#current-attribute-value').append('<option value="' + item.id + '">' + item.name + '</option>');
+        });
+      }
+    },
+
+    updateJournalSelections: function() {
+      var self = this;
+      var store = self.$el.find('#current-store').val();
+      if (store != 9999999) {
+        /*=============================
+            ACCOUNT JOURNAL SELECTION
+        =============================*/
+        var journal = self.$el.find('#current-journal').empty();
+        self.$el.find('#current-journal').append('<option value="9999999">Todas las facturas</option>');
+        _.each(self.AccountJournal, function(item) {
+          if (parseFloat(store) == item.store_ids) {
+            self.$el.find('#current-journal').append('<option value="' + item.id + '">' + item.name + '</option>');
+          }
+        });
+      } else {
+        /*=============================
+            ACCOUNT JOURNAL SELECTION
+        =============================*/
+        var journal = self.$el.find('#current-journal').empty();
+        self.$el.find('#current-journal').append('<option value="9999999">Todas las facturas</option>');
+        _.each(self.AccountJournal, function(item) {
+          self.$el.find('#current-journal').append('<option value="' + item.id + '">' + item.name + '</option>');
+        });
+      }
+    },
+    updatePeriodSelections: function() {
+      var self = this;
+      var period = self.$el.find('#current-period').val();
+      if (period != 9999999) {
+        self.$el.find('#current-date').val(9999999);
+        self.$el.find('#current-date').prop('disabled', 'disabled');
+        self.$el.find('.datepicker').css('display', 'none');
+      } else {
+        self.$el.find('#current-date').prop('disabled', false);
+      }
+    },
+
+    /*====================================================================
+        GET RES COMPANY
+    ====================================================================*/
+    getResCompany: function(id) {
+      var self = this;
+      return _.filter(self.ResCompany, function(item) {
+        return item.id == id;
+      })
+    },
+
+    /*====================================================================
+        GET RES CURRENCY BASE
+    ====================================================================*/
+    getResCurrency: function(id) {
+      var self = this;
+      return _.filter(self.ResCurrency, function(item) {
+        return item.id === id;
+      })
+    },
+
+    /*====================================================================
+        GET POS ORDER LINE
+    ====================================================================*/
+    getPosOrderLine: function(id) {
+      var self = this;
+      var content = []
+      var period = self.$el.find('#current-period').val();
+      var date = self.$el.find('#current-date').val();
+      var desde = self.$el.find('#from').val();
+      var hasta = self.$el.find('#to').val();
+
+      content = _.flatten(_.filter(self.PosOrderLine, function(item) {
+        return item.product_id[0] === id;
+      }));
+
+      /*
+      =================================================
+          FILTRAR PERIODO
+      =================================================
+      */
+      if (period && period != 9999999) {
+        var periodo = _.flatten(_.filter(self.AccountPeriod, function(item) {
+          return item.id == period;
+        }));
+        content = _.flatten(_.filter(content, function(item) {
+          var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+          return moment(utc._d).format('YYYY-MM') === moment(periodo[0].date_start).format('YYYY-MM');
+        }));
+      }
+      /*
+      =================================================
+          FILTRAR POR FECHAS
+      =================================================
+      */
+      if (date && date != 9999999) {
+        if (date == 'range') {
+          if (desde) {
+            var date = desde.split('/')
+            date = (date[2] + "-" + date[1] + "-" + date[0]);
+            content = _.flatten(_.filter(content, function(item) {
+              var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+              return moment(utc._d).format('YYYY-MM-DD') >= date;
+            }));
+          }
+          if (hasta) {
+            var date = hasta.split('/')
+            date = (date[2] + "-" + date[1] + "-" + date[0]);
+            content = _.flatten(_.filter(content, function(item) {
+              var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+              return moment(utc._d).format('YYYY-MM-DD') <= date;
+            }));
+          }
+        }
+
+        if (date == 'today') {
+          var today = moment().format('YYYY-MM-DD');
+          content = _.flatten(_.filter(content, function(item) {
+            var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+            return moment(utc._d).format('YYYY-MM-DD') == today;
+          }));
+        }
+
+        if (date == 'yesterday') {
+          var yesterday = moment().add(-1, 'days').format('YYYY-MM-DD');
+          content = _.flatten(_.filter(content, function(item) {
+            var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+            return moment(utc._d).format('YYYY-MM-DD') == yesterday;
+          }));
+        }
+
+        if (date == 'currentMonth') {
+          var currentMonth = moment().format('YYYY-MM');
+          content = _.flatten(_.filter(content, function(item) {
+            var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+            return moment(utc._d).format('YYYY-MM') == currentMonth;
+          }));
+        }
+
+        if (date == 'lastMonth') {
+          var lastMonth = moment().add(-1, 'months').format('YYYY-MM');
+          content = _.flatten(_.filter(content, function(item) {
+            var utc = moment.utc(item.create_date, 'YYYY-MM-DD h:mm:ss A');
+            return moment(utc._d).format('YYYY-MM') == lastMonth;
+          }));
+
+        }
+      }
+
+      return content;
+    },
+
+    /*====================================================================
+        GET ACCOUNT INVOICE LINE
+    ====================================================================*/
+    getAccountInvoiceLine: function(id) {
+      var self = this;
+      return _.flatten(_.filter(self.AccountInvoiceLine, function(item) {
+        return item.product_id[0] == id;
+      }));
+    },
+
+    /*====================================================================
+        GET PRODUCT PRODUCT
+    ====================================================================*/
+    getProductProduct: function(id) {
+      var self = this;
+      var attribute = self.$el.find('#current-attribute').val();
+      var attribute_value = self.$el.find('#current-attribute-value').val();
+
+      //categorias
+
+          // Nivel 0, nivel 1
+          var category_ids = _.map(_.filter(self.ProductCategory,function (item) {
+              return item.id == id || item.parent_id[0] == id;
+          }), function(map){
+              return map.id;
+          });
+
+          // Nivel 0, nivel 1, nivel 2
+          var category_children_ids = _.map(_.filter(self.ProductCategory,function (item) {
+              return _.contains(category_ids, item.parent_id[0]) || item.id == id;
+          }), function(map){
+              return map.id;
+          });
+
+          // Nivel 0, nivel 1, nivel 2, nivel 3
+          var category_grandchildren_ids = _.map(_.filter(self.ProductCategory,function (item) {
+              return _.contains(category_children_ids, item.parent_id[0]) || item.id == id;
+          }), function(map){
+              return map.id;
+          }) ;
+
+          // Nivel 0, nivel 1, nivel 2, nivel 3, nivel 4
+          var categ_ids =  _.map(_.filter(self.ProductCategory,function (item) {
+              return _.contains(category_grandchildren_ids, item.parent_id[0]) || item.id == id;
+          }), function(map){
+              return map.id;
+          });
+
+      var content = _.filter(self.ProductProduct, function(item) {
+        return categ_ids.includes(item.categ_id.id);
+      });
+      /*
+      =================================================
+          FILTRAR VALOR DEL ATRIBUTO
+      =================================================
+      */
+      if (attribute && attribute != 9999999 && attribute_value == 9999999) {
+        content = _.filter(content, function(item) {
+          return _.contains(item.attribute_ids, parseInt(attribute));
+        });
+      }
+      /*
+      =================================================
+          FILTRAR VALOR DEL ATRIBUTO
+      =================================================
+      */
+      if (attribute_value && attribute_value != 9999999) {
+        content = _.filter(content, function(item) {
+          return _.contains(item.atribute_value_ids, parseFloat(attribute_value));
+        });
+      }
+
+      return content;
+    },
+
+    getMainCategories: function(){
+      var self = this;
+      var category = self.$el.find('#current-category').val();
+
+      var category_ids = _.map(_.filter(self.ProductCategory, function(item) {
+        return !item.parent_id;
+      }), function(map){
+        return map.child_id;
+      });
+
+      if(category && category != 9999999){
+        var categories = _.filter(self.ProductCategory, function(item){
+          return item.id == parseInt(category);
+        });
+      }
+      else{
+        var categories = _.filter(self.ProductCategory, function(item){
+          return category_ids[0].includes(item.id);
+        });
+      }
+    
+      return categories;
+    },
+
+    /*====================================================================
+        BUILD
+    ====================================================================*/
+    BuildTable: function() {
+      var self = this;
+      var data = [];
+      var data_level0 = []
+      var type = $('#current-type').val();
+      var company = $('#current-company').val();
+      if (company && company != 9999999) {
+        var ResCompany = self.getResCompany(company).shift();
+        var CurrencyBase = self.getResCurrency(ResCompany.currency_id[0]).shift();
+      } else {
+        var CurrencyBase = self.getResCurrency(self.ResCompany[0].currency_id[0]).shift();
+      };
+      /*
+      ==========================================
+          RECORRER PRODUCTOS
+      ==========================================
+      */
+      var ProductCategory = self.getMainCategories();
+      _.each(ProductCategory, function(i) {
+        var header_qty = 0;
+        var header_amount = 0;
+        data = [];
+        var ProductProduct = self.getProductProduct(i.id);
+        _.each(ProductProduct, function(item) {
+          var info = [];
+          var AccountInvoiceLine = self.getAccountInvoiceLine(item.id);
+          var PosOrderLine = self.getPosOrderLine(item.id);
+          /*
+          ==========================================
+              ACCOUNT INVOICE LINE
+          ==========================================
+          */
+          var invoice_amount = 0;
+          var invoice_qty = 0;
+          if (type == 'sale' || type == 9999999) {
+            if (AccountInvoiceLine.length > 0) {
+              var Currency = self.getResCurrency(AccountInvoiceLine[0].invoice_currency[0]).shift();
+              /*
+              ==========================================
+                  AMOUNT TOTAL
+              ==========================================
+              */
+              invoice_amount = _.reduce(_.map(AccountInvoiceLine, function(map) {
+                return map.amount_currency
+              }), function(meno, num) {
+                return meno + num
+              }, 0);
+              /*
+              ==========================================
+                  QUANTITY TOTAL
+              ==========================================
+              */
+              invoice_qty = _.reduce(_.map(AccountInvoiceLine, function(map) {
+                return map.quantity
+              }), function(meno, num) {
+                return meno + num
+              }, 0);
+              _.each(AccountInvoiceLine, function(index) {
+                var price_unit = 0;
+                var amount = 0;
+                if (Currency.id != CurrencyBase.id) {
+                  price_unit = accounting.formatMoney(index.price_unit_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator) + ' (' + accounting.formatMoney(index.price_unit, '', Currency.decimal_places, Currency.thousands_separator, Currency.decimal_separator) + ')';
+                  amount = accounting.formatMoney(index.amount_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator) + ' (' + accounting.formatMoney(index.price_subtotal, '', Currency.decimal_places, Currency.thousands_separator, Currency.decimal_separator) + ')';
+                } else {
+                  price_unit = accounting.formatMoney(index.price_unit_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                  amount = accounting.formatMoney(index.amount_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                };
+                info.push({
+                  invoice_id: index.invoice_id,
+                  invoice_name: index.number,
+                  price_unit: price_unit,
+                  quantity: index.quantity,
+                  amount: amount,
+                });
+              });
+            };
+          };
+          /*
+          ==========================================
+              POS ORDER LINE
+          ==========================================
+          */
+          var pos_amount = 0;
+          var pos_qty = 0;
+          if (PosOrderLine.length > 0) {
+            var Currency = self.getResCurrency(PosOrderLine[0].order_currency[0]).shift();
+            /*
+            ==========================================
+                AMOUNT TOTAL
+            ==========================================
+            */
+            pos_amount = _.reduce(_.map(PosOrderLine, function(map) {
+              return map.price_subtotal_incl_currency
+            }), function(meno, num) {
+              return meno + num
+            }, 0);
+            /*
+            ==========================================
+                QUANTITY TOTAL
+            ==========================================
+            */
+            pos_qty = _.reduce(_.map(PosOrderLine, function(map) {
+              return map.qty
+            }), function(meno, num) {
+              return meno + num
+            }, 0);
+            /*
+            ==========================================
+                INFO
+            ==========================================
+            */
+            _.each(PosOrderLine, function(index) {
+              var price_unit = 0;
+              var amount = 0;
+              if (Currency.id != CurrencyBase.id) {
+                price_unit = accounting.formatMoney(index.price_unit_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator) + ' (' + accounting.formatMoney(index.price_unit, '', Currency.decimal_places, Currency.thousands_separator, Currency.decimal_separator) + ')';
+                amount = accounting.formatMoney(index.price_subtotal_incl_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator) + ' (' + accounting.formatMoney(index.price_subtotal_incl, '', Currency.decimal_places, Currency.thousands_separator, Currency.decimal_separator) + ')';
+              } else {
+                price_unit = accounting.formatMoney(index.price_unit_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+                amount = accounting.formatMoney(index.price_subtotal_incl_currency, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator);
+              };
+              info.push({
+                invoice_id: index.order_id[0],
+                invoice_name: index.order_id[1],
+                price_unit: price_unit,
+                quantity: index.qty,
+                amount: amount,
+              });
+            });
+          };
+
+          var amount = invoice_amount + pos_amount;
+          var qty = invoice_qty + pos_qty;
+
+          header_qty = header_qty + qty;
+          header_amount = header_amount + amount;
+
+          data.push({
+            id: item.id,
+            product: item.display_name,
+            categ_id: item.categ_id.complete_name,
+            qty_total: accounting.formatMoney(qty, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+            total_amount: accounting.formatMoney(amount, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+            atribute_value_ids: item.atribute_value_ids,
+
+            qty_no_format: qty,
+            amount_no_format: amount,
+
+            info: info,
+          });
+
+          data.sort(function(a, b) {
+            return b.amount_no_format - a.amount_no_format
+          });
+        });
+
+
+        if (header_amount > 0) {
+          data_level0.push({
+            id: i.id,
+            name: i.name,
+            header_qty: accounting.formatMoney(header_qty, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+            header_amount: accounting.formatMoney(header_amount, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+
+            header_qty_no_format: header_qty,
+            header_amount_no_format: header_amount,
+
+            data: data,
+
+            decimal_places: CurrencyBase.decimal_places,
+            thousands_separator: CurrencyBase.thousands_separator,
+            decimal_separator: CurrencyBase.decimal_separator,
+          })
+        }
+      })
+      data_level0.sort(function(a, b) {
+        return b.header_amount_no_format - a.header_amount_no_format
+      });
+
+      var datas = [];
+      _.each(data_level0, function(each) {
+        _.each(each.data, function(item) {
+          datas.push(item);
+        })
+      })
+      datas.sort(function(a, b) {
+        return b.amount_no_format - a.amount_no_format
+      });
+
+      self.content = data_level0;
+      self.loadTable(data_level0);
+      self.CallCharts(datas, CurrencyBase);
+    },
+
+    /*
+    ======================================================================
+        LLAMAR LOS GRAFICOS
+    ======================================================================
+    */
+    CallCharts: function(data, CurrencyBase) {
+      var self = this;
+      /*
+      ================================================
+      BAR CHART
+      ================================================
+      */
+      var BarChart = new model.eiru_reports.ReportChartWidget(self);
+      var rank = 10;
+      var item;
+      var label = [];
+      var body = [];
+      for (var i = 0; i < rank; i++) {
+        if (data[i]) {
+          item = data[i];
+        } else {
+          item = {};
+          item.product = "N/A";
+          item.amount_no_format = 0;
+        }
+        label.push(item.product.trim());
+        body.push(item.amount_no_format);
+      }
+      BarChart.BuildBarChart(data, CurrencyBase, label, body);
+
+      /*
+      ================================================
+      PIE CHART
+      ================================================
+      */
+      var PieChart = new model.eiru_reports.ReportChartWidget(self);
+      var rank = 10;
+      var item;
+      var label = [];
+      var body = [];
+      var amount_total = _.reduce(_.map(data, function(map) {
+        return map.amount_no_format
+      }), function(meno, num) {
+        return meno + num
+      }, 0);
+      for (var i = 0; i < rank; i++) {
+        if (data[i]) {
+          item = data[i];
+        } else {
+          item = {};
+          item.product = "N/A";
+          item.amount_no_format = 0;
+        }
+        label.push(item.product.trim());
+        body.push((item.amount_no_format / amount_total) * 100);
+      }
+      var num = 0;
+      for (var i = 9; i < data.length; i++) {
+        num += data[i].amount_no_format;
+      }
+      if (num > 0) {
+        label.push('Otros Productos');
+        body.push((num / amount_total) * 100);
+      }
+      PieChart.BuildPieChart(data, CurrencyBase, label, body);
+
+      /*
+      ================================================
+      DOUGHNUT CHART
+      ================================================
+      */
+      var PieChart = new model.eiru_reports.ReportChartWidget(self);
+      var info = [];
+      var content = [];
+      var ProductAttributeValue = self.ProductAttributeValue;
+      if (ProductAttributeValue.length > 0) {
+        _.each(ProductAttributeValue, function(item) {
+          content = _.filter(data, function(index) {
+            return _.contains(index.atribute_value_ids, item.id);
+          });
+          var amount = _.reduce(_.map(content, function(map) {
+            return map.amount_no_format
+          }), function(meno, num) {
+            return meno + num
+          }, 0);
+          if (amount > 0) {
+            info.push({
+              name: item.attribute_id[1] + ' - ' + item.name + ' - ' + accounting.formatMoney(amount, CurrencyBase.symbol, CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+              amount_no_format: amount
+            });
+          };
+        });
+        info.sort(function(a, b) {
+          return b.amount_no_format - a.amount_no_format
+        });
+
+        var rank = 9;
+        var item;
+        var label = [];
+        var body = [];
+
+        var amount_total = _.reduce(_.map(data, function(map) {
+          return map.amount_no_format
+        }), function(meno, num) {
+          return meno + num
+        }, 0);
+
+        for (var i = 0; i < rank; i++) {
+          if (info[i]) {
+            item = info[i];
+          } else {
+            item = {};
+            item.name = "N/A";
+            item.amount_no_format = 0;
+          }
+          label.push(item.name.trim());
+          body.push((item.amount_no_format / amount_total) * 100);
+        };
+        var num = 0;
+
+        for (var i = 9; i < info.length; i++) {
+          num += info[i].amount_no_format;
+        };
+
+        if (num > 0) {
+          label.push('Otros Atributos');
+          body.push((num / amount_total) * 100);
+        };
+        PieChart.BuildDoughnutChart(data, CurrencyBase, label, body);
+        self.$el.find('.doughnut-chart').css('display', 'block');
+      };
+
+
+      self.$el.find('.report-form').css('display', 'block');
+      self.$el.find('.search-form').unblock();
+      self.$el.find('.report-form').unblock();
+    },
+
+
+
+    /*====================================================================
+        LOAD BOOTSTRAP TABLE
+    ====================================================================*/
+    loadTable: function(rowsTable) {
+      var self = this;
+      var data = [];
+      self.rowsData = rowsTable;
+      var table = this.$el.find('#table');
+
+      table.bootstrapTable({
+        data: rowsTable,
+        detailView: true,
+        onExpandRow: function(index, row, $detail) {
+          $detail.html('<div class="panel panel-first-style"><div class="panel-heading panel-header-first-style">Productos de la categoría</div><table id="table2"></table></div>').find('table').bootstrapTable({
+            columns: [{
+                field: 'product',
+                title: 'Producto',
+                align: 'left',
+                class: 'hover',
+              },
+              {
+                field: 'qty_total',
+                title: 'Cantidad',
+                align: 'left',
+              },
+              {
+                field: 'total_amount',
+                title: 'Monto',
+                align: 'right',
+              },
+            ],
+
+            data: row.data,
+            detailView: true,
+            onExpandRow: function(index, row, $detail) {
+              $detail.html('<div class="panel panel-first-style"><div class="panel-heading panel-header-first-style">Pedidos del produto</div><table id="table3"></table></div>').find('table').bootstrapTable({
+                columns: [{
+                    field: 'invoice_name',
+                    title: 'Factura',
+                    align: 'left',
+                  },
+                  {
+                    field: 'price_unit',
+                    title: 'Precio Unitario',
+                    align: 'left',
+                  },
+                  {
+                    field: 'quantity',
+                    title: 'Cantidad',
+                    align: 'center',
+                  },
+                  {
+                    field: 'amount',
+                    title: 'Monto',
+                    align: 'right',
+                  },
+                ],
+                data: row.info,
+              })
+              $('#table2').removeClass('table-hover');
+              $('thead').css('background', 'none');
+            }
+          })
+          $('#table3').removeClass('table-hover');
+          $('thead').css('background', 'none');
+        }
+      })
+
+      table.bootstrapTable('load', rowsTable);
+    },
+
+    /*====================================================================
+        PRINT PDF
+    ====================================================================*/
+    clickOnAction: function(e) {
+      var self = this;
+      var ResCompany;
+      var action = this.$el.find(e.target).val();
+      var company = $('#current-company').val();
+      if (company && company != 9999999) {
+        ResCompany = self.getResCompany(company).shift();
+        var CurrencyBase = self.getResCurrency(ResCompany.currency_id[0]).shift();
+      } else {
+        ResCompany = self.ResCompany[0];
+        var CurrencyBase = self.getResCurrency(self.ResCompany[0].currency_id[0]).shift();
+      }
+      var getColumns = [];
+      var rows = [];
+      var table = this.$el.find("#table");
+      var column = table.bootstrapTable('getVisibleColumns');
+      var row = table.bootstrapTable('getData');
+
+
+      // impresion por categorias
+
+      if (action === 'pdf_categ') {
+        var header_qty = qtyFormatter(row);
+        var header_amount = totalFormatter(row);
+
+        row.push({
+          name: 'Totales',
+          header_qty: header_qty,
+          header_amount: header_amount,
+        })
+        var data = _.map(column, function(val) {
+          return val.field
+        });
+        _.each(_.map(column, function(val) {
+          return val
+        }), function(item) {
+          getColumns.push([{
+            title: item.title,
+            dataKey: item.field
+          }]);
+        });
+        /*
+        ============================================================
+            CONFIGURACION DEL PDF
+        ============================================================
+        */
+        var pdf_title = 'Ranking de productos (+Vendidos).';
+        var pdf_type = '';
+        var pdf_name = 'ranking_productos_';
+        var pdf_columnStyles = {
+          name: {
+            halign: 'left'
+          },
+          header_qty: {
+            columnWidth: 30,
+            halign: 'right'
+          },
+          header_amount: {
+            columnWidth: 30,
+            halign: 'right'
+          },
+        };
+        /*
+        ============================================================
+            LLAMAR FUNCION DE IMPRESION
+        ============================================================
+        */
+        var filter = self.getFilter();
+        var pdf = new model.eiru_reports.ReportPdfWidget(self);
+        pdf.drawPDF(
+          _.flatten(getColumns),
+          row,
+          ResCompany,
+          pdf_title,
+          pdf_type,
+          pdf_name,
+          pdf_columnStyles,
+          filter,
+        );
+      }
+
+      // impresion por producto
+
+      if (action === 'pdf_product') {
+        var row_categ = [];
+        _.each(row, function(each) {
+          _.each(each.data, function(item) {
+            row_categ.push(item);
+          })
+        })
+
+        row_categ.sort(function(a, b) {
+          return b.amount_no_format - a.amount_no_format
+        });
+
+        var qty = _.reduce(_.map(row_categ, function(map) {
+          return map.qty_no_format;
+        }), function(memo, num) {
+          return memo + num;
+        });
+
+        var amount = _.reduce(_.map(row_categ, function(map) {
+          return map.amount_no_format;
+        }), function(memo, num) {
+          return memo + num;
+        });
+
+        row_categ.push({
+          product: 'Totales',
+          qty_total: accounting.formatMoney(qty, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+          total_amount: accounting.formatMoney(amount, '', CurrencyBase.decimal_places, CurrencyBase.thousands_separator, CurrencyBase.decimal_separator),
+        })
+
+        var getColumns = [{
+            'title': 'Producto',
+            'dataKey': 'product'
+          },
+          {
+            'title': 'Categoría',
+            'dataKey': 'categ_id'
+          },
+          {
+            'title': 'Cantidad',
+            'dataKey': 'qty_total'
+          },
+          {
+            'title': 'Monto',
+            'dataKey': 'total_amount'
+          },
+        ];
+
+        /*
+        ============================================================
+            CONFIGURACION DEL PDF
+        ============================================================
+        */
+        var pdf_title = 'Ranking de productos (+Vendidos).';
+        var pdf_type = '';
+        var pdf_name = 'ranking_productos_';
+        var pdf_columnStyles = {
+          product: {
+            halign: 'left'
+          },
+          categ_id: {
+            halign: 'left'
+          },
+          qty_total: {
+            columnWidth: 30,
+            halign: 'right'
+          },
+          total_amount: {
+            columnWidth: 30,
+            halign: 'right'
+          },
+        };
+        /*
+        ============================================================
+            LLAMAR FUNCION DE IMPRESION
+        ============================================================
+        */
+        var filter = self.getFilter();
+        var pdf = new model.eiru_reports.ReportPdfWidget(self);
+        pdf.drawPDF(
+          _.flatten(getColumns),
+          row_categ,
+          ResCompany,
+          pdf_title,
+          pdf_type,
+          pdf_name,
+          pdf_columnStyles,
+          filter,
+        );
+      }
+    },
+    getFilter: function() {
+      var self = this;
+      var company = self.$el.find('#current-company').val();
+      var store = self.$el.find('#current-store').val();
+      var type = self.$el.find('#current-type').val();
+      var period = self.$el.find('#current-period').val();
+      var category = self.$el.find('#current-category').val();
+      var date = self.$el.find('#current-date').val();
+      var desde = self.$el.find('#from').val();
+      var hasta = self.$el.find('#to').val();
+      var filter = [];
+      if (company && company != 9999999) {
+        var ResCompany = _.filter(self.ResCompany, function(item) {
+          return item.id == company;
+        });
+        filter.push({
+          title: 'Empresa',
+          value: ResCompany[0].name,
+        });
+      };
+      if (store && store != 9999999) {
+        var ResStore = _.filter(self.ResStore, function(item) {
+          return item.id == store;
+        });
+        filter.push({
+          title: 'Sucursal',
+          value: ResStore[0].name,
+        });
+      };
+      if (type && type != 9999999) {
+        filter.push({
+          title: 'Tipo de Venta',
+          value: $("#current-type option:selected").text(),
+        });
+      };
+      if (period && period != 9999999) {
+        var AccountPeriod = _.filter(self.AccountPeriod, function(item) {
+          return item.id == period;
+        });
+        filter.push({
+          title: 'Periodo',
+          value: AccountPeriod[0].name,
+        });
+      };
+      if (category && category != 9999999) {
+        var categ = _.filter(self.ProductCategory, function(item) {
+          return item.id == category;
+        });
+        filter.push({
+          title: 'Categoría',
+          value: categ[0].name,
+        });
+      };
+      if (date && date != 9999999) {
+        moment.locale('es', {
+          months: 'Enero_Febrero_Marzo_Abril_Mayo_Junio_Julio_Agosto_Septiembre_Octubre_Noviembre_Diciembre'.split('_'),
+        });
+        if (date == 'range') {
+          filter.push({
+            title: 'Fecha',
+            value: desde + ' al ' + hasta,
+          });
+        } else {
+          if (date == 'today') {
+            var fecha = moment().format('DD/MM/YYYY');
+          };
+          if (date == 'yesterday') {
+            var fecha = moment().add(-1, 'days').format('DD/MM/YYYY');
+          }
+          if (date == 'currentMonth') {
+            var fecha = moment().format('MMMM/YYYY');
+          }
+          if (date == 'lastMonth') {
+            var fecha = moment().add(-1, 'months').format('MMMM/YYYY');
+          }
+          filter.push({
+            title: 'Fecha',
+            value: fecha,
+          });
+        };
+      };
+      return filter;
+    },
+  });
+}

+ 184 - 0
static/src/reports/report_sale_ranking_product.xml

@@ -0,0 +1,184 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<template xml:space="preserve">
+    <t t-name="ReportProductRankingBaco">
+        <div class="report_view">
+            <div class="reporting_page_header">
+                <h1 class="report_title">Ranking de productos por categoría</h1>
+            </div>
+            <div class="container search-form" style="border-bottom:1px solid #eee; width:95%;">
+                <div class="row">
+                    <div class="col-lg-3 company filter-style">
+                        <label>Empresa</label>
+                        <select id="current-company" class="form-control form-control-sm"></select>
+                    </div>
+                    <div class="col-lg-3 store filter-style">
+                        <label>Sucursal</label>
+                        <select id="current-store" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 journal filter-style">
+                        <label>Factura</label>
+                        <select id="current-journal" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 type filter-style">
+                        <label>Tipo de Venta</label>
+                        <select id="current-type" class="form-control form-control-sm">
+                            <option value="9999999">Todos los Tipos</option>
+                            <option value="tpv">Terminal</option>
+                            <option value="sale">Normal</option>
+                        </select>
+                    </div>
+                    <div class="col-lg-3 filter-style">
+                        <label>Periodo</label>
+                        <select id="current-period" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 filter-style">
+                        <label>Categoria</label>
+                        <select id="current-category" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 brand filter-style">
+                        <label>Marca</label>
+                        <select id="current-brand" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 attribute filter-style">
+                        <label>Atributo</label>
+                        <select id="current-attribute" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 attribute-value filter-style">
+                        <label>Valor del Atributo</label>
+                        <select id="current-attribute-value" class="form-control form-control-sm">
+                        </select>
+                    </div>
+                    <div class="col-lg-3 filter-style">
+                        <label>Fechas</label>
+                        <select id="current-date" class="form-control form-control-sm">
+                            <option value="9999999">Sin fechas</option>
+                            <option value="today">Hoy</option>
+                            <option value="yesterday">Ayer</option>
+                            <option value="currentMonth">Mes Actual</option>
+                            <option value="lastMonth">Mes Pasado</option>
+                            <option value="range">Busqueda Avanzada</option>
+                        </select>
+                    </div>
+                </div>
+                <div class="row" >
+                    <div class="datepicker" style="display:none;">
+                        <div class="col-lg-3 filter-style col-md-offset-3">
+                            <div class="input-group">
+                                <span class="input-group-addon" id="basic-addon1">Desde</span>
+                                <input type="text" id="from" class="form-control" aria-describedby="basic-addon1"/>
+                            </div>
+                        </div>
+                        <div class="col-lg-3 filter-style">
+                            <div class="input-group">
+                                <span class="input-group-addon" id="basic-addon1">Hasta</span>
+                                <input type="text" id="to" class="form-control" aria-describedby="basic-addon1"/>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+                <div class="row">
+                    <div class="text-center" style="padding-top:20px;">
+                        <button id="generate" class="myButton" aria-label="Left Align" style="color:#fff;display:none;">
+                          Generar
+                      </button>
+                    </div>
+                    <br/>
+                </div>
+            </div>
+
+            <div class="report-form" style="display:none;">
+                <div class="chart-container center-block" style="padding-top:20px;height:300px;border-bottom:1px solid #eee; width:95%;">
+                    <canvas class="reporting-chart"></canvas>
+                </div>
+                <div class="chart-container center-block" style="padding-top:20px; height:300px;border-bottom:1px solid #eee; width:95%;">
+                    <canvas class="reporting-pie-chart"></canvas>
+                </div>
+                <div class="chart-container center-block doughnut-chart" style="padding-top:20px; height:300px; padding-bottom:20px;border-bottom:1px solid #eee; width:95%;display:none;">
+                    <canvas class="reporting-doughnut-chart"></canvas>
+                </div>
+                <div id="toolbar">
+                    <button class="oe_button myButton" value="pdf_categ">Imprimir Informe por categoría</button>
+                    <button class="oe_button myButton" value="pdf_product">Imprimir Informe por producto</button>
+                </div>
+                <div class="container" style="width:95%;">
+                    <table id="table"
+                        data-pagination="true"
+                        data-toggle="table"
+                        data-toolbar="#toolbar"
+                        data-show-columns="true"
+                        data-classes="table table-condensed  table-no-bordered"
+                        data-search="true"
+                        data-show-export="true"
+                        data-show-toggle="true"
+                        data-pagination-detail-h-align="left"
+                        data-show-footer="true"
+                        data-footer-style="footerStyle"
+                        data-buttons-class="oe_button myButton"
+                        data-show-pagination-switch="true"
+                        data-page-size="10"
+                        data-search-on-enter-key="true"
+                        data-undefined-text=" "
+                        data-detail-view="true"
+                        >
+                        <thead style="background:none;">
+                            <tr>
+                                <th data-field="name" data-align="left">Categoría</th>
+                                <th data-field="header_qty" data-align="left" data-footer-formatter="qtyFormatter">Cantidad</th>
+                                <th data-field="header_amount" data-align="left" data-footer-formatter="totalFormatter">Monto</th>
+                            </tr>
+                        </thead>
+                    </table>
+                </div>
+            </div>
+            <script>
+                <!--
+                    TOTAL
+                -->
+                function totalFormatter(rowsTable) {
+                    var decimal_places = 0;
+                    var thousands_separator = '.';
+                    var decimal_separator = ',';
+                    if(rowsTable.length > 0){
+                        decimal_places = rowsTable[0].decimal_places;
+                        thousands_separator = rowsTable[0].thousands_separator;
+                        decimal_separator = rowsTable[0].decimal_separator;
+                    }
+                    var amount =  _.reduce(_.map(rowsTable,function(item){
+                        return (item.header_amount_no_format);
+                    }), function(memo, num){
+                    return memo + num; },0)
+                    return accounting.formatNumber(amount,decimal_places,thousands_separator,decimal_separator);
+                }
+                <!--
+                    TOTAL
+                -->
+                function qtyFormatter(rowsTable) {
+                    var decimal_places = 0;
+                    var thousands_separator = '.';
+                    var decimal_separator = ',';
+                    var quantity =  _.reduce(_.map(rowsTable,function(item){
+                        return (item.header_qty_no_format);
+                    }), function(memo, num){
+                    return memo + num; },0)
+                    return accounting.formatNumber(quantity,decimal_places,thousands_separator,decimal_separator);
+                }
+                <!--
+                    FOOTER STYLE
+                -->
+                function footerStyle(row, index) {
+                    return {
+                        css: {
+                          "font-weight": "bold"
+                        }
+                    };
+                };
+            </script>
+        </div>
+    </t>
+</template>

+ 5 - 0
static/src/xml/eiru_reporting.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<template xml:space="preserve">
+    
+</template>

+ 5 - 0
static/src/xml/eiru_reporting_base.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<template xml:space="preserve">
+    
+</template>

+ 20 - 0
templates.xml

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+  <data>
+    <template id="eiru_reports_rrhh_assets" inherit_id="eiru_assets.assets">
+      <xpath expr="." position="inside">
+        <link rel="stylesheet" href="/eiru_reports_rrhh/static/src/css/custom.css"/>
+
+        <!-- configuration < main > -->
+        <script type="text/javascript" src="/eiru_reports_baco/static/src/js/main.js"/>
+        <script type="text/javascript" src="/eiru_reports_baco/static/src/js/reporting_base.js"/>
+
+        <!-- ================================= BACO ========================================= -->
+
+        <!--============================= Ranking de productos ==============================-->
+        <script type="text/javascript" src="/eiru_reports_baco/static/src/js/reports/report_product_ranking_baco.js"/>
+
+      </xpath>
+    </template>
+  </data>
+</openerp>

+ 10 - 0
views/actions.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+	<data>
+
+		<record id="product_ranking_baco_action" model="ir.actions.client">
+			<field name="name">Ranking de productos por categoría</field>
+			<field name="tag">eiru_reports_baco.product_ranking_baco_action</field>
+		</record>
+	</data>
+</openerp>

+ 12 - 0
views/menus.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<openerp>
+  <data>
+    <!-- main menu name -->
+    <menuitem id="baco_parent_menu" name="BACO" parent="eiru_reports.eiru_reports_main_menu" sequence="10"/>
+
+    <!-- sub menu -->
+
+    <menuitem id="product_ranking_baco_menu" parent="baco_parent_menu" name="Ranking de productos por categoría" action="product_ranking_baco_action" sequence="1"/>
+
+  </data>
+</openerp>