diff --git a/sale_report_advanced/README.rst b/sale_report_advanced/README.rst new file mode 100644 index 000000000..d5ad3a5d2 --- /dev/null +++ b/sale_report_advanced/README.rst @@ -0,0 +1,46 @@ +.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg + :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +Advanced Sales Reports +====================== +This Module Helps to Generate Advanced sales reports. + +Configuration +============= +* No additional configurations needed + +License +------- +General Public License, Version 3 (AGPL v3). +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +Developer: (V17) Ayana KP, +Contact: odoo@cybrosys.com + +Contacts +-------- +* Mail Contact : odoo@cybrosys.com +* Website : https://cybrosys.com + +Bug Tracker +----------- +Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. + +Maintainer +========== +.. image:: https://cybrosys.com/images/logo.png + :target: https://cybrosys.com +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit `Our Website `__ + +Further information +=================== +HTML Description: ``__ diff --git a/sale_report_advanced/__init__.py b/sale_report_advanced/__init__.py new file mode 100644 index 000000000..f598f0f01 --- /dev/null +++ b/sale_report_advanced/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +from . import controllers +from . import wizard diff --git a/sale_report_advanced/__manifest__.py b/sale_report_advanced/__manifest__.py new file mode 100644 index 000000000..722d42408 --- /dev/null +++ b/sale_report_advanced/__manifest__.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +{ + 'name': 'Advanced Sales Reports', + 'version': '17.0.1.0.0', + 'category': 'Sales', + 'summary': """This Module Helps to Generate Advanced sales reports""", + 'description': """This module helps you to print reports like Sales Analysis, + Sales By Category, Sales Indent, Sales Invoice ,Product Profit , + Hourly Sales in PDF and XLSX format.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['sale_management', 'account'], + 'data': [ + 'security/ir.model.access.csv', + 'wizard/sale_report_advance_views.xml', + 'wizard/sale_report_invoice_views.xml', + 'wizard/sale_report_analysis_views.xml', + 'wizard/sale_report_weekly_views.xml', + 'wizard/sale_report_category_views.xml', + 'wizard/sale_report_indent_views.xml', + 'views/sale_report_advanced_views.xml', + 'report/sale_advanced_reports.xml', + 'report/invoice_analysis_templates.xml', + 'report/sales_indent_templates.xml', + 'report/sale_profit_templates.xml', + 'report/sales_category_templates.xml', + 'report/sales_analysis_templates.xml', + 'report/sales_weekly_templates.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'sale_report_advanced/static/src/js/action_manager.js', + ], + }, + 'images': ['static/description/banner.png'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/sale_report_advanced/controllers/__init__.py b/sale_report_advanced/controllers/__init__.py new file mode 100644 index 000000000..bd697665c --- /dev/null +++ b/sale_report_advanced/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +from . import sale_report_advanced diff --git a/sale_report_advanced/controllers/sale_report_advanced.py b/sale_report_advanced/controllers/sale_report_advanced.py new file mode 100644 index 000000000..105654107 --- /dev/null +++ b/sale_report_advanced/controllers/sale_report_advanced.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +from odoo import http +from odoo.http import content_disposition, request +from odoo.tools import html_escape + + +class XLSXReportController(http.Controller): + @http.route('/xlsx_reports', type='http', auth='user', methods=['POST'], + csrf=False) + def get_report_xlsx(self, model, options, output_format, report_name): + """Generate an XLSX reports based on the provided data and return + it as a response. + Args: + model (str): The name of the model on which the reports is based. + data (str): The data required for generating the reports. + output_format (str): The desired output format for the reports (e.g., 'xlsx'). + report_name (str): The name to be given to the generated reports file. + Returns: + Response: The generated reports file as a response. + Raises: + Exception: If an error occurs during reports generation. """ + uid = request.session.uid + report_obj = request.env[model].with_user(uid) + 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 e: + se = http.serialize_exception(e) + error = { + 'code': 200, + 'message': 'Odoo Server Error', + 'data': se + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/sale_report_advanced/doc/RELEASE_NOTES.md b/sale_report_advanced/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..5845e1b3b --- /dev/null +++ b/sale_report_advanced/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 12.12.2023 +#### Version 17.0.1.0.0 +##### ADD +- Initial Commit for Advanced Sales Reports diff --git a/sale_report_advanced/report/invoice_analysis_templates.xml b/sale_report_advanced/report/invoice_analysis_templates.xml new file mode 100644 index 000000000..d2c4762a7 --- /dev/null +++ b/sale_report_advanced/report/invoice_analysis_templates.xml @@ -0,0 +1,97 @@ + + + + + diff --git a/sale_report_advanced/report/sale_advanced_reports.xml b/sale_report_advanced/report/sale_advanced_reports.xml new file mode 100644 index 000000000..a43d5028b --- /dev/null +++ b/sale_report_advanced/report/sale_advanced_reports.xml @@ -0,0 +1,46 @@ + + + + + Sales + sale.report.advance + qweb-pdf + sale_report_advanced.sale_report_view + sale_report_advanced.sale_report_view + + + Invoice Analysis + sale.report.invoice + qweb-pdf + sale_report_advanced.invoice_analysis_view + sale_report_advanced.invoice_analysis_view + + + Sales Category + sale.report.category + qweb-pdf + sale_report_advanced.sales_category_view + sale_report_advanced.sales_category_view + + + Product Sales Indent + sale.report.indent + qweb-pdf + sale_report_advanced.sales_indent_view + sale_report_advanced.sales_indent_view + + + Sales Analysis Report + sale.report.analysis + qweb-pdf + sale_report_advanced.sales_analysis_view + sale_report_advanced.sales_analysis_view + + + Hourly Sales Report + sale.report.weekly + qweb-pdf + sale_report_advanced.sales_hourly_view + sale_report_advanced.sales_hourly_view + + diff --git a/sale_report_advanced/report/sale_profit_templates.xml b/sale_report_advanced/report/sale_profit_templates.xml new file mode 100644 index 000000000..5f5593907 --- /dev/null +++ b/sale_report_advanced/report/sale_profit_templates.xml @@ -0,0 +1,353 @@ + + + + + diff --git a/sale_report_advanced/report/sales_analysis_templates.xml b/sale_report_advanced/report/sales_analysis_templates.xml new file mode 100644 index 000000000..fd1c8e1f4 --- /dev/null +++ b/sale_report_advanced/report/sales_analysis_templates.xml @@ -0,0 +1,168 @@ + + + + + diff --git a/sale_report_advanced/report/sales_category_templates.xml b/sale_report_advanced/report/sales_category_templates.xml new file mode 100644 index 000000000..c71fefef6 --- /dev/null +++ b/sale_report_advanced/report/sales_category_templates.xml @@ -0,0 +1,109 @@ + + + + + diff --git a/sale_report_advanced/report/sales_indent_templates.xml b/sale_report_advanced/report/sales_indent_templates.xml new file mode 100644 index 000000000..33bc5fdba --- /dev/null +++ b/sale_report_advanced/report/sales_indent_templates.xml @@ -0,0 +1,59 @@ + + + + + diff --git a/sale_report_advanced/report/sales_weekly_templates.xml b/sale_report_advanced/report/sales_weekly_templates.xml new file mode 100644 index 000000000..b5ab57fab --- /dev/null +++ b/sale_report_advanced/report/sales_weekly_templates.xml @@ -0,0 +1,65 @@ + + + + + diff --git a/sale_report_advanced/security/ir.model.access.csv b/sale_report_advanced/security/ir.model.access.csv new file mode 100644 index 000000000..28e71bf3e --- /dev/null +++ b/sale_report_advanced/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sale_report_advance,access.sale.report.advance,model_sale_report_advance,,1,1,1,1 +access_sale_report_invoice,access.sale.report.invoice,model_sale_report_invoice,,1,1,1,1 +access_sale_report_category,access.sale.report.category,model_sale_report_category,,1,1,1,1 +access_sale_report_indent,access.sale.report.indent,model_sale_report_indent,,1,1,1,1 +access_sale_report_analysis,access.sale.report.analysis,model_sale_report_analysis,,1,1,1,1 +access_sale_report_weekly,access.sale.report.weekly,model_sale_report_weekly,,1,1,1,1 diff --git a/sale_report_advanced/static/description/assets/icons/capture (1).png b/sale_report_advanced/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/capture (1).png differ diff --git a/sale_report_advanced/static/description/assets/icons/check.png b/sale_report_advanced/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/check.png differ diff --git a/sale_report_advanced/static/description/assets/icons/chevron.png b/sale_report_advanced/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/chevron.png differ diff --git a/sale_report_advanced/static/description/assets/icons/cogs.png b/sale_report_advanced/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/cogs.png differ diff --git a/sale_report_advanced/static/description/assets/icons/consultation.png b/sale_report_advanced/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/consultation.png differ diff --git a/sale_report_advanced/static/description/assets/icons/ecom-black.png b/sale_report_advanced/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/ecom-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/education-black.png b/sale_report_advanced/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/education-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/hotel-black.png b/sale_report_advanced/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/hotel-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/img.png b/sale_report_advanced/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/img.png differ diff --git a/sale_report_advanced/static/description/assets/icons/license.png b/sale_report_advanced/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/license.png differ diff --git a/sale_report_advanced/static/description/assets/icons/lifebuoy.png b/sale_report_advanced/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/lifebuoy.png differ diff --git a/sale_report_advanced/static/description/assets/icons/manufacturing-black.png b/sale_report_advanced/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/manufacturing-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/photo-capture.png b/sale_report_advanced/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/photo-capture.png differ diff --git a/sale_report_advanced/static/description/assets/icons/pos-black.png b/sale_report_advanced/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/pos-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/puzzle.png b/sale_report_advanced/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/puzzle.png differ diff --git a/sale_report_advanced/static/description/assets/icons/restaurant-black.png b/sale_report_advanced/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/restaurant-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/service-black.png b/sale_report_advanced/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/service-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/trading-black.png b/sale_report_advanced/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/trading-black.png differ diff --git a/sale_report_advanced/static/description/assets/icons/training.png b/sale_report_advanced/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/training.png differ diff --git a/sale_report_advanced/static/description/assets/icons/update.png b/sale_report_advanced/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/update.png differ diff --git a/sale_report_advanced/static/description/assets/icons/user.png b/sale_report_advanced/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/user.png differ diff --git a/sale_report_advanced/static/description/assets/icons/wrench.png b/sale_report_advanced/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/sale_report_advanced/static/description/assets/icons/wrench.png differ diff --git a/sale_report_advanced/static/description/assets/misc/Cybrosys R.png b/sale_report_advanced/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/sale_report_advanced/static/description/assets/misc/Cybrosys R.png differ diff --git a/sale_report_advanced/static/description/assets/misc/email.svg b/sale_report_advanced/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/phone.svg b/sale_report_advanced/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/sale_report_advanced/static/description/assets/misc/star (1) 2.svg b/sale_report_advanced/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/support (1) 1.svg b/sale_report_advanced/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/support-email.svg b/sale_report_advanced/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/tick-mark.svg b/sale_report_advanced/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/whatsapp 1.svg b/sale_report_advanced/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/misc/whatsapp.svg b/sale_report_advanced/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/sale_report_advanced/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sale_report_advanced/static/description/assets/modules/1.png b/sale_report_advanced/static/description/assets/modules/1.png new file mode 100644 index 000000000..ba1058c42 Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/1.png differ diff --git a/sale_report_advanced/static/description/assets/modules/2.png b/sale_report_advanced/static/description/assets/modules/2.png new file mode 100644 index 000000000..6949185dd Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/2.png differ diff --git a/sale_report_advanced/static/description/assets/modules/3.png b/sale_report_advanced/static/description/assets/modules/3.png new file mode 100644 index 000000000..4e506f79b Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/3.png differ diff --git a/sale_report_advanced/static/description/assets/modules/4.png b/sale_report_advanced/static/description/assets/modules/4.png new file mode 100644 index 000000000..e78427938 Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/4.png differ diff --git a/sale_report_advanced/static/description/assets/modules/5.png b/sale_report_advanced/static/description/assets/modules/5.png new file mode 100755 index 000000000..272ec20f9 Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/5.png differ diff --git a/sale_report_advanced/static/description/assets/modules/6.png b/sale_report_advanced/static/description/assets/modules/6.png new file mode 100644 index 000000000..7d5c3154f Binary files /dev/null and b/sale_report_advanced/static/description/assets/modules/6.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/1.png b/sale_report_advanced/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..e61b47d98 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/1.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/10.png b/sale_report_advanced/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..b4497a0fc Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/10.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/11.png b/sale_report_advanced/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..d863ed76f Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/11.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/12.png b/sale_report_advanced/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..a7193ad2e Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/12.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/13.png b/sale_report_advanced/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..4198b4ca6 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/13.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/14.png b/sale_report_advanced/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..2e8129a0b Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/14.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/15.png b/sale_report_advanced/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..5d5346c03 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/15.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/16.png b/sale_report_advanced/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..e1e9a691e Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/16.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/2.png b/sale_report_advanced/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..926003a42 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/2.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/2023-11-13_10-33.png b/sale_report_advanced/static/description/assets/screenshots/2023-11-13_10-33.png new file mode 100644 index 000000000..4ef3ad91a Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/2023-11-13_10-33.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/3.png b/sale_report_advanced/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..0b7cc8dba Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/3.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/4.png b/sale_report_advanced/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..e251254d6 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/4.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/5.png b/sale_report_advanced/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..3cc72729f Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/5.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/6.png b/sale_report_advanced/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..e9153a395 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/6.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/7.png b/sale_report_advanced/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..f75243590 Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/7.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/8.png b/sale_report_advanced/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..fbf60ac6d Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/8.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/9.png b/sale_report_advanced/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..19150664b Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/9.png differ diff --git a/sale_report_advanced/static/description/assets/screenshots/hero-v17.gif b/sale_report_advanced/static/description/assets/screenshots/hero-v17.gif new file mode 100644 index 000000000..c20533e5f Binary files /dev/null and b/sale_report_advanced/static/description/assets/screenshots/hero-v17.gif differ diff --git a/sale_report_advanced/static/description/banner.png b/sale_report_advanced/static/description/banner.png new file mode 100644 index 000000000..612be4b77 Binary files /dev/null and b/sale_report_advanced/static/description/banner.png differ diff --git a/sale_report_advanced/static/description/icon.png b/sale_report_advanced/static/description/icon.png new file mode 100644 index 000000000..ff5ebf54e Binary files /dev/null and b/sale_report_advanced/static/description/icon.png differ diff --git a/sale_report_advanced/static/description/index.html b/sale_report_advanced/static/description/index.html new file mode 100644 index 000000000..3a140fe78 --- /dev/null +++ b/sale_report_advanced/static/description/index.html @@ -0,0 +1,944 @@ + + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+
+
+
+

