Browse Source

Oct 31 : [ADD] Initial Commit 'stock_3d_view'

pull/254/merge
RisvanaCybro 2 years ago
parent
commit
168f860a61
  1. 46
      stock_3d_view/README.rst
  2. 23
      stock_3d_view/__init__.py
  3. 63
      stock_3d_view/__manifest__.py
  4. 22
      stock_3d_view/controllers/__init__.py
  5. 170
      stock_3d_view/controllers/stock_3d_view.py
  6. 6
      stock_3d_view/doc/RELEASE_NOTES.md
  7. 22
      stock_3d_view/models/__init__.py
  8. 67
      stock_3d_view/models/stock_location.py
  9. BIN
      stock_3d_view/static/description/assets/icons/check.png
  10. BIN
      stock_3d_view/static/description/assets/icons/chevron.png
  11. BIN
      stock_3d_view/static/description/assets/icons/cogs.png
  12. BIN
      stock_3d_view/static/description/assets/icons/consultation.png
  13. BIN
      stock_3d_view/static/description/assets/icons/ecom-black.png
  14. BIN
      stock_3d_view/static/description/assets/icons/education-black.png
  15. BIN
      stock_3d_view/static/description/assets/icons/hotel-black.png
  16. BIN
      stock_3d_view/static/description/assets/icons/license.png
  17. BIN
      stock_3d_view/static/description/assets/icons/lifebuoy.png
  18. BIN
      stock_3d_view/static/description/assets/icons/logo.png
  19. BIN
      stock_3d_view/static/description/assets/icons/manufacturing-black.png
  20. BIN
      stock_3d_view/static/description/assets/icons/pos-black.png
  21. BIN
      stock_3d_view/static/description/assets/icons/puzzle.png
  22. BIN
      stock_3d_view/static/description/assets/icons/restaurant-black.png
  23. BIN
      stock_3d_view/static/description/assets/icons/service-black.png
  24. BIN
      stock_3d_view/static/description/assets/icons/trading-black.png
  25. BIN
      stock_3d_view/static/description/assets/icons/training.png
  26. BIN
      stock_3d_view/static/description/assets/icons/update.png
  27. BIN
      stock_3d_view/static/description/assets/icons/user.png
  28. BIN
      stock_3d_view/static/description/assets/icons/wrench.png
  29. BIN
      stock_3d_view/static/description/assets/modules/module_1.png
  30. BIN
      stock_3d_view/static/description/assets/modules/module_2.png
  31. BIN
      stock_3d_view/static/description/assets/modules/module_3.png
  32. BIN
      stock_3d_view/static/description/assets/modules/module_4.png
  33. BIN
      stock_3d_view/static/description/assets/modules/module_5.jpeg
  34. BIN
      stock_3d_view/static/description/assets/modules/module_6.png
  35. BIN
      stock_3d_view/static/description/assets/screenshots/1.png
  36. BIN
      stock_3d_view/static/description/assets/screenshots/2.png
  37. BIN
      stock_3d_view/static/description/assets/screenshots/3.png
  38. BIN
      stock_3d_view/static/description/assets/screenshots/4.png
  39. BIN
      stock_3d_view/static/description/assets/screenshots/5.png
  40. BIN
      stock_3d_view/static/description/assets/screenshots/66.png
  41. BIN
      stock_3d_view/static/description/assets/screenshots/77.png
  42. BIN
      stock_3d_view/static/description/assets/screenshots/hero.gif
  43. BIN
      stock_3d_view/static/description/banner.png
  44. BIN
      stock_3d_view/static/description/icon.png
  45. 806
      stock_3d_view/static/description/index.html
  46. 125
      stock_3d_view/static/src/css/3d_view.scss
  47. 387
      stock_3d_view/static/src/js/form_3d_view.js
  48. 412
      stock_3d_view/static/src/js/listview_3d.js
  49. 12
      stock_3d_view/static/src/xml/stock_location_3d_templates.xml
  50. 30
      stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml
  51. 41
      stock_3d_view/static/src/xml/stock_location_modal_templates.xml
  52. 75
      stock_3d_view/views/stock_location_views.xml

