Browse Source

Nov 30: [ADD] Initial Commit 'inventory_advanced_reports'

pull/254/merge
Cybrosys Technologies 5 months ago
parent
commit
181d7dbdc2
  1. 49
      inventory_advanced_reports/README.rst
  2. 24
      inventory_advanced_reports/__init__.py
  3. 67
      inventory_advanced_reports/__manifest__.py
  4. 22
      inventory_advanced_reports/controllers/__init__.py
  5. 58
      inventory_advanced_reports/controllers/inventory_advanced_reports.py
  6. 7
      inventory_advanced_reports/doc/RELEASE_NOTES.md
  7. 29
      inventory_advanced_reports/report/__init__.py
  8. 180
      inventory_advanced_reports/report/age_breakdown_report.py
  9. 106
      inventory_advanced_reports/report/age_breakdown_report_views.xml
  10. 172
      inventory_advanced_reports/report/aging_report.py
  11. 75
      inventory_advanced_reports/report/aging_report_views.xml
  12. 200
      inventory_advanced_reports/report/fsn_report.py
  13. 91
      inventory_advanced_reports/report/fsn_report_views.xml
  14. 260
      inventory_advanced_reports/report/fsn_xyz_report.py
  15. 102
      inventory_advanced_reports/report/fsn_xyz_report_views.xml
  16. 274
      inventory_advanced_reports/report/out_of_stock_report.py
  17. 158
      inventory_advanced_reports/report/out_of_stock_report_views.xml
  18. 303
      inventory_advanced_reports/report/over_stock_report.py
  19. 153
      inventory_advanced_reports/report/over_stock_report_views.xml
  20. 201
      inventory_advanced_reports/report/stock_movement_report.py
  21. 140
      inventory_advanced_reports/report/stock_movement_report_views.xml
  22. 142
      inventory_advanced_reports/report/xyz_report.py
  23. 67
      inventory_advanced_reports/report/xyz_report_views.xml
  24. 16
      inventory_advanced_reports/security/ir.model.access.csv
  25. BIN
      inventory_advanced_reports/static/description/assets/icons/check.png
  26. BIN
      inventory_advanced_reports/static/description/assets/icons/chevron.png
  27. BIN
      inventory_advanced_reports/static/description/assets/icons/cogs.png
  28. BIN
      inventory_advanced_reports/static/description/assets/icons/consultation.png
  29. BIN
      inventory_advanced_reports/static/description/assets/icons/ecom-black.png
  30. BIN
      inventory_advanced_reports/static/description/assets/icons/education-black.png
  31. BIN
      inventory_advanced_reports/static/description/assets/icons/hotel-black.png
  32. BIN
      inventory_advanced_reports/static/description/assets/icons/license.png
  33. BIN
      inventory_advanced_reports/static/description/assets/icons/lifebuoy.png
  34. BIN
      inventory_advanced_reports/static/description/assets/icons/logo.png
  35. BIN
      inventory_advanced_reports/static/description/assets/icons/manufacturing-black.png
  36. BIN
      inventory_advanced_reports/static/description/assets/icons/pos-black.png
  37. BIN
      inventory_advanced_reports/static/description/assets/icons/puzzle.png
  38. BIN
      inventory_advanced_reports/static/description/assets/icons/restaurant-black.png
  39. BIN
      inventory_advanced_reports/static/description/assets/icons/service-black.png
  40. BIN
      inventory_advanced_reports/static/description/assets/icons/trading-black.png
  41. BIN
      inventory_advanced_reports/static/description/assets/icons/training.png
  42. BIN
      inventory_advanced_reports/static/description/assets/icons/update.png
  43. BIN
      inventory_advanced_reports/static/description/assets/icons/user.png
  44. BIN
      inventory_advanced_reports/static/description/assets/icons/wrench.png
  45. BIN
      inventory_advanced_reports/static/description/assets/misc/categories.png
  46. BIN
      inventory_advanced_reports/static/description/assets/misc/check-box.png
  47. BIN
      inventory_advanced_reports/static/description/assets/misc/compass.png
  48. BIN
      inventory_advanced_reports/static/description/assets/misc/corporate.png
  49. BIN
      inventory_advanced_reports/static/description/assets/misc/customer-support.png
  50. BIN
      inventory_advanced_reports/static/description/assets/misc/cybrosys-logo.png
  51. BIN
      inventory_advanced_reports/static/description/assets/misc/features.png
  52. BIN
      inventory_advanced_reports/static/description/assets/misc/logo.png
  53. BIN
      inventory_advanced_reports/static/description/assets/misc/pictures.png
  54. BIN
      inventory_advanced_reports/static/description/assets/misc/pie-chart.png
  55. BIN
      inventory_advanced_reports/static/description/assets/misc/reports-icon.png
  56. BIN
      inventory_advanced_reports/static/description/assets/misc/right-arrow.png
  57. BIN
      inventory_advanced_reports/static/description/assets/misc/star.png
  58. BIN
      inventory_advanced_reports/static/description/assets/misc/support.png
  59. BIN
      inventory_advanced_reports/static/description/assets/misc/whatsapp.png
  60. BIN
      inventory_advanced_reports/static/description/assets/modules/1.png
  61. BIN
      inventory_advanced_reports/static/description/assets/modules/2.png
  62. BIN
      inventory_advanced_reports/static/description/assets/modules/3.png
  63. BIN
      inventory_advanced_reports/static/description/assets/modules/4.png
  64. BIN
      inventory_advanced_reports/static/description/assets/modules/5.png
  65. BIN
      inventory_advanced_reports/static/description/assets/modules/6.png
  66. BIN
      inventory_advanced_reports/static/description/assets/modules/budget_image.png
  67. BIN
      inventory_advanced_reports/static/description/assets/modules/credit_image.png
  68. BIN
      inventory_advanced_reports/static/description/assets/modules/employee_image.png
  69. BIN
      inventory_advanced_reports/static/description/assets/modules/export_image.png
  70. BIN
      inventory_advanced_reports/static/description/assets/modules/gantt_image.png
  71. BIN
      inventory_advanced_reports/static/description/assets/modules/quotation_image.png
  72. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 1.png
  73. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 2.png
  74. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 3.png
  75. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 4.png
  76. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 5.png
  77. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 0.png
  78. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 1.png
  79. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 2.png
  80. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 3.png
  81. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 4.png
  82. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 5.png
  83. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 6.png
  84. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 7.png
  85. BIN
      inventory_advanced_reports/static/description/assets/screenshots/AGING 8.png
  86. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 1.png
  87. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 10.png
  88. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 2.png
  89. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 3.png
  90. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 4.png
  91. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 5.png
  92. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 6.png
  93. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 7.png
  94. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 8.png
  95. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN 9.png
  96. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 1.png
  97. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 2.png
  98. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 3.png
  99. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 4.png
  100. BIN
      inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 5.png