+ Advanced Sales Reports

+

+ This Module Helps to Generate Advanced sales reports. +

+
+ +
+
+
+
+
+

+ Key Highlights +

+
+
+
+
+
+ +
+
+

+ Helps to Print Reports.

+

The module helps you to print reports like Sales Analysis, Sales By Category, Sales Indent, Sales + Invoice ,Product Profit ,Hourly Sales in PDF and XLSX format. +

+
+
+
+
+
+
+ +
+
+

+ Print Report in both PDF and XLSX format.

+

Print All Reports in both PDF and XLSX format. +

+
+
+
+
+
+
+ +
+
+

+ Report in Date Range.

+

We can apply + different filters to print reports. +

+
+
+
+
+
+
+ +
+
+

+ Community & Enterprise Support.

+

Available in + Odoo 17.0 Community, Enterprise and Odoo.sh. +

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

+ Menu of All Reports.

+
+
+
+
+
+
+ +
+
+

+ Product Profit Report.

+
+
+
+
+
+
+ +
+
+

+ Product Profit PDF report.

+
+
+
+
+
+
+ +
+
+

+ Product Profit Report XLSX.

+
+
+
+
+
+
+ +
+
+

+ Sales Invoice Analysis Report.

+
+
+
+
+
+
+ +
+
+

+ Sales Invoice Analysis PDF report.

+
+
+
+
+
+
+ +
+
+

+ Sales Invoice Analysis XLSX report.

+
+
+
+
+
+
+ +
+
+

+ Sales Category Report.

