diff --git a/stock_3d_view/README.rst b/stock_3d_view/README.rst new file mode 100755 index 000000000..6d246122e --- /dev/null +++ b/stock_3d_view/README.rst @@ -0,0 +1,48 @@ +.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg + :target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 + +Stock 3D View +============= +Virtual 3D Visualization of warehouses and Locations + +Configuration +============= +The user should be added to the security group: Administrator(Inventory/ Stock) inorder to get access to the 3d view.. + +License +------- +General Public License, Version 3 (LGPL v3). +(https://www.gnu.org/licenses/lgpl-3.0-standalone.html) + +Company +------- +* `Cybrosys Techno Solutions `__ + +Credits +------- +* Developers: (V15) Prasanna Kumara B, + (V16) Busthana Shrin, +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/stock_3d_view/__init__.py b/stock_3d_view/__init__.py new file mode 100644 index 000000000..50349d36a --- /dev/null +++ b/stock_3d_view/__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 controllers +from . import models diff --git a/stock_3d_view/__manifest__.py b/stock_3d_view/__manifest__.py new file mode 100644 index 000000000..4198c420e --- /dev/null +++ b/stock_3d_view/__manifest__.py @@ -0,0 +1,61 @@ +# -*- 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': "Stock 3D View", + 'version': '16.0.1.0.0', + 'category': 'Warehouse', + 'summary': """Virtual 3D Visualization of warehouses and Locations""", + 'description': """This module innovative addition to the inventory and + warehouse management module, enhancing the traditional methods of tracking + stock and warehouse operations. Leveraging advanced visualization + technology, this app provides users with an immersive and dynamic + three-dimensional representation of their warehouses, inventory items, and + stock movements.""", + 'author': 'Cybrosys Techno Solutions', + 'company': 'Cybrosys Techno Solutions', + 'maintainer': 'Cybrosys Techno Solutions', + 'website': "https://www.cybrosys.com", + 'depends': ['stock', 'web'], + 'data': [ + 'views/stock_location_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + '/stock_3d_view/static/src/js/form_3d_view.js', + '/stock_3d_view/static/src/js/listview_3d.js', + '/stock_3d_view/static/src/css/3d_view.scss', + 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.js', + 'https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js', + "https://cdn.jsdelivr.net/npm/three@0.122.0/examples/js/controls/OrbitControls.min.js", + 'stock_3d_view/static/src/xml/stock_location_3d_templates.xml', + 'stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml', + 'stock_3d_view/static/src/xml/stock_location_modal_templates.xml', + ], + }, + 'images': [ + 'static/description/banner.jpg', + ], + 'license': 'LGPL-3', + 'installable': True, + 'auto_install': False, + 'application': False, +} diff --git a/stock_3d_view/controllers/__init__.py b/stock_3d_view/controllers/__init__.py new file mode 100644 index 000000000..4b111b3f6 --- /dev/null +++ b/stock_3d_view/controllers/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 stock_3d_view diff --git a/stock_3d_view/controllers/stock_3d_view.py b/stock_3d_view/controllers/stock_3d_view.py new file mode 100644 index 000000000..b82bf78da --- /dev/null +++ b/stock_3d_view/controllers/stock_3d_view.py @@ -0,0 +1,170 @@ +"""This module handles the requests made by js files and returns the corresponding data.""" +# -*- 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 math +from odoo import http +from odoo.http import request + + +class Stock3DView(http.Controller): + """Class for handling the requests and responses""" + + @http.route('/3Dstock/warehouse', type='json', auth='public') + def get_warehouse_data(self, company_id): + """ + This method is used to handle the request for warehouse data. + ------------------------------------------------ + @param self: object pointer. + @param company_id: current company id. + @return: a list of warehouses created under the active company. + """ + warehouse = request.env['stock.warehouse'].sudo().search([]) + warehouse_list = [] + warehouse_list.clear() + for rec in warehouse: + if rec.company_id.id == company_id: + warehouse_list.append((rec.id, rec.name)) + return warehouse_list + + @http.route('/3Dstock/data', type='json', auth='public') + def get_stock_data(self, company_id, wh_id): + """ + This method is used to handle the request for location data. + ------------------------------------------------ + @param self: object pointer + @param company_id: current company id. + @param wh_id: the selected warehouse id. + @return:a list of locations with their dimensions and positions of + selected warehouse. + """ + warehouse = request.env['stock.warehouse'].sudo().search( + [('id', '=', int(wh_id)), ('company_id', '=', int(company_id))]) + locations = request.env['stock.location'].sudo().search( + [('company_id', '=', int(company_id)), + ('active', '=', 'true'), + ('usage', '=', 'internal')]) + location_dict = {} + for loc in locations: + for wh in warehouse: + if loc.warehouse_id.id == warehouse.id: + if loc.id not in ( + wh.lot_stock_id.id, wh.wh_input_stock_loc_id.id, + wh.wh_qc_stock_loc_id.id, + wh.wh_pack_stock_loc_id.id, wh.wh_output_stock_loc_id.id): + length = int(loc.length * 3.779 * 2) + width = int(loc.width * 3.779 * 2) + height = int(loc.height * 3.779 * 2) + location_dict.update( + {loc.unique_code: [loc.pos_x, loc.pos_y, loc.pos_z, + length, width, height]}) + + return location_dict + + @http.route('/3Dstock/data/quantity', type='json', auth='public') + def get_stock_count_data(self, loc_code): + """ + This method is used to handle the request for location's current stock + quantity. + ------------------------------------------------ + @param self: object pointer. + @param loc_code: the selected location code. + @return: current quantity of selected location. + """ + quantity = request.env['stock.quant'].sudo().search( + [('location_id.unique_code', '=', loc_code)]).mapped( + 'quantity') + capacity = request.env['stock.location'].sudo().search( + [('unique_code', '=', loc_code)]).max_capacity + count = math.fsum(quantity) + quant_data = (0, 0) + if capacity: + if capacity > 0: + load = int((count * 100) / capacity) + quant_data = (capacity, load) + else: + if count > 0: + quant_data = (0, -1) + return quant_data + + @http.route('/3Dstock/data/product', type='json', auth='public') + def get_stock_product_data(self, loc_code): + """ + This method is used to handle the request for data of products of + selected location. + ------------------------------------------------ + @param self: object pointer. + @param loc_code: the selected location code. + @return: a dictionary including total capacity, current capacity and + products stored in selected location. + """ + products = request.env['stock.quant'].sudo().search( + [('location_id.unique_code', '=', loc_code)]) + quantity_obj = request.env['stock.quant'].sudo().search( + [('location_id.unique_code', '=', loc_code)]).mapped( + 'quantity') + capacity = request.env['stock.location'].sudo().search( + [('unique_code', '=', loc_code)]).max_capacity + product_list = [] + product_list.clear() + if products: + for rec in products: + product_list.append((rec.product_id.display_name, rec.quantity)) + load = math.fsum(quantity_obj) + if capacity > 0: + space = capacity - load + else: + space = 0 + data = { + 'capacity': capacity, + 'space': space, + 'product_list': product_list + } + return data + + @http.route('/3Dstock/data/standalone', type='json', auth='public') + def get_standalone_stock_data(self, company_id, loc_id): + """ + This method is used to handle the request for individual location data. + ------------------------------------------------ + @param self: object pointer. + @param company_id: the current company id. + @param loc_id: the selected location code. + @return: a dictionary including of selected location's dimensions and + positions. + """ + warehouse = request.env['stock.location'].sudo().search( + [('company_id.id', '=', int(company_id)), + ('id', '=', int(loc_id))]).mapped('warehouse_id') + locations = request.env['stock.location'].sudo().search( + [('company_id.id', '=', int(company_id)), + ('active', '=', 'true'), + ('usage', '=', 'internal')]) + location_dict = {} + for loc in locations: + if loc.warehouse_id.id == warehouse.id: + length = int(loc.length * 3.779 * 2) + width = int(loc.width * 3.779 * 2) + height = int(loc.height * 3.779 * 2) + location_dict.update( + {loc.unique_code: [loc.pos_x, loc.pos_y, loc.pos_z, + length, width, height, loc.id]}) + return location_dict diff --git a/stock_3d_view/doc/RELEASE_NOTES.md b/stock_3d_view/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..04fb6a7b1 --- /dev/null +++ b/stock_3d_view/doc/RELEASE_NOTES.md @@ -0,0 +1,6 @@ +## Module + +#### 26.09.2024 +#### Version 16.0.1.0.0 +#### ADD +- Initial commit for Stock 3D View diff --git a/stock_3d_view/models/__init__.py b/stock_3d_view/models/__init__.py new file mode 100644 index 000000000..aa681a236 --- /dev/null +++ b/stock_3d_view/models/__init__.py @@ -0,0 +1,22 @@ +# -*- 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 stock_location diff --git a/stock_3d_view/models/stock_location.py b/stock_3d_view/models/stock_location.py new file mode 100644 index 000000000..73f9cafad --- /dev/null +++ b/stock_3d_view/models/stock_location.py @@ -0,0 +1,67 @@ +"""This module inherits stock.location model.""" +# -*- 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 StockLocation(models.Model): + """Class for adding fields to stock.location""" + _inherit = 'stock.location' + + length = fields.Float(string="Length (M)", + help="Length of the location in meters") + width = fields.Float(string="Width (M)", + help="Width of the location in meters") + height = fields.Float(string="Height (M)", + help="Height of the location in meters") + pos_x = fields.Float(string="X (in px)", + help="Position of the location along X-axis") + pos_y = fields.Float(string="Y (in px)", + help="Position of the location along Y-axis") + pos_z = fields.Float(string="Z (in px)", + help="Position of the location along Z-axis") + unique_code = fields.Char(string="Location Code", + help="Unique code of the location") + max_capacity = fields.Integer(string="Capacity (Units)", + help="Maximum capacity of the location in " + "terms of Units") + + _sql_constraints = [ + ('unique_code', 'UNIQUE(unique_code)', + "The location code must be unique per company !"), + ] + + def action_view_location_3d_button(self): + """ + This method is used to handle the view_location_3d_button button action. + ------------------------------------------------ + @param self: object pointer. + @return: client action with location id and company id to display. + """ + return { + 'type': 'ir.actions.client', + 'tag': 'open_form_3d_view', + 'context': { + 'loc_id': self.id, + 'company_id': self.company_id.id, + } + } diff --git a/stock_3d_view/static/description/assets/icons/check.png b/stock_3d_view/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/check.png differ diff --git a/stock_3d_view/static/description/assets/icons/chevron.png b/stock_3d_view/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/chevron.png differ diff --git a/stock_3d_view/static/description/assets/icons/cogs.png b/stock_3d_view/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/cogs.png differ diff --git a/stock_3d_view/static/description/assets/icons/consultation.png b/stock_3d_view/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/consultation.png differ diff --git a/stock_3d_view/static/description/assets/icons/ecom-black.png b/stock_3d_view/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/ecom-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/education-black.png b/stock_3d_view/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/education-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/hotel-black.png b/stock_3d_view/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/hotel-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/license.png b/stock_3d_view/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/license.png differ diff --git a/stock_3d_view/static/description/assets/icons/lifebuoy.png b/stock_3d_view/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/lifebuoy.png differ diff --git a/stock_3d_view/static/description/assets/icons/logo.png b/stock_3d_view/static/description/assets/icons/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/logo.png differ diff --git a/stock_3d_view/static/description/assets/icons/manufacturing-black.png b/stock_3d_view/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/manufacturing-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/pos-black.png b/stock_3d_view/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/pos-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/puzzle.png b/stock_3d_view/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/puzzle.png differ diff --git a/stock_3d_view/static/description/assets/icons/restaurant-black.png b/stock_3d_view/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/restaurant-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/service-black.png b/stock_3d_view/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/service-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/trading-black.png b/stock_3d_view/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/trading-black.png differ diff --git a/stock_3d_view/static/description/assets/icons/training.png b/stock_3d_view/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/training.png differ diff --git a/stock_3d_view/static/description/assets/icons/update.png b/stock_3d_view/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/update.png differ diff --git a/stock_3d_view/static/description/assets/icons/user.png b/stock_3d_view/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/user.png differ diff --git a/stock_3d_view/static/description/assets/icons/wrench.png b/stock_3d_view/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/stock_3d_view/static/description/assets/icons/wrench.png differ diff --git a/stock_3d_view/static/description/assets/modules/1.png b/stock_3d_view/static/description/assets/modules/1.png new file mode 100644 index 000000000..359d3e4d6 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/1.png differ diff --git a/stock_3d_view/static/description/assets/modules/2.png b/stock_3d_view/static/description/assets/modules/2.png new file mode 100644 index 000000000..7a17f2667 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/2.png differ diff --git a/stock_3d_view/static/description/assets/modules/3.png b/stock_3d_view/static/description/assets/modules/3.png new file mode 100644 index 000000000..baaa60084 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/3.png differ diff --git a/stock_3d_view/static/description/assets/modules/4.png b/stock_3d_view/static/description/assets/modules/4.png new file mode 100644 index 000000000..ed99b5959 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/4.png differ diff --git a/stock_3d_view/static/description/assets/modules/5.png b/stock_3d_view/static/description/assets/modules/5.png new file mode 100644 index 000000000..55fb7ba18 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/5.png differ diff --git a/stock_3d_view/static/description/assets/modules/6.png b/stock_3d_view/static/description/assets/modules/6.png new file mode 100644 index 000000000..e339ff769 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/6.png differ diff --git a/stock_3d_view/static/description/assets/modules/module_1.png b/stock_3d_view/static/description/assets/modules/module_1.png new file mode 100644 index 000000000..492980ad0 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_1.png differ diff --git a/stock_3d_view/static/description/assets/modules/module_2.png b/stock_3d_view/static/description/assets/modules/module_2.png new file mode 100644 index 000000000..65a3645b0 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_2.png differ diff --git a/stock_3d_view/static/description/assets/modules/module_3.png b/stock_3d_view/static/description/assets/modules/module_3.png new file mode 100644 index 000000000..5f7b7d385 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_3.png differ diff --git a/stock_3d_view/static/description/assets/modules/module_4.png b/stock_3d_view/static/description/assets/modules/module_4.png new file mode 100644 index 000000000..d01ceb1ab Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_4.png differ diff --git a/stock_3d_view/static/description/assets/modules/module_5.jpeg b/stock_3d_view/static/description/assets/modules/module_5.jpeg new file mode 100644 index 000000000..3b6a99a25 Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_5.jpeg differ diff --git a/stock_3d_view/static/description/assets/modules/module_6.png b/stock_3d_view/static/description/assets/modules/module_6.png new file mode 100644 index 000000000..39b4ec3ed Binary files /dev/null and b/stock_3d_view/static/description/assets/modules/module_6.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/1.png b/stock_3d_view/static/description/assets/screenshots/1.png new file mode 100644 index 000000000..767b2a252 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/1.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/2.png b/stock_3d_view/static/description/assets/screenshots/2.png new file mode 100644 index 000000000..939d020c1 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/2.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/3.png b/stock_3d_view/static/description/assets/screenshots/3.png new file mode 100644 index 000000000..a0607d1d0 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/3.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/4.png b/stock_3d_view/static/description/assets/screenshots/4.png new file mode 100644 index 000000000..2b86f9a4c Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/4.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/5.png b/stock_3d_view/static/description/assets/screenshots/5.png new file mode 100644 index 000000000..964a85d65 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/5.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/66.png b/stock_3d_view/static/description/assets/screenshots/66.png new file mode 100644 index 000000000..bbc65cc42 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/66.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/77.png b/stock_3d_view/static/description/assets/screenshots/77.png new file mode 100644 index 000000000..5491e8a04 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/77.png differ diff --git a/stock_3d_view/static/description/assets/screenshots/hero.gif b/stock_3d_view/static/description/assets/screenshots/hero.gif new file mode 100644 index 000000000..3738b3c84 Binary files /dev/null and b/stock_3d_view/static/description/assets/screenshots/hero.gif differ diff --git a/stock_3d_view/static/description/banner.jpg b/stock_3d_view/static/description/banner.jpg new file mode 100644 index 000000000..0755294b2 Binary files /dev/null and b/stock_3d_view/static/description/banner.jpg differ diff --git a/stock_3d_view/static/description/icon.png b/stock_3d_view/static/description/icon.png new file mode 100644 index 000000000..8823d2e29 Binary files /dev/null and b/stock_3d_view/static/description/icon.png differ diff --git a/stock_3d_view/static/description/index.html b/stock_3d_view/static/description/index.html new file mode 100644 index 000000000..0b0128f14 --- /dev/null +++ b/stock_3d_view/static/description/index.html @@ -0,0 +1,787 @@ +
+
+
+
+ +
+
+
+ Community +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+
+
+ +
+
+
+

+ Stock 3D View +

+

+ Virtual 3D Visualization of Warehouses and Locations +

+ +
+
+ + + +
+
+

+ Overview +

+
+ +
+

+ This app helps users to visualize their stock and locations + virtually with integrated features. + One can view and manipulate the 3D structure of a stock location + using this app. + Nowadays warehouse management is hard to handle manually. + So, users can handle warehouse management virtually with the + help of this app. + This app visualizes a 3D view based on multiple warehouses and + locations. + This app enables users to view, rotate 360° and zoom multi + warehouse locations in a 3D space. +

+
+
+ +
+
+

+ Configuration +

+
+
+
+

+ Hardware Requirements

+

+ RAM : 16GB or Above +

+

+ Graphics card : 4 GB additional +

+

+ Processor : Intel i5 or above, AMD Ryzen 5 or above +

+
+
+
+
+

+ Software Requirements

+

+ Web Browser capable of running WebGL: WebGL is a JavaScript + API, or programmable interface, for + drawing interactive 2D and 3D + graphics in web pages. WebGL connects your web browser up to + your device’s graphics + card, providing you with far more graphical processing power + than is available on a + traditional website. +

+
+
+
+ +
+
+

+ Features +

+
+
+
+ +
+
+

+ Multiple Warehouse Selection

+

+ Users can view 3d visualization of all locations based + multiple warehouses. +

+
+
+
+
+ +
+
+

+ Location Specific 3D View

+

+ Users can view 3d visualization of individual locations as + highlighted in the entire warehouse. +

+
+
+
+
+ +
+
+

+ Actual Warehouse into Virtual Warehouse

+

+ Users can enter dimension and position of a Location. +

+
+
+
+
+ +
+
+

+ Status of each Location

+

+ Users can quickly identify the status of location like free + space/overloaded through their indexing + based on their load and capacity. +

+
+
+
+
+ +
+
+

+ Current Stock of each Location

+

+ Users can easily get the data about current stock of each + location by tapping twice on them. +

+
+
+
+ +
+
+

+ Screenshots +

+
+
+

+ Location Form View

+

+ Enter the location properties such as location Code, Capacity, + Dimension and Position along X,Y and Z + axis. +

+ +
+
+

+ 3D View Based on Warehouses

+

+ Click on the 3D button marked below to open 3D view based on + warehouses. +

+ +
+
+

+ Multiple Warehouse Selection

+

+ Users can switch to any warehouse by using the dropdown option + highlighted below. +

+ +
+
+

+ Location Specific 3D View

+

+ Please open a location and provide a proper dimension, position, + code and parent stock. Click on the + button "3D" located inside header. +

+ +
+
+

+ Individual Location Highlight

+

+ Users can view the selected location's 3D view individually. The + location is indexed based on its load + and capacity. +

+ +
+
+

+ Get Stock Data

+

+ Double tap on the location to get its stock data. +

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

Related + 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 +

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

Need Help?

+
+
+
+ + +
+ + + +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+
+ + +
\ No newline at end of file diff --git a/stock_3d_view/static/src/css/3d_view.scss b/stock_3d_view/static/src/css/3d_view.scss new file mode 100644 index 000000000..1f6dd734b --- /dev/null +++ b/stock_3d_view/static/src/css/3d_view.scss @@ -0,0 +1,125 @@ +canvas { + border: 7px solid #666666; + background-color: white; + position: relative; +} + +.customselect{ +bottom: 744px; +left: 44px; +height: 25px; +width: 150px; +position: absolute; +color: indigo; +font-size: 15px; +} + + +.rectangle { +bottom: 650px; +right: 7px; + height: 140px; + width: 180px; + position: absolute; + +} + +.square1 { + height: 20px; + width: 20px; + background-color: #cc0000; + border-radius: 5px; +} + +.squareText1 { + height: 20px; + width: 170px; + color: black; + margin-top: -19px; + padding-left: 30px; + font-weight: 600; + font-size: 13px; +} + +.square2 { + height: 20px; + width: 20px; + background-color: #e6b800; + border-radius: 5px; + margin-top: 2px; +} + +.squareText2 { + height: 20px; + width: 170px; + color: black; + margin-top: -19px; + padding-left: 30px; + font-weight: 600; + font-size: 13px; +} + +.square3 { + height: 20px; + width: 20px; + background-color: #00802b; + border-radius: 5px; + margin-top: 2px; +} + +.squareText3 { + height: 20px; + width: 170px; + color: black; + margin-top: -19px; + padding-left: 30px; + font-weight: 600; + font-size: 13px; +} + +.square4 { + height: 20px; + width: 20px; + background-color: #8c8c8c; + border-radius: 5px; + margin-top: 2px; +} + +.square4blue { + height: 20px; + width: 20px; + background-color: #0066ff; + border-radius: 5px; + margin-top: 2px; +} + +.squareText4 { + height: 20px; + width: 170px; + color: black; + margin-top: -19px; + padding-left: 30px; + font-weight: 600; + font-size: 13px; +} + +.closeBtn { + bottom: 759px; + left: 10px; + font-size: 33px; + font-weight: 600; + position: absolute; + color: indigo; + background: transparent; + border: none; + outline: none; +} + +.closeBtn:focus { + outline: 0 !important; +} + +.closeBtn:hover { + animation: pulse 1s; + box-shadow: 0 0 0 2em rgba(#fff,0); +} diff --git a/stock_3d_view/static/src/js/form_3d_view.js b/stock_3d_view/static/src/js/form_3d_view.js new file mode 100644 index 000000000..b51b11b2d --- /dev/null +++ b/stock_3d_view/static/src/js/form_3d_view.js @@ -0,0 +1,387 @@ +odoo.define('stock_3d_view.action_open_form_3d_view', function(require) { + 'use strict'; + var AbstractAction = require('web.AbstractAction'); + var core = require('web.core'); + var rpc = require('web.rpc'); + var QWeb = core.qweb; + var Dialog = require('web.Dialog'); + var ajax = require('web.ajax'); + var PositionDialog = Dialog.extend({ + /** + * Initializes the PositionDialog instance. + * + * @constructor + * @param {Object} parent - The parent object. + * @param {Object} options - Configuration options. + * @param {Object} options.pointer - The pointer object. + * @param {Function} options.close - Callback function for closing the dialog. + */ + init: function(parent, options) { + var opt = options; + this._super(...arguments) + this.pointer = opt.pointer + this.onClickClose = opt.close + }, + /** + * Renders the element and sets its position. + * + */ + renderElement: function() { + this._super() + this.$modal.find('.modal-dialog').css({ + position: 'absolute', + left: this.pointer.x, + top: this.pointer.y, + }) + } + }) + //Extends abstract action class to add event listener for 3D button. + var open_form_3d_view = AbstractAction.extend({ + template: 'Location3DFormView', + events: _.extend({}, AbstractAction.prototype.events, { + 'click .breadcrumb-item a': 'onBreadcrumbClick' + }), + /** + * Handles the click event on breadcrumbs. + * + * @param {Event} ev - The click event object. + */ + onBreadcrumbClick: function(ev) { + let jsId = this.$(ev.target).attr('jsId'); + this.actionService.restore(jsId); + }, + /** + * Initializes the open_form_3d_view instance. + * + * @constructor + * @param {Object} parent - The parent object. + * @param {Object} action - The action configuration. + */ + init: function(parent, action) { + this._super(...arguments) + this.breadcrumbs = parent.wowlEnv.config.breadcrumbs + this.actionService = parent.actionService; + }, + /** + * Starts the open_form_3d_view action. + */ + start: function() { + this.Open3DView(); + }, + /** + * Starts the process of displaying rendered 3D object of stock.location. + */ + Open3DView: function() { + var self = this; + var wh_data; + var data; + var loc_quant; + let controls, renderer, clock, scene, camera, pointer, raycaster; + var mesh, group; + var material; + var loc_color; + var loc_opacity = 0.5; + var textSize; + let selectedObject = null; + var dialogs = null; + var wh_id; + var location_id = self.searchModel.config.context.loc_id || localStorage.getItem("location_id"); + //sets location_id and company_id in local storage + if (self.searchModel.config.context.loc_id != null) { + localStorage.setItem("location_id", self.searchModel.config.context.loc_id); + localStorage.setItem("company_id", self.searchModel.config.context.company_id); + } + + var colorDiv = document.createElement("div"); + colorDiv.classList.add("rectangle"); + var color1 = document.createElement("div"); + color1.classList.add("square1"); + colorDiv.appendChild(color1); + var colorText1 = document.createElement("div"); + colorText1.classList.add("squareText1"); + colorText1.innerHTML = "Overload"; + colorDiv.appendChild(colorText1); + var color2 = document.createElement("div"); + color2.classList.add("square2"); + colorDiv.appendChild(color2); + var colorText2 = document.createElement("div"); + colorText2.classList.add("squareText2"); + colorText2.innerHTML = "Almost Full"; + colorDiv.appendChild(colorText2); + var color3 = document.createElement("div"); + color3.classList.add("square3"); + colorDiv.appendChild(color3); + var colorText3 = document.createElement("div"); + colorText3.classList.add("squareText3"); + colorText3.innerHTML = "Free Space Available"; + colorDiv.appendChild(colorText3); + var color4 = document.createElement("div"); + color4.classList.add("square4blue"); + colorDiv.appendChild(color4); + var colorText4 = document.createElement("div"); + colorText4.classList.add("squareText4"); + colorText4.innerHTML = "No Product/Load"; + colorDiv.appendChild(colorText4); + + start(); + /** + * The complete working of fetching data from stock.location and displaying them in the form of 3d objects. + * + * @async + */ + async function start() { + /** + * Make a jsonRpc call to fetch location details. + * + * @await + * @param {integer} company_id + * @param {integer} loc_id + */ + await ajax.jsonRpc('/3Dstock/data/standalone', 'call', { + 'company_id': self.searchModel.config.context.company_id || localStorage.getItem("company_id"), + 'loc_id': self.searchModel.config.context.loc_id || localStorage.getItem("location_id"), + }).then(function(incoming_data) { + data = incoming_data; + }); + //creating a new three.scene + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xdfdfdf); + clock = new THREE.Clock(); + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.5, 6000); + camera.position.set(0, 200, 300) + renderer = new THREE.WebGLRenderer({ + antialias: true + }); + //setting size and pixel ratio for the renderer. + renderer.setSize(window.innerWidth, window.innerHeight / 1.163); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.render(scene, camera); + var o_content = self.$('.o_content') + o_content.append(renderer.domElement); + self.$el.find('.o_content').append(colorDiv); + controls = new THREE.OrbitControls(camera, renderer.domElement); + const baseGeometry = new THREE.BoxGeometry(800, 0, 800); + const baseMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + transparent: false, + opacity: 1, + side: THREE.FrontSide, + }); + const baseCube = new THREE.Mesh(baseGeometry, baseMaterial); + scene.add(baseCube); + group = new THREE.Group(); + //traversing through each location object + for (let [key, value] of Object.entries(data)) { + //checks if the dimension values of the location are non zero + if ((value[0] > 0) || (value[1] > 0) || (value[2] > 0) || (value[3] > 0) || (value[4] > 0) || (value[5] > 0)) { + const geometry = new THREE.BoxGeometry(value[3], value[5], value[4]); + geometry.translate(0, (value[5] / 2), 0); + const edges = new THREE.EdgesGeometry(geometry); + /** + * Make a jsonRpc call to fetch the stock details of particular location. + * + * @await + * @param {integer} loc_code + */ + await ajax.jsonRpc('/3Dstock/data/quantity', 'call', { + 'loc_code': key, + }).then(function(quant_data) { + loc_quant = quant_data; + }); + //checking the quantity and capacity of the location to determine the color of the location + if (localStorage.getItem("location_id") == value[6]) { + if (loc_quant[0] > 0) { + if (loc_quant[1] > 100) { + loc_color = 0xcc0000; + loc_opacity = 0.8; + } else if (loc_quant[1] > 50) { + loc_color = 0xe6b800; + loc_opacity = 0.8; + } else { + loc_color = 0x00802b; + loc_opacity = 0.8; + } + } else { + if (loc_quant[1] == -1) { + loc_color = 0x00802b; + loc_opacity = 0.8; + } else { + loc_color = 0x0066ff; + loc_opacity = 0.8; + } + } + } else { + loc_color = 0x8c8c8c; + loc_opacity = 0.5; + } + //creating a 3D box geometry for each location + material = new THREE.MeshBasicMaterial({ + color: loc_color, + transparent: true, + opacity: loc_opacity + }); + mesh = new THREE.Mesh(geometry, material); + const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ + color: 0x404040 + })); + line.position.x = value[0]; + line.position.y = value[1]; + line.position.z = value[2]; + mesh.position.x = value[0]; + mesh.position.y = value[1]; + mesh.position.z = value[2]; + const loader = new THREE.FontLoader(); + loader.load('https://threejs.org/examples/fonts/droid/droid_sans_bold.typeface.json', function(font) { + const textcolor = 0x000000; + const textMat = new THREE.MeshBasicMaterial({ + color: textcolor, + side: THREE.DoubleSide, + }); + const textmessage = key; + if (value[3] > value[4]) { + textSize = (value[4] / 2) - (value[4] / 2.9); + } else { + textSize = (value[3] / 2) - (value[3] / 2.9); + } + const textshapes = font.generateShapes(textmessage, textSize); + const textgeometry = new THREE.ShapeGeometry(textshapes); + textgeometry.translate(0, ((value[5] / 2) - (textSize / (textSize - 1.5))), 0); + const text = new THREE.Mesh(textgeometry, textMat); + if (value[4] > value[3]) { + text.rotation.y = Math.PI / 2; + text.position.x = value[0]; + text.position.y = value[1]; + text.position.z = value[2] + (textSize * 2) + ((value[3] / 3.779 / 2) / 2) + (textSize / 2); + } else { + text.position.x = value[0] - (textSize * 2) - ((value[4] / 3.779 / 2) / 2) - (textSize / 2); + text.position.y = value[1]; + text.position.z = value[2]; + } + scene.add(text); + }); + scene.add(mesh); + scene.add(line); + mesh.name = key; + mesh.userData = { + color: loc_color, + loc_id: value[6], + }; + group.add(mesh); + } + } + scene.add(group); + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector3(); + animate(); + } + /** + * Handles the resizing and setting the pixel ration on window resize. + */ + function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight / 1.163); + } + /** + * Animates and renders the three.renderer object to make changes on the scene. + */ + function animate() { + requestAnimationFrame(animate); + const delta = clock.getDelta(); + renderer.render(scene, camera); + var canvas = document.getElementsByTagName("canvas")[0]; + var colorBox = document.querySelector(".rectangle"); + //checking the canvas element adding event listener on canvas. + if (canvas == null) { + window.removeEventListener('dblclick', onPointerMove); + window.removeEventListener('resize', onWindowResize); + if (colorBox) { + colorBox.style.display = "none"; + } + } else { + window.addEventListener('dblclick', onPointerMove); + window.addEventListener('resize', onWindowResize); + if (colorBox) { + colorBox.style.display = "block"; + } + } + } + /** + * gets the coordinates of the mouse point and checks for any objects. + * + * @async + * @param {object} event + */ + async function onPointerMove(event) { + var products; + var button; + if (dialogs == null) { + if (selectedObject) { + selectedObject.material.color.set(selectedObject.userData.color); + selectedObject = null; + } else { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / (window.innerHeight)) * 2 + 1 + 0.13; + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObject(group, true); + if (intersects.length > 0) { + const res = intersects.filter(function(res) { + return res && res.object; + })[0]; + if (res && res.object) { + if (res.object.userData.loc_id == location_id) { + /** + * Make a jsonRpc call to fetch the details of products and their quantity of selected location. + * + * @await + * @param {integer} loc_code + */ + await ajax.jsonRpc('/3Dstock/data/product', 'call', { + 'loc_code': res.object.name, + }).then(function(product_data) { + products = product_data; + }); + selectedObject = res.object; + selectedObject.material.color.set(0x00ffcc); + + function onClickClose() { + if (selectedObject) { + selectedObject.material.color.set(selectedObject.userData.color); + selectedObject = null; + dialogs.close(); + dialogs = null; + } + } + //opens a new dialogbox with proeduct and quantity details + dialogs = new PositionDialog(this, { + title: ('Location: ' + res.object.name), + size: 'small', + $content: $(QWeb.render('ViewLocationData', { + data: products, + })), + placement: 'bottom', + renderFooter: false, + pointer: { + x: event.clientX, + y: event.clientY, + }, + close: onClickClose, + }).open(); + + if (dialogs) { + window.addEventListener('click', onClickClose); + } else { + window.removeEventListener('click', onClickClose); + } + } + } + } + } + } + } + } + + }); + core.action_registry.add("open_form_3d_view", open_form_3d_view); + return open_form_3d_view; +}); \ No newline at end of file diff --git a/stock_3d_view/static/src/js/listview_3d.js b/stock_3d_view/static/src/js/listview_3d.js new file mode 100644 index 000000000..3372be9d6 --- /dev/null +++ b/stock_3d_view/static/src/js/listview_3d.js @@ -0,0 +1,413 @@ +odoo.define('button_near_create.tree_button', function(require) { + "use strict"; + var ListController = require('web.ListController'); + var ListView = require('web.ListView'); + var viewRegistry = require('web.view_registry'); + var core = require('web.core'); + var session = require('web.session'); + var Dialog = require('web.Dialog'); + var QWeb = core.qweb; + var rpc = require('web.rpc'); + var ajax = require('web.ajax'); + var PositionDialog = Dialog.extend({ + /** + * Initialize the PositionDialog. + * + * @param {Object} parent - The parent object. + * @param {Object} options - Dialog options. + * @param {Object} pointer - Object containing x and y coordinates. + * @param {Function} close - Function to be called on dialog close. + */ + init: function(parent, options) { + var opt = options; + this._super(...arguments) + this.pointer = opt.pointer + this.onClickClose = opt.close + }, + /** + * Render the PositionDialog element. + * Set the dialog's position based on the provided coordinates. + */ + renderElement: function() { + this._super() + this.$modal.find('.modal-dialog').css({ + position: 'absolute', + left: this.pointer.x, + top: this.pointer.y, + }) + } + }) + //Extends list controller class to add event listener for 3D button. + var TreeButton = ListController.extend({ + buttons_template: 'StockLocationListView', + events: _.extend({}, ListController.prototype.events, { + 'click .open_3d_view': '_Open3DView', + }), + /** + * Starts the process of displaying rendered 3D object of stock.location. + */ + _Open3DView: async function(ev) { + var self = this; + var wh_data; + var data; + var loc_quant; + let controls, renderer, clock, scene, camera, pointer, raycaster; + var mesh, group; + var material; + var loc_color; + var loc_opacity = 0.5; + var textSize; + let selectedObject = null; + var dialogs = null; + var wh_id; + /** + * Make a jsonRpc call to fetch available warehouses and list it in the dropdown. + * + * @await + * @param {integer} company_id + */ + await ajax.jsonRpc('/3Dstock/warehouse', 'call', { + 'company_id': session.user_context.allowed_company_ids[0], + }).then(function(incoming_data) { + wh_data = incoming_data; + }); + wh_id = wh_data[0][0]; + var select = document.createElement("select"); + select.name = "mySelect"; + for (let i = 0; i < wh_data.length; i++) { + var option = document.createElement("option"); + option.value = wh_data[i][0]; + option.text = wh_data[i][1]; + select.appendChild(option); + select.classList.add("customselect"); + } + + var closeDiv = document.createElement("button"); + closeDiv.classList.add("closeBtn"); + closeDiv.innerHTML = "×" + var colorDiv = document.createElement("div"); + colorDiv.classList.add("rectangle"); + var color1 = document.createElement("div"); + color1.classList.add("square1"); + colorDiv.appendChild(color1); + var colorText1 = document.createElement("div"); + colorText1.classList.add("squareText1"); + colorText1.innerHTML = "Overload"; + colorDiv.appendChild(colorText1); + var color2 = document.createElement("div"); + color2.classList.add("square2"); + colorDiv.appendChild(color2); + var colorText2 = document.createElement("div"); + colorText2.classList.add("squareText2"); + colorText2.innerHTML = "Almost Full"; + colorDiv.appendChild(colorText2); + var color3 = document.createElement("div"); + color3.classList.add("square3"); + colorDiv.appendChild(color3); + var colorText3 = document.createElement("div"); + colorText3.classList.add("squareText3"); + colorText3.innerHTML = "Free Space Available"; + colorDiv.appendChild(colorText3); + var color4 = document.createElement("div"); + color4.classList.add("square4"); + colorDiv.appendChild(color4); + var colorText4 = document.createElement("div"); + colorText4.classList.add("squareText4"); + colorText4.innerHTML = "No Product/Load"; + colorDiv.appendChild(colorText4); + + start(); + /** + * The complete working of fetching data from stock.location and displaying them in the form of 3d objects. + * + * @async + */ + async function start() { + /** + * Make a jsonRpc call to fetch location details of corresponding warehouse. + * + * @await + * @param {integer} company_id + * @param {integer} wh_id + */ + await ajax.jsonRpc('/3Dstock/data', 'call', { + 'company_id': session.user_context.allowed_company_ids[0], + 'wh_id': wh_id, + }).then(function(incoming_data) { + data = incoming_data; + }); + scene = new THREE.Scene(); + scene.background = new THREE.Color(0xdfdfdf); + clock = new THREE.Clock(); + camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.5, 6000); + camera.position.set(0, 200, 300) + renderer = new THREE.WebGLRenderer({ + antialias: true + }); + renderer.setSize(window.innerWidth, window.innerHeight / 1.164); + renderer.setPixelRatio(window.devicePixelRatio); + renderer.render(scene, camera); + self.$el.find('.o_content').html(renderer.domElement); + self.$el.find('.o_content').append(select); + self.$el.find('.o_content').append(colorDiv); + self.$el.find('.o_content').append(closeDiv); + var dropdown = document.querySelector(".customselect"); + if (dropdown) { + dropdown.addEventListener("change", warehouseChange); + } + var closeBtn = document.querySelector(".closeBtn"); + if (closeBtn) { + closeBtn.addEventListener("click", onWindowClose); + } + controls = new THREE.OrbitControls(camera, renderer.domElement); + const baseGeometry = new THREE.BoxGeometry(800, 0, 800); + const baseMaterial = new THREE.MeshBasicMaterial({ + color: 0xffffff, + transparent: false, + opacity: 1, + side: THREE.BackSide, + }); + const baseCube = new THREE.Mesh(baseGeometry, baseMaterial); + scene.add(baseCube); + group = new THREE.Group(); + //traversing through each location object + for (let [key, value] of Object.entries(data)) { + //checks if the dimension values of the location are non zero + if ((value[0] > 0) || (value[1] > 0) || (value[2] > 0) || (value[3] > 0) || (value[4] > 0) || (value[5] > 0)) { + const geometry = new THREE.BoxGeometry(value[3], value[5], value[4]); + geometry.translate(0, (value[5] / 2), 0); + const edges = new THREE.EdgesGeometry(geometry); + /** + * Make a jsonRpc call to fetch the stock details of particular location. + * + * @await + * @param {integer} loc_code + */ + await ajax.jsonRpc('/3Dstock/data/quantity', 'call', { + 'loc_code': key, + }).then(function(quant_data) { + loc_quant = quant_data; + }); + //checking the quantity and capacity of the location to determine the color of the location + if (loc_quant[0] > 0) { + if (loc_quant[1] > 100) { + loc_color = 0xcc0000; + loc_opacity = 0.8; + } else if (loc_quant[1] > 50) { + loc_color = 0xe6b800; + loc_opacity = 0.8; + } else { + loc_color = 0x00802b; + loc_opacity = 0.8; + } + } else { + if (loc_quant[1] == -1) { + loc_color = 0x00802b; + loc_opacity = 0.8; + } else { + loc_color = 0x8c8c8c; + loc_opacity = 0.5; + } + } + //creating a 3D box geometry for each location + material = new THREE.MeshBasicMaterial({ + color: loc_color, + transparent: true, + opacity: loc_opacity + }); + mesh = new THREE.Mesh(geometry, material); + const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ + color: 0x404040 + })); + line.position.x = value[0]; + line.position.y = value[1]; + line.position.z = value[2]; + mesh.position.x = value[0]; + mesh.position.y = value[1]; + mesh.position.z = value[2]; + const loader = new THREE.FontLoader(); + loader.load('https://threejs.org/examples/fonts/droid/droid_sans_bold.typeface.json', function(font) { + const textcolor = 0x000000; + const textMat = new THREE.MeshBasicMaterial({ + color: textcolor, + side: THREE.DoubleSide, + }); + const textmessage = key; + if (value[3] > value[4]) { + textSize = (value[4] / 2) - (value[4] / 2.9); + } else { + textSize = (value[3] / 2) - (value[3] / 2.9); + } + const textshapes = font.generateShapes(textmessage, textSize); + const textgeometry = new THREE.ShapeGeometry(textshapes); + textgeometry.translate(0, ((value[5] / 2) - (textSize / (textSize - 1.5))), 0); + const text = new THREE.Mesh(textgeometry, textMat); + if (value[4] > value[3]) { + text.rotation.y = Math.PI / 2; + text.position.x = value[0]; + text.position.y = value[1]; + text.position.z = value[2] + (textSize * 2) + ((value[3] / 3.779 / 2) / 2) + (textSize / 2); + } else { + text.position.x = value[0] - (textSize * 2) - ((value[4] / 3.779 / 2) / 2) - (textSize / 2); + text.position.y = value[1]; + text.position.z = value[2]; + } + scene.add(text); + }); + scene.add(mesh); + scene.add(line); + mesh.name = key; + mesh.userData = { + color: loc_color + }; + group.add(mesh); + } + } + scene.add(group); + raycaster = new THREE.Raycaster(); + pointer = new THREE.Vector3(); + + animate(); + } + /** + * Triggered when users change warehouse and calls the start() function with the latest warehouse id. + */ + function warehouseChange() { + console.log('111111111111111111111111111111111') + var selectedBox = document.querySelector(".customselect"); + var selectValue = selectedBox.value; + wh_id = selectValue; + start(); + } + /** + * Handles the resizing and setting the pixel ration on window resize. + */ + function onWindowResize() { + camera.aspect = window.innerWidth / window.innerHeight; + camera.updateProjectionMatrix(); + renderer.setSize(window.innerWidth, window.innerHeight / 1.164); + } + /** + * Triggered when user clicks on close button. + */ + function onWindowClose() { + window.location.reload(); + } + /** + * Animates and renders the three.renderer object to make changes on the scene. + */ + function animate() { + requestAnimationFrame(animate); + const delta = clock.getDelta(); + renderer.render(scene, camera); + var canvas = document.getElementsByTagName("canvas")[0]; + var selectedBox = document.querySelector(".customselect"); + var colorBox = document.querySelector(".rectangle"); + var closeDiv = document.querySelector(".closeBtn"); + //checking the canvas element adding event listener on canvas. + if (canvas == null) { + window.removeEventListener('dblclick', onPointerMove); + window.removeEventListener('resize', onWindowResize); + if (selectedBox) { + selectedBox.style.display = "none"; + } + if (colorBox) { + colorBox.style.display = "none"; + } + if (closeDiv) { + closeDiv.style.display = "none"; + } + } else { + window.addEventListener('dblclick', onPointerMove); + window.addEventListener('resize', onWindowResize); + if (selectedBox) { + selectedBox.style.display = "block"; + } + if (colorBox) { + colorBox.style.display = "block"; + } + if (closeDiv) { + closeDiv.style.display = "block"; + } + } + } + /** + * gets the coordinates of the mouse point and checks for any objects. + * + * @async + * @param {object} event + */ + async function onPointerMove(event) { + var products; + var button; + if (dialogs == null) { + if (selectedObject) { + selectedObject.material.color.set(selectedObject.userData.color); + selectedObject = null; + } else { + pointer.x = (event.clientX / window.innerWidth) * 2 - 1; + pointer.y = -(event.clientY / (window.innerHeight)) * 2 + 1 + 0.13; + raycaster.setFromCamera(pointer, camera); + const intersects = raycaster.intersectObject(group, true); + if (intersects.length > 0) { + const res = intersects.filter(function(res) { + return res && res.object; + })[0]; + if (res && res.object) { + /** + * Make a jsonRpc call to fetch the details of products and their quantity of selected location. + * + * @await + * @param {integer} loc_code + */ + await ajax.jsonRpc('/3Dstock/data/product', 'call', { + 'loc_code': res.object.name, + }).then(function(product_data) { + products = product_data; + }); + selectedObject = res.object; + selectedObject.material.color.set(0x00ffcc); + + function onClickClose() { + if (selectedObject) { + selectedObject.material.color.set(selectedObject.userData.color); + selectedObject = null; + dialogs.close(); + dialogs = null; + } + } + //opens a new dialogbox with proeduct and quantity details + dialogs = new PositionDialog(this, { + title: ('Location: ' + res.object.name), + size: 'small', + $content: $(QWeb.render('ViewLocationData', { + data: products, + })), + placement: 'bottom', + renderFooter: false, + pointer: { + x: event.clientX, + y: event.clientY, + }, + close: onClickClose, + }).open(); + + if (dialogs) { + window.addEventListener('click', onClickClose); + } else { + window.removeEventListener('click', onClickClose); + } + } + } + } + } + } + } + }); + var StockLocationTreeView = ListView.extend({ + config: _.extend({}, ListView.prototype.config, { + Controller: TreeButton, + }), + }); + viewRegistry.add('3d_button_in_stock', StockLocationTreeView); +}); \ No newline at end of file diff --git a/stock_3d_view/static/src/xml/stock_location_3d_templates.xml b/stock_3d_view/static/src/xml/stock_location_3d_templates.xml new file mode 100644 index 000000000..388cf9822 --- /dev/null +++ b/stock_3d_view/static/src/xml/stock_location_3d_templates.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml b/stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml new file mode 100644 index 000000000..fb482a64c --- /dev/null +++ b/stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml @@ -0,0 +1,30 @@ + + diff --git a/stock_3d_view/static/src/xml/stock_location_modal_templates.xml b/stock_3d_view/static/src/xml/stock_location_modal_templates.xml new file mode 100644 index 000000000..c897fa0fd --- /dev/null +++ b/stock_3d_view/static/src/xml/stock_location_modal_templates.xml @@ -0,0 +1,41 @@ + + + +
+ +
Total Capacity: Units
+
Available Space: Units
+
+ + + + + + + + + + + + + + + + + + + + +
#ProductQuantity
+ + + + + +
+
+ +
No Products Stored Here..!
+
+
+
diff --git a/stock_3d_view/views/stock_location_views.xml b/stock_3d_view/views/stock_location_views.xml new file mode 100644 index 000000000..537fea4a2 --- /dev/null +++ b/stock_3d_view/views/stock_location_views.xml @@ -0,0 +1,75 @@ + + + + stock.location.view.form.inherit.stock_3d_view + + stock.location + + + + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + + Location 3D View + open_form_3d_view + + + + + stock.location.view.tree.inherit.stock_3d_view + + stock.location + + + + 3d_button_in_stock + + + + +