| @ -0,0 +1,49 @@ | |||||
|  | .. 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 Advanced Reports | ||||
|  | ========================== | ||||
|  | Helps to manage different types of Inventory Reports like FSN Report, Out Of Stock Report, etc. | ||||
|  | 
 | ||||
|  | Configuration | ||||
|  | ============= | ||||
|  | - No additional configuration required. | ||||
|  | 
 | ||||
|  | License | ||||
|  | ------- | ||||
|  | Lesser General Public License, Version 3 (LGPL v3). | ||||
|  | (https://www.gnu.org/licenses/lgpl-3.0-standalone.html) | ||||
|  | 
 | ||||
|  | Company | ||||
|  | ------- | ||||
|  | * `Cybrosys Techno Solutions <https://cybrosys.com/>`__ | ||||
|  | 
 | ||||
|  | Credits | ||||
|  | ------- | ||||
|  | Developer:  (V15) Gayathri V, | ||||
|  |             (V16) Anusha C | ||||
|  | 
 | ||||
|  | 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 https://www.cybrosys.com | ||||
|  | 
 | ||||
|  | Further information | ||||
|  | =================== | ||||
|  | HTML Description: `<static/description/index.html>`__ | ||||
| @ -0,0 +1,24 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 controllers | ||||
|  | from . import report | ||||
|  | from . import wizard | ||||
| @ -0,0 +1,67 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 Advanced Reports", | ||||
|  |     "version": "15.0.1.0.0", | ||||
|  |     "category": 'Warehouse', | ||||
|  |     "summary": "Helps to Manage different types of Inventory Reports.", | ||||
|  |     "description": "Helps to Manage different types of Inventory Reports like " | ||||
|  |                    "FSN Report, Out Of Stock Report, Inventory XYZ Report, etc", | ||||
|  |     "author": "Cybrosys Techno Solutions", | ||||
|  |     'company': 'Cybrosys Techno Solutions', | ||||
|  |     'maintainer': 'Cybrosys Techno Solutions', | ||||
|  |     'website': 'https://www.cybrosys.com', | ||||
|  |     "depends": ["stock", "purchase", "sale_management"], | ||||
|  |     "data": ["security/ir.model.access.csv", | ||||
|  |              "report/aging_report_views.xml", | ||||
|  |              "report/fsn_report_views.xml", | ||||
|  |              "report/xyz_report_views.xml", | ||||
|  |              "report/fsn_xyz_report_views.xml", | ||||
|  |              "report/out_of_stock_report_views.xml", | ||||
|  |              "report/over_stock_report_views.xml", | ||||
|  |              "report/age_breakdown_report_views.xml", | ||||
|  |              "report/stock_movement_report_views.xml", | ||||
|  |              "wizard/inventory_aging_report_views.xml", | ||||
|  |              "wizard/inventory_aging_data_report_views.xml", | ||||
|  |              "wizard/inventory_fsn_report_views.xml", | ||||
|  |              "wizard/inventory_fsn_data_report_views.xml", | ||||
|  |              "wizard/inventory_xyz_report_views.xml", | ||||
|  |              "wizard/inventory_xyz_data_report_views.xml", | ||||
|  |              "wizard/inventory_fsn_xyz_report_views.xml", | ||||
|  |              "wizard/inventory_fsn_xyz_data_report_views.xml", | ||||
|  |              "wizard/inventory_out_of_stock_report_views.xml", | ||||
|  |              "wizard/inventory_out_of_stock_data_report_views.xml", | ||||
|  |              "wizard/inventory_age_breakdown_report_views.xml", | ||||
|  |              "wizard/inventory_over_stock_report_views.xml", | ||||
|  |              "wizard/inventory_over_stock_data_report_views.xml", | ||||
|  |              "wizard/inventory_stock_movement_report_views.xml", | ||||
|  |              ], | ||||
|  |     'assets': { | ||||
|  |         'web.assets_backend': [ | ||||
|  |             'inventory_advanced_reports/static/src/js/action_manager.js'] | ||||
|  |     }, | ||||
|  |     'images': ['static/description/banner.png'], | ||||
|  |     'license': 'LGPL-3', | ||||
|  |     'installable': True, | ||||
|  |     'auto_install': False, | ||||
|  |     'application': False, | ||||
|  | } | ||||
| @ -0,0 +1,22 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author: Gayathri V (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 inventory_advanced_reports | ||||
| @ -0,0 +1,58 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author: Gayathri V (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/>. | ||||
|  | # | ||||
|  | ############################################################################### | ||||
|  | import json | ||||
|  | from odoo import http | ||||
|  | from odoo.http import content_disposition, request | ||||
|  | from odoo.tools import html_escape | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class XLSXReportController(http.Controller): | ||||
|  |     """This model is used to connect the frontend to the backend""" | ||||
|  | 
 | ||||
|  |     @http.route('/xlsx_reports', type='http', auth='public', methods=['POST'], | ||||
|  |                 csrf=False) | ||||
|  |     def get_report_xlsx(self, model, options, output_format, report_name): | ||||
|  |         """This function is called when a post request is made to this route""" | ||||
|  |         uid = request.session.uid | ||||
|  |         report_obj = request.env[model].with_user(uid) | ||||
|  |         options = json.loads(options) | ||||
|  |         token = 'dummy-because-api-expects-one' | ||||
|  |         try: | ||||
|  |             if output_format == 'xlsx': | ||||
|  |                 response = request.make_response( | ||||
|  |                     None, | ||||
|  |                     headers=[ | ||||
|  |                         ('Content-Type', 'application/vnd.ms-excel'), | ||||
|  |                         ('Content-Disposition', | ||||
|  |                          content_disposition(report_name + '.xlsx')) | ||||
|  |                     ]) | ||||
|  |                 report_obj.get_xlsx_report(options, response) | ||||
|  |             response.set_cookie('fileToken', token) | ||||
|  |             return response | ||||
|  |         except Exception as exception: | ||||
|  |             serialise = http.serialize_exception(exception) | ||||
|  |             error = { | ||||
|  |                 'code': 200, | ||||
|  |                 'message': 'Odoo Server Error', | ||||
|  |                 'data': serialise | ||||
|  |             } | ||||
|  |             return request.make_response(html_escape(json.dumps(error))) | ||||
| @ -0,0 +1,7 @@ | |||||
|  | ## Module <inventory_advanced_reports> | ||||
|  | 
 | ||||
|  | #### 07.06.2024 | ||||
|  | #### Version 15.0.1.0.0 | ||||
|  | ##### ADD | ||||
|  | 
 | ||||
|  | - Initial Commit for Inventory Advanced Reports | ||||
| @ -0,0 +1,29 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author: Gayathri V (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 aging_report | ||||
|  | from . import age_breakdown_report | ||||
|  | from . import fsn_report | ||||
|  | from . import fsn_xyz_report | ||||
|  | from . import out_of_stock_report | ||||
|  | from . import over_stock_report | ||||
|  | from . import stock_movement_report | ||||
|  | from . import xyz_report | ||||
| @ -0,0 +1,180 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class AgeBreakdownReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_breakdown' | ||||
|  |     _description = 'Age Breakdown Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         product_ids = data['product_ids'] | ||||
|  |         category_ids = data['category_ids'] | ||||
|  |         company_ids = data['company_ids'] | ||||
|  |         age_breakdown_days = data['age_breakdown_days'] | ||||
|  |         params = [] | ||||
|  |         param_count = 0 | ||||
|  |         query = """ | ||||
|  |         SELECT  | ||||
|  |             CASE | ||||
|  |                 WHEN pp.default_code IS NOT NULL  | ||||
|  |                     THEN CONCAT(pp.default_code, ' - ', pt.name) | ||||
|  |                 ELSE | ||||
|  |                     pt.name | ||||
|  |             END AS product_code_and_name,  | ||||
|  |             c.complete_name AS category_name, | ||||
|  |             c.id AS category_id, | ||||
|  |             pp.id AS product_id, | ||||
|  |             company.id AS company_id, | ||||
|  |             company.name AS company_name, | ||||
|  |             COALESCE(SUM(svl.remaining_qty), 0) AS qty_available, | ||||
|  |             SUM(svl.remaining_value) AS stock_value, | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= 1 AND age.days_between <= %s  | ||||
|  |                 THEN svl.remaining_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_qty_1", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= %s+1 AND age.days_between <= %s*2  | ||||
|  |                 THEN svl.remaining_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_qty_2", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >=  (%s*2)+1 AND age.days_between <= %s*3  | ||||
|  |                 THEN svl.remaining_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_qty_3", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= (%s*3)+1 AND age.days_between <= %s*4  | ||||
|  |                 THEN svl.remaining_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_qty_4", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= (%s*4)+1 THEN svl.remaining_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_qty_5", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= 1 AND age.days_between <= %s  | ||||
|  |                 THEN svl.remaining_value | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_value_1", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= %s+1 AND age.days_between <= %s*2  | ||||
|  |                 THEN svl.remaining_value | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_value_2", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= (%s*2)+1 AND age.days_between <= %s*3  | ||||
|  |                 THEN svl.remaining_value | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_value_3", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= (%s*3)+1 AND age.days_between <= %s*4  | ||||
|  |                 THEN svl.remaining_value | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_value_4", | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN age.days_between >= (%s*4)+1 THEN svl.remaining_value | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS "age_breakdown_value_5" | ||||
|  |         FROM product_product pp | ||||
|  |         INNER JOIN product_template pt ON pp.product_tmpl_id = pt.id | ||||
|  |         INNER JOIN product_category c ON pt.categ_id = c.id | ||||
|  |         LEFT JOIN stock_move sm ON sm.product_id = pp.id | ||||
|  |         LEFT JOIN stock_picking_type spt ON sm.picking_type_id = spt.id | ||||
|  |         LEFT JOIN res_company company ON sm.company_id = company.id | ||||
|  |         LEFT JOIN LATERAL ( | ||||
|  |             SELECT EXTRACT(day FROM CURRENT_DATE - sm.date) AS days_between | ||||
|  |         ) AS age ON true | ||||
|  |         INNER JOIN stock_valuation_layer svl ON svl.stock_move_id = sm.id | ||||
|  |         WHERE pt.detailed_type = 'product' | ||||
|  |             AND sm.state = 'done' | ||||
|  |             AND svl.remaining_value IS NOT NULL | ||||
|  |                         """ | ||||
|  |         params.extend([age_breakdown_days] * 16) | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category for category in category_ids] | ||||
|  |             params.append(category_ids) | ||||
|  |             query += "(pt.categ_id = ANY(%s))" | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company for company in company_ids] | ||||
|  |             query += " AND (sm.company_id = ANY(%s))"  # Specify the table alias | ||||
|  |             params.append(company_ids) | ||||
|  |             param_count += 1 | ||||
|  |         query += """ | ||||
|  |                     GROUP BY | ||||
|  |                                 CASE | ||||
|  |                                     WHEN pp.default_code IS NOT NULL  | ||||
|  |                                         THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                                         pt.name) | ||||
|  |                                     ELSE | ||||
|  |                                         pt.name | ||||
|  |                                 END,  | ||||
|  |                                 c.complete_name, | ||||
|  |                                 company.id,  | ||||
|  |                                 c.id, | ||||
|  |                                 company.name, | ||||
|  |                                 pp.id; | ||||
|  |                 """ | ||||
|  |         self.env.cr.execute(query, params) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         main_header = age_breakdown_days | ||||
|  |         if result_data: | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': 'report.inventory_advanced_reports.' | ||||
|  |                              'report_inventory_breakdown', | ||||
|  |                 'data': values, | ||||
|  |                 'options': result_data, | ||||
|  |                 'main_header': self.get_header(main_header) | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
|  | 
 | ||||
|  |     def get_header(self, main_header): | ||||
|  |         """For getting the header for the report""" | ||||
|  |         age_breakdown1 = main_header | ||||
|  |         age_breakdown2 = main_header * 2 | ||||
|  |         age_breakdown3 = main_header * 3 | ||||
|  |         age_breakdown4 = main_header * 4 | ||||
|  |         return ['1-' + str(age_breakdown1), | ||||
|  |                 str(age_breakdown1 + 1) + '-' + str(age_breakdown2), | ||||
|  |                 str(age_breakdown2 + 1) + '-' + str(age_breakdown3), | ||||
|  |                 str(age_breakdown3 + 1) + '-' + str(age_breakdown4), | ||||
|  |                 'ABOVE ' + str(age_breakdown4)] | ||||
| @ -0,0 +1,106 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||
|  | <odoo> | ||||
|  |     <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_age_breakdown_action" | ||||
|  |             model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory Age Breakdown Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_breakdown</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_breakdown</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_breakdown</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_age_breakdown_report"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report --> | ||||
|  |     <template id="report_inventory_breakdown"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory Age Breakdown Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center"/> | ||||
|  |                             <th align="center"/> | ||||
|  |                             <th align="center"/> | ||||
|  |                             <th align="center"/> | ||||
|  |                             <th align="center"/> | ||||
|  |                             <t t-foreach="main_header" t-as="header"> | ||||
|  |                             <th align="center" colspan="2"><t t-esc="header"/></th> | ||||
|  |                             </t> | ||||
|  |                         </tr> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">TOTAL STOCK</th> | ||||
|  |                             <th align="center">STOCK VALUE</th> | ||||
|  |                             <th align="center">STOCK</th> | ||||
|  |                             <th align="center">VALUE</th> | ||||
|  |                             <th align="center">STOCK</th> | ||||
|  |                             <th align="center">VALUE</th> | ||||
|  |                             <th align="center">STOCK</th> | ||||
|  |                             <th align="center">VALUE</th> | ||||
|  |                             <th align="center">STOCK</th> | ||||
|  |                             <th align="center">VALUE</th> | ||||
|  |                             <th align="center">STOCK</th> | ||||
|  |                             <th align="center">VALUE</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <t t-log="new"/> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['qty_available']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['stock_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['age_breakdown_qty_1']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['age_breakdown_value_1']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_qty_2']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_value_2']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_qty_3']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_value_3']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_qty_4']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_value_4']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_qty_5']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['age_breakdown_value_5']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,172 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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, fields, models | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class AgingReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_aging' | ||||
|  |     _description = 'Aging Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         product_ids = data['product_ids'] | ||||
|  |         category_ids = data['category_ids'] | ||||
|  |         company_ids = data['company_ids'] | ||||
|  |         params = [] | ||||
|  |         param_count = 0 | ||||
|  |         query = """ | ||||
|  |                     SELECT  | ||||
|  |                 CASE | ||||
|  |                     WHEN pp.default_code IS NOT NULL  | ||||
|  |                         THEN CONCAT(pp.default_code, ' - ', pt.name) | ||||
|  |                     ELSE | ||||
|  |                         pt.name | ||||
|  |                 END AS product_code_and_name,  | ||||
|  |                 c.complete_name AS category_name, | ||||
|  |                 c.id AS category_id, | ||||
|  |                 pp.id AS product_id, | ||||
|  |                 company.id AS company_id, | ||||
|  |                 company.name AS company_name, | ||||
|  |                 COALESCE(SUM(svl.remaining_qty), 0) AS qty_available, | ||||
|  |                 ( | ||||
|  |                     SELECT SUM(sm_inner.product_uom_qty) | ||||
|  |                     FROM stock_move sm_inner | ||||
|  |                     INNER JOIN res_company company_inner ON sm_inner.company_id = company_inner.id | ||||
|  |                     WHERE sm_inner.product_id = pp.id | ||||
|  |                     AND sm_inner.state = 'done' | ||||
|  |                     AND sm_inner.date < ( | ||||
|  |                         SELECT MAX(sm_inner2.date) | ||||
|  |                         FROM stock_move sm_inner2 | ||||
|  |                         WHERE sm_inner2.product_id = pp.id | ||||
|  |                         AND sm_inner2.state = 'done' | ||||
|  |                         AND company_inner.id = sm_inner2.company_id | ||||
|  |                     ) | ||||
|  |                 ) AS prev_qty_available, | ||||
|  |                 ( | ||||
|  |                     SELECT MIN(sm_inner.date) | ||||
|  |                     FROM stock_move sm_inner | ||||
|  |                     WHERE sm_inner.product_id = pp.id | ||||
|  |                     AND sm_inner.state = 'done' | ||||
|  |                     AND (company.id IS NULL OR company.id = sm_inner.company_id) | ||||
|  |                 ) AS receipt_date | ||||
|  |             FROM  | ||||
|  |                 product_product pp | ||||
|  |                 INNER JOIN product_template pt ON pp.product_tmpl_id = pt.id | ||||
|  |                 INNER JOIN product_category c ON pt.categ_id = c.id | ||||
|  |                 LEFT JOIN stock_move sm ON sm.product_id = pp.id | ||||
|  |                 LEFT JOIN stock_picking_type spt ON sm.picking_type_id = spt.id | ||||
|  |                 LEFT JOIN res_company company ON sm.company_id = company.id | ||||
|  |                 INNER JOIN stock_valuation_layer svl ON svl.stock_move_id = sm.id | ||||
|  |             WHERE  | ||||
|  |                 pt.detailed_type = 'product' | ||||
|  |                 AND sm.state = 'done' | ||||
|  |                                     """ | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category for category in category_ids] | ||||
|  |             params.append(category_ids) | ||||
|  |             query += "(pt.categ_id = ANY(%s))" | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company for company in company_ids] | ||||
|  |             query += " AND (sm.company_id = ANY(%s))" | ||||
|  |             params.append(company_ids) | ||||
|  |             param_count += 1 | ||||
|  |         query += """ | ||||
|  |                     GROUP BY  | ||||
|  |                         CASE | ||||
|  |                             WHEN pp.default_code IS NOT NULL  | ||||
|  |                                 THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                                 pt.name) | ||||
|  |                             ELSE | ||||
|  |                                 pt.name | ||||
|  |                         END,  | ||||
|  |                         c.complete_name, | ||||
|  |                         company.id,  | ||||
|  |                         c.id, | ||||
|  |                         company.name, | ||||
|  |                         pp.id; | ||||
|  |                 """ | ||||
|  |         self.env.cr.execute(query, params) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         today = fields.datetime.now().date() | ||||
|  |         for row in result_data: | ||||
|  |             receipt_date = row.get('receipt_date') | ||||
|  |             if receipt_date: | ||||
|  |                 receipt_date = receipt_date.date() | ||||
|  |                 row['days_since_receipt'] = (today - receipt_date).days | ||||
|  |             product = self.env['product.product'].browse(row.get('product_id')) | ||||
|  |             standard_price = product.standard_price | ||||
|  |             current_stock = row.get('qty_available') | ||||
|  |             prev_stock = row.get('prev_qty_available') | ||||
|  |             if prev_stock is None: | ||||
|  |                 prev_stock = current_stock | ||||
|  |                 row['prev_qty_available'] = current_stock | ||||
|  |             if standard_price and current_stock: | ||||
|  |                 row['current_value'] = current_stock * standard_price | ||||
|  |             else: | ||||
|  |                 row[ | ||||
|  |                     'current_value'] = 0 | ||||
|  |             row[ | ||||
|  |                 'prev_value'] = prev_stock * standard_price \ | ||||
|  |                 if prev_stock is not None else 0 | ||||
|  |             total_current_stock = sum( | ||||
|  |                 item.get('qty_available') for item in result_data if | ||||
|  |                 item.get('qty_available') is not None) | ||||
|  |             if total_current_stock: | ||||
|  |                 stock_percentage = (current_stock / total_current_stock) * 100 | ||||
|  |             else: | ||||
|  |                 stock_percentage = 0.0 | ||||
|  |             row['stock_percentage'] = round(stock_percentage, 2) | ||||
|  |             current_value = row.get('current_value') | ||||
|  |             total_value = sum( | ||||
|  |                 item.get('current_value', 0) for item in result_data) | ||||
|  |             if total_value: | ||||
|  |                 stock_value_percentage = (current_value / total_value) * 100 | ||||
|  |             else: | ||||
|  |                 stock_value_percentage = 0.0 | ||||
|  |             row['stock_value_percentage'] = round(stock_value_percentage, 2) | ||||
|  |         print(result_data,'fgthyujikoo') | ||||
|  |         if result_data: | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': | ||||
|  |                     'report.inventory_advanced_reports.report_inventory_aging', | ||||
|  |                 'data': values, | ||||
|  |                 'options': result_data, | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
| @ -0,0 +1,75 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||
|  | <odoo> | ||||
|  |     <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_aging_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory Aging Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_aging</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_aging</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_aging</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_aging_report"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_aging"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory Aging Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">CURRENT STOCK</th> | ||||
|  |                             <th align="center">CURRENT VALUE</th> | ||||
|  |                             <th align="center">STOCK QUANT(%)</th> | ||||
|  |                             <th align="center">STOCK VALUE(%)</th> | ||||
|  |                             <th align="center">OLDEST STOCK AGE</th> | ||||
|  |                             <th align="center">OLDEST STOCK</th> | ||||
|  |                             <th align="center">OLDEST STOCK VALUE</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <t t-log="new"/> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['qty_available']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['current_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['stock_percentage']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['stock_value_percentage']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['days_since_receipt']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['prev_qty_available']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['prev_value']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,200 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 | ||||
|  | from datetime import datetime | ||||
|  | 
 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class FsnReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_fsn' | ||||
|  |     _description = 'FSN Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         if data is None or not isinstance(data, dict): | ||||
|  |             raise ValueError("Invalid or missing data for the report") | ||||
|  |         product_ids = data.get('product_ids', []) | ||||
|  |         category_ids = data.get('category_ids', []) | ||||
|  |         company_ids = data.get('company_ids', []) | ||||
|  |         warehouse_ids = data.get('warehouse_ids', []) | ||||
|  |         start_date = data.get('start_date') | ||||
|  |         end_date = data.get('end_date') | ||||
|  |         fsn = data.get('fsn') | ||||
|  |         if not start_date or not end_date: | ||||
|  |             raise ValueError( | ||||
|  |                 "Missing start_date or end_date in the data") | ||||
|  |         start_date = datetime.strptime(start_date, '%Y-%m-%d') | ||||
|  |         end_date = datetime.strptime(end_date, '%Y-%m-%d') | ||||
|  |         filtered_product_stock = [] | ||||
|  |         query = """ | ||||
|  |             SELECT | ||||
|  |                 product_id, | ||||
|  |                 product_code_and_name, | ||||
|  |                 category_id, | ||||
|  |                 category_name, | ||||
|  |                 company_id, | ||||
|  |                 warehouse_id, | ||||
|  |                 opening_stock, | ||||
|  |                 closing_stock, | ||||
|  |                 sales, | ||||
|  |                 average_stock, | ||||
|  |                 CASE | ||||
|  |                     WHEN sales > 0  | ||||
|  |                     THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                     ELSE 0 | ||||
|  |                 END AS turnover_ratio, | ||||
|  |                 CASE | ||||
|  |                     WHEN | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END > 3 THEN 'Fast Moving' | ||||
|  |                     WHEN | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END >= 1 AND | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END <= 3 THEN 'Slow Moving' | ||||
|  |                     ELSE 'Non Moving' | ||||
|  |                 END AS fsn_classification | ||||
|  |             FROM | ||||
|  |                 (SELECT | ||||
|  |                     pp.id AS product_id, | ||||
|  |                     pt.categ_id AS category_id, | ||||
|  |                     CASE | ||||
|  |                         WHEN pp.default_code IS NOT NULL  | ||||
|  |                             THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                             pt.name) | ||||
|  |                         ELSE | ||||
|  |                             pt.name | ||||
|  |                     END AS product_code_and_name,  | ||||
|  |                     pc.complete_name AS category_name, | ||||
|  |                     company.id AS company_id, | ||||
|  |                     sw.id AS warehouse_id, | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)) AS opening_stock, | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)) AS closing_stock, | ||||
|  |                     SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                     AND sld_dest.usage = 'customer'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                     ((SUM(CASE WHEN sm.date <= %s  | ||||
|  |                     AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END))+ | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)))/2 AS average_stock | ||||
|  |                 FROM | ||||
|  |                     stock_move sm | ||||
|  |                 JOIN | ||||
|  |                     product_product pp ON sm.product_id = pp.id | ||||
|  |                 JOIN | ||||
|  |                     product_template pt ON pp.product_tmpl_id = pt.id | ||||
|  |                 JOIN | ||||
|  |                     product_category pc ON pt.categ_id = pc.id | ||||
|  |                 JOIN | ||||
|  |                     res_company company ON company.id = sm.company_id | ||||
|  |                 JOIN | ||||
|  |                     stock_warehouse sw ON sw.company_id = company.id | ||||
|  |                 LEFT JOIN | ||||
|  |                     stock_location sld_dest ON sm.location_dest_id = sld_dest.id | ||||
|  |                 LEFT JOIN | ||||
|  |                     stock_location sld_src ON sm.location_id = sld_src.id | ||||
|  |                 WHERE | ||||
|  |                     sm.state = 'done' | ||||
|  |                         """ | ||||
|  |         params = [ | ||||
|  |             start_date, start_date, end_date, end_date, start_date, end_date, | ||||
|  |             start_date, start_date, end_date, end_date | ||||
|  |         ] | ||||
|  |         sub_queries = [] | ||||
|  |         sub_params = [] | ||||
|  |         param_count = 0 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category_id for category_id in category_ids] | ||||
|  |             query += "pt.categ_id IN %s" | ||||
|  |             params.append(tuple(category_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             query += f" AND company.id IN %s" | ||||
|  |             sub_params.append(tuple(company_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if warehouse_ids: | ||||
|  |             query += f" AND sw.id IN %s" | ||||
|  |             sub_params.append(tuple(warehouse_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if sub_queries: | ||||
|  |             query += " AND " + " AND ".join(sub_queries) | ||||
|  |         query += """ | ||||
|  |                             GROUP BY pp.id, pt.categ_id, | ||||
|  |                     CASE | ||||
|  |                         WHEN pp.default_code IS NOT NULL  | ||||
|  |                             THEN CONCAT(pp.default_code,' - ',pt.name) | ||||
|  |                         ELSE | ||||
|  |                             pt.name | ||||
|  |                     END , pc.complete_name, company.id, sw.id | ||||
|  |                         ) AS subquery | ||||
|  |                         """ | ||||
|  |         self.env.cr.execute(query, tuple(params + sub_params)) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         for fsn_data in result_data: | ||||
|  |             if fsn_data.get('fsn_classification') == str(fsn): | ||||
|  |                 filtered_product_stock.append(fsn_data) | ||||
|  |         if fsn == 'All' and not result_data: | ||||
|  |             raise ValidationError("No corresponding data to print") | ||||
|  |         elif fsn != 'All' and filtered_product_stock == []: | ||||
|  |             raise ValidationError("No corresponding data to print") | ||||
|  |         return { | ||||
|  |             'doc_ids': docids, | ||||
|  |             'doc_model': | ||||
|  |                 'report.inventory_advanced_reports.report_inventory_fsn', | ||||
|  |             'data': values, | ||||
|  |             'options': result_data if fsn == 'All' else filtered_product_stock, | ||||
|  |         } | ||||
| @ -0,0 +1,91 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  | <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_fsn_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory FSN Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_fsn</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_fsn</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_fsn</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_fsn_report"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_fsn"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <br/><br/><br/><br/> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory FSN Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div> | ||||
|  |                     <div> | ||||
|  |                         <t t-if="data.get('start_date') and data.get('end_date')"> | ||||
|  |                             <strong> | ||||
|  |                                 Start Date: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['start_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 End Date: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['end_date']"/> | ||||
|  |                         </t> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">OPENING STOCK</th> | ||||
|  |                             <th align="center">CLOSING STOCK</th> | ||||
|  |                             <th align="center">AVERAGE STOCK</th> | ||||
|  |                             <th align="center">SALES</th> | ||||
|  |                             <th align="center">TURNOVER RATIO</th> | ||||
|  |                             <th align="center">FSN CLASSIFICATION</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['opening_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['closing_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['average_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['turnover_ratio']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['fsn_classification']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,260 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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, fields, models | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class FsnXyzReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_fsn_xyz' | ||||
|  |     _description = 'FSN-XYZ Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         if data is None or not isinstance(data, dict): | ||||
|  |             raise ValueError("Invalid or missing data for the report") | ||||
|  |         product_ids = data.get('product_ids', []) | ||||
|  |         category_ids = data.get('category_ids', []) | ||||
|  |         company_ids = data.get('company_ids', []) | ||||
|  |         warehouse_ids = data.get('warehouse_ids', []) | ||||
|  |         start_date = data.get('start_date') | ||||
|  |         end_date = data.get('end_date') | ||||
|  |         fsn = data.get('fsn') | ||||
|  |         xyz = data.get('xyz') | ||||
|  |         if not start_date or not end_date: | ||||
|  |             raise ValueError( | ||||
|  |                 "Missing start_date or end_date in the data") | ||||
|  |         start_date = fields.datetime.strptime(start_date, '%Y-%m-%d') | ||||
|  |         end_date = fields.datetime.strptime(end_date, '%Y-%m-%d') | ||||
|  |         filtered_product_stock = [] | ||||
|  |         query = """ | ||||
|  |                     SELECT | ||||
|  |                         product_id, | ||||
|  |         product_code_and_name, | ||||
|  |         category_id, | ||||
|  |         category_name, | ||||
|  |         company_id, | ||||
|  |         warehouse_id, | ||||
|  |         opening_stock, | ||||
|  |         closing_stock, | ||||
|  |         sales, | ||||
|  |         average_stock, | ||||
|  |         current_stock, | ||||
|  |         stock_value, | ||||
|  |         stock_percentage, | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END AS turnover_ratio, | ||||
|  |                         CASE | ||||
|  |                             WHEN | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0  | ||||
|  |                                     THEN  | ||||
|  |                                     ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END > 3 THEN 'Fast Moving' | ||||
|  |                             WHEN | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0  | ||||
|  |                                     THEN  | ||||
|  |                                     ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END >= 1 AND | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0  | ||||
|  |                                     THEN  | ||||
|  |                                     ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END <= 3 THEN 'Slow Moving' | ||||
|  |                             ELSE 'Non Moving' | ||||
|  |                         END AS fsn_classification, | ||||
|  |                         SUM(stock_percentage) OVER (ORDER BY stock_value DESC)  | ||||
|  |                         AS cumulative_stock_percentage, | ||||
|  |                         CASE | ||||
|  |             WHEN SUM(stock_percentage) OVER (ORDER BY stock_value DESC) < 70  | ||||
|  |             THEN 'X' | ||||
|  |             WHEN SUM(stock_percentage) OVER (ORDER BY stock_value DESC) >= 70  | ||||
|  |             AND SUM(stock_percentage) OVER (ORDER BY stock_value DESC) <= 90  | ||||
|  |             THEN 'Y' | ||||
|  |             ELSE 'Z' | ||||
|  |         END AS xyz_classification, | ||||
|  |         CONCAT( | ||||
|  |             CASE | ||||
|  |                 WHEN | ||||
|  |                     CASE | ||||
|  |                         WHEN sales > 0  | ||||
|  |                         THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                         ELSE 0 | ||||
|  |                     END > 3 THEN 'F' | ||||
|  |                 WHEN | ||||
|  |                     CASE | ||||
|  |                         WHEN sales > 0  | ||||
|  |                         THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                         ELSE 0 | ||||
|  |                     END >= 1 AND | ||||
|  |                     CASE | ||||
|  |                         WHEN sales > 0  | ||||
|  |                         THEN ROUND((sales / NULLIF(average_stock, 0)), 2) | ||||
|  |                         ELSE 0 | ||||
|  |                     END <= 3 THEN 'S' | ||||
|  |                 ELSE 'N' | ||||
|  |             END, | ||||
|  |             CASE | ||||
|  |                 WHEN SUM(stock_percentage) OVER (ORDER BY stock_value DESC) < 70  | ||||
|  |                 THEN 'X' | ||||
|  |                 WHEN SUM(stock_percentage)  | ||||
|  |                 OVER (ORDER BY stock_value DESC) >= 70 AND SUM(stock_percentage)  | ||||
|  |                 OVER (ORDER BY stock_value DESC) <= 90 THEN 'Y' | ||||
|  |                 ELSE 'Z' | ||||
|  |             END | ||||
|  |         ) AS combined_classification | ||||
|  |             FROM | ||||
|  |                 (SELECT | ||||
|  |                     pp.id AS product_id, | ||||
|  |                     pt.categ_id AS category_id, | ||||
|  |                     CASE | ||||
|  |                         WHEN pp.default_code IS NOT NULL  | ||||
|  |                             THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                             pt.name) | ||||
|  |                         ELSE | ||||
|  |                             pt.name | ||||
|  |                     END AS product_code_and_name,  | ||||
|  |                     pc.complete_name AS category_name, | ||||
|  |                     company.id AS company_id, | ||||
|  |                     sw.id AS warehouse_id, | ||||
|  |                     SUM(svl.remaining_qty) AS current_stock, | ||||
|  |                     SUM(svl.remaining_value) AS stock_value, | ||||
|  |                     COALESCE(ROUND((SUM(svl.remaining_value) /  | ||||
|  |                     NULLIF(SUM(SUM(svl.remaining_value))  | ||||
|  |                     OVER (), 0)) * 100, 2),0) AS stock_percentage, | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal' | ||||
|  |                      THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)) AS opening_stock, | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal' | ||||
|  |                      THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)) AS closing_stock, | ||||
|  |                     SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                     AND sld_dest.usage = 'customer'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                     ((SUM(CASE WHEN sm.date <= %s  | ||||
|  |                     AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END))+ | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)))/2 AS average_stock | ||||
|  |                 FROM | ||||
|  |             stock_move sm | ||||
|  |         JOIN | ||||
|  |             product_product pp ON sm.product_id = pp.id | ||||
|  |         JOIN | ||||
|  |             product_template pt ON pp.product_tmpl_id = pt.id | ||||
|  |         JOIN | ||||
|  |             product_category pc ON pt.categ_id = pc.id | ||||
|  |         JOIN | ||||
|  |             res_company company ON company.id = sm.company_id | ||||
|  |         JOIN | ||||
|  |             stock_warehouse sw ON sw.company_id = company.id | ||||
|  |         JOIN | ||||
|  |             stock_valuation_layer svl ON svl.stock_move_id = sm.id     | ||||
|  |         LEFT JOIN | ||||
|  |             stock_location sld_dest ON sm.location_dest_id = sld_dest.id | ||||
|  |         LEFT JOIN | ||||
|  |             stock_location sld_src ON sm.location_id = sld_src.id | ||||
|  |         WHERE | ||||
|  |             sm.state = 'done' | ||||
|  |             AND pp.active = TRUE | ||||
|  |             AND pt.active = TRUE | ||||
|  |             AND pt.type = 'product' | ||||
|  |             AND svl.remaining_value IS NOT NULL | ||||
|  |         """ | ||||
|  |         params = [ | ||||
|  |             start_date, start_date, end_date, end_date, start_date, end_date, | ||||
|  |             start_date, start_date, end_date, end_date | ||||
|  |         ] | ||||
|  |         sub_queries = [] | ||||
|  |         sub_params = [] | ||||
|  |         param_count = 0 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category_id for category_id in category_ids] | ||||
|  |             query += "pt.categ_id IN %s" | ||||
|  |             params.append(tuple(category_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             query += f" AND sm.company_id IN %s" | ||||
|  |             sub_params.append(tuple(company_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if warehouse_ids: | ||||
|  |             query += f" AND sw.id IN %s" | ||||
|  |             sub_params.append(tuple(warehouse_ids)) | ||||
|  |             param_count += 1 | ||||
|  |         if sub_queries: | ||||
|  |             query += " AND " + " AND ".join(sub_queries) | ||||
|  |         query += """ | ||||
|  |                     GROUP BY | ||||
|  |             pp.id,pt.name, pt.categ_id,pc.complete_name, company.id, sw.id   | ||||
|  |     ) AS subquery | ||||
|  |     ORDER BY stock_value DESC | ||||
|  |                     """ | ||||
|  |         self.env.cr.execute(query, tuple(params + sub_params)) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         for fsn_data in result_data: | ||||
|  |             if ( | ||||
|  |                     (fsn == 'All' and xyz == 'All') or | ||||
|  |                     (fsn == 'All' and fsn_data.get('xyz_classification') == str( | ||||
|  |                         xyz)) or | ||||
|  |                     (xyz == 'All' and fsn_data.get('fsn_classification') == str( | ||||
|  |                         fsn)) or | ||||
|  |                     (fsn_data.get('fsn_classification') == str( | ||||
|  |                         fsn) and fsn_data.get('xyz_classification') == str(xyz)) | ||||
|  |             ): | ||||
|  |                 filtered_product_stock.append(fsn_data) | ||||
|  |         if (fsn == 'All' or xyz == 'All') and not result_data: | ||||
|  |             raise ValidationError("No corresponding data to print") | ||||
|  |         elif not filtered_product_stock: | ||||
|  |             raise ValidationError("No corresponding data to print") | ||||
|  |         return { | ||||
|  |             'doc_ids': docids, | ||||
|  |             'doc_model': | ||||
|  |                 'report.inventory_advanced_reports.report_inventory_fsn_xyz', | ||||
|  |             'data': values, | ||||
|  |             'options': filtered_product_stock, | ||||
|  |         } | ||||
| @ -0,0 +1,102 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  | <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_fsn_xyz_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory FSN XYZ Report</field> | ||||
|  |         <field name="model"> | ||||
|  |             report.inventory_advanced_reports.report_inventory_fsn_xyz | ||||
|  |         </field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_fsn_xyz</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_fsn_xyz</field> | ||||
|  |         <field name="paperformat_id" ref="base.paperformat_euro"/> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_fsn_xyz_report"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Report for the report--> | ||||
|  |     <template id="report_inventory_fsn_xyz"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |             <br/><br/><br/><br/> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory FSN-XYZ Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div> | ||||
|  |                     <div> | ||||
|  |                         <t t-if="data.get('start_date') and data.get('end_date')"> | ||||
|  |                             <strong> | ||||
|  |                                 Start Date: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['start_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 End Date: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['end_date']"/> | ||||
|  |                         </t> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">AVERAGE STOCK</th> | ||||
|  |                             <th align="center">SALES</th> | ||||
|  |                             <th align="center">TURNOVER RATIO</th> | ||||
|  |                             <th align="center">CURRENT STOCK</th> | ||||
|  |                             <th align="center">STOCK VALUE</th> | ||||
|  |                             <th align="center">FSN CLASSIFICATION</th> | ||||
|  |                             <th align="center">XYZ CLASSIFICATION</th> | ||||
|  |                             <th align="center">FSN-XYZ CLASSIFICATION</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['average_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['turnover_ratio']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['current_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['stock_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['fsn_classification']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['xyz_classification']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['combined_classification']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,274 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class OutOfStockReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_out_of_stock' | ||||
|  |     _description = 'OutOfStock Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         if data is None or not isinstance(data, dict): | ||||
|  |             raise ValueError("Invalid or missing data for the report") | ||||
|  |         product_ids = data.get('product_ids', []) | ||||
|  |         category_ids = data.get('category_ids', []) | ||||
|  |         company_ids = data.get('company_ids', []) | ||||
|  |         warehouse_ids = data.get('warehouse_ids', []) | ||||
|  |         inventory_for_next_x_days = data.get('inventory_for_next_x_days', []) | ||||
|  |         start_date = data.get('start_date') | ||||
|  |         end_date = data.get('end_date') | ||||
|  |         if not start_date or not end_date: | ||||
|  |             raise ValueError( | ||||
|  |                 "Missing start_date or end_date in the data") | ||||
|  |         query = """ | ||||
|  |                     SELECT | ||||
|  |                         product_id, | ||||
|  |                         product_code_and_name, | ||||
|  |                         category_id, | ||||
|  |                         category_name, | ||||
|  |                         company_id, | ||||
|  |                         current_stock, | ||||
|  |                         warehouse_id, | ||||
|  |                         incoming_quantity, | ||||
|  |                         outgoing_quantity, | ||||
|  |                         virtual_stock, | ||||
|  |                         sales, | ||||
|  |                         ads, | ||||
|  |                         advance_stock_days, | ||||
|  |                         ROUND(advance_stock_days * ads, 0) AS demanded_quantity, | ||||
|  |                         ROUND(CASE | ||||
|  |                         WHEN ads = 0 THEN virtual_stock / 0.001 | ||||
|  |                         ELSE virtual_stock / ads | ||||
|  |                     END,0) AS in_stock_days, | ||||
|  |                     ROUND(CASE | ||||
|  |                         WHEN ads = 0 THEN GREATEST(advance_stock_days -  | ||||
|  |                         ROUND(virtual_stock / 0.001, 2), 0) | ||||
|  |                         ELSE GREATEST(advance_stock_days -  | ||||
|  |                         ROUND(virtual_stock / ads, 2), 0) | ||||
|  |                     END ,0) AS out_of_stock_days, | ||||
|  |                     ROUND( | ||||
|  |                             CASE | ||||
|  |                             WHEN advance_stock_days = 0 THEN 0 | ||||
|  |                             ELSE | ||||
|  |                                 CASE | ||||
|  |                                 WHEN ads = 0 THEN GREATEST(advance_stock_days -  | ||||
|  |                                 ROUND(virtual_stock / 0.001, 2), 0) | ||||
|  |                                 ELSE GREATEST(advance_stock_days -  | ||||
|  |                                 ROUND(virtual_stock / ads, 2), 0) | ||||
|  |                                 END | ||||
|  |                             END, 2 | ||||
|  |                             ) AS out_of_stock_ratio, | ||||
|  |                                         ROUND( | ||||
|  |                                             CASE | ||||
|  |                                             WHEN ads = 0  | ||||
|  |                                             THEN  | ||||
|  |                                             GREATEST(advance_stock_days -  | ||||
|  |                                             ROUND(virtual_stock / 0.001, 2), 0) | ||||
|  |                                             ELSE GREATEST(advance_stock_days -  | ||||
|  |                                             ROUND(virtual_stock / ads, 2), 0) | ||||
|  |                                             END * ads, 0 | ||||
|  |                                         ) AS out_of_stock_qty, | ||||
|  |                                         ROUND( | ||||
|  |                                 CASE | ||||
|  |                                 WHEN virtual_stock = 0 THEN 0  | ||||
|  |                                 ELSE sales / virtual_stock | ||||
|  |                                 END, 2 | ||||
|  |                                 ) AS turnover_ratio, | ||||
|  |                         CASE | ||||
|  |                             WHEN | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0 THEN  | ||||
|  |                                     ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END > 3 THEN 'Fast Moving' | ||||
|  |                             WHEN | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0 THEN  | ||||
|  |                                     ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END >= 1 AND | ||||
|  |                                 CASE | ||||
|  |                                     WHEN sales > 0 THEN  | ||||
|  |                                     ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                                     ELSE 0 | ||||
|  |                                 END <= 3 THEN 'Slow Moving' | ||||
|  |                             ELSE 'Non Moving' | ||||
|  |                         END AS fsn_classification | ||||
|  |                     FROM( | ||||
|  |                         SELECT  | ||||
|  |                             CASE | ||||
|  |                                 WHEN pp.default_code IS NOT NULL  | ||||
|  |                                     THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                                     pt.name) | ||||
|  |                                 ELSE | ||||
|  |                                     pt.name | ||||
|  |                             END AS product_code_and_name, | ||||
|  |                             company.id AS company_id, | ||||
|  |                             company.name AS company_name, | ||||
|  |                             sm.product_id AS product_id, | ||||
|  |                             pc.id AS category_id, | ||||
|  |                             pc.complete_name AS category_name, | ||||
|  |                             sw.id AS warehouse_id, | ||||
|  |                                     SUM(CASE | ||||
|  |                         WHEN sld_dest.usage = 'internal' AND sm.state IN  | ||||
|  |                         ('assigned', 'confirmed', 'waiting')  | ||||
|  |                         THEN sm.product_uom_qty | ||||
|  |                         ELSE 0 | ||||
|  |                     END) AS incoming_quantity, | ||||
|  |                     SUM(CASE | ||||
|  |                         WHEN sld_src.usage = 'internal' AND sm.state IN  | ||||
|  |                         ('assigned', 'confirmed', 'waiting')  | ||||
|  |                         THEN sm.product_uom_qty | ||||
|  |                         ELSE 0 | ||||
|  |                     END) AS outgoing_quantity, | ||||
|  |                     SUM(CASE | ||||
|  |                         WHEN sld_dest.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                         THEN sm.product_uom_qty | ||||
|  |                         ELSE 0 | ||||
|  |                     END) - | ||||
|  |                     SUM(CASE | ||||
|  |                         WHEN sld_src.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                         THEN sm.product_uom_qty | ||||
|  |                         ELSE 0 | ||||
|  |                     END) AS current_stock, | ||||
|  |                     SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) - | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END)+ | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) - | ||||
|  |             SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) AS virtual_stock, | ||||
|  |                      SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                      AND sld_dest.usage = 'customer'  | ||||
|  |                      THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                             ROUND(SUM(CASE | ||||
|  |                 WHEN sm.date BETWEEN %s AND %s AND sld_src.usage = 'internal'  | ||||
|  |                 AND sm.state = 'done' THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |             END) / ((date %s - date %s)+1), 2) AS ads, | ||||
|  |             %s AS advance_stock_days | ||||
|  |                     FROM stock_move sm | ||||
|  |                     INNER JOIN product_product pp ON pp.id = sm.product_id | ||||
|  |                     INNER JOIN product_template pt ON pt.id = pp.product_tmpl_id | ||||
|  |                     INNER JOIN res_company company ON company.id = sm.company_id | ||||
|  |                     INNER JOIN stock_warehouse sw ON sw.company_id = company.id | ||||
|  |                     INNER JOIN product_category pc ON pc.id = pt.categ_id | ||||
|  |                     LEFT JOIN ( | ||||
|  |                         SELECT sm.id AS move_id, usage | ||||
|  |                         FROM stock_location sld | ||||
|  |                         INNER JOIN stock_move sm ON sld.id = sm.location_dest_id | ||||
|  |                     ) sld_dest ON sm.id = sld_dest.move_id | ||||
|  |                     LEFT JOIN ( | ||||
|  |                         SELECT sm.id AS move_id, usage | ||||
|  |                         FROM stock_location sld | ||||
|  |                         INNER JOIN stock_move sm ON sld.id = sm.location_id | ||||
|  |                     ) sld_src ON sm.id = sld_src.move_id | ||||
|  |                     WHERE pp.active = TRUE | ||||
|  |                             AND pt.active = TRUE | ||||
|  |                             AND pt.type = 'product' | ||||
|  |                                 """ | ||||
|  |         params = [ | ||||
|  |             start_date, end_date, | ||||
|  |             start_date, end_date, | ||||
|  |             end_date, start_date, | ||||
|  |             inventory_for_next_x_days | ||||
|  |         ] | ||||
|  |         sub_queries = [] | ||||
|  |         sub_params = [] | ||||
|  |         param_count = 0 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category for category in category_ids] | ||||
|  |             params.append(category_ids) | ||||
|  |             query += "(pt.categ_id = ANY(%s))" | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company for company in company_ids] | ||||
|  |             query += " AND (sm.company_id = ANY(%s))" | ||||
|  |             sub_params.append(company_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if warehouse_ids: | ||||
|  |             warehouse_ids = [warehouse for warehouse in warehouse_ids] | ||||
|  |             query += " AND (sw.id = ANY(%s))" | ||||
|  |             sub_params.append(warehouse_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if sub_queries: | ||||
|  |             query += " AND " + " AND ".join(sub_queries) | ||||
|  |         query += """ GROUP BY pp.id, pt.name, pc.id, company.id, sm.product_id,  | ||||
|  |         sw.id) AS sub_query """ | ||||
|  |         self.env.cr.execute(query, tuple(params + sub_params)) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         for data in result_data: | ||||
|  |             product_id = data.get('product_id') | ||||
|  |             out_of_stock_qty = data.get('out_of_stock_qty') | ||||
|  |             total_value = sum( | ||||
|  |                 item.get('out_of_stock_qty', 0) for item in result_data) | ||||
|  |             if total_value: | ||||
|  |                 out_of_stock_qty_percentage = \ | ||||
|  |                     (out_of_stock_qty / total_value) * 100 | ||||
|  |             else: | ||||
|  |                 out_of_stock_qty_percentage = 0.0 | ||||
|  |             data['out_of_stock_qty_percentage'] = round( | ||||
|  |                 out_of_stock_qty_percentage, 2) | ||||
|  |             cost = self.env['product.product'].search([ | ||||
|  |                 ('id', '=', product_id)]).standard_price | ||||
|  |             data['cost'] = cost | ||||
|  |             data['out_of_stock_value'] = out_of_stock_qty * cost | ||||
|  |         if result_data: | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': | ||||
|  |                     'report.inventory_advanced_reports.' | ||||
|  |                     'report_inventory_out_of_stock', | ||||
|  |                 'data': values, | ||||
|  |                 'options': result_data, | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
| @ -0,0 +1,158 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  | <!--    Custom paper format for the report--> | ||||
|  |     <record id="paperformat_inventory_reports" model="report.paperformat"> | ||||
|  |         <field name="name">Over Stock</field> | ||||
|  |         <field name="default" eval="True"/> | ||||
|  |         <field name="format">custom</field> | ||||
|  |         <field name="page_height">297</field> | ||||
|  |         <field name="page_width">500</field> | ||||
|  |         <field name="orientation">Landscape</field> | ||||
|  |         <field name="margin_top">30</field> | ||||
|  |         <field name="margin_bottom">23</field> | ||||
|  |         <field name="margin_left">5</field> | ||||
|  |         <field name="margin_right">5</field> | ||||
|  |         <field name="header_line" eval="False"/> | ||||
|  |         <field name="header_spacing">20</field> | ||||
|  |         <field name="dpi">90</field> | ||||
|  |     </record> | ||||
|  | <!--    Action for the report --> | ||||
|  |     <record id="report_inventory_out_of_stock_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory Out Of Stock Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_out_of_stock</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_out_of_stock</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_out_of_stock</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_out_of_stock_report"/> | ||||
|  |         <field name="paperformat_id" | ||||
|  |                ref="inventory_advanced_reports.paperformat_inventory_reports"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_out_of_stock"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory Out Of Stock Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div> | ||||
|  |                     <div> | ||||
|  |                         <t t-if="data.get('start_date') and data.get('end_date')"> | ||||
|  |                             <strong> | ||||
|  |                                 Sales History from: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['start_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 Sales History Upto: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['end_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 Inventory Analysis For Next: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['inventory_for_next_x_days']"/> | ||||
|  |                             days | ||||
|  |                         </t> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">CURRENT STOCK</th> | ||||
|  |                             <th align="center">INCOMING</th> | ||||
|  |                             <th align="center">OUTGOING</th> | ||||
|  |                             <th align="center">VIRTUAL STOCK</th> | ||||
|  |                             <th align="center">SALES</th> | ||||
|  |                             <th align="center">ADS</th> | ||||
|  |                             <th align="center">DEMANDED QTY</th> | ||||
|  |                             <th align="center">IN STOCK DAYS</th> | ||||
|  |                             <th align="center">OUT OF STOCK DAYS</th> | ||||
|  |                             <th align="center">OUT OF STOCK RATIO</th> | ||||
|  |                             <th align="center">COST PRICE</th> | ||||
|  |                             <th align="center">OUT OF STOCK QTY</th> | ||||
|  |                             <th align="center">OUT OF STOCK QTY(%)</th> | ||||
|  |                             <th align="center">OUT OF STOCK VALUE(%)</th> | ||||
|  |                             <th align="center">TURNOVER RATIO(%)</th> | ||||
|  |                             <th align="center">FSN CLASSIFICATION</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['current_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['incoming_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['outgoing_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['virtual_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['ads']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['demanded_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['in_stock_days']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['out_of_stock_days']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['out_of_stock_ratio']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['cost']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['out_of_stock_qty']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['out_of_stock_qty_percentage']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['out_of_stock_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['turnover_ratio']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['fsn_classification']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,303 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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, fields, models | ||||
|  | 
 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class OverStockReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_over_stock' | ||||
|  |     _description = 'Over Stock Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         if data is None or not isinstance(data, dict): | ||||
|  |             raise ValueError("Invalid or missing data for the report") | ||||
|  |         product_ids = data.get('product_ids', []) | ||||
|  |         category_ids = data.get('category_ids', []) | ||||
|  |         company_ids = data.get('company_ids', []) | ||||
|  |         warehouse_ids = data.get('warehouse_ids', []) | ||||
|  |         inventory_for_next_x_days = data.get('inventory_for_next_x_days', []) | ||||
|  |         start_date = data.get('start_date') | ||||
|  |         end_date = data.get('end_date') | ||||
|  |         if not start_date or not end_date: | ||||
|  |             raise ValueError( | ||||
|  |                 "Missing start_date or end_date in the data") | ||||
|  |         processed_product_ids = [] | ||||
|  |         filtered_result_data = [] | ||||
|  |         query = """ | ||||
|  |                     SELECT | ||||
|  |                             product_id, | ||||
|  |                             product_code_and_name, | ||||
|  |                             category_id, | ||||
|  |                             category_name, | ||||
|  |                             company_id, | ||||
|  |                             current_stock, | ||||
|  |                             warehouse_id, | ||||
|  |                             incoming_quantity, | ||||
|  |                             outgoing_quantity, | ||||
|  |                             virtual_stock, | ||||
|  |                             sales, | ||||
|  |                             ads, | ||||
|  |                             advance_stock_days, | ||||
|  |                             ROUND(advance_stock_days * ads, 0)  | ||||
|  |                             AS demanded_quantity, | ||||
|  |                             ROUND(CASE | ||||
|  |                         WHEN ads = 0 THEN virtual_stock / 0.001 | ||||
|  |                         ELSE virtual_stock / ads | ||||
|  |                     END,0) AS in_stock_days, | ||||
|  |                     ROUND(virtual_stock-(ads*advance_stock_days),0)  | ||||
|  |                     AS over_stock_qty, | ||||
|  |                 ROUND( | ||||
|  |         CASE | ||||
|  |         WHEN virtual_stock = 0 THEN 0  | ||||
|  |         ELSE sales / virtual_stock | ||||
|  |         END, 2 | ||||
|  |     ) AS turnover_ratio, | ||||
|  |                 CASE | ||||
|  |                     WHEN | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END > 3 THEN 'Fast Moving' | ||||
|  |                     WHEN | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END >= 1 AND | ||||
|  |                         CASE | ||||
|  |                             WHEN sales > 0  | ||||
|  |                             THEN ROUND((sales / NULLIF(virtual_stock, 0)), 2) | ||||
|  |                             ELSE 0 | ||||
|  |                         END <= 3 THEN 'Slow Moving' | ||||
|  |                     ELSE 'Non Moving' | ||||
|  |                 END AS fsn_classification | ||||
|  |                 FROM( | ||||
|  |                 SELECT  | ||||
|  |                     CASE | ||||
|  |                         WHEN pp.default_code IS NOT NULL  | ||||
|  |                             THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                             pt.name) | ||||
|  |                         ELSE | ||||
|  |                             pt.name | ||||
|  |                     END AS product_code_and_name, | ||||
|  |                     company.id AS company_id, | ||||
|  |                     company.name AS company_name, | ||||
|  |                     sm.product_id AS product_id, | ||||
|  |                     pc.id AS category_id, | ||||
|  |                     pc.complete_name AS category_name, | ||||
|  |                     sw.id AS warehouse_id, | ||||
|  |                             SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) AS incoming_quantity, | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) AS outgoing_quantity, | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) - | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) AS current_stock, | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) - | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state = 'done'  | ||||
|  |                 THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END)+ | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_dest.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) - | ||||
|  |                 SUM(CASE | ||||
|  |                 WHEN sld_src.usage = 'internal' AND sm.state  | ||||
|  |                 IN ('assigned', 'confirmed', 'waiting') THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) AS virtual_stock, | ||||
|  |                 SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                 AND sld_dest.usage = 'customer'  | ||||
|  |                 THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                     ROUND(SUM(CASE | ||||
|  |                 WHEN sm.date BETWEEN %s AND %s AND sld_src.usage = 'internal'  | ||||
|  |                 AND sm.state = 'done' THEN sm.product_uom_qty | ||||
|  |                 ELSE 0 | ||||
|  |                 END) / ((date %s - date %s)+1), 2) AS ads, | ||||
|  |                 %s AS advance_stock_days | ||||
|  |                 FROM stock_move sm | ||||
|  |                 INNER JOIN product_product pp ON pp.id = sm.product_id | ||||
|  |                 INNER JOIN product_template pt ON pt.id = pp.product_tmpl_id | ||||
|  |                 INNER JOIN res_company company ON company.id = sm.company_id | ||||
|  |                 INNER JOIN stock_warehouse sw ON sw.company_id = company.id | ||||
|  |                 INNER JOIN product_category pc ON pc.id = pt.categ_id | ||||
|  |                 LEFT JOIN ( | ||||
|  |                     SELECT sm.id AS move_id, usage | ||||
|  |                     FROM stock_location sld | ||||
|  |                     INNER JOIN stock_move sm ON sld.id = sm.location_dest_id | ||||
|  |                 ) sld_dest ON sm.id = sld_dest.move_id | ||||
|  |                 LEFT JOIN ( | ||||
|  |                     SELECT sm.id AS move_id, usage | ||||
|  |                     FROM stock_location sld | ||||
|  |                     INNER JOIN stock_move sm ON sld.id = sm.location_id | ||||
|  |                 ) sld_src ON sm.id = sld_src.move_id | ||||
|  |                 WHERE pp.active = TRUE | ||||
|  |                         AND pt.active = TRUE | ||||
|  |                         AND pt.type = 'product' | ||||
|  |                         """ | ||||
|  |         params = [ | ||||
|  |             start_date, end_date, | ||||
|  |             start_date, end_date, | ||||
|  |             end_date, start_date, | ||||
|  |             inventory_for_next_x_days | ||||
|  |         ] | ||||
|  |         sub_queries = [] | ||||
|  |         sub_params = [] | ||||
|  |         param_count = 0 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             query += "pp.id = ANY(%s)" | ||||
|  |             params.append(product_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids and category_ids: | ||||
|  |             query += " OR " | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category for category in category_ids] | ||||
|  |             params.append(category_ids) | ||||
|  |             query += "(pt.categ_id = ANY(%s))" | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += ")" | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company for company in company_ids] | ||||
|  |             query += " AND (sm.company_id = ANY(%s))" | ||||
|  |             sub_params.append(company_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if warehouse_ids: | ||||
|  |             warehouse_ids = [warehouse for warehouse in warehouse_ids] | ||||
|  |             query += " AND (sw.id = ANY(%s))" | ||||
|  |             sub_params.append(warehouse_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if sub_queries: | ||||
|  |             query += " AND " + " AND ".join(sub_queries) | ||||
|  |         query += """ GROUP BY pp.id, pt.name, pc.id, company.id, sm.product_id,  | ||||
|  |         sw.id) AS sub_query """ | ||||
|  |         self.env.cr.execute(query, tuple(params + sub_params)) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         for data in result_data: | ||||
|  |             product_id = data.get('product_id') | ||||
|  |             if product_id not in processed_product_ids: | ||||
|  |                 processed_product_ids.append( | ||||
|  |                     product_id) | ||||
|  |                 filtered_result_data.append(data) | ||||
|  |         for data in filtered_result_data: | ||||
|  |             over_stock_qty = data.get('over_stock_qty') | ||||
|  |             product_id = data.get('product_id') | ||||
|  |             total_qty = sum( | ||||
|  |                 item.get('over_stock_qty', 0) for item in filtered_result_data) | ||||
|  |             if total_qty: | ||||
|  |                 over_stock_qty_percentage = \ | ||||
|  |                     (over_stock_qty / total_qty) * 100 | ||||
|  |             else: | ||||
|  |                 over_stock_qty_percentage = 0.0 | ||||
|  |             data['over_stock_qty_percentage'] = round( | ||||
|  |                 over_stock_qty_percentage, 2) | ||||
|  |             cost = self.env['product.product'].search([ | ||||
|  |                 ('id', '=', product_id)]).standard_price | ||||
|  |             data['cost'] = cost | ||||
|  |             data['over_stock_value'] = over_stock_qty * cost | ||||
|  |             latest_po = '' | ||||
|  |             confirmed_po = self.env['purchase.order.line'].search([ | ||||
|  |                 ('product_id', '=', product_id), | ||||
|  |                 ('state', '=', 'purchase'), | ||||
|  |             ]) | ||||
|  |             for po in confirmed_po: | ||||
|  |                 if latest_po: | ||||
|  |                     if latest_po.date_approve < po.date_approve: | ||||
|  |                         latest_po = po | ||||
|  |                 else: | ||||
|  |                     latest_po = po | ||||
|  |             data['po_qty'] = 0 | ||||
|  |             data['po_price_total'] = 0 | ||||
|  |             if latest_po: | ||||
|  |                 start_date = fields.Datetime.from_string(start_date).date() | ||||
|  |                 end_date = fields.Datetime.from_string(end_date).date() | ||||
|  |                 po_date = fields.Datetime.from_string(latest_po.order_id.date_approve) | ||||
|  |                 if start_date <= po_date.date() <= end_date: | ||||
|  |                     data['po_qty'] += latest_po.product_qty | ||||
|  |                     data['po_price_total'] += latest_po.price_total | ||||
|  |                     data['po_date'] = po_date | ||||
|  |                     data['po_currency'] = latest_po.currency_id.name | ||||
|  |                     data['po_partner'] = latest_po.partner_id.name | ||||
|  |                 else: | ||||
|  |                     data['po_price_total'] = None | ||||
|  |                     data['po_qty'] = None | ||||
|  |                     data['po_currency'] = None | ||||
|  |                     data['po_partner'] = None | ||||
|  |                     data[ | ||||
|  |                         'po_date'] = None | ||||
|  |             else: | ||||
|  |                 data['po_price_total'] = None | ||||
|  |                 data['po_qty'] = None | ||||
|  |                 data['po_date'] = None | ||||
|  |                 data['po_partner'] = None | ||||
|  |                 data['po_currency'] = None | ||||
|  |         total_value = sum( | ||||
|  |             item.get('over_stock_value', 0) for item in filtered_result_data) | ||||
|  |         for data in filtered_result_data: | ||||
|  |             over_stock_value = data.get('over_stock_value') | ||||
|  |             if total_value: | ||||
|  |                 over_stock_value_percentage = \ | ||||
|  |                     (over_stock_value / total_value) * 100 | ||||
|  |             else: | ||||
|  |                 over_stock_value_percentage = 0.0 | ||||
|  |             data['over_stock_value_percentage'] = round( | ||||
|  |                 over_stock_value_percentage, 2) | ||||
|  |         if filtered_result_data: | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': | ||||
|  |                     'report.inventory_advanced_reports.' | ||||
|  |                     'report_inventory_over_stock', | ||||
|  |                 'data': values, | ||||
|  |                 'options': filtered_result_data, | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
| @ -0,0 +1,153 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  | <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_over_stock_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory Over Stock Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_over_stock</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_over_stock</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_over_stock</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_over_stock_report"/> | ||||
|  |         <field name="paperformat_id" | ||||
|  |                ref="inventory_advanced_reports.paperformat_inventory_reports"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_over_stock"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory Over Stock Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div> | ||||
|  |                     <div> | ||||
|  |                         <t t-if="data.get('start_date') and data.get('end_date')"> | ||||
|  |                             <strong> | ||||
|  |                                 Sales History from: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['start_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 Sales History Up to: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['end_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 Inventory Analysis For Next: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['inventory_for_next_x_days']"/>days | ||||
|  |                         </t> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">CURRENT STOCK</th> | ||||
|  |                             <th align="center">INCOMING</th> | ||||
|  |                             <th align="center">OUTGOING</th> | ||||
|  |                             <th align="center">VIRTUAL STOCK</th> | ||||
|  |                             <th align="center">SALES</th> | ||||
|  |                             <th align="center">ADS</th> | ||||
|  |                             <th align="center">DEMANDED QTY</th> | ||||
|  |                             <th align="center">COVERAGE DAYS</th> | ||||
|  |                             <th align="center">OVER STOCK QTY</th> | ||||
|  |                             <th align="center">OVER STOCK QTY(%)</th> | ||||
|  |                             <th align="center">OVER STOCK VALUE</th> | ||||
|  |                             <th align="center">OVER STOCK VALUE(%)</th> | ||||
|  |                             <th align="center">TURNOVER RATIO(%)</th> | ||||
|  |                             <th align="center">FSN CLASSIFICATION</th> | ||||
|  |                             <th align="center">LAST PO DATE</th> | ||||
|  |                             <th align="center">LAST PO QTY</th> | ||||
|  |                             <th align="center">LAST PO PRICE</th> | ||||
|  |                             <th align="center">CURRENCY</th> | ||||
|  |                             <th align="center">PARTNER</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['current_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['incoming_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['outgoing_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['virtual_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['ads']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['demanded_quantity']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['in_stock_days']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['over_stock_qty']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['over_stock_qty_percentage']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['over_stock_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['over_stock_value_percentage']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['turnover_ratio']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['fsn_classification']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['po_date']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['po_qty']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['po_price_total']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['po_currency']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['po_partner']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,201 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class StockMovementReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_movement' | ||||
|  |     _description = 'Stock Movement Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         if data is None or not isinstance(data, dict): | ||||
|  |             raise ValueError("Invalid or missing data for the report") | ||||
|  |         product_ids = data.get('product_ids', []) | ||||
|  |         category_ids = data.get('category_ids', []) | ||||
|  |         company_ids = data.get('company_ids', []) | ||||
|  |         warehouse_ids = data.get('warehouse_ids', []) | ||||
|  |         report_up_to_certain_date = data.get('report_up_to_certain_date', []) | ||||
|  |         up_to_certain_date = data.get('up_to_certain_date', []) | ||||
|  |         start_date = data.get('start_date') | ||||
|  |         end_date = data.get('end_date') | ||||
|  |         if not start_date or not end_date: | ||||
|  |             raise ValueError( | ||||
|  |                 "Missing start_date or end_date in the data") | ||||
|  |         query = """ | ||||
|  |                         SELECT | ||||
|  |                             pp.id as product_id, | ||||
|  |                             CASE | ||||
|  |                             WHEN pp.default_code IS NOT NULL  | ||||
|  |                                 THEN CONCAT(pp.default_code, ' - ',  | ||||
|  |                                 pt.name) | ||||
|  |                             ELSE | ||||
|  |                                 pt.name | ||||
|  |                             END AS product_code_and_name,  | ||||
|  |                             pc.complete_name AS category_name, | ||||
|  |                             company.name AS company_name,                        | ||||
|  |                 """ | ||||
|  |         if report_up_to_certain_date: | ||||
|  |             query += """ | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'inventory'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS opening_stock, | ||||
|  |                     (SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END)) AS closing_stock, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'customer'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'customer'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS sales_return, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'supplier'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS purchase, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'supplier'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS purchase_return, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS internal_in, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'internal'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS internal_out, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'inventory'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS adj_in, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'inventory'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS adj_out, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s  | ||||
|  |                     AND sld_dest.usage = 'production'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS production_in, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'production'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS production_out, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_dest.usage = 'transit'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS transit_in, | ||||
|  |                     SUM(CASE WHEN sm.date <= %s AND sld_src.usage = 'transit'  | ||||
|  |                     THEN sm.product_uom_qty ELSE 0 END) AS transit_out | ||||
|  |                     """ | ||||
|  |             params = [up_to_certain_date] * 15 | ||||
|  |         else: | ||||
|  |             query += """ | ||||
|  |                         (SUM(CASE WHEN sm.date <= %s  | ||||
|  |                         AND sld_dest.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                         SUM(CASE WHEN sm.date <= %s  | ||||
|  |                         AND sld_src.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END)) AS opening_stock, | ||||
|  |                         (SUM(CASE WHEN sm.date <= %s  | ||||
|  |                         AND sld_dest.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) - | ||||
|  |                         SUM(CASE WHEN sm.date <= %s  | ||||
|  |                         AND sld_src.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END)) AS closing_stock, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_dest.usage = 'customer'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS sales, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_src.usage = 'customer'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS sales_return, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_src.usage = 'supplier'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS purchase, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_dest.usage = 'supplier'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS purchase_return, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_dest.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS internal_in, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_src.usage = 'internal'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS internal_out, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_dest.usage = 'inventory'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS adj_in, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_src.usage = 'inventory'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS adj_out, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s  | ||||
|  |                         AND %s AND sld_dest.usage = 'production'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS production_in, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s AND %s  | ||||
|  |                         AND sld_src.usage = 'production'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS production_out, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s  | ||||
|  |                         AND %s AND sld_dest.usage = 'transit'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS transit_in, | ||||
|  |                         SUM(CASE WHEN sm.date BETWEEN %s  | ||||
|  |                         AND %s AND sld_src.usage = 'transit'  | ||||
|  |                         THEN sm.product_uom_qty ELSE 0 END) AS transit_out | ||||
|  |                     """ | ||||
|  |             params = [start_date, start_date, end_date, end_date, start_date, | ||||
|  |                       end_date, start_date, end_date, start_date, end_date, | ||||
|  |                       start_date, end_date, start_date, end_date, start_date, | ||||
|  |                       end_date, start_date, end_date, start_date, end_date, | ||||
|  |                       start_date, end_date, start_date, end_date, start_date, | ||||
|  |                       end_date, start_date, end_date] | ||||
|  |         query += """ | ||||
|  |                     FROM stock_move sm | ||||
|  |                     INNER JOIN product_product pp ON pp.id = sm.product_id | ||||
|  |                     INNER JOIN product_template pt ON pt.id = pp.product_tmpl_id | ||||
|  |                     INNER JOIN res_company company ON company.id = sm.company_id | ||||
|  |                     INNER JOIN stock_warehouse sw ON sw.company_id = company.id | ||||
|  |                     INNER JOIN product_category pc ON pc.id = pt.categ_id    | ||||
|  |                 """ | ||||
|  |         query += """ | ||||
|  |                     LEFT JOIN stock_location sld_dest  | ||||
|  |                     ON sm.location_dest_id = sld_dest.id | ||||
|  |                     LEFT JOIN stock_location sld_src  | ||||
|  |                     ON sm.location_id = sld_src.id | ||||
|  |                     WHERE | ||||
|  |                 sm.state = 'done' | ||||
|  |                         """ | ||||
|  |         sub_queries = [] | ||||
|  |         if product_ids: | ||||
|  |             product_ids = [product_id for product_id in product_ids] | ||||
|  |             sub_queries.append("pp.id = ANY(%s)") | ||||
|  |             params.append(product_ids) | ||||
|  |         if category_ids: | ||||
|  |             category_ids = [category for category in category_ids] | ||||
|  |             sub_queries.append("pt.categ_id = ANY(%s)") | ||||
|  |             params.append(category_ids) | ||||
|  |         if sub_queries: | ||||
|  |             query += " AND (" + " OR ".join(sub_queries) + ")" | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company for company in company_ids] | ||||
|  |             query += " AND sm.company_id = ANY(%s)" | ||||
|  |             params.append(company_ids) | ||||
|  |         if warehouse_ids: | ||||
|  |             warehouse_ids = [warehouse for warehouse in warehouse_ids] | ||||
|  |             query += " AND sw.id = ANY(%s)" | ||||
|  |             params.append(warehouse_ids) | ||||
|  |         query += """ | ||||
|  |                     GROUP BY pp.id,pt.name,pc.complete_name,company.name | ||||
|  |                 """ | ||||
|  |         self.env.cr.execute(query, params) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         if result_data: | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': 'inventory.overstock.report', | ||||
|  |                 'data': values, | ||||
|  |                 'options': result_data, | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
| @ -0,0 +1,140 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8" ?> | ||||
|  | <odoo> | ||||
|  |     <!--    Action for the report--> | ||||
|  |     <record id="report_inventory_stock_movement_action" | ||||
|  |             model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory Stock Movement Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_movement</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_movement</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_movement</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_stock_movement_report"/> | ||||
|  |         <field name="paperformat_id" | ||||
|  |                ref="inventory_advanced_reports.paperformat_inventory_reports"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  |     <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_movement"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory Stock Movement Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <br/> | ||||
|  |                 <div> | ||||
|  |                     <div> | ||||
|  |                         <t t-if="data.get('start_date') and data.get('end_date') and not data['up_to_certain_date']"> | ||||
|  |                             <strong> | ||||
|  |                                 Date from: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['start_date']"/> | ||||
|  |                             <br/> | ||||
|  |                             <strong> | ||||
|  |                                 Date to: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['end_date']"/> | ||||
|  |                         </t> | ||||
|  |                         <t t-if="data.get('up_to_certain_date')"> | ||||
|  |                             <strong> | ||||
|  |                                 Stock Movements Up To: | ||||
|  |                             </strong> | ||||
|  |                             <span t-esc="data['up_to_certain_date']"/> | ||||
|  |                         </t> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <br/> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">COMPANY</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">OPENING STOCK</th> | ||||
|  |                             <th align="center">SALES</th> | ||||
|  |                             <th align="center">SALES RETURN</th> | ||||
|  |                             <th align="center">PURCHASE</th> | ||||
|  |                             <th align="center">PURCHASE RETURN</th> | ||||
|  |                             <th align="center">INTERNAL IN</th> | ||||
|  |                             <th align="center">INTERNAL OUT</th> | ||||
|  |                             <th align="center">ADJUSTMENT IN</th> | ||||
|  |                             <th align="center">ADJUSTMENT OUT</th> | ||||
|  |                             <th align="center">PRODUCTION IN</th> | ||||
|  |                             <th align="center">PRODUCTION OUT</th> | ||||
|  |                             <th align="center">TRANSIT IN</th> | ||||
|  |                             <th align="center">TRANSIT OUT</th> | ||||
|  |                             <th align="center">CLOSING STOCK</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['company_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['opening_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['sales_return']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['purchase']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['purchase_return']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['internal_in']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['internal_out']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['adj_in']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['adj_out']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['production_in']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['production_out']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['transit_in']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['transit_out']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['closing_stock']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| @ -0,0 +1,142 @@ | |||||
|  | # -*- coding: utf-8 -*- | ||||
|  | ############################################################################### | ||||
|  | # | ||||
|  | #  Cybrosys Technologies Pvt. Ltd. | ||||
|  | # | ||||
|  | #  Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) | ||||
|  | #  Author:  Gayathri V (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 | ||||
|  | from odoo.exceptions import ValidationError | ||||
|  | 
 | ||||
|  | 
 | ||||
|  | class XyzReport(models.AbstractModel): | ||||
|  |     """Create an abstract model for passing reporting values""" | ||||
|  |     _name = 'report.inventory_advanced_reports.report_inventory_xyz' | ||||
|  |     _description = 'XYZ Report' | ||||
|  | 
 | ||||
|  |     @api.model | ||||
|  |     def _get_report_values(self, docids, data=None): | ||||
|  |         """This function has working in get the pdf report.""" | ||||
|  |         values = data | ||||
|  |         product_ids = data['product_ids'] | ||||
|  |         category_ids = data['category_ids'] | ||||
|  |         company_ids = data['company_ids'] | ||||
|  |         xyz = data['xyz'] | ||||
|  |         params = [] | ||||
|  |         param_count = 0 | ||||
|  |         query = """ | ||||
|  |         SELECT  | ||||
|  |             CASE | ||||
|  |                 WHEN pp.default_code IS NOT NULL  | ||||
|  |                     THEN CONCAT(pp.default_code, ' - ', pt.name) | ||||
|  |                 ELSE | ||||
|  |                     pt.name | ||||
|  |             END AS product_code_and_name, | ||||
|  |             svl.company_id, | ||||
|  |             company.name AS company_name, | ||||
|  |             svl.product_id, | ||||
|  |             pt.categ_id AS product_category_id, | ||||
|  |             c.complete_name AS category_name, | ||||
|  |             SUM(svl.remaining_qty) AS current_stock, | ||||
|  |             SUM(svl.remaining_value) AS stock_value | ||||
|  |         FROM stock_valuation_layer svl | ||||
|  |         INNER JOIN res_company company ON company.id = svl.company_id | ||||
|  |         INNER JOIN product_product pp ON pp.id = svl.product_id | ||||
|  |         INNER JOIN product_template pt ON pt.id = pp.product_tmpl_id | ||||
|  |         INNER JOIN product_category c ON c.id = pt.categ_id | ||||
|  |         WHERE pp.active = TRUE | ||||
|  |             AND pt.active = TRUE | ||||
|  |             AND pt.type = 'product' | ||||
|  |             AND svl.remaining_value IS NOT NULL | ||||
|  |         """ | ||||
|  |         if company_ids: | ||||
|  |             company_ids = [company_id for company_id in company_ids] | ||||
|  |             query += f" AND (company.id IS NULL OR company.id = ANY(%s))" | ||||
|  |             params.append(company_ids) | ||||
|  |             param_count += 1 | ||||
|  |         if product_ids or category_ids: | ||||
|  |             query += " AND (" | ||||
|  |             if product_ids: | ||||
|  |                 product_ids = [product_id for product_id in product_ids] | ||||
|  |                 query += f"pp.id = ANY(%s)" | ||||
|  |                 params.append(product_ids) | ||||
|  |                 param_count += 1 | ||||
|  |             if product_ids and category_ids: | ||||
|  |                 query += " OR " | ||||
|  |             if category_ids: | ||||
|  |                 category_ids = [category_id for category_id in | ||||
|  |                                 category_ids] | ||||
|  |                 query += f"c.id = ANY(%s)" | ||||
|  |                 params.append(category_ids) | ||||
|  |                 param_count += 1 | ||||
|  |             query += ")" | ||||
|  |         query += """ | ||||
|  |                 GROUP BY  | ||||
|  |                     svl.company_id, | ||||
|  |             company.name, | ||||
|  |             svl.product_id, | ||||
|  |             CASE | ||||
|  |                 WHEN pp.default_code IS NOT NULL  | ||||
|  |                     THEN CONCAT(pp.default_code, ' - ', pt.name) | ||||
|  |                 ELSE | ||||
|  |                     pt.name | ||||
|  |             END, | ||||
|  |             pt.categ_id, | ||||
|  |             c.complete_name | ||||
|  |             ORDER BY SUM(svl.remaining_value) DESC; | ||||
|  |                 """ | ||||
|  |         self.env.cr.execute(query, params) | ||||
|  |         result_data = self.env.cr.dictfetchall() | ||||
|  |         total_current_value = 0 | ||||
|  |         cumulative_stock = 0 | ||||
|  |         filtered_stock = [] | ||||
|  |         for row in result_data: | ||||
|  |             current_value = row.get('stock_value') | ||||
|  |             total_current_value += current_value | ||||
|  |         for value in result_data: | ||||
|  |             current_value = value.get('stock_value') | ||||
|  |             if total_current_value != 0 and current_value: | ||||
|  |                 stock_percentage = (current_value / total_current_value) * 100 | ||||
|  |             else: | ||||
|  |                 stock_percentage = 0.0 | ||||
|  |             value['stock_percentage'] = round(stock_percentage, 2) | ||||
|  |             cumulative_stock += value['stock_percentage'] | ||||
|  |             value['cumulative_stock_percentage'] = round(cumulative_stock, 2) | ||||
|  |             if cumulative_stock < 70: | ||||
|  |                 xyz_classification = 'X' | ||||
|  |             elif 70 <= cumulative_stock <= 90: | ||||
|  |                 xyz_classification = 'Y' | ||||
|  |             else: | ||||
|  |                 xyz_classification = 'Z' | ||||
|  |             value['xyz_classification'] = xyz_classification | ||||
|  |         if result_data: | ||||
|  |             for xyz_class in result_data: | ||||
|  |                 if xyz_class.get('xyz_classification') == str(xyz): | ||||
|  |                     filtered_stock.append(xyz_class) | ||||
|  |             if xyz == 'All' and not result_data: | ||||
|  |                 raise ValidationError("No corresponding data to print") | ||||
|  |             elif xyz != 'All' and filtered_stock == []: | ||||
|  |                 raise ValidationError("No corresponding data to print") | ||||
|  |             return { | ||||
|  |                 'doc_ids': docids, | ||||
|  |                 'doc_model': | ||||
|  |                     'report.inventory_advanced_reports.report_inventory_xyz', | ||||
|  |                 'data': values, | ||||
|  |                 'options': result_data if xyz == 'All' else filtered_stock, | ||||
|  |             } | ||||
|  |         else: | ||||
|  |             raise ValidationError("No records found for the given criteria!") | ||||
| @ -0,0 +1,67 @@ | |||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||
|  | <odoo> | ||||
|  |     <!--    action for the report--> | ||||
|  |     <record id="report_inventory_xyz_action" model="ir.actions.report"> | ||||
|  |         <field name="name">Inventory XYZ Report</field> | ||||
|  |         <field name="model">report.inventory_advanced_reports.report_inventory_xyz</field> | ||||
|  |         <field name="report_type">qweb-pdf</field> | ||||
|  |         <field name="report_name">inventory_advanced_reports.report_inventory_xyz</field> | ||||
|  |         <field name="report_file">inventory_advanced_reports.report_inventory_xyz</field> | ||||
|  |         <field name="binding_model_id" | ||||
|  |                ref="model_inventory_aging_report"/> | ||||
|  |         <field name="binding_type">report</field> | ||||
|  |     </record> | ||||
|  | <!--    Template for the report--> | ||||
|  |     <template id="report_inventory_xyz"> | ||||
|  |         <t t-call="web.html_container"> | ||||
|  |             <t t-call="web.external_layout"> | ||||
|  |                 <div class="page"> | ||||
|  |                     <div class="text-center"> | ||||
|  |                         <h1>Inventory XYZ Report</h1> | ||||
|  |                     </div> | ||||
|  |                 </div> | ||||
|  |                 <table class="table table-condensed table-bordered table-striped"> | ||||
|  |                     <thead> | ||||
|  |                         <tr> | ||||
|  |                             <th align="center">Sl.no</th> | ||||
|  |                             <th align="center">PRODUCT</th> | ||||
|  |                             <th align="center">CATEGORY</th> | ||||
|  |                             <th align="center">CURRENT STOCK</th> | ||||
|  |                             <th align="center">STOCK VALUE</th> | ||||
|  |                             <th align="center">STOCK VALUE(%)</th> | ||||
|  |                             <th align="center">CUMULATIVE STOCK(%)</th> | ||||
|  |                             <th align="center">XYZ CLASSIFICATION</th> | ||||
|  |                         </tr> | ||||
|  |                     </thead> | ||||
|  |                     <tbody> | ||||
|  |                         <tr t-foreach="options" t-as="new"> | ||||
|  |                             <t t-log="new"/> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new_index + 1"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['product_code_and_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['category_name']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['current_stock']"/> | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['stock_value']"/> | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['stock_percentage']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td><t t-esc="new['cumulative_stock_percentage']"/>% | ||||
|  |                             </td> | ||||
|  |                             <td> | ||||
|  |                                 <t t-esc="new['xyz_classification']"/> | ||||
|  |                             </td> | ||||
|  |                         </tr> | ||||
|  |                     </tbody> | ||||
|  |                 </table> | ||||
|  |             </t> | ||||
|  |         </t> | ||||
|  |     </template> | ||||
|  | </odoo> | ||||
| 
 | 
| After Width: | Height: | Size: 3.6 KiB | 
| After Width: | Height: | Size: 310 B | 
| After Width: | Height: | Size: 1.3 KiB | 
| After Width: | Height: | Size: 1.4 KiB | 
| After Width: | Height: | Size: 576 B | 
| After Width: | Height: | Size: 733 B | 
| After Width: | Height: | Size: 911 B | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 3.4 KiB | 
| After Width: | Height: | Size: 673 B | 
| After Width: | Height: | Size: 878 B | 
| After Width: | Height: | Size: 653 B | 
| After Width: | Height: | Size: 905 B | 
| After Width: | Height: | Size: 839 B | 
| After Width: | Height: | Size: 427 B | 
| After Width: | Height: | Size: 627 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 988 B | 
| After Width: | Height: | Size: 1.2 KiB | 
| After Width: | Height: | Size: 1.5 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 1.9 KiB | 
| After Width: | Height: | Size: 1.1 KiB | 
| After Width: | Height: | Size: 2.1 KiB | 
| After Width: | Height: | Size: 4.4 KiB | 
| After Width: | Height: | Size: 589 B | 
| After Width: | Height: | Size: 3.4 KiB | 
| After Width: | Height: | Size: 1.7 KiB | 
| After Width: | Height: | Size: 2.3 KiB | 
| After Width: | Height: | Size: 1.5 KiB | 
| After Width: | Height: | Size: 967 B | 
| After Width: | Height: | Size: 1.6 KiB | 
| After Width: | Height: | Size: 3.8 KiB | 
| After Width: | Height: | Size: 5.0 KiB | 
| After Width: | Height: | Size: 55 KiB | 
| After Width: | Height: | Size: 75 KiB | 
| After Width: | Height: | Size: 45 KiB | 
| After Width: | Height: | Size: 49 KiB | 
| After Width: | Height: | Size: 51 KiB | 
| After Width: | Height: | Size: 47 KiB | 
| After Width: | Height: | Size: 59 KiB | 
| After Width: | Height: | Size: 60 KiB | 
| After Width: | Height: | Size: 60 KiB | 
| After Width: | Height: | Size: 60 KiB | 
| After Width: | Height: | Size: 56 KiB | 
| After Width: | Height: | Size: 58 KiB | 
| After Width: | Height: | Size: 45 KiB | 
| After Width: | Height: | Size: 76 KiB | 
| After Width: | Height: | Size: 49 KiB | 
| After Width: | Height: | Size: 77 KiB | 
| After Width: | Height: | Size: 33 KiB | 
| After Width: | Height: | Size: 56 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 64 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 19 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 48 KiB | 
| After Width: | Height: | Size: 80 KiB | 
| After Width: | Height: | Size: 53 KiB | 
| After Width: | Height: | Size: 60 KiB | 
| After Width: | Height: | Size: 65 KiB | 
| After Width: | Height: | Size: 57 KiB | 
| After Width: | Height: | Size: 67 KiB | 
| After Width: | Height: | Size: 86 KiB | 
| After Width: | Height: | Size: 57 KiB | 
| After Width: | Height: | Size: 42 KiB | 
| After Width: | Height: | Size: 57 KiB | 
| After Width: | Height: | Size: 69 KiB | 
| After Width: | Height: | Size: 40 KiB | 
| After Width: | Height: | Size: 119 KiB | 
| After Width: | Height: | Size: 67 KiB | 
| After Width: | Height: | Size: 52 KiB | 
| After Width: | Height: | Size: 67 KiB | 
| After Width: | Height: | Size: 34 KiB |