diff --git a/inventory_stock_dashboard_odoo/README.rst b/inventory_stock_dashboard_odoo/README.rst new file mode 100644 index 000000000..2a95679f8 --- /dev/null +++ b/inventory_stock_dashboard_odoo/README.rst @@ -0,0 +1,47 @@ +.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg + :target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +Inventory Dashboard Odoo 17 +=========================== +Dashboard for Inventory module. + +Configuration +============= +No additional configurations needed. + +Company +------- +* `Cybrosys Techno Solutions `__ + +License +------- +Lesser General Public License, Version 3 (LGPL v3) +(https://www.gnu.org/licenses/lgpl-3.0-standalone.html) + +Credits +------- +* Developer : (V17) Aysha Shalin + Contact: odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/inventory_stock_dashboard_odoo/__init__.py b/inventory_stock_dashboard_odoo/__init__.py new file mode 100644 index 000000000..1e1910bf3 --- /dev/null +++ b/inventory_stock_dashboard_odoo/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import models diff --git a/inventory_stock_dashboard_odoo/__manifest__.py b/inventory_stock_dashboard_odoo/__manifest__.py new file mode 100644 index 000000000..c10397255 --- /dev/null +++ b/inventory_stock_dashboard_odoo/__manifest__.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +{ + 'name': 'Inventory Dashboard Odoo 17', + 'version': '17.0.1.0.0', + 'category': 'Warehouse', + 'summary': 'Detailed dashboard view for Inventory module.', + 'description': """This module presents a detailed dashboard view for the + Inventory module, delivering a compr ehensive and concise overview that + serves as a valuable tool for both inventory users and administrators.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['stock', 'base'], + 'data': [ + 'views/res_config_settings_views.xml', + 'views/dashboard_menu.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&display=swap', + 'inventory_stock_dashboard_odoo/static/src/css/dashboard.css', + 'inventory_stock_dashboard_odoo/static/src/js/dashboard.js', + 'inventory_stock_dashboard_odoo/static/src/xml/inventory_dashboard_template.xml', + 'https://cdn.jsdelivr.net/npm/chart.js', + ], + }, + 'images': ['static/description/banner.png'], + 'license': 'LGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/inventory_stock_dashboard_odoo/doc/RELEASE_NOTES.md b/inventory_stock_dashboard_odoo/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..3f5e95b60 --- /dev/null +++ b/inventory_stock_dashboard_odoo/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 29.01.2024 +#### Version 17.0.1.0.0 +##### ADD +- Initial commit for Inventory Dashboard Odoo 17 diff --git a/inventory_stock_dashboard_odoo/models/__init__.py b/inventory_stock_dashboard_odoo/models/__init__.py new file mode 100644 index 000000000..55c560524 --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from . import res_config_settings +from . import stock_move +from . import stock_move_line +from . import stock_picking +from . import stock_quant diff --git a/inventory_stock_dashboard_odoo/models/res_config_settings.py b/inventory_stock_dashboard_odoo/models/res_config_settings.py new file mode 100644 index 000000000..f6f817ba9 --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/res_config_settings.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + """ Inheriting res.config.settings model for adding custom fields in + settings.""" + _inherit = "res.config.settings" + + out_of_stock = fields.Boolean( + string='Out of stock', + config_parameter='inventory_stock_dashboard_odoo.out_of_stock', + help="Enable if out of stock") + out_of_stock_quantity = fields.Integer( + string='Quantity', + config_parameter='inventory_stock_dashboard_odoo.out_of_stock_quantity', + required=True, + help='Set the minimum quantity for considering a product as out of' + 'stock.') + dead_stock_bol = fields.Boolean( + string='Enable dead stock', + config_parameter='inventory_stock_dashboard_odoo.dead_stock_bol', + help='Enable if you want to consider dead stock.') + dead_stock = fields.Integer( + string='Dead stock', + config_parameter='inventory_stock_dashboard_odoo.dead_stock', + required=True, + help='Set the threshold quantity for considering a product as dead ' + 'stock.') + dead_stock_type = fields.Selection( + [('day', 'Day'), ('week', 'Week'), ('month', 'Month')], + string='Type', default='day', + config_parameter='inventory_stock_dashboard_odoo.dead_stock_type', + required=True, + help='Select the time period to determine dead stock based on product' + 'sales.') diff --git a/inventory_stock_dashboard_odoo/models/stock_move.py b/inventory_stock_dashboard_odoo/models/stock_move.py new file mode 100644 index 000000000..d4e4d22ea --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/stock_move.py @@ -0,0 +1,277 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import api, models + + +class StockMove(models.Model): + """ Extends 'stock.move' and provides methods for retrieving data for + dashboard.""" + _inherit = "stock.move" + + @api.model + def get_the_top_products(self): + """ Returns top ten products and done quantity.""" + company_id = self.env.company.id + query = '''select product_template.name,sum(product_uom_qty) from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + inner join product_product on stock_move.product_id = product_product.id + inner join product_template on product_template.id = product_product.product_tmpl_id + where stock_move.state = 'done' and stock_move.company_id=%s and stock_picking_type.code = 'outgoing' and + stock_move.create_date between (now() - interval '10 day') and now() + group by product_template.name ORDER BY sum DESC''' % company_id + self._cr.execute(query) + top_product = self._cr.dictfetchall() + total_quantity = [] + product_name = [] + for record in top_product[:10]: + total_quantity.append(record.get('sum')) + product_name.append(record.get('name')['en_US']) + value = {'products': product_name, 'count': total_quantity} + return value + + @api.model + def top_products_last_ten(self): + """ Returns top ten products and done quantity for last 10 days.""" + company_id = self.env.company.id + query = '''select product_template.name,sum(product_uom_qty) from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + inner join product_product on stock_move.product_id = product_product.id + inner join product_template on product_template.id = product_product.product_tmpl_id + where stock_move.state = 'done' and stock_move.company_id=%s and stock_picking_type.code = 'outgoing' and + stock_move.create_date between (now() - interval '10 day') and now() + group by product_template.name ORDER BY sum DESC''' % company_id + self._cr.execute(query) + top_product = self._cr.dictfetchall() + total_quantity = [] + product_name = [] + for record in top_product[:10]: + total_quantity.append(record.get('sum')) + product_name.append(record.get('name')['en_US']) + value = {'products': product_name, 'count': total_quantity} + return value + + @api.model + def top_products_last_thirty(self): + """ Returns top ten products and done quantity for last 30 days.""" + company_id = self.env.company.id + query = '''select product_template.name,sum(product_uom_qty) from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + inner join product_product on stock_move.product_id = product_product.id + inner join product_template on product_template.id = product_product.product_tmpl_id + where stock_move.state = 'done' and stock_move.company_id=%s and stock_picking_type.code = 'outgoing' + and stock_move.create_date between (now() - interval '30 day') and now() + group by product_template.name ORDER BY sum DESC''' % company_id + self._cr.execute(query) + top_product = self._cr.dictfetchall() + total_quantity = [] + product_name = [] + for record in top_product[:10]: + total_quantity.append(record.get('sum')) + product_name.append(record.get('name')['en_US']) + value = {'products': product_name, 'count': total_quantity} + return value + + @api.model + def top_products_last_three_months(self): + """ Returns top ten products and done quantity for last 3 months.""" + company_id = self.env.company.id + query = '''select product_template.name,sum(product_uom_qty) from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + inner join product_product on stock_move.product_id = product_product.id + inner join product_template on product_template.id = product_product.product_tmpl_id + where stock_move.state = 'done' and stock_move.company_id=%s and stock_picking_type.code ='outgoing' + and stock_move.create_date between (now() - interval '3 month') and now() + group by product_template.name ORDER BY sum DESC''' % company_id + self._cr.execute(query) + top_product = self._cr.dictfetchall() + total_quantity = [] + product_name = [] + for record in top_product[:10]: + total_quantity.append(record.get('sum')) + product_name.append(record.get('name')['en_US']) + value = {'products': product_name, 'count': total_quantity} + return value + + @api.model + def top_products_last_year(self): + """ Returns top ten products and done quantity for last year.""" + company_id = self.env.company.id + query = '''select product_template.name,sum(product_uom_qty) from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + inner join product_product on stock_move.product_id = product_product.id + inner join product_template on product_template.id = product_product.product_tmpl_id + where stock_move.state = 'done' and stock_move.company_id=%s and + stock_picking_type.code = 'outgoing' and + stock_move.create_date between (now() - interval '1 year') and now() + group by product_template.name ORDER BY sum DESC''' % company_id + self._cr.execute(query) + top_product = self._cr.dictfetchall() + total_quantity = [] + product_name = [] + for record in top_product[:10]: + total_quantity.append(record.get('sum')) + product_name.append(record.get('name')['en_US']) + value = {'products': product_name, 'count': total_quantity} + return value + + @api.model + def get_stock_moves(self): + """ Returns location name and quantity_done of stock moves graph""" + company_id = self.env.company.id + query = ('''select stock_location.complete_name, count(stock_move.id) from stock_move + inner join stock_location on stock_move.location_id = stock_location.id where stock_move.state = 'done' + and stock_move.company_id = %s group by stock_location.complete_name''' % company_id) + self._cr.execute(query) + stock_move = self._cr.dictfetchall() + count = [] + complete_name = [] + for record in stock_move: + count.append(record.get('count')) + complete_name.append(record.get('complete_name')) + value = {'name': complete_name, 'count': count} + return value + + @api.model + def stock_move_last_ten_days(self, post): + """ Returns location name and quantity_done of stock moves graph last + ten days.""" + company_id = self.env.company.id + query = ('''select stock_location.name,sum(stock_move_line.quantity) from stock_move_line + inner join stock_location on stock_move_line.location_id = stock_location.id + where stock_move_line.state = 'done' and stock_move_line.company_id = %s + and stock_move_line.create_date between (now() - interval '10 day') and now() + group by stock_location.name''' % company_id) + self._cr.execute(query) + location_quantity = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in location_quantity: + quantity_done.append(record.get('sum')) + name.append(record.get('name')) + value = {'name': name, 'count': quantity_done} + return value + + @api.model + def this_month(self, post): + """ Returns location name and quantity_done of stock moves graph for + current month.""" + company_id = self.env.company.id + query = ('''select stock_location.name,sum(stock_move_line.quantity) from stock_move_line + inner join stock_location on stock_move_line.location_id = stock_location.id + where stock_move_line.state = 'done' and stock_move_line.company_id = %s + and stock_move_line.create_date between (now() - interval '1 months') and now() + group by stock_location.name''' % company_id) + self._cr.execute(query) + location_quantity = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in location_quantity: + quantity_done.append(record.get('sum')) + name.append(record.get('name')) + value = {'name': name, 'count': quantity_done} + return value + + @api.model + def last_three_month(self, post): + """ Returns location name and quantity_done of stock moves graph for + last three months.""" + company_id = self.env.company.id + query = ('''select stock_location.name,sum(stock_move_line.quantity) from stock_move_line + inner join stock_location on stock_move_line.location_id = stock_location.id + where stock_move_line.state = 'done' and stock_move_line.company_id = %s + and stock_move_line.create_date between (now() - interval '3 months') and now() + group by stock_location.name''' % company_id) + self._cr.execute(query) + location_quantity = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in location_quantity: + quantity_done.append(record.get('sum')) + name.append(record.get('name')) + value = {'name': name, 'count': quantity_done} + return value + + @api.model + def last_year(self, post): + """ Returns location name and quantity_done of stock moves graph for + last year.""" + company_id = self.env.company.id + query = ('''select stock_location.name,sum(stock_move_line.quantity) from stock_move_line + inner join stock_location on stock_move_line.location_id = stock_location.id + where stock_move_line.state = 'done' and stock_move_line.company_id = %s + and stock_move_line.create_date between (now() - interval '12 months') and now() + group by stock_location.name''' % company_id) + self._cr.execute(query) + location_quantity = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in location_quantity: + quantity_done.append(record.get('sum')) + name.append(record.get('name')) + value = {'name': name, 'count': quantity_done} + return value + + @api.model + def get_dead_of_stock(self): + """ Returns product name and dead quantity of dead stock graph.""" + company_id = self.env.company.id + sett_dead_stock_bool = self.env['ir.config_parameter'].sudo(). \ + get_param("inventory_stock_dashboard_odoo.dead_stock_bol", default="") + sett_dead_stock_quantity = self.env['ir.config_parameter'].sudo().get_param( + "inventory_stock_dashboard_odoo.dead_stock", + default="") + sett_dead_stock_type = self.env['ir.config_parameter'].sudo().get_param( + "inventory_stock_dashboard_odoo.dead_stock_type", + default="") + if sett_dead_stock_bool == "True": + if sett_dead_stock_quantity: + out_stock_value = int(sett_dead_stock_quantity) + query = '''select product_product.id,stock_quant.quantity from product_product + inner join stock_quant on product_product.id = stock_quant.product_id + where stock_quant.company_id = %s and product_product.create_date not between (now() - interval '%s %s') + and now() and product_product.id NOT IN (select product_id from stock_move + inner join stock_picking on stock_move.picking_id = stock_picking.id + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + where stock_move.company_id = %s and stock_picking_type.code = 'outgoing' and + stock_move.state = 'done' and stock_move.create_date between (now() - interval '%s %s') and now() + group by product_id)''' % \ + (company_id, out_stock_value, sett_dead_stock_type, company_id, out_stock_value, + sett_dead_stock_type) + self._cr.execute(query) + result = self._cr.fetchall() + total_quantity = [] + product_name = [] + for record in result: + if record[1] > 0: + complete_name = self.env['product.product'].browse(record[0]).display_name + product_name.append(complete_name) + total_quantity.append(record[1]) + value = { + 'product_name': product_name, + 'total_quantity': total_quantity + } + return value diff --git a/inventory_stock_dashboard_odoo/models/stock_move_line.py b/inventory_stock_dashboard_odoo/models/stock_move_line.py new file mode 100644 index 000000000..cad413926 --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/stock_move_line.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import api, models + + +class StockMoveLine(models.Model): + """ Extends 'stock.move.line' and provides methods for retrieving data for + dashboard.""" + _inherit = "stock.move.line" + + @api.model + def get_product_moves(self): + """ Returns product move and quantity_done.""" + company_id = self.env.company.id + query = ('''select product_template.name,sum(stock_move_line.quantity) from stock_move_line + inner join product_product on stock_move_line.product_id = product_product.id + inner join product_template on product_product.product_tmpl_id = product_template.id + where stock_move_line.company_id = %s group by product_template.name''' % company_id) + self._cr.execute(query) + products_quantity = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in products_quantity: + quantity_done.append(record.get('sum')) + name.append(record.get('name')) + value = {'name': name, 'count': quantity_done} + category_query = '''select product_category.id,product_category.name from stock_move_line + inner join product_product on stock_move_line.product_id = product_product.id + inner join product_template on product_product.product_tmpl_id = product_template.id + inner join product_category on product_template.categ_id = product_category.id + where stock_move_line.company_id = %s and stock_move_line.state = 'done' + group by product_category.id''' % company_id + self._cr.execute(category_query) + category = self._cr.dictfetchall() + category_id = [] + category_name = [] + for record in category: + category_id.append(record.get('id')) + category_name.append(record.get('name')) + value1 = {'category_id': category_id, 'category_name': category_name} + return value, value1 + + @api.model + def product_move_by_category(self, option): + """ Returns category name and quantity_done.""" + category_id = int(option) + company_id = self.env.company.id + query = ('''select product_template.name,sum(stock_move_line.quantity) from stock_move_line + inner join product_product on stock_move_line.product_id = product_product.id + inner join product_template on product_product.product_tmpl_id = product_template.id + inner join product_category on product_template.categ_id = product_category.id + where stock_move_line.company_id = %s and product_category.id = %s group by product_template.name''' % + (company_id, category_id)) + self._cr.execute(query) + product_move = self._cr.dictfetchall() + quantity_done = [] + name = [] + for record in product_move: + quantity_done.append(record.get('sum')) + name.append(record.get('name')['en_US']) + value = { + 'name': name, + 'count': quantity_done, + } + return value diff --git a/inventory_stock_dashboard_odoo/models/stock_picking.py b/inventory_stock_dashboard_odoo/models/stock_picking.py new file mode 100644 index 000000000..6f1e8fd7a --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/stock_picking.py @@ -0,0 +1,101 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import api, models + + +class StockPicking(models.Model): + """ Extends 'stock.picking' for retrieving stock picking data for + dashboard.""" + _inherit = "stock.picking" + + @api.model + def get_operation_types(self): + """ Method for retrieving operation type tiles, operation type graph and + returns operation type details. + no_transfer - count of transfers for each operation type, + late - count of late occurrences for each operation type, + waiting - count of waiting operations for each operation type, + operation_type_name - retrieve all operation type names, + backorder - count the number of backorders for each operation type. + """ + no_transfer = {} + stock_picking_type = self.env['stock.picking.type'].search([]) + stock_picking = self.env['stock.picking'].search([]) + stock = [] + length = [] + names = [] + late = {} + query = '''select stock_picking.picking_type_id, count(stock_picking.picking_type_id) from stock_picking + inner join stock_picking_type on stock_picking.picking_type_id = stock_picking_type.id + where stock_picking.company_id = %s and + stock_picking.state in ('assigned', 'waiting', 'confirmed') and (has_deadline_issue = true or + date_deadline <= now() or scheduled_date <= now()) + group by stock_picking.picking_type_id''' % self.env.company.id + self._cr.execute(query) + lates = self._cr.dictfetchall() + for rec in lates: + late.update({rec.get('picking_type_id'): rec.get('count')}) + waiting = {} + backorder = {} + operation_type_name = {} + for i in stock_picking_type: + names.append(i.name) + orders = stock_picking.filtered(lambda r: r.picking_type_id.id == i.id) + stock.append(len(orders)) + length_stock_picking = len(orders) + length.append(len(stock_picking.filtered(lambda r: r.picking_type_id.id == i.id))) + no_transfer.update({i.id: length_stock_picking}) + operation_type_name.update({i.id: i.name}) + if len(orders) > 0: + if len(orders.filtered(lambda r: r.state == 'confirmed')) > 0: + waiting.update({i.id: len(orders.filtered(lambda r: r.state == 'confirmed'))}) + if len(orders.mapped('backorder_id')) > 0: + backorder.update({i.id: len(orders.mapped('backorder_id'))}) + return no_transfer, late, waiting, operation_type_name, backorder + + @api.model + def get_product_category(self): + """ Return product categories and categories that have on-hand product + quantities.""" + category_ids = self.env['product.category'].search([]) + category_name = [] + product_count = [] + for rec in category_ids: + name = rec.name + category_name.append(name) + count = rec.product_count + product_count.append(count) + value = {'name': category_name, 'count': product_count} + return value + + @api.model + def get_locations(self): + """ Returns locations and locations that have on-hand product + quantities.""" + stock_quant_ids = self.env['stock.quant'].search([]) + locations = stock_quant_ids.mapped('location_id') + value = {} + for rec in locations: + loc_stock_quant = stock_quant_ids.filtered(lambda x: x.location_id == rec) + on_hand_quantity = sum(loc_stock_quant.mapped('inventory_quantity_auto_apply')) + value[rec.name] = on_hand_quantity + return value diff --git a/inventory_stock_dashboard_odoo/models/stock_quant.py b/inventory_stock_dashboard_odoo/models/stock_quant.py new file mode 100644 index 000000000..90d0665ca --- /dev/null +++ b/inventory_stock_dashboard_odoo/models/stock_quant.py @@ -0,0 +1,59 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Aysha Shalin (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################### +from odoo import api, models + + +class StockQuant(models.Model): + """ Extends 'stock.quant' for retrieving data of products that are out of + stock.""" + _inherit = "stock.quant" + + @api.model + def get_out_of_stock(self): + """ Returns products and quantities that are out of stock.""" + company_id = self.env.company.id + sett_out_stock_bool = self.env['ir.config_parameter'].sudo(). \ + get_param("inventory_stock_dashboard_odoo.out_of_stock", default="") + sett_out_stock_quantity = self.env['ir.config_parameter'].sudo().\ + get_param("inventory_stock_dashboard_odoo.out_of_stock_quantity", default="") + if sett_out_stock_bool == "True": + if sett_out_stock_quantity: + out_stock_value = int(sett_out_stock_quantity) + query = '''select product_product.id,sum(stock_quant.quantity) from product_product + inner join stock_quant on product_product.id = stock_quant.product_id + where stock_quant.quantity < %s and stock_quant.company_id = %s group by product_product.id''' \ + % (out_stock_value, company_id) + self._cr.execute(query) + result = self._cr.fetchall() + total_quantity = [] + product_name = [] + for record in result: + total_quantity.append(record[1]) + for record in result: + complete_name = self.env['product.product'].browse( + record[0]).display_name + product_name.append(complete_name) + value = { + 'product_name': product_name, + 'total_quantity': total_quantity + } + return value diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/capture (1).png b/inventory_stock_dashboard_odoo/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/capture (1).png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/check.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/check.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/chevron.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/chevron.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/cogs.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/cogs.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/consultation.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/consultation.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/ecom-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/ecom-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/education-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/education-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/hotel-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/hotel-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/img.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/img.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/license.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/license.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/lifebuoy.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/lifebuoy.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/manufacturing-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/manufacturing-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/photo-capture.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/photo-capture.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/pos-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/pos-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/puzzle.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/puzzle.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/restaurant-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/restaurant-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/service-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/service-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/trading-black.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/trading-black.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/training.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/training.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/update.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/update.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/user.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/user.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/icons/wrench.png b/inventory_stock_dashboard_odoo/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/icons/wrench.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/Cybrosys R.png b/inventory_stock_dashboard_odoo/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/misc/Cybrosys R.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/email.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/phone.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/star (1) 2.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/support (1) 1.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/support-email.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/tick-mark.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp 1.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp.svg b/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_barcode_scanning.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_barcode_scanning.png new file mode 100644 index 000000000..33d248669 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_barcode_scanning.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_report_generator.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_report_generator.png new file mode 100644 index 000000000..6125017d4 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/inventory_report_generator.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/odoo_product_tags.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/odoo_product_tags.png new file mode 100644 index 000000000..b73a9af74 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/odoo_product_tags.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/product_barcode.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/product_barcode.png new file mode 100644 index 000000000..40fba50af Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/product_barcode.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/product_batch_report.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/product_batch_report.png new file mode 100644 index 000000000..55fa8c462 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/product_batch_report.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/modules/quality_assurance.png b/inventory_stock_dashboard_odoo/static/description/assets/modules/quality_assurance.png new file mode 100644 index 000000000..489d1fe06 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/modules/quality_assurance.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/1.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..b6a4e9d01 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/1.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/10.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..502efd7bd Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/10.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/11.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..8f02ad75d Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/11.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/12.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..b5f349b5b Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/12.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/13.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..9459ddbcf Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/13.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/14.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..6796e06d2 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/14.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/2.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..283ce9253 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/2.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/3.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..9c0c7386d Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/3.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/4.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..89e8ac335 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/4.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/5.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..069289bc5 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/5.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/6.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..d02d7f290 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/6.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/7.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..b06853173 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/7.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/8.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..2aa559e09 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/8.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/9.png b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..96965210e Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/9.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/assets/screenshots/hero.gif b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..0b80e3f56 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/assets/screenshots/hero.gif differ diff --git a/inventory_stock_dashboard_odoo/static/description/banner.png b/inventory_stock_dashboard_odoo/static/description/banner.png new file mode 100644 index 000000000..5004b22f7 Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/banner.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/icon.png b/inventory_stock_dashboard_odoo/static/description/icon.png new file mode 100644 index 000000000..dd0ce43de Binary files /dev/null and b/inventory_stock_dashboard_odoo/static/description/icon.png differ diff --git a/inventory_stock_dashboard_odoo/static/description/index.html b/inventory_stock_dashboard_odoo/static/description/index.html new file mode 100644 index 000000000..302a69824 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/description/index.html @@ -0,0 +1,756 @@ + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+
+
+
+

+ Inventory Dashboard Odoo 17

+

+ Detailed Dashboard View for Inventory. +

+
+ +
+
+
+
+
+

Key Highlights +

+
+
+
+
+
+ +
+
+

Detailed View for Users and Admins.

+

This Module Offers a Comprehensive and Concise Overview of the Inventory Module at a Glance, Serving as a Valuable Tool for Both Inventory Users and Administrators. +

+
+
+
+
+
+
+ +
+
+

Simplifies the Analysis of the Inventory Module's Functions.

+

Allows to Easily Analyse the Functionalities of Inventory Module Using this Dashboard. +

+
+
+
+
+
+
+ +
+
+

Dashboard Tiles and Various Graphs.

+

Dynamic and clickable Dashboard Tiles, Along with a Variety of Graphs, Greatly Enhance the Analysis Process. +

+
+
+
+
+
+
+ +
+
+

Filtering Options for Specific Graphs.

+

Graphs can be Filtered According to Various Time Periods. +

+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ Dynamic And Clickable Dashboard Tiles.

+

User can Click the Tiles and States in Tile,That shows the Detailed View of Corresponding Operation Type. +

+
+
+
+
+
+
+ +
+
+

+ Different Types of Graphs.

+

Inventory Dashboard have Different Types of Graphs that will Give You Complete Analysis of the Inventory Module. +

+
+
+
+
+
+
+ +
+
+

+ Show Details Button on the Inventory Dashboard Provides Detailed Information About that Specific Graph.

+

The Inventory Dashboard Features a Variety of Graphs that Provide a Comprehensive Analysis of the Inventory Module. +

+
+
+
+
+
+
+ +
+
+

+ Filtering Options for Specific Graphs.

+

Graphs can be Filtered According to Various Time Periods. +

+
+
+
+
+
+
+ +
+
+

+ 1. Top Moving Products.

+

The Ten Best-selling Products Along with their Corresponding Sales Counts. +

+
+
+
+
+
+
+ +
+
+

+ 2. Product Categories.

+

Displaying Product Categories and the Product Count Within their Respective Categories. +

+
+
+
+
+
+
+ +
+
+

+ 3. Product Moves By Category.

+

+ This Graph Shows the Product Move Report and is Filtered Based on the Available Categories in the Product Movement. +

+
+
+
+
+
+
+ +
+
+

+ 4. Stock Moves By Location.

+

+ Displays the Stock Move Report by Location and the Corresponding Stock Move Count. +

+
+
+
+
+
+
+ +
+
+

+ 5. Operation Types.

+

+ Operation Types and their Corresponding Transfer Counts. +

+
+
+
+
+
+
+ +
+
+

+ 6. Dead Stock.

+

Dead Stocks Refer to Unsold Inventory Items that have been Stored in Your Warehouse or Store for a Specified Period. + Users can Configure the Dead Stock Duration in the Settings, and there is an Option to Enable or Disable it. Users can Also Enable or Disable the Graph in the Settings. +

+
+
+
+
+
+
+ +
+
+

+ The Graph Displays Dead Stock Products and their Current Stock.

+
+
+
+
+
+
+ +
+
+

+ 7. Out of Stock Products.

+

Displaying Out-of-stock Products. Users can Configure the Out-of-stock Products Using Inventory Settings, + and there is an Option for Enabling and Disabling this Feature. Users can Also Enable or Disable the Graph in the Settings. +

+
+
+
+
+
+
+ +
+
+

+ The Graph Displays Out-of-stock Products and Their Current Stock.

+
+
+
+
+
+
+ +
+
+

+ 8. Locations Table.

+

Locations and Their Corresponding On-hand Quantities. +

+
+
+
+
+
+
+
    +
  • + Detailed View for Users and Admins. +
  • +
  • + Dynamic And Clickable Tiles and Various Graphs. +
  • +
  • + Show Details Button for Graphs. +
  • +
  • + Dead Stock and Out of Stock Graph. +
      +
    • Users have the Option to Enable or Disable the Dead Stock and Out of Stock Graphs.
    • +
    +
  • +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on:06th November 2023 +
+

+ Initial Commit for Inventory Dashboard Odoo 17.

+
+
+
+
+
+
+
+

Related Products

+
+
+ +
+
+

Our Services

+
+
+
+
+
+
+
+
+ service-icon +
+
+

Odoo Customization

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Support

+
+
+
+
+
+
+ service-icon +
+
+

Hire Odoo Developer

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Integration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Migration

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Consultancy

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Implementation

+
+
+
+
+
+
+ service-icon +
+
+

Odoo Licensing Consultancy

+
+
+
+
+
+
+

Our Industries

+
+
+
+
+
+
+ +

Trading

+

Easily procure and sell your products

+
+
+
+
+ +

POS

+

Easy configuration and convivial experience

+
+
+
+
+ +

Education

+

A platform for educational management

+
+
+
+
+ +

Manufacturing

+

Plan, track and schedule your operations

+
+
+
+
+ +

E-commerce & Website

+

Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +

Service Management

+

Keep track of services and invoice

+
+
+
+
+ +

Restaurant

+

Run your bar or restaurant methodically

+
+
+
+
+ +

Hotel Management

+

An all-inclusive hotel management application

+
+
+
+
+
+
+

Support

+
+
+
+
+
+
+
+ +
+ Need + Help? +

Got questions or need help? Get in touch.

+
odoo@cybrosys.com +
+
+
+
+
+
+
+
+ +
+ WhatsApp +

Say hi to us on WhatsApp!

+
+91 + 99456767686
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/inventory_stock_dashboard_odoo/static/src/css/dashboard.css b/inventory_stock_dashboard_odoo/static/src/css/dashboard.css new file mode 100644 index 000000000..4b0ad4843 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/src/css/dashboard.css @@ -0,0 +1,880 @@ +.item-container { + background-image: -webkit-linear-gradient(white, white); +} +.item-header { + padding-left: 30px; + background-image: -webkit-linear-gradient(white, #c3c9d4); +} +.col-sm, +.col-sm-1, +.col-sm-10, +.col-sm-11, +.col-sm-12, +.col-sm-2, +.col-sm-3, +.col-sm-4, +.col-xl-auto { + position: relative; + padding-right: 7.5px; + padding-left: 7.5px; +} +#location_table thead th { + border-bottom: none; + background-color: #67b7dc; + color: white; + width: 250px; + height: 30px; +} +.accounts-dashboard-wrap .card-header { + background-color: + transparent; + border-bottom: 1px solid rgba(0, 0, 0, .125); + padding: .75rem 1.25rem; + position: relative; + border-top-left-radius: .25rem; + border-top-right-radius: .25rem; +} +.accounts-dashboard-wrap .fa:hover { + -ms-transform: scale(1.5); + /* IE 9 */ + -webkit-transform: scale(1.5); + /* Safari 3-8 */ + transform: scale(1.5); +} +.accounts-dashboard-wrap .card-header>.card-tools { + float: right; + margin-right: -.625rem; +} +.right { + float: left; +} +.accounts-dashboard-wrap .tooltip:hover .tooltiptext { + visibility: visible; +} +.accounts-dashboard-wrap .col-6 { + -ms-flex: 0 0 50%; + flex: 0 0 50%; + max-width: 50%; +} +.accounts-dashboard-wrap .fa-cog { + content: "\f013" +} +.accounts-dashboard-wrap .fa, +.accounts-dashboard-wrap .fas { + font-weight: 900; +} +.accounts-dashboard-wrap .fa, +.accounts-dashboard-wrap .fab, +.accounts-dashboard-wrap .fad, +.accounts-dashboard-wrap .fal, +.accounts-dashboard-wrap .far, +.accounts-dashboard-wrap .fas { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + display: inline-block; + font-style: normal; + font-variant: normal; + text-rendering: auto; + line-height: 1; +} +.accounts-dashboard-wrap .info-box .info-box-icon { + + border-radius: .25rem; + -ms-flex-align: center; + align-items: center; + display: -ms-flexbox; + display: flex; + font-size: 1.875rem; + -ms-flex-pack: center; + justify-content: center; + text-align: center; + width: 70px; +} +.accounts-dashboard-wrap .info-box { + box-shadow: 0 0 1px rgba(0, 0, 0, .125), 0 1px 3px rgba(0, 0, 0, .2); + border-radius: .25rem; + background: #fff; + display: -ms-flexbox; + display: flex; + margin-bottom: 1rem; + min-height: 80px; + padding: .5rem; + position: relative; +} +.accounts-dashboard-wrap .o_datepicker .o_datepicker_input { + width: 100%; + cursor: pointer; +} +.accounts-dashboard-wrap #overdue { + width: 100%; + cursor: pointer; +} +.accounts-dashboard-wrap .o_input { + border: 1px solid #cfcfcf; + border-top-style: none; + border-right-style: none; + border-left-style: none; +} +.accounts-dashboard-wrap .in_graph { + padding-left: 90px; + height: auto; + padding-bottom: 65px; + text-align: center !important; +} +.accounts-dashboard-wrap .oh_dashboards { + padding-top: 15px; + background-color: #f8faff !important; +} +.accounts-dashboard-wrap .container-fluid.o_in_dashboard { + padding: 0px !important; +} +.accounts-dashboard-wrap .o_action_manager { + overflow-y: scroll !important; + max-width: 100%; +} +// new tile + +.oh_dashboards { + background-color: #ececec; +} +.accounts-dashboard-wrap .container { + margin: 50px 0 0 100px; +} +.accounts-dashboard-wrap .o_dashboards { + color: #2a2a2a; + background-color: #f2f2f2 !important; +} +.accounts-dashboard-wrap .dash-header { + margin: 15px 0px 12px 0 !important; + display: block; + padding: 7px 25px 7px 0; + color: #0e1319; + font-size: 2rem; + font-weight: 400; + background-color: + rgba(255, 255, 255, 0.9) !important; + color: #212529; + padding: 1.5rem; + border-radius: 3px; + box-shadow: 0 0px 10px 0px rgba(0, 0, 0, 0.05) !important; + display: flex; + justify-content: space-between; + align-items: center; +} +.accounts-dashboard-wrap .dashboard-h1 { + display: block; + padding: 7px 25px 7px 0; + color: #0e1319; + font-size: 2rem; + font-weight: 400; + color: #212529; + float: left; + margin-bottom: 0; +} +.accounts-dashboard-wrap .card { + position: relative !important; + border-top: 0 !important; + margin-bottom: 30px !important; + width: 100% !important; + background-color: #ffffff !important; + border-radius: 0.25rem !important; + padding: 0px !important; + -webkit-transition: .5s !important; + transition: .5s !important; + display: -ms-flexbox !important; + display: flex !important; + -ms-flex-direction: column !important; + flex-direction: column !important; + box-shadow: 0 0px 10px 0px rgba(0, 0, 0, 0.05) !important; + border-radius: 0.25rem; +} +.accounts-dashboard-wrap .card-header { + border: 0; + padding: 0; +} +.accounts-dashboard-wrap .card-header>.card-tools { + float: right; + margin-right: 0.375rem; + margin-top: 5px; + margin-bottom: 10px; +} +.accounts-dashboard-wrap .card-header i.fa { + font-size: 1.3rem; + display: inline-block; + padding: 0 0px; + margin: 0 0px; + color: #57769c; + opacity: .8; + -webkit-transition: 0.3s linear; + transition: 0.3s linear; +} +.accounts-dashboard-wrap .dropdown-toggle::after { + display: inline-block; + margin-left: 0.255em; + vertical-align: 0.255em; + content: ""; + border-top: 0.3em solid; + border-right: 0.3em solid transparent; + border-bottom: 0; + border-left: 0.3em solid transparent; + color: #7891af; +} +.accounts-dashboard-wrap .account-details { + display: flex; +} +.main-title { + color: #a3a3a3; + display: block; + margin-bottom: 5px; + font-size: 20px; + font-weight: 400; +} +.accounts-dashboard-wrap .main-title { + display: block; + margin-bottom: 5px; + font-size: 13px; + font-weight: 600; + color: #fff !important; + text-transform: uppercase; + padding: 1rem; + border-radius: 5px; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.accounts-dashboard-wrap .card-body { + background-color: rgba(255, 255, 255, 0.9) !important; + color: #212529; + padding-top: 0; +} +.accounts-dashboard-wrap .tile.wide.invoice { + margin-bottom: 27px; + -webkit-box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0.05); + box-shadow: 1px 5px 24px 0 rgba(68, 102, 242, 0); + background-color: #ffffff; + border-radius: 5px; + position: relative; + width: 100%; + padding: 0rem 0rem; + border: 1px solid rgba(0, 0, 0, 0.07); + height: 140px; +} +.accounts-dashboard-wrap .box-1 .main-title { + background: #67b7dc; + color: #fff; +} +.accounts-dashboard-wrap .box-2 .main-title { + background: #6794dc !important; + color: #fff; +} +.accounts-dashboard-wrap .box-3 .main-title { + background: #8067dc; + color: #fff; +} +.accounts-dashboard-wrap .box-4 .main-title { + background: #c767dc; + color: #fff; +} +.accounts-dashboard-wrap .count { + margin-bottom: 1rem; +} +.accounts-dashboard-wrap .main-title~div { + display: flex; + justify-content: space-between; + margin-top: 1rem; + padding: 1rem; + background: #fff; +} +#location_table { + background-color: #fff; + color: (--mauve); +} +#tile_main_div:hover { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + animation-name: example; + animation-duration: 0.25s; + border-left: 8px solid var(--mauve); + box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); +} +#tiles:hover { + border-top-left-radius: 10px; + border-bottom-left-radius: 10px; + animation-name: example; + animation-duration: 0.25s; + box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22); +} +.details_table{ +align-content: left; +} +.graph_details_table{ + position: absolute; + top: 45px; + right: 15px; + background-color: white; + border-collapse: collapse; + border: 1px solid #ddd; +} +.graph_details_table th{ + background-color:#67b7dc; + color: white; + width: 250px; + height: 30px; +} +.graph_details_table td{ +border: 1px solid #ddd; +height: 20px; +} +.graph_details_table tr:nth-child(even){background-color: #f2f2f2;} +.graph_details_table tr:hover {background-color: #ddd;} +#product_moves_selection{ + position: relative; + right: 10px; + top: 5px; +} +:root { + /* Primary */ + --mauve: #7D7EAF; + --pink-dark: #BD85BA; + --pink: #F78EAD; + --peach: #FFA48E; + --orange: #FFCA71; + --gold: #CEA716; + --green: #1EC198; + --grey: #a0a0a0; + /* Light */ + --mauve-light: #e5e5ef; + --pink-dark-light: #f2e7f1; + --pink-light: #fde8ef; + --peach-light: #ffede8; + --orange-light: #fff4e3; + --gold-light: #faf6e8; + --green-light: #e9f9f5; + --grey-light: #e0e0e0; + + /*Lighter*/ + --grey-lighter: #fafafa; + --grey-dark-lighter: #f3f3f3; +} +/* Background */ +.bg-mauve-light { + background-color: var(--mauve-light); +} +.bg-pink-dark-light { + background-color: var(--pink-dark-light); +} +.bg-pink-light { + background-color: var(--pink-light); +} +.bg-peach-light { + background-color: var(--peach-light); +} +.bg-orange-light { + background-color: var(--orange-light); +} +.bg-gold-light { + background-color: var(--gold-light); +} +.bg-green-light { + background-color: var(--green-light); +} +/* Text */ +.text-mauve { + color: var(--mauve); +} +.text-pink-dark { + color: var(--pink-dark); +} +.text-pink { + color: var(--pink); +} +.text-peach { + color: var(--peach); +} +.text-orange { + color: var(--orange); +} +.text-gold { + color: var(--gold); +} +.text-green { + color: var(--green); +} +/* Cards */ +/*.dashboard-card { + border-radius: 0.3rem; + display: flex; + justify-content: center; + padding: 1.7rem 1.5rem 1.5rem 1.5rem; + margin: 1rem auto; + height: 90px; +}*/ +.dashboard-card__icon-container { + height: 50px; + width: 50px; + border-radius: 50%; +} +.dashboard-card__icon-container i { + font-size: 20px; +} +.dashboard-card__details { + margin-left: 0rem !important; + max-width: 120px; +} +.dashboard-card__details h3 { + font-weight: 700; + font-size: 1.5rem; +} +.dashboard-card__details h4 { + font-weight: 700; + font-size: 0.7rem; + color: var(--grey); + margin-top: -5px; +} +h2.section-header { + font-weight: 700; + font-size: 1.5rem; +} +.chart-container { + border-radius: 0.3rem; + padding: 1rem; + margin: 1rem auto; +} +.chart-container.card-shadow { + height: 100%; +} +.half_chart.chart-container.card-shadow { + height: 49%; +} +.chart-container h2 { + font-weight: 700; + font-size: 1.125rem; +} +.item-container { + background-color: var(--grey-lighter); + border-radius: 0.3rem; + padding: 1.2rem 1rem; + margin: 1rem auto; +} +.item-container:hover { + background-color: var(--grey-dark-lighter); + transition: all 0.3s ease-in-out; + cursor: pointer; +} +.count-container { + font-weight: 700; + font-size: 2rem; + background-color: var(--mauve-light); + color: var(--mauve); + height: 60px !important; + width: 60px !important; + border-radius: 50%; + display: -webkit-box; + display: -webkit-flex; + display: flex; + justify-content: center; + align-items: center; +} +.item-header { + display: flex; + align-items: flex-start; +} +.item-title h3 { + font-size: 1.3rem; + font-weight: 700; +} +.item-content ul { + list-style: none; + padding-left: 0px; +} +.item-content ul>li { + font-size: 0.9rem; + color: var(--grey); + font-weight: 700; +} +/* Misc */ +.card-shadow { + -webkit-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + -moz-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); +} +.table td, +.table th { + border-top: 1px solid #eceff2; +} +.crm_scroll_table { + max-height: 395px; + overflow-y: auto; +} +.recent_activity_div .crm_scroll_table { + max-height: 435px; +} +.crm_scroll_table thead { + position: sticky; + top: 0; +} +.dashboard-card__stat_late:hover { + border-bottom-color: darkgray; +} +.dashboard-card__stat_waiting:hover { + border-bottom-color: darkgray; +} +.dashboard-card__stat_backorder:hover { + border-bottom-color: darkgray; +} + +.location_value { + font-weight: 700; + font-size: 1.2rem; + text-align: center; +} +.btn__custom-info{ + background-color: #e9ebf6; + color: #070920; + border-radius: 1.5rem; + padding: 0.15rem 2rem; + border: 1px solid #dddfea; + position: absolute; + right: 0px; + top: -3px; +} +.btn__custom-info:hover{ + background-color: #dddfea; + border: 1px solid #d2d4dd; + transition: all 0.3s ease-in-out; +} +.btn_info { + position: absolute; + top: 18px; + right: 17px; + background-color: #e9ebf6; + height: 28px; + width: 26px; + border-radius: 24%; +} +#top_product_selection { + position: absolute; + top: 18px; +} +#top_product_button { + position: absolute; + top: 1px; + right: 63px; +} +#product_move_select { + position: absolute; + top: 1px; + right: 63px; +} +#product_move_selection { + position: absolute; + top: 18px; +} +#stock_move_select { + position: absolute; + top: 1px; + right: 63px; +} +#stock_moves_selection { + position: absolute; + top: 18px; +} +.location_table_value{ +text-align: center; +} +/* X-Small devices (portrait phones, less than 576px)*/ +@media (max-width: 575.98px) { + #top_product_selection{ + width: 180%; + margin-left: -100%; + } + #product_move_selection{ + width: 180%; + margin-left: -100%; + } + #stock_move_selection{ + width: 180%; + margin-left: -100%; + } +} +.tile-container { + padding: 1rem; + display: flex; + justify-content: space-around; + align-items: center; + flex-wrap: wrap; + border-radius: 0.5rem; + background-image: url('./assets/background.png'); + background-repeat: no-repeat; + background-size: cover; + background-position-x: right; +} +.tile-container:hover { + opacity: 0.9; + cursor: pointer; + transition: all 0.4s ease-in-out; +} +.title-container__icon-container { + padding: 1.3rem 1rem; + border-radius: 0.5rem; +} +.title-container__icon { + font-size: 1.5rem; +} +.title-container__count { + font-size: 1.6rem; + font-weight: 600; +} +.title-container__title { + font-size: 1rem; + font-weight: 600; +} +/* Colors */ +.red-bkg { + background-color: #FFE1E2; +} +.red-font { + color: #E8565E; +} +.blue-bkg { + background-color: #C2D5FF; +} +.blue-font { + color: #225AE3; +} +.green-bkg { + background-color: #BDE4E0; +} +.green-font { + color: #2CA79A; +} +.pink-bkg { + background-color: #FFE4EF; +} +.pink-font { + color: #CE3372; +} +.yellow-bkg { + background-color: #F9ECC6; +} +.yellow-font { + color: #CBA846; +} +.white-bkg { + background-color: #FFFFFF; +} +.white-font { + color: #FFFFFF; +} +/*Second Section CSS */ +.card-contianer__header { + background-color: #37274B; + padding: 0.4rem; + border-top: 3px solid #D84315; +} +.card-container__content { + padding: 1rem; +} +.card-container__normal-header { + font-size: 1.2rem; + color: #000000; + font-weight: 600; +} +.card-container__header-text { + font-size: 1.2rem; + color: #FFFFFF; + font-weight: 400; + text-align: center; +} +.dashboard-card { + box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 2px 6px 2px; + padding: 1.5rem; + margin: 0.5rem auto; + min-height: 185px; + display: flex !important; + justify-content: space-between !important; + align-items: flex-start !important; +} +.dashboard-card--border-top { + border-top: 5px solid #000; +} +.dashboard-card--border-top-red { + border-color: #D32F2F; +} +.dashboard-card--border-top-blue { + border-color: #2962FF; +} +.dashboard-card--border-top-green { + border-color: #00FF00; +} +.dashboard-card--border-top-purple { + border-color: #800080; +} +.dashboard-card--border-top-brown { + border-color: #964B00; +} +.dashboard-card--border-top-pink { + border-color: #FFC0CB; +} +.dashboard-card--border-top-grey { + border-color: #696969; +} +.dashboard-card--border-top-black { + border-color: black; +} +.dashboard-card--border-top-rebecca { + border-color: #663399; +} +.dashboard-card--border-top-steel { + border-color: #607D8B; +} +.dashboard-card--border-top-orange { + border-color: #FFA500; +} +.dashboard-card__title { + font-weight: bold; + display: block; + margin-top: 0.5rem; +} +.dashboard-card__count { + font-size: 3rem; +} +.dashboard-card__stats { + list-style: none; + padding-left: 0; + width: 50% +} +.dashboard-card__stat_late { + padding: 0.5rem 0rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} +.dashboard-card__stat_waiting { + padding: 0.5rem 0rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} +.dashboard-card__stat_backorder { + padding: 0.5rem 0rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); +} +.dashboard-card__stat-count_late { + min-width: 25px; + min-height: 20px; + background-color: rgba(0, 0, 0, 0.1); + font-size: 0.9rem; + font-weight: bold; + color: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; +} +.dashboard-card__stat-count_waiting { + min-width: 25px; + min-height: 20px; + background-color: rgba(0, 0, 0, 0.1); + font-size: 0.9rem; + font-weight: bold; + color: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; +} +.dashboard-card__stat-count_backorder { + min-width: 25px; + min-height: 20px; + background-color: rgba(0, 0, 0, 0.1); + font-size: 0.9rem; + font-weight: bold; + color: rgba(0, 0, 0, 0.6); + display: flex; + justify-content: center; + align-items: center; + border-radius: 50%; +} +.oh_dashboards { +font-family: 'Raleway', sans-serif; +} +.main-part{ +width:100%; +margin:0 auto; +text-align: center; +padding: 0px 5px; +} +.cpanel{ +width:32%; +display: inline-block; +background-color:#34495E; +color:#fff; +margin-top: 50px; +} +.icon-part i{ +font-size: 30px; +padding:10px; +border:1px solid #fff; +border-radius:50%; +margin-top:-25px; +margin-bottom: 10px; +background-color:#34495E; +} +.icon-part p{ +margin:0px; +font-size: 20px; +padding-bottom: 10px; +} +.card-content-part{ +background-color: #2F4254; +padding: 5px 0px; +} +.cpanel .card-content-part:hover{ +background-color: #5a5a5a; +cursor: pointer; +} +.card-content-part a{ +color:#fff; +text-decoration: none; +} +.cpanel-green .icon-part,.cpanel-green .icon-part i{ +background-color: #16A085; +} +.cpanel-green .card-content-part{ +background-color: #149077; +} +.cpanel-orange .icon-part,.cpanel-orange .icon-part i{ +background-color: #F39C12; +} +.cpanel-orange .card-content-part{ +background-color: #DA8C10; +} +.cpanel-blue .icon-part,.cpanel-blue .icon-part i{ +background-color: #2980B9; +} +.cpanel-blue .card-content-part{ +background-color:#2573A6; +} +.cpanel-red .icon-part,.cpanel-red .icon-part i{ +background-color:#E74C3C; +} +.cpanel-red .card-content-part{ +background-color:#CF4436; +} +.cpanel-skyblue .icon-part,.cpanel-skyblue .icon-part i{ +background-color:#8E44AD; +} +.cpanel-skyblue .card-content-part{ +background-color:#803D9B; +} +.icon-part{ + height: 121px; + background-color: #16A085; + min-height: 120px; +} +.col-lg-3:hover { + -ms-transform: scale(1); + /* IE 9 */ + -webkit-transform: scale(1); + /* Safari 3-8 */ + transform: scale(1.05); +} +option { + color: black; +} diff --git a/inventory_stock_dashboard_odoo/static/src/js/dashboard.js b/inventory_stock_dashboard_odoo/static/src/js/dashboard.js new file mode 100644 index 000000000..f08b1f422 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/src/js/dashboard.js @@ -0,0 +1,1087 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { onWillStart, onMounted, useState, useRef } from "@odoo/owl"; +import { useService } from "@web/core/utils/hooks"; +const actionRegistry = registry.category("actions"); +import { _t } from "@web/core/l10n/translation"; +const { DateTime } = luxon; +var op_type; +/* This class represents dashboard in Inventory. */ +class Dashboard extends owl.Component{ + setup() { + this.orm = useService('orm') + this.rootRef = useRef('root') + this.state = useState({ + countDictionary : [], + op_types: [], + operations: [], + colors: [], + late_status: [], + waiting_status: [], + backorder_status: [], + MoveData: [], + operationDict: [], + category: [], + categCountDict: [], + categName: [], + location_data: [], + monthly_stock: [], + monthly_stock_count: [], + out_stock: [], + out_stock_count: [], + dead_stock_name: [], + }); + // When the component is about to start, fetch data in tiles + onWillStart(async () => { + this.props.title = 'Dashboard'; + }); + // When the component is mounted, render various charts + onMounted(async () => { + await this.render_graphs(); + }); + } + render_graphs(){ + this.render_operation_tile(); + this.render_top_product_bar_graph(); + this.render_stock_moves(); + this.render_product_move_graph_this_month(); + this.render_product_category(); + this.render_storage_location(); + this.render_out_of_stock_graph(); + this.render_dead_of_stock_graph(); + } + // Fetch data operation type tiles and graphs + render_operation_tile(){ + let chartStatus = Chart.getChart('operation'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + var result = this.orm.call('stock.picking', 'get_operation_types', [] + ).then( (result) => { + op_type = result[3]; + const colors = ["red", "blue","green","orange","purple","steel","rebecca","brown","pink","grey","black"]; + this.state.op_types = result[0]; + this.state.operations = result[3]; + this.state.colors = colors; + this.state.late_status = result[1]; + this.state.waiting_status = result[2]; + this.state.backorder_status = result[4]; + var ctx = this.rootRef.el.querySelector("#operation") + var name = Object.values(result[3]) + var count = Object.values(result[0]) + var operationDict = {} + for (var i = 0; i < name.length; i++) { + var operation = name[i]; + var operationCount = count[i]; + operationDict[operation] = operationCount; + } + this.state.operationDict = operationDict + this.rootRef.el.querySelector('#operation_type_table').style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, // Specify bar border width + type: 'bar', // Set this data to a line chart + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, // Instruct chart js to respond nicely. + maintainAspectRatio: false, // Add to prevent default behaviour of full-width/height + } + }); + }); + } + // Top moving products bar graph + async render_top_product_bar_graph(){ + let chartStatus = Chart.getChart('canvaspie'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + const ctx = this.rootRef.el.querySelector("#canvaspie"); + await this.orm.call("stock.move", "get_the_top_products", [] + ).then( (result) => { + var products = result.products + var count = result.count; + var countDictionary = {}; + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var productCount = count[i]; + countDictionary[product] = productCount; + } + this.state.countDictionary = countDictionary; + this.rootRef.el.querySelector("#pro_info").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: products, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Stock moves pie graph + async render_stock_moves(){ + let chartStatus = Chart.getChart('stock_moves'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "get_stock_moves", [] + ).then( (result) => { + var name = result.name + var count = result.count; + var stockMoveDict = {} + for (var i = 0; i < name.length; i++) { + var location = name[i]; + var stockCount = count[i]; + stockMoveDict[location] = stockCount; + } + this.state.MoveData = stockMoveDict; + this.rootRef.el.querySelector('#stock_move_table').style.display = 'none'; + var ctx = this.rootRef.el.querySelector("#stock_moves"); + var myChart = new Chart(ctx, { + type: 'pie', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'pie', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Product move line graph + render_product_move_graph_this_month(){ + let chartStatus = Chart.getChart('product_move_graph'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move.line", "get_product_moves", [] + ).then((result) => { + var ctx = this.rootRef.el.querySelector("#product_move_graph"); + var name = result[0].name + var count = result[0].count + var category_name = result[1].category_name + var category_id = result[1].category_id + this.state.category = category_name + this.state.categoryId = category_id + var option = this.state.categoryId[0] + this.rootRef.el.querySelector('#product_move_table').style.display = 'none'; + this.orm.call("stock.move.line", "product_move_by_category", [option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#product_move_graph"); + var name = result.name + var count = result.count; + this.state.monthly_stock = name + this.state.monthly_stock_count = count + this.rootRef.el.querySelector("#product_move_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'line', + data: { + labels: name, + datasets: [{ + label: 'Quantity Done', + data: count, + backgroundColor: '#003f5c', + borderColor: '#003f5c', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'line', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + }); + } + // Product categories doughnut graph + render_product_category(){ + let chartStatus = Chart.getChart('product_category'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.picking", "get_product_category", []). + then((result) => { + var ctx = this.rootRef.el.querySelector("#product_category"); + var name = result.name + var count = result.count + var categCountDict = {} + this.state.categCountDict = name; + this.state.categName = count; + this.rootRef.el.querySelector("#category_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'doughnut', + data: { + labels: name, + datasets: [{ + label: 'Quantity Done', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c", + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'doughnut', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Location-on hand table + render_storage_location(){ + this.orm.call("stock.picking", "get_locations", + ).then((result) => { + this.state.location_data = result + }); + } + // Dead stock graph + render_dead_of_stock_graph(){ + let chartStatus = Chart.getChart(''); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "get_dead_of_stock",[] + ).then( (result) => { + if (result) { + this.rootRef.el.querySelector("#dead_stock").style.display = 'block'; + var ctx = this.rootRef.el.querySelector("#dead_stock_graph"); + var name = result.product_name + var count = result.total_quantity + this.state.dead_stock_name = name + this.state.dead_stock_count = count + this.rootRef.el.querySelector('#dead_stock_table').style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'line', + data: { + labels: name, + datasets: [{ + label: 'Current Stock', + data: count, + backgroundColor: '#003f5c', + borderColor: '#003f5c', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'line', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + } + else{ + this.rootRef.el.querySelector('#dead_stock').style.display = 'none'; + } + }); + } + // Out of stock graph + render_out_of_stock_graph(){ + let chartStatus = Chart.getChart(''); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.quant", "get_out_of_stock",[] + ).then( (result) => { + if (result) { + var ctx = this.rootRef.el.querySelector("#out_of_stock_graph"); + var name = result.product_name + var count = result.total_quantity + this.state.out_stock = name + this.state.out_stock_count = count + this.rootRef.el.querySelector("#out_of_stock").style.display = 'block'; + this.rootRef.el.querySelector("#out_of_stock_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: name, + datasets: [{ + label: 'Current Stock', + data: count, + backgroundColor: '#003f5c', + borderColor: '#003f5c', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + } + else{ + this.rootRef.el.querySelector("#out_of_stock").style.display = 'none'; + } + }); + } + // Top ten products details button click + async onclick_top_product_info(event) { + var pro_info = this.rootRef.el.querySelector("#pro_info"); + if (pro_info.style.display === "none") { + pro_info.style.display = 'block'; + } else { + pro_info.style.display = 'none'; + } + } + // Stock move details button click + onclick_stock_move_info() { + var move_info = this.rootRef.el.querySelector("#stock_move_table"); + if (move_info.style.display === "none") { + move_info.style.display = 'block'; + } else { + move_info.style.display = 'none'; + } + } + // Operation types table details button click + onclick_operation_type_info() { + var operation_type_table = this.rootRef.el.querySelector("#operation_type_table"); + if (operation_type_table.style.display === "none") { + operation_type_table.style.display = 'block'; + } else { + operation_type_table.style.display = 'none'; + } + } + // Product category graph details button click + onclick_product_category_info() { + var category = this.rootRef.el.querySelector("#category_table"); + if (category.style.display === "none") { + category.style.display = 'block'; + } else { + category.style.display = 'none'; + } + } + // Product move info button + onclick_product_move_info() { + var category = this.rootRef.el.querySelector("#product_move_table"); + if (category.style.display === "none") { + category.style.display = 'block'; + } else { + category.style.display = 'none'; + } + } + // Out of stock table details button + onclick_out_of_stock_info() { + var out_of_stock_table = this.rootRef.el.querySelector("#out_of_stock_table"); + if (out_of_stock_table.style.display === "none") { + out_of_stock_table.style.display = 'block'; + } else { + out_of_stock_table.style.display = 'none'; + } + } + // Dead stock table details button + onclick_dead_stock_info() { + var dead_stock_table = this.rootRef.el.querySelector("#dead_stock_table"); + if (dead_stock_table.style.display === "none") { + dead_stock_table.style.display = 'block'; + } else { + dead_stock_table.style.display = 'none'; + } + } + // Top product selection filters + onchange_top_product_selection(events){ + var option = $(events.target).val(); + // Top product moves in 10 days + if (option == 'top_last_10_days'){ + let chartStatus = Chart.getChart('canvaspie'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "top_products_last_ten", [] + ).then((result) => { + var ctx = this.rootRef.el.querySelector("#canvaspie"); + var products = result.products + var count = result.count; + var countDictionary = {}; + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var productCount = count[i]; + countDictionary[product] = productCount; + } + this.state.countDictionary = countDictionary; + this.rootRef.el.querySelector("#pro_info").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: products, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Top product moves in 30 days + if (option == 'top_last_30_days'){ + let chartStatus = Chart.getChart('canvaspie'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "top_products_last_thirty", [] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#canvaspie"); + var products = result.products + var count = result.count; + var countDictionary = {}; + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var productCount = count[i]; + countDictionary[product] = productCount; + } + this.state.countDictionary = countDictionary; + this.rootRef.el.querySelector("#pro_info").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: products, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Top product moves in 3 months + if (option == 'top_last_3_month'){ + let chartStatus = Chart.getChart('canvaspie'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "top_products_last_three_months", [] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#canvaspie"); + var products = result.products + var count = result.count; + var countDictionary = {}; + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var productCount = count[i]; + countDictionary[product] = productCount; + } + this.state.countDictionary = countDictionary; + this.rootRef.el.querySelector("#pro_info").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: products, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Top product moves in last year + if (option == 'top_last_year'){ + let chartStatus = Chart.getChart('canvaspie'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "top_products_last_year",[] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#canvaspie"); + var products = result.products + var count = result.count; + var countDictionary = {}; + for (var i = 0; i < products.length; i++) { + var product = products[i]; + var productCount = count[i]; + countDictionary[product] = productCount; + } + this.state.countDictionary = countDictionary; + this.rootRef.el.querySelector("#pro_info").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'bar', + data: { + labels: products, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'bar', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + } + // Stock moves filter + async onchange_stock_moves_selection(events){ + let chartStatus = Chart.getChart('stock_moves'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + var option = $(events.target).val(); + // Stock moves in 10 days + if (option == 'last_10_days'){ + this.orm.call("stock.move", "stock_move_last_ten_days", [option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#stock_moves"); + var name = result.name + var count = result.count; + var stockMoveDict = {} + for (var i = 0; i < name.length; i++) { + var location = name[i]; + var stockCount = count[i]; + stockMoveDict[location] = stockCount; + } + this.state.MoveData = stockMoveDict; + var myChart = new Chart(ctx, { + type: 'pie', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'pie', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Stock moves in current month + if (option == 'this_month'){ + let chartStatus = Chart.getChart('stock_moves'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "this_month",[option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#stock_moves"); + var name = result.name + var count = result.count; + var stockMoveDict = {} + for (var i = 0; i < name.length; i++) { + var location = name[i]; + var stockCount = count[i]; + stockMoveDict[location] = stockCount; + } + this.state.MoveData = stockMoveDict; + this.rootRef.el.querySelector("#stock_move_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'pie', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'pie', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Stock moves in last 3 months + if (option == 'last_3_month'){ + let chartStatus = Chart.getChart('stock_moves'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "last_three_month", [option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#stock_moves"); + var name = result.name + var count = result.count; + var stockMoveDict = {} + for (var i = 0; i < name.length; i++) { + var location = name[i]; + var stockCount = count[i]; + stockMoveDict[location] = stockCount; + } + this.state.MoveData = stockMoveDict; + this.rootRef.el.querySelector("#stock_move_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'pie', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'pie', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // Stock moves in last year + else if (option == 'last_year'){ + let chartStatus = Chart.getChart('stock_moves'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + this.orm.call("stock.move", "last_year", [option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#stock_moves"); + var name = result.name + var count = result.count; + this.rootRef.el.querySelector("#stock_move_table").style.display = 'none'; + var stockMoveDict = {} + for (var i = 0; i < name.length; i++) { + var location = name[i]; + var stockCount = count[i]; + stockMoveDict[location] = stockCount; + } + this.state.MoveData = stockMoveDict; + var myChart = new Chart(ctx, { + type: 'pie', + data: { + labels: name, + datasets: [{ + label: 'Count', + data: count, + backgroundColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + borderColor: [ + "#003f5c","#2f4b7c","#f95d6a","#665191", + "#d45087","#ff7c43","#ffa600","#a05195", + "#6d5c16","#CCCCFF" + ], + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'pie', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + } + // Product move selection + onchange_product_moves_selection(events){ + let chartStatus = Chart.getChart('product_move_graph'); + if (chartStatus != undefined) { + chartStatus.destroy(); + } + var option = $(events.target).val(); + this.orm.call("stock.move.line", "product_move_by_category", [option] + ).then( (result) => { + var ctx = this.rootRef.el.querySelector("#product_move_graph"); + var name = result.name + var count = result.count; + this.state.monthly_stock = name + this.state.monthly_stock_count = count + this.rootRef.el.querySelector("#product_move_table").style.display = 'none'; + var myChart = new Chart(ctx, { + type: 'line', + data: { + labels: name, + datasets: [{ + label: 'Quantity Done', + data: count, + backgroundColor: '#003f5c', + borderColor: '#003f5c', + barPercentage: 0.5, + barThickness: 6, + maxBarThickness: 8, + minBarLength: 0, + borderWidth: 1, + type: 'line', + fill: false + }] + }, + options: { + scales: { + y: { + beginAtZero: true + }, + }, + responsive: true, + maintainAspectRatio: false, + } + }); + }); + } + // On clicking tiles + async onclick_tiles (f) { + var id = parseInt($(f.currentTarget).attr('id')); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.env.services['action'].doAction({ + name: _t(op_type[$(f.currentTarget).attr('id')]), + type: 'ir.actions.act_window', + res_model: 'stock.picking', + views: [[false, 'list'], [false, 'form']], + view_mode: 'tree,form,calendar', + domain: [['picking_type_id', '=', id]], + target: 'current', + }, options); + } + // On clicking tile of late status + onclick_late_status (f) { + f.stopPropagation(); + var id = parseInt($(f.currentTarget).attr('id')); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.env.services['action'].doAction({ + name: _t(op_type[id]+'/Late'), + type: 'ir.actions.act_window', + res_model: 'stock.picking', + view_mode: 'tree,form,calendar', + views: [[false, 'list'],[false, 'form']], + domain: [['picking_type_id', '=', id],['state', 'in', ['assigned', 'waiting', 'confirmed']], + ['scheduled_date', '<=', DateTime.local().toFormat('yyyy-MM-dd')],], + target: 'current', + }, options) + } + // Onc licking of tile waiting status + onclick_waiting_status (f) { + f.stopPropagation(); + var id = parseInt($(f.currentTarget).attr('id')); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.env.services['action'].doAction({ + name: _t(op_type[id]+'/Waiting'), + type: 'ir.actions.act_window', + res_model: 'stock.picking', + view_mode: 'tree,form,calendar', + views: [[false, 'list'],[false, 'form']], + domain: [['picking_type_id', '=', id],['state', '=', 'confirmed']], + target: 'current', + }, options) + } + // On clicking of tile backorder status + onclick_backorders_status (f) { + f.stopPropagation(); + var id = parseInt($(f.currentTarget).attr('id')); + var options = { + on_reverse_breadcrumb: this.on_reverse_breadcrumb, + }; + this.env.services['action'].doAction({ + name: _t(op_type[id]+'/Backorders'), + type: 'ir.actions.act_window', + res_model: 'stock.picking', + view_mode: 'tree,form,calendar', + views: [[false, 'list'],[false, 'form']], + domain: [['picking_type_id', '=', id],['backorder_id', '!=', false]], + target: 'current', + }, options) + } +} +Dashboard.template = "Dashboard"; +actionRegistry.add('inventory_dashboard_tag', Dashboard); diff --git a/inventory_stock_dashboard_odoo/static/src/xml/inventory_dashboard_template.xml b/inventory_stock_dashboard_odoo/static/src/xml/inventory_dashboard_template.xml new file mode 100644 index 000000000..5f1663337 --- /dev/null +++ b/inventory_stock_dashboard_odoo/static/src/xml/inventory_dashboard_template.xml @@ -0,0 +1,344 @@ + + + + +
+ +
+
+
+ +
+
+
+ + + + +
+
    + +
  • +
    +
    + Late +
    +
    + +
    +
    +
  • +
    + +
  • +
    +
    + Waiting +
    +
    + +
    +
    +
  • +
    + +
  • +
    +
    + Backorder +
    +
    + +
    +
    +
  • +
    +
+
+
+
+
+
+
+ +
+ +
+
+
+

Top Moving Products

+
+ +
+ + + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+
+
+

Product Categories

+ + + + + + + + + + + + +
CategoriesOnhand Quantity
+
+
+
+ +
+
+
+ +
+
+
+

Product Moves By Category

+
+ +
+ + + + + + + + + + + + +
ProductsQuantity Done
+
+
+
+ +
+
+
+ +
+
+
+

Stock Moves By Location

+
+ +
+ + + + + + + + + + + + + +
+
+
+ +
+
+
+ +
+
+
+

Operation Types

+ + + + + + + + + + + + +
Operation TypesTransfer Count
+
+
+
+ +
+
+
+ +
+
+
+

Locations

+
+
+ + + + + + + + + + + + + + + +

Location

On Hand Quantity

+ + + +
+
+
+ + + + +
+
+
+
diff --git a/inventory_stock_dashboard_odoo/views/dashboard_menu.xml b/inventory_stock_dashboard_odoo/views/dashboard_menu.xml new file mode 100644 index 000000000..885a9664e --- /dev/null +++ b/inventory_stock_dashboard_odoo/views/dashboard_menu.xml @@ -0,0 +1,14 @@ + + + + + Dashboard + inventory_dashboard_tag + + + + diff --git a/inventory_stock_dashboard_odoo/views/res_config_settings_views.xml b/inventory_stock_dashboard_odoo/views/res_config_settings_views.xml new file mode 100644 index 000000000..ac94c298f --- /dev/null +++ b/inventory_stock_dashboard_odoo/views/res_config_settings_views.xml @@ -0,0 +1,51 @@ + + + + + res.config.settings.view.form.inherit.inventory.stock.dashboard.odoo + res.config.settings + + + + +
+

Dashboard

+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
+