46
stock_3d_view/README.rst

@ -0,0 +1,46 @@
.. 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 <https://cybrosys.com/>`__
Credits
-------
* Developers: (V16) Prasanna Kumara B, 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 <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

23
stock_3d_view/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import controllers
from . import models

63
stock_3d_view/__manifest__.py

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
{
'name': "Stock 3D View",
'version': '15.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",
],
'web.assets_qweb': [
'stock_3d_view/static/src/xml/stock_location_3d_templates.xml',
'stock_3d_view/static/src/xml/stock_location_modal_templates.xml',
'stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml',
],
},
'images': [
'static/description/banner.png',
],
'license': 'LGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

22
stock_3d_view/controllers/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import stock_3d_view

170
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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import 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

6
stock_3d_view/doc/RELEASE_NOTES.md

@ -0,0 +1,6 @@
## Module <stock_3d_view>
#### 27.09.2023
#### Version 15.0.1.0.0
#### ADD
- Initial commit for Stock 3D View

22
stock_3d_view/models/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import stock_location

67
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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import 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,
}
}

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
stock_3d_view/static/description/assets/modules/module_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
stock_3d_view/static/description/assets/modules/module_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
stock_3d_view/static/description/assets/modules/module_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

BIN
stock_3d_view/static/description/assets/modules/module_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
stock_3d_view/static/description/assets/modules/module_5.jpeg

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
stock_3d_view/static/description/assets/modules/module_6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
stock_3d_view/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
stock_3d_view/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

BIN
stock_3d_view/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

BIN
stock_3d_view/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
stock_3d_view/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

BIN
stock_3d_view/static/description/assets/screenshots/66.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
stock_3d_view/static/description/assets/screenshots/77.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 130 KiB

BIN
stock_3d_view/static/description/assets/screenshots/hero.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

BIN
stock_3d_view/static/description/banner.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
stock_3d_view/static/description/icon.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

806
stock_3d_view/static/description/index.html

