diff --git a/import_dashboard/README.rst b/import_dashboard/README.rst new file mode 100644 index 000000000..3be6ea312 --- /dev/null +++ b/import_dashboard/README.rst @@ -0,0 +1,48 @@ +.. image:: https://img.shields.io/badge/licence-LGPL--3-green.svg + :target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +Import Dashboard +================ +Import Dashboard Module for Odoo 16 used for importing large amounts of different data into the system with lesser time. + +Configuration +============= +* No additional configuration needed. + +Company +------- +* `Cybrosys Techno Solutions `__ + +License +------- +General Public License, Version 3 (LGPL v3). +(http://www.gnu.org/licenses/lgpl-3.0-standalone.html) + +Credits +------- +* Developers: (V16) Vishnuraj P + (V15) Raneesha MK +* 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/import_dashboard/__init__.py b/import_dashboard/__init__.py new file mode 100644 index 000000000..d4b63604d --- /dev/null +++ b/import_dashboard/__init__.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import models +from . import wizard diff --git a/import_dashboard/__manifest__.py b/import_dashboard/__manifest__.py new file mode 100644 index 000000000..50acd0d84 --- /dev/null +++ b/import_dashboard/__manifest__.py @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +{ + 'name': "Import Dashboard", + 'version': '16.0.1.0.0', + 'category': 'Extra Tools', + 'summary': "Odoo supports various file formats, including CSV, Excel. " + "The data must be organized into separate sheets or tabs within" + " the same file for each type of data being imported", + 'description': """ The Import Dashboard feature in Odoo is a powerful tool + that can save users a significant amount of time when importing large + amounts of data into the system. It streamlines the import process and + reduces the likelihood of errors, making it a valuable feature for + businesses that rely on accurate and timely data.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['base'], + 'data': [ + 'security/ir.model.access.csv', + 'views/res_config_settings_views.xml', + 'views/menus.xml', + 'wizard/import_message_views.xml', + 'wizard/import_bill_of_material_views.xml', + 'wizard/import_task_views.xml', + 'wizard/import_vendor_pricelist_views.xml', + 'wizard/import_product_template_views.xml', + 'wizard/import_payment_views.xml', + 'wizard/import_attendance_views.xml', + 'wizard/import_product_pricelist_views.xml', + 'wizard/import_partner_views.xml', + 'wizard/import_pos_order_views.xml', + 'wizard/import_invoice_views.xml', + 'wizard/import_purchase_order_views.xml', + 'wizard/import_sale_order_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'import_dashboard/static/src/js/import_dashboard.js', + 'import_dashboard/static/src/css/style.scss', + 'import_dashboard/static/src/xml/dashboard_templates.xml', + ] + }, + 'images': [ + 'static/description/banner.jpg', + ], + 'license': 'LGPL-3', + 'installable': True, + 'auto_install': False, + 'application': True, +} diff --git a/import_dashboard/doc/RELEASE_NOTES.md b/import_dashboard/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..368468865 --- /dev/null +++ b/import_dashboard/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 06.05.2024 +#### Version 16.0.1.0.0 +#### ADD + +- Initial commit for Import Dashboard diff --git a/import_dashboard/models/__init__.py b/import_dashboard/models/__init__.py new file mode 100644 index 000000000..a810a92f1 --- /dev/null +++ b/import_dashboard/models/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import ir_config_parameter +from . import ir_module_module +from . import res_config_settings diff --git a/import_dashboard/models/ir_config_parameter.py b/import_dashboard/models/ir_config_parameter.py new file mode 100644 index 000000000..098cf9333 --- /dev/null +++ b/import_dashboard/models/ir_config_parameter.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, models, _ +from odoo.exceptions import ValidationError + + +class IrConfigParameter(models.Model): + """ Model for storing configuration parameters. """ + _inherit = 'ir.config_parameter' + + @api.model + def create(self, vals_list): + """For checking the necessary modules are installed""" + if vals_list.get('key') == 'import_bom': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'mrp')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_pos': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'point_of_sale')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_sale': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'sale_management')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_attendance': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'hr_attendance')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_purchase_order': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'purchase')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_vendor_pricelist': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'purchase')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_invoice': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'account')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_payment': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'account')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_task': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'project')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + if vals_list.get('key') == 'import_product_template ': + if vals_list['value']: + check = self.env["ir.module.module"].search( + [('name', '=', 'product')]) + if check.state == 'uninstalled': + vals_list['value'] = False + raise ValidationError(_('The module is not found. Please ' + 'make sure you have it installed')) + else: + pass + return super(IrConfigParameter, self).create(vals_list) + + @api.model + def check_user_group(self): + """For enabling the corresponding tiles in the dashboard """ + bill_of_material = self.env['ir.config_parameter'].sudo().get_param( + "import_bom") + pos = self.env['ir.config_parameter'].sudo().get_param("import_pos") + import_attendance = self.env['ir.config_parameter'].sudo().get_param( + "import_attendance") + import_payment = self.env['ir.config_parameter'].sudo().get_param( + "import_payment") + import_task = self.env['ir.config_parameter'].sudo().get_param( + "import_task") + import_sale = self.env['ir.config_parameter'].sudo().get_param( + "import_sale") + import_purchase = self.env['ir.config_parameter'].sudo().get_param( + "import_purchase_order") + import_product_template = self.env[ + 'ir.config_parameter'].sudo().get_param("import_product_template") + import_partner = self.env['ir.config_parameter'].sudo().get_param( + "import_partner") + import_invoice = self.env['ir.config_parameter'].sudo().get_param( + "import_invoice") + import_pricelist = self.env['ir.config_parameter'].sudo().get_param( + "import_pricelist") + import_vendor_pricelist = self.env[ + 'ir.config_parameter'].sudo().get_param("import_vendor_pricelist") + return { + 'bill_of_material': bill_of_material, + 'pos': pos, + 'import_attendance': import_attendance, + 'import_payment': import_payment, + 'import_task': import_task, + 'import_sale': import_sale, + 'import_purchase': import_purchase, + 'import_product_template': import_product_template, + 'import_partner': import_partner, + 'import_invoice': import_invoice, + 'import_pricelist': import_pricelist, + 'import_vendor_pricelist': import_vendor_pricelist, + } diff --git a/import_dashboard/models/ir_module_module.py b/import_dashboard/models/ir_module_module.py new file mode 100644 index 000000000..4dcc7027e --- /dev/null +++ b/import_dashboard/models/ir_module_module.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import models + + +class IrModuleModule(models.Model): + """ Model for inherit and add feature to method 'button_uninstall' """ + _inherit = 'ir.module.module' + + def button_uninstall(self): + """When a module is uninstalled set + the corresponding boolean field to False""" + if self.name == 'mrp': + self.env['ir.config_parameter'].sudo().set_param( + "import_bom", False) + if self.name == 'point_of_sale': + self.env['ir.config_parameter'].sudo().set_param( + "import_pos", False) + if self.name == 'hr_attendance': + self.env['ir.config_parameter'].sudo().set_param( + "import_attendance", False) + if self.name == 'sale_management': + self.env['ir.config_parameter'].sudo().set_param( + "import_sale", False) + if self.name == 'purchase': + self.env['ir.config_parameter'].sudo().set_param( + "import_purchase_order", False) + self.env['ir.config_parameter'].sudo().set_param( + "import_vendor_pricelist", False) + if self.name == 'account': + self.env['ir.config_parameter'].sudo().set_param( + "import_invoice", False) + self.env['ir.config_parameter'].sudo().set_param( + "import_payment", False) + if self.name == 'project': + self.env['ir.config_parameter'].sudo().set_param( + "import_task", False) + if self.name == 'product': + self.env['ir.config_parameter'].sudo().set_param( + "import_product_template", False) + return super().button_uninstall() diff --git a/import_dashboard/models/res_config_settings.py b/import_dashboard/models/res_config_settings.py new file mode 100644 index 000000000..dbebca9b0 --- /dev/null +++ b/import_dashboard/models/res_config_settings.py @@ -0,0 +1,125 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import api, fields, models + + +class ResConfigSettings(models.TransientModel): + """ Model for enable import options in settings. """ + _inherit = 'res.config.settings' + + import_bom = fields.Boolean( + config_parameter='import_dashboard.import_bom', default=False, + help='For importing bom files', string="Import BoM") + import_pos = fields.Boolean( + config_parameter='import_dashboard.import_pos', default=False, + help='For importing pos', string="Import POS") + import_attendance = fields.Boolean( + string="Import Attendance", help='For importing attendance', + config_parameter='import_dashboard.import_attendance', default=False) + import_payment = fields.Boolean( + string="Import Payment", help='For importing payments', + default=False, config_parameter='import_dashboard.import_payment') + import_task = fields.Boolean( + string="Import Task", default=False, help='For importing tasks', + config_parameter='import_dashboard.import_task', ) + import_sale = fields.Boolean( + string="Import Sale", help='For importing sales orders', + config_parameter='import_dashboard.import_sale', default=False) + import_purchase_order = fields.Boolean( + config_parameter='import_dashboard.import_purchase_order', default=False + , string="Import Purchase Order", help='For importing purchase orders') + import_product_template = fields.Boolean( + string="Import Product Template", help='For importing Products', + config_parameter='import_dashboard.import_product_template', + default=False) + import_partner = fields.Boolean( + string="Import Partner", help='For importing partners', default=False, + config_parameter='import_dashboard.import_partner') + import_invoice = fields.Boolean( + string="Import Invoices", help='For importing invoices', default=False, + config_parameter='import_dashboard.import_invoice') + import_pricelist = fields.Boolean( + string="Import Pricelist", help='For importing price lists', + config_parameter='import_dashboard.import_pricelist', default=False) + import_vendor_pricelist = fields.Boolean( + string="Import Vendor Pricelist", default=False, + config_parameter='import_dashboard.import_vendor_pricelist', + help='For importing vendor price lists') + + @api.model + def get_values(self): + """Getting the values of the corresponding importing items""" + res = super(ResConfigSettings, self).get_values() + res['import_bom'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_bom') + res['import_pos'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_pos') + res['import_attendance'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_attendance') + res['import_payment'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_payment') + res['import_task'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_task') + res['import_sale'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_sale') + res['import_purchase_order'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_purchase_order') + res['import_product_template'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_product_template') + res['import_partner'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_partner') + res['import_invoice'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_invoice') + res['import_pricelist'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_pricelist') + res['import_vendor_pricelist'] = self.env[ + 'ir.config_parameter'].sudo().get_param('import_vendor_pricelist') + return res + + @api.model + def set_values(self): + """Setting the values of the corresponding importing items""" + self.env['ir.config_parameter'].sudo().set_param( + 'import_bom', self.import_bom) + self.env['ir.config_parameter'].sudo().set_param( + 'import_pos', self.import_pos) + self.env['ir.config_parameter'].sudo().set_param( + 'import_attendance', self.import_attendance) + self.env['ir.config_parameter'].sudo().set_param( + 'import_payment', self.import_payment) + self.env['ir.config_parameter'].sudo().set_param( + 'import_task', self.import_task) + self.env['ir.config_parameter'].sudo().set_param( + 'import_sale', self.import_sale) + self.env['ir.config_parameter'].sudo().set_param( + 'import_purchase_order', self.import_purchase_order) + self.env['ir.config_parameter'].sudo().set_param( + 'import_product_template', self.import_product_template) + self.env['ir.config_parameter'].sudo().set_param( + 'import_partner', self.import_partner) + self.env['ir.config_parameter'].sudo().set_param( + 'import_invoice', self.import_invoice) + self.env['ir.config_parameter'].sudo().set_param( + 'import_pricelist', self.import_pricelist) + self.env['ir.config_parameter'].sudo().set_param( + 'import_vendor_pricelist', self.import_vendor_pricelist) + super(ResConfigSettings, self).set_values() diff --git a/import_dashboard/security/ir.model.access.csv b/import_dashboard/security/ir.model.access.csv new file mode 100644 index 000000000..6531dbe21 --- /dev/null +++ b/import_dashboard/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_import_attendance,access.import.attendance,model_import_attendance,base.group_user,1,1,1,1 +access_import_bill_of_material,access.import.bill.of.material,model_import_bill_of_material,base.group_user,1,1,1,1 +access_import_invoice,access.import.invoice,model_import_invoice,base.group_user,1,1,1,1 +access_import_message,access.import.message,model_import_message,base.group_user,1,1,1,1 +access_import_partner,access.import.partner,model_import_partner,base.group_user,1,1,1,1 +access_import_payment,access.import.payment,model_import_payment,base.group_user,1,1,1,1 +access_import_pos_order,access.import.pos.order,model_import_pos_order,base.group_user,1,1,1,1 +access_import_product_pricelist,access.import.product.pricelist,model_import_product_pricelist,base.group_user,1,1,1,1 +access_import_product_template,access.import.product.template,model_import_product_template,base.group_user,1,1,1,1 +access_import_purchase_order,access.import.purchase.order,model_import_purchase_order,base.group_user,1,1,1,1 +access_import_sale_order,access.import.sale.order,model_import_sale_order,base.group_user,1,1,1,1 +access_import_task,access.import.task,model_import_task,base.group_user,1,1,1,1 +access_import_vendor_pricelist,access.import.vendor.pricelist,model_import_vendor_pricelist,base.group_user,1,1,1,1 diff --git a/import_dashboard/static/description/assets/icons/check.png b/import_dashboard/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/import_dashboard/static/description/assets/icons/check.png differ diff --git a/import_dashboard/static/description/assets/icons/chevron.png b/import_dashboard/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/chevron.png differ diff --git a/import_dashboard/static/description/assets/icons/cogs.png b/import_dashboard/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/cogs.png differ diff --git a/import_dashboard/static/description/assets/icons/consultation.png b/import_dashboard/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/import_dashboard/static/description/assets/icons/consultation.png differ diff --git a/import_dashboard/static/description/assets/icons/ecom-black.png b/import_dashboard/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/ecom-black.png differ diff --git a/import_dashboard/static/description/assets/icons/education-black.png b/import_dashboard/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/import_dashboard/static/description/assets/icons/education-black.png differ diff --git a/import_dashboard/static/description/assets/icons/hotel-black.png b/import_dashboard/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/import_dashboard/static/description/assets/icons/hotel-black.png differ diff --git a/import_dashboard/static/description/assets/icons/license.png b/import_dashboard/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/import_dashboard/static/description/assets/icons/license.png differ diff --git a/import_dashboard/static/description/assets/icons/lifebuoy.png b/import_dashboard/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/import_dashboard/static/description/assets/icons/lifebuoy.png differ diff --git a/import_dashboard/static/description/assets/icons/manufacturing-black.png b/import_dashboard/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/import_dashboard/static/description/assets/icons/manufacturing-black.png differ diff --git a/import_dashboard/static/description/assets/icons/pos-black.png b/import_dashboard/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/pos-black.png differ diff --git a/import_dashboard/static/description/assets/icons/puzzle.png b/import_dashboard/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/puzzle.png differ diff --git a/import_dashboard/static/description/assets/icons/restaurant-black.png b/import_dashboard/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/restaurant-black.png differ diff --git a/import_dashboard/static/description/assets/icons/service-black.png b/import_dashboard/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/import_dashboard/static/description/assets/icons/service-black.png differ diff --git a/import_dashboard/static/description/assets/icons/trading-black.png b/import_dashboard/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/import_dashboard/static/description/assets/icons/trading-black.png differ diff --git a/import_dashboard/static/description/assets/icons/training.png b/import_dashboard/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/import_dashboard/static/description/assets/icons/training.png differ diff --git a/import_dashboard/static/description/assets/icons/update.png b/import_dashboard/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/import_dashboard/static/description/assets/icons/update.png differ diff --git a/import_dashboard/static/description/assets/icons/user.png b/import_dashboard/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/import_dashboard/static/description/assets/icons/user.png differ diff --git a/import_dashboard/static/description/assets/icons/wrench.png b/import_dashboard/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/import_dashboard/static/description/assets/icons/wrench.png differ diff --git a/import_dashboard/static/description/assets/misc/categories.png b/import_dashboard/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/categories.png differ diff --git a/import_dashboard/static/description/assets/misc/check-box.png b/import_dashboard/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/check-box.png differ diff --git a/import_dashboard/static/description/assets/misc/compass.png b/import_dashboard/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/import_dashboard/static/description/assets/misc/compass.png differ diff --git a/import_dashboard/static/description/assets/misc/corporate.png b/import_dashboard/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/import_dashboard/static/description/assets/misc/corporate.png differ diff --git a/import_dashboard/static/description/assets/misc/customer-support.png b/import_dashboard/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/import_dashboard/static/description/assets/misc/customer-support.png differ diff --git a/import_dashboard/static/description/assets/misc/cybrosys-logo.png b/import_dashboard/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/import_dashboard/static/description/assets/misc/cybrosys-logo.png differ diff --git a/import_dashboard/static/description/assets/misc/features.png b/import_dashboard/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/features.png differ diff --git a/import_dashboard/static/description/assets/misc/logo.png b/import_dashboard/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/import_dashboard/static/description/assets/misc/logo.png differ diff --git a/import_dashboard/static/description/assets/misc/pictures.png b/import_dashboard/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/pictures.png differ diff --git a/import_dashboard/static/description/assets/misc/pie-chart.png b/import_dashboard/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/pie-chart.png differ diff --git a/import_dashboard/static/description/assets/misc/right-arrow.png b/import_dashboard/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/right-arrow.png differ diff --git a/import_dashboard/static/description/assets/misc/star.png b/import_dashboard/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/import_dashboard/static/description/assets/misc/star.png differ diff --git a/import_dashboard/static/description/assets/misc/support.png b/import_dashboard/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/support.png differ diff --git a/import_dashboard/static/description/assets/misc/whatsapp.png b/import_dashboard/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/import_dashboard/static/description/assets/misc/whatsapp.png differ diff --git a/import_dashboard/static/description/assets/modules/1.png b/import_dashboard/static/description/assets/modules/1.png new file mode 100644 index 000000000..8eb34c3c5 Binary files /dev/null and b/import_dashboard/static/description/assets/modules/1.png differ diff --git a/import_dashboard/static/description/assets/modules/2.png b/import_dashboard/static/description/assets/modules/2.png new file mode 100644 index 000000000..f15663e28 Binary files /dev/null and b/import_dashboard/static/description/assets/modules/2.png differ diff --git a/import_dashboard/static/description/assets/modules/3.png b/import_dashboard/static/description/assets/modules/3.png new file mode 100644 index 000000000..69ecacfce Binary files /dev/null and b/import_dashboard/static/description/assets/modules/3.png differ diff --git a/import_dashboard/static/description/assets/modules/4.png b/import_dashboard/static/description/assets/modules/4.png new file mode 100644 index 000000000..b51b9c69d Binary files /dev/null and b/import_dashboard/static/description/assets/modules/4.png differ diff --git a/import_dashboard/static/description/assets/modules/5.png b/import_dashboard/static/description/assets/modules/5.png new file mode 100644 index 000000000..0ae7f6425 Binary files /dev/null and b/import_dashboard/static/description/assets/modules/5.png differ diff --git a/import_dashboard/static/description/assets/modules/6.png b/import_dashboard/static/description/assets/modules/6.png new file mode 100644 index 000000000..e790c45ac Binary files /dev/null and b/import_dashboard/static/description/assets/modules/6.png differ diff --git a/import_dashboard/static/description/assets/screenshots.zip b/import_dashboard/static/description/assets/screenshots.zip new file mode 100644 index 000000000..bf4dd77b3 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots.zip differ diff --git a/import_dashboard/static/description/assets/screenshots/1.png b/import_dashboard/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..600e40c10 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/1.png differ diff --git a/import_dashboard/static/description/assets/screenshots/10.png b/import_dashboard/static/description/assets/screenshots/10.png new file mode 100644 index 000000000..4d9b3f47a Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/10.png differ diff --git a/import_dashboard/static/description/assets/screenshots/11.png b/import_dashboard/static/description/assets/screenshots/11.png new file mode 100644 index 000000000..f3e01b7b4 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/11.png differ diff --git a/import_dashboard/static/description/assets/screenshots/12.png b/import_dashboard/static/description/assets/screenshots/12.png new file mode 100644 index 000000000..980265776 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/12.png differ diff --git a/import_dashboard/static/description/assets/screenshots/13.png b/import_dashboard/static/description/assets/screenshots/13.png new file mode 100644 index 000000000..1f3c8d78d Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/13.png differ diff --git a/import_dashboard/static/description/assets/screenshots/14.png b/import_dashboard/static/description/assets/screenshots/14.png new file mode 100644 index 000000000..b5b1b0c0a Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/14.png differ diff --git a/import_dashboard/static/description/assets/screenshots/15.png b/import_dashboard/static/description/assets/screenshots/15.png new file mode 100644 index 000000000..de48e859b Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/15.png differ diff --git a/import_dashboard/static/description/assets/screenshots/2.png b/import_dashboard/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..cfcf245d8 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/2.png differ diff --git a/import_dashboard/static/description/assets/screenshots/3.png b/import_dashboard/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..85a02f132 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/3.png differ diff --git a/import_dashboard/static/description/assets/screenshots/4.png b/import_dashboard/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..50d1daa90 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/4.png differ diff --git a/import_dashboard/static/description/assets/screenshots/5.png b/import_dashboard/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..b4bdb10c6 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/5.png differ diff --git a/import_dashboard/static/description/assets/screenshots/6.png b/import_dashboard/static/description/assets/screenshots/6.png new file mode 100644 index 000000000..89bce7402 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/6.png differ diff --git a/import_dashboard/static/description/assets/screenshots/7.png b/import_dashboard/static/description/assets/screenshots/7.png new file mode 100644 index 000000000..6dce0562b Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/7.png differ diff --git a/import_dashboard/static/description/assets/screenshots/8.png b/import_dashboard/static/description/assets/screenshots/8.png new file mode 100644 index 000000000..c0a25cd1c Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/8.png differ diff --git a/import_dashboard/static/description/assets/screenshots/9.png b/import_dashboard/static/description/assets/screenshots/9.png new file mode 100644 index 000000000..f4c91c0bd Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/9.png differ diff --git a/import_dashboard/static/description/assets/screenshots/hero.gif b/import_dashboard/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..c396a72a7 Binary files /dev/null and b/import_dashboard/static/description/assets/screenshots/hero.gif differ diff --git a/import_dashboard/static/description/banner.jpg b/import_dashboard/static/description/banner.jpg new file mode 100644 index 000000000..d143036f8 Binary files /dev/null and b/import_dashboard/static/description/banner.jpg differ diff --git a/import_dashboard/static/description/icon.png b/import_dashboard/static/description/icon.png new file mode 100644 index 000000000..2dc63b0c0 Binary files /dev/null and b/import_dashboard/static/description/icon.png differ diff --git a/import_dashboard/static/description/index.html b/import_dashboard/static/description/index.html new file mode 100644 index 000000000..c067985f3 --- /dev/null +++ b/import_dashboard/static/description/index.html @@ -0,0 +1,730 @@ +
+ +
+ +
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ Import Dashboard

