diff --git a/all_in_one_sales_kit/README.rst b/all_in_one_sales_kit/README.rst new file mode 100755 index 000000000..4d50510f3 --- /dev/null +++ b/all_in_one_sales_kit/README.rst @@ -0,0 +1,48 @@ +.. 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 + +All In One Sales Kit +==================== +This module combines a variety of sales features. + +Configuration +============= +* No Additional configuration is needed. + +Company +------- +* `Cybrosys Techno Solutions `__ + +License +------- +General Public License, Version 3 (AGPL-3). +(https://www.gnu.org/licenses/agpl-3.0-standalone.html) + +Credits +------- +Developers: (V17) Ashwin A, + (V16) Swetha Anand, +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/all_in_one_sales_kit/__init__.py b/all_in_one_sales_kit/__init__.py new file mode 100644 index 000000000..af6138cba --- /dev/null +++ b/all_in_one_sales_kit/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 models +from . import report +from . import wizard diff --git a/all_in_one_sales_kit/__manifest__.py b/all_in_one_sales_kit/__manifest__.py new file mode 100644 index 000000000..5fd7722a3 --- /dev/null +++ b/all_in_one_sales_kit/__manifest__.py @@ -0,0 +1,102 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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': 'All In One Sales Kit', + 'version': '17.0.1.0.0', + 'category': 'Sales', + 'summary': 'This module combines a variety of sales features.', + 'description': 'Sale Order Line Images, Barcode Scan Support for Sales, ' + 'Advanced Sale Reports (Product Profit Report, ' + 'Sales Invoice Analysis Report, Sales Category Report, ' + 'Sales Indent Report, Sales Analysis Report, ' + 'Hourly Sales Report), Product Pack, and ' + 'Salesperson Signature for Confirm Order are some of the ' + 'features included in this module.' + 'Previous Sale Product Rate, Create Various Sale Order ' + 'Versions, Create Custom Fields for Sale Orders, ' + 'Recognise Previous Sales of Products,' + 'A separate quotation number,' + 'Multiple warehouses in sale order lines,' + 'sales order and quotation line views,' + 'approval of the sale order discount,sales restrictions ' + 'for out-of-stock items depending on forecast and ' + 'stock level,automate the sale process,' + 'Sales one-stop report generation,Add more than one ' + 'item to the quotation,pivot view for partner sales,' + 'sale order archive,Dashboard.', + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': 'https://www.cybrosys.com', + 'depends': ['sale_management', 'delivery', 'stock'], + 'data': [ + 'security/all_in_one_sales_kit_groups.xml', + 'security/ir.model.access.csv', + 'data/field_widget_data.xml', + 'data/ir_sequence_data.xml', + 'wizard/product_sale_order_history_views.xml', + 'wizard/sale_order_dynamic_fields_views.xml', + 'wizard/sale_report_advance_views.xml', + 'wizard/sale_report_analysis_views.xml', + 'wizard/sale_report_category_views.xml', + 'wizard/sale_report_indent_views.xml', + 'wizard/sale_report_invoice_views.xml', + 'wizard/sale_report_weekly_views.xml', + 'wizard/select_product_pack_views.xml', + 'views/res_config_settings_views.xml', + 'views/all_in_one_sales_kit_menus.xml', + 'views/sale_order_views.xml', + 'views/sale_order_line_views.xml', + 'views/res_users_views.xml', + 'views/sale_report_views.xml', + 'views/product_template_views.xml', + 'views/ir_fields_search_views.xml', + 'views/product_product_views.xml', + 'views/dashboard_menu.xml', + 'report/invoice_analysis_templates.xml', + 'report/sale_order_document_templates.xml', + 'report/sale_order_report_templates.xml', + 'report/sale_profit_templates.xml', + 'report/sale_reports.xml', + 'report/sales_analysis_templates.xml', + 'report/sales_category_templates.xml', + 'report/sales_indent_templates.xml', + 'report/sales_weekly_templates.xml' + ], + 'assets': { + 'web.assets_backend': [ + 'all_in_one_sales_kit/static/src/css/sale_report.css', + 'all_in_one_sales_kit/static/src/scss/dashboard.scss', + 'all_in_one_sales_kit/static/src/js/action_manager.js', + 'all_in_one_sales_kit/static/src/js/sale_report.js', + 'all_in_one_sales_kit/static/src/js/dashboard.js', + 'all_in_one_sales_kit/static/src/xml/sale_report_templates.xml', + 'all_in_one_sales_kit/static/src/xml/dashboard_templates.xml', + 'https://cdn.jsdelivr.net/npm/chart.js', + ], + }, + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/all_in_one_sales_kit/controllers/__init__.py b/all_in_one_sales_kit/controllers/__init__.py new file mode 100644 index 000000000..770ccc94d --- /dev/null +++ b/all_in_one_sales_kit/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 all_in_one_sales_kit diff --git a/all_in_one_sales_kit/controllers/all_in_one_sales_kit.py b/all_in_one_sales_kit/controllers/all_in_one_sales_kit.py new file mode 100644 index 000000000..03aaf2379 --- /dev/null +++ b/all_in_one_sales_kit/controllers/all_in_one_sales_kit.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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): + """This class is to add sales xlsx reports.""" + + @http.route('/xlsx_reports', type='http', auth='user', + methods=['POST'], csrf=False) + def get_report_xlsx(self, model, options, output_format, + report_name, **kw): + """This is to pass data required for the xlsx report.""" + 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')) + ] + ) + request.env[model].sudo().browse( + request.session.uid).get_xlsx_report(json.loads(options), + response) + response.set_cookie('fileToken', token) + return response + except Exception: + error = { + 'code': 200, + 'message': 'Odoo Server Error', + } + return request.make_response(html_escape(json.dumps(error))) diff --git a/all_in_one_sales_kit/data/field_widget_data.xml b/all_in_one_sales_kit/data/field_widget_data.xml new file mode 100644 index 000000000..5b22ea740 --- /dev/null +++ b/all_in_one_sales_kit/data/field_widget_data.xml @@ -0,0 +1,77 @@ + + + + + + image + Image + + + + many2many_tags + Many2many Tags + + + + binary + Binary + + + + radio + Radio + + + + priority + Priority + + + + monetary + Monetary + + + + selection + Selection + + + + image + Image + + + + many2many_tags + Many2many Tags + + + + binary + Binary + + + + radio + Radio + + + + priority + Priority + + + + monetary + Monetary + + + + selection + Selection + + + diff --git a/all_in_one_sales_kit/data/ir_sequence_data.xml b/all_in_one_sales_kit/data/ir_sequence_data.xml new file mode 100644 index 000000000..780573bcf --- /dev/null +++ b/all_in_one_sales_kit/data/ir_sequence_data.xml @@ -0,0 +1,11 @@ + + + + + Quotation + sale.order + SQ + 5 + + + diff --git a/all_in_one_sales_kit/doc/RELEASE_NOTES.md b/all_in_one_sales_kit/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..87ad9297f --- /dev/null +++ b/all_in_one_sales_kit/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 11.03.2024 +#### Version 17.0.1.0.0 +#### ADD + +- Initial commit for All In One Sales Kit diff --git a/all_in_one_sales_kit/models/__init__.py b/all_in_one_sales_kit/models/__init__.py new file mode 100644 index 000000000..00b9e4864 --- /dev/null +++ b/all_in_one_sales_kit/models/__init__.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 field_widget +from . import ir_model_fields +from . import pack_product +from . import product_product +from . import product_template +from . import res_config_settings +from . import res_partner +from . import res_users +from . import sale_order +from . import sale_order_line +from . import sales_report diff --git a/all_in_one_sales_kit/models/field_widget.py b/all_in_one_sales_kit/models/field_widget.py new file mode 100644 index 000000000..97bbdc3e8 --- /dev/null +++ b/all_in_one_sales_kit/models/field_widget.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import fields, models + + +class FieldWidget(models.Model): + """ + Need of this model is because we can't filter a selection field dynamically + so when we select a field its widgets also need to change according to + field type, we can't do it by a 'selection' field, + need a 'Many2one' field. + """ + + _name = 'field.widget' + _description = 'Field Widgets' + _rec_name = 'description' + + name = fields.Char(string="Name", help="Name of widget.") + description = fields.Char( + string="Description", help="Description about the widget.") diff --git a/all_in_one_sales_kit/models/ir_model_fields.py b/all_in_one_sales_kit/models/ir_model_fields.py new file mode 100644 index 000000000..5e1a5ccac --- /dev/null +++ b/all_in_one_sales_kit/models/ir_model_fields.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import fields, models + + +class IrModelFields(models.Model): + """Adding a new field to understand the dynamically created fields.""" + + _inherit = 'ir.model.fields' + + is_dynamic = fields.Boolean( + string="Dynamic Field", + help="To make sure it is a dynamically created field.") diff --git a/all_in_one_sales_kit/models/pack_product.py b/all_in_one_sales_kit/models/pack_product.py new file mode 100644 index 000000000..7ad4bfb86 --- /dev/null +++ b/all_in_one_sales_kit/models/pack_product.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import api, fields, models, _ +from odoo.exceptions import ValidationError + + +class PackProduct(models.Model): + """ A new model is created to store products as pack.""" + + _name = 'pack.product' + _rec_name = 'product_tmpl_id' + _description = 'Select Pack Products' + + product_id = fields.Many2one( + 'product.product', string='Product', required=True, + domain=[('is_pack', '=', False)], help="Product") + product_tmpl_id = fields.Many2one( + 'product.template', string='Product', help="Product") + price = fields.Float( + string='Price', compute='_compute_price', store=True, + help="Computed price of product according to the quantity.") + quantity = fields.Integer( + string='Quantity', default=1, + help="Quantity of product that should be in the pack.") + qty_available = fields.Float( + string='Quantity Available', compute='_compute_qty_available', + store=True, readonly=False, help="Available quantity of product.") + total_available_quantity = fields.Float( + string='Total Quantity', + help="Total Quantity available of that product") + + @api.depends('product_id', + 'total_available_quantity', + 'product_id.qty_available') + def _compute_qty_available(self): + """It is to compute the available quantity.""" + for record in self: + location_id = record.product_tmpl_id.pack_location_id + if location_id: + stock_quant = self.env['stock.quant'].search( + [('product_id', '=', record.product_id.id), + ('location_id', '=', location_id.id)]) + if stock_quant: + record.qty_available = stock_quant.quantity + else: + record.qty_available = False + else: + record.qty_available = False + + @api.depends('product_id', 'quantity') + def _compute_price(self): + """It is to compute price of each product compared to quantity.""" + for record in self: + record.price = record.product_id.lst_price * record.quantity + + @api.onchange('quantity') + def _onchange_quantity(self): + """It is to set price.""" + self.price = self.product_id.lst_price * self.quantity + + @api.constrains('quantity') + def _check_quantity(self): + """This function is to ensure product quantity is positive.""" + if any([product.quantity < 0 for product in self]): + raise ValidationError(_('You can not enter negative quantities.')) diff --git a/all_in_one_sales_kit/models/product_product.py b/all_in_one_sales_kit/models/product_product.py new file mode 100644 index 000000000..9c7bd352a --- /dev/null +++ b/all_in_one_sales_kit/models/product_product.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import fields, models +from odoo.exceptions import UserError + + +class ProductProduct(models.Model): + """Inherits product.product.""" + _inherit = "product.product" + + order_partner_id = fields.Many2one( + 'res.partner', string="Partner", help="Current Partner") + + def action_sale_product_prices(self): + """It is to show to product history.""" + rel_view_id = self.env.ref( + 'all_in_one_sales_kit.sale_order_line_view_tree') + if self.order_partner_id.id: + sale_lines = self.env['sale.order.line'].search( + [('product_id', '=', self.id), + ('order_partner_id', '=', self.order_partner_id.id)], + order='create_date DESC').ids + else: + sale_lines = self.env['sale.order.line'].search( + [('product_id', '=', self.id)], + order='create_date DESC').ids + if not sale_lines: + raise UserError("No sales history found.!") + else: + return { + 'domain': [('id', 'in', sale_lines)], + 'views': [(rel_view_id.id, 'tree')], + 'name': 'Sales History', + 'res_model': 'sale.order.line', + 'view_id': False, + 'type': 'ir.actions.act_window', + } + + def action_add_quotation(self): + """It is a button function on clicking the product is entered to + the order line.""" + order_id = self.env.context.get('order_id') + list = self.env['sale.order.line'].search( + [('order_id', '=', order_id)]).mapped('product_id') + if self in list: + order_line_id = self.env['sale.order.line'].search( + [('order_id', '=', order_id), ('product_id', '=', self.id)]) + order_line_id.product_uom_qty += 1 + else: + self.env['sale.order.line'].create({ + 'product_id': self.id, + 'order_id': order_id, + 'qty_available': self.qty_available, + 'forecast_quantity': self.virtual_available, + }) diff --git a/all_in_one_sales_kit/models/product_template.py b/all_in_one_sales_kit/models/product_template.py new file mode 100644 index 000000000..831be780d --- /dev/null +++ b/all_in_one_sales_kit/models/product_template.py @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class ProductTemplate(models.Model): + """Inherits product.template.""" + _inherit = 'product.template' + + def default_pack_location(self): + """Sets the default value for the 'location_id' field based + on the current user's warehouse.""" + warehouse = self.env['stock.warehouse'].search( + [('company_id', '=', self.env.company.id)], limit=1) + if warehouse: + return warehouse.lot_stock_id.id + + is_pack = fields.Boolean(string='Is a Pack', help="The product is a pack.") + pack_price = fields.Integer( + string="Pack Price", compute='_compute_pack_price', store=True, + help="Total price of products inside the pack") + pack_products_ids = fields.One2many( + 'pack.product', 'product_tmpl_id', + string='Pack Products', copy=True, help="Products inside the pack") + pack_quantity = fields.Integer( + string='Pack Quantity', help="Pack quantity available") + pack_location_id = fields.Many2one( + 'stock.location', + domain=[('usage', 'in', ['internal', 'transit'])], + default=default_pack_location, help="Warehouse", string="Warehouse") + + @api.depends('pack_products_ids', 'pack_products_ids.price') + def _compute_pack_price(self): + """It is to set total price of the pack according to the + products in the pack.""" + price = 0 + for record in self: + for line in record.pack_products_ids: + price = price + line.price + record.pack_price = price + + @api.model + def create(self, values): + """Here create function is over ride to check whether the product is + a pack or not.""" + if values.get('is_pack', False): + if not values.get('pack_products_ids', []): + raise UserError(_( + 'You need to add atleast one product in the Pack...!')) + if values.get('type', False) == 'service': + raise UserError(_( + 'You cannot define a pack product as a service..!')) + return super(ProductTemplate, self).create(values) + + def write(self, values): + """Here, the write function is overridden to determine if + there is at least one product inside the package.""" + super(ProductTemplate, self).write(values) + if self.is_pack: + if not self.pack_products_ids: + raise UserError(_( + 'You need to add atleast one product in the Pack...!')) + if self.type == 'service': + raise UserError( + _('You cannot define a pack product as a service..!')) + + def action_update_price_product(self): + """Updates the 'list_price' field of the current object + based on the value of the 'pack_price' field on button click.""" + self.list_price = self.pack_price + + def action_get_quantity(self): + """It is to return the pack quantity.""" + total_quantity = 1 + flag = 1 + while flag: + for line in self.pack_products_ids: + if line.qty_available >= line.quantity * total_quantity: + continue + else: + if line.product_id.type != 'product': + continue + flag = 0 + break + if flag: + total_quantity = total_quantity + 1 + self.pack_quantity = total_quantity - 1 + + def action_update_quantity(self): + """It is to return the updated pack quantity.""" + product_id = len( + self.product_variant_ids) == 1 and self.product_variant_id.id + location_id = self.pack_location_id.id + if not location_id: + warehouse = self.env['stock.warehouse'].search( + [('company_id', '=', self.env.company.id)], limit=1) + location_id = warehouse.lot_stock_id.id + if not location_id: + raise UserError(_( + 'You need to select the location to update' + ' the pack quantity...!')) + self.env['stock.quant'].with_context(inventory_mode=True).sudo( + ).create({ + 'product_id': product_id, + 'location_id': location_id, + 'inventory_quantity': self.pack_quantity, + }) + + @api.onchange('pack_location_id') + def _onchange_pack_location_id(self): + """It is to change the available quantity based on location.""" + for line in self.pack_products_ids: + stock_quant = self.env['stock.quant'].search( + [('product_id', '=', line.product_id.id), + ('location_id', '=', self.pack_location_id.id)]) + if stock_quant: + line.total_available_quantity = stock_quant.quantity + else: + line.total_available_quantity = stock_quant.quantity diff --git a/all_in_one_sales_kit/models/res_config_settings.py b/all_in_one_sales_kit/models/res_config_settings.py new file mode 100644 index 000000000..a37435f21 --- /dev/null +++ b/all_in_one_sales_kit/models/res_config_settings.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + """ This is to add new fields to the settings.res.config.settings is + inherited.""" + _inherit = 'res.config.settings' + + show_product_image_in_sale_report = fields.Boolean( + string="Show Product Image", default=False, + help="Enable Show Product Image") + sale_document_approve = fields.Boolean( + config_parameter='all_in_one_sales_kit.sale_document_approve', + string="Sale Document Approval", + help="Sale Approval") + product_restriction = fields.Boolean( + string='Out Of Stock Product Restriction', + help='Enable Out Of Stock Product Restriction') + check_stock = fields.Selection( + [('on_hand_quantity', 'On Hand Quantity'), + ('forecast_quantity', 'Forecast Quantity')], string="Based On", + help='Choose the type of restriction') + automate_invoice = fields.Boolean( + string='Create Invoice', default=False, + help="Create invoices for sales order") + automate_validate_invoice = fields.Boolean( + string='Validate Invoice', default=False, + help="Automate validation of invoice") + automate_print_invoices = fields.Boolean( + string='Print Invoices', default=False, + help="Print invoice from corresponding sales order") + + @api.model + def set_values(self): + """The function set_values() is to store the new fields values.""" + self.env['ir.config_parameter'].sudo().set_param( + 'sale_product_image.show_product_image_in_sale_report', + self.show_product_image_in_sale_report) + self.env['ir.config_parameter'].sudo().set_param( + 'sale_stock_restrict.product_restriction', + self.product_restriction) + self.env['ir.config_parameter'].sudo().set_param( + 'sale_stock_restrict.check_stock', self.check_stock) + self.env['ir.config_parameter'].sudo().set_param( + 'automate_print_invoices', self.automate_print_invoices) + self.env['ir.config_parameter'].sudo().set_param( + 'automate_invoice', self.automate_invoice) + self.env['ir.config_parameter'].sudo().set_param( + 'automate_validate_invoice', self.automate_validate_invoice) + res = super(ResConfigSettings, self).set_values() + return res + + def get_values(self): + """Show the new field values.""" + res = super(ResConfigSettings, self).get_values() + ir_config_param = self.env['ir.config_parameter'].sudo().get_param + res.update( + show_product_image_in_sale_report=ir_config_param( + 'sale_product_image.show_product_image_in_sale_report', + self.show_product_image_in_sale_report), + product_restriction=ir_config_param( + 'sale_stock_restrict.product_restriction'), + check_stock=ir_config_param('sale_stock_restrict.check_stock'), + automate_print_invoices=ir_config_param('automate_print_invoices'), + automate_invoice=ir_config_param('automate_invoice'), + automate_validate_invoice=ir_config_param( + 'automate_validate_invoice'), + ) + return res diff --git a/all_in_one_sales_kit/models/res_partner.py b/all_in_one_sales_kit/models/res_partner.py new file mode 100644 index 000000000..e873b9bae --- /dev/null +++ b/all_in_one_sales_kit/models/res_partner.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import models + + +class ResPartner(models.Model): + """res.partner is inherited.""" + _inherit = 'res.partner' + + def action_view_sale_order(self): + """This is to add a new pivot view to customer to show + their sale orders.""" + action = self.env['ir.actions.act_window']._for_xml_id( + 'sale.act_res_partner_2_sale_order') + all_child = self.with_context(active_test=False).search( + [('id', 'child_of', self.ids)]) + action["domain"] = [("partner_id", "in", all_child.ids)] + action["view_mode"] = "tree,kanban,form,graph,pivot" + action["views"] = [(False, 'tree'), (False, 'kanban'), + (False, 'form'), (False, 'graph'), (False, 'pivot')] + return action diff --git a/all_in_one_sales_kit/models/res_users.py b/all_in_one_sales_kit/models/res_users.py new file mode 100644 index 000000000..8626ef33f --- /dev/null +++ b/all_in_one_sales_kit/models/res_users.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import fields, models + + +class ResUsers(models.Model): + """Here res.users is inherited to add two fields.""" + + _inherit = 'res.users' + + discount_control = fields.Boolean( + string='Discount Control', default=False, + help="Enable to set a discount limit value.") + allow_discount = fields.Float( + string='Allow Discount', + help="Enter the discount limit in percentage here.") diff --git a/all_in_one_sales_kit/models/sale_order.py b/all_in_one_sales_kit/models/sale_order.py new file mode 100644 index 000000000..9f99859a2 --- /dev/null +++ b/all_in_one_sales_kit/models/sale_order.py @@ -0,0 +1,473 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 itertools +from odoo import api, fields, models, _ +from odoo.exceptions import UserError + + +class SaleOrder(models.Model): + """Inherits Sales""" + _inherit = "sale.order" + + is_version = fields.Boolean(string="Is Version", + help="For checking version or not") + version_count = fields.Integer(string="Sale Version Count", + compute='_compute_version_count', + help="Count of version created") + current_version_id = fields.Many2one("sale.order", + string="Current Version", + help="For creating versions") + version_ids = fields.One2many("sale.order", + string="Version", + inverse_name="current_version_id", + help="Versions created") + quotation_ref = fields.Char(string='Quotation Reference', + copy=False, readonly=True, tracking=True, + help="Quotation Reference") + state = fields.Selection( + selection_add=[('waiting_for_approval', 'Waiting For Approval'), + ('sale',)]) + approval_user_id = fields.Many2one('res.users', + string='Discount Approved By', + help="Discount approving person.") + onhand_check = fields.Boolean(string='Enable OnHand', + help='To check whether it is based on' + ' on hand quantity') + forecast_check = fields.Boolean(string='Enable Forecast', + help='To check whether it is based on' + ' Forecast quantity') + automate_print_invoices = fields.Boolean( + string='Print Invoices', + help="Print invoices for corresponding sale orders") + signature = fields.Binary(string='Signature', + help="Field for adding " + "the signature of the " + "sales person") + check_signature = fields.Boolean(compute='_compute_check_signature', + help="To check signature approval is " + "needed") + settings_approval = fields.Boolean(compute='_compute_settings_approval', + help="To check signature approval is " + "enabled in settings") + active = fields.Boolean(string='Active', help='Active', default=True) + user_salesperson = fields.Boolean(string="User Salesperson", + compute="_compute_user_salesperson", + help="Check if user is salesperson") + + @api.depends('user_salesperson') + def _compute_user_salesperson(self): + """Computes the user_salesperson field based on login user""" + for rec in self: + if rec.user_id == rec.env.user: + rec.user_salesperson = True + else: + rec.user_salesperson = False + + @api.depends('signature') + def _compute_check_signature(self): + """In this function computes the value of + the boolean field check signature + which is used to hide/unhide the validate + button in the current document""" + if self.env['ir.config_parameter'].sudo().get_param( + 'all_in_one_sales_kit.sale_document_approve'): + if self.signature: + self.check_signature = True + else: + self.check_signature = False + else: + self.check_signature = True + + def action_create_versions(self): + """For creating the versions of the sale order""" + sale_order_copy_id = self.copy() + sale_order_copy_id.is_version = True + length = len(self.version_ids) + sale_order_copy_id.name = "%s-%s" % (self.name, str(length + 1)) + + self.write({'version_ids': [(4, sale_order_copy_id.id)]}) + + @api.depends('version_ids') + def _compute_version_count(self): + """For calculating the number of versions created""" + for sale in self: + sale.version_count = len(sale.version_ids) + + @api.depends('partner_id') + def _compute_settings_approval(self): + """Computes the settings_approval field based on settings field.""" + for rec in self: + if rec.env['ir.config_parameter'].sudo().get_param( + 'all_in_one_sales_kit.sale_document_approve'): + rec.settings_approval = True + else: + rec.settings_approval = False + + def action_view_versions(self): + """Action for viewing versions""" + action = { + "type": "ir.actions.act_window", + "view_mode": "kanban,tree,form", + "name": _("Sale Order Versions"), + "res_model": self._name, + "domain": [('id', 'in', self.version_ids.ids)], + "target": "current", + } + return action + + def action_confirm(self): + """Override the confirm button of the sale order for cancelling the + other versions and making the current version main,also method for + confirming the sale order discount and sending mail for the approving + person if approval limit crossed.Super the method create to confirm + quotation, create and validate invoice""" + res = super().action_confirm() + automate_invoice = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'automate_invoice') + automate_print_invoices = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'automate_print_invoices') + automate_validate_invoice = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'automate_validate_invoice') + if automate_print_invoices: + self.automate_print_invoices = True + if automate_invoice: + self._create_invoices() + if automate_validate_invoice: + self.invoice_ids.action_post() + if not self.version_ids: + parent_sale = self.current_version_id + versions = parent_sale.mapped('version_ids').ids + if versions: + versions.append(parent_sale.id) + for version in parent_sale.version_ids: + if version.state == 'sale': + # Updating the version name into main version name and + # other versions state into cancel + version.current_version_id.update({'is_version': True, + 'state': 'cancel'}) + version.update({'version_ids': versions, + "name": version.current_version_id.name, + 'is_version': False}) + if version.state == 'draft': + version.update({'state': 'cancel'}) + else: + if self.state == 'sale': + for sale in self.version_ids: + sale.update({'state': 'cancel'}) + low_qty = ["Can't confirm the sale order due to: \n"] + for rec in self.order_line: + product_restriction = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'sale_stock_restrict.product_restriction') + check_stock = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'sale_stock_restrict.check_stock') + if product_restriction: + if rec.product_id.detailed_type == 'product': + if check_stock == 'on_hand_quantity': + if rec.product_uom_qty > rec.qty_available: + self.onhand_check = True + onhand_qty_list = "You have added %s units of %s" \ + " but you only have %s units" \ + " available.\n" % ( + rec.product_uom_qty, + rec.product_id.name, + rec.qty_available) + low_qty.append(onhand_qty_list) + + if check_stock == 'forecast_quantity': + if rec.product_uom_qty > rec.forecast_quantity: + self.forecast_check = True + forecast_qty_list = "You have added %s" \ + " units of %s but " \ + "you only have" \ + " %s units available.\n" % ( + rec.product_uom_qty, + rec.product_id.name, + rec.forecast_quantity) + low_qty.append(forecast_qty_list) + listToStr = ' '.join(map(str, low_qty)) + if self.onhand_check: + raise UserError(listToStr) + if self.forecast_check: + raise UserError(listToStr) + to_approve = False + discount_vals = self.order_line.mapped('discount') + approval_users = self.env.ref( + 'all_in_one_sales_kit.group_approval_manager').users + user_discount = self.env.user.allow_discount + if self.env.user.discount_control == True: + for rec in discount_vals: + if rec > user_discount: + to_approve = True + break + if to_approve: + display_id = self.id + action_id = self.env.ref( + 'sale.action_quotations_with_onboarding').id + base_url = self.env['ir.config_parameter'].sudo().get_param( + 'web.base.url') + redirect_link = "/web#id=%s&cids=1&menu_id=178&action=%s" \ + "&model" \ + "=sale.order&view_type=form" % ( + display_id, action_id) + url = base_url + redirect_link + for user in approval_users: + mail_body = """ +

Hello,

+

New sale order '%s' + Created with Discount by '%s' + need your approval on it.

+

To Approve, Cancel Order, + Click on the Following + Link: + + Click Me +

+

Thank You.

""" % (self.name, + self.env.user.name, + url) + mail_values = { + 'subject': "'%s' Discount Approval Request" % (self.name), + 'body_html': mail_body, + 'email_to': user.partner_id.email, + 'model': 'sale.order', + } + mail_id = self.env['mail.mail'].sudo().create(mail_values) + mail_id.sudo().send() + self.state = 'waiting_for_approval' + for line in self.order_line: + if line.product_id.is_pack: + for record in line.product_id.pack_products_ids: + dest_loc = self.env.ref( + 'stock.stock_location_customers').id + self.env['stock.move'].create({ + 'name': record.product_id.name, + 'product_id': record.product_id.id, + 'product_uom_qty': + record.quantity * line.product_uom_qty, + 'product_uom': record.product_id.uom_id.id, + 'picking_id': self.picking_ids[0].id, + 'location_id': + self.picking_ids.picking_type_id.default_location_src_id.id, + 'location_dest_id': dest_loc, + }) + return res + + @api.model + def create(self, vals): + """Method for generating sequence for quotation """ + res = super(SaleOrder, self).create(vals) + seq_val = self.env.ref( + 'all_in_one_sales_kit.seq_quotation').id + res.quotation_ref = self.env['ir.sequence'].browse( + seq_val).next_by_id() + return res + + def action_waiting_approval(self): + """Method for approving the sale order discount""" + self.approval_user_id = self.env.user.id + self.state = 'sale' + + def action_print_invoice(self): + """Method to print invoice""" + data = self.invoice_ids + return self.env.ref('account.account_invoices').report_action(data) + + @api.model + def get_data(self): + """To get data to the sales dashboard.""" + domain = [('user_id', '=', self.env.user.id)] + quotation = self.env['sale.order'].search( + domain + [('state', '=', 'draft')]) + my_sale_order_templates = self.env['sale.order'].search( + domain + [('state', '=', 'sale')]) + quotation_sent = self.env['sale.order'].search( + domain + [('state', '=', 'sent')]) + quotation_cancel = self.env['sale.order'].search( + domain + [('state', '=', 'cancel')]) + customers = self.env['res.partner'].search([]) + to_invoice = self.env['sale.order'].search( + domain + [('invoice_status', '=', 'to invoice')]) + products = self.env['product.template'].search([]) + return { + 'quotation': len(quotation), + 'my_sale_order_templates': len(my_sale_order_templates), + 'quotation_sent': len(quotation_sent), + 'quotation_cancel': len(quotation_cancel), + 'customers': len(customers), + 'products': len(products), + 'to_invoice': len(to_invoice), + } + + @api.model + def get_value(self, start_date, end_date): + """It is to pass values according to start and end date to the + dashboard.""" + if start_date and end_date: + domain = [('user_id', '=', self.env.user.id), + ('date_order', '>=', start_date), + ('date_order', '<=', end_date)] + elif start_date: + domain = [('user_id', '=', self.env.user.id), + ('date_order', '>=', start_date)] + elif end_date: + domain = [('user_id', '=', self.env.user.id), + ('date_order', '<=', end_date)] + + quotation = self.env['sale.order'].search( + domain + [('state', '=', 'draft')]) + my_sale_order_templates = self.env['sale.order'].search( + domain + [('state', '=', 'sale')]) + quotation_sent = self.env['sale.order'].search( + domain + [('state', '=', 'sent')]) + quotation_cancel = self.env['sale.order'].search( + domain + [('state', '=', 'cancel')]) + customers = self.env['res.partner'].search([]) + products = self.env['product.template'].search([]) + to_invoice = self.env['sale.order'].search( + domain + [('invoice_status', '=', 'to invoice')]) + return { + 'quotation': len(quotation), + 'my_sale_order_templates': len(my_sale_order_templates), + 'quotation_sent': len(quotation_sent), + 'quotation_cancel': len(quotation_cancel), + 'customers': len(customers), + 'products': len(products), + 'to_invoice': len(to_invoice), + } + + @api.model + def get_lead_customer(self): + """Returns customer data to the graph of dashboard""" + lead_template = {} + sale = {} + partner_id = self.env['res.partner'].sudo().search([]) + vals = self.env['sale.order'].sudo().search([ + ]).mapped('partner_id').ids + for record in partner_id: + if record.id in vals: + record.ref = vals.count(record.id) + sale.update({record: vals.count(record.id)}) + sort = dict( + sorted(sale.items(), key=lambda item: item[1], reverse=True)) + out = dict(itertools.islice(sort.items(), 10)) + for count in out: + lead_template[count.name] = out[count] + return { + 'lead_templates': lead_template, + } + + @api.model + def get_lead_product(self): + """Returns product data to the graph of dashboard""" + lead_template = {} + sale = {} + product_id = self.env['product.template'].search([]) + for record in product_id: + sale.update({record: record.sales_count}) + sort = dict( + sorted(sale.items(), key=lambda item: item[1], reverse=True)) + out = dict(itertools.islice(sort.items(), 10)) + for product in out: + lead_template[product.name] = out[product] + return { + 'lead_templates': lead_template, + } + + @api.model + def get_lead_order(self): + """Returns lead sale order data to the graph of dashboard""" + lead_template = {} + sale = {} + order_id = self.env['sale.order'].search([('state', '=', 'sale')]) + for record in order_id: + sale.update({record: record.amount_total}) + sort = dict( + sorted(sale.items(), key=lambda item: item[1], reverse=True)) + out = dict(itertools.islice(sort.items(), 10)) + for order in out: + lead_template[order.name] = out[order] + return { + 'lead_templates': lead_template, + } + + @api.model + def get_my_monthly_comparison(self): + """Returns my monthly sale count data to the graph of dashboard""" + lead_template = {} + sales_order = self.env['sale.order'].search( + [('user_id', '=', self.env.user.id)]) + list = [rec.date_order.month for rec in sales_order] + for i in range(1, 13): + count = list.count(i) + lead_template.update({ + i: count + }) + return { + 'lead_templates': lead_template, + } + + @api.model + def get_sales_team(self): + """Returns sales team data to the graph of dashboard""" + lead_template = {} + sale = {} + sales_team = self.env['crm.team'].search([]) + for record in sales_team: + total = sum(self.env['sale.order'].search( + [('state', '=', 'sale'), + ('team_id', '=', record.id)]).mapped('amount_total')) + sale.update({record: total}) + sort = dict( + sorted(sale.items(), key=lambda item: item[1], reverse=True)) + out = dict(itertools.islice(sort.items(), 10)) + for team in out: + lead_template[team.name] = out[team] + return { + 'lead_templates': lead_template, + } + + @api.model + def get_least_sold(self): + """Returns least sold product data to the graph of dashboard""" + lead_template = {} + sale = {} + product_id = self.env['product.template'].search([]) + for record in product_id: + if record.sales_count != 0: + sale.update({record: record.sales_count}) + sort = dict( + sorted(sale.items(), key=lambda item: item[1], reverse=False)) + out = dict(itertools.islice(sort.items(), 10)) + for product in out: + lead_template[product.name] = out[product] + return { + 'lead_templates': lead_template, + } diff --git a/all_in_one_sales_kit/models/sale_order_line.py b/all_in_one_sales_kit/models/sale_order_line.py new file mode 100644 index 000000000..78ffddde0 --- /dev/null +++ b/all_in_one_sales_kit/models/sale_order_line.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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 odoo import api, fields, models, _ +from odoo.tools import float_compare + + +class SaleOrderLine(models.Model): + """Inherits sale.order.line.""" + _inherit = 'sale.order.line' + + sale_date = fields.Datetime(comodel_name='sale.order', string='Sale Date', + related='order_id.date_order', store=True, + help="Sale order date") + product_warehouse_id = fields.Many2one( + 'stock.warehouse', + string='Warehouse', help='Warehouses') + order_line_image = fields.Binary(string="Image", + related="product_id.image_1920", + help="Product image should be shown or " + "not.") + contact_email = fields.Char(string="Email", + related="order_partner_id.email", + help="Email of the customer.") + contact_phone = fields.Char(string="Phone no.", + related="order_partner_id.phone", + help="Phone no. of the customer.") + qty_available = fields.Float(string="On Hand Quantity", + help='Count of On Hand quantity') + forecast_quantity = fields.Float(string="Forecast Quantity", + help='Count of Forecast quantity') + discount = fields.Float(string='Discount (%)', + digits=(16, 2), default=0.0, + help="Discount in percentage.") + total_discount = fields.Float(string="Total Discount", + default=0.0, store=True, + help="Total Discount.") + barcode_scan = fields.Char(string='Product Barcode', + help="Here you can provide " + "the barcode for the product") + + def action_get_product_form(self): + """ + This method returns an action that opens a form view for a specific product. + It sets the order partner ID based on the order's partner ID and constructs an action + to open the product's form view with the product's details. + :return: Dictionary representing an action to open the product's form view. + :rtype: dict + """ + self.product_id.order_partner_id = self.order_id.partner_id.id + return { + 'name': self.product_id.name, + 'view_mode': 'form', + 'res_model': 'product.product', + 'type': 'ir.actions.act_window', + 'target': 'current', + 'res_id': self.product_id.id + } + + def _action_launch_stock_rule(self, previous_product_uom_qty=False): + """ + Overwriting the function for adding functionalities of + multiple warehouses in the sale order line. + param previous_product_uom_qty(str): + Uom quantity of previous product + boolean: Returns True, if the picking created. + """ + if self._context.get("skip_procurement"): + return True + precision = self.env['decimal.precision'].precision_get( + 'Product Unit of Measure') + procurements = [] + for line in self: + line = line.with_company(line.company_id) + if line.state != 'sale' or not line.product_id.type in ( + 'consu', 'product'): + continue + qty = line._get_qty_procurement(previous_product_uom_qty) + if float_compare(qty, line.product_uom_qty, + precision_digits=precision) == 0: + continue + group_id = line._get_procurement_group() + if not group_id: + group_id = self.env['procurement.group'].create( + line._prepare_procurement_group_vals()) + line.order_id.procurement_group_id = group_id + else: + updated_vals = {} + if group_id.partner_id != line.order_id.partner_shipping_id: + updated_vals.update( + {'partner_id': line.order_id.partner_shipping_id.id}) + if group_id.move_type != line.order_id.picking_policy: + updated_vals.update( + {'move_type': line.order_id.picking_policy}) + if updated_vals: + group_id.write(updated_vals) + values = line._prepare_procurement_values(group_id=group_id) + #replacing default warehouse_id into product_warehouse_id in the + #sale order line and adding it into procurement values. + if line.product_warehouse_id: + values['warehouse_id'] = line.product_warehouse_id + product_qty = line.product_uom_qty - qty + line_uom = line.product_uom + quant_uom = line.product_id.uom_id + product_qty, procurement_uom = line_uom._adjust_uom_quantities( + product_qty, quant_uom) + procurements.append(self.env['procurement.group'].Procurement( + line.product_id, product_qty, procurement_uom, + line.order_id.partner_shipping_id.property_stock_customer, + line.product_id.display_name, line.order_id.name, + line.order_id.company_id, values)) + if procurements: + self.env['procurement.group'].run(procurements) + orders = self.mapped('order_id') + for order in orders: + pickings_to_confirm = order.picking_ids.filtered( + lambda p: p.state not in ['cancel', 'done']) + if pickings_to_confirm: + pickings_to_confirm.action_confirm() + return True + + @api.onchange('product_id') + def _onchange_product_id(self): + """it is to check product stock according th the chosen product + restriction.""" + product_restriction = self.env['ir.config_parameter'].sudo().get_param( + 'sale_stock_restrict.product_restriction') + check_stock = self.env[ + 'ir.config_parameter'].sudo().get_param( + 'sale_stock_restrict.check_stock') + if product_restriction: + if check_stock == 'on_hand_quantity': + self.qty_available = self.product_id.qty_available + if check_stock == 'forecast_quantity': + self.forecast_quantity = self.product_id.virtual_available + + @api.onchange('barcode_scan') + def _onchange_barcode_scan(self): + """It is to add the scanned products to the order line.""" + product_rec = self.env['product.product'] + if self.barcode_scan: + product = product_rec.search([('barcode', '=', self.barcode_scan)]) + self.product_id = product.id + + def action_get_product_history_data(self): + """It is to pass previous history of the chosen product for that + customer.""" + values = [] + customer_id = self.order_id.partner_id + customer_order = self.env['sale.order'].search( + [('partner_id', '=', customer_id.id), ( + 'state', 'in', ('sale', 'done'))]) + for order in customer_order: + for line in order.order_line: + if line.product_id == self.product_id: + values.append((0, 0, {'sale_order_id': order.id, + 'history_price': line.price_unit, + 'history_qty': line.product_uom_qty, + 'history_total': order.amount_total + })) + history_id = self.env['product.sale.order.history'].create({ + 'product_id': self.product_id.id, + 'product_sale_history_ids': values + }) + return { + 'name': 'Customer Product Sales History', + 'view_mode': 'form', + 'res_model': 'product.sale.order.history', + 'type': 'ir.actions.act_window', + 'target': 'new', + 'res_id': history_id.id + } + + def action_add_catalog_control(self): + """It is to add function to the button catalog.""" + return { + 'type': 'ir.actions.act_window', + 'name': _('Products'), + 'context': {'order_id': self.env.context.get('id')}, + 'res_model': 'product.product', + 'view_mode': 'kanban,tree,form', + 'target': 'current', + } diff --git a/all_in_one_sales_kit/models/sales_report.py b/all_in_one_sales_kit/models/sales_report.py new file mode 100644 index 000000000..12ddcae62 --- /dev/null +++ b/all_in_one_sales_kit/models/sales_report.py @@ -0,0 +1,540 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# 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 io +from odoo import api, fields, models + +try: + from odoo.tools.misc import xlsxwriter +except ImportError: + import xlsxwriter + + +class SalesReport(models.Model): + """A new class sales.report is created.""" + _name = "sales.report" + _description = "Sales Report" + + sale_report = fields.Char(string="Sale Report", help="Sale Report") + date_from = fields.Datetime(string="Date From", + help="Date from which the report should be " + "viewed.") + date_to = fields.Datetime(string="Date to", + help="Date to which the report should be viewed.") + report_type = fields.Selection([ + ('report_by_order', 'Report By Order'), + ('report_by_order_detail', 'Report By Order Detail'), + ('report_by_product', 'Report By Product'), + ('report_by_categories', 'Report By Categories'), + ('report_by_salesperson', 'Report By Sales Person'), + ('report_by_state', 'Report By State')], default='report_by_order', + help="Report type", string="Report type") + + @api.model + def sale_report(self, option): + """This is to pass values to the sales report.""" + report_values = self.env['sales.report'].browse(option) + data = { + 'report_type': report_values.report_type, + 'model': self, + } + if report_values.date_from: + data.update({ + 'date_from': report_values.date_from, + }) + if report_values.date_to: + data.update({ + 'date_to': report_values.date_to, + }) + filters = self.get_filter(option) + lines = self._get_report_values(data).get('SALE') + main_line = self._get_report_values(data).get('sale_main') + + return { + 'name': "Sale Orders", + 'type': 'ir.actions.client', + 'tag': 's_r', + 'orders': data, + 'filters': filters, + 'report_lines': lines, + 'report_main_line': main_line, + } + + def get_filter(self, option): + """It is to get value for the chosen filter.""" + data = self.get_filter_data(option) + filters = {} + if data.get('report_type') == 'report_by_order': + filters['report_type'] = 'Report By Order' + elif data.get('report_type') == 'report_by_order_detail': + filters['report_type'] = 'Report By Order Detail' + elif data.get('report_type') == 'report_by_product': + filters['report_type'] = 'Report By Product' + elif data.get('report_type') == 'report_by_categories': + filters['report_type'] = 'Report By Categories' + elif data.get('report_type') == 'report_by_salesperson': + filters['report_type'] = 'Report By Sales Person' + elif data.get('report_type') == 'report_by_state': + filters['report_type'] = 'Report By State' + else: + filters['report_type'] = 'report_by_order' + return filters + + def get_filter_data(self, option): + """It is to get data according to the filter selected.""" + filter_dict = { + 'report_type': self.env['sales.report'].browse(option).report_type, + } + return filter_dict + + @api.model + def create(self, vals): + """It super create.""" + res = super(SalesReport, self).create(vals) + return res + + def write(self, vals): + """It super write.""" + res = super(SalesReport, self).write(vals) + return res + + def _get_report_sub_lines(self, data): + """This is to get the table lines according to the filter.""" + report_sub_lines = [] + if data.get('report_type') == 'report_by_order': + query = ''' + select so.id,so.name as number,so.date_order, + so.partner_id,so.amount_total, + so.user_id,res_partner.name as customer, + res_users.partner_id as user_partner,so.id as id, + sum(sale_order_line.product_uom_qty), + (SELECT res_partner.name as sales_man + FROM res_partner + WHERE res_partner.id = res_users.partner_id) + from sale_order as so + inner join res_partner + on so.partner_id = res_partner.id + inner join res_users + on so.user_id = res_users.id + inner join sale_order_line + on so.id = sale_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += "group by so.user_id,res_users.partner_id," \ + "res_partner.name,so.partner_id,so.date_order," \ + "so.name,so.amount_total,so.id" + self._cr.execute(query) + report_by_order = self._cr.dictfetchall() + report_sub_lines.append(report_by_order) + elif data.get('report_type') == 'report_by_order_detail': + query = ''' + SELECT so.id,so.name as number,so.date_order, + res_partner.name as customer, + rc.name as company, + product_template.name as product, + product_product.default_code, + so_line.product_uom_qty, + so_line.price_subtotal,so.amount_total, + so.partner_id, + so.user_id,ru.id,so_line.product_id, + sum(so_line.product_uom_qty), + (SELECT res_partner.name as salesman + FROM res_partner + WHERE res_partner.id = res_users.partner_id) + from sale_order as so + inner join sale_order_line as so_line + on so.id = so_line.order_id + inner join product_product + ON so_line.product_id=product_product.id + inner join product_template + ON product_product.product_tmpl_id = + product_template.id + inner join res_partner + on so.partner_id=res_partner.id + inner join res_users on so.user_id = res_users.id + inner join res_company as rc on so.company_id=rc.id + inner join res_users as ru on so.user_id=ru.id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += ''' group by so.user_id, so.name, so.id,so.date_order, + res_partner.name,rc.name,product_template.name, + product_product.default_code,so_line.product_uom_qty, + so_line.price_subtotal,so.amount_total,so.partner_id, + so.user_id,ru.id,so_line.product_id, + res_users.partner_id + ''' + self._cr.execute(query) + report_by_order_details = self._cr.dictfetchall() + report_sub_lines.append(report_by_order_details) + elif data.get('report_type') == 'report_by_product': + query = ''' + SELECT so.id,so.date_order, + product_template.name as product, + product_category.name as category, + product_product.default_code, + so_line.product_uom_qty, + so.amount_total,so.name as number + From sale_order as so + inner join sale_order_line + as so_line on so.id = so_line.order_id + inner join product_product + ON so_line.product_id=product_product.id + inner join product_template + ON product_product.product_tmpl_id = + product_template.id + inner join product_category + on product_category.id = product_template.categ_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += "group by so.id,so.date_order,product_template.name," \ + "product_category.name,product_product.default_code," \ + "so_line.product_uom_qty" + self._cr.execute(query) + report_by_product = self._cr.dictfetchall() + report_sub_lines.append(report_by_product) + elif data.get('report_type') == 'report_by_categories': + query = ''' + select product_category.name, + sum(so_line.product_uom_qty) as qty, + sum(so_line.price_subtotal) as amount_total + from sale_order_line as so_line + inner join product_template + on so_line.product_id = product_template.id + inner join product_category + on product_category.id = product_template.categ_id + inner join sale_order + on so_line.order_id = sale_order.id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where sale_order.date_order >= '%s' " % data.get( + 'date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "sale_order.date_order <= '%s' " % data.get( + 'date_to') + query += "group by product_category.name" + self._cr.execute(query) + report_by_categories = self._cr.dictfetchall() + report_sub_lines.append(report_by_categories) + elif data.get('report_type') == 'report_by_salesperson': + query = ''' + select res_partner.name, + sum(sale_order_line.product_uom_qty) as qty, + sum(sale_order_line.price_subtotal) as amount, + count(so.id) as order + from sale_order as so + inner join res_users + on so.user_id = res_users.id + inner join res_partner + on res_users.partner_id = res_partner.id + inner join sale_order_line + on so.id = sale_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += "group by res_partner.name" + self._cr.execute(query) + report_by_salesperson = self._cr.dictfetchall() + report_sub_lines.append(report_by_salesperson) + elif data.get('report_type') == 'report_by_state': + query = ''' + select so.state,count(so.id), + sum(sale_order_line.product_uom_qty) as qty, + sum(sale_order_line.price_subtotal) + as amount from sale_order as so + inner join sale_order_line + on so.id = sale_order_line.order_id + ''' + term = 'Where ' + if data.get('date_from'): + query += "Where so.date_order >= '%s' " % data.get('date_from') + term = 'AND ' + if data.get('date_to'): + query += term + "so.date_order <= '%s' " % data.get('date_to') + query += "group by so.state" + self._cr.execute(query) + report_by_state = self._cr.dictfetchall() + report_sub_lines.append(report_by_state) + return report_sub_lines + + def _get_report_total_value(self, data, report): + """It is to pass total for each report type.""" + report_main_lines = [] + if data.get('report_type') == 'report_by_order': + self._cr.execute(''' + select count(so.id) as order,sum(so.amount_total) as amount + from sale_order as so + ''') + report_by_order = self._cr.dictfetchall() + report_main_lines.append(report_by_order) + elif data.get('report_type') == 'report_by_order_detail': + self._cr.execute(''' + select count(so_line.id) as order, + sum(so_line.price_subtotal) as total + from sale_order_line as so_line + ''') + report_by_order_detail = self._cr.dictfetchall() + report_main_lines.append(report_by_order_detail) + elif data.get('report_type') == 'report_by_product': + self._cr.execute(''' + select count(so_line.product_id) as order, + sum(so_line.price_subtotal) as amount + from sale_order_line as so_line + ''') + report_by_product = self._cr.dictfetchall() + report_main_lines.append(report_by_product) + else: + report_main_lines = False + return report_main_lines + + def _get_report_values(self, data): + """It is to pass sublines for report.""" + docs = data['model'] + if data['report_type'] == 'report_by_order_detail': + report = ['Report By Order Detail'] + elif data['report_type'] == 'report_by_product': + report = ['Report By Product'] + elif data['report_type'] == 'report_by_categories': + report = ['Report By Categories'] + elif data['report_type'] == 'report_by_salesperson': + report = ['Report By Sales Person'] + elif data['report_type'] == 'report_by_state': + report = ['Report By State'] + else: + report = ['Report By Order'] + report_res_total = self._get_report_total_value(data, report) + if data.get('report_type'): + report_res = self._get_report_sub_lines(data)[0] + else: + report_res = self._get_report_sub_lines(data) + if data.get('report_type') == 'report_by_order': + report_res_total = self._get_report_total_value(data, report)[0] + return { + 'doc_ids': self.ids, + 'docs': docs, + 'SALE': report_res, + 'sale_main': report_res_total, + } + + def get_xlsx_report(self, data, response): + """This function is to pass values to the xlsx report.""" + report_data_main = data['report_lines'] + output = io.BytesIO() + filters = data['filters'] + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet() + head = workbook.add_format({'align': 'center', 'bold': True, + 'font_size': '20px'}) + heading = workbook.add_format( + {'align': 'center', 'bold': True, 'font_size': '10px', + 'border': 2, + 'border_color': 'black'}) + txt_l = workbook.add_format( + {'font_size': '10px', 'border': 1, 'bold': True}) + sheet.merge_range('A2:H3', + 'Sales Report', + head) + if filters.get('report_type') == 'Report By Order': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('A7', 'Sale', heading) + sheet.write('B7', 'Date Order', heading) + sheet.write('C7', 'Customer', heading) + sheet.write('D7', 'Sales Person', heading) + sheet.write('E7', 'Total Qty', heading) + sheet.write('F7', 'Amount Total', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + sheet.set_column(8, 5, 15) + for rec_data in report_data_main: + row += 1 + sheet.write(row, col, rec_data['number'], txt_l) + sheet.write(row, col + 1, rec_data['date_order'], txt_l) + sheet.write(row, col + 2, rec_data['customer'], txt_l) + sheet.write(row, col + 3, rec_data['sales_man'], txt_l) + sheet.write(row, col + 4, rec_data['sum'], txt_l) + sheet.write(row, col + 5, rec_data['amount_total'], txt_l) + if filters.get('report_type') == 'Report By Order Detail': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('A7', 'Sale', heading) + sheet.write('B7', 'Date Order', heading) + sheet.write('C7', 'Customer', heading) + sheet.write('D7', 'Company', heading) + sheet.write('E7', 'Sales Person', heading) + sheet.write('F7', 'Product Name', heading) + sheet.write('G7', 'Product Code', heading) + sheet.write('H7', 'Quantity', heading) + sheet.write('I7', 'Price Subtotal', heading) + sheet.write('J7', 'Amount Total', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + sheet.set_column(8, 5, 15) + sheet.set_column(9, 6, 15) + sheet.set_column(10, 7, 15) + sheet.set_column(11, 8, 15) + sheet.set_column(12, 9, 15) + for rec_data in report_data_main: + row += 1 + product = [val for val in rec_data['product'].values()] + sheet.write(row, col, rec_data['number'], txt_l) + sheet.write(row, col + 1, rec_data['date_order'], txt_l) + sheet.write(row, col + 2, rec_data['customer'], txt_l) + sheet.write(row, col + 3, rec_data['company'], txt_l) + sheet.write(row, col + 4, rec_data['salesman'], txt_l) + sheet.write(row, col + 5, product[0], txt_l) + sheet.write(row, col + 6, rec_data['default_code'], txt_l) + sheet.write(row, col + 7, rec_data['product_uom_qty'], txt_l) + sheet.write(row, col + 8, rec_data['price_subtotal'], txt_l) + sheet.write(row, col + 9, rec_data['amount_total'], txt_l) + if filters.get('report_type') == 'Report By Product': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('A7', 'Product', heading) + sheet.write('B7', 'Category', heading) + sheet.write('C7', 'Product Code', heading) + sheet.write('D7', 'Quantity', heading) + sheet.write('E7', 'Amount Total', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + sheet.set_column(7, 4, 15) + for rec_data in report_data_main: + row += 1 + product = [val for val in rec_data['product'].values()] + sheet.write(row, col, product[0], txt_l) + sheet.write(row, col + 1, rec_data['category'], txt_l) + sheet.write(row, col + 2, rec_data['default_code'], txt_l) + sheet.write(row, col + 3, rec_data['product_uom_qty'], txt_l) + sheet.write(row, col + 4, rec_data['amount_total'], txt_l) + if filters.get('report_type') == 'Report By Categories': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('B7', 'Category', heading) + sheet.write('C7', 'Qty', heading) + sheet.write('D7', 'Amount Total', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 1 + sheet.set_column(3, 1, 15) + sheet.set_column(4, 2, 15) + sheet.set_column(5, 3, 15) + for rec_data in report_data_main: + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['qty'], txt_l) + sheet.write(row, col + 2, rec_data['amount_total'], txt_l) + if filters.get('report_type') == 'Report By Sales Person': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('A7', 'Sales Person', heading) + sheet.write('B7', 'Total Order', heading) + sheet.write('C7', 'Total Qty', heading) + sheet.write('D7', 'Total Amount', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + for rec_data in report_data_main: + row += 1 + sheet.write(row, col, rec_data['name'], txt_l) + sheet.write(row, col + 1, rec_data['order'], txt_l) + sheet.write(row, col + 2, rec_data['qty'], txt_l) + sheet.write(row, col + 3, rec_data['amount'], txt_l) + if filters.get('report_type') == 'Report By State': + sheet.merge_range('B5:D5', 'Report Type: ' + + filters.get('report_type'), txt_l) + sheet.write('A7', 'State', heading) + sheet.write('B7', 'Total Count', heading) + sheet.write('C7', 'Quantity', heading) + sheet.write('D7', 'Amount', heading) + lst = [] + for rec in report_data_main[0]: + lst.append(rec) + row = 6 + col = 0 + sheet.set_column(3, 0, 15) + sheet.set_column(4, 1, 15) + sheet.set_column(5, 2, 15) + sheet.set_column(6, 3, 15) + for rec_data in report_data_main: + row += 1 + if rec_data['state'] == 'draft': + sheet.write(row, col, 'Quotation', txt_l) + elif rec_data['state'] == 'sent': + sheet.write(row, col, 'Quotation Sent', txt_l) + elif rec_data['state'] == 'sale': + sheet.write(row, col, 'Sale Order', txt_l) + sheet.write(row, col + 1, rec_data['count'], txt_l) + sheet.write(row, col + 2, rec_data['qty'], txt_l) + sheet.write(row, col + 3, rec_data['amount'], txt_l) + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/all_in_one_sales_kit/report/__init__.py b/all_in_one_sales_kit/report/__init__.py new file mode 100644 index 000000000..08e82c941 --- /dev/null +++ b/all_in_one_sales_kit/report/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +################################################################################ +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies(). +# Author: Cybrosys Technologies () +# +# 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_order_report diff --git a/all_in_one_sales_kit/report/invoice_analysis_templates.xml b/all_in_one_sales_kit/report/invoice_analysis_templates.xml new file mode 100644 index 000000000..d1e3c75ad --- /dev/null +++ b/all_in_one_sales_kit/report/invoice_analysis_templates.xml @@ -0,0 +1,97 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sale_order_document_templates.xml b/all_in_one_sales_kit/report/sale_order_document_templates.xml new file mode 100644 index 000000000..cca45206e --- /dev/null +++ b/all_in_one_sales_kit/report/sale_order_document_templates.xml @@ -0,0 +1,26 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sale_order_report.py b/all_in_one_sales_kit/report/sale_order_report.py new file mode 100644 index 000000000..bbada1a22 --- /dev/null +++ b/all_in_one_sales_kit/report/sale_order_report.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2022-TODAY Cybrosys Technologies(). +# +# 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 odoo import api, models + + +class SaleOrderReport(models.AbstractModel): + """It is to add new abstract model for sale_order_report.""" + _name = 'report.all_in_one_sales_kit.sale_order_report' + _description = "Sale Order Report" + + @api.model + def _get_report_values(self, docids, data=None): + """It is to pass report values.""" + if self.env.context.get('sale_order_report'): + if data.get('report_data'): + report_lines = data.get('report_data')['report_lines'] + total_amount = sum( + line.get('amount_total', 0) for line in report_lines) + data.update({'report_main_line_data': report_lines, + 'Filters': data.get('report_data')['filters'], + 'company': self.env.company, + 'total_amount': total_amount, + }) + return data diff --git a/all_in_one_sales_kit/report/sale_order_report_templates.xml b/all_in_one_sales_kit/report/sale_order_report_templates.xml new file mode 100644 index 000000000..4106a73e6 --- /dev/null +++ b/all_in_one_sales_kit/report/sale_order_report_templates.xml @@ -0,0 +1,397 @@ + + + + + + + + + + + diff --git a/all_in_one_sales_kit/report/sale_profit_templates.xml b/all_in_one_sales_kit/report/sale_profit_templates.xml new file mode 100644 index 000000000..4cf0e79c7 --- /dev/null +++ b/all_in_one_sales_kit/report/sale_profit_templates.xml @@ -0,0 +1,353 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sale_reports.xml b/all_in_one_sales_kit/report/sale_reports.xml new file mode 100644 index 000000000..144e49b78 --- /dev/null +++ b/all_in_one_sales_kit/report/sale_reports.xml @@ -0,0 +1,59 @@ + + + + + Sales + sale.report.advance + qweb-pdf + all_in_one_sales_kit.sale_profit_report_view + all_in_one_sales_kit.sale_profit_report_view + + + + Invoice Analysis + sale.report.invoice + qweb-pdf + all_in_one_sales_kit.invoice_analysis_view + all_in_one_sales_kit.invoice_analysis_view + + + + Sales Category + sale.report.category + qweb-pdf + all_in_one_sales_kit.sales_category_view + all_in_one_sales_kit.sales_category_view + + + + Product Sales Indent + sale.report.indent + qweb-pdf + all_in_one_sales_kit.sales_indent_view + all_in_one_sales_kit.sales_indent_view + + + + Sales Analysis Report + sale.report.analysis + qweb-pdf + all_in_one_sales_kit.sales_analysis_view + all_in_one_sales_kit.sales_analysis_view + + + + Hourly Sales Report + sale.report.weekly + qweb-pdf + all_in_one_sales_kit.sales_hourly_view + all_in_one_sales_kit.sales_hourly_view + + + + Sales All In One Report + sales.report + qweb-pdf + all_in_one_sales_kit.sale_order_report + all_in_one_sales_kit.sale_order_report + + diff --git a/all_in_one_sales_kit/report/sales_analysis_templates.xml b/all_in_one_sales_kit/report/sales_analysis_templates.xml new file mode 100644 index 000000000..bddedc67b --- /dev/null +++ b/all_in_one_sales_kit/report/sales_analysis_templates.xml @@ -0,0 +1,173 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sales_category_templates.xml b/all_in_one_sales_kit/report/sales_category_templates.xml new file mode 100644 index 000000000..6a910de62 --- /dev/null +++ b/all_in_one_sales_kit/report/sales_category_templates.xml @@ -0,0 +1,114 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sales_indent_templates.xml b/all_in_one_sales_kit/report/sales_indent_templates.xml new file mode 100644 index 000000000..73a4512b7 --- /dev/null +++ b/all_in_one_sales_kit/report/sales_indent_templates.xml @@ -0,0 +1,61 @@ + + + + + diff --git a/all_in_one_sales_kit/report/sales_weekly_templates.xml b/all_in_one_sales_kit/report/sales_weekly_templates.xml new file mode 100644 index 000000000..aa6901ed9 --- /dev/null +++ b/all_in_one_sales_kit/report/sales_weekly_templates.xml @@ -0,0 +1,68 @@ + + + + + diff --git a/all_in_one_sales_kit/security/all_in_one_sales_kit_groups.xml b/all_in_one_sales_kit/security/all_in_one_sales_kit_groups.xml new file mode 100644 index 000000000..f26c825ee --- /dev/null +++ b/all_in_one_sales_kit/security/all_in_one_sales_kit_groups.xml @@ -0,0 +1,20 @@ + + + + + + Sale Order Discount Approval + Category for Discount Approval + + + + Discount Approval Manager + + + + + Create Custom Fields in Sale + + + diff --git a/all_in_one_sales_kit/security/ir.model.access.csv b/all_in_one_sales_kit/security/ir.model.access.csv new file mode 100644 index 000000000..0bff65dbb --- /dev/null +++ b/all_in_one_sales_kit/security/ir.model.access.csv @@ -0,0 +1,14 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sale_report_advance_user,access.sale.report.advance.user,model_sale_report_advance,base.group_user,1,1,1,1 +access_sale_report_invoice_user,access.sale.report.invoice.user,model_sale_report_invoice,base.group_user,1,1,1,1 +access_sale_report_category_user,access.sale.report.category.user,model_sale_report_category,base.group_user,1,1,1,1 +access_sale_report_indent_user,access.sale.report.indent.user,model_sale_report_indent,base.group_user,1,1,1,1 +access_sale_report_analysis_user,access.sale.report.analysis.user,model_sale_report_analysis,base.group_user,1,1,1,1 +access_sale_report_weekly_user,access.sale.report.weekly.user,model_sale_report_weekly,base.group_user,1,1,1,1 +access_product_sale_history_line_id_user,access.product.sale.history.line.name.user,model_product_sale_history_line,base.group_user,1,1,1,1 +access_product_sale_order_history_id_user,access.product.sale.order.history.name.user,model_product_sale_order_history,base.group_user,1,1,1,1 +access_wizard_sale_order_dynamic_fields_user,access.wizard.sale.order.dynamic.fields.user,model_sale_order_dynamic_fields,base.group_user,1,1,1,1 +access_field_widget_user,access.wizard.field.widget.user,model_field_widget,base.group_user,1,1,1,1 +access_pack_product_user,access.pack.product.user,model_pack_product,base.group_user,1,1,1,1 +access_select_product_pack_user,access.select.product.pack.user,model_select_product_pack,base.group_user,1,1,1,1 +access_sales_report_user,access.sales.report.user,model_sales_report,base.group_user,1,1,1,1 diff --git a/all_in_one_sales_kit/static/description/assets/icons/capture (1).png b/all_in_one_sales_kit/static/description/assets/icons/capture (1).png new file mode 100644 index 000000000..8824deafc Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/capture (1).png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/check.png b/all_in_one_sales_kit/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/check.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/chevron.png b/all_in_one_sales_kit/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/chevron.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/cogs.png b/all_in_one_sales_kit/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/cogs.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/consultation.png b/all_in_one_sales_kit/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/consultation.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/ecom-black.png b/all_in_one_sales_kit/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/ecom-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/education-black.png b/all_in_one_sales_kit/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/education-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/hotel-black.png b/all_in_one_sales_kit/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/hotel-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/img.png b/all_in_one_sales_kit/static/description/assets/icons/img.png new file mode 100644 index 000000000..70197f477 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/img.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/license.png b/all_in_one_sales_kit/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/license.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/lifebuoy.png b/all_in_one_sales_kit/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/lifebuoy.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/manufacturing-black.png b/all_in_one_sales_kit/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/manufacturing-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/photo-capture.png b/all_in_one_sales_kit/static/description/assets/icons/photo-capture.png new file mode 100644 index 000000000..06c111758 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/photo-capture.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/pos-black.png b/all_in_one_sales_kit/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/pos-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/puzzle.png b/all_in_one_sales_kit/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/puzzle.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/restaurant-black.png b/all_in_one_sales_kit/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/restaurant-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/service-black.png b/all_in_one_sales_kit/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/service-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/trading-black.png b/all_in_one_sales_kit/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/trading-black.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/training.png b/all_in_one_sales_kit/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/training.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/update.png b/all_in_one_sales_kit/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/update.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/user.png b/all_in_one_sales_kit/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/user.png differ diff --git a/all_in_one_sales_kit/static/description/assets/icons/wrench.png b/all_in_one_sales_kit/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/icons/wrench.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/Cybrosys R.png b/all_in_one_sales_kit/static/description/assets/misc/Cybrosys R.png new file mode 100644 index 000000000..da4058087 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/Cybrosys R.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/categories.png b/all_in_one_sales_kit/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/categories.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/check-box.png b/all_in_one_sales_kit/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/check-box.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/compass.png b/all_in_one_sales_kit/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/compass.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/corporate.png b/all_in_one_sales_kit/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/corporate.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/customer-support.png b/all_in_one_sales_kit/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/customer-support.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/cybrosys-logo.png b/all_in_one_sales_kit/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/cybrosys-logo.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/email.svg b/all_in_one_sales_kit/static/description/assets/misc/email.svg new file mode 100644 index 000000000..15291cdc3 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/email.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/features.png b/all_in_one_sales_kit/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/features.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/logo.png b/all_in_one_sales_kit/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/logo.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/phone.svg b/all_in_one_sales_kit/static/description/assets/misc/phone.svg new file mode 100644 index 000000000..b7bd7f251 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/phone.svg @@ -0,0 +1,3 @@ + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/pictures.png b/all_in_one_sales_kit/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/pictures.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/pie-chart.png b/all_in_one_sales_kit/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/pie-chart.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/right-arrow.png b/all_in_one_sales_kit/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/right-arrow.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/star (1) 2.svg b/all_in_one_sales_kit/static/description/assets/misc/star (1) 2.svg new file mode 100644 index 000000000..5ae9f507a --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/star (1) 2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/star.png b/all_in_one_sales_kit/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/star.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/support (1) 1.svg b/all_in_one_sales_kit/static/description/assets/misc/support (1) 1.svg new file mode 100644 index 000000000..7d37a8f30 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/support (1) 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/support-email.svg b/all_in_one_sales_kit/static/description/assets/misc/support-email.svg new file mode 100644 index 000000000..eb70370d6 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/support-email.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/support.png b/all_in_one_sales_kit/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/support.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/tick-mark.svg b/all_in_one_sales_kit/static/description/assets/misc/tick-mark.svg new file mode 100644 index 000000000..2dbb40187 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/tick-mark.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/whatsapp 1.svg b/all_in_one_sales_kit/static/description/assets/misc/whatsapp 1.svg new file mode 100644 index 000000000..0bfaf8fc6 --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/whatsapp 1.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/misc/whatsapp.png b/all_in_one_sales_kit/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/misc/whatsapp.png differ diff --git a/all_in_one_sales_kit/static/description/assets/misc/whatsapp.svg b/all_in_one_sales_kit/static/description/assets/misc/whatsapp.svg new file mode 100644 index 000000000..b618aea1d --- /dev/null +++ b/all_in_one_sales_kit/static/description/assets/misc/whatsapp.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/all_in_one_sales_kit/static/description/assets/modules/1.png b/all_in_one_sales_kit/static/description/assets/modules/1.png new file mode 100644 index 000000000..612be4b77 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/1.png differ diff --git a/all_in_one_sales_kit/static/description/assets/modules/2.png b/all_in_one_sales_kit/static/description/assets/modules/2.png new file mode 100644 index 000000000..eba886e6a Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/2.png differ diff --git a/all_in_one_sales_kit/static/description/assets/modules/3.png b/all_in_one_sales_kit/static/description/assets/modules/3.png new file mode 100644 index 000000000..69d47fc7d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/3.png differ diff --git a/all_in_one_sales_kit/static/description/assets/modules/4.png b/all_in_one_sales_kit/static/description/assets/modules/4.png new file mode 100644 index 000000000..d56d032b2 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/4.png differ diff --git a/all_in_one_sales_kit/static/description/assets/modules/5.png b/all_in_one_sales_kit/static/description/assets/modules/5.png new file mode 100644 index 000000000..bb820c6cb Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/5.png differ diff --git a/all_in_one_sales_kit/static/description/assets/modules/6.png b/all_in_one_sales_kit/static/description/assets/modules/6.png new file mode 100644 index 000000000..3977383be Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/modules/6.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/1.png b/all_in_one_sales_kit/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..1ca712100 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/1.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/10.png b/all_in_one_sales_kit/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..14ca98d6a Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/10.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/11.png b/all_in_one_sales_kit/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..c90a39c36 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/11.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/12.png b/all_in_one_sales_kit/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..8f94922f7 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/12.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/13.png b/all_in_one_sales_kit/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..ab5e8dbb6 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/13.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/14.png b/all_in_one_sales_kit/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..d61797abf Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/14.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/15.png b/all_in_one_sales_kit/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..6d66347c3 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/15.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/16.png b/all_in_one_sales_kit/static/description/assets/screenshots/16.png new file mode 100644 index 000000000..0bf0d57d6 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/16.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/17.png b/all_in_one_sales_kit/static/description/assets/screenshots/17.png new file mode 100644 index 000000000..19cbbc23d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/17.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/18.png b/all_in_one_sales_kit/static/description/assets/screenshots/18.png new file mode 100644 index 000000000..18ad4a5a6 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/18.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/19.png b/all_in_one_sales_kit/static/description/assets/screenshots/19.png new file mode 100644 index 000000000..db38582e9 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/19.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/2.png b/all_in_one_sales_kit/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..2b4281203 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/2.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/20.png b/all_in_one_sales_kit/static/description/assets/screenshots/20.png new file mode 100644 index 000000000..a21d677c5 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/20.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/21.png b/all_in_one_sales_kit/static/description/assets/screenshots/21.png new file mode 100644 index 000000000..2b18be864 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/21.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/22.png b/all_in_one_sales_kit/static/description/assets/screenshots/22.png new file mode 100644 index 000000000..c7cffe1fe Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/22.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/23.png b/all_in_one_sales_kit/static/description/assets/screenshots/23.png new file mode 100644 index 000000000..4d982c352 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/23.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/24.png b/all_in_one_sales_kit/static/description/assets/screenshots/24.png new file mode 100644 index 000000000..c1c6e9164 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/24.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/25.png b/all_in_one_sales_kit/static/description/assets/screenshots/25.png new file mode 100644 index 000000000..aadb349a5 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/25.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/26.png b/all_in_one_sales_kit/static/description/assets/screenshots/26.png new file mode 100644 index 000000000..46eaf4579 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/26.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/27.png b/all_in_one_sales_kit/static/description/assets/screenshots/27.png new file mode 100644 index 000000000..4e5fdfafc Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/27.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/28.png b/all_in_one_sales_kit/static/description/assets/screenshots/28.png new file mode 100644 index 000000000..591b7bf53 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/28.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/29.png b/all_in_one_sales_kit/static/description/assets/screenshots/29.png new file mode 100644 index 000000000..095aee722 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/29.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/3.png b/all_in_one_sales_kit/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..2c1817471 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/3.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/30.png b/all_in_one_sales_kit/static/description/assets/screenshots/30.png new file mode 100644 index 000000000..3fccffc64 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/30.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/31.png b/all_in_one_sales_kit/static/description/assets/screenshots/31.png new file mode 100644 index 000000000..be0b637ef Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/31.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/32.png b/all_in_one_sales_kit/static/description/assets/screenshots/32.png new file mode 100644 index 000000000..0478a4bba Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/32.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/33.png b/all_in_one_sales_kit/static/description/assets/screenshots/33.png new file mode 100644 index 000000000..a638f7793 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/33.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/34.png b/all_in_one_sales_kit/static/description/assets/screenshots/34.png new file mode 100644 index 000000000..8c4d2a8e3 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/34.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/35.png b/all_in_one_sales_kit/static/description/assets/screenshots/35.png new file mode 100644 index 000000000..55a8c89c5 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/35.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/36.png b/all_in_one_sales_kit/static/description/assets/screenshots/36.png new file mode 100644 index 000000000..b6469b73d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/36.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/37.png b/all_in_one_sales_kit/static/description/assets/screenshots/37.png new file mode 100644 index 000000000..f6678049c Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/37.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/38.png b/all_in_one_sales_kit/static/description/assets/screenshots/38.png new file mode 100644 index 000000000..1e9027be4 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/38.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/39.png b/all_in_one_sales_kit/static/description/assets/screenshots/39.png new file mode 100644 index 000000000..0a6724b3f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/39.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/4.png b/all_in_one_sales_kit/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..879d51d99 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/4.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/40.png b/all_in_one_sales_kit/static/description/assets/screenshots/40.png new file mode 100644 index 000000000..8e21df7cd Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/40.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/41.png b/all_in_one_sales_kit/static/description/assets/screenshots/41.png new file mode 100644 index 000000000..ae72a8083 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/41.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/42.png b/all_in_one_sales_kit/static/description/assets/screenshots/42.png new file mode 100644 index 000000000..5bd3e96e8 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/42.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/43.png b/all_in_one_sales_kit/static/description/assets/screenshots/43.png new file mode 100644 index 000000000..da13a3ca1 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/43.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/44.png b/all_in_one_sales_kit/static/description/assets/screenshots/44.png new file mode 100644 index 000000000..c2a78a839 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/44.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/45.png b/all_in_one_sales_kit/static/description/assets/screenshots/45.png new file mode 100644 index 000000000..4ba60d624 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/45.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/46.png b/all_in_one_sales_kit/static/description/assets/screenshots/46.png new file mode 100644 index 000000000..9a9a3d30e Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/46.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/47.png b/all_in_one_sales_kit/static/description/assets/screenshots/47.png new file mode 100644 index 000000000..626cbcf0c Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/47.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/48.png b/all_in_one_sales_kit/static/description/assets/screenshots/48.png new file mode 100644 index 000000000..63094fea9 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/48.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/49.png b/all_in_one_sales_kit/static/description/assets/screenshots/49.png new file mode 100644 index 000000000..39e35e4e8 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/49.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/5.png b/all_in_one_sales_kit/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..3949995bd Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/5.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/50.png b/all_in_one_sales_kit/static/description/assets/screenshots/50.png new file mode 100644 index 000000000..8d5789b78 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/50.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/51.png b/all_in_one_sales_kit/static/description/assets/screenshots/51.png new file mode 100644 index 000000000..fa9286cec Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/51.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/52.png b/all_in_one_sales_kit/static/description/assets/screenshots/52.png new file mode 100644 index 000000000..f9ecd705f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/52.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/53.png b/all_in_one_sales_kit/static/description/assets/screenshots/53.png new file mode 100644 index 000000000..3ee481367 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/53.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/54.png b/all_in_one_sales_kit/static/description/assets/screenshots/54.png new file mode 100644 index 000000000..ff85f3c64 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/54.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/55.png b/all_in_one_sales_kit/static/description/assets/screenshots/55.png new file mode 100644 index 000000000..5e395992e Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/55.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/56.png b/all_in_one_sales_kit/static/description/assets/screenshots/56.png new file mode 100644 index 000000000..44e40ee9b Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/56.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/57.png b/all_in_one_sales_kit/static/description/assets/screenshots/57.png new file mode 100644 index 000000000..925595d6c Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/57.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/58.png b/all_in_one_sales_kit/static/description/assets/screenshots/58.png new file mode 100644 index 000000000..7aeb9f129 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/58.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/59.png b/all_in_one_sales_kit/static/description/assets/screenshots/59.png new file mode 100644 index 000000000..09c63519d Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/59.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/6.png b/all_in_one_sales_kit/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..72334f9eb Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/6.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/7.png b/all_in_one_sales_kit/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..7abe7d574 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/7.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/8.png b/all_in_one_sales_kit/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..3e890eceb Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/8.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/9.png b/all_in_one_sales_kit/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..27dabf618 Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/9.png differ diff --git a/all_in_one_sales_kit/static/description/assets/screenshots/hero.png b/all_in_one_sales_kit/static/description/assets/screenshots/hero.png new file mode 100644 index 000000000..805e5d25f Binary files /dev/null and b/all_in_one_sales_kit/static/description/assets/screenshots/hero.png differ diff --git a/all_in_one_sales_kit/static/description/banner.jpg b/all_in_one_sales_kit/static/description/banner.jpg new file mode 100644 index 000000000..0ab872946 Binary files /dev/null and b/all_in_one_sales_kit/static/description/banner.jpg differ diff --git a/all_in_one_sales_kit/static/description/icon.png b/all_in_one_sales_kit/static/description/icon.png new file mode 100644 index 000000000..b22ce85a8 Binary files /dev/null and b/all_in_one_sales_kit/static/description/icon.png differ diff --git a/all_in_one_sales_kit/static/description/index.html b/all_in_one_sales_kit/static/description/index.html new file mode 100644 index 000000000..e40792c7f --- /dev/null +++ b/all_in_one_sales_kit/static/description/index.html @@ -0,0 +1,1342 @@ + + + + + + + Odoo App 3 Index + + + + + + + + +
+
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+
+
+
+
+

All In One Sales Kit

+

+ This Module Combines A Variety Of Sales Features. +

+
+ +
+
+
+
+
+

+ Key Highlights +

+
+
+
+
+
+ +
+
+

+ Sale Order Line Images

+
+
+
+
+
+
+ +
+
+

+ Advanced Sale Reports

+
+
+
+
+
+
+ +
+
+

+ Sales Invoice Analysis Report

+
+
+
+
+
+
+ +
+
+

+ Product Pack

+
+
+
+
+
+
+ +
+
+

+ Salesperson Signature

+
+
+
+
+
+
+ +
+
+

+ Previous Sale Product Rate

+
+
+
+
+
+
+ +
+
+

+ Create Various Sale Order Versions

+
+
+
+
+
+
+ +
+
+

+ Multiple warehouses in sale order lines

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

+ Sales Dashboard

+

+ To filter the tile values, choose a start + date and an end date. +

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

+ Sale Order Line Product Images

+

+ Go to settings -> Enable Show product image + in report under product catalog to print + image in the send report. We can add product + image in order line in sale order. We can + view these images on the quotation send to + customer also. +

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

+ Sales Reports

+

+ Different sale analysis reports are + available. +

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

+ Product Pack

+

+ Go to products -> Product Pack.We can create + new product packages here. While creating + one 'is a pack will be enabled' also a new + page to add products under this pack is + available.From there itself pack price and + pack quantity will be known.While creating a + new quotation we can add the product pack + using the button 'Add product pack'. +

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

+ Salesperson's signature approval

+

+ Go to settings -> Enable Sale Document Approve to make salesperson's signature to confirm sale order.Only after adding sales person's signature confirm button will become visible. +

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

+ Previous sales rate and customer product report

+

+ Two new buttons are available to know previous sales rate of a product and customer product report for a product selected from the order line. +

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

+ Sale order versions

+

+ Different sale orders versions can be created using the 'create version' button,and they can be viewed from the smart tab 'version'.If one of the versions are confirmed the remaining will automatically move to cancelled state. +

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

+ Create custom fields

+

+ Go to users -> Enable Create custom fields in sale,Go to sale->configuration -> create fields a wizard to daa features of new field is shown fill the requirements and apply 'create' to create custom fields in sale order form view. +

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

+ Separate sequence for quotation and multiple warehouses

+

+ Quotation sequence is generated while creating a new quotation.Also in the order lines different warehouses can be chosen for different products and delivery orders for multiple warehouses are created separately. +

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

+ Sale order and quotation line views

+

+ Orders -> Quotation line views , this will show all the quotation lines with product image.Orders -> Order line views , this will show all the order lines with product image. + +

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

+ Sale order discount approval

+

+ Go to users -> Select Sale order discount approval as Discount approval manager,Go under Discount control enable discount control , set discount limit in percentage.While confirming a sale order if the discount is greater than allowed discount limit then the order moves to waiting for approval state and the discount approval manager can decide whether to choose or cancel the order. +

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

+ Out of stock product restriction,Automate orders,Add multiple products to quotation

+

+ Go to settings -> Choose the grounds on which the stock restriction should be implemented.While confirming the sale order if the restriction enabled prevails then error message is shown.By selecting Add to catalog , we are directed to the product view and using add quotation button the products are added to the current sale order lines. +

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

+ Sales pivot view in customer,Sale order archive,Barcode scanning for sales

+

+ Orders -> Customer -> sales smart tab we can get sales analysis of that customer in pivot view also.Select a sale order -> Action -> archive by using this we can archive a sale order and using unarchive option under it, we can unarchive the archived sale orders.In the filters option we can filter archived sale orders.Products -> General information we can enter the product's barcode,and we just need to scan the barcode of the product to enter the product to sale order line. +

+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+ +
+ + +
+
+
    +
  • + Community & Enterprise Support +
  • +
  • + Allows you see the product images in sale order line and also allow us to print the product image in the send report +
  • +
  • + Supports barcode scanning in sales +
  • +
  • + Helps you to print reports various reports +
  • +
  • + Provides opportunity to sell products in pack +
  • +
  • + It allows the salesperson's to add his signature and also available the option for making the validate option in sales visible/invisible based on the salesperson's signature. +
  • +
  • + It is intended to display the prior sales and purchases of a specific product for a specific partner +
  • +
  • + It is intended to display the prior sales of a specific product for a specific partner +
  • +
  • + A sale order can have various versions that can be seen through the smart tabs inside the sale order +
  • +
  • + A sale order custom field option helps in creating custom fields without coding +
  • +
  • + In this each order line of a sale order will have a history button that, when clicked, will open a wizard displaying the sale history of that product for the individual customer selected +
  • +
  • + A separate sequence is added for quotation and sale order +
  • +
  • + A field warehouses is added in sale order lines and when confirming the order if multiple warehouses are selected then separate delivery orders are created +
  • +
  • + Under order two new menu items are added Sale order lines, Quotation lines +
  • +
  • + Adds a field for the user to set the manager for discount approval +
  • +
  • + Based on the quantity on hand and forecasted, this module controls out-of-stock products +
  • +
  • + Options to create and validate invoices automatically +
  • +
  • + It shows the sales analysis of a company in many aspects such as by order,order details,salesman and so on +
  • +
  • + Add multiple products to quotation +
  • +
  • + Display client sales analysis in their view as pivot +
  • +
  • + Option to archive and unarchive sale order +
  • +
  • + Sales Dashboard +
  • + +
+
+
+
+
+
+
Version + 17.0.1.0.0|Released on: 11th March 2024 +
+

+ Initial Commit for All In One Sales Kit.

+
+
+
+
+
+
+
+

+ 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/all_in_one_sales_kit/static/src/css/sale_report.css b/all_in_one_sales_kit/static/src/css/sale_report.css new file mode 100644 index 000000000..f5f9bdf82 --- /dev/null +++ b/all_in_one_sales_kit/static/src/css/sale_report.css @@ -0,0 +1,147 @@ +.time_range_pr { + width: 125px; + border: 2px solid #ccc; + border-radius: 5px; + padding: 10px; + +} +.apply_sale { + margin-right: 5px; + padding: 4px; +} +#apply_filter { + margin-right: 25px; +} +#pdf, #xlsx, #apply_filter { + color: white; + background-color: #7c7bad; + border-color: #7c7bad; +} +a.dropdown-toggle.report-type { + margin: 10px; +} +.search-Result-Selection { + border: 2px solid #ccc; + max-width: 360px; + min-width: 250px; + margin-right: 10px; + margin-left: 10px; + border-radius: 5px; + min-height: 40px; +} +.search-Result-Selection:hover{ + border:2px solid #eaeaea; +} +.dropdown-togglereport-type{ + min-height: 40px; + padding-top: 10px; +} +.low_case, #report_res { + text-transform: capitalize; + text-align: center; +} +#report_res{ + padding-right: 3px; +} +.print-btns { + margin-bottom: 30px; +} +.table_pr_head { + background-color: #714b67; + color: #fff; + padding: 20px; + margin: 20px; + height: 57px; + width: 100%; + border: 1px solid #000; +} +tr.pr-line { + height: 48px; +} +tr.table_pr_head th { + font-size: 18px; + text-align: center; +} +.table_view_sr { + overflow-y: scroll; + position: relative; + height: 500px; + max-width: 100%; +} +.my_custom_dropdown{ + min-width:200px; + height:150px; + padding: 20px; +} +.my_custom_dropdown input{ + height:25px; +} +.report_type{ + min-width: 150px; + padding: 10px 10px 10px; +} +.sub_container_right, .print-btns { + display: flex; + justify-content: space-around; + align-items: center; +} +.form-inline { + display: flex; + flex-direction: row; + align-items: center; +} +input[type="date"]{ + font-family: "Roboto Mono",monospace; + font-size: 14px; + border: none; + outline: none; + border-radius: 5px; +} +@media screen and (max-width: 768px) { + .sub_container_right, .print-btns { + flex-flow: column; + align-items: center; + } + .sub_container_left { + order: 2; + margin: 0; + } + .sub_container_right { + flex-flow: row; + margin-bottom: 10px; + flex-wrap: wrap !important; + width: 100%; + } +} +@media screen and (max-width: 576px) { + .apply_filter { + width: 100%; + position: relative; + } + #apply_filter { + position: absolute; + margin: 30px 0 0; + top: 50%; + left: 50%; + -ms-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); + } + #date_chose { + margin-bottom: 15px; + } + .search-Result-Selection { + text-align: center; + } + .sub_container_left { + order: 2; + display: flex; + margin-top: 50px; + width: 100%; + justify-content: center; + align-items: center; + flex-wrap: wrap !important; + } + .sub_container_right { + flex-direction: column; + } +} diff --git a/all_in_one_sales_kit/static/src/img/Products.png b/all_in_one_sales_kit/static/src/img/Products.png new file mode 100644 index 000000000..5f71b84b6 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/Products.png differ diff --git a/all_in_one_sales_kit/static/src/img/Quotations-cancel.png b/all_in_one_sales_kit/static/src/img/Quotations-cancel.png new file mode 100644 index 000000000..d695d3267 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/Quotations-cancel.png differ diff --git a/all_in_one_sales_kit/static/src/img/Quotations-send.png b/all_in_one_sales_kit/static/src/img/Quotations-send.png new file mode 100644 index 000000000..45ed3b9e1 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/Quotations-send.png differ diff --git a/all_in_one_sales_kit/static/src/img/Quotations.png b/all_in_one_sales_kit/static/src/img/Quotations.png new file mode 100644 index 000000000..8a7c5ec47 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/Quotations.png differ diff --git a/all_in_one_sales_kit/static/src/img/custmers.png b/all_in_one_sales_kit/static/src/img/custmers.png new file mode 100644 index 000000000..81d8d5b64 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/custmers.png differ diff --git a/all_in_one_sales_kit/static/src/img/sales-orders.png b/all_in_one_sales_kit/static/src/img/sales-orders.png new file mode 100644 index 000000000..50a4e1844 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/sales-orders.png differ diff --git a/all_in_one_sales_kit/static/src/img/to-invoice.png b/all_in_one_sales_kit/static/src/img/to-invoice.png new file mode 100644 index 000000000..98ca37d29 Binary files /dev/null and b/all_in_one_sales_kit/static/src/img/to-invoice.png differ diff --git a/all_in_one_sales_kit/static/src/js/action_manager.js b/all_in_one_sales_kit/static/src/js/action_manager.js new file mode 100644 index 000000000..fe4018d4e --- /dev/null +++ b/all_in_one_sales_kit/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"; + +/** +This handler is responsible for generating XLSX reports. +*/ +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), + }); + } +}); \ No newline at end of file diff --git a/all_in_one_sales_kit/static/src/js/dashboard.js b/all_in_one_sales_kit/static/src/js/dashboard.js new file mode 100644 index 000000000..2a3e885e7 --- /dev/null +++ b/all_in_one_sales_kit/static/src/js/dashboard.js @@ -0,0 +1,231 @@ +/** @odoo-module */ +import { registry } from "@web/core/registry" +import { useService } from "@web/core/utils/hooks" +import { session } from '@web/session'; +const { Component, useRef, onMounted ,useState } = owl + +export class SalesDashboard extends Component { +// Extending Component + setup(){ + this.state = useState({ + data:{}, + chart:[], + lead_customer_data: {}, + date:{}, + }) + this.action = useService("action"); + this.LeadCustomer = useRef("LeadCustomer"); + this.LeadProducts = useRef("LeadProducts"); + this.LeadSaleOrders = useRef("LeadSaleOrders"); + this.SalesTeamRevenue = useRef("SalesTeamRevenue"); + this.LeastSoldProduct = useRef("LeastSoldProduct"); + this.MonthlyQuotation = useRef("MonthlyQuotation"); + this.orm = useService("orm"); + onMounted(async()=> { + await this.FetchData(); + }) + } + async FetchData(){ + /* Function for fetching data */ + this.state.data = await this.orm.call('sale.order', 'get_data', []) + this.state.lead_customer_data = await this.orm.call('sale.order','get_lead_customer', []) + this.state.lead_product_data = await this.orm.call( 'sale.order', 'get_lead_product',[]) + this.state.lead_sale_data = await this.orm.call('sale.order','get_lead_order', []) + this.state.lead_sale_team = await this.orm.call('sale.order','get_sales_team', []) + this.state.least_sold = await this.orm.call('sale.order','get_least_sold', []) + this.state.monthly_comparison = await this.orm.call('sale.order','get_my_monthly_comparison', []) + this.chart(this.LeadCustomer.el,'doughnut',Object.keys(this.state.lead_customer_data.lead_templates),Object.values(this.state.lead_customer_data.lead_templates),) + this.chart(this.LeadProducts.el,'doughnut',Object.keys(this.state.lead_product_data.lead_templates),Object.values(this.state.lead_product_data.lead_templates),) + this.labeled_chart(this.LeadSaleOrders.el,'bar',Object.keys(this.state.lead_sale_data.lead_templates),'Sale Amount',Object.values(this.state.lead_sale_data.lead_templates),) + this.chart(this.SalesTeamRevenue.el,'pie',Object.keys(this.state.lead_sale_team.lead_templates),Object.values(this.state.lead_sale_team.lead_templates),) + this.labeled_chart(this.LeastSoldProduct.el,'bar',Object.keys(this.state.least_sold.lead_templates),'Product Count',Object.values(this.state.least_sold.lead_templates),) + this.labeled_chart(this.MonthlyQuotation.el,'line',Object.keys(this.state.monthly_comparison.lead_templates),'Quotation Count',Object.values(this.state.monthly_comparison.lead_templates),) + } + async DateChanged(){ + this.state.data = await this.orm.call('sale.order', 'get_value', [this.state.date.start_date, this.state.date.end_date]) + } + + chart(canvas,type,labels,data){ + this.state.chart.push(new Chart( + canvas, + { + type:type, + data: { + labels: labels, + datasets: [ + { + data: data, + backgroundColor: [ + 'rgb(255,20,147)', + 'rgb(186,85,211)', + 'rgb(0,0,255)', + 'rgb(0,191,255)', + 'rgb(0,206,209)', + 'rgb(32,178,170)', + 'rgb(173,255,47)', + 'rgb(205,92,92)', + 'rgb(178,34,34)', + 'rgb(0,128,128)', + ], + } + ] + }, + } + )) + } + labeled_chart(canvas,type,labels,label,data){ + this.state.chart.push(new Chart( + canvas, + { + type:type, + data: { + labels: labels, + datasets: [ + { + label: label, + data: data, + backgroundColor: [ + 'rgb(255,20,147)', + 'rgb(186,85,211)', + 'rgb(0,0,255)', + 'rgb(0,191,255)', + 'rgb(0,206,209)', + 'rgb(32,178,170)', + 'rgb(173,255,47)', + 'rgb(205,92,92)', + 'rgb(178,34,34)', + 'rgb(0,128,128)', + ], + } + ] + }, + } + )) + } + on_dashboard_quotation_action(){ + /* Function for quotation dashboard */ + if (this.state.date.start_date && this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'draft'], ['date_order', '>=', this.state.date.start_date], ['date_order', '<=', this.state.date.end_date]] + } + else if (this.state.date.start_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'draft'], ['date_order', '>=', this.state.date.start_date]] + } + else if (this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'draft'], ['date_order', '<=', this.state.date.end_date]] + } + else { + var domain = [['user_id', '=', session.uid], ['state', '=', 'draft']] + } + return this.action.doAction({ + type: "ir.actions.act_window", + name: 'Quotations', + res_model: 'sale.order', + views: [[false, 'tree'], [false, 'form']], + target: "current", + domain: domain, + }); + } + on_dashboard_my_sale_order_action(){ + /* Function for Sale order dashboard */ + if (this.state.date.start_date && this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sale'], ['date_order', '>=', this.state.date.start_date], ['date_order', '<=', this.state.date.end_date]] + } + else if (this.state.date.start_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sale'], ['date_order', '>=', this.state.date.start_date]] + } + else if (this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sale'], ['date_order', '<=', this.state.date.end_date]] + } + else { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sale']] + } + return this.action.doAction({ + type: "ir.actions.act_window", + name: 'Sale Orders', + res_model: 'sale.order', + views: [[false, 'tree'], [false, 'form']], + target: "current", + domain: domain, + }); + } + on_dashboard_quotation_sent_action(){ + /* Function for Quotation sent dashboard */ + if (this.state.date.start_date && this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sent'], ['date_order', '>=', this.state.date.start_date], ['date_order', '<=', this.state.date.end_date]] + } + else if (this.state.date.start_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sent'], ['date_order', '>=', this.state.date.start_date]] + } + else if (this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sent'], ['date_order', '<=', this.state.date.end_date]] + } + else { + var domain = [['user_id', '=', session.uid], ['state', '=', 'sent']] + } + return this.action.doAction({ + type: "ir.actions.act_window", + name: 'Quotations Sent', + res_model: 'sale.order', + views: [[false, 'tree'], [false, 'form']], + target: "current", + domain: domain, + }); + } + on_dashboard_quotation_cancel_action(){ + /* Function for Quotation Cancel dashboard */ + var domain = [['user_id', '=', session.uid], ['state', '=', 'cancel']] + return this.action.doAction({ + type: "ir.actions.act_window", + name: 'Quotations Cancel', + res_model: 'sale.order', + views: [[false, 'tree'], [false, 'form']], + target: "current", + domain: domain, + }); + } + on_dashboard_customers_action(){ + /* Function for Customers dashboard */ + return this.action.doAction({ + type: 'ir.actions.act_window', + name: 'Customers', + res_model: 'res.partner', + views: [[false, 'kanban'], [false, 'tree'], [false, 'form']], + target: 'current', + }); + } + on_dashboard_products_action(){ + /* Function for Products dashboard */ + return this.action.doAction({ + type: 'ir.actions.act_window', + name: 'Products', + res_model: 'product.template', + views: [[false, 'kanban'], [false, 'tree'], [false, 'form']], + target: 'current', + }); + } + on_dashboard_to_invoice_action(){ + /* Function for To invoice dashboard */ + if (this.state.date.start_date && this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['invoice_status', '=', 'to invoice'], ['date_order', '>=', this.state.date.start_date], ['date_order', '<=', this.state.date.end_date]] + } + else if (this.state.date.start_date) { + var domain = [['user_id', '=', session.uid], ['invoice_status', '=', 'to invoice'], ['date_order', '>=', this.state.date.start_date]] + } + else if (this.state.date.end_date) { + var domain = [['user_id', '=', session.uid], ['invoice_status', '=', 'to invoice'], ['date_order', '<=', this.state.date.end_date]] + } + else { + var domain = [['user_id', '=', session.uid], ['invoice_status', '=', 'to invoice']] + } + return this.action.doAction({ + type: 'ir.actions.act_window', + name: 'To Invoice', + res_model: 'sale.order', + views: [[false, 'kanban'], [false, 'tree'], [false, 'form']], + target: 'current', + domain: domain, + }); + } +} +SalesDashboard.template = "DashboardDashboard" +registry.category("actions").add('sale_dashboard', SalesDashboard) diff --git a/all_in_one_sales_kit/static/src/js/sale_report.js b/all_in_one_sales_kit/static/src/js/sale_report.js new file mode 100644 index 000000000..baba38bb3 --- /dev/null +++ b/all_in_one_sales_kit/static/src/js/sale_report.js @@ -0,0 +1,91 @@ +/** @odoo-module */ +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { BlockUI } from "@web/core/ui/block_ui"; +import { download } from "@web/core/network/download"; +const { Component, useRef, onMounted ,useState } = owl + +export class SaleReport extends Component { + setup(){ + this.state = useState({ + rpt_type: 'report_by_order', + data:{}, + }) + this.action = useService("action"); + this.ReportRes = useRef('ReportRes'); + this.ReportType = useRef('ReportType'); + this.DateFrom = useRef('DateFrom'); + this.DateTo = useRef('DateTo'); + this.orm = useService("orm"); + onMounted(async()=> { + await this.FetchData(); + }) + } + async FetchData(){ + /* Function for fetching datas */ + const id = await this.orm.call("sales.report", "create", [{}]); + this.state.id = id + this.state.data = await this.orm.call('sales.report','sale_report',[id]) + } + async ChangeReportType(){ + /* Function for changing report type */ + this.ReportRes.el.innerHTML= this.state.rpt_type + } + async apply_filter(){ + /* Function for applying various filters */ + var filter_data_selected = {}; + if (this.DateFrom.el.value){ + filter_data_selected.date_from = this.DateFrom.el.value + ' 00:00:00' + } + if (this.DateTo.el.value){ + filter_data_selected.date_to = this.DateTo.el.value + ' 00:00:00' + } + filter_data_selected.report_type = this.state.rpt_type + await this.orm.call('sales.report', 'write', [this.state.id, filter_data_selected]) + this.state.data = await this.orm.call('sales.report','sale_report',[this.state.id]) + } + print_pdf(){ + /* Function for Printing pdf */ + this.action.doAction({ + 'type': 'ir.actions.report', + 'report_type': 'qweb-pdf', + 'report_name': 'all_in_one_sales_kit.sale_order_report', + 'report_file': 'all_in_one_sales_kit.sale_order_report', + 'data': { + 'report_data': this.state.data + }, + 'context': { + 'active_model': 'sales.report', + 'landscape': 1, + 'sale_order_report': true + }, + 'display_name': 'Sale Order', + }); + } + print_xlsx() { + /* Function for Printing xlsx */ + var self = this; + var action = { + 'data': { + 'model': 'sales.report', + 'options': JSON.stringify(this.state.data), + 'output_format': 'xlsx', + 'report_name': 'Sale Report', + }, + }; + self.downloadXlsx(action); + } + async downloadXlsx(action){ + /* It is to pass data needed to print xlsx report */ + BlockUI; + await download({ + url: '/xlsx_reports', + data: action.data, + complete: () => unblockUI, + error: (error) => self.call('crash_manager', 'rpc_error', error), + }); + } + +} +SaleReport.template = "SaleReport" +registry.category("actions").add('s_r', SaleReport) diff --git a/all_in_one_sales_kit/static/src/scss/dashboard.scss b/all_in_one_sales_kit/static/src/scss/dashboard.scss new file mode 100644 index 000000000..0d7ec4486 --- /dev/null +++ b/all_in_one_sales_kit/static/src/scss/dashboard.scss @@ -0,0 +1,123 @@ +p, span, a, ul, li, button { + font-size: inherit; + font-weight: inherit; + line-height: inherit; +} + +strong { + font-weight: 600; +} + +h1, h2, h3, h4, h5, h6 { + line-height: 1.5em; + font-weight: 500; +} + +strong { + font-weight: 400; +} + +div span { + font-weight: 400; +} + +:root { + /* Primary */ + --mauve: #7D7EAF; + + /* Light */ + --mauve-light: #e5e5ef; + --mauve-taupe: #B0FFDF; +} +.bg-mauve-light { + background-color: var(--mauve-taupe); +} +.text-mauve { + color: var(--mauve); +} +.dashboard-card { + border-radius: 0.3rem; + display: flex; + justify-content: left; + padding: 1.1rem 1.5rem 1.5rem 1.5rem; + margin: 1rem auto; + height: 90px; +} + +.dashboard-card__icon-container { + height: 50px; + width: 50px; + border-radius: 15px; +} + +.dashboard-card__icon-container i { + font-size: 20px; +} +.dashboard-card__details h3 { + font-weight: 700; + font-size: 1.2rem; + text-indent: 20px; +} + +.dashboard-card__details h4 { + font-weight: 700; + font-size: 1.0rem; + margin-top: -3px; + text-indent: 20px; +} +h2.section-header { + font-weight: 500; + font-size: 1.5rem; +} +.card-shadow { + box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); +} +.lead_customer{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:left; +} +.lead_product{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:right; +} +.lead_order{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:left; +} +.bar{ + margin-top:40px; +} +.graph{ + margin-top:40px; +} +.team{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:right; +} +.my_monthly_comparison{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:right; +} +.least_sold{ + background-color: #f2f2f2; + height:500px; + width:48%; + float:left; +} +input, button, select, optgroup, textarea { + margin: 0; + font-family: inherit; + color: grey; + font-size: inherit; + line-height: inherit; +} diff --git a/all_in_one_sales_kit/static/src/xml/dashboard_templates.xml b/all_in_one_sales_kit/static/src/xml/dashboard_templates.xml new file mode 100644 index 000000000..a9c57701a --- /dev/null +++ b/all_in_one_sales_kit/static/src/xml/dashboard_templates.xml @@ -0,0 +1,406 @@ + + + + +
+
+
+
+
+
+ + + + + + + + + +
Start Date
End Date
+
+
+
+
+
+
+ +
+
+

+ +

+

Quotations

+
+
+
+
+
+
+ +
+
+

+ +

+

Sale Orders

+
+
+
+
+
+
+ +
+
+

+ + + +

+

Quotation Sent

+
+
+
+
+
+
+ +
+
+

+ + + +

+

Quotation Cancel

+
+
+
+
+
+
+ +
+
+

+ + + +

+

Customers

+
+
+
+
+
+
+ +
+
+

+ + + +

+

Products

+
+
+
+
+
+
+ +
+
+

+ + + +

+

To Invoice

+
+
+
+
+
+ +
+

Top Customers

+
+ +
+
+
+
+ +
+

Top Products

+
+ +
+
+
+
+
+
+ +
+

Top Sale Orders

+
+ +
+
+
+
+ +
+

Sales Team Revenue

+
+ +
+
+
+
+
+
+ +
+

Least Sold Products

+
+ +
+
+
+
+ +
+

My Monthly Quotation Count

+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ Start Date: + + End Date: + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+

+ +
+ +

+

Quotations

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

Sale Orders

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

Quotation Sent

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

Quotation Cancel

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

Customers

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

Products

+
+
+
+
+
+
+ +
+
+

+ +
+ +

+

To Invoice

+
+
+
+
+
+
+

Top Customers

+
+ +
+
+
+
+
+

Top Products

+
+ +
+
+
+
+
+
+
+

Top Sale Orders

+
+ +
+
+
+
+
+

Sales Team Revenue

+
+ +
+
+
+
+
+
+
+

Least Sold Products

+
+ +
+
+
+
+
+

My Monthly Quotation Count

+
+ +
+
+
+
+
+
+
+
diff --git a/all_in_one_sales_kit/static/src/xml/sale_report_templates.xml b/all_in_one_sales_kit/static/src/xml/sale_report_templates.xml new file mode 100644 index 000000000..7d82e9644 --- /dev/null +++ b/all_in_one_sales_kit/static/src/xml/sale_report_templates.xml @@ -0,0 +1,442 @@ + + + + +
+
+
+

Sale Report

+
+
+
+ +
+
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
OrderDate OrderCustomerSales RepresentativeTotal QtyAmount TotalNote
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
OrderDate OrderCustomerSales RepresentativeProduct CodeProduct NamePrice unitQtyPrice Subtotal
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + +
CategoryProduct CodeProduct NameQtyAmount Total
+ + + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + +
CategoryQtyAmount Total
+ + + + + + + + + + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + +
Sales RepresentativeTotal OrderTotal QtyTotal Amount
+ + + + + + + + + + + + + + + +
+
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + +
StateTotal CountQuantityAmount
+ + + + + + + + + + + + + + + +
+
+
+ +
+
+
diff --git a/all_in_one_sales_kit/views/all_in_one_sales_kit_menus.xml b/all_in_one_sales_kit/views/all_in_one_sales_kit_menus.xml new file mode 100644 index 000000000..6d54cb5d4 --- /dev/null +++ b/all_in_one_sales_kit/views/all_in_one_sales_kit_menus.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + diff --git a/all_in_one_sales_kit/views/dashboard_menu.xml b/all_in_one_sales_kit/views/dashboard_menu.xml new file mode 100644 index 000000000..c3a31afea --- /dev/null +++ b/all_in_one_sales_kit/views/dashboard_menu.xml @@ -0,0 +1,11 @@ + + + + + Dashboard + sale_dashboard + + + diff --git a/all_in_one_sales_kit/views/ir_fields_search_views.xml b/all_in_one_sales_kit/views/ir_fields_search_views.xml new file mode 100644 index 000000000..61699f6e7 --- /dev/null +++ b/all_in_one_sales_kit/views/ir_fields_search_views.xml @@ -0,0 +1,16 @@ + + + + + ir.model.fields.view.form.inherit.all.in.one.sales.kit + ir.model.fields + + + + + + + + + diff --git a/all_in_one_sales_kit/views/product_product_views.xml b/all_in_one_sales_kit/views/product_product_views.xml new file mode 100644 index 000000000..60df81da8 --- /dev/null +++ b/all_in_one_sales_kit/views/product_product_views.xml @@ -0,0 +1,192 @@ + + + + + product.product.view.kanban + product.product + + + + + + + + + +
+
+ Product +
+
+ + + + + + + +
+ +
+
    +
  • + Price: + + +
  • +
+
+
+
+ + + + + + + + product.product.view.form.inherit.all.in.one.sales.kit + product.product + primary + + + +
+ Product Variant + false +
+ +
+ + + + + + + + + + + + + + + + +
+
+ + + Product Pack + ir.actions.act_window + product.template + kanban,form + [('is_pack','=',True)] + {'default_is_pack' : True} + +

+ There is no product pack to show...! +

+
+
+ + + product.template.view.search.inherit.all.in.one.sales.kit + + product.template + + + + + + + + +
diff --git a/all_in_one_sales_kit/views/res_config_settings_views.xml b/all_in_one_sales_kit/views/res_config_settings_views.xml new file mode 100644 index 000000000..b1bb0cf2b --- /dev/null +++ b/all_in_one_sales_kit/views/res_config_settings_views.xml @@ -0,0 +1,105 @@ + + + + + res.config.settings.view.form.inherit.all.in.one.sales.kit + res.config.settings + + + +
+
+ +
+
+
+
+
+ +

Sale Document Approval

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

Out Of Stock Product Restriction

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

Automate Orders

+
+
+
+ +
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
+
+
+
+
diff --git a/all_in_one_sales_kit/views/res_users_views.xml b/all_in_one_sales_kit/views/res_users_views.xml new file mode 100644 index 000000000..c444ea41b --- /dev/null +++ b/all_in_one_sales_kit/views/res_users_views.xml @@ -0,0 +1,23 @@ + + + + + res.users.view.form.inherit.all.in.one.sales.kit + res.users + + + + + + + + + + + + + + + diff --git a/all_in_one_sales_kit/views/sale_order_line_views.xml b/all_in_one_sales_kit/views/sale_order_line_views.xml new file mode 100644 index 000000000..3a41ade01 --- /dev/null +++ b/all_in_one_sales_kit/views/sale_order_line_views.xml @@ -0,0 +1,482 @@ + + + + + sale.order.line.view.form + sale.order.line + +
+ + + + +

+ +

+
+ + + + + + +
+ + + + + + + + + + + + +
+
+
+
+
+ + + sale.order.line.view.tree + sale.order.line + + + + + + + + + + + + + + + + + + + + + sale.order.line.view.kanban + sale.order.line + + + + + + + + + + + +
+
+ +
+
+
+
+ + + +
+
+
+ Product: + +
+ Quantity: + +
+ Price + +
+ Sub Total: + + +
+
+
+
+
+
+
+ + + sale.order.line.view.pivot + sale.order.line + + + + + + + + + + + + + sale.order.line.view.graph + sale.order.line + + + + + + + + + + sale.order.line.view.calender + sale.order.line + + + + + + + + + + + sale.order.line.view.search + sale.order.line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Sale Order Lines + ir.actions.act_window + sale.order.line + tree,kanban,form,pivot,graph,calendar + [('state', 'not in', ('draft', 'sent'))] + + + + + + sale.order.line.view.form + sale.order.line + +
+ + + + +

+ +

+
+ + + + + + +
+ + + + + + + + + + + +
+
+
+
+
+ + + sale.order.line.view.tree + sale.order.line + + + + + + + + + + + + + + + + + + + + + sale.order.line.view.kanban + sale.order.line + + + + + + + + + + + +
+
+ +
+
+
+
+ + + +
+
+
+ Product: + +
+ Quantity: + +
+ Price + +
+ Sub Total: + + +
+
+
+
+
+
+
+ + + sale.order.line.view.pivot + sale.order.line + + + + + + + + + + + + + sale.order.line.view.graph + sale.order.line + + + + + + + + + + sale.order.line.view.calender + sale.order.line + + + + + + + + + + + sale.order.line.view.search + sale.order.line + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Quotation Lines + ir.actions.act_window + sale.order.line + tree,kanban,form,pivot,graph,calendar + [ + ('state', 'in', ('draft', 'sent', 'cancel'))] + + + + + + sale.order.line.view.tree + sale.order.line + + + + + + + + + + + + + + + +
diff --git a/all_in_one_sales_kit/views/sale_order_views.xml b/all_in_one_sales_kit/views/sale_order_views.xml new file mode 100644 index 000000000..021b74624 --- /dev/null +++ b/all_in_one_sales_kit/views/sale_order_views.xml @@ -0,0 +1,184 @@ + + + + + sale.order.view.form.inherit.all.in.one.sales.kit + sale.order + + + + + + + + + + + state not in ['cancel'] or is_version == True + + + + + + + + + + + + +