@ -0,0 +1,806 @@
<div class="container"
style="padding: 1rem !important; margin-bottom: 1rem !important;">
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12 d-flex justify-content-between"
style="border-bottom: 1px solid #d5d5d5;">
<div class="my-3">
<img src="./assets/icons/logo.png"
style="width: auto !important; height: 40px !important;">
</div>
<div class="my-3 d-flex align-items-center">
<div
style="background-color: #7C7BAD !important; color: #fff !important; font-weight: 600 !important; padding: 5px 15px 8px !important; margin: 0 5px !important;">
<i class="fa fa-check mr-1"></i>Community
</div>
<div
style="background-color: #875A7B !important; color: #fff !important; font-weight: 600 !important; padding: 5px 15px 8px !important; margin: 0 5px !important;">
<i class="fa fa-check mr-1"></i>Enterprise
</div>
<div
style="background-color: #875A7B !important; color: #fff !important; font-weight: 600 !important; padding: 5px 15px 8px !important; margin: 0 5px !important;">
<i class="fa fa-check mr-1"></i>Odoo.sh
</div>
</div>
</div>
</div>
</div>
<div class="container" style="padding: 0rem 1.5rem 4rem !important">
<div class="row" style="height: 900px !important;">
<div class="col-sm-12 col-md-12 col-lg-12"
style="padding: 4rem 1rem !important; background-color: #714B67 !important; height: 600px !important; border-radius: 20px !important;">
<h1
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #FFFFFF !important; font-size: 3.5rem !important; text-align: center !important;">
Stock 3D View
</h1>
<p
style="font-family: 'Montserrat', sans-serif !important; font-weight: 300 !important; color: #FFFFFF !important; font-size: 1.4rem !important; text-align: center !important;">
Virtual 3D Visualization of Warehouses and Locations
</p>
<img src="assets/screenshots/hero.gif" class="img-responsive" width="100%"
height="auto"/>
</div>
</div>
<div class="row">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin-bottom: 2rem !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-compass mr-2"></i>Explore this module
</h2>
</div>
<div class="col-md-6">
<a href="#overview" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Overview</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
Learn more about this module
</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right"
style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
<div class="col-md-6">
<a href="#configuration" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Configuration</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
View configurations for this module
</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right"
style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
<div class="col-md-6">
<a href="#features" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Features</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
View features of this module
</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right"
style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
<div class="col-md-6">
<a href="#screenshots" style="text-decoration: none !important;">
<div class="row"
style="background-color: #f5f2f5 !important; border-radius: 10px !important; margin: 1rem !important; padding: 1.5em !important; height: 100px !important;">
<div class="col-8">
<h3
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.2rem !important;">
Screenshots</h3>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #714B67 !important; font-size: 0.9rem !important;">
See key screenshots of this module
</p>
</div>
<div class="col-4 text-right d-flex justify-content-end align-items-center">
<i class="fa fa-chevron-right"
style="color: #714B67 !important;"></i>
</div>
</div>
</a>
</div>
</div>
<div class="row" id="overview">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-pie-chart mr-2"></i>Overview
</h2>
</div>
<div class="col-mg-12 pl-3">
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important; line-height: 30px !important;">
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.
</p>
</div>
</div>
<div class="row" id="configuration">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-cogs mr-2"></i>Configuration
</h2>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
<i class="fa fa-laptop mr-4"></i>Hardware Requirements</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
RAM : 16GB or Above
</p>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Graphics card : 4 GB additional
</p>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Processor : Intel i5 or above, AMD Ryzen 5 or above
</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
<i class="fa fa-laptop mr-4"></i>Software Requirements</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
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.
</p>
</div>
</div>
</div>
<div class="row" id="features">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-star mr-2"></i>Features
</h2>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Multiple Warehouse Selection</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can view 3d visualization of all locations based
multiple warehouses.
</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Location Specific 3D View</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can view 3d visualization of individual locations as
highlighted in the entire warehouse.
</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Actual Warehouse into Virtual Warehouse</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can enter dimension and position of a Location.
</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Status of each Location</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can quickly identify the status of location like free
space/overloaded through their indexing
based on their load and capacity.
</p>
</div>
</div>
<div class="col-md-6 pl-3 py-3 d-flex">
<div>
<img src="assets/icons/check.png">
</div>
<div>
<h4
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Current Stock of each Location</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can easily get the data about current stock of each
location by tapping twice on them.
</p>
</div>
</div>
</div>
<div class="row" id="screenshots">
<div class="col-md-12"
style="border-bottom: 1px solid #d5d5d5 !important; margin: 2rem 0 !important">
<h2
style="font-family: 'Montserrat', sans-serif !important; font-weight: 600 !important; color: #714B67 !important; font-size: 1.5rem !important;">
<i class="fa fa-image mr-2"></i>Screenshots
</h2>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Location Form View</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Enter the location properties such as location Code, Capacity,
Dimension and Position along X,Y and Z
axis.
</p>
<img src="assets/screenshots/1.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
3D View Based on Warehouses</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Click on the 3D button marked below to open 3D view based on
warehouses.
</p>
<img src="assets/screenshots/2.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Multiple Warehouse Selection</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can switch to any warehouse by using the dropdown option
highlighted below.
</p>
<img src="assets/screenshots/3.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Location Specific 3D View</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Please open a location and provide a proper dimension, position,
code and parent stock. Click on the
button "3D" located inside header.
</p>
<img src="assets/screenshots/4.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Individual Location Highlight</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Users can view the selected location's 3D view individually. The
location is indexed based on its load
and capacity.
</p>
<img src="assets/screenshots/5.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
<div class="col-lg-12 my-2">
<h4 class="mt-2"
style="font-family: 'Roboto', sans-serif !important; font-weight: 600 !important; color: #282F33 !important; font-size: 1.3rem !important;">
Get Stock Data</h4>
<p
style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Double tap on the location to get its stock data.
</p>
<img src="assets/screenshots/66.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
<br/>
<br/>
<img src="assets/screenshots/77.png"
class="img-responsive img-thumbnail border" width="100%"
height="auto"/>
</div>
</div>
<!-- SUGGESTED PRODUCTS -->
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center"
style="text-align: center; padding: 2.5rem 1rem !important;">
<h2 style="color: #212529 !important;">Suggested Products</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
<div id="demo1" class="row carousel slide" data-ride="carousel">
<!-- The slideshow -->
<div class="carousel-inner">
<div class="carousel-item active" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/export_stockinfo_xls/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_1.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/stock_intercompany_transfer/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_2.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/product_brand_inventory/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_3.png">
</div>
</a>
</div>
</div>
<div class="carousel-item" style="min-height:0px">
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/product_batch_report/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_4.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/merge_picking_orders/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_5.png">
</div>
</a>
</div>
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16"
style="float:left">
<a href="https://apps.odoo.com/apps/modules/15.0/stock_valuation_report/"
target="_blank">
<div style="border-radius:10px">
<img class="img img-responsive center-block"
style="border-top-left-radius:10px; border-top-right-radius:10px"
src="./assets/modules/module_6.png">
</div>
</a>
</div>
</div>
</div>
<!-- Left and right controls -->
<a class="carousel-control-prev" href="#demo1" data-slide="prev"
style="left:-25px;width: 35px;color: #000;">
<span class="carousel-control-prev-icon"><i
class="fa fa-chevron-left"
style="font-size:24px"></i></span>
</a>
<a class="carousel-control-next" href="#demo1" data-slide="next"
style="right:-25px;width: 35px;color: #000;">
<span class="carousel-control-next-icon"><i
class="fa fa-chevron-right"
style="font-size:24px"></i></span>
</a>
</div>
</div>
</div>
<!-- END OF SUGGESTED PRODUCTS -->
<!-- OUR SERVICES -->
<section class="container" style="margin-top: 6rem !important;">
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Our Services</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/cogs.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Customization
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/wrench.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Implementation
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/lifebuoy.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Support
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/user.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Hire Odoo Developer
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/puzzle.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Integration
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/update.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Migration
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/consultation.png"
class="img-responsive" height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo
Consultancy
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/training.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Implementation
</h6>
</div>
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4">
<div class="d-flex justify-content-center align-items-center mx-3 my-3"
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;">
<img src="assets/icons/license.png" class="img-responsive"
height="48px" width="48px">
</div>
<h6 class="text-center"
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;">
Odoo Licensing Consultancy
</h6>
</div>
</div>
</section>
<!-- END OF OUR SERVICES -->
<!-- OUR INDUSTRIES -->
<section class="container" style="margin-top: 6rem !important;">
<div class="row">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Our Industries</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/trading-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Trading
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easily procure and sell your products
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/pos-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
POS
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Easy configuration and convivial experience
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/education-black.png"
class="img-responsive mb-3" height="48px"
width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Education
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
A platform for educational management
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/manufacturing-black.png"
class="img-responsive mb-3" height="48px"
width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Manufacturing
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Plan, track and schedule your operations
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/ecom-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
E-commerce &amp; Website
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Mobile friendly, awe-inspiring product pages
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/service-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Service Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Keep track of services and invoice
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/restaurant-black.png"
class="img-responsive mb-3" height="48px"
width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Restaurant
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
Run your bar or restaurant methodically
</p>
</div>
</div>
<div class="col-lg-3">
<div class="my-4 d-flex flex-column justify-content-center"
style="background-color: #f6f8f9 !important; border-radius: 10px; padding: 2rem !important; height: 250px !important;">
<img src="./assets/icons/hotel-black.png"
class="img-responsive mb-3" height="48px" width="48px">
<h5
style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;">
Hotel Management
</h5>
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;">
An all-inclusive hotel management application
</p>
</div>
</div>
</div>
</section>
<!-- END OF OUR INDUSTRIES -->
<!-- FOOTER -->
<!-- Footer Section -->
<section class="container" style="margin: 5rem auto 2rem;">
<div class="row" style="max-width:1540px;">
<div class="col-lg-12 d-flex flex-column justify-content-center align-items-center">
<h2 style="color: #212529 !important;">Need Help?</h2>
<hr
style="border: 3px solid #714B67 !important; background-color: #714B67 !important; width: 80px !important; margin-bottom: 2rem !important;"/>
</div>
</div>
<!-- Contact Cards -->
<div class="row d-flex justify-content-center align-items-center"
style="max-width:1540px; margin: 0 auto 2rem auto;">
<div class="col-lg-12"
style="padding: 0rem 3rem 2rem; border-radius: 10px; margin-right: 3rem; ">
<div class="row mt-4">
<div class="col-lg-6">
<a href="mailto:odoo@cybrosys.com" target="_blank"
class="btn btn-block mb-2 deep_hover"
style="text-decoration: none; background-color: #4d4d4d; color: #FFF; border-radius: 4px;">
<i class="fa fa-envelope mr-2"></i>odoo@cybrosys.com
</a>
</div>
<div class="col-lg-6">
<a href="https://api.whatsapp.com/send?phone=918606827707"
target="_blank"
class="btn btn-block mb-2 deep_hover"
style="text-decoration: none; background-color: #25D366; color: #FFF; border-radius: 4px;">
<i class="fa fa-whatsapp mr-2"></i>WhatsApp
</a>
</div>
</div>
</div>
</div>
<!-- End of Contact Cards -->
</section>
<!-- Footer -->
<section class="oe_container" style="padding: 2rem 3rem 1rem;">
<div class="row"
style="max-width:1540px; margin: 0 auto; margin-right: 3rem; ">
<!-- Logo -->
<div class="col-lg-12 d-flex justify-content-center align-items-center"
style="margin-top: 3rem;">
<img src="https://www.cybrosys.com/images/logo.png"
width="200px" height="auto"/>
</div>
<!-- End of Logo -->
<div class="col-lg-12">
<hr
style="margin-top: 3rem;background: linear-gradient(90deg, rgba(2,0,36,0) 0%, rgba(229,229,229,1) 33%, rgba(229,229,229,1) 58%, rgba(0,212,255,0) 100%); height: 2px; border-style: none;">
<!-- End of Footer Section -->
</div>
</div>
</section>
<!-- END OF FOOTER -->
</div>