+

+ This module can save users a significant amount of time when + importing large amounts of data into the system. It helps + businesses that rely on accurate and timely data. +

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

+ Explore This + Module

+
+ + + + +
+
+ +
+

+ Overview +

+
+
+
+ The Import Dashboard feature in Odoo is a powerful tool that + can save users a significant amount of time when importing large amounts + of data into the system. It streamlines the import process and reduces + the likelihood of errors, making it a valuable feature for businesses + that rely on accurate and timely data. +
+
+ + + +
+
+ +
+

+ Features +

+
+
+
+
+ + Community & + Enterprise Support. +
+
+ + + Odoo supports various file formats, including CSV, Excel. + +
+
+ + + The Import Dashboard feature can be accessed through the "Import" button in the Odoo user interface. + +
+
+ + This feature supports the import of different types of data, including Products, Customers, Suppliers, Invoices, and more. +
+
+
+ +
+ + This has also includes validation rules to ensure that the imported data meets the required format and data types. +
+ +
+ + After the data has been imported, users can review the results and make any necessary adjustments.. +
+ +
+ + The all in one import feature can save time and reduce errors when importing data into Odoo, making it a valuable tool for users who need to import large amounts of data. +
+ +
+
+ + + +
+
+ +
+

+ Screenshots

+
+ +
+
+
+

+ Install Import Dashboard App. +

+ +
+
+

+ Enable the Settings for importing records that are needed. +

+ +
+
+

+ The Dashboard view for import different records. +

+ +
+
+

+ Sale Order import wizard with different options. +

+ +
+
+

+ Purchase Order import wizard with different options. +

+ +
+
+

+ Invoice import wizard with different options. +

+ +
+
+

+ Partner import wizard with different options. +

+ +
+
+

+ Product Pricelist import wizard with different options. +

+ +
+
+

+ Bill Of Material import wizard with different options. +

+ +
+
+

+ POS Order import wizard with different options. +

+ +
+
+

+ Attendance import wizard with different options. +

+ +
+
+

+ Payment import wizard with different options. +

+ +
+
+

+ Project Tasks import wizard with different options. +

+ +
+
+

+ Product Import wizard with different options. +

+ +
+
+

+ Vendor Pricelist import wizard with different options. +

+ +
+ +
+
+ + + +
+
+

Suggested Products

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

+ Our Services +

+
+ +
+
+
+
+ +
+
+ Odoo + Customization
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ Odoo + Support
+
+ + +
+
+ +
+
+ Hire + Odoo + Developer
+
+ +
+
+ +
+
+ Odoo + Integration
+
+ +
+
+ +
+
+ Odoo + Migration
+
+ + +
+
+ +
+
+ Odoo + Consultancy
+
+ +
+
+ +
+
+ Odoo + Implementation
+
+ +
+
+ +
+
+ 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 86068 + 27707