+
+
+
+
+
+
+ +
+
+

+ Sales Category PDF report.

+
+
+
+
+
+
+ +
+
+

+ Sales Category XLSX report.

+
+
+
+
+
+
+ +
+
+

+ Sales Indent Report.

+
+
+
+
+
+
+ +
+
+

+ Sales Indent PDF report.

+
+
+
+
+
+
+ +
+
+

+ Sales Indent XLSX report.

+
+
+
+
+
+
+ +
+
+

+ Sales Analysis Report.

+
+
+
+
+
+
+ +
+
+

+ Sales Analysis PDF Report.

+
+
+
+
+
+
+ +
+
+

+ Sales Analysis XLSX Report.

+
+
+
+
+
+
+ +
+
+

+ Hourly Sales Report.

+
+
+
+
+
+
+
    +
  • + Helps you to + Print Reports like Sales Analysis. +
  • +
  • + Sales By + Category Report. +
  • +
  • + Print Report in + both PDF and XLSX format. +
  • + +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on:5th December 2023 +
+

+ + Initial Commit for Advanced Sales Reports.

+
+
+
+
+
+
+
+

+ Related Products

+
+
+ +
+
+

+ Our Services

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

Odoo + Customization

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

Odoo + Implementation

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

Odoo + Support

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

Hire + Odoo Developer

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

Odoo + Integration

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

Odoo + Migration

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

Odoo + Consultancy

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

Odoo + Implementation

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

Odoo + Licensing Consultancy

+
+
+
+
+
+
+

+ Our Industries

+ +
+
+
+
+
+
+ +

Trading

+

Easily procure and sell your products

+
+
+
+
+ +

POS

+

Easy configuration and convivial experience

+
+
+
+
+ +

+ Education

+

A platform for educational management

+
+
+
+
+ +

+ Manufacturing

+

Plan, track and schedule your operations

+
+
+
+
+ +

E-commerce & + Website

+

Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +

Service + Management

+

Keep track of services and invoice

+
+
+
+
+ +

+ Restaurant

+

Run your bar or restaurant methodically

+
+
+
+
+ +

Hotel + Management

+

An all-inclusive hotel management application

+
+
+
+
+
+
+

+ Support

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

Got + questions or need help? Get in touch.

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

Say hi to + us on WhatsApp!