125
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);
}

387
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;
});

412
stock_3d_view/static/src/js/listview_3d.js

@ -0,0 +1,412 @@
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.buttons',
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 = "&times;"
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() {
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);
});

12
stock_3d_view/static/src/xml/stock_location_3d_templates.xml

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<!--extending listview button template to add 3D action button-->
<t t-extend="ListView.buttons" t-name="StockLocationListView.buttons">
<t t-jquery="button.o_list_button_add" t-operation="after">
<button type="button" class="btn btn-primary fa fa-lg fa-cubes open_3d_view"
style="margin-left: 3px; margin-right: 3px;">
3D
</button>
</t>
</t>
</templates>

30
stock_3d_view/static/src/xml/stock_location_breadcrumb_templates.xml

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<template>
<!--adding breadcrumbs manually in stock.location form view.-->
<t t-name="Location3DFormView">
<div class="o_action">
<div class="o_control_panel">
<div class="o_cp_top">
<div class="o_cp_top_left">
<t t-slot="control-panel-top-left">
<ol class="breadcrumb">
<t t-foreach="widget.breadcrumbs" t-as="breadcrumb">
<li class="breadcrumb-item">
<a href="#" t-att-jsId="breadcrumb.jsId">
<t t-esc="breadcrumb.name"/>
</a>
</li>
</t>
<li class="breadcrumb-item active">
<span class="text-muted">3D view</span>
</li>
</ol>
</t>
</div>
</div>
<div class="o_cp_bottom"/>
</div>
<div class="o_content"/>
</div>
</t>
</template>