+
+
+
+
+
+
+
+ +
+
+
+ \ No newline at end of file diff --git a/import_dashboard/static/images/calendar.png b/import_dashboard/static/images/calendar.png new file mode 100644 index 000000000..1452bb8ca Binary files /dev/null and b/import_dashboard/static/images/calendar.png differ diff --git a/import_dashboard/static/images/cart.png b/import_dashboard/static/images/cart.png new file mode 100644 index 000000000..0b7d9167d Binary files /dev/null and b/import_dashboard/static/images/cart.png differ diff --git a/import_dashboard/static/images/icons8-invoice-64 .png b/import_dashboard/static/images/icons8-invoice-64 .png new file mode 100644 index 000000000..582c0330a Binary files /dev/null and b/import_dashboard/static/images/icons8-invoice-64 .png differ diff --git a/import_dashboard/static/images/icons8-list-48.png b/import_dashboard/static/images/icons8-list-48.png new file mode 100644 index 000000000..0046af448 Binary files /dev/null and b/import_dashboard/static/images/icons8-list-48.png differ diff --git a/import_dashboard/static/images/icons8-paid-bill-64.png b/import_dashboard/static/images/icons8-paid-bill-64.png new file mode 100644 index 000000000..e7eee2f11 Binary files /dev/null and b/import_dashboard/static/images/icons8-paid-bill-64.png differ diff --git a/import_dashboard/static/images/icons8-partner-64.png b/import_dashboard/static/images/icons8-partner-64.png new file mode 100644 index 000000000..73745c04e Binary files /dev/null and b/import_dashboard/static/images/icons8-partner-64.png differ diff --git a/import_dashboard/static/images/icons8-vendor-64.png b/import_dashboard/static/images/icons8-vendor-64.png new file mode 100644 index 000000000..320f693eb Binary files /dev/null and b/import_dashboard/static/images/icons8-vendor-64.png differ diff --git a/import_dashboard/static/images/payment-terminal.png b/import_dashboard/static/images/payment-terminal.png new file mode 100644 index 000000000..6df6043bb Binary files /dev/null and b/import_dashboard/static/images/payment-terminal.png differ diff --git a/import_dashboard/static/images/payment.png b/import_dashboard/static/images/payment.png new file mode 100644 index 000000000..e875bd52a Binary files /dev/null and b/import_dashboard/static/images/payment.png differ diff --git a/import_dashboard/static/images/tasks.png b/import_dashboard/static/images/tasks.png new file mode 100644 index 000000000..9c82816a0 Binary files /dev/null and b/import_dashboard/static/images/tasks.png differ diff --git a/import_dashboard/static/sample_templates/Attendance_Template.csv b/import_dashboard/static/sample_templates/Attendance_Template.csv new file mode 100644 index 000000000..aa8ccab03 --- /dev/null +++ b/import_dashboard/static/sample_templates/Attendance_Template.csv @@ -0,0 +1,2 @@ +Employee,Check In,Check Out,Worked Hours +Abigail Peterson,2023-07-30 08:00:24,2023-07-30 12:01:33,4.01916666666667 diff --git a/import_dashboard/static/sample_templates/Attendance_Template.xlsx b/import_dashboard/static/sample_templates/Attendance_Template.xlsx new file mode 100644 index 000000000..15f734662 Binary files /dev/null and b/import_dashboard/static/sample_templates/Attendance_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/BOM_Template.csv b/import_dashboard/static/sample_templates/BOM_Template.csv new file mode 100644 index 000000000..6988730e5 --- /dev/null +++ b/import_dashboard/static/sample_templates/BOM_Template.csv @@ -0,0 +1,2 @@ +Product,Product/Internal Reference,Product/Barcode,Quantity,Reference,BoM Type,Components,Components/Internal Reference,Components/Barcode,BoM Lines/Quantity +TestProduct,TEST_0003,1111111,5,,Manufacture this product,Screw,CONS_25630,,10 diff --git a/import_dashboard/static/sample_templates/BOM_Template.xlsx b/import_dashboard/static/sample_templates/BOM_Template.xlsx new file mode 100644 index 000000000..e6d6710fc Binary files /dev/null and b/import_dashboard/static/sample_templates/BOM_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Invoice_Template.csv b/import_dashboard/static/sample_templates/Invoice_Template.csv new file mode 100644 index 000000000..15ad02129 --- /dev/null +++ b/import_dashboard/static/sample_templates/Invoice_Template.csv @@ -0,0 +1,2 @@ +Partner,Payment Reference,Invoice Date,Due Date,Salesperson,Number,Label,Product,Account Code,Quantity,Uom,Price,Disc.%,Taxes,Internal Reference,Variant Values,Barcode +Azure Interior,INV/2023/00024,08/09/2023,08/20/2023,Marc Demo,INV/2023/00024,[FURN_6741] Large Meeting Table Conference room table,Four Person Desk,400000,1,Units,400,,Tax 10%,FURN_8220,,6548523917 diff --git a/import_dashboard/static/sample_templates/Invoice_Template.xlsx b/import_dashboard/static/sample_templates/Invoice_Template.xlsx new file mode 100644 index 000000000..c261a4692 Binary files /dev/null and b/import_dashboard/static/sample_templates/Invoice_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Partner_Template.csv b/import_dashboard/static/sample_templates/Partner_Template.csv new file mode 100644 index 000000000..5a72071e7 --- /dev/null +++ b/import_dashboard/static/sample_templates/Partner_Template.csv @@ -0,0 +1,3 @@ +Is company? (y/n),Related Company,Job Position,Title,Name,Street,Street2,City,Country,State,Zip,Tax ID,Phone,Mobile,Email,Website,Tags,Salesperson +n,Azure Interior,Manager,Mr,Abdu,street1,Street2,utopian_city1,Italy,Aveiro,12345,1234567890,987654321,123456789,abc@abc.com,qwerty.com,"tag1, tag2",Mitchell Admin +y,,,,Sukus,street1,Street2,utopian_city2,Greece,Azuay,543210,9876543210,907654321,123456787,suku@asd.mail,sukus.com,"tag3, tag4, tag5",Mitchell Admin diff --git a/import_dashboard/static/sample_templates/Partner_Template.xlsx b/import_dashboard/static/sample_templates/Partner_Template.xlsx new file mode 100644 index 000000000..551f61653 Binary files /dev/null and b/import_dashboard/static/sample_templates/Partner_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Payment_Template.csv b/import_dashboard/static/sample_templates/Payment_Template.csv new file mode 100644 index 000000000..9e179334b --- /dev/null +++ b/import_dashboard/static/sample_templates/Payment_Template.csv @@ -0,0 +1,2 @@ +Date,Number,Journal,Amount,Customer/Vendor,Payment Type,Reference +2023-08-28 07:32:33,PBNK5/2023/00009,Bank,200,Billy Fox,Send,88888888888 diff --git a/import_dashboard/static/sample_templates/Payment_Template.xlsx b/import_dashboard/static/sample_templates/Payment_Template.xlsx new file mode 100644 index 000000000..e11072094 Binary files /dev/null and b/import_dashboard/static/sample_templates/Payment_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Pos_Order_template.csv b/import_dashboard/static/sample_templates/Pos_Order_template.csv new file mode 100644 index 000000000..2ddd53146 --- /dev/null +++ b/import_dashboard/static/sample_templates/Pos_Order_template.csv @@ -0,0 +1,5 @@ +Order Ref,Session,Receipt Number,Order Date,Customer,Responsible,Total,Tax Amount,Amount Returned,Paid Amount,State,Product,Product Code,Barcode,Quantity,Unit Price,Discount %,Sub Total +Shop/0003,POS/00011,Order 00005-001-0001,2023-08-24,Billy Fox,Mitchell Admin,50.5,0.5,0,61,done,Office Chair,FURN_7777,,2,70,,141 +,,,,,,,,,,,Office Chair Black,FURN_0269,,5,120.5,,602.5 +Shop/0005,POS/00011,Order 00005-001-0001,2023-08-24,Billy Fox,Mitchell Admin,50.5,0.5,0,61,done,Office Chair,FURN_7777,,2,70,,141 +,,,,,,,,,,,Office Chair Black,FURN_0269,,5,120.5,,602.5 diff --git a/import_dashboard/static/sample_templates/Pos_Order_template.xlsx b/import_dashboard/static/sample_templates/Pos_Order_template.xlsx new file mode 100644 index 000000000..09578ff05 Binary files /dev/null and b/import_dashboard/static/sample_templates/Pos_Order_template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Product_Pricelist_Template.csv b/import_dashboard/static/sample_templates/Product_Pricelist_Template.csv new file mode 100644 index 000000000..778b290f5 --- /dev/null +++ b/import_dashboard/static/sample_templates/Product_Pricelist_Template.csv @@ -0,0 +1,2 @@ +Name,Product,Internal Reference ,Barcode,Variant Values,Fixed Price,Minimum Quantity,Start_date,End_date,Discount%,Extra Fee,Rounding Method,Min. Margin,Max. Margin,Other Pricelist,Product Category +Test Price List,Cabinet with Doors,E-COM11,1254,,222,,2023-08-28 07:32:33,2023-08-30 07:32:33,,,,,,, diff --git a/import_dashboard/static/sample_templates/Product_Pricelist_Template.xlsx b/import_dashboard/static/sample_templates/Product_Pricelist_Template.xlsx new file mode 100644 index 000000000..62dcd5ada Binary files /dev/null and b/import_dashboard/static/sample_templates/Product_Pricelist_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Product_Template_Template.csv b/import_dashboard/static/sample_templates/Product_Template_Template.csv new file mode 100644 index 000000000..b875481ae --- /dev/null +++ b/import_dashboard/static/sample_templates/Product_Template_Template.csv @@ -0,0 +1,3 @@ +Name,Internal Reference,Product Type,Sales Price,Cost +Office Chair,TEST_0003,product,120,90 +ProductTemp2,TEST_0014,product,100,88 diff --git a/import_dashboard/static/sample_templates/Product_Template_Template.xlsx b/import_dashboard/static/sample_templates/Product_Template_Template.xlsx new file mode 100644 index 000000000..e635285ab Binary files /dev/null and b/import_dashboard/static/sample_templates/Product_Template_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Purchase_Template.csv b/import_dashboard/static/sample_templates/Purchase_Template.csv new file mode 100644 index 000000000..ad625606b --- /dev/null +++ b/import_dashboard/static/sample_templates/Purchase_Template.csv @@ -0,0 +1,2 @@ +Order Reference,Vendor,Vendor Reference,Order Deadline,Receipt Date,Purchase Representative,Description,Delivery Date,Quantity,Uom,Unit Price,Taxes,Product,Variant Values,Barcode +P00020,Billy Fox,,08/29/2023,08/31/2023,Marc Demo,[FURN_6666] Acoustic Bloc Screens,08/31/2023,2,Units,800,Tax 10%,My Acoustic Bloc Screens,Leg: Steel,5824693 diff --git a/import_dashboard/static/sample_templates/Purchase_Template.xlsx b/import_dashboard/static/sample_templates/Purchase_Template.xlsx new file mode 100644 index 000000000..ae0d4d1ed Binary files /dev/null and b/import_dashboard/static/sample_templates/Purchase_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Sale_Order_Template.csv b/import_dashboard/static/sample_templates/Sale_Order_Template.csv new file mode 100644 index 000000000..82463bc97 --- /dev/null +++ b/import_dashboard/static/sample_templates/Sale_Order_Template.csv @@ -0,0 +1,2 @@ +Order Reference,Customer,Quotation Date,Salesperson,Product,Description,Quantity,Uom,Unit Price,Taxes,Disc.%,Internal Reference,Variant Values +S00008,Billy Fox,08/24/2023,Mitchell Admin,Customizable Desk,"[FURN_0096] Customizable Desk (Steel, White) 160x80cm, with large legs. ",2,Units,750,Tax 15%,0,,"Legs: Steel, Color: White" diff --git a/import_dashboard/static/sample_templates/Sale_Order_Template.xlsx b/import_dashboard/static/sample_templates/Sale_Order_Template.xlsx new file mode 100644 index 000000000..ef8094e1d Binary files /dev/null and b/import_dashboard/static/sample_templates/Sale_Order_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Task_Template.csv b/import_dashboard/static/sample_templates/Task_Template.csv new file mode 100644 index 000000000..80e830c3d --- /dev/null +++ b/import_dashboard/static/sample_templates/Task_Template.csv @@ -0,0 +1,2 @@ +Project,Title,Customer,Deadline,Parent Task +Test,new_test,Billy Fox,08/25/2023, diff --git a/import_dashboard/static/sample_templates/Task_Template.xlsx b/import_dashboard/static/sample_templates/Task_Template.xlsx new file mode 100644 index 000000000..343bbc44f Binary files /dev/null and b/import_dashboard/static/sample_templates/Task_Template.xlsx differ diff --git a/import_dashboard/static/sample_templates/Vendor_Pricelist.csv b/import_dashboard/static/sample_templates/Vendor_Pricelist.csv new file mode 100644 index 000000000..f5f4bb3df --- /dev/null +++ b/import_dashboard/static/sample_templates/Vendor_Pricelist.csv @@ -0,0 +1,2 @@ +Vendor,Product Template,Currency,Quantity,Price,Delivery Lead Time +Lumber Inc,Large Desk,USD,5,100,10 diff --git a/import_dashboard/static/sample_templates/Vendor_Pricelist.xlsx b/import_dashboard/static/sample_templates/Vendor_Pricelist.xlsx new file mode 100644 index 000000000..a582285e7 Binary files /dev/null and b/import_dashboard/static/sample_templates/Vendor_Pricelist.xlsx differ diff --git a/import_dashboard/static/src/css/style.scss b/import_dashboard/static/src/css/style.scss new file mode 100644 index 000000000..8f073783a --- /dev/null +++ b/import_dashboard/static/src/css/style.scss @@ -0,0 +1,84 @@ +.main-container{ + overflow-y: auto; + height: 100vh; + overflow-x: none; +} +.card-shadow { + height: 65%; + -webkit-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + -moz-box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + box-shadow: 1px 3px 5px 0px rgba(222, 222, 222, 1); + color: #243c64; + size: 10rem; + text-align: center; + padding:15px; + background:white; + border: 2px solid #71639e; + width:300px; +} +.card3{ + display:flex; + justify-content:center; + align-items:center; + height: 32vh; +} +.card6{ + display:flex; + justify-content:center; + align-items:center; + height: 32vh; +} +.card12{ + display:flex; + justify-content:center; + align-items:center; + height: 32vh; +} + +.stat-widget-one{ + display:flex; + flex-direction: column; + height:100%; + justify-content: space-around; +} +.stat-icon{ + background: white; + width: 85px; + height: 80px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 3px; +} +.row{ + padding-right: 2%; + padding-left: 2%; +} +.card-shadow-container{ + display: flex; + justify-content: flex-start; +} +.btn { + width: fit-content; + align-self: center; +} +.stat-content{ + margin-left: 35px; + font-size: 20px; + font-weight: 600; +} +.stat-head{ + margin-center: 35px; + font-size: 20px; + font-weight: 600; +} +@media (max-width: 767.98px) { + .stat-icon{ + height: 70px; + } +} +@media (max-height: 671px) { + .stat-icon{ + height: 10px; + } +} \ No newline at end of file diff --git a/import_dashboard/static/src/js/import_dashboard.js b/import_dashboard/static/src/js/import_dashboard.js new file mode 100644 index 000000000..ef9826116 --- /dev/null +++ b/import_dashboard/static/src/js/import_dashboard.js @@ -0,0 +1,399 @@ +odoo.define('import_dashboard.dashboard', function (require){ +"use strict"; + var AbstractAction = require('web.AbstractAction'); + var core = require('web.core'); + var QWeb = core.qweb; + var rpc = require('web.rpc'); + var ajax = require('web.ajax'); + var check_bill_of_material = '' + var check_pos ='' + var check_attendance ='' + var check_payment ='' + var check_task ='' + var check_sale ='' + var check_purchase ='' + var check_product ='' + var check_partner ='' + var check_invoice ='' + var check_pricelist ='' + var check_vendor_pricelist ='' + var ImportDashBoard = AbstractAction.extend({ + template: 'imp_dash', + events: { + 'click .import_sale': 'import_sale', + 'click .import_purchase': 'import_purchase', + 'click .import_invoice': 'import_invoice', + 'click .import_partner': 'import_partner', + 'click .import_product_pricelist': 'import_product_pricelist', + 'click .import_bill_of_material': 'import_bill_of_material', + 'click .import_product_template': 'import_product_template', + 'click .import_vendor_pricelist': 'import_vendor_pricelist', + 'click .import_pos': 'import_pos', + 'click .import_attendance': 'import_attendance', + 'click .import_payment': 'import_payment', + 'click .import_task': 'import_task', + 'click .import_sales_pricelist': 'import_sales_pricelist', + 'click .import_inventory': 'import_inventory', + 'click .import_inventory_with_lot': 'import_inventory_with_lot', + }, + willStart: function(){ + //For enabling the corresponding boolean field for modules in the dashboard + var self = this; + return this._super() + .then(function() { + var def0 = self._rpc({ + model: 'ir.config_parameter', + method: 'check_user_group' + }).then(function(result) { + if (result['bill_of_material']){ + self.check_bill_of_material=true; + } + else{ + self.check_bill_of_material=false; + } + if (result['pos']){ + self.check_pos=true; + } + else{ + self.check_pos=false; + } + if (result['import_attendance']){ + self.check_attendance=true; + } + else{ + self.check_attendance=false; + } + if (result['import_payment']){ + self.check_payment=true; + } + else{ + self.check_payment=false; + } + if (result['import_task']){ + self.check_task=true; + } + else{ + self.check_task=false; + } + + if (result['import_sale']){ + self.check_sale=true; + } + else{ + self.check_sale=false; + } + if (result['import_purchase']){ + self.check_purchase=true; + } + else{ + self.check_purchase=false; + } + if (result['import_product_template']){ + self.check_product=true; + } + else{ + self.check_product=false; + } + if (result['import_partner']){ + self.check_partner=true; + } + else{ + self.check_partner=false; + } + if (result['import_invoice']){ + self.check_invoice=true; + } + else{ + self.check_invoice=false; + } + if (result['import_pricelist']){ + self.check_pricelist=true; + } + else{ + self.check_pricelist=false; + } + if (result['import_vendor_pricelist']){ + self.check_vendor_pricelist=true; + } + else{ + self.check_vendor_pricelist=false; + } + }); + return $.when(def0); + }); + }, + start: function () { + //super the start function for calling the check_data() function + var self = this; + return this._super().then(function() { + self.check_data(); + }); + }, + check_data: function(){ + //For showing the corresponding tiles in the dashboard + var self = this; + if(self.check_bill_of_material==true){ + self.$("card_bill_of_material").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_bill_of_material").hide(); + } + if(self.check_pos==true){ + self.$("#card_pos").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_pos").hide(); + } + if(self.check_attendance==true){ + self.$("#card_attendance").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_attendance").hide(); + } + if(self.check_payment==true){ + self.$("#card_payment").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_payment").hide(); + } + if(self.check_task==true){ + self.$("#card_task").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_task").hide(); + } + if(self.check_sale==true){ + self.$("#card_sale").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_sale").hide(); + } + if(self.check_purchase==true){ + self.$("#card_purchase").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_purchase").hide(); + } + if(self.check_product==true){ + self.$("#card_product").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_product").hide(); + } + if(self.check_partner==true){ + self.$("#card_partner").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_partner").hide(); + } + if(self.check_invoice==true){ + self.$("#card_invoice").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_invoice").hide(); + } + if(self.check_pricelist==true){ + self.$("#card_pricelist").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_pricelist").hide(); + } + if(self.check_vendor_pricelist==true){ + self.$("#card_vendor_pricelist").show(); + self.$('.start_msg').hide(); + } + else{ + self.$("#card_vendor_pricelist").hide(); + } + }, + import_sale: function (e) { + //For loading the import of sales order form view + var self = this; + this.do_action({ + name: "Import Sale Order", + type: 'ir.actions.act_window', + res_model: 'import.sale.order', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_purchase: function (e) { + //For loading the import of purchase order form view + this.do_action({ + name: "Import Purchase Order", + type: 'ir.actions.act_window', + res_model: 'import.purchase.order', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_invoice: function (e) { + //For loading the import of invoice form view + this.do_action({ + name: "Import Invoice", + type: 'ir.actions.act_window', + res_model: 'import.invoice', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_partner: function (e) { + //For loading the import of partner form view + this.do_action({ + name: "Import Partner", + type: 'ir.actions.act_window', + res_model: 'import.partner', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_product_pricelist: function (e) { + //For loading the import of product price-list form view + this.do_action({ + name: "Import Product Pricelist", + type: 'ir.actions.act_window', + res_model: 'import.product.pricelist', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_bill_of_material: function (e) { + //For loading the import of BOM form view + var self = this; + this.do_action({ + name: "Import Bill of Material", + type: 'ir.actions.act_window', + res_model: 'import.bill.of.material', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_product_template: function (e) { + //For loading the import of product template form view + var self = this; + this.do_action({ + name: "Import Product", + type: 'ir.actions.act_window', + res_model: 'import.product.template', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_vendor_pricelist: function (e) { + //For loading the import of vendor price-list form view + var self = this; + this.do_action({ + name: "Import Vendor Pricelist", + type: 'ir.actions.act_window', + res_model: 'import.vendor.pricelist', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_pos: function (e) { + //For loading the import of pos orders form view + var self = this; + this.do_action({ + name: "Import POS", + type: 'ir.actions.act_window', + res_model: 'import.pos.order', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_attendance: function (e) { + //For loading the import of attendence form view + var self = this; + this.do_action({ + name: "Import Attendance", + type: 'ir.actions.act_window', + res_model: 'import.attendance', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_payment: function (e) { + //For loading the import of payments form view + var self = this; + this.do_action({ + name: "Import Payment", + type: 'ir.actions.act_window', + res_model: 'import.payment', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_task: function (e) { + //For loading the import of task form view + var self = this; + this.do_action({ + name: "Import Task", + type: 'ir.actions.act_window', + res_model: 'import.task', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_sales_pricelist: function (e) { + //For loading the import of sales price-list form view + var self = this; + this.do_action({ + name: "Import Sales Pricelist", + type: 'ir.actions.act_window', + res_model: 'import.sales.pricelist', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_inventory: function (e) { + //For loading the import of inventory without lot and serial number form view + var self = this; + this.do_action({ + name: "Import Inventory without Lot and Serial Number", + type: 'ir.actions.act_window', + res_model: 'import.inventory', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, + import_inventory_with_lot: function (e) { + //For loading the import of inventory with lot and serial number form view + var self = this; + this.do_action({ + name: "Import Inventory with Lot and Serial Number", + type: 'ir.actions.act_window', + res_model: 'import.inventory.with.lot', + view_mode: 'form', + views: [[false, 'form']], + target: 'new', + }) + }, +}) +core.action_registry.add('import_dashboard_tag', ImportDashBoard); +return ImportDashBoard; +}); diff --git a/import_dashboard/static/src/xml/dashboard_templates.xml b/import_dashboard/static/src/xml/dashboard_templates.xml new file mode 100644 index 000000000..a05f9542b --- /dev/null +++ b/import_dashboard/static/src/xml/dashboard_templates.xml @@ -0,0 +1,398 @@ + + + + +
+
+
+

+ Empty Dashboard +

+

+ Enable Settings To View Your Dashboard. +

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

Sale

+
+ Import Sale Order +
+
+
+
+ +
+ +
+
+ + +
+
+
+
+
+ +
+
+
Purchase
+
+ Import Purchase Order +
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Invoice
+
+ Import Invoice +
+ +
+
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Partner +
+
+ Import Partner +
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
Product + Pricelist +
+
+ Import Product Pricelist +
+
+
+ +
+
+
+ +
+
+ +
+
+
+ +
+
+
+

Bill of Material

+
+ Import Bill of Material +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+

POS

+
+ Import POS Orders +
+
+
+
+ +
+
+
+ +
+
+
+
+
+ +
+
+
+

Attendance

+
+ Import Attendance +
+
+
+
+ +
+
+
+
+
+ +
+
+
+ +
+
+
+

Payment

+
+ Import Payment +
+
+
+
+ +
+
+
+ +
+
+
+
+
+
+ +
+ +
+
+

Task

+
+ Import Task +
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+ +
+
+

Product Template

+
+ Import Product Template +
+ +
+
+
+ +
+
+
+ + +
+
+
+
+
+ +
+
+
+

Vendor Pricelist

+
+ Import Vendor Pricelist +
+ +
+
+
+ +
+
+
+
+
+
+
+
diff --git a/import_dashboard/views/menus.xml b/import_dashboard/views/menus.xml new file mode 100644 index 000000000..588452e3f --- /dev/null +++ b/import_dashboard/views/menus.xml @@ -0,0 +1,19 @@ + + + + + Import Dashboard + import_dashboard_tag + + + + + + diff --git a/import_dashboard/views/res_config_settings_views.xml b/import_dashboard/views/res_config_settings_views.xml new file mode 100644 index 000000000..bb13d21f7 --- /dev/null +++ b/import_dashboard/views/res_config_settings_views.xml @@ -0,0 +1,175 @@ + + + + + res.config.settings.view.form.inherit.import.dashboard + res.config.settings + + + + +
+

Import

+
+
+
+ +
+
+
+
+ Import Bill of + Materials + +
+
+
+
+ +
+
+
+
+ Import Sale +
+
+
+
+ +
+
+
+
+ Import Partner +
+
+
+
+ +
+
+
+
+ Import Purchase + +
+
+
+
+ +
+
+
+
+ Import POS Orders + +
+
+
+
+ +
+
+
+
+ Import Invoice +
+
+
+
+ +
+
+
+
+ Import Product + Template + +
+
+
+
+ +
+
+
+
+ Import Vendor + Pricelist + +
+
+
+
+ +
+
+
+
+ Import Product + Pricelist + +
+
+
+
+ +
+
+
+
+ Import Attendance + +
+
+
+
+ +
+
+
+
+ Import Payment +
+
+
+
+ +
+
+
+
+ Import Project + Tasks + +
+
+
+
+
+
+
+ + + Settings + ir.actions.act_window + res.config.settings + form + inline + {'module' : 'import_dashboard', 'bin_size': False} + + +
diff --git a/import_dashboard/wizard/__init__.py b/import_dashboard/wizard/__init__.py new file mode 100644 index 000000000..dae4f1760 --- /dev/null +++ b/import_dashboard/wizard/__init__.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from . import import_attendance +from . import import_bill_of_material +from . import import_invoice +from . import import_message +from . import import_partner +from . import import_payment +from . import import_pos_order +from . import import_product_pricelist +from . import import_product_template +from . import import_purchase_order +from . import import_sale_order +from . import import_task +from . import import_vendor_pricelist diff --git a/import_dashboard/wizard/import_attendance.py b/import_dashboard/wizard/import_attendance.py new file mode 100644 index 000000000..63a5187c5 --- /dev/null +++ b/import_dashboard/wizard/import_attendance.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo import fields, models +from odoo.exceptions import ValidationError + + +class ImportAttendance(models.TransientModel): + """ Model for Attendance import wizard. """ + _name = 'import.attendance' + _description = 'Attendance Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xls', 'XLS File')], + string='Select File Type', default='csv', + help="It helps to select File Type") + file_upload = fields.Binary(string="Upload File", + help="It helps to upload files") + + def action_import_attendance(self): + """Creating attendance record using uploaded xl/csv files""" + hr_employee = self.env['hr.employee'] + hr_attendance = self.env['hr.attendance'] + datas = {} + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + datas = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + """File not Valid.\n\nPlease check the """ + """type and format of the file and try again!""") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + datas = data + for item in datas: + vals = {} + if item.get('Employee'): + employee = hr_employee.search( + [('name', '=', item.get('Employee'))]) + if employee: + vals['employee_id'] = employee.id + else: + raise ValidationError( + "There is no employee with that name." + "\n\nPlease check and try again!") + if item.get('Check In'): + if self.file_type == 'csv': + vals['check_in'] = item.get('Check In') + else: + vals['check_in'] = xlrd.xldate_as_datetime( + item.get('Check In'), 0) + if item.get('Check Out'): + if self.file_type == 'csv': + vals['check_out'] = item.get('Check Out') + else: + vals['check_out'] = xlrd.xldate_as_datetime( + item.get('Check Out'), 0) + if item.get('Worked Hours'): + vals['worked_hours'] = item.get('Worked Hours') + hr_attendance.create(vals) + return { + 'effect': { + 'fadeout': 'slow', + 'message': 'Imported Successfully', + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_attendance_views.xml b/import_dashboard/wizard/import_attendance_views.xml new file mode 100644 index 000000000..aa769f486 --- /dev/null +++ b/import_dashboard/wizard/import_attendance_views.xml @@ -0,0 +1,23 @@ + + + + + import.attendance.view.form + import.attendance + +
+ + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_bill_of_material.py b/import_dashboard/wizard/import_bill_of_material.py new file mode 100644 index 000000000..4b16fc150 --- /dev/null +++ b/import_dashboard/wizard/import_bill_of_material.py @@ -0,0 +1,211 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo import fields, models +from odoo.exceptions import ValidationError + + +class ImportBillOfMaterial(models.TransientModel): + """ Model for importing Bill of Materials. """ + _name = 'import.bill.of.material' + _description = 'Bill of Material Import' + + file_type = fields.Selection(selection=[('csv', 'CSV File'), + ('xls', 'XLS File')], default='csv', + string='Select File Type', + help="Uploading file Type") + file_upload = fields.Binary(string='Upload File', + help="Helps to upload file") + import_product_by = fields.Selection( + selection=[ + ('default_code', 'Internal Reference'), ('barcode', 'Barcode')], + default='default_code', string="Import Products By", + help="Helps to import product") + bom_type = fields.Selection( + selection=[('manufacture_this_product', 'Manufacture this Product'), + ('kit', 'Kit'), ('both', 'Both')], string="Bom Type", + default='both', help="Helps to choose the bom type") + bom_component = fields.Selection( + selection=[('add', 'Add Components'), + ('do_not', 'Do not add Components')], default='add', + string="Bom Component", help="Helps to choose the bom component") + + def action_import_bom(self): + """Creating sale order record using uploaded xl/csv files""" + datas = {} + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + datas = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + """File not Valid.\n\nPlease check the """ + """type and format of the file and try again!""") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + datas = data + row = 0 + imported = 0 + updated = 0 + error_msg = "" + warning_msg = "" + for item in datas: + row += 1 + vals = {} + if item.get('Product'): + if self.import_product_by == 'default_code' and item.get( + 'Product/Internal Reference'): + product_tmpl = self.env['product.template'].search( + [('default_code', + '=', item.get( + 'Product/Internal Reference'))]) + elif self.import_product_by == 'barcode' and item.get( + 'Product/Barcode'): + product_tmpl = self.env['product.template'].search( + [('barcode', '=', item.get('Product/Barcode'))]) + else: + product_tmpl = self.env['product.template'].search( + [('name', '=', item.get('Product'))]) + vals['product_tmpl_id'] = product_tmpl.id + if not product_tmpl: + product_template = self.env['product.template'].create({ + 'name': item.get('Product'), + 'default_code': item.get('Product/Internal Reference'), + 'barcode': item.get('Product/Barcode') + }) + warning_msg += ("\n◼ A Product is created with " + "\"Internal Reference\" as " + "product name since \"Product\"" + " name is missing in file." + "(row %d)" % row) + vals['product_tmpl_id'] = product_template.id + else: + error_msg += "\n\t⚠ Product name missing in file!" + if item.get('Quantity'): + vals['product_qty'] = item.get('Quantity') + if item.get('Reference'): + vals['code'] = item.get('Reference') + if self.bom_type == 'manufacture_this_product': + vals['type'] = 'normal' + elif self.bom_type == 'kit': + vals['type'] = 'phantom' + else: + if item.get('BoM Type'): + if item.get('BoM Type') == 'Manufacture this product': + vals['type'] = 'normal' + else: + vals['type'] = 'phantom' + components = {} + if self.bom_component == 'add': + if item.get('Components'): + if item.get('Components/Internal Reference'): + product_tmpl = self.env['product.product'].search([( + 'default_code', + '=', + item.get( + 'Components/Internal Reference'))]) + elif item.get('Components/Barcode'): + product_tmpl = self.env['product.product'].search( + [('barcode', '=', item.get('Components/Barcode'))]) + else: + product_tmpl = self.env['product.product'].search( + [('name', '=', item.get('Components'))]) + components['product_id'] = product_tmpl.id + if not product_tmpl: + product_template = ( + self.env['product.product'].create({ + 'name': item.get('Components'), + 'default_code': item.get( + 'Components/Internal Reference'), + 'barcode': item.get('Components/Barcode') + })) + warning_msg += ("\n◼ A Component Product is created with " + "\"Internal Reference\" as " + "product name since \"Product\"" + " name is missing in file." + "(row %d)" % row) + components['product_id'] = product_template.id + if item.get('BoM Lines/Quantity'): + components['product_qty'] = item.get( + 'BoM Lines/Quantity') + vals['bom_line_ids'] = [(0, 0, components)] + else: + raise ValidationError( + "File not contain any BoM Lines/Components." + "\n\nPlease check the file.") + if item.get('Product'): + bom_id = self.env['mrp.bom'].search([('product_tmpl_id', '=', vals['product_tmpl_id'])]) + if bom_id and self.bom_component == 'add': + bom_id.write({ + 'bom_line_ids': [(0, 0, components)] + }) + updated += 1 + else: + self.env['mrp.bom'].create(vals) + imported += 1 + if error_msg: + error_msg = "\n\n🏮 ERROR 🏮" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + msg = (("Imported %d records.\nUpdated %d records" + % (imported, updated)) + + warning_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_bill_of_material_views.xml b/import_dashboard/wizard/import_bill_of_material_views.xml new file mode 100644 index 000000000..1df6ba09d --- /dev/null +++ b/import_dashboard/wizard/import_bill_of_material_views.xml @@ -0,0 +1,28 @@ + + + + + import.bill.of.material.view.form + import.bill.of.material + +
+ + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_invoice.py b/import_dashboard/wizard/import_invoice.py new file mode 100644 index 000000000..9e7a1bcd7 --- /dev/null +++ b/import_dashboard/wizard/import_invoice.py @@ -0,0 +1,427 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import datetime +import io +import re +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models + + +class ImportInvoice(models.TransientModel): + """ Model for import invoice """ + _name = 'import.invoice' + _description = 'Invoice Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xlsx', 'XLSX File')], + string='Import File Type', default='csv', + help="It helps to choose the file type") + file = fields.Binary(string="File", help="File") + update_posted = fields.Boolean( + string='Update Posted Record?', + help='If enabled, the records in "Posted" state will converted to draft' + ' and values are updated. These records will then again be posted' + ' if "Post Automatically" is activated') + auto_post = fields.Boolean(string='Post Automatically', + help="Post Automatically") + journal = fields.Selection( + selection=[('Bank', 'Bank'), ('Cash', 'Cash')], string='Journal', + default='Bank', help='It helps to choose Journal type') + order_number = fields.Selection( + selection=[('from_system', 'From System'), ('from_file', 'From File')], + string='Number', default='from_file', help="Order number") + import_product_by = fields.Selection( + selection=[('name', 'Name'), ('default_code', 'Internal Reference'), + ('barcode', 'Barcode')], required=True, default="name", + string="Import invoice by", help="Product import") + type = fields.Selection( + selection=[('out_invoice', 'Invoice'), ('in_invoice', 'Bill'), + ('out_refund', 'Credit Note'), ('in_refund', 'Refund')], + string='Invoicing Type', required=True, help="Invoice type", + default="out_invoice") + + def action_import_invoice(self): + """Creating Invoice record using uploaded xl/csv files""" + account_move = self.env['account.move'] + res_partner = self.env['res.partner'] + res_users = self.env['res.users'] + account_account = self.env['account.account'] + uom_uom = self.env['uom.uom'] + account_tax = self.env['account.tax'] + product_product = self.env['product.product'] + product_attribute = self.env['product.attribute'] + product_attribute_value = self.env['product.attribute.value'] + product_template_attribute_value = self.env[ + 'product.template.attribute.value'] + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + imported = 0 + confirmed = 0 + imported_invoices = [] + error_msg = "" + partner_added_msg = "" + warning_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\n❌Row {rn} not imported.".format(rn=row) + import_error_msg = "" + missing_fields_msg = "" + fields_msg = "\n\t🚫Missing required field(s):" + partner_msg = "\n🆕New Partner(s) added:" + vals['move_type'] = self.type + if item.get('Partner'): + partner = res_partner.search( + [('name', '=', item['Partner'])]) + if not partner: + partner = res_partner.create({ + 'name': item['Partner'] + }) + vals['partner_id'] = partner.id + if partner_added_msg: + partner_added_msg += ( + "\n\t\trow {rn}: {partner}").format( + rn=row, partner=item['Partner']) + else: + partner_added_msg += ( + partner_msg + "\n\t\trow {rn}: " + "\"{partner}\"").format( + rn=row, partner=item['Partner']) + elif len(partner) > 1: + if import_error_msg: + import_error_msg += ( + "\n\t\t⚠ Multiple Partners with " + "name (%s) found!" + % item['Partner']) + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t⚠ Multiple Partners with name (%s) found!" + % item['Partner']) + else: + vals['partner_id'] = partner.id + else: + if missing_fields_msg: + missing_fields_msg += "\n\t\t❗ \"Partner\"" + else: + missing_fields_msg += (fields_msg + + "\n\t\t❗ \"Partner\"") + if import_error_msg: + import_error_msg += missing_fields_msg + elif missing_fields_msg: + import_error_msg += (row_not_import_msg + + missing_fields_msg) + if item.get('Payment Reference'): + vals['payment_reference'] = item['Payment Reference'] + if item.get('Invoice Date'): + date = item['Invoice Date'] + try: + invoice_date = datetime.datetime.strptime(date, + '%m/%d/%Y') + vals['invoice_date'] = invoice_date + except: + if import_error_msg: + import_error_msg += ("\n\t\t⚠ Please check the date" + " format of Invoice Date is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t⚠ Please check the date format of " + "Invoice Date is mm/dd/yyyy") + if item.get('Due Date'): + date = item['Due Date'] + try: + due_date = datetime.datetime.strptime(date, '%m/%d/%Y') + vals['invoice_date_due'] = due_date + except: + if import_error_msg: + import_error_msg += ("\n\t\t⚠ Please check the date" + " format of Due Date is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t⚠ Please check the date format of Due " + "Date is mm/dd/yyyy") + if item.get('Salesperson'): + sales_person = res_users.search([('name', '=', + item['Salesperson'])]) + if sales_person: + vals['invoice_user_id'] = sales_person.id + if import_error_msg: + error_msg += import_error_msg + continue + invoice = account_move.search( + [('name', '=', item.get('Number')), + ('move_type', '=', vals['move_type'])]) + if invoice: + if len(invoice) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple invoice with same Number(%s) " + "found!" + % item['Number']) + continue + if vals: + if self.update_posted and invoice.state == 'posted': + invoice.button_draft() + invoice.write(vals) + elif invoice.state == 'draft': + invoice.write(vals) + elif not invoice: + if self.order_number == 'from_system': + invoice = account_move.create(vals) + if self.order_number == 'from_file': + if item.get('Number'): + vals['name'] = item['Number'] + invoice = account_move.create(vals) + else: + error_msg += (row_not_import_msg + + fields_msg + + "\n\t\t\"Number\"") + continue + line_vals = {} + pro_vals = {} + if item.get('Label'): + line_vals['name'] = item['Label'] + else: + if not item.get('Product'): + import_error_msg += row_not_import_msg + ( + "\n\t⚠ Product and Label missing in " + "file!") + continue + if item.get('Account Code'): + account = account_account.search( + [('code', '=', int(item['Account Code']))]) + line_vals['account_id'] = account.id + if item.get('Quantity'): + line_vals['quantity'] = item['Quantity'] + if item.get('Uom'): + uom = uom_uom.search([('name', '=', item['Uom'])]) + if uom: + pro_vals['uom_id'] = line_vals[ + 'product_uom_id'] = uom.id + if item.get('Price'): + pro_vals['lst_price'] = line_vals['price_unit'] = item[ + 'Price'] + if item.get('Disc.%'): + line_vals['discount'] = item['Disc.%'] + if item.get('Taxes'): + tax_name = item['Taxes'] + tax_amount = (re.findall(r"(\d+)%", tax_name))[0] + tax = account_tax.search([('name', '=', tax_name), + ('type_tax_use', '=', 'sale')], + limit=1) + if not tax: + tax = account_tax.create({ + 'name': tax_name, + 'type_tax_use': 'sale', + 'amount': tax_amount if tax_amount else 0.0 + }) + pro_vals['taxes_id'] = line_vals['tax_ids'] = [tax.id] + if item.get('Product'): + pro_vals['name'] = item['Product'] + if item.get('Internal Reference'): + pro_vals['default_code'] = item['Internal Reference'] + if self.import_product_by == 'name': + if item.get('Product'): + product = product_product.search( + [('name', '=', item['Product'])]) + if not product: + product = product_product.create(pro_vals) + if len(product) > 1: + if item.get('Variant Values'): + pro_tmpl_id = product.mapped('product_tmpl_id') + if len(pro_tmpl_id) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Product records are" + "linked with the product variant " + "\"%s\"" + "." % item['Product']) + continue + variant_values = item['Variant Values'].split( + ',') + variant_value_ids = [] + for var in variant_values: + k_v = var.partition(":") + attr = k_v[0].strip() + attr_val = k_v[2].strip() + var_attr_ids = product_attribute.search( + [('name', '=', attr)]).ids + var_attr_val_ids = (product_attribute_value. + search( + [('name', '=', attr_val), + ('attribute_id', 'in', + var_attr_ids)]).ids) + pro_temp_attr_val_id = ( + product_template_attribute_value.search( + [( + 'product_attribute_value_id', 'in', + var_attr_val_ids), + ('product_tmpl_id', '=', + pro_tmpl_id.id)]).id) + variant_value_ids += [pro_temp_attr_val_id] + if variant_value_ids: + product = product.filtered( + lambda p: + p.product_template_variant_value_ids.ids + == variant_value_ids) + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Product variant with variant" + "values \"%s\" not found." + % (item['Variant Values'])) + continue + if len(product) != 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple variants with same " + "Variant Values \"%s\" found. " + "Please" + "check if the product Variant " + "Values" + " are unique." + % (item['Variant Values'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Products with same " + "Name \"%s\" found. Please " + "provide unique product \"Variant " + "Values\" to filter the records." + % (item['Product'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Product name missing in file!") + continue + if self.import_product_by == 'default_code': + if item.get('Internal Reference'): + product = product_product.search( + [('default_code', '=', item['Internal Reference'])]) + if not product: + if not item.get('Product'): + warning_msg += ("\n◼ A Product is created with " + "\"Internal Reference\" as " + "product name since \"Product\"" + " name is missing in file." + "(row %d)" % row) + pro_vals['name'] = item['Internal Reference'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Products with same Internal" + " Reference(%s) found!" + % item['Internal Reference']) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Internal Reference missing in file!") + continue + # Product_by barcode + if self.import_product_by == 'barcode': + if item.get('Barcode'): + product = product_product.search( + [('barcode', '=', item['Barcode'])]) + if not product: + if not item.get('Product'): + warning_msg += ( + "\n◼ No value under \"Product\" at " + "row %d, thus added \"Barcode\" as " + "product name" % row) + pro_vals['name'] = item['Barcode'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Other Product(s) with same " + "Barcode (%s) found!" % item['Barcode']) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Barcode missing in file!") + continue + if self.import_product_by and product: + line_vals['product_id'] = product.id + invoice.write({ + 'invoice_line_ids': [(0, 0, line_vals)] + }) + imported += 1 + imported_invoices += [invoice] + if self.auto_post and imported_invoices: + for inv in imported_invoices: + inv.action_post() + confirmed += 1 + if error_msg: + error_msg = "\n\n🏮 WARNING 🏮" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + msg = (("Imported %d records.\nPosted %d records" + % (imported, confirmed)) + partner_added_msg + + warning_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_invoice_views.xml b/import_dashboard/wizard/import_invoice_views.xml new file mode 100644 index 000000000..3f5627c37 --- /dev/null +++ b/import_dashboard/wizard/import_invoice_views.xml @@ -0,0 +1,30 @@ + + + + + import.invoice.view.form + import.invoice + +
+ + + + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_message.py b/import_dashboard/wizard/import_message.py new file mode 100644 index 000000000..9438e08a7 --- /dev/null +++ b/import_dashboard/wizard/import_message.py @@ -0,0 +1,34 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +from odoo import fields, models + + +class ImportMessage(models.TransientModel): + """ Model for shows message while importing models """ + _name = 'import.message' + _description = 'Message Import' + + message = fields.Text(string="Message", readonly=True, help="Message") + + def action_import_message(self): + """For returning the corresponding window""" + return {'type': 'ir.actions.act_window_close'} diff --git a/import_dashboard/wizard/import_message_views.xml b/import_dashboard/wizard/import_message_views.xml new file mode 100644 index 000000000..1395df451 --- /dev/null +++ b/import_dashboard/wizard/import_message_views.xml @@ -0,0 +1,19 @@ + + + + + import.message.view.form + import.message + +
+

+ +

+
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_partner.py b/import_dashboard/wizard/import_partner.py new file mode 100644 index 000000000..64fc215be --- /dev/null +++ b/import_dashboard/wizard/import_partner.py @@ -0,0 +1,341 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo import fields, models +from odoo.exceptions import ValidationError + + +class ImportPartner(models.TransientModel): + """ Model for import partners """ + _name = 'import.partner' + _description = 'Partner Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xlsx', 'XLSX File')], + string='Import File Type', default='xlsx', help="File type") + method = fields.Selection( + selection=[('create_update', 'Create or Update Customer/Vendor'), + ('create', 'Create Customer/Vendor')], + string='Import Method', default='create_update', + help="Helps to choose the import Method") + update_by = fields.Selection(selection=[('name', 'Name'), + ('email', 'Email'), + ('phone', 'Phone'), + ('mobile', 'Mobile')], + string='Update By', default='name', + help="Update using the fields") + is_customer = fields.Boolean(string='Is Customer', help="Is Customer") + is_vendor = fields.Boolean(string='Is Vendor', help="Is Vendor") + file_upload = fields.Binary(string="Upload File", + help="It helps to upload files") + + def action_import_partner(self): + """Creating Partner record using uploaded xl/csv files""" + res_partner = self.env['res.partner'] + res_users = self.env['res.users'] + res_country_state = self.env['res.country.state'] + res_country = self.env['res.country'] + res_partner_title = self.env['res.partner.title'] + res_partner_category = self.env['res.partner.category'] + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + headers = sheet.row_values(0) # list + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) # list + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + created = 0 + updated = 0 + error_msg = "" + warning_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\nRow {rn} not imported.".format(rn=row) + if item.get('Is company? (y/n)') in ['y', 'Y']: + vals['company_type'] = 'company' + else: + vals['company_type'] = 'person' + if item.get('Related Company'): + rel_company = res_partner.search( + [('name', '=', item['Related Company'])]) + vals['parent_id'] = rel_company.id + if item.get('Job Position'): + vals['function'] = item['Job Position'] + if item.get('Title'): + title = res_partner_title.search( + [('name', '=', item['Title'])]) + if not title: + title = res_partner_title.create({ + 'name': item['Title'] + }) + vals['title'] = title.id + if item.get('Name'): + vals['name'] = item['Name'] + if item.get('Street'): + vals['street'] = item['Street'] + if item.get('Street2'): + vals['street2'] = item['Street2'] + if item.get('City'): + vals['city'] = item['City'] + state_vals = {} + if item.get('Country'): + country = res_country.search( + [('name', '=', item['Country'])]) + vals['country_id'] = state_vals['country_id'] = country.id + if item.get('State'): + country_state = res_country_state.search( + [('name', '=', item['State'])]) + if not country_state: + state_vals['name'] = state_vals['code'] = item['State'] + country_state = res_country_state.create(state_vals) + vals['state_id'] = country_state.id + if item.get('Zip'): + vals['zip'] = item['Zip'] + if item.get('Tax ID'): + vals['vat'] = item['Tax ID'] + if item.get('Phone'): + vals['phone'] = item['Phone'] + if item.get('Mobile'): + vals['mobile'] = item['Mobile'] + if item.get('Email'): + vals['email'] = item['Email'] + if item.get('Website'): + vals['website'] = item['Website'] + if item.get('Tags'): + tags = item['Tags'].split(',') + tag_list = [] + for tag in tags: + tag_list += [tag.strip()] + tag_ids = res_partner_category.search( + [('name', 'in', tag_list)]).ids + if not tag_ids: + tag_ids = [] + for tag in tag_list: + tag_id = res_partner_category.create({ + 'name': tag + }) + tag_ids += [tag_id.id] + if tag_ids: + vals['category_id'] = tag_ids + if item.get('Salesperson'): + salesperson = res_users.search( + [('name', '=', item['Salesperson'])]) + if not salesperson: + warning_msg += ("\nSalesperson (%s) not found!(row %d)" + % (item['Salesperson'], row)) + elif len(salesperson) > 1: + warning_msg += ("\nSalesperson not copied from row %d: " + "Multiple Salespersons with name (%s) " + "found!" + % (row, item['Salesperson'])) + else: + vals['user_id'] = salesperson.id + if self.method == 'create_update': + if self.update_by == 'name': + if item.get('Name'): + partner = res_partner.search([('name', '=', + item['Name'])]) + if not partner: + res_partner.create(vals) + created += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with name " + "(%s) found!" % item['Name']) + continue + else: + partner.write(vals) + updated += 1 + else: + error_msg += row_not_import_msg + ( + "\n\tName missing!") + continue + if self.update_by == 'email': + if item.get('Email'): + partner = res_partner.search([('email', '=', + item['Email'])]) + if not partner: + if not vals.get('name'): + error_msg += row_not_import_msg + ( + "\n\tName missing!") + continue + else: + partner = res_partner.search( + [('name', '=', vals['name'])]) + if not partner: + res_partner.create(vals) + created += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\t Multiple Partners with " + "name (%s) found!" + % item['Name']) + continue + else: + partner.write(vals) + updated += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with Email " + "(%s) found!" % item['Email']) + continue + else: + partner.write(vals) + updated += 1 + else: + error_msg += row_not_import_msg + ( + "\n\tEmail missing!") + continue + if self.update_by == 'phone': + if item.get('Phone'): + partner = res_partner.search([('phone', '=', + item['Phone'])]) + if not partner: + if not vals.get('name'): + error_msg += row_not_import_msg + ( + "\n\tName missing!") + continue + else: + partner = res_partner.search( + [('name', '=', vals['name'])]) + if not partner: + res_partner.create(vals) + created += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with " + "name (%s) found!" + % item['Name']) + continue + else: + partner.write(vals) + updated += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with Phone " + "(%s) found!" % item['Phone']) + continue + else: + partner.write(vals) + updated += 1 + else: + error_msg += row_not_import_msg + ( + "\n\tPhone missing!") + continue + if self.update_by == 'mobile': + if item.get('Mobile'): + partner = res_partner.search([('mobile', '=', + item['Mobile'])]) + if not partner: + if not vals.get('name'): + error_msg += row_not_import_msg + ( + "\n\tName missing!") + continue + else: + partner = res_partner.search( + [('name', '=', vals['name'])]) + if not partner: + res_partner.create(vals) + created += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with " + "name (%s) found!" + % item['Name']) + continue + else: + partner.write(vals) + updated += 1 + elif len(partner) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Partners with Mobile " + "(%s) found!" % item['Mobile']) + continue + else: + partner.write(vals) + updated += 1 + else: + error_msg += row_not_import_msg + ( + "\n\tMobile missing!") + continue + elif self.method == 'create': + if vals.get('name'): + res_partner.create(vals) + created += 1 + else: + error_msg += row_not_import_msg + ( + "\n\tName missing!") + continue + if error_msg: + error_msg = "\n\n⚠ Warning ⚠" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + msg = (("Created %d records.\nUpdated %d records" + % (created, updated)) + warning_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_partner_views.xml b/import_dashboard/wizard/import_partner_views.xml new file mode 100644 index 000000000..bff5f2311 --- /dev/null +++ b/import_dashboard/wizard/import_partner_views.xml @@ -0,0 +1,30 @@ + + + + + import.partner.view.form + import.partner + +
+ + + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_payment.py b/import_dashboard/wizard/import_payment.py new file mode 100644 index 000000000..76b1c6f20 --- /dev/null +++ b/import_dashboard/wizard/import_payment.py @@ -0,0 +1,148 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models, _ + + +class ImportPayment(models.TransientModel): + """ Model for import payment """ + _name = 'import.payment' + _description = 'Payment Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xlsx', 'XLS File')], + string='Select File Type', default='xlsx', + help='It helps to choose the file type') + file_upload = fields.Binary(string='File Upload', + help='It helps to upload files') + + def action_import_payment(self): + """ Method to import payments from .csv or .xlsx files. """ + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file, and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + """File not Valid.\n\nPlease check the """ + """type and format of the file and try again!""") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + imported = 0 + updated = 0 + error_msg = "" + info_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\nRow {rn} not imported.".format(rn=row) + if item.get('Amount'): + vals['amount'] = item['Amount'] + if item.get('Date'): + vals['date'] = item['Date'] + if item.get('Journal'): + journal = self.env['account.journal'].search( + [('name', '=', item.get('Journal'))]) + if journal: + vals['journal_id'] = journal.id + if item.get('Customer/Vendor'): + partner = self.env['res.partner'].search( + [('name', '=', item.get('Customer/Vendor'))]) + if not partner: + partner = self.env['res.partner'].create({ + 'name': item.get('Customer/Vendor') + }) + info_msg += f"\nCreated new partner with name :{item.get('Customer/Vendor')}" + vals['partner_id'] = partner.id + if item.get('Reference'): + vals['ref'] = item.get('Reference') + if item.get('Payment Type') == 'Send': + vals['payment_type'] = 'outbound' + elif item.get('Payment Type') == 'Receive': + vals['payment_type'] = 'inbound' + else: + error_msg += row_not_import_msg + ( + "\n\tPayment type Values is wrong in file!") + continue + if item.get('Number'): + payment_ref = self.env['account.payment'].search( + [('name', '=', item.get('Number'))]) + if payment_ref: + payment_ref.write(vals) + updated += 1 + else: + vals['name'] = item.get('Number') + self.env['account.payment'].create(vals) + imported += 1 + if error_msg: + error_msg = "\n\n⚠⚠⚠ERROR!!!⚠⚠⚠" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + if info_msg: + info_msg = f"\nInformation : {info_msg}" + msg = (("Imported %d records. Updated %d records" + % (imported, updated)) + info_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_payment_views.xml b/import_dashboard/wizard/import_payment_views.xml new file mode 100644 index 000000000..60cbfca9d --- /dev/null +++ b/import_dashboard/wizard/import_payment_views.xml @@ -0,0 +1,23 @@ + + + + + import.payment.view.form + import.payment + +
+ + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_pos_order.py b/import_dashboard/wizard/import_pos_order.py new file mode 100644 index 000000000..a4fef73d7 --- /dev/null +++ b/import_dashboard/wizard/import_pos_order.py @@ -0,0 +1,167 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import datetime +import io +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models, _ + + +class ImportPosOrder(models.TransientModel): + """ Model for import POS Orders """ + _name = 'import.pos.order' + _description = 'Pos Orders Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xls', 'XLS File')], + string='Select File Type', default='xls', + help='It helps to choose the file type') + file_upload = fields.Binary(string='File Upload', + help="It helps to upload file") + import_product_by = fields.Selection( + selection=[('name', 'Name'), ('default_code', 'Internal Reference'), + ('barcode', 'Barcode')], string="Import order by", + help="Import product", default="name") + + def action_import_pos_order(self): + """Creating POS Order record using uploaded xl/csv files""" + datas = {} + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + datas = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + """File not Valid.\n\nPlease check the """ + """type and format of the file and try again!""") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + datas = data + pos_order = None + for item in datas: + vals = {} + order_ref = None + if item.get('Order Ref'): + order_ref = self.env['pos.order'].search( + [('name', '=', item.get('Order Ref'))]) + if order_ref: + error_msg = ('POS order with order reference :` %s ` is already exists.' % item.get('Order Ref')) + error_message = self.env['import.message'].create({'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + else: + vals['pricelist_id'] = (self.env.user.partner_id. + property_product_pricelist.id) + vals['company_id'] = self.env.user.company_id.id + vals['name'] = item.get('Order Ref') + vals['amount_tax'] = item.get('Tax Amount') if item.get( + 'Tax Amount') else 0.0 + vals['amount_total'] = item.get('Total') if item.get( + 'Total') else 0.0 + vals['amount_paid'] = item.get('Paid Amount') if item.get( + 'Paid Amount') else 0.0 + vals['amount_return'] = item.get( + 'Amount Returned') if item.get( + 'Amount Returned') else 0.0 + if item.get('Responsible'): + vals['user_id'] = self.env['res.users'].search( + [('name', '=', item.get('Responsible'))]).id + else: + continue + vals['session_id'] = self.env['pos.session'].search( + [('name', '=', item.get('Session'))]).id + if not vals['session_id']: + vals['session_id'] = self.env['pos.session'].create({ + 'name': item.get('Session'), + 'user_id': vals['user_id'], + 'config_id': 1 + }).id + if item.get('Receipt Number'): + vals['pos_reference'] = item.get('Receipt Number') + if item.get('Order Date'): + if self.file_type == 'csv': + vals['date_order'] = item.get('Order Date') + else: + vals[ + 'date_order'] = datetime.datetime.fromtimestamp( + item.get('Order Date')).strftime( + '%Y-%m-%d %H:%M:%S') + if item.get('Customer'): + partner_id = self.env['res.partner'].search( + [('name', '=', item.get('Customer'))]) + if not partner_id: + partner_id = self.env['res.partner'].create({ + 'name': item.get('Customer') + }) + vals['partner_id'] = partner_id.id + lines = {} + if item.get('Product'): + lines['product_id'] = self.env[ + 'product.product'].search( + [('name', '=', item.get('Product'))]).id + lines['full_product_name'] = item.get('Product') + lines['qty'] = item.get('Quantity') + lines['price_unit'] = item.get('Unit Price') + lines['discount'] = item.get('Discount %') + lines['price_subtotal'] = item.get('Sub Total') + lines['price_subtotal_incl'] = 0.0 + vals['lines'] = [(0, 0, lines)] + if item.get('Session'): + pos_order = self.env['pos.order'].create(vals) + if not item.get('Session'): + if item.get('Product'): + lines['order_id'] = pos_order.id + self.env['pos.order.line'].create(lines) + return { + 'effect': { + 'fadeout': 'slow', + 'message': 'Imported Successfully', + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_pos_order_views.xml b/import_dashboard/wizard/import_pos_order_views.xml new file mode 100644 index 000000000..153910dc7 --- /dev/null +++ b/import_dashboard/wizard/import_pos_order_views.xml @@ -0,0 +1,26 @@ + + + + + import.pos.order.view.form + import.pos.order + +
+ + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_product_pricelist.py b/import_dashboard/wizard/import_product_pricelist.py new file mode 100644 index 000000000..b26a4e97a --- /dev/null +++ b/import_dashboard/wizard/import_product_pricelist.py @@ -0,0 +1,487 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models + + +class ImportProductPricelist(models.TransientModel): + """ Model for import product pricelist """ + _name = 'import.product.pricelist' + _description = 'Product Pricelist Import' + + file_type = fields.Selection(selection=[('csv', 'CSV File'), ('xlsx', + 'XLSX File')], + string='Import File Type', default='csv', + help="file type") + import_product_by = fields.Selection(selection=[ + ('name', 'Name'), ('default_code', 'Internal Reference'), + ('barcode', 'Barcode')], required=True, string="Import price list by", + help="Import product") + product_pricelist_setting = fields.Selection( + selection=[('basic', 'Multiple prices per product'), + ('advanced', 'Advanced price rules (discounts, formulas)')], + string='Pricelist Method', default='basic', + help="pricelist method type") + compute_price = fields.Selection( + selection=[('fixed', 'Fixed Price'), + ('percentage', 'Discount'), + ('formula', 'Formula')], + string='Computation', default='fixed', help="Computation type") + applied_on = fields.Selection( + selection=[('3_global', 'All Products'), + ('2_product_category', 'Product Category'), + ('1_product', 'Product'), + ('0_product_variant', 'Product Variant')], + string='Apply On', help="Appply on specific category", + default='3_global') + base = fields.Selection(selection=[ + ('list_price', 'Sales Price'), + ('standard_price', 'Cost'), + ('pricelist', 'Other Pricelist')], string="Based on", help="Base on", + default='list_price', required=True) + country_group_ids = fields.Many2many(comodel_name='res.country.group', + string='Country Groups', + help="country groups") + company_id = fields.Many2one(comodel_name='res.company', string='Company', + help="company") + file_upload = fields.Binary(string='File Upload', + help="It helps to upload file") + + def action_import_product_pricelist(self): + """Creating pricelist record using uploaded xl/csv files""" + ir_config_parameter = self.env['ir.config_parameter'] + product_template = self.env['product.template'] + product_product = self.env['product.product'] + product_pricelist = self.env['product.pricelist'] + product_category = self.env['product.category'] + product_attribute = self.env['product.attribute'] + product_attribute_value = self.env['product.attribute.value'] + product_template_attribute_value = self.env[ + 'product.template.attribute.value'] + if self.product_pricelist_setting == 'basic': + ir_config_parameter.set_param('product.product_pricelist_setting', + 'basic') + elif self.product_pricelist_setting == 'advanced': + ir_config_parameter.set_param('product.product_pricelist_setting', + 'advanced') + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file, and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + """File not Valid.\n\nPlease check the """ + """type and format of the file and try again!""") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + imported = 0 + created = 0 + error_msg = "" + info_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\nRow {rn} not imported.".format(rn=row) + import_error_msg = "" + missing_fields_msg = "" + fields_msg = "\n\tMissing required field(s):" + if item.get('Name'): + vals['name'] = item['Name'] + else: + if missing_fields_msg: + missing_fields_msg += "\n\t\t\"Name\" " + else: + missing_fields_msg += ( + row_not_import_msg + fields_msg + + "\n\t\t\"Name\"") + if self.company_id: + vals['company_id'] = self.company_id.id + if self.country_group_ids: + vals['country_group_ids'] = self.country_group_ids.ids + import_error_msg += missing_fields_msg + if import_error_msg: + error_msg += import_error_msg + continue + price_list = product_pricelist.search([('name', '=', + item['Name'])]) + if not price_list: + price_list = product_pricelist.create(vals) + created += 1 + elif len(price_list) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Pricelist with " + "name \"%s\" exists." + % item['Name']) + continue + else: + if vals.get('company_id'): + price_list.company_id = vals['company_id'] + info_msg += ("\n\tCompany value updated from row %d" + % row) + if vals.get('country_groups_ids'): + price_list.country_groups_ids = vals[ + 'country_groups_ids'] + info_msg += ("\n\tCountry Groups updated from row %d" + % row) + vals_list = {} + + def variant_search(var_vals, pro_tmpl): + """returns product_product record matching the + variant values""" + if not (var_vals and pro_tmpl): + return False + variant_values = var_vals.split(',') + variant_value_ids = [] + for var in variant_values: + k_v = var.partition(":") + attr = k_v[0].strip() + attr_val = k_v[2].strip() + var_attr_ids = product_attribute.search( + [('name', '=', attr)]).ids + var_attr_val_ids = product_attribute_value.search( + [('name', '=', attr_val), + ('attribute_id', 'in', var_attr_ids)]).ids + pro_temp_attr_val_id = ( + product_template_attribute_value.search([ + ('product_attribute_value_id', 'in', + var_attr_val_ids), ('product_tmpl_id', '=', + pro_tmpl.id)]).id) + variant_value_ids += [pro_temp_attr_val_id] + if variant_value_ids: + product_var = product_product.search( + [('product_template_variant_value_ids', '=', + variant_value_ids)]) + return product_var + + # Multiple prices per product + if self.product_pricelist_setting == 'basic': + if self.import_product_by == 'name': + if item.get('Product'): + pro_tmpl = product_template.search( + [('name', '=', item['Product'])]) + if not pro_tmpl: + pro_tmpl = product_template.create({ + 'name': (item['Product']).title() + }) + info_msg += ("\n\tNew Product (%s) created!" + "(row: %d)" + % ((item['Product']).title(), + row)) + elif len(pro_tmpl) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records with " + "name \"%s\" exists.(row: %d)" + % (item['Product'], row)) + continue + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tProduct name missing in file!") + continue + if self.import_product_by == 'default_code': + if item.get('Internal Reference'): + pro_pro = product_product.search( + [('default_code', '=', item[ + 'Internal Reference'])]) + if not pro_pro: + error_msg += row_not_import_msg + ( + "\n\tProduct with Internal Reference %s" + " not found!" + % item['Internal Reference']) + continue + elif len(pro_pro) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Products with " + "Internal Reference \"%s\" exists." + % item['Internal Reference']) + continue + pro_tmpl = pro_pro.product_tmpl_id + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tInternal Reference missing in file!") + continue + if self.import_product_by == 'barcode': + if item.get('Barcode'): + pro_pro = product_product.search( + [('barcode', '=', item['Barcode'])]) + if not pro_pro: + error_msg += row_not_import_msg + ( + "\n\tProduct with barcode %s not found!" + % item['Barcode']) + continue + elif len(pro_pro) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records with " + "same Barcode \"%s\" exists." + % item['Barcode']) + continue + pro_tmpl = pro_pro.product_tmpl_id + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tBarcode missing in file!") + continue + if item.get('Variant Values') and pro_tmpl: + variant = variant_search(item['Variant Values'], + pro_tmpl) + if variant: + vals_list['product_id'] = variant.id + if item.get('Fixed Price'): + vals_list['fixed_price'] = item['Fixed Price'] + # Advanced price rules + if self.product_pricelist_setting == 'advanced': + vals_list['compute_price'] = self.compute_price + vals_list['base'] = self.base + vals_list['applied_on'] = self.applied_on + + def parent_category(category): + """return the parent category""" + if category: + parent_categ = category.rpartition('/')[0] + if parent_categ: + parent = product_category.search( + [('complete_name', '=', parent_categ)]) + if parent: + p_id = parent.id + else: + grand_parent_id = parent_category( + parent_categ) + parent = product_category.create({ + 'name': parent_categ.rpartition('/')[ + 2], + 'parent_id': grand_parent_id + }) + p_id = parent.id + return p_id + + # Price computation + if self.compute_price == 'fixed': + if item.get('Fixed Price'): + vals_list['fixed_price'] = item['Fixed Price'] + elif self.compute_price == 'percentage': + if item.get('Discount%'): + vals_list['percent_price'] = item['Discount'] + elif self.compute_price == 'formula': + if item.get('Discount%'): + vals_list['price_discount'] = item['Discount%'] + if item.get('Extra Fee'): + vals_list['price_surcharge'] = item['Extra Fee'] + if item.get('Rounding Method'): + vals_list['price_round'] = item['Rounding Method'] + if item.get('Min. Margin'): + vals_list['price_min_margin'] = item['Min. Margin'] + if item.get('Max. Margin'): + vals_list['price_max_margin'] = item['Max. Margin'] + if self.base == 'pricelist': + if item.get('Other Pricelist'): + other_pricelist = product_pricelist.search( + [('name', '=', item['Other Pricelist'])], + limit=1) + if other_pricelist: + vals_list['base_pricelist_id'] = ( + other_pricelist.id) + else: + error_msg += row_not_import_msg + ( + "\n\t\"Other Pricelist\" missing in file!") + continue + if self.applied_on == '2_product_category': + if item.get('Product Category'): + item_category = (item['Product Category']).replace( + " ", "").replace("/", " / ").title() + item_categ_name = item_category.rpartition('/')[2] + categ = product_category.search( + [('complete_name', '=', item_category)], + limit=1) + if not categ: + categ = product_category.create({ + 'name': item_categ_name, + 'parent_id': parent_category(item_category) + }) + vals_list['categ_id'] = categ.id + else: + error_msg += row_not_import_msg + ( + "\n\tProduct Category missing in file!") + continue + if self.applied_on == '1_product': + if self.import_product_by == 'name': + if item.get('Product'): + pro_tmpl = product_template.search( + [('name', '=', item['Product'])]) + if not pro_tmpl: + pro_tmpl = product_template.create({ + 'name': item['Product'] + }) + info_msg += ( + "\n\tNew Product (%s) created!" + "(row: %d)" + % (item['Product'], row)) + elif len(pro_tmpl) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records with " + "name \"%s\" exists.(row: %d)" + % (item['Product'], row)) + continue + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tProduct name missing in file!") + continue + if self.import_product_by == 'default_code': + if item.get('Internal Reference'): + pro_pro = product_product.search( + [('default_code', '=', + item['Internal Reference'])]) + if not pro_pro: + error_msg += row_not_import_msg + ( + "\n\tProduct with Internal " + "Reference %s not found!" + % item['Internal Reference']) + continue + if len(pro_pro) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records with " + "Internal Reference \"%s\" exists." + % item['Internal Reference']) + continue + pro_tmpl = pro_pro.product_tmpl_id + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tInternal Reference missing!") + continue + if self.import_product_by == 'barcode': + if item.get('Barcode'): + pro_pro = product_product.search( + [('barcode', '=', item['Barcode'])]) + if not pro_pro: + error_msg += row_not_import_msg + ( + "\n\tProduct with Barcode %s not " + "found!" + % item['Barcode']) + continue + if len(pro_pro) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records with " + "same Barcode \"%s\" exists." + % item['Barcode']) + continue + pro_tmpl = pro_pro.product_tmpl_id + vals_list['product_tmpl_id'] = pro_tmpl.id + else: + error_msg += row_not_import_msg + ( + "\n\tBarcode missing!") + continue + if self.applied_on == '0_product_variant': + if item.get('Product'): + product_variant = product_product.search( + [('name', '=', item['Product'])]) + if not product_variant: + error_msg += row_not_import_msg + ( + "\n\tProduct not found!") + continue + elif len(product_variant) > 1: + pro_tmpl_id = product_variant.mapped( + 'product_tmpl_id') + if len(pro_tmpl_id) > 1: + error_msg += row_not_import_msg + ( + "\n\tMultiple Product records are " + "linked with the product variant " + "\"%s\"" + ". (row: %d)" % ( + item['Product'], row)) + continue + if item.get('Variant Values'): + variant = variant_search( + item['Variant Values'], pro_tmpl_id) + if variant: + vals_list['product_id'] = variant.id + else: + error_msg += row_not_import_msg + ( + "\n\tVariant Values missing in " + "file!") + continue + else: + vals_list['product_id'] = product_variant.id + if item.get('Minimum Quantity'): + vals_list['min_quantity'] = item['Minimum Quantity'] + if item.get('Start_date'): + vals_list['date_start'] = item['Start_date'] + if item.get('End_date'): + vals_list['date_end'] = item['End_date'] + price_list.write({ + 'item_ids': [(0, 0, vals_list)] + }) + imported += 1 + if error_msg: + error_msg = "\n\n⚠⚠⚠Warning!!!⚠⚠⚠" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Done!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new', + } + if info_msg: + info_msg = "\n\nNotes:" + info_msg + msg = (("Imported %d records." + % imported) + info_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_product_pricelist_views.xml b/import_dashboard/wizard/import_product_pricelist_views.xml new file mode 100644 index 000000000..c55214fa7 --- /dev/null +++ b/import_dashboard/wizard/import_product_pricelist_views.xml @@ -0,0 +1,51 @@ + + + + + import.product.pricelist.view.form + import.product.pricelist + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_product_template.py b/import_dashboard/wizard/import_product_template.py new file mode 100644 index 000000000..4a6092827 --- /dev/null +++ b/import_dashboard/wizard/import_product_template.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models, _ + + +class ImportProductTemplate(models.TransientModel): + """ Model for import product template. """ + _name = 'import.product.template' + _description = 'Product Template Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xls', 'Excel File')], + string='Select File Type', default='csv', help="file type") + method = fields.Selection(selection=[('create_product', 'Create Product'), + ('update_product', + 'Create or Update Product')], + string='Method', default='create_product', + help="method") + import_product_by = fields.Selection( + selection=[('name', 'Name'), + ('internal_reference', 'Internal Reference'), + ], string="Product Update By", + default='name', help="It helps to import product") + file_upload = fields.Binary(string='File Upload', + help="It helps to upload file") + + def action_import_product_template(self): + """Creating product record using uploaded xl/csv files""" + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + items = csv_reader + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) + data += [{k: v for k, v in zip(headers, row)}] + items = data + updated = 0 + created = 0 + for item in items: + product = False + vals = { + "name": item.get('Name'), + "type": item.get('Product Type'), + "default_code": item.get('Internal Reference'), + "list_price": item.get('Sales Price'), + "standard_price": item.get('Cost') + } + if self.method == "create_product": + product = self.env['product.template'].search( + [('name', '=', item.get('Name'))]) + if self.method == "update_product": + # if method is update and if product exists then update the product + if self.import_product_by == "name": + product = self.env['product.template'].search( + [('name', '=', item.get('Name'))]) + elif self.import_product_by == "internal_reference": + product = self.env['product.template'].search( + [('default_code', '=', item.get('Internal Reference'))]) + if product: + self.env['product.template'].browse(product.id).write(vals) + updated += 1 + if not product: + self.env['product.template'].create(vals) + created += 1 + + msg = (("Imported %d records.\nUpdated %d records." + % (created, updated))) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_product_template_views.xml b/import_dashboard/wizard/import_product_template_views.xml new file mode 100644 index 000000000..1e98783f5 --- /dev/null +++ b/import_dashboard/wizard/import_product_template_views.xml @@ -0,0 +1,28 @@ + + + + + import.product.template.view.form + import.product.template + +
+ + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_purchase_order.py b/import_dashboard/wizard/import_purchase_order.py new file mode 100644 index 000000000..c2ddc5bda --- /dev/null +++ b/import_dashboard/wizard/import_purchase_order.py @@ -0,0 +1,407 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import datetime +import io +import re +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models + + +class ImportPurchaseOrder(models.TransientModel): + """ Model for import purchase orders. """ + _name = 'import.purchase.order' + _description = 'Purchase Order import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xlsx', 'XLSX File')], + string='Select File Type', default='csv', + help="File type to import") + file_upload = fields.Binary(string="File Upload", + help="Helps to upload your file") + auto_confirm_quot = fields.Boolean( + string='Confirm Quotation Automatically', + help="Automatically confirm the quotation") + order_number = fields.Selection( + selection=[('from_system', 'From System'), + ('from_file', 'From File')], + string='Reference', default='from_file', help="reference") + import_product_by = fields.Selection( + selection=[('name', 'Name'), ('default_code', 'Internal Reference'), + ('barcode', 'Barcode')], + default="name", string="Import order by", help="import product") + + def action_import_purchase_order(self): + """Creating purchase record using uploaded xl/csv files""" + purchase_order = self.env['purchase.order'] + res_partner = self.env['res.partner'] + res_users = self.env['res.users'] + product_product = self.env['product.product'] + product_attribute = self.env['product.attribute'] + product_attribute_value = self.env['product.attribute.value'] + product_template_attribute_value = self.env[ + 'product.template.attribute.value'] + account_tax = self.env['account.tax'] + uom_uom = self.env['uom.uom'] + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format of " + "the file and try again!") + headers = sheet.row_values(0) # list + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) # list + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + imported = 0 + confirmed = 0 + imported_purchaseorders = [] + error_msg = "" + vendor_added_msg = "" + warning_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\n❌Row {rn} not imported.".format(rn=row) + import_error_msg = "" + missing_fields_msg = "" + fields_msg = "\n\t🚫Missing required field(s):" + vendor_msg = "\n🆕New Vendor(s) added:" + if not item.get('Order Reference'): + if missing_fields_msg: + missing_fields_msg += "\n\t\t\t\"❗Order Reference\" " + else: + missing_fields_msg += (fields_msg + + "\n\t\t\t\"❗Order Reference\"") + if item.get('Vendor'): + vendor = res_partner.search([('name', '=', item['Vendor'])]) + if not vendor: + vendor = res_partner.create({ + 'name': item['Vendor'] + }) + vals['partner_id'] = vendor.id + if vendor_added_msg: + vendor_added_msg += ( + "\n\t\trow {rn}: {vendor}").format( + rn=row, vendor=item['Vendor']) + else: + vendor_added_msg += ( + vendor_msg + "\n\t\trow {rn}: " + "\"{vendor}\"").format( + rn=row, vendor=item['Vendor']) + elif len(vendor) > 1: + if import_error_msg: + import_error_msg += ("\n\t\t❎Multiple Partners with" + " name (%s) found!" + % item['Vendor']) + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t❎Multiple Partners with name (%s) " + "found!" + % item['Vendor']) + else: + vals['partner_id'] = vendor.id + else: + if missing_fields_msg: + missing_fields_msg += "\n\t\t\t\"❗Vendor\"" + else: + missing_fields_msg += (fields_msg + + "\n\t\t\t\"❗Vendor\"") + if import_error_msg: + import_error_msg += missing_fields_msg + elif missing_fields_msg: + import_error_msg += (row_not_import_msg + + missing_fields_msg) + if item.get('Vendor Reference'): + vals['partner_ref'] = item['Vendor Reference'] + if item.get('Order Deadline'): + date = item['Order Deadline'] + try: + order_deadline = datetime.datetime.strptime( + date, '%m/%d/%Y') + vals['date_order'] = order_deadline + except: + if import_error_msg: + import_error_msg += ("\n\t\t❎Please check the Order" + " Deadline and format is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t❎Please check the Order Deadline and " + "format is mm/dd/yyyy") + if item.get('Receipt Date'): + date = item['Receipt Date'] + try: + receipt_date = datetime.datetime.strptime( + date, '%m/%d/%Y') + vals['date_planned'] = receipt_date + except: + if import_error_msg: + import_error_msg += ("\n\t\t❎Please check the " + "Receipt Date and format is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t❎Please check the Receipt Date and " + "format is mm/dd/yyyy") + if item.get('Purchase Representative'): + purchase_representative = res_users.search( + [('name', '=', item['Purchase Representative'])]) + if purchase_representative: + vals['user_id'] = purchase_representative.id + if import_error_msg: + error_msg += import_error_msg + continue + purchaseorder = purchase_order.search( + [('name', '=', item.get('Order Reference'))]) + if purchaseorder: + if len(purchaseorder) > 1: + error_msg += row_not_import_msg + ( + "\n\t❎Multiple purchase order with same Order " + "Reference(%s) found!" + % (item.get('Order Reference'))) + continue + if vals and purchaseorder.state in ['draft', 'sent']: + purchaseorder.write(vals) + elif not purchaseorder: + if self.order_number == 'from_system': + purchaseorder = purchase_order.create(vals) + if self.order_number == 'from_file': + vals['name'] = item.get('Order Reference') + purchaseorder = purchase_order.create(vals) + line_vals = {} + pro_vals = {} + if item.get('Description'): + line_vals['name'] = item['Description'] + if item.get('Delivery Date'): + date = item['Delivery Date'] + try: + delivery_date = datetime.datetime.strptime( + date, '%m/%d/%Y') + line_vals['date_planned'] = delivery_date + except: + if import_error_msg: + import_error_msg += ("\n\t\t❎Please check the " + "Delivery Date and format is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t❎Please check the Delivery Date and " + "format is mm/dd/yyyy") + if item.get('Quantity'): + line_vals['product_qty'] = item['Quantity'] + if item.get('Uom'): + uom = uom_uom.search([('name', '=', item['Uom'])]) + if uom: + pro_vals['uom_id'] = line_vals['product_uom'] = uom.id + if item.get('Unit Price'): + pro_vals['lst_price'] = line_vals['price_unit'] = item[ + 'Unit Price'] + if item.get('Taxes'): + tax_name = item['Taxes'] + tax_amount = (re.findall(r"(\d+)%", tax_name))[0] + tax = account_tax.search( + [('name', '=', tax_name), + ('type_tax_use', '=', 'purchase')], limit=1) + if not tax: + tax = account_tax.create({ + 'name': tax_name, + 'type_tax_use': 'purchase', + 'amount': tax_amount if tax_amount else 0.0 + }) + pro_vals['taxes_id'] = line_vals['taxes_id'] = [tax.id] + if item.get('Product'): + pro_vals['name'] = item['Product'] + if item.get('Internal Reference'): + pro_vals['default_code'] = item['Internal Reference'] + if self.import_product_by == 'name': + if item.get('Product'): + product = product_product.search([('name', '=', + item['Product'])]) + if not product: + product = product_product.create(pro_vals) + if len(product) > 1: + if item.get('Variant Values'): + pro_tmpl_id = product.mapped('product_tmpl_id') + if len(pro_tmpl_id) > 1: + error_msg += row_not_import_msg + ( + "\n\t❎Multiple Product records are " + "linked with the product variant " + "\"%s\"" + "." % (item['Product'])) + continue + variant_values = item['Variant Values'].split( + ',') + variant_value_ids = [] + for var in variant_values: + k_v = var.partition(":") + attr = k_v[0].strip() + attr_val = k_v[2].strip() + var_attr_ids = product_attribute.search( + [('name', '=', attr)]).ids + var_attr_val_ids = (product_attribute_value + .search( + [('name', '=', attr_val), + ('attribute_id', 'in', + var_attr_ids)]).ids) + pro_temp_attr_val_id = ( + product_template_attribute_value.search( + [('product_attribute_value_id', 'in', + var_attr_val_ids), + ('product_tmpl_id', '=', + pro_tmpl_id.id)]).id) + variant_value_ids += [pro_temp_attr_val_id] + if variant_value_ids: + product = product.filtered( + lambda p: + p.product_template_variant_value_ids.ids + == variant_value_ids) + else: + error_msg += row_not_import_msg + ( + "\n\t❎Product variant with variant " + "values \"%s\" not found." + % (item['Variant Values'])) + continue + if len(product) != 1: + error_msg += row_not_import_msg + ( + "\n\t❎Multiple variants with same " + "Variant Values \"%s\" found. " + "Please check if the product " + "Variant Values are unique." + % (item['Variant Values'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Products with same " + "Name \"%s\" found. Please " + "provide unique product \"Variant " + "Values\" to filter the records." + % (item['Product'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t❎Product name missing in file!") + continue + if self.import_product_by == 'default_code': + if item.get('Internal Reference'): + product = product_product.search( + [('default_code', '=', item['Internal Reference'])]) + if not product: + if not item.get('Product'): + warning_msg += ("\nℹA Product is created with " + "\"Internal Reference\" as " + "product name since \"Product\"" + " name is missing at row %d." + % row) + pro_vals['name'] = item['Internal Reference'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t❎Multiple Products with same Internal " + "Reference(%s) found!" + % (item['Internal Reference'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t❎Internal Reference missing in file!") + continue + if self.import_product_by == 'barcode': + if item.get('Barcode'): + product = product_product.search([('barcode', '=', + item['Barcode'])]) + if not product: + if not item.get('Product'): + warning_msg += ( + "\nℹNo value under \"Product\" at " + "row %d, thus added \"Barcode\" as " + "product name" % row) + pro_vals['name'] = item['Barcode'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t❎Other Product(s) with same " + "Barcode (%s) found!" % item['Barcode']) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t❎Barcode missing in file!") + continue + if self.import_product_by and product: + line_vals['product_id'] = product.id + purchaseorder.write({ + 'order_line': [(0, 0, line_vals)] + }) + imported += 1 + imported_purchaseorders += [purchaseorder] + if self.auto_confirm_quot and imported_purchaseorders: + for po in imported_purchaseorders: + po.button_confirm() + confirmed += 1 + if error_msg: + error_msg = "\n\n⚠ WARNING ⚠" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + + msg = (("Imported %d records.\nConfirmed %d records" + % (imported, confirmed)) + vendor_added_msg + warning_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_purchase_order_views.xml b/import_dashboard/wizard/import_purchase_order_views.xml new file mode 100644 index 000000000..efb61173d --- /dev/null +++ b/import_dashboard/wizard/import_purchase_order_views.xml @@ -0,0 +1,29 @@ + + + + + import.purchase.order.view.form + import.purchase.order + +
+ + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_sale_order.py b/import_dashboard/wizard/import_sale_order.py new file mode 100644 index 000000000..6e568ea03 --- /dev/null +++ b/import_dashboard/wizard/import_sale_order.py @@ -0,0 +1,386 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import datetime +import io +import re +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models + + +class ImportSaleOrder(models.TransientModel): + """ Model for import sale orders. """ + _name = 'import.sale.order' + _description = 'Sale Order Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xlsx', 'XLSX File')], + string='Select File Type', default='csv', help='File type') + file_upload = fields.Binary(string="Upload File", + help="Helps to upload your file") + auto_confirm_quot = fields.Boolean( + string='Confirm Quotation Automatically', + help='Automatically confirm the quotation') + order_number = fields.Selection( + selection=[('from_system', 'From System'), + ('from_file', 'From File')], + string='Order / Quotation Number', default='from_file', + help='Order or Quotation Number') + import_product_by = fields.Selection( + selection=[('name', 'Name'), + ('default_code', 'Internal Reference'), + ('barcode', 'Barcode')], default='name', + string="Import order by", help="import product") + + def action_import_sale_order(self): + """Creating sale order record using uploaded xl/csv files""" + sale_order = self.env['sale.order'] + res_partner = self.env['res.partner'] + res_users = self.env['res.users'] + product_product = self.env['product.product'] + product_attribute = self.env['product.attribute'] + product_attribute_value = self.env['product.attribute.value'] + product_template_attribute_value = self.env[ + 'product.template.attribute.value'] + account_tax = self.env['account.tax'] + uom_uom = self.env['uom.uom'] + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format of " + "the file and try again!") + items = csv_reader + if self.file_type == 'xlsx': + try: + fp = tempfile.NamedTemporaryFile(delete=False, + suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the " + "type and format of the file and try again!") + headers = sheet.row_values(0) + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) # list + data += [{k: v for k, v in zip(headers, row)}] + items = data + row = 0 + imported = 0 + confirmed = 0 + imported_saleorders = [] + error_msg = "" + cust_added_msg = "" + warning_msg = "" + if items: + for item in items: + row += 1 + vals = {} + row_not_import_msg = "\n◼ Row {rn} not imported.".format( + rn=row) + import_error_msg = "" + missing_fields_msg = "" + fields_msg = "\n\t\t🚫Missing required field(s):" + cust_msg = "\n🆕New Customer(s) added:" + if not item.get('Order Reference'): + if missing_fields_msg: + missing_fields_msg += "\n\t\t\t❗ \"Order Reference\" " + else: + missing_fields_msg += (fields_msg + + "\n\t\t\t❗ \"Order Reference\"") + if item.get('Customer'): + customer = res_partner.search( + [('name', '=', item['Customer'])]) + if not customer: + customer = res_partner.create({ + 'name': item['Customer'] + }) + vals['partner_id'] = customer.id + if cust_added_msg: + cust_added_msg += ( + "\n\t\trow {rn}: {cust}").format( + rn=row, cust=item['Customer']) + else: + cust_added_msg += ( + cust_msg + "\n\t\trow {rn}: " + "\"{cust}\"").format( + rn=row, cust=item['Customer']) + elif len(customer) > 1: + if import_error_msg: + import_error_msg += ( + "\n\t\t⚠ Multiple Partners with" + " name (%s) found!" + % item['Customer']) + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t⚠ Multiple Partners with name (%s) " + "found!" + % item['Customer']) + else: + vals['partner_id'] = customer.id + else: + if missing_fields_msg: + missing_fields_msg += "\n\t\t\t❗ \"Customer\"" + else: + missing_fields_msg += (fields_msg + + "\n\t\t\t❗ \"Customer\"") + if import_error_msg: + import_error_msg += missing_fields_msg + elif missing_fields_msg: + import_error_msg += (row_not_import_msg + + missing_fields_msg) + if item.get('Quotation Date'): + date = item['Quotation Date'] + try: + quot_date = datetime.datetime.strptime( + date, '%m/%d/%Y') + vals['date_order'] = quot_date + except: + if import_error_msg: + import_error_msg += ("\n\t\t⚠ Please check the " + "Quotation Date and format is " + "mm/dd/yyyy") + else: + import_error_msg += row_not_import_msg + ( + "\n\t\t⚠ Please check the Quotation Date and " + "format is mm/dd/yyyy") + + if item.get('Salesperson'): + sales_person = res_users.search( + [('name', '=', item['Salesperson'])]) + if sales_person: + vals['user_id'] = sales_person.id + if import_error_msg: + error_msg += import_error_msg + continue + saleorder = sale_order.search( + [('name', '=', item['Order Reference'])]) + if saleorder: + if len(saleorder) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple sale order with same Order " + "Reference(%s) found!" + % (item['Order Reference'])) + continue + if vals and saleorder.state in ['draft', 'sent']: + saleorder.write(vals) + elif not saleorder: + if self.order_number == 'from_system': + saleorder = sale_order.create(vals) + if self.order_number == 'from_file': + vals['name'] = item.get('Order Reference') + saleorder = sale_order.create(vals) + line_vals = {} + pro_vals = {} + if item.get('Description'): + line_vals['name'] = item['Description'] + if item.get('Quantity'): + line_vals['product_uom_qty'] = item['Quantity'] + if item.get('Uom'): + uom = uom_uom.search([('name', '=', item['Uom'])]) + if uom: + pro_vals['uom_id'] = line_vals['product_uom'] = uom.id + if item.get('Unit Price'): + pro_vals['lst_price'] = line_vals['price_unit'] = item[ + 'Unit Price'] + if item.get('Taxes'): + tax_name = item['Taxes'] + tax_amount = (re.findall(r"(\d+)%", tax_name))[0] + tax = account_tax.search( + [('name', '=', tax_name), + ('type_tax_use', '=', 'sale')], limit=1) + if not tax: + tax = account_tax.create({ + 'name': tax_name, + 'type_tax_use': 'sale', + 'amount': tax_amount if tax_amount else 0.0 + }) + pro_vals['taxes_id'] = line_vals['tax_id'] = [tax.id] + if item.get('Disc.%'): + line_vals['discount'] = item['Disc.%'] + if item.get('Product'): + pro_vals['name'] = item['Product'] + if item.get('Internal Reference'): + pro_vals['default_code'] = item['Internal Reference'] + if self.import_product_by == 'name': + if item.get('Product'): + product = product_product.search([('name', '=', + item['Product'])]) + if not product: + product = product_product.create(pro_vals) + if len(product) > 1: + if item.get('Variant Values'): + pro_tmpl_id = product.mapped('product_tmpl_id') + if len(pro_tmpl_id) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Product records are" + "linked with the product variant " + "\"%s\"" + "." % item['Product']) + continue + variant_values = item['Variant Values'].split( + ',') + variant_value_ids = [] + for var in variant_values: + k_v = var.partition(":") + attr = k_v[0].strip() + attr_val = k_v[2].strip() + var_attr_ids = product_attribute.search( + [('name', '=', attr)]).ids + var_attr_val_ids = (product_attribute_value. + search( + [('name', '=', attr_val), + ('attribute_id', 'in', + var_attr_ids)]).ids) + pro_temp_attr_val_id = ( + product_template_attribute_value.search( + [( + 'product_attribute_value_id', 'in', + var_attr_val_ids), + ('product_tmpl_id', '=', + pro_tmpl_id.id)]).id) + variant_value_ids += [pro_temp_attr_val_id] + if variant_value_ids: + product = product.filtered( + lambda p: + p.product_template_variant_value_ids.ids + == variant_value_ids) + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Product variant with variant" + "values \"%s\" not found." + % (item['Variant Values'])) + continue + if len(product) != 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple variants with same " + "Variant Values \"%s\" found. " + "Please" + "check if the product Variant " + "Values" + " are unique." + % (item['Variant Values'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Products with same " + "Name \"%s\" found. Please " + "provide unique product \"Variant " + "Values\" to filter the records." + % (item['Product'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Product name missing in file!") + continue + if self.import_product_by == 'default_code': + if item.get('Internal Reference'): + product = product_product.search( + [('default_code', '=', item['Internal Reference'])]) + if not product: + if not item.get('Product'): + warning_msg += ("\n◼ A Product is created with " + "\"Internal Reference\" as " + "product name since \"Product\"" + " name is missing in file." + "(row %d)" % row) + + pro_vals['name'] = item['Internal Reference'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Multiple Products with same Internal" + " Reference(%s) found!" + % (item['Internal Reference'])) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Internal Reference missing in file!") + continue + if self.import_product_by == 'barcode': + if item.get('Barcode'): + product = product_product.search( + [('barcode', '=', item['Barcode'])]) + if not product: + if not item.get('Product'): + warning_msg += ( + "\n◼ No value under \"Product\" at " + "row %d, thus added \"Barcode\" as " + "product name" % row) + pro_vals['name'] = item['Barcode'] + product = product_product.create(pro_vals) + if len(product) > 1: + error_msg += row_not_import_msg + ( + "\n\t⚠ Other Product(s) with same " + "Barcode (%s) found!" % item['Barcode']) + continue + else: + error_msg += row_not_import_msg + ( + "\n\t⚠ Barcode missing in file!") + continue + if self.import_product_by and product: + line_vals['product_id'] = product.id + saleorder.write({ + 'order_line': [(0, 0, line_vals)] + }) + imported += 1 + imported_saleorders += [saleorder] + if self.auto_confirm_quot and imported_saleorders: + for so in imported_saleorders: + so.action_confirm() + confirmed += 1 + if error_msg: + error_msg = "\n\n🏮 WARNING 🏮" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + msg = (("Imported %d records.\nConfirmed %d records" + % (imported, confirmed)) + cust_added_msg + warning_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_sale_order_views.xml b/import_dashboard/wizard/import_sale_order_views.xml new file mode 100644 index 000000000..790c61af1 --- /dev/null +++ b/import_dashboard/wizard/import_sale_order_views.xml @@ -0,0 +1,29 @@ + + + + + import.sale.order.view.form + import.sale.order + +
+ + + + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_task.py b/import_dashboard/wizard/import_task.py new file mode 100644 index 000000000..7063e89be --- /dev/null +++ b/import_dashboard/wizard/import_task.py @@ -0,0 +1,145 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +import datetime +from odoo.exceptions import ValidationError +from odoo import fields, models + + +class ImportTask(models.TransientModel): + """ Model for import project task. """ + _name = 'import.task' + _description = 'Task Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xls', 'XLS File')], default='csv', + string='Select File Type', help='File type') + file_upload = fields.Binary(string="Upload File", + help="Helps to upload your file") + user_id = fields.Many2one(comodel_name='res.users', string='Assigned to', + help="assigned to user") + + def action_import_task(self): + """Creating task record using uploaded xl/csv files""" + res_partner = self.env['res.partner'] + project_project = self.env['project.project'] + project_task = self.env['project.task'] + items = False + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + items = csv_reader + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format of " + "the file and try again!") + headers = sheet.row_values(0) # list + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) # list + data += [{k: v for k, v in zip(headers, row)}] + items = data + imported = 0 + info_msg = "" + error_msg = "" + for item in items: + vals = {} + if item.get('Project'): + project = project_project.search( + [('name', '=', item.get('Project'))]) + if not project: + project = project_project.create({ + 'name': item.get('Project') + }) + info_msg += f"\nCreated new project with name :{item.get('Project')}" + vals['project_id'] = project.id + if item.get('Title'): + vals['name'] = item.get('Title') + else: + error_msg += "⚠Title missing in file!" + if item.get('Customer'): + partner = res_partner.search( + [['name', '=', item.get('Customer')]]) + if not partner: + partner = res_partner.create({ + 'name': item.get('Customer') + }) + info_msg += f"\nCreated new partner with name :{item.get('Customer')}" + vals['partner_id'] = partner.id + if item.get('Deadline'): + vals['date_deadline'] = datetime.datetime.strptime( + item.get('Deadline'), '%m/%d/%Y') + if item.get('Parent Task'): + parent_task = project_task.search( + [('name', '=', item.get('Parent Task'))]) + if len(parent_task) > 1: + parent_task = parent_task[0] + vals['parent_id'] = parent_task.id + vals['user_ids'] = self.user_id + if error_msg: + error_msg = "\n\n🏮 ERROR 🏮" + error_msg + error_message = self.env['import.message'].create( + {'message': error_msg}) + return { + 'name': 'Error!', + 'type': 'ir.actions.act_window', + 'view_mode': 'form', + 'res_model': 'import.message', + 'res_id': error_message.id, + 'target': 'new' + } + task_id = project_task.create(vals) + if task_id: + imported += 1 + if info_msg: + info_msg = f"\nInformation : {info_msg}" + msg = (("Imported %d records." + % imported) + info_msg) + message = self.env['import.message'].create( + {'message': msg}) + if message: + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_task_views.xml b/import_dashboard/wizard/import_task_views.xml new file mode 100644 index 000000000..92ef86fc8 --- /dev/null +++ b/import_dashboard/wizard/import_task_views.xml @@ -0,0 +1,26 @@ + + + + + import.task.view.form + import.task + +
+ + + + + + + + + +
+
+
+
+
+
diff --git a/import_dashboard/wizard/import_vendor_pricelist.py b/import_dashboard/wizard/import_vendor_pricelist.py new file mode 100644 index 000000000..5017ee27a --- /dev/null +++ b/import_dashboard/wizard/import_vendor_pricelist.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2024-TODAY Cybrosys Technologies() +# Author: Cybrosys Techno Solutions() +# +# You can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. +# +# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE +# (LGPL v3) along with this program. +# If not, see . +# +############################################################################# +import base64 +import binascii +import csv +import io +import tempfile +import xlrd +from odoo.exceptions import ValidationError +from odoo import fields, models, _ + + +class ImportVendorPricelist(models.TransientModel): + """ Model for import vendor pricelist. """ + _name = 'import.vendor.pricelist' + _description = 'Vendor Pricelist Import' + + file_type = fields.Selection( + selection=[('csv', 'CSV File'), ('xls', 'Excel File')], + string='Select File Type', default='csv', help='File type') + company_id = fields.Many2one(comodel_name='res.company', string='Company', + help="Company", required=True, + default=lambda self: self.env.company) + file_upload = fields.Binary(string="Upload File", + help="Helps to upload your file") + + def action_import_vendor_pricelist(self): + """Creating vendor pricelist record using uploaded xl/csv files""" + items = False + if self.file_type == 'csv': + try: + csv_data = base64.b64decode(self.file_upload) + data_file = io.StringIO(csv_data.decode("utf-8")) + data_file.seek(0) + csv_reader = csv.DictReader(data_file, delimiter=',') + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format " + "of the file and try again!") + items = csv_reader + if self.file_type == 'xls': + try: + fp = tempfile.NamedTemporaryFile(delete=False, suffix=".xlsx") + fp.write(binascii.a2b_base64(self.file_upload)) + fp.seek(0) + workbook = xlrd.open_workbook(fp.name) + sheet = workbook.sheet_by_index(0) + except: + raise ValidationError( + "File not Valid.\n\nPlease check the type and format of " + "the file and try again!") + headers = sheet.row_values(0) # list + data = [] + for row_index in range(1, sheet.nrows): + row = sheet.row_values(row_index) # list + data += [{k: v for k, v in zip(headers, row)}] + items = data + info_msg = "" + imported = 0 + for item in items: + vendor_name = None + product_name = None + currency = None + if item.get('Vendor'): + vendor_name = self.env['res.partner'].search( + [('name', '=', item.get('Vendor'))]) + if not vendor_name: + vendor_name = self.env['res.partner'].create({ + 'name': item.get('Vendor'), + }) + info_msg += f"\nCreated new partner with name: {item.get('Vendor')}" + if item.get('Product Template'): + product_name = self.env['product.template'].search( + [('name', '=', item.get('Product Template'))]) + if not product_name: + product_name = self.env['product.template'].create( + {'name': item.get('Product Template')}) + info_msg += f"\nCreated new product with name: {item.get('Product Template')}" + if item.get('Currency'): + currency = self.env['res.currency'].search( + [('name', '=', item.get('Currency'))]) + self.env['product.supplierinfo'].create({ + "product_tmpl_id": product_name.id, + "partner_id": vendor_name.id, + "min_qty": item.get('Quantity'), + "price": item.get('Price'), + "delay": item.get('Delivery Lead Time'), + 'company_id': self.company_id.id, + 'currency_id': currency.id, + }) + imported += 1 + if info_msg: + info_msg = f"\nInformation : {info_msg}" + msg = (("Imported %d records." + % imported) + info_msg) + return { + 'effect': { + 'fadeout': 'slow', + 'message': msg, + 'type': 'rainbow_man', + } + } diff --git a/import_dashboard/wizard/import_vendor_pricelist_views.xml b/import_dashboard/wizard/import_vendor_pricelist_views.xml new file mode 100644 index 000000000..b0cd29c37 --- /dev/null +++ b/import_dashboard/wizard/import_vendor_pricelist_views.xml @@ -0,0 +1,24 @@ + + + + + import.vendor.pricelist.view.form + import.vendor.pricelist + +
+ + + + + + + + +
+
+
+