49
inventory_advanced_reports/README.rst

@ -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>`__

24
inventory_advanced_reports/__init__.py

@ -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

67
inventory_advanced_reports/__manifest__.py

@ -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,
}

22
inventory_advanced_reports/controllers/__init__.py

@ -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

58
inventory_advanced_reports/controllers/inventory_advanced_reports.py

@ -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)))

7
inventory_advanced_reports/doc/RELEASE_NOTES.md

@ -0,0 +1,7 @@
## Module <inventory_advanced_reports>
#### 07.06.2024
#### Version 15.0.1.0.0
##### ADD
- Initial Commit for Inventory Advanced Reports

29
inventory_advanced_reports/report/__init__.py

@ -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

180
inventory_advanced_reports/report/age_breakdown_report.py

@ -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)]

106
inventory_advanced_reports/report/age_breakdown_report_views.xml

@ -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>

172
inventory_advanced_reports/report/aging_report.py

@ -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!")

75
inventory_advanced_reports/report/aging_report_views.xml

@ -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>

200
inventory_advanced_reports/report/fsn_report.py

@ -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,
}

91
inventory_advanced_reports/report/fsn_report_views.xml

@ -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>

260
inventory_advanced_reports/report/fsn_xyz_report.py

@ -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,
}

102
inventory_advanced_reports/report/fsn_xyz_report_views.xml

@ -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>

274
inventory_advanced_reports/report/out_of_stock_report.py

@ -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!")

158
inventory_advanced_reports/report/out_of_stock_report_views.xml

@ -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>

303
inventory_advanced_reports/report/over_stock_report.py

@ -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!")

153
inventory_advanced_reports/report/over_stock_report_views.xml

@ -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>

201
inventory_advanced_reports/report/stock_movement_report.py

@ -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!")

140
inventory_advanced_reports/report/stock_movement_report_views.xml

@ -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>

142
inventory_advanced_reports/report/xyz_report.py

@ -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!")

67
inventory_advanced_reports/report/xyz_report_views.xml

@ -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>

16
inventory_advanced_reports/security/ir.model.access.csv

@ -0,0 +1,16 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_inventory_aging_report_user,access.inventory.aging.report.user,model_inventory_aging_report,base.group_user,1,1,1,1
access_inventory_age_data_report_user,access.inventory.age.data.report.user,model_inventory_aging_data_report,base.group_user,1,1,1,1
access_inventory_fsn_report_user,access.inventory.fsn.report.user,model_inventory_fsn_report,base.group_user,1,1,1,1
access_inventory_aging_data_report_user,access.inventory.aging.data.report.user,model_inventory_aging_data_report,base.group_user,1,1,1,1
access_inventory_fsn_data_report_user,access.inventory.fsn.data.report.user,model_inventory_fsn_data_report,base.group_user,1,1,1,1
access_inventory_xyz_report_user,access.inventory.xyz.report.user,model_inventory_xyz_report,base.group_user,1,1,1,1
access_inventory_xyz_data_report_user,access.inventory.xyz.data.report.user,model_inventory_xyz_data_report,base.group_user,1,1,1,1
access_inventory_fsn_xyz_report_user,access.inventory.fsn.xyz.report.user,model_inventory_fsn_xyz_report,base.group_user,1,1,1,1
access_inventory_fsn_xyz_data_report_user,access.inventory.fsn.xyz.data.report.user,model_inventory_fsn_xyz_data_report,base.group_user,1,1,1,1
access_inventory_out_of_stock_report_user,access.inventory.out.of.stock.report.user,model_inventory_out_of_stock_report,base.group_user,1,1,1,1
access_inventory_out_of_stock_data_report_user,access.inventory.out.of.stock.data.report.user,model_inventory_out_of_stock_data_report,base.group_user,1,1,1,1
access_inventory_age_breakdown_report_user,access.inventory.age.breakdown.report.user,model_inventory_age_breakdown_report,base.group_user,1,1,1,1
access_inventory_over_stock_report_user,access.inventory.over.stock.report.user,model_inventory_over_stock_report,base.group_user,1,1,1,1
access_inventory_over_stock_data_report_user,access.inventory.over.stock.data.report.user,model_inventory_over_stock_data_report,base.group_user,1,1,1,1
access_inventory_stock_movement_report_user,access.inventory.stock.movement.report.user,model_inventory_stock_movement_report,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_inventory_aging_report_user access.inventory.aging.report.user model_inventory_aging_report base.group_user 1 1 1 1
3 access_inventory_age_data_report_user access.inventory.age.data.report.user model_inventory_aging_data_report base.group_user 1 1 1 1
4 access_inventory_fsn_report_user access.inventory.fsn.report.user model_inventory_fsn_report base.group_user 1 1 1 1
5 access_inventory_aging_data_report_user access.inventory.aging.data.report.user model_inventory_aging_data_report base.group_user 1 1 1 1
6 access_inventory_fsn_data_report_user access.inventory.fsn.data.report.user model_inventory_fsn_data_report base.group_user 1 1 1 1
7 access_inventory_xyz_report_user access.inventory.xyz.report.user model_inventory_xyz_report base.group_user 1 1 1 1
8 access_inventory_xyz_data_report_user access.inventory.xyz.data.report.user model_inventory_xyz_data_report base.group_user 1 1 1 1
9 access_inventory_fsn_xyz_report_user access.inventory.fsn.xyz.report.user model_inventory_fsn_xyz_report base.group_user 1 1 1 1
10 access_inventory_fsn_xyz_data_report_user access.inventory.fsn.xyz.data.report.user model_inventory_fsn_xyz_data_report base.group_user 1 1 1 1
11 access_inventory_out_of_stock_report_user access.inventory.out.of.stock.report.user model_inventory_out_of_stock_report base.group_user 1 1 1 1
12 access_inventory_out_of_stock_data_report_user access.inventory.out.of.stock.data.report.user model_inventory_out_of_stock_data_report base.group_user 1 1 1 1
13 access_inventory_age_breakdown_report_user access.inventory.age.breakdown.report.user model_inventory_age_breakdown_report base.group_user 1 1 1 1
14 access_inventory_over_stock_report_user access.inventory.over.stock.report.user model_inventory_over_stock_report base.group_user 1 1 1 1
15 access_inventory_over_stock_data_report_user access.inventory.over.stock.data.report.user model_inventory_over_stock_data_report base.group_user 1 1 1 1
16 access_inventory_stock_movement_report_user access.inventory.stock.movement.report.user model_inventory_stock_movement_report base.group_user 1 1 1 1

BIN
inventory_advanced_reports/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
inventory_advanced_reports/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
inventory_advanced_reports/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
inventory_advanced_reports/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
inventory_advanced_reports/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
inventory_advanced_reports/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
inventory_advanced_reports/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
inventory_advanced_reports/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
inventory_advanced_reports/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
inventory_advanced_reports/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
inventory_advanced_reports/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
inventory_advanced_reports/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
inventory_advanced_reports/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
inventory_advanced_reports/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/categories.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/check-box.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/compass.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/corporate.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/customer-support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/cybrosys-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/features.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 589 B

BIN
inventory_advanced_reports/static/description/assets/misc/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/pictures.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/pie-chart.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/reports-icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/right-arrow.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 967 B

BIN
inventory_advanced_reports/static/description/assets/misc/star.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/support.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
inventory_advanced_reports/static/description/assets/misc/whatsapp.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/budget_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/credit_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/employee_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/export_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/gantt_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
inventory_advanced_reports/static/description/assets/modules/quotation_image.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGE BREAKDOWN 5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/AGING 8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 8.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN 9.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
inventory_advanced_reports/static/description/assets/screenshots/FSN XYZ 5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save