41
stock_3d_view/static/src/xml/stock_location_modal_templates.xml

@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" ?>
<templates xml:space="preserve">
<!--template to display location details such as product and its quantity-->
<div t-name="ViewLocationData">
<t t-if="data['capacity'] > 0">
<center><h6>Total Capacity: <span t-esc="data['capacity']"/> Units</h6></center>
<center><h6>Available Space: <span t-esc="data['space']"/> Units</h6></center>
</t>
<t t-if="data['product_list'].length > 0">
<table>
<thead>
<tr>
<th>#</th>
<th style="padding-left: 10px;">Product</th>
<th style="padding-left: 10px;">Quantity</th>
</tr>
</thead>
<tbody>
<t t-set="num" t-value="1"/>
<t t-foreach="data['product_list']" t-as="i">
<tr>
<td>
<span t-esc="num"/>
</td>
<td style="padding-left: 10px;">
<span t-esc="i['0']"/>
</td>
<td style="padding-left: 10px;">
<span t-esc="i['1']"/>
</td>
</tr>
<t t-set="num" t-value="num + 1"/>
</t>
</tbody>
</table>
</t>
<t t-else="">
<center><span>No Products Stored Here..!</span></center>
</t>
</div>
</templates>

75
stock_3d_view/views/stock_location_views.xml