+
+91 + 99456767686 +
+
+
+
+
+
+
+
+
+ + + + + + diff --git a/sale_report_advanced/static/src/js/action_manager.js b/sale_report_advanced/static/src/js/action_manager.js new file mode 100644 index 000000000..af2d1b837 --- /dev/null +++ b/sale_report_advanced/static/src/js/action_manager.js @@ -0,0 +1,19 @@ +/** @odoo-module **/ +import { registry } from "@web/core/registry"; +import { BlockUI } from "@web/core/ui/block_ui"; +import { download } from "@web/core/network/download"; +/**XLSX HandlerThis handler is responsible for generating XLSX reports. +It sends a request to the server to generate the report in XLSX format and +downloads the generated file.@param {Object} action - The action object +containing the report details.@returns {Promise} - A promise that resolves +when the report generation is complete.*/ +registry.category("ir.actions.report handlers").add("xlsx", async function (action) { + if (action.report_type === 'xlsx') { + BlockUI; + await download({ + url: '/xlsx_reports', + data: action.data, + complete: () => unblockUI, + error: (error) => self.call('crash_manager', 'rpc_error', error), + }); + }}); diff --git a/sale_report_advanced/views/sale_report_advanced_views.xml b/sale_report_advanced/views/sale_report_advanced_views.xml new file mode 100644 index 000000000..ad7c94dfd --- /dev/null +++ b/sale_report_advanced/views/sale_report_advanced_views.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + diff --git a/sale_report_advanced/wizard/__init__.py b/sale_report_advanced/wizard/__init__.py new file mode 100644 index 000000000..af8503f0b --- /dev/null +++ b/sale_report_advanced/wizard/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +from . import sale_report_advance +from . import sale_report_invoice +from . import sale_report_category +from . import sale_report_indent +from . import sale_report_analysis +from . import sale_report_weekly diff --git a/sale_report_advanced/wizard/sale_report_advance.py b/sale_report_advanced/wizard/sale_report_advance.py new file mode 100644 index 000000000..560a95756 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_advance.py @@ -0,0 +1,462 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAdvance(models.TransientModel): + """ This transient model is used to create and configure parameters + for generating reports related to sales.""" + _name = "sale.report.advance" + _description = 'Sale Report Advance' + + customer_ids = fields.Many2many('res.partner', + string="Customers", help="Select specific" + " customers for the report.") + product_ids = fields.Many2many('product.product', + string='Products', help="Select specific" + " products for the report.") + from_date = fields.Date(string="Start Date", help="Specify the start date " + "of the report period.") + to_date = fields.Date(string="End Date", help="Specify the end date of the " + "report period.") + type = fields.Selection( + [('customer', 'Customers'), ('product', 'Products'), ('both', 'Both')], + string='Report Print By', default='customer', required=True, + help="Choose the type of report to generate: by Customers, " + "Products, or Both.") + company_ids = fields.Many2many('res.company', string='Companies', + help="Filter the report by " + "selecting specific companies.") + today_date = fields.Date(string='Date', default=fields.Date.today(), + help="Default value is set to today's date" + " for the report.") + + def _get_data(self): + """ Function generating data for report in sale advance report """ + sales_order_line = self.env['sale.order.line'].search( + [('order_id.state', '!=', 'cancel')]) + domain = [('state', '!=', 'cancel')] + if self.from_date: + domain.append(('date_order', '>=', self.from_date)) + if self.to_date: + domain.append(('date_order', '<=', self.to_date)) + if self.company_ids: + domain.append(('company_id', 'in', self.company_ids.ids)) + sales_order = self.env['sale.order'].search(domain) + if not sales_order: + raise ValidationError("No data available for printing.") + result = [] + customers = [] + products = [] + for rec in self.customer_ids: + data = { + 'id': rec, + 'name': rec.name + } + customers.append(data) + for rec in self.product_ids: + data = { + 'id': rec, + 'name': rec.name + } + products.append(data) + margin = 0 + if self.type == 'product': + for rec in products: + for lines in sales_order_line: + if lines.product_id == rec['id']: + profit = round( + lines.product_id.list_price - lines.product_id.standard_price, + 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price,2) + res = { + 'sequence': lines.order_id.name, + 'date': lines.order_id.date_order, + 'product_id': lines.product_id, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': lines.order_id.partner_id.name, + } + result.append(res) + if self.type == 'customer': + for rec in customers: + for so in sales_order: + if so.partner_id == rec['id']: + for lines in so.order_line: + profit = round( + lines.product_id.list_price - lines.product_id.standard_price, + 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner_id': so.partner_id, + } + result.append(res) + if self.type == 'both': + for rec in customers: + for p in products: + for so in sales_order: + if so.partner_id == rec['id']: + for lines in so.order_line: + if lines.product_id == p['id']: + profit = round( + lines.product_id.list_price - lines.product_id.standard_price, + 2) + if lines.product_id.standard_price != 0: + margin = round((profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': so.partner_id.name, + } + result.append(res) + if self.from_date and self.to_date and not self.customer_ids and not self.product_ids: + for so in sales_order: + for lines in so.order_line: + profit = round( + lines.product_id.list_price - lines.product_id.standard_price, + 2) + if lines.product_id.standard_price != 0: + margin = round( + (profit * 100) / lines.product_id.standard_price, 2) + res = { + 'sequence': so.name, + 'date': so.date_order, + 'product': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'cost': lines.product_id.standard_price, + 'price': lines.product_id.list_price, + 'profit': profit, + 'margin': margin, + 'partner': so.partner_id.name, + } + result.append(res) + if not result: + raise ValidationError("No data available for printing.") + datas = { + 'ids': self, + 'model': 'sale.report.advance', + 'form': result, + 'partner_id': customers, + 'product_id': products, + 'start_date': self.from_date, + 'end_date': self.to_date, + 'type': self.type, + 'no_value': False, + } + if self.from_date and self.to_date and not self.customer_ids and not self.product_ids: + datas['no_value'] = True + return datas + + def action_get_report(self): + """ Generate and display a custom sales report. + :return: An action to display the custom sales report. + :rtype: dict """ + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_sale_report').report_action([], + data=datas) + + def action_get_excel_report(self): + """ Generate and return an Excel report for advanced sales reporting. + :return: A dictionary describing the action to be performed for + the Excel report. + :rtype: dict """ + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.advance', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sale Advance Report', + }, + } + + def get_xlsx_report(self, data, response): + """Function for generating xlsx report """ + loaded_data = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record = [] + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:N3', 'Sales Profit Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', loaded_data.get('start_date'), txt) + sheet.write('L6', 'To:', cell_format) + sheet.merge_range('M6:N6', loaded_data.get('end_date'), txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#bbd5fc', + 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#c0dbfa', 'border': 1}) + if loaded_data.get('type') == 'product': + record = loaded_data.get('product_id') + if loaded_data.get('type') == 'customer': + record = loaded_data.get('partner_id') + h_row = 7 + h_col = 9 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if loaded_data.get('type') == 'product' or loaded_data.get( + 'type') == 'customer': + for rec in record: + sheet.merge_range(h_row, h_col - 3, h_row, h_col + 4, + rec['name'], format3) + row = row + count + 3 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 15) + col += 1 + if loaded_data.get('type') == 'product': + sheet.write(row, col, 'Customer', format2) + sheet.set_column('I:I', 20) + col += 1 + elif loaded_data.get('type') == 'customer': + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I', 20) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Cost', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Profit', format2) + col += 1 + sheet.write(row, col, 'Margin(%)', format2) + col += 1 + col = 6 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_cost = 0 + t_price = 0 + t_profit = 0 + t_margin = 0 + t_col = 8 + for val in loaded_data.get('form'): + if loaded_data.get('type') == 'customer': + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, + val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, + val['product'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, + val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], + format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], + format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, + val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, + val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + if loaded_data.get('type') == 'product': + if val['product_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, + val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, + val['partner'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, + val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], + format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], + format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, + val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, + val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format4) + t_col += 1 + sheet.write(t_row, t_col, t_cost, format4) + t_col += 1 + sheet.write(t_row, t_col, t_price, format4) + t_col += 1 + sheet.write(t_row, t_col, t_profit, format4) + t_col += 1 + sheet.write(t_row, t_col, t_margin, format4) + t_col += 1 + h_row = h_row + count + 3 + if loaded_data.get('type') == 'both' or loaded_data.get( + 'no_value') == True: + row += 3 + row_number += 2 + t_qty = 0 + t_cost = 0 + t_price = 0 + t_profit = 0 + t_margin = 0 + t_col = 9 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 15) + col += 1 + sheet.write(row, col, 'Customer', format2) + sheet.set_column('I:I', 20) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('J:J', 20) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Cost', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Profit', format2) + col += 1 + sheet.write(row, col, 'Margin', format2) + col += 1 + row_number += 1 + for val in loaded_data.get('form'): + column_number = 6 + sheet.write(row_number, column_number, val['sequence'], format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['partner'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['product'], format1) + sheet.set_column('J:J', 20) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['cost'], format1) + t_cost += val['cost'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['profit'], format1) + t_profit += val['profit'] + column_number += 1 + sheet.write(row_number, column_number, val['margin'], format1) + t_margin += val['margin'] + column_number += 1 + row_number += 1 + sheet.write(row_number, t_col, 'Total', format4) + t_col += 1 + sheet.write(row_number, t_col, t_qty, format4) + t_col += 1 + sheet.write(row_number, t_col, t_cost, format4) + t_col += 1 + sheet.write(row_number, t_col, t_price, format4) + t_col += 1 + sheet.write(row_number, t_col, t_profit, format4) + t_col += 1 + sheet.write(row_number, t_col, t_margin, format4) + t_col += 1 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_advance_views.xml b/sale_report_advanced/wizard/sale_report_advance_views.xml new file mode 100644 index 000000000..33ff9aaa9 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_advance_views.xml @@ -0,0 +1,36 @@ + + + + + sale.report.advance.view.form + sale.report.advance + +
+ + + + + + + + +
+
+
+
+
+ + Sale Report Advance + ir.actions.act_window + sale.report.advance + form + + new + +
diff --git a/sale_report_advanced/wizard/sale_report_analysis.py b/sale_report_advanced/wizard/sale_report_analysis.py new file mode 100644 index 000000000..5a3670b07 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_analysis.py @@ -0,0 +1,417 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportAnalysis(models.TransientModel): + """Model for Sale report analysis """ + _name = "sale.report.analysis" + _description = "Sale Report Analysis" + + customer_ids = fields.Many2many('res.partner', string="Customers", + required=True, + help='Select the customers of sales') + product_ids = fields.Many2many('product.product', string='Products', + help="Select products of sale report") + from_date = fields.Date(string="Start Date", help='Start date of report') + to_date = fields.Date(string="End Date", help="End date of report") + status = fields.Selection( + [('all', 'All'), ('draft', 'Draft'), ('sent', 'Quotation Sent'), + ('sale', 'Sale Order'), ('done', 'Locked')], + string='Status', default='all', required=True, + help="Select the status of sale order") + print_type = fields.Selection( + [('sale', 'Sale Order'), ('product', 'Products')], + string='Print By', default='sale', required=True, + help='Select the type of print: "Sale Order" or "Products".') + today_date = fields.Date(string="Date", default=fields.Date.today(), + help="Date of the report") + + def action_get_analysis_report(self): + """ Generate a sales analysis report. + :return: Action to display the sales analysis report. """ + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_sales_analysis').report_action([], + data=datas) + + def _get_data(self): + """ Retrieve and format data for a sales analysis report. + :return: Formatted data for the report. """ + result = [] + if self.print_type == 'sale': + if not self.status == 'all': + sale_order = self.env['sale.order'].sudo().search( + [('state', '=', self.status), ('state', '!=', 'cancel')]) + filtered = self._get_filtered(sale_order) + else: + sale_order = self.env['sale.order'].search( + [('state', '!=', 'cancel')]) + filtered = self._get_filtered(sale_order) + for rec in filtered: + paid = self._get_total_paid_amount(rec.invoice_ids) + res = { + 'so': rec.name, + 'date': rec.date_order, + 'sales_person': rec.user_id.name, + 's_amt': rec.amount_total, + 'p_amt': paid, + 'balance': rec.amount_total - paid, + 'partner_id': rec.partner_id, + } + result.append(res) + else: + if not self.status == 'all': + sale_order_line = self.env['sale.order.line'].search( + [('order_id.state', '=', self.status), + ('order_id.state', '!=', 'cancel')]) + filtered = self._get_filtered_order_line(sale_order_line) + else: + sale_order_line = self.env['sale.order.line'].search( + [('order_id.state', '!=', 'cancel')]) + filtered = self._get_filtered_order_line(sale_order_line) + for rec in filtered: + res = { + 'so': rec.order_id.name, + 'date': rec.order_id.date_order, + 'product_id': rec.product_id.name, + 'price': rec.product_id.list_price, + 'quantity': rec.product_uom_qty, + 'discount': rec.discount, + 'tax': rec.product_id.taxes_id.amount, + 'total': rec.price_subtotal, + 'partner_id': rec.order_id.partner_id, + } + result.append(res) + datas = { + 'ids': self, + 'model': 'sale.report.analysis', + 'form': result, + 'partner_id': self._get_customers(), + 'start_date': self.from_date, + 'end_date': self.to_date, + 'type': self.print_type + } + return datas + + def _get_total_paid_amount(self, invoices): + """ Calculate the total paid amount from a list of invoices. + :return: Total paid amount. """ + total = 0 + for inv in invoices: + if inv.payment_state == 'paid': + total += inv.amount_total + return total + + def _get_filtered_order_line(self, sale_order_line): + """ Filter sale order lines based on date, customers, and products. + :param sale_order_line: List of sale order lines. + :type sale_order_line: list + :return: Filtered list of sale order lines. + :rtype: list + This method filters a list of sale order lines based on optional + date range, customer selection, and product selection. + The filtered list is returned. """ + if self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.date_order.date() <= self.to_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + elif not self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() <= self.to_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + elif self.from_date and not self.to_date: + filtered = list(filter(lambda + x: x.order_id.date_order.date() >= self.from_date and x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + else: + filtered = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.product_id in self.product_ids, + sale_order_line)) + return filtered + + def _get_filtered(self, sale_order): + """ Filter sale orders based on date and customers. + :param sale_order: List of sale orders. + :type sale_order: list + :return: Filtered list of sale orders. + :rtype: list + This method filters a list of sale orders based on optional date + range and customer selection. The filtered list is returned. """ + if self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.date_order.date() <= self.to_date and x.partner_id in self.customer_ids, + sale_order)) + elif not self.from_date and self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() <= self.to_date and x.partner_id in self.customer_ids, + sale_order)) + elif self.from_date and not self.to_date: + filtered = list(filter(lambda + x: x.date_order.date() >= self.from_date and x.partner_id in self.customer_ids, + sale_order)) + else: + filtered = list(filter(lambda + x: x.partner_id in self.customer_ids, + sale_order)) + return filtered + + def _get_customers(self): + """ Retrieve customer data. + :return: List of customer information. + :rtype: list + This method retrieves customer data and returns a list of customer + information, including their ID and name. """ + customers = [] + for rec in self.customer_ids: + data = {'id': rec, + 'name': rec.name} + customers.append(data) + return customers + + def action_get_excel_analysis_report(self): + """ Generate an Excel analysis report. + :return: Dictionary specifying the Excel report action. + :rtype: dict + This method prepares the data for an Excel analysis report and + returns a dictionary that defines the action to generate the + report in XLSX format. """ + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.analysis', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sale Analysis Report', + }, + } + + def get_xlsx_report(self, data, response): + """Function for generating an Excel analysis report""" + loaded_data = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + record = [] + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + if loaded_data.get('type') == 'sale': + sheet.merge_range('G2:L3', 'Sales Analysis Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', loaded_data.get('start_date'), txt) + sheet.write('J6', 'To:', cell_format) + sheet.merge_range('K6:L6', loaded_data.get('end_date'), txt) + h_col = 9 + else: + sheet.merge_range('G2:N3', 'Sales Analysis Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', loaded_data.get('start_date'), txt) + sheet.write('K6', 'To:', cell_format) + sheet.merge_range('L6:N6', loaded_data.get('end_date'), txt) + h_col = 10 + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', + 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True}) + if loaded_data.get('partner_id'): + record = loaded_data.get('partner_id') + h_row = 7 + count = 0 + row = 5 + row_number = 6 + t_row = 6 + for rec in record: + if loaded_data.get('type') == 'sale': + sheet.merge_range(h_row, h_col - 3, h_row, h_col + 2, + rec['name'], format1) + row = row + count + 3 + col = 6 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 17) + col += 1 + sheet.write(row, col, 'Sales Person', format2) + sheet.set_column('I:I', 15) + col += 1 + sheet.write(row, col, 'Sales Amount', format2) + sheet.set_column('J:J', 13) + col += 1 + sheet.write(row, col, 'Amount Paid', format2) + sheet.set_column('K:K', 12) + col += 1 + sheet.write(row, col, 'Balance', format2) + sheet.set_column('L:L', 10) + count = 0 + row_number = row_number + count + 3 + t_samt = 0 + t_pamt = 0 + t_bal = 0 + t_col = 8 + for val in loaded_data.get('form'): + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], + format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('H:H', 17) + column_number += 1 + sheet.write(row_number, column_number, + val['sales_person'], format1) + sheet.set_column('I:I', 15) + column_number += 1 + sheet.write(row_number, column_number, val['s_amt'], + format1) + sheet.set_column('J:J', 13) + t_samt += val['s_amt'] + column_number += 1 + sheet.write(row_number, column_number, val['p_amt'], + format1) + sheet.set_column('K:K', 12) + t_pamt += val['p_amt'] + column_number += 1 + sheet.write(row_number, column_number, val['balance'], + format1) + sheet.set_column('L:L', 10) + t_bal += val['balance'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_samt, format4) + t_col += 1 + sheet.write(t_row, t_col, t_pamt, format4) + t_col += 1 + sheet.write(t_row, t_col, t_bal, format4) + h_row = h_row + count + 3 + if loaded_data.get('type') == 'product': + sheet.merge_range(h_row, h_col - 4, h_row, h_col + 3, + rec['name'], format1) + row = row + count + 3 + col = 6 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 17) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I', 17) + col += 1 + sheet.write(row, col, 'Quantity', format2) + sheet.set_column('J:J', 7) + col += 1 + sheet.write(row, col, 'Price', format2) + sheet.set_column('K:K', 12) + col += 1 + sheet.write(row, col, 'Discount(%)', format2) + sheet.set_column('L:L', 12) + col += 1 + sheet.write(row, col, 'Tax(%)', format2) + sheet.set_column('M:M', 10) + col += 1 + sheet.write(row, col, 'Subtotal', format2) + sheet.set_column('N:N', 10) + col += 1 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_price = 0 + t_tax = 0 + t_total = 0 + t_col = 8 + for val in loaded_data.get('form'): + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], + format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('H:H', 17) + column_number += 1 + sheet.write(row_number, column_number, + val['product_id'], format1) + sheet.set_column('I:I', 17) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], + format1) + sheet.set_column('J:J', 7) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], + format1) + sheet.set_column('K:K', 12) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['discount'], + format1) + sheet.set_column('L:L', 12) + column_number += 1 + sheet.write(row_number, column_number, val['tax'], + format1) + sheet.set_column('M:M', 10) + t_tax += val['tax'] + column_number += 1 + sheet.write(row_number, column_number, val['total'], + format1) + sheet.set_column('N:N', 10) + t_total += val['total'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format4) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format4) + t_col += 1 + sheet.write(t_row, t_col, t_price, format4) + t_col += 2 + sheet.write(t_row, t_col, t_tax, format4) + t_col += 1 + sheet.write(t_row, t_col, t_total, format4) + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_analysis_views.xml b/sale_report_advanced/wizard/sale_report_analysis_views.xml new file mode 100644 index 000000000..f4cd5f2b5 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_analysis_views.xml @@ -0,0 +1,40 @@ + + + + + sale.report.analysis.view.form + sale.report.analysis + +
+ + + + + + + + + + + + +
+
+
+
+
+ + Sales Analysis Report + ir.actions.act_window + sale.report.analysis + form + + new + +
diff --git a/sale_report_advanced/wizard/sale_report_category.py b/sale_report_advanced/wizard/sale_report_category.py new file mode 100644 index 000000000..96e864f1b --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_category.py @@ -0,0 +1,267 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportCategory(models.TransientModel): + """ Model for handling sales report categories. """ + _name = "sale.report.category" + _description = "Transient model for sales report categories" + + category_ids = fields.Many2many('product.category', + string="Categories", required=True, + help="Select one or more product categories.") + from_date = fields.Date(string="Start Date", + help="Specify the beginning date for your report.") + to_date = fields.Date(string="End Date", + help="Specify the ending date for your report.") + company_ids = fields.Many2many('res.company', string='Companies', + help="Select one or more companies for the report.") + today_date = fields.Date(string="Date", default=fields.Date.today(), + help="Automatically set to the current date.") + + def action_get_category_report(self): + """ Generate a category-based sales report. + :return: Action to display the category-based sales report. """ + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_sale_category').report_action([], + data=datas) + + def _get_data(self): + """ Retrieve and format data for a category-based sales report. + :return: A dictionary containing report data. + :rtype: dict + This method retrieves and formats data for a category-based sales + report. It filters sales order lines based on optional date range + and company selection and then categorizes the data. The resulting + dictionary contains the report data ready for use in + generating the report. """ + domain = [('order_id.state', '!=', 'cancel')] + if self.from_date: + domain.append(('order_id.date_order', '>=', self.from_date)) + elif self.to_date: + domain.append(('order_id.date_order', '<=', self.to_date)) + elif self.company_ids: + domain.append(('order_id.company_id', 'in', self.company_ids)) + sale_order_line = self.env['sale.order.line'].search(domain) + category = self._get_category() + res = self._get_category_wise(sale_order_line, category) + if not res: + raise ValidationError("No data available for printing.") + datas = { + 'ids': self, + 'model': 'sale.report.category', + 'form': res, + 'categ_id': category, + 'start_date': self.from_date, + 'end_date': self.to_date, + } + return datas + + def _get_category_wise(self, order_lines, category): + """ Categorize sales data by product category. + :param order_lines: List of sales order lines. + :param category: List of product categories. + :return: Categorized sales data. """ + result = [] + for cat in category: + for lines in order_lines: + if cat['id'] == lines.product_id.categ_id: + total = lines.product_id.taxes_id.amount + lines.price_subtotal + res = { + 'so': lines.order_id.name, + 'date': lines.order_id.date_order, + 'product_id': lines.product_id.name, + 'quantity': lines.product_uom_qty, + 'tax': lines.product_id.taxes_id.amount, + 'uom': lines.product_id.uom_id.name, + 'price': lines.product_id.list_price, + 'subtotal': lines.price_subtotal, + 'total': total, + 'category_id': lines.product_id.categ_id, + } + result.append(res) + return result + + def _get_category(self): + """ Retrieve and format product categories. + :return: List of product categories with IDs and names. + :rtype: list + This method retrieves and formats a list of product categories, + including their IDs and names. """ + category = [] + for rec in self.category_ids: + data = {'id': rec, + 'name': rec.complete_name} + category.append(data) + return category + + def action_get_excel_category_report(self): + """ Generate an Excel category-based sales report. + :return: Dictionary specifying the Excel report action. + :rtype: dict + This method prepares the data for an Excel category-based sales + report and returns a dictionary + that defines the action to generate the report in XLSX format. """ + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.category', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sale Category Report', + }, + } + + def get_xlsx_report(self, data, response): + """ Generate an Excel category-based sales report.""" + loaded_data = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:O3', 'Sales Category Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', loaded_data.get('start_date'), txt) + sheet.write('M6', 'To:', cell_format) + sheet.merge_range('N6:O6', loaded_data.get('end_date'), txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#bbd5fc', + 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#c0dbfa'}) + h_row = 7 + h_col = 10 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if loaded_data.get('categ_id'): + record = loaded_data.get('categ_id') + for rec in record: + sheet.merge_range(h_row, h_col - 4, h_row, h_col + 4, + rec['name'], format4) + row = row + count + 3 + sheet.write(row, col, 'Order', format2) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column('H:H', 15) + col += 1 + sheet.write(row, col, 'Product', format2) + sheet.set_column('I:I', 20) + col += 1 + sheet.write(row, col, 'UOM', format2) + col += 1 + sheet.write(row, col, 'Quantity', format2) + col += 1 + sheet.write(row, col, 'Price', format2) + col += 1 + sheet.write(row, col, 'Tax(%)', format2) + col += 1 + sheet.write(row, col, 'Subtotal', format2) + col += 1 + sheet.write(row, col, 'Total', format2) + col += 1 + col = 6 + count = 0 + row_number = row_number + count + 3 + t_qty = 0 + t_price = 0 + t_subtotal = 0 + t_total = 0 + t_col = 9 + for val in loaded_data.get('form'): + if val['category_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], + format1) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, + val['product_id'], format1) + sheet.set_column('I:I', 20) + column_number += 1 + sheet.write(row_number, column_number, val['uom'], + format1) + column_number += 1 + sheet.write(row_number, column_number, val['quantity'], + format1) + t_qty += val['quantity'] + column_number += 1 + sheet.write(row_number, column_number, val['price'], + format1) + t_price += val['price'] + column_number += 1 + sheet.write(row_number, column_number, val['tax'], + format1) + column_number += 1 + sheet.write(row_number, column_number, val['subtotal'], + format1) + t_subtotal += val['subtotal'] + column_number += 1 + sheet.write(row_number, column_number, val['total'], + format1) + t_total += val['total'] + column_number += 1 + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format3) + t_col += 1 + sheet.write(t_row, t_col, t_qty, format3) + t_col += 1 + sheet.write(t_row, t_col, t_price, format3) + t_col += 2 + sheet.write(t_row, t_col, t_subtotal, format3) + t_col += 1 + sheet.write(t_row, t_col, t_total, format3) + t_col += 1 + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_category_views.xml b/sale_report_advanced/wizard/sale_report_category_views.xml new file mode 100644 index 000000000..0582bb036 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_category_views.xml @@ -0,0 +1,32 @@ + + + + + sale.report.category.view.form + sale.report.category + +
+ + + + + + +
+
+
+
+
+ + Sales Category Report + ir.actions.act_window + sale.report.category + form + + new + +
diff --git a/sale_report_advanced/wizard/sale_report_indent.py b/sale_report_advanced/wizard/sale_report_indent.py new file mode 100644 index 000000000..83f045937 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_indent.py @@ -0,0 +1,250 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportIndent(models.TransientModel): + """ Model for handling sales report indents. + This transient model is used for managing data related to sales + report indents. It is designed for + temporary and intermediary data storage and is not intended for + permanent database storage. """ + _name = "sale.report.indent" + _description = "Sale Report Indent" + + customer_ids = fields.Many2many('res.partner', string="Customers", + required=True, help="Select one or more" + " customers for the report.") + category_ids = fields.Many2many('product.category', string="Categories", + required=True, + help="Select one or more product categories.") + from_date = fields.Date(string="Start Date", + help="Specify the beginning date for your report.") + to_date = fields.Date(string="End Date", + help="Specify the ending date for your report.") + status = fields.Selection( + [('all', 'All'), ('draft', 'Draft'), ('sent', 'Quotation Sent'), + ('sale', 'Sale Order'), ('done', 'Locked')], + string='Status', default='all', required=True, + help="Select the status of the orders to include in the report.") + company_ids = fields.Many2many('res.company', string='Companies', + help="Select one or more companies" + " for the report.") + today_date = fields.Date(default=fields.Date.today(), + help="Automatically set to the current date.") + + def action_get_indent_report(self): + """ Generate a category-based sales report. + :return: Action to display the category-based sales report. """ + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_sale_indent').report_action([], + data=datas) + + def _get_data(self): + """ Retrieve and format data for a sales report. + :return: A dictionary containing report data. + :rtype: dict + This method retrieves and formats data for a sales report. + It filters sales order lines based on optional date + range and company selection, then categorizes the data using the + '_get_orders' method. The resulting dictionary + contains the report data ready for use in generating the report. + It includes information such as order details, + product categories, customers, start and end dates. + """ + domain = [('order_id.state', '!=', 'cancel')] + if self.from_date: + domain.append(('order_id.date_order', '>=', self.from_date)) + elif self.to_date: + domain.append(('order_id.date_order', '<=', self.to_date)) + elif self.company_ids: + domain.append(('order_id.company_id', 'in', self.company_ids)) + sale_order_line = self.env['sale.order.line'].search(domain) + res = self._get_orders(sale_order_line) + if not res: + raise ValidationError("No data available for printing.") + datas = { + 'ids': self, + 'model': 'sale.report.indent', + 'form': res, + 'categ_id': self._get_category(), + 'partner_id': self._get_customers(), + 'start_date': self.from_date, + 'end_date': self.to_date, + } + return datas + + def _get_orders(self, sale_order_line): + """ Filter and categorize sales orders. + :param sale_order_line: List of sales order lines. + :return: Categorized sales orders. """ + if self.status == 'all': + filtered_order = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.product_id.categ_id in self.category_ids, + sale_order_line)) + else: + filtered_order = list(filter(lambda + x: x.order_id.partner_id in self.customer_ids and x.order_id.state in self.status and x.product_id.categ_id in self.category_ids, + sale_order_line)) + return self._get_customer_wise(filtered_order) + + def _get_customer_wise(self, order): + """ Categorize sales data by customer and category. + :param order: List of sales orders. + :return: Categorized sales data. """ + result = [] + for rec in order: + res = { + 'product_id': rec.product_id.name, + 'quantity': rec.product_uom_qty, + 'partner_id': rec.order_id.partner_id, + 'category_id': rec.product_id.categ_id, + } + result.append(res) + return result + + def _get_category(self): + """ Retrieve and format product categories. + :return: List of product categories with IDs and names. """ + category = [] + for rec in self.category_ids: + data = { + 'id': rec, + 'name': rec.complete_name + } + category.append(data) + return category + + def _get_customers(self): + """ Retrieve and format customer data. + :return: List of customer information with IDs and names. """ + customers = [] + for rec in self.customer_ids: + data = { + 'id': rec, + 'name': rec.name + } + customers.append(data) + return customers + + def action_get_excel_indent_report(self): + """ Generate an Excel category-based sales report. + :return: Dictionary specifying the Excel report action. + """ + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.indent', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sales Indent Report', + }, + } + + def get_xlsx_report(self, data, response): + """ Generate an Excel category-based sales report. """ + loaded_data = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:N3', 'Product Sales Indent Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('H6', 'From:', cell_format) + sheet.merge_range('I6:J6', loaded_data.get('start_date'), txt) + sheet.write('K6', 'To:', cell_format) + sheet.merge_range('L6:M6', loaded_data.get('end_date'), txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', + 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#b6d0fa', 'border': 1}) + if loaded_data.get('categ_id'): + categ = loaded_data.get('categ_id') + if loaded_data.get('partner_id'): + partner = loaded_data.get('partner_id') + h_row = 6 + h_col = 12 + c_row = 6 + row = 7 + row_number = 6 + for rec in partner: + count = 0 + h_row += 1 + sheet.merge_range(h_row, h_col - 5, h_row, h_col, rec['name'], + format3) + c_row = c_row + 2 + row += 2 + row_number += 2 + for cat in categ: + count += 2 + c_col = 12 + col = 9 + sheet.merge_range(c_row, c_col-5, c_row, c_col, cat['name'], + format1) + sheet.merge_range(row, c_col-5,row, c_col, 'Product', format4) + sheet.set_column('J:J', 17) + col += 1 + sheet.merge_range(row, c_col-5, row, c_col, 'Quantity', format4) + sheet.set_column('K:K', 17) + row_number += 2 + c_count = 0 + for val in loaded_data.get('form'): + if val['category_id'] == cat['id'] and val['partner_id'] == rec['id']: + c_count += 1 + count += 1 + column_number = 9 + column_number1 = 12 + sheet.merge_range(row_number, column_number-2,row_number, column_number, + val['product_id'], format1) + sheet.set_column('J:J', 17) + column_number += 1 + sheet.merge_range(row_number, column_number1-2, row_number, column_number1, val['quantity'], + format1) + sheet.set_column('K:K', 17) + row_number += 1 + row = row + c_count + 2 + c_row = c_row + c_count + 2 + h_row = h_row + count + 1 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_indent_views.xml b/sale_report_advanced/wizard/sale_report_indent_views.xml new file mode 100644 index 000000000..f65e1c115 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_indent_views.xml @@ -0,0 +1,37 @@ + + + + + Product Sales Indent + sale.report.indent + +
+ + + + + + + + + + + +
+
+
+
+
+ + Product Sales Indent Report + ir.actions.act_window + sale.report.indent + form + + new + +
diff --git a/sale_report_advanced/wizard/sale_report_invoice.py b/sale_report_advanced/wizard/sale_report_invoice.py new file mode 100644 index 000000000..94ddbcbe6 --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_invoice.py @@ -0,0 +1,278 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportInvoice(models.TransientModel): + """ Model for handling sales report invoices. + This transient model is used for managing data related to sales + report invoices. """ + _name = "sale.report.invoice" + _description = 'Sales Report Invoices' + + customer_ids = fields.Many2many('res.partner', + string="Customers", required=True, + help="Select one or more customers for " + "the report.") + from_date = fields.Date(string="Start Date", + help="Specify the beginning date for your report.") + to_date = fields.Date(string="End Date", + help="Specify the ending date for your report.") + status = fields.Selection( + [('open', 'Open'), ('paid', 'Paid'), ('both', 'Both')], + string='Status', default='open', required=True, + help="Select the status of the invoices to include in the report.") + company_ids = fields.Many2many('res.company', string='Companies', + help="Select one or more companies for " + "the report.") + today_date = fields.Date(default=fields.Date.today(), + help="Automatically set to the current date.") + + def action_get_invoice_report(self): + """ Generate an invoice analysis report. + :return: Action to display the invoice analysis report. """ + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_invoice_analysis').report_action([], + data=datas) + + def _get_data(self): + """Get report values for sale invoice report """ + domain = [('state', '!=', 'cancel')] + if self.from_date: + domain.append(('date_order', '>=', self.from_date)) + if self.to_date: + domain.append(('date_order', '<=', self.to_date)) + if self.company_ids: + domain.append(('company_id', 'in', self.company_ids)) + sales_order = self.env['sale.order'].search(domain) + if not sales_order: + raise ValidationError("No data available for printing.") + result = [] + customers = [] + for record in self.customer_ids: + data = { + 'id': record, + 'name': record.name + } + customers.append(data) + for so in sales_order: + for cust in customers: + if cust['id'] == so['partner_id'] and so.invoice_ids: + if self.status == 'open': + for inv in so.invoice_ids: + if inv.payment_state != 'paid': + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total - inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + elif self.status == 'paid': + for inv in so.invoice_ids: + if inv.payment_state == 'paid': + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total - inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + else: + for inv in so.invoice_ids: + res = { + 'so': so.name, + 'partner_id': so.partner_id, + 'order_date': so.date_order, + 'invoice': inv.name, + 'date': inv.invoice_date, + 'invoiced': inv.amount_total, + 'paid': inv.amount_total - inv.amount_residual, + 'due': inv.amount_residual, + } + result.append(res) + if not result: + raise ValidationError("No data available for printing.") + datas = { + 'ids': self, + 'model': 'sale.report.invoice', + 'form': result, + 'partner_id': customers, + 'start_date': self.from_date, + 'end_date': self.to_date, + 'status': self.status, + } + return datas + + def action_get_excel_invoice_report(self): + """ Generate an Excel invoice analysis report. + :return: Action to create the Excel report. """ + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.invoice', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sale Invoice Report', + }, + } + + def get_xlsx_report(self, data, response): + """ Generate an Excel invoice analysis report. """ + loaded_data = json.loads(data) + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + cell_format = workbook.add_format({'font_size': '12px', }) + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + txt = workbook.add_format({'font_size': '10px', 'align': 'center'}) + sheet.merge_range('G2:M3', 'Invoice Analysis Report', head) + if loaded_data.get('start_date') and loaded_data.get('end_date'): + sheet.write('G6', 'From:', cell_format) + sheet.merge_range('H6:I6', loaded_data.get('start_date'), txt) + sheet.write('K6', 'To:', cell_format) + sheet.merge_range('L6:M6', loaded_data.get('end_date'), txt) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'left', 'bg_color': '#bbd5fc', + 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#bbd5fc', + 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True}) + record = loaded_data.get('partner_id') + h_row = 7 + h_col = 9 + count = 0 + row = 5 + col = 6 + row_number = 6 + t_row = 6 + if loaded_data.get('partner_id'): + for rec in record: + sheet.merge_range(h_row, h_col - 3, h_row, h_col + 3, + rec['name'], format4) + row = row + count + 3 + sheet.write(row, col, 'Order Number', format2) + sheet.set_column('G:G', 12) + col += 1 + sheet.write(row, col, 'Order Date', format2) + sheet.set_column('H:H', 15) + col += 1 + sheet.write(row, col, 'Invoice Number', format2) + sheet.set_column('I:I', 13) + col += 1 + sheet.write(row, col, 'Invoice Date', format2) + sheet.set_column('J:J', 15) + col += 1 + sheet.write(row, col, 'Amount Invoiced', format2) + sheet.set_column('K:K', 11) + col += 1 + sheet.write(row, col, 'Amount Paid', format2) + sheet.set_column('L:L', 11) + col += 1 + sheet.write(row, col, 'Amount Due', format2) + sheet.set_column('M:M', 11) + col += 1 + col = 6 + count = 0 + t_invoiced = 0 + t_paid = 0 + t_due = 0 + row_number = row_number + count + 3 + t_col = 9 + for val in loaded_data.get('form'): + if val['partner_id'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['so'], + format1) + sheet.set_column('G:G', 12) + column_number += 1 + sheet.write(row_number, column_number, + val['order_date'], format1) + sheet.set_column('H:H', 15) + column_number += 1 + sheet.write(row_number, column_number, val['invoice'], + format1) + sheet.set_column('I:I', 13) + column_number += 1 + sheet.write(row_number, column_number, val['date'], + format1) + sheet.set_column('J:J', 15) + column_number += 1 + sheet.write(row_number, column_number, val['invoiced'], + format1) + sheet.set_column('K:K', 14) + t_invoiced += val['invoiced'] + column_number += 1 + sheet.write(row_number, column_number, val['paid'], + format1) + sheet.set_column('L:L', 11) + t_paid += val['paid'] + column_number += 1 + sheet.write(row_number, column_number, val['due'], + format1) + sheet.set_column('M:M', 11) + t_due += val['due'] + row_number += 1 + t_row = t_row + count + 3 + sheet.write(t_row, t_col, 'Total', format3) + sheet.set_column('J:J', 15) + t_col += 1 + sheet.write(t_row, t_col, t_invoiced, format3) + sheet.set_column('K:K', 14) + t_col += 1 + sheet.write(t_row, t_col, t_paid, format3) + sheet.set_column('L:L', 11) + t_col += 1 + sheet.write(t_row, t_col, t_due, format3) + sheet.set_column('M:M', 11) + h_row = h_row + count + 3 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_invoice_views.xml b/sale_report_advanced/wizard/sale_report_invoice_views.xml new file mode 100644 index 000000000..e27f7042d --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_invoice_views.xml @@ -0,0 +1,35 @@ + + + + + Sales Invoice Analysis + sale.report.invoice + +
+ + + + + + + + + +
+
+
+
+
+ + Sale Invoice Report + ir.actions.act_window + sale.report.invoice + form + + new + +
diff --git a/sale_report_advanced/wizard/sale_report_weekly.py b/sale_report_advanced/wizard/sale_report_weekly.py new file mode 100644 index 000000000..4117e78dd --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_weekly.py @@ -0,0 +1,208 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# Author: Ayana KP (odoo@cybrosys.com) +# +# You can modify it under the terms of the GNU AFFERO +# GENERAL PUBLIC LICENSE (AGPL 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 AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. +# +# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE +# (AGPL v3) along with this program. +# If not, see . +# +############################################################################### +import json +import io +from odoo.tools import date_utils +from odoo.exceptions import ValidationError +from odoo import fields, models, _ +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SaleReportWeekly(models.TransientModel): + """ Model for handling sales report in weekly . + This transient model is used for managing data related to sales + report invoices. """ + _name = "sale.report.weekly" + _description = 'Sales Report Weekly' + + date = fields.Date(string='Date', required=True, + help="Select the date of the report") + invoice_status = fields.Selection( + [('invoiced', 'Fully Invoiced'), + ('to invoice', 'To Invoice'), + ('no', 'Nothing to Invoice')], + string='Invoice Status', default='no', required=True, + help='Select the invoice status for this record. ' + 'Fully Invoiced: All invoices are fully paid. ' + 'To Invoice: There are pending invoices. ' + 'Nothing to Invoice: No invoices are related to this record.') + amount_type = fields.Selection( + [('total', 'Total Amount'), ('untax', 'Untaxed Amount')], + string='Total Amount', default='total', + help='Select the type of amount to be used. ' + 'Total Amount: Includes taxes. ' + 'Untaxed Amount: Excludes taxes.') + today_date = fields.Date( + default=fields.Date.today(), + help='This field is automatically set to the current date.') + + def action_get_weekly_report(self): + """Generates and displays a weekly sales report.""" + datas = self._get_data() + return self.env.ref( + 'sale_report_advanced.action_sales_weekly').report_action([], + data=datas) + + def _get_data(self): + """ Function for getting data for report """ + result = [] + times = {'morning': 'Morning (5:00-12:00)', + 'noon': 'Noon (1:00-17:00)', + 'evening': 'Evening (18:00-23:00)'} + sale_orders = self.env['sale.order'].sudo().search([ + ('invoice_status', '=', self.invoice_status), + ('date_order', '>=', self.date), + ('state', '!=', 'cancel') + ]) + print('saaa',sale_orders) + if not sale_orders: + raise ValidationError("No data available for printing.") + for rec in sale_orders: + if self.amount_type == 'total': + amount = rec.amount_total + else: + amount = rec.amount_untaxed + for time_id, time_name in times.items(): + if rec.date_order.hour > self._get_time_start( + time_id) and rec.date_order.hour <= self._get_time_end( + time_id): + res = { + 'order': rec.name, + 'amount': amount, + 'time': time_id, + 'date': rec.date_order.date() + } + result.append(res) + print('res', res) + if not result: + raise ValidationError("No data available for printing.") + datas = { + 'ids': self, + 'model': 'sale.report.weekly', + 'form': result, + 'date': self.date, + 'type': self.amount_type, + 'times': times + } + print('datas',datas) + return datas + + def _get_time_start(self, time_id): + """Get the starting hour corresponding to a given time identifier.""" + time_mapping = {'morning': 5, 'noon': 12, 'evening': 17} + return time_mapping[time_id] + + def _get_time_end(self, time_id): + """Get the ending hour corresponding to a given time identifier.""" + time_mapping = {'morning': 12, 'noon': 17, 'evening': 23} + return time_mapping[time_id] + + def action_get_excel_weekly_report(self): + """Generate and return a configuration dictionary for an Excel + weekly report.""" + datas = self._get_data() + return { + 'type': 'ir.actions.report', + 'report_type': 'xlsx', + 'data': {'model': 'sale.report.weekly', + 'output_format': 'xlsx', + 'options': json.dumps(datas, + default=date_utils.json_default), + 'report_name': 'Sale Hourly Report', + }, + } + + def get_xlsx_report(self, data, response): + """ Function for generating xlsx report """ + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + head = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '20px'}) + sheet.merge_range('G2:I3', 'Hourly Sales Report', head) + format1 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bg_color': '#f5f9ff', + 'border': 1}) + format2 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#6BA6FE', 'border': 1}) + format3 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, + 'bg_color': '#95ff63', 'border': 1}) + format4 = workbook.add_format( + {'font_size': 10, 'align': 'center', 'bold': True, 'border': 1}) + h_row = 7 + h_col = 7 + count = 0 + row = 4 + row_number = 5 + for rec in data['times']: + col = 6 + row = row + count + 4 + sheet.merge_range(h_row, h_col - 1, h_row, h_col + 1, rec['name'], + format3) + sheet.write(row, col, 'Order', format2) + sheet.set_column(row, col, 15) + col += 1 + sheet.write(row, col, 'Date', format2) + sheet.set_column(row, col, 15) + col += 1 + if data['type'] == 'total': + sheet.write(row, col, 'Total', format2) + sheet.set_column(row, col, 15) + col += 1 + else: + sheet.write(row, col, 'Untaxed Total', format2) + sheet.set_column(row, col, 15) + col += 1 + t_total = 0 + row_number = row_number + 4 + count = 0 + t_col = 7 + for val in data['form']: + if val['time'] == rec['id']: + count += 1 + column_number = 6 + sheet.write(row_number, column_number, val['order'], + format1) + sheet.set_column(row_number, column_number, 15) + column_number += 1 + sheet.write(row_number, column_number, val['date'], format1) + sheet.set_column(row_number, column_number, 15) + column_number += 1 + sheet.write(row_number, column_number, val['amount'], + format1) + t_total += val['amount'] + sheet.set_column(row_number, column_number, 15) + row_number += 1 + sheet.write(row_number, t_col, 'Total', format4) + sheet.set_column(row_number, t_col, 15) + t_col += 1 + sheet.write(row_number, t_col, t_total, format4) + h_row = h_row + count + 4 + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/sale_report_advanced/wizard/sale_report_weekly_views.xml b/sale_report_advanced/wizard/sale_report_weekly_views.xml new file mode 100644 index 000000000..7c194f76a --- /dev/null +++ b/sale_report_advanced/wizard/sale_report_weekly_views.xml @@ -0,0 +1,33 @@ + + + + + sale.report.weekly.view.form + sale.report.weekly + +
+ + + + + + + +
+
+
+
+
+ + Hourly Sales Report + ir.actions.act_window + sale.report.weekly + form + + new + +