@ -0,0 +1,45 @@ |
|||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
Product Expiry Dashboard |
|||
======================== |
|||
This Module help to analyse products and their expiry |
|||
|
|||
Configuration |
|||
============= |
|||
* No configuration needed |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
Credits |
|||
------- |
|||
* Developer: (V16) Hafeesul Ali, Kailas Krishna |
|||
Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (AGPL v3). |
|||
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
|||
|
|||
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,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-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 AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import models |
@ -0,0 +1,48 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-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 AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
'name': 'Product Expiry Dashboard', |
|||
'version': '16.0.1.0.0', |
|||
'category': 'Productivity', |
|||
'summary': 'This module provides visualized product expiry data', |
|||
'description': 'This module presents visualized charts showcasing ' |
|||
'product expiration data categorized by location and ' |
|||
'category', |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['product_expiry', 'stock'], |
|||
'data': ['views/product_expiry_views.xml'], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'odoo_product_expiry_dashboard/static/src/js/product_expiry_action.js', |
|||
'odoo_product_expiry_dashboard/static/src/xml/product_expiry_dashboard.xml', |
|||
'odoo_product_expiry_dashboard/static/src/css/style.css', |
|||
'https://cdn.jsdelivr.net/npm/chart.js' |
|||
]}, |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': 'AGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False |
|||
} |
@ -0,0 +1,6 @@ |
|||
## Module <odoo_product_expiry_dashboard> |
|||
|
|||
#### 17.08.2024 |
|||
#### Version 16.0.1.0.0 |
|||
#### ADD |
|||
- Initial commit for Product Expiry Dashboard |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-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 AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import stock_lot |
@ -0,0 +1,304 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-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 AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class StockLot(models.Model): |
|||
_inherit = "stock.lot" |
|||
|
|||
@api.model |
|||
def search_params(self, start_date, end_date, enabled_companies): |
|||
"""Method to get domain for searching.""" |
|||
if start_date and end_date: |
|||
search_params = [('expiration_date', '>=', start_date), |
|||
('expiration_date', '<=', end_date), |
|||
('company_id', 'in', enabled_companies)] |
|||
elif start_date: |
|||
search_params = [('expiration_date', '>=', start_date), |
|||
('company_id', 'in', enabled_companies)] |
|||
else: |
|||
search_params = [('company_id', 'in', enabled_companies)] |
|||
return search_params |
|||
|
|||
@api.model |
|||
def get_product_expiry(self, *args): |
|||
"""Method to get products that expires in 1 day ,7 days,30 days |
|||
and 120 days. |
|||
Args: |
|||
*args(dict):Start date and End date to add filtration |
|||
Returns: |
|||
dict: A dict contains 1 day ,7 day ,30 day,120 day, and their |
|||
respective counts of products that will expire. |
|||
""" |
|||
data = [{"one_day": [], "counts": 0}, |
|||
{"seven_day": [], "counts": 0}, |
|||
{"thirty_day": [], "counts": 0}, |
|||
{"one_twenty_day": [], "counts": 0}] |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date'), args[1]) |
|||
for record in self.search(search_params): |
|||
if record.expiration_date and record.product_qty != 0: |
|||
date_difference = (fields.Date.to_date( |
|||
record.expiration_date) - fields.Date.today()).days |
|||
if date_difference == 1: |
|||
data[0]["one_day"].append(record.id) |
|||
data[0]["counts"] += record.product_qty |
|||
elif 7 >= date_difference >= 1: |
|||
data[1]["seven_day"].append(record.id) |
|||
data[1]["counts"] += record.product_qty |
|||
elif 30 >= date_difference > 7: |
|||
data[2]["thirty_day"].append(record.id) |
|||
data[2]["counts"] += record.product_qty |
|||
elif 120 >= date_difference > 30: |
|||
data[3]["one_twenty_day"].append(record.id) |
|||
return data |
|||
|
|||
@api.model |
|||
def get_product_expired_today(self, enabled_companies): |
|||
""" |
|||
Method to get products that expired today |
|||
Returns: |
|||
int:count of products. |
|||
""" |
|||
count = len([record for record in self.search([]) if |
|||
record.expiration_date and |
|||
fields.Date.to_date(record.expiration_date) == fields.date.today() and |
|||
record.company_id.id in enabled_companies]) |
|||
return count |
|||
|
|||
@api.model |
|||
def get_expired_product(self, *args): |
|||
""" |
|||
Method to get products that expired |
|||
Args: |
|||
*args(dict):Start date and End date to add filtration. |
|||
Returns: |
|||
dict: A dict that contains expired products and their count. |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
products_dict = {record: {'product_qty': record.product_qty, |
|||
'product': record.product_id.name} |
|||
for record in self.search(search_params) |
|||
if record.expiration_date and record.product_qty != 0 |
|||
and record.product_expiry_alert} |
|||
expired_products_dict = {product: sum(data['product_qty'] |
|||
for lot, data in |
|||
products_dict.items() if |
|||
data['product'] == product) |
|||
for product in set( |
|||
data['product'] for lot, data in products_dict.items())} |
|||
return expired_products_dict |
|||
|
|||
@api.model |
|||
def get_product_expiry_by_category(self, *args): |
|||
""" |
|||
Method to get category of products that expired. |
|||
Args: |
|||
*args(dict):Start date and End date to add filtration. |
|||
Returns: |
|||
dict: A dict that contains expired products category and their |
|||
count. |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
products_category_dict = {record: {'product_qty': record.product_qty, |
|||
'product_category': |
|||
record.product_id.categ_id.name} |
|||
for record in self.search(search_params) |
|||
if record.expiration_date and |
|||
record.product_qty != 0 |
|||
and record.product_expiry_alert} |
|||
expired_product_category_dict = {product: sum(data['product_qty'] |
|||
for lot, data in |
|||
products_category_dict. |
|||
items() if |
|||
data['product_category'] |
|||
== product) |
|||
for product in |
|||
set(data['product_category'] |
|||
for lot, data in |
|||
products_category_dict.items())} |
|||
return expired_product_category_dict |
|||
|
|||
@api.model |
|||
def get_near_expiry_product(self, *args): |
|||
""" |
|||
Method to get products that will expire in coming 7 days. |
|||
Returns: |
|||
dict:A dict that contains products and their count |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
if len(search_params) != 0: |
|||
product_dict = {record: {'product_name': record.product_id.name, |
|||
'product_qty': record.product_qty |
|||
} |
|||
for record in self.search(search_params) |
|||
if |
|||
record.expiration_date and record.product_qty != 0} |
|||
|
|||
else: |
|||
product_dict = {record: {'product_name': record.product_id.name, |
|||
'product_qty': record.product_qty |
|||
} |
|||
for record in self.search([]) |
|||
if |
|||
record.expiration_date and record.product_qty and 7 >= |
|||
(fields.Date.to_date(record.expiration_date) - |
|||
fields.Date.today()).days > 0} |
|||
nearby_exp_products_dict = {product: sum( |
|||
data['product_qty'] for lot, data in product_dict.items() if |
|||
data['product_name'] == product) |
|||
for product in |
|||
set(data['product_name'] for lot, data in |
|||
product_dict.items()) |
|||
} |
|||
return nearby_exp_products_dict |
|||
|
|||
@api.model |
|||
def get_near_expiry_category(self, *args): |
|||
""" |
|||
Method to get cate'gory of products that will expire in coming 7 days. |
|||
Returns: |
|||
dict:A dict that contains category and their count |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
if len(search_params) != 0: |
|||
product_dict = { |
|||
record: {'category': record.product_id.categ_id.name, |
|||
'product_qty': record.product_qty |
|||
} |
|||
for record in self.search(search_params) |
|||
if |
|||
record.expiration_date and record.product_qty != 0} |
|||
else: |
|||
product_dict = {record: {'category': record.product_id.categ_id.name, |
|||
'product_qty': record.product_qty |
|||
} |
|||
for record in self.search([]) |
|||
if |
|||
record.expiration_date and record.product_qty and 7 >= |
|||
(fields.Date.to_date(record.expiration_date) - |
|||
fields.Date.today()).days > 0} |
|||
|
|||
nearby_exp_products_dict = {product: sum( |
|||
data['product_qty'] for lot, data in product_dict.items() if |
|||
data['category'] == product) |
|||
for product in |
|||
set(data['category'] for lot, data in |
|||
product_dict.items()) |
|||
} |
|||
return nearby_exp_products_dict |
|||
|
|||
@api.model |
|||
def get_expire_product_location(self, *args): |
|||
"""Method to get products locations that will expire |
|||
in coming 7 days |
|||
Returns: |
|||
dict:A dict of location and their respective count |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
if len(search_params) != 0: |
|||
location_dict = { |
|||
record: {'location': location.location_id.display_name, |
|||
'count': location.inventory_quantity_auto_apply} for |
|||
record in self.search(search_params) if |
|||
record.expiration_date and record.product_qty != 0 |
|||
for location in record.quant_ids if |
|||
location.inventory_quantity_auto_apply > 0} |
|||
else: |
|||
location_dict = { |
|||
record: {'location': location.location_id.display_name, |
|||
'count': location.inventory_quantity_auto_apply} for |
|||
record in self.search([]) if |
|||
record.expiration_date and record.product_qty and 7 >= ( |
|||
fields.Date.to_date(record.expiration_date) - fields.Date.today()).days > 0 |
|||
for location in record.quant_ids if |
|||
location.inventory_quantity_auto_apply > 0} |
|||
nearby_expiry_location = {product: sum( |
|||
data['count'] for lot, data in location_dict.items() if |
|||
data['location'] == product) for product in set( |
|||
data['location'] for lot, data in location_dict.items())} |
|||
return nearby_expiry_location |
|||
|
|||
@api.model |
|||
def get_expire_product_warehouse(self, *args): |
|||
"""Method to get products warehouse that will expire |
|||
in coming 7 days |
|||
Returns: |
|||
dict:A dict of warehouse and their respective counts. |
|||
""" |
|||
search_params = self.search_params(args[0].get('start_date'), |
|||
args[0].get('end_date')) |
|||
if len(search_params) != 0: |
|||
warehouse_dict = { |
|||
record: { |
|||
'warehouse': location.location_id.warehouse_id.display_name, |
|||
'count': location.inventory_quantity_auto_apply} for |
|||
record in self.search([]) if |
|||
record.expiration_date and record.product_qty != 0 |
|||
for location in record.quant_ids if |
|||
location.inventory_quantity_auto_apply > 0} |
|||
else: |
|||
warehouse_dict = { |
|||
record: { |
|||
'warehouse': location.location_id.warehouse_id.display_name, |
|||
'count': location.inventory_quantity_auto_apply} for |
|||
record in self.search([]) if |
|||
record.expiration_date and record.product_qty and 7 >= ( |
|||
fields.Date.to_date( |
|||
record.expiration_date) - fields.Date.today()).days > 0 |
|||
for location in record.quant_ids if |
|||
location.inventory_quantity_auto_apply > 0} |
|||
nearby_expiry_warehouse = {product: sum( |
|||
data['count'] for lot, data in warehouse_dict.items() if |
|||
data['warehouse'] == product) for product in set( |
|||
data['warehouse'] for lot, data in warehouse_dict.items())} |
|||
return nearby_expiry_warehouse |
|||
|
|||
@api.model |
|||
def get_today_expire(self): |
|||
"""Method to get products that will expire current day |
|||
Returns: |
|||
A dict which includes list of products id, |
|||
list of products name, list of products quantity, and |
|||
list of products category. |
|||
""" |
|||
data = {'id': [], 'name': [], 'qty': [], 'categ': []} |
|||
for line in self.search([]): |
|||
if line.expiration_date and line.expiration_date.date() == fields.date.today(): |
|||
data['id'].append(line.id) |
|||
data['name'].append(line.product_id.name) |
|||
data['qty'].append(line.product_qty) |
|||
found_category = False |
|||
for rec in data['categ']: |
|||
for key, value in rec.items(): |
|||
if line.product_id.categ_id.name == key: |
|||
rec[key] += line.product_qty |
|||
found_category = True |
|||
break |
|||
if not found_category: |
|||
data['categ'].append({line.product_id.categ_id.name: line.product_qty}) |
|||
return data |
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: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 84 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 122 KiB |
After Width: | Height: | Size: 185 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 182 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 187 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 116 KiB |
After Width: | Height: | Size: 301 KiB |
After Width: | Height: | Size: 97 KiB |
After Width: | Height: | Size: 11 KiB |
@ -0,0 +1,631 @@ |
|||
<div style="background-color: #714B67; min-height: 600px; width: 100%; padding: 15px; position: relative;"> |
|||
<!-- TITLE BAR --> |
|||
<div |
|||
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;"> |
|||
<img src="assets/misc/cybrosys-logo.png" width="42" height="42" style="width: 42px; height: 42px;"/> |
|||
<div> |
|||
<div style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Community |
|||
</div> |
|||
<div style="color: #875A7B; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Enterprise |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF TITLE BAR --> |
|||
|
|||
<!-- APP HERO --> |
|||
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;">Product Expiry Dashboard</h1> |
|||
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;">Allows To View All Product Details Those Are About To Expire.</p> |
|||
<!-- END OF APP HERO --> |
|||
<img src="./assets/screenshots/hero.gif" |
|||
style="width: 75%; height: auto; position: absolute; margin-left: auto; margin-right: auto; top: 45%; left: 12%; right: auto;"/> |
|||
</div> |
|||
|
|||
<!-- NAVIGATION SECTION --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/compass.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Explore This |
|||
Module</h2> |
|||
</div> |
|||
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;"> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#overview"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span> |
|||
<span |
|||
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn |
|||
more about this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#features"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span> |
|||
<span |
|||
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
features of this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#screenshots"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span> |
|||
<span |
|||
style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
screenshots for this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<!-- END OF NAVIGATION SECTION --> |
|||
|
|||
<!-- OVERVIEW SECTION --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pie-chart.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Overview |
|||
</h2> |
|||
</div> |
|||
<div class="row" style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 py-4"> |
|||
The Product Expiry Dashboard provides information on the number of |
|||
expired and nearly expiring products. It also offers a graphical view of |
|||
these products, along with their respective locations and warehouses. |
|||
This application facilitates swift decision-making by allowing users to |
|||
monitor items that are approaching their expiration dates. |
|||
</div> |
|||
</div> |
|||
<!-- END OF OVERVIEW SECTION --> |
|||
|
|||
<!-- FEATURES SECTION --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/features.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Features |
|||
</h2> |
|||
</div> |
|||
<div class="row" style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Contains Tiles for displaying Count of Products which will Expire soon based on their Expiry Date..</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Analyse your products expiry on which location and date.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Shows all Products which are already Expired.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Graphical view of Expired Products.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Graphical view of Nearly Expiring Products based on their Location and Warehouse.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" style="margin-top: 40px; margin-bottom: 40px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Community & Enterprise Support.</span> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF FEATURES SECTION --> |
|||
|
|||
<!-- SCREENSHOTS SECTION --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="screenshots"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pictures.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Screenshots |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Product Expiry Dashboard.</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
Navigate to Inventory --> Product Expiry Dashboard menu. |
|||
</p> |
|||
<img src="assets/screenshots/01.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Product Expiry Dashboard.</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
1.We can view items that have reached their expiration date |
|||
today, those expiring within a day, a week, thirty days, and |
|||
one hundred and twenty days </br> |
|||
2.Additionally, we can also observe items that have already |
|||
expired, as well as expired items categorized by their |
|||
respective product categories |
|||
</p> |
|||
<img src="assets/screenshots/1.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Different Charts.</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
We can view products expiring within 7 days by categorized by category, location and warehouse. |
|||
</p> |
|||
<img src="assets/screenshots/2.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Product Expired Today</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
We can see count of products expired today, with a clickable button |
|||
that redirects to the product list view. |
|||
</p> |
|||
<img src="assets/screenshots/3.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">List View of Products Expired Today.</h3> |
|||
<img src="assets/screenshots/4.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Products Expiry Cards</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
Cards displaying product count expiring tomorrow, in 7 days, |
|||
30 days and 120 days, each clickable to redirect to its corresponding list view. |
|||
</p> |
|||
<img src="assets/screenshots/5.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">List View of Products Expiry in Tomorrow</h3> |
|||
<img src="assets/screenshots/6.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Card Filtration</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
The cards can be filtered using date. |
|||
</p> |
|||
<img src="assets/screenshots/7.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Graphs based on Card Filtration</h3> |
|||
<img src="assets/screenshots/8.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">Total Expired Products</h3> |
|||
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;"> |
|||
Total expired products based on product name and product catrgory. |
|||
</p> |
|||
<img src="assets/screenshots/9.png" class="img-thumbnail"> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- END OF SCREENSHOTS SECTION --> |
|||
|
|||
<!-- RELATED PRODUCTS --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/categories.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Related |
|||
Products |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div id="demo1" class="row carousel slide" data-ride="carousel"> |
|||
<!-- The slideshow --> |
|||
<div class="carousel-inner" style="padding: 30px;"> |
|||
<div class="carousel-item" style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/product_management_app/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module01.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/16.0/odoo_dynamic_dashboard/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module02.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/16.0/inventory_stock_dashboard_odoo/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module03.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item active" style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/project_dashboard_odoo/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module04.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/16.0/crm_dashboard/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module05.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/16.0/dashboard_pos/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="./assets/modules/module06.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Left and right controls --> |
|||
<a class="carousel-control-prev" href="#demo1" data-slide="prev" style="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="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 RELATED PRODUCTS --> |
|||
|
|||
<!-- OUR SERVICES --> |
|||
|
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/star.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Our Services |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<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> |
|||
</div> |
|||
<!-- END OF END OF OUR SERVICES --> |
|||
|
|||
<!-- OUR INDUSTRIES --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/corporate.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Our |
|||
Industries |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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: 0px; 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> |
|||
</div> |
|||
<!-- END OF END OF OUR INDUSTRIES --> |
|||
|
|||
<!-- SUPPORT --> |
|||
<div class="d-flex align-items-center" style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/customer-support.png"/> |
|||
</div> |
|||
<h2 class="mt-2" style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;">Support</h2> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/support.png" height="48" width="48" style="width: 42px; height: 42px;" /> |
|||
</div> |
|||
<div> |
|||
<h4>Need Help?</h4> |
|||
<p style="line-height: 100%;">Got questions or need help? Get in touch.</p> |
|||
<a href="mailto:odoo@cybrosys.com"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">odoo@cybrosys.com</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/whatsapp.png" height="52" width="52" style="width: 52px; height: 52px;" /> |
|||
</div> |
|||
<div> |
|||
<h4>WhatsApp</h4> |
|||
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p> |
|||
<a href="https://api.whatsapp.com/send?phone=918606827707"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;">+91 8606827707</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center"> |
|||
<img src="assets/misc/logo.png" width="144" height="31" style="width:144px; height: 31px; margin-top: 40px;" /> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF SUPPORT --> |
@ -0,0 +1,76 @@ |
|||
.order-card { |
|||
color: #fff; |
|||
} |
|||
|
|||
.bg-c-blue { |
|||
background: linear-gradient(45deg,#4099ff,#73b4ff); |
|||
} |
|||
|
|||
.bg-c-green { |
|||
background: linear-gradient(45deg,#2ed8b6,#59e0c5); |
|||
} |
|||
|
|||
.bg-c-yellow { |
|||
background: linear-gradient(45deg,#FFB64D,#ffcb80); |
|||
} |
|||
|
|||
.bg-c-pink { |
|||
background: linear-gradient(45deg,#FF5370,#ff869a); |
|||
} |
|||
|
|||
.card { |
|||
border-radius: 5px; |
|||
-webkit-box-shadow: 0 1px 2.94px 0.06px rgba(4,26,55,0.16); |
|||
box-shadow: 0 1px 2.94px 0.06px rgba(4,26,55,0.16); |
|||
border: none; |
|||
margin-bottom: 30px; |
|||
-webkit-transition: all 0.3s ease-in-out; |
|||
transition: all 0.3s ease-in-out; |
|||
} |
|||
|
|||
.card .card-block { |
|||
padding: 25px; |
|||
} |
|||
|
|||
.order-card i { |
|||
font-size: 26px; |
|||
} |
|||
|
|||
.f-left { |
|||
float: left; |
|||
} |
|||
|
|||
.f-right { |
|||
float: right; |
|||
} |
|||
|
|||
.card-block:hover{ |
|||
box-shadow: 5px 5px 10px #ccc; |
|||
} |
|||
|
|||
.expiry_dashboard_row{ |
|||
padding-left: 50px; |
|||
padding-right: 50px; |
|||
} |
|||
|
|||
.dashboard-heading{ |
|||
min-height: 140px; |
|||
padding: 20px; |
|||
text-align: center; |
|||
background: #376270; |
|||
} |
|||
|
|||
.banner-heading { |
|||
color: #ffffff; |
|||
font-size: 44px; |
|||
margin: 0; |
|||
margin-top: 20px; |
|||
} |
|||
|
|||
.product_expired_heading{ |
|||
background: aliceblue; |
|||
width: 236px; |
|||
float: left; |
|||
border-radius: 16px; |
|||
height: 102px; |
|||
} |
@ -0,0 +1,602 @@ |
|||
/** @odoo-module */ |
|||
import { registry } from "@web/core/registry" |
|||
import rpc from 'web.rpc'; |
|||
const { Component, onWillStart, useState } = owl; |
|||
const actionRegistry = registry.category("actions"); |
|||
var session = require('web.session'); |
|||
|
|||
export class ProductExpiryDashboard extends Component { |
|||
setup() { |
|||
super.setup(...arguments); |
|||
this.state = useState({ |
|||
data: null, |
|||
charts:[] |
|||
}) |
|||
onWillStart(async () => { |
|||
await this.fetch_products_expiry() |
|||
await this.product_expired_today() |
|||
await this.get_today_expire_products() |
|||
await this.get_today_expire_products_category() |
|||
await this.near_exp_products() |
|||
await this.near_exp_category() |
|||
await this.get_expire_product_location() |
|||
await this.get_expire_product_warehouse() |
|||
await this.render_expired_products_graph() |
|||
await this.expiry_by_category() |
|||
}); |
|||
} |
|||
// Filter products based on the specified start and end dates.
|
|||
async filter_date(ev) { |
|||
// Get the start and end dates selected by the user from input fields.
|
|||
var start_date = $("#start_date").val(); |
|||
var end_date = $("#end_date").val(); |
|||
if (this.state.charts.length != 0) { |
|||
this.state.charts.forEach((item)=> { |
|||
item.destroy() |
|||
}); |
|||
} |
|||
// Fetch and display products that fall within the specified date range.
|
|||
await this.fetch_products_expiry(start_date, end_date); |
|||
await this.near_exp_products(start_date, end_date); |
|||
await this.near_exp_category(start_date, end_date); |
|||
await this.get_expire_product_location(start_date, end_date); |
|||
await this.get_expire_product_warehouse(start_date, end_date); |
|||
await this.render_expired_products_graph(start_date, end_date); |
|||
await this.expiry_by_category(start_date, end_date); |
|||
} |
|||
fetch_products_expiry(start_date, end_date) { |
|||
// Remove existing elements before updating the product expiry counts.
|
|||
$("#one_day").remove(); |
|||
$("#seven_day").remove(); |
|||
$("#thirty_day").remove(); |
|||
$("#one_twenty_day").remove(); |
|||
var self = this; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch product expiry data from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_product_expiry', |
|||
args: [date_dict, session.user_context.allowed_company_ids] |
|||
}).then(function(result) { |
|||
self.state.data = result |
|||
var seven_day = result[0]['counts'] + result[1]['counts']; |
|||
// Update the HTML elements to display the product counts for different expiry periods.
|
|||
$(".one-day").append('<center>' |
|||
+ '<span style="font-size: xxx-large;" id="one_day">' + result[0]['counts'] + '</span>' |
|||
+ '</center>'); |
|||
$(".seven-day").append('<center>' |
|||
+ '<span style="font-size: xxx-large;" id="seven_day">' + seven_day + '</span>' |
|||
+ '</center>'); |
|||
$(".thirty-day").append('<center>' |
|||
+ '<span style="font-size: xxx-large;" id="thirty_day">' + result[2]['counts'] + '</span>' |
|||
+ '</center>'); |
|||
$(".one-twenty-day").append('<center>' |
|||
+ '<span style="font-size: xxx-large;" id="one_twenty_day">' + result[3]['counts'] + '</span>' |
|||
+ '</center>'); |
|||
}); |
|||
} |
|||
// Fetches the count of products that have expired today using an
|
|||
// RPC query and updates the HTML element to display the count.
|
|||
product_expired_today() { |
|||
// Send an RPC query to fetch the count of products that have expired today.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_product_expired_today', |
|||
args: [session.user_context.allowed_company_ids] |
|||
}).then(function (result) { |
|||
// Update the HTML element to display the count of products expired today.
|
|||
$('.product_expired_heading').append('<p style="font-size: 38px;margin-top: -10px;">' + result + '</p>'); |
|||
|
|||
}); |
|||
} |
|||
// Fetches the list of products that have expired today
|
|||
click_expired_today() { |
|||
var self = this; |
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_today_expire', |
|||
}).then(function (result) { |
|||
var id = [] |
|||
for( var data in result['id']){ |
|||
id.push(parseInt(result['id'][data])) |
|||
} |
|||
// Perform the action to display products that have expired on the current day.
|
|||
self.env.services['action'].doAction({ |
|||
name: "Products Expired Today", |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'stock.lot', |
|||
views: [[false, 'tree'], [false, 'form']], |
|||
domain: [['id', 'in', id]], |
|||
target: 'current', |
|||
context: { |
|||
'create': false |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
// Fetches the list of products that have expired within 1 day(tomorrow)
|
|||
one_day_click() { |
|||
// Perform the action to display products that are expiring in one day with negative product quantities.
|
|||
this.env.services['action'].doAction({ |
|||
name: "Expiry Tomorrow", |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'stock.lot', |
|||
views: [[false, 'tree'], [false, 'form']], |
|||
domain: [['id', 'in', this.state.data[0]['one_day']]], |
|||
target: 'current', |
|||
context: { |
|||
'create': false |
|||
} |
|||
}); |
|||
} |
|||
// Fetches the list of products that have expired with in 7days
|
|||
seven_day_click() { |
|||
const ids = [...this.state.data[1]['seven_day'], ...this.state.data[0]['one_day']] |
|||
// Perform the action to display products that are expiring within the seven-day range.
|
|||
this.env.services['action'].doAction({ |
|||
name: "Expiry in Seven Days", |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'stock.lot', |
|||
views: [[false, 'tree'], [false, 'form']], |
|||
domain: [['id', 'in', ids]], |
|||
target: 'current', |
|||
context: { |
|||
'create': false |
|||
} |
|||
}); |
|||
} |
|||
// Fetches the list of products that have expired in 30 days
|
|||
thirty_day_click() { |
|||
const ids = [...this.state.data[2]['thirty_day']] |
|||
// Perform the action to display products that are expiring within the thirty-day range.
|
|||
this.env.services['action'].doAction({ |
|||
name: "Expiry in Thirty Days", |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'stock.lot', |
|||
views: [[false, 'tree'], [false, 'form']], |
|||
domain: [['id', 'in', ids]], |
|||
target: 'current', |
|||
context: { |
|||
'create': false |
|||
} |
|||
}); |
|||
} |
|||
// Fetches the list of products that have expired in 120 days
|
|||
one_twenty_day_click() { |
|||
const ids = [...this.state.data[3]['one_twenty_day']] |
|||
// Perform the action to display products that are expiring within the one hundred twenty-day range.
|
|||
this.env.services['action'].doAction({ |
|||
name: "Expiry in One Twenty Days", |
|||
type: 'ir.actions.act_window', |
|||
res_model: 'stock.lot', |
|||
views: [[false, 'tree'], [false, 'form']], |
|||
domain: [['id', 'in', ids]], |
|||
target: 'current', |
|||
context: { |
|||
'create': false |
|||
} |
|||
}); |
|||
} |
|||
// Line Chart for Products Expired Today
|
|||
get_today_expire_products() { |
|||
// Initialize arrays to hold product warehouse names and their corresponding quantities.
|
|||
var expire_product_name = []; |
|||
var expire_product_qty = []; |
|||
// Send an RPC query to fetch data about products that are about to expire, categorized by their warehouses, from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_today_expire', |
|||
}).then(function (result) { |
|||
for( var data in result['name']){ |
|||
expire_product_name.push(result['name'][data]) |
|||
expire_product_qty.push(parseInt(result['qty'][data])) |
|||
} |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#today_expire_products'); |
|||
new Chart(ctx, { |
|||
type: 'line', |
|||
data: { |
|||
labels: expire_product_name, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: expire_product_qty, |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
// Bar chart for Products Expired Today by Category
|
|||
get_today_expire_products_category() { |
|||
// Initialize arrays to hold product warehouse names and their corresponding quantities.
|
|||
var expire_product_categ_name = []; |
|||
var expire_product_qty = []; |
|||
// Send an RPC query to fetch data about products that are about to expire, categorized by their warehouses, from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_today_expire', |
|||
}).then(function (result) { |
|||
// Extract product warehouse names and their corresponding quantities from the fetched data.
|
|||
for( var data in result['categ']){ |
|||
for (var key in result['categ'][data]) { |
|||
if (result['categ'][data].hasOwnProperty(key)) { |
|||
var value = result['categ'][data][key]; |
|||
} |
|||
expire_product_categ_name.push(key) |
|||
expire_product_qty.push(parseInt(value)) |
|||
} |
|||
} |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#today_expire_products_category'); |
|||
new Chart(ctx, { |
|||
type: 'bar', |
|||
data: { |
|||
labels: expire_product_categ_name, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: expire_product_qty, |
|||
backgroundColor: [ |
|||
'rgba(75, 192, 192, 0.2)', |
|||
'rgba(54, 162, 235, 0.2)', |
|||
'rgba(153, 102, 255, 0.2)', |
|||
'rgba(201, 203, 207, 0.2)', |
|||
'rgba(255, 99, 132, 0.2)', |
|||
'rgba(255, 159, 64, 0.2)', |
|||
'rgba(255, 205, 86, 0.2)', |
|||
], |
|||
borderColor: [ |
|||
'rgb(75, 192, 192)', |
|||
'rgb(54, 162, 235)', |
|||
'rgb(153, 102, 255)', |
|||
'rgb(201, 203, 207)', |
|||
'rgb(255, 99, 132)', |
|||
'rgb(255, 159, 64)', |
|||
'rgb(255, 205, 86)', |
|||
], |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
// chart for products expire in 7 days
|
|||
near_exp_products(start_date, end_date) { |
|||
var self = this; |
|||
// Initialize arrays to hold product names and their corresponding near expiry quantities.
|
|||
var product_array = []; |
|||
var nearby_expire_qty = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about products that are near their expiry date from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_near_expiry_product', |
|||
args: [date_dict], |
|||
}).then(function (result) { |
|||
// Extract product names and their corresponding near expiry quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_array.push(index); |
|||
nearby_expire_qty.push(name); |
|||
}); |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#nearby_expire_product'); |
|||
self.state.charts.push(new Chart(ctx, { |
|||
type: 'bar', |
|||
data: { |
|||
labels: product_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: nearby_expire_qty, |
|||
backgroundColor: [ |
|||
'rgba(255, 205, 86, 0.2)', |
|||
'rgba(75, 192, 192, 0.2)', |
|||
'rgba(54, 162, 235, 0.2)', |
|||
'rgba(153, 102, 255, 0.2)', |
|||
'rgba(201, 203, 207, 0.2)', |
|||
'rgba(255, 99, 132, 0.2)', |
|||
'rgba(255, 159, 64, 0.2)', |
|||
], |
|||
borderColor: [ |
|||
'rgb(255, 205, 86)', |
|||
'rgb(75, 192, 192)', |
|||
'rgb(54, 162, 235)', |
|||
'rgb(153, 102, 255)', |
|||
'rgb(201, 203, 207)', |
|||
'rgb(255, 99, 132)', |
|||
'rgb(255, 159, 64)', |
|||
], |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
})); |
|||
}); |
|||
} |
|||
// Chart for products category expire in 7 days
|
|||
near_exp_category(start_date, end_date) { |
|||
var self = this; |
|||
// Initialize arrays to hold product category names and their corresponding near expiry quantities.
|
|||
var product_category_array = []; |
|||
var nearby_expire_qty = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about products that are near their expiry date, categorized by their product categories.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_near_expiry_category', |
|||
args: [date_dict], |
|||
}).then(function (result) { |
|||
// Extract product category names and their corresponding near expiry quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_category_array.push(index); |
|||
nearby_expire_qty.push(name); |
|||
}); |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#nearby_expire_catg'); |
|||
self.state.charts.push(new Chart(ctx, { |
|||
type: 'bar', |
|||
data: { |
|||
labels: product_category_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: nearby_expire_qty, |
|||
backgroundColor: [ |
|||
'rgba(255, 159, 64, 0.2)', |
|||
'rgba(255, 205, 86, 0.2)', |
|||
'rgba(75, 192, 192, 0.2)', |
|||
'rgba(54, 162, 235, 0.2)', |
|||
'rgba(153, 102, 255, 0.2)', |
|||
'rgba(201, 203, 207, 0.2)', |
|||
'rgba(255, 99, 132, 0.2)', |
|||
], |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
})); |
|||
}); |
|||
} |
|||
// Expiry in Seven Days
|
|||
get_expire_product_location(start_date, end_date) { |
|||
var self = this; |
|||
// Initialize arrays to hold product location names and their corresponding quantities.
|
|||
var product_location_array = []; |
|||
var nearby_expire_qty = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about products that are about to expire, categorized by their locations, from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_expire_product_location', |
|||
args: [date_dict], |
|||
}).then(function (result) { |
|||
// Extract product location names and their corresponding quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_location_array.push(index); |
|||
nearby_expire_qty.push(name); |
|||
}); |
|||
// Render the pie chart using Chart.js.
|
|||
const ctx = $('#nearby_expire_location'); |
|||
self.state.charts.push(new Chart(ctx, { |
|||
type: 'pie', |
|||
data: { |
|||
labels: product_location_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: nearby_expire_qty, |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
})); |
|||
}); |
|||
} |
|||
// Products by Warehouse Expire in 7 Days
|
|||
get_expire_product_warehouse(start_date, end_date) { |
|||
var self = this; |
|||
// Initialize arrays to hold product warehouse names and their corresponding quantities.
|
|||
var product_warehouse_array = []; |
|||
var nearby_expire_qty = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about products that are about to expire, categorized by their warehouses, from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_expire_product_warehouse', |
|||
args: [date_dict], |
|||
}).then(function (result) { |
|||
// Extract product warehouse names and their corresponding quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_warehouse_array.push(index); |
|||
nearby_expire_qty.push(name); |
|||
}); |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#nearby_expire_warehouse'); |
|||
self.state.charts.push(new Chart(ctx, { |
|||
type: 'doughnut', |
|||
data: { |
|||
labels: product_warehouse_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: nearby_expire_qty, |
|||
backgroundColor: [ |
|||
'rgba(75, 192, 192, 0.2)', |
|||
'rgba(54, 162, 235, 0.2)', |
|||
'rgba(153, 102, 255, 0.2)', |
|||
'rgba(201, 203, 207, 0.2)', |
|||
'rgba(255, 99, 132, 0.2)', |
|||
'rgba(255, 159, 64, 0.2)', |
|||
'rgba(255, 205, 86, 0.2)', |
|||
], |
|||
borderColor: [ |
|||
'rgb(75, 192, 192)', |
|||
'rgb(54, 162, 235)', |
|||
'rgb(153, 102, 255)', |
|||
'rgb(201, 203, 207)', |
|||
'rgb(255, 99, 132)', |
|||
'rgb(255, 159, 64)', |
|||
'rgb(255, 205, 86)', |
|||
], |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
})); |
|||
}); |
|||
} |
|||
// Expired Products
|
|||
render_expired_products_graph(start_date, end_date) { |
|||
// Check if a chart with the ID 'expired_product_count' already exists and destroy it if it does.
|
|||
let chartStatus = Chart.getChart('expired_product_count'); |
|||
if (chartStatus !== undefined) { |
|||
chartStatus.destroy(); |
|||
} |
|||
// Initialize arrays to hold product names and their corresponding expired quantities.
|
|||
var product_array = []; |
|||
var expired_qty_array = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about expired products from the server.
|
|||
let data = rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_expired_product', |
|||
args: [date_dict,] |
|||
}).then(function (result) { |
|||
// Extract product names and their corresponding expired quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_array.push(index); |
|||
expired_qty_array.push(name); |
|||
}); |
|||
// Render the pie chart using Chart.js.
|
|||
const ctx = $('#expired_product_count'); |
|||
new Chart(ctx, { |
|||
type: 'pie', |
|||
data: { |
|||
labels: product_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: expired_qty_array, |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
// Expired Products by Category
|
|||
expiry_by_category(start_date, end_date) { |
|||
// Check if a chart with the ID 'expired_product_category_count' already exists and destroy it if it does.
|
|||
let chartStatus = Chart.getChart('expired_product_category_count'); |
|||
if (chartStatus !== undefined) { |
|||
chartStatus.destroy(); |
|||
} |
|||
// Initialize arrays to hold product category names and their corresponding expired quantities.
|
|||
var product_category_array = []; |
|||
var expired_qty_array = []; |
|||
// Prepare the date dictionary to pass as an argument in the RPC query.
|
|||
var date_dict = { 'start_date': start_date, 'end_date': end_date }; |
|||
// Send an RPC query to fetch data about expired products categorized by their product categories from the server.
|
|||
rpc.query({ |
|||
model: 'stock.lot', |
|||
method: 'get_product_expiry_by_category', |
|||
args: [date_dict,session.user_context.allowed_company_ids] |
|||
}).then(function (result) { |
|||
// Extract product category names and their corresponding expired quantities from the fetched data.
|
|||
$.each(result, function (index, name) { |
|||
product_category_array.push(index); |
|||
expired_qty_array.push(name); |
|||
}); |
|||
// Render the bar chart using Chart.js.
|
|||
const ctx = $('#expired_product_category_count'); |
|||
new Chart(ctx, { |
|||
type: 'polarArea', |
|||
data: { |
|||
labels: product_category_array, |
|||
datasets: [{ |
|||
label: 'Quantity', |
|||
data: expired_qty_array, |
|||
borderWidth: 1 |
|||
}] |
|||
}, |
|||
options: { |
|||
plugins: { |
|||
legend: { |
|||
position: 'bottom', |
|||
}, |
|||
title: { |
|||
display: true, |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
ProductExpiryDashboard.template = "ProductExpiryDashboard" |
|||
actionRegistry.add('product_expiry', ProductExpiryDashboard); |
@ -0,0 +1,178 @@ |
|||
<?xml version="1.0" encoding="UTF-8" ?> |
|||
<template id="expiry_dashboard_template"> |
|||
<!-- Product Expiry Dashboard Template --> |
|||
<t t-name="ProductExpiryDashboard" owl="1"> |
|||
<!-- Main component for the Product Expiry Dashboard --> |
|||
<!-- Container for the entire dashboard content --> |
|||
<div class="o_action_manager " |
|||
style="height:100%; overflow-y: scroll; overflow-x: scroll;"> |
|||
<!-- Main section of the dashboard --> |
|||
<section class="dashboard_main_section" id="main_section_manager"> |
|||
<!-- Dashboard header --> |
|||
<div class="dashboard-heading shadow-sm"> |
|||
<!-- Section to display products expired today --> |
|||
<div class="product_expired_heading card-block" t-on-click="click_expired_today"> |
|||
<!-- Heading for products expired today --> |
|||
<h2 style="margin-top: 8px;">Products Expired Today</h2> |
|||
<hr/> |
|||
</div> |
|||
<!-- Centered banner for the dashboard --> |
|||
<center> |
|||
<div style="width: 544px"> |
|||
<h1 class="banner-heading">Product Expiry Dashboard</h1> |
|||
</div> |
|||
</center> |
|||
<!-- Date filtering form --> |
|||
<div class="col-12 " style="color: white; display: flex; justify-content: flex-end;"> |
|||
<form class="form-group"> |
|||
<span>Start Date: </span> |
|||
<input type="date" id="start_date" name="start_date" t-on-change="filter_date" style="border-radius: 5px; font-family: monospace; padding: 5px; border: none;"/> |
|||
<span> End Date: </span> |
|||
<input type="date" id="end_date" name="end_date" t-on-change="filter_date" style="border-radius: 5px; font-family: monospace; padding: 5px; border: none;"/> |
|||
</form> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<!-- Row for displaying cards showing expiries within different timeframes --> |
|||
<div class="row expiry_dashboard_row"> |
|||
<div class="col-md-4 col-xl-3"> |
|||
<!-- Card for products expiring in 1 day --> |
|||
<div class="card bg-c-blue order-card"> |
|||
<div class="card-block one-day" t-on-click="one_day_click"> |
|||
<h2 style="color:white; text-align:center;">Expiry Tomorrow</h2> |
|||
<hr/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-xl-3"> |
|||
<!-- Card for products expiring in 7 days --> |
|||
<div class="card bg-c-green order-card"> |
|||
<div class="card-block seven-day" t-on-click="seven_day_click"> |
|||
<h2 style="color:white; text-align:center;">Expiry in 7 Days</h2> |
|||
<hr/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-xl-3"> |
|||
<!-- Card for products expiring in 30 days --> |
|||
<div class="card bg-c-yellow order-card"> |
|||
<div class="card-block thirty-day" t-on-click="thirty_day_click"> |
|||
<h2 style="color:white; text-align:center;">Expiry in 30 Days</h2> |
|||
<hr/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md-4 col-xl-3"> |
|||
<!-- Card for products expiring in 120 days --> |
|||
<div class="card bg-c-pink order-card"> |
|||
<div class="card-block one-twenty-day" t-on-click="one_twenty_day_click"> |
|||
<h2 style="color:white; text-align:center;">Expiry in 120 Days</h2> |
|||
<hr/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<br/> |
|||
<!-- Row for displaying charts related to expired products --> |
|||
<div class="row"> |
|||
<div class="col-md"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;" |
|||
t-on-click="click_expired_today"> |
|||
<center> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Products Expired Today</h2> |
|||
</center> |
|||
<!-- Chart for displaying the count of expired products --> |
|||
<div> |
|||
<canvas id="today_expire_products"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Products Expired Today by Category</h2> |
|||
</center> |
|||
<!-- Chart for displaying the count of expired products by category --> |
|||
<div> |
|||
<canvas id="today_expire_products_category"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Row for displaying charts related to products expiring within 7 days --> |
|||
<div class="row"> |
|||
<div class="col-md"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;" |
|||
t-on-click="seven_day_click"> |
|||
<center> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Products Expire in 7 Days</h2> |
|||
</center> |
|||
<!-- Chart for displaying products expiring within 7 days --> |
|||
<div> |
|||
<canvas t-ref="asd" id="nearby_expire_product"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-md"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Products Category Expire in 7 Days</h2> |
|||
</center> |
|||
<!-- Chart for displaying products expiring by category within 7 days --> |
|||
<div> |
|||
<canvas id="nearby_expire_catg"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Row for displaying charts related to products expiring within 7 days and their location/warehouse --> |
|||
<div class="row"> |
|||
<div class="col-lg-3"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<!-- Chart for displaying products expiring by location within 7 days --> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Location Expire in 7 Days</h2> |
|||
<canvas id="nearby_expire_location"/> |
|||
</center> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-3"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<!-- Chart for displaying products expiring by warehouse within 7 days --> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Products by Warehouse Expire in 7 Days</h2> |
|||
<canvas id="nearby_expire_warehouse"/> |
|||
</center> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-3"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<!-- Chart for displaying products expiring by location within 7 days --> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Expired Products</h2> |
|||
<canvas id="expired_product_count"/> |
|||
</center> |
|||
</div> |
|||
</div> |
|||
<div class="col-lg-3"> |
|||
<div class="shadow-sm m-2 p-4" |
|||
style="border-radius:5px; background-color: snow;"> |
|||
<center> |
|||
<!-- Chart for displaying products expiring by warehouse within 7 days --> |
|||
<h2 class="chart_heading" style="margin-top: 8px;">Expired Products by Category</h2> |
|||
<canvas id="expired_product_category_count"/> |
|||
</center> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</t> |
|||
</template> |
@ -0,0 +1,21 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<odoo> |
|||
<data> |
|||
<!-- Record for defining a client action for the Expiry Dashboard --> |
|||
<record id="action_expiry_dashboard" model="ir.actions.client"> |
|||
<!-- Name of the client action --> |
|||
<field name="name">Expiry Dashboard</field> |
|||
<!-- Tag to identify the client action as related to product expiry --> |
|||
<field name="tag">product_expiry</field> |
|||
<!-- Target for opening the client action in the current window --> |
|||
<field name="target">current</field> |
|||
</record> |
|||
<!-- Menu item for accessing the Product Expiry Dashboard --> |
|||
<menuitem |
|||
id="expiry_dashboard" |
|||
name="Product Expiry Dashboard" |
|||
action="action_expiry_dashboard" |
|||
parent="stock.menu_stock_root" |
|||
sequence="1"/> |
|||
</data> |
|||
</odoo> |