@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_location_form" model="ir.ui.view">
<field name="name">stock.location.view.form.inherit.stock_3d_view
</field>
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_form"/>
<field name="arch" type="xml">
<!--adding a 3D button in stock.location form view.-->
<xpath expr="//sheet" position="before">
<header>
<button type="object" name="action_view_location_3d_button"
string="3D" id="view_location_3d_button"
class="btn btn-primary fa fa-lg fa-cubes"
attrs="{'invisible':['|',('location_id', '=', False),('length','=',0),('width','=',0)]}"/>
</header>
</xpath>
<!--adding location dimension fields in stock.location form view.-->
<xpath expr="//sheet" position="inside">
<notebook>
<page string="3D Properties"
attrs="{'invisible': [('usage', '!=', 'internal')]}">
<group>
<group string="Dimension">
<field name="length"
attrs="{'required': [('usage', '=', 'internal')]}"/>
<field name="width"
attrs="{'required': [('usage', '=', 'internal')]}"/>
<field name="height"
attrs="{'required': [('usage', '=', 'internal')]}"/>
</group>
<group string="Position">
<field name="pos_x"
attrs="{'required': [('usage', '=', 'internal')]}"/>
<field name="pos_y"
attrs="{'required': [('usage', '=', 'internal')]}"/>
<field name="pos_z"
attrs="{'required': [('usage', '=', 'internal')]}"/>
</group>
</group>
</page>
</notebook>
</xpath>
<!--adding unique code field of the location inside the stock.location form view.-->
<xpath expr="//field[@name='usage']" position="after">
<field name="unique_code"
attrs="{'invisible': [('usage', '!=', 'internal')], 'required': [('usage', '=', 'internal')]}"/>
</xpath>
<!--adding maximum capacity field of the location inside the stock.location form view.-->
<xpath expr="//field[@name='storage_category_id']" position="after">
<field name="max_capacity"
attrs="{'invisible': [('usage', '!=', 'internal')]}"/>
</xpath>
</field>
</record>
<!--client action for 3d view of locations.-->
<record id="stock_location_3d_action" model="ir.actions.client">
<field name="name">Location 3D View</field>
<field name="tag">open_form_3d_view</field>
</record>
<data>
<!--adding button to open 3d view in stock.location tree view-->
<record id="view_location_tree2" model="ir.ui.view">
<field name="name">stock.location.view.tree.inherit.stock_3d_view
</field>
<field name="model">stock.location</field>
<field name="inherit_id" ref="stock.view_location_tree2"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="js_class">3d_button_in_stock</attribute>
</xpath>
</field>
</record>
</data>
</odoo>
Loading…
Cancel
Save