@ -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>`__ |
@ -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 |
@ -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, |
||||
|
} |
@ -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 |
@ -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 |
@ -0,0 +1,6 @@ |
|||||
|
## Module <stock_3d_view> |
||||
|
|
||||
|
#### 27.09.2023 |
||||
|
#### Version 15.0.1.0.0 |
||||
|
#### ADD |
||||
|
- Initial commit for Stock 3D View |
@ -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 |
@ -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, |
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 159 KiB |
After Width: | Height: | Size: 195 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 130 KiB |
After Width: | Height: | Size: 194 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 8.2 KiB |
@ -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 & 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> |
@ -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); |
||||
|
} |
@ -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; |
||||
|
}); |
@ -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 = "×" |
||||
|
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); |
||||
|
}); |
@ -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> |
@ -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> |
@ -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> |
@ -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> |