| @ -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 18 | ||||
|  | =========================== | ||||
|  | Dashboard for Inventory module. | ||||
|  | 
 | ||||
|  | Configuration | ||||
|  | ============= | ||||
|  | No additional configurations needed. | ||||
|  | 
 | ||||
|  | Company | ||||
|  | ------- | ||||
|  | * `Cybrosys Techno Solutions <https://cybrosys.com/>`__ | ||||
|  | 
 | ||||
|  | License | ||||
|  | ------- | ||||
|  | Lesser General Public License, Version 3 (LGPL v3) | ||||
|  | (https://www.gnu.org/licenses/lgpl-3.0-standalone.html) | ||||
|  | 
 | ||||
|  | Credits | ||||
|  | ------- | ||||
|  | * Developer : (V18) Renu | ||||
|  |   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 <https://cybrosys.com/>`__ | ||||
|  | 
 | ||||
|  | Further information | ||||
|  | =================== | ||||
|  | HTML Description: `<static/description/index.html>`__ | ||||
| @ -0,0 +1,22 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################# | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################# | ||||
|  | from . import models | ||||
| @ -0,0 +1,53 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | { | ||||
|  |     'name': 'Inventory Dashboard Odoo 18', | ||||
|  |     'version': '18.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, | ||||
|  | } | ||||
| @ -0,0 +1,6 @@ | |||||
|  | ## Module <inventory_stock_dashboard_odoo> | ||||
|  | 
 | ||||
|  | #### 29.01.2025 | ||||
|  | #### Version 18.0.1.0.0 | ||||
|  | ##### ADD | ||||
|  | - Initial commit for Inventory Dashboard Odoo 18 | ||||
| @ -0,0 +1,26 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | from . import res_config_settings | ||||
|  | from . import stock_move | ||||
|  | from . import stock_move_line | ||||
|  | from . import stock_picking | ||||
|  | from . import stock_quant | ||||
| @ -0,0 +1,56 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | 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.') | ||||
| @ -0,0 +1,277 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | 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 | ||||
| @ -0,0 +1,86 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | 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.""" | ||||
|  |         if option is None: | ||||
|  |             return {} | ||||
|  |         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 | ||||
| @ -0,0 +1,101 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | 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 | ||||
| @ -0,0 +1,59 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #    Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #    Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #    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 <http://www.gnu.org/licenses/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | 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 | ||||
| After Width: | Height: | Size: 2.2 KiB | 
| After Width: | Height: | Size: 28 KiB | 
| After Width: | Height: | Size: 628 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 210 KiB | 
| After Width: | Height: | Size: 209 KiB | 
| After Width: | Height: | Size: 109 KiB | 
| After Width: | Height: | Size: 495 B | 
| After Width: | Height: | Size: 1.0 KiB | 
| After Width: | Height: | Size: 624 B | 
| After Width: | Height: | Size: 136 KiB | 
| After Width: | Height: | Size: 214 KiB | 
| After Width: | Height: | Size: 36 KiB | 
| After Width: | Height: | Size: 3.6 KiB | 
| After Width: | Height: | Size: 310 B | 
| After Width: | Height: | Size: 929 B | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 3.3 KiB | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 17 KiB | 
| After Width: | Height: | Size: 542 B | 
| After Width: | Height: | Size: 576 B | 
| After Width: | Height: | Size: 733 B | 
| After Width: | Height: | Size: 4.3 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 4.0 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 738 KiB | 
| After Width: | Height: | Size: 2.2 KiB | 
| After Width: | Height: | Size: 911 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 600 B | 
| After Width: | Height: | Size: 673 B | 
| After Width: | Height: | Size: 2.0 KiB | 
| After Width: | Height: | Size: 462 B | 
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 926 B | 
| After Width: | Height: | Size: 9.0 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 7.0 KiB | 
| After Width: | Height: | Size: 878 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 653 B | 
| After Width: | Height: | Size: 800 B | 
| After Width: | Height: | Size: 905 B | 
| After Width: | Height: | Size: 189 KiB | 
| After Width: | Height: | Size: 4.3 KiB | 
| After Width: | Height: | Size: 839 B | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 5.9 KiB | 
| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 34 KiB | 
| After Width: | Height: | Size: 26 KiB | 
| After Width: | Height: | Size: 3.8 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 1.9 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 427 B | 
| After Width: | Height: | Size: 627 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 988 B | 
| After Width: | Height: | Size: 3.7 KiB | 
| After Width: | Height: | Size: 5.0 KiB | 
| After Width: | Height: | Size: 875 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 767 KiB | 
| After Width: | Height: | Size: 138 KiB | 
| After Width: | Height: | Size: 760 KiB | 
| After Width: | Height: | Size: 92 KiB | 
| After Width: | Height: | Size: 697 KiB | 
| After Width: | Height: | Size: 1.1 MiB | 
| After Width: | Height: | Size: 130 KiB | 
| After Width: | Height: | Size: 124 KiB | 
| After Width: | Height: | Size: 162 KiB | 
| After Width: | Height: | Size: 122 KiB | 
| After Width: | Height: | Size: 170 KiB | 
| After Width: | Height: | Size: 25 KiB | 
| After Width: | Height: | Size: 136 KiB | 
| After Width: | Height: | Size: 108 KiB | 
| After Width: | Height: | Size: 51 KiB | 
| After Width: | Height: | Size: 45 KiB | 
| After Width: | Height: | Size: 62 KiB | 
| After Width: | Height: | Size: 110 KiB | 
| After Width: | Height: | Size: 45 KiB | 
| After Width: | Height: | Size: 23 KiB | 
| After Width: | Height: | Size: 398 KiB | 
| After Width: | Height: | Size: 880 KiB |