@ -0,0 +1,53 @@ |
|||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
||||
|
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
||||
|
:alt: License: AGPL-3 |
||||
|
|
||||
|
Amazon Forecast Integration |
||||
|
================ |
||||
|
Manage your stock using the forecast report generated based on historical |
||||
|
time series data |
||||
|
|
||||
|
Installation |
||||
|
------------ |
||||
|
- www.odoo.com/documentation/16.0/setup/install.html |
||||
|
- Install our custom addon |
||||
|
|
||||
|
Configuration |
||||
|
------------- |
||||
|
* Add the parameter limit_time_real in the conf file with value equal to 7200 |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
Affero General Public License v3.0 (AGPL v3) |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
Developer : (V16) Shafna K @ Cybrosys, 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,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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,60 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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': "Amazon Forecast Integration", |
||||
|
'version': '16.0.1.0.0', |
||||
|
'category': 'Warehouse', |
||||
|
'summary': 'To predict the stock demand.', |
||||
|
'description': """ |
||||
|
This module helps to predict the demand and maintain right level of |
||||
|
inventory. |
||||
|
""", |
||||
|
'sequence': 20, |
||||
|
'author': " Cybrosys Techno Solutions", |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'website': 'https://www.cybrosys.com', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'support': 'Cybrosys Techno Solutions', |
||||
|
'depends': ['base', 'stock'], |
||||
|
'data': [ |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/res_config_settings_views.xml', |
||||
|
'views/amazon_dataset_views.xml', |
||||
|
'views/amazon_fetch_data_views.xml', |
||||
|
'views/amazon_bucket_views.xml', |
||||
|
'views/menus.xml' |
||||
|
], |
||||
|
'assets': { |
||||
|
'web.assets_backend': [ |
||||
|
'https://cdn.jsdelivr.net/npm/chart.js', |
||||
|
'https://cdn.jsdelivr.net/npm/chartjs-adapter-date-fns@3.0.0/dist/chartjs-adapter-date-fns.bundle.min.js', |
||||
|
'amazon_forecast_integration/static/src/js/graphView.js', |
||||
|
'amazon_forecast_integration/static/src/xml/graph_view.xml', |
||||
|
] |
||||
|
}, |
||||
|
'external_dependencies': {'python': ['boto3']}, |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,7 @@ |
|||||
|
## Module <amazon_forecast_integration> |
||||
|
|
||||
|
#### 20.04.2024 |
||||
|
#### Version 16.0.1.0.0 |
||||
|
#### ADD |
||||
|
|
||||
|
- Initial Commit for Amazon Forecast Integration |
@ -0,0 +1,25 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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 amazon_bucket |
||||
|
from . import amazon_dataset |
||||
|
from . import amazon_fetch_data |
||||
|
from . import res_config_settings |
@ -0,0 +1,136 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
import boto3 |
||||
|
import re |
||||
|
from odoo import fields, models |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class AmazonBucket(models.Model): |
||||
|
"""Class to create an Amazon S3 bucket""" |
||||
|
_name = "amazon.bucket" |
||||
|
_description = "Amazon Bucket" |
||||
|
_rec_name = "bucket_name" |
||||
|
|
||||
|
bucket_name = fields.Char(string="Bucket Name", required=True, |
||||
|
help="""Provide the bucket name. |
||||
|
Eg: |
||||
|
- docexamplebucket1 |
||||
|
- log-delivery-march-2020 |
||||
|
- my-hosted-content""") |
||||
|
state = fields.Selection([ |
||||
|
('create_bucket', 'Create Bucket'), |
||||
|
('push_to_bucket', 'Push To bucket'), |
||||
|
('pushed', 'Pushed') |
||||
|
], help="States of bucket creation.", string="State", |
||||
|
default='create_bucket') |
||||
|
file_path = fields.Char(string="File Path", |
||||
|
help="Provide the file path of your data.") |
||||
|
s3_uri = fields.Char(string="S3 URI", help="After pushing the data to s3 " |
||||
|
"Bucket, URI will be computed.") |
||||
|
|
||||
|
def _validate_bucket_name(self, bucket_name): |
||||
|
if len(bucket_name) < 3 or len(bucket_name) > 63: |
||||
|
raise ValueError("Bucket name must be between 3 and 63 characters" |
||||
|
" long") |
||||
|
if not re.match("^[a-z0-9.-]+$", bucket_name): |
||||
|
raise ValueError("Bucket name can consist only of lowercase " |
||||
|
"letters, numbers, dots, and hyphens") |
||||
|
if not bucket_name[0].isalnum() or not bucket_name[-1].isalnum(): |
||||
|
raise ValueError("Bucket name must begin and end with a letter or" |
||||
|
" number") |
||||
|
if ".." in bucket_name: |
||||
|
raise ValueError("Bucket name must not contain two adjacent " |
||||
|
"periods") |
||||
|
if re.match(r"^\d+\.\d+\.\d+\.\d+$", bucket_name): |
||||
|
raise ValueError("Bucket name must not be formatted as an " |
||||
|
"IP address") |
||||
|
reserved_prefixes = ['xn--', 'sthree-', 'sthree-configurator-'] |
||||
|
reserved_suffixes = ['-s3alias', '--ol-s3'] |
||||
|
for prefix in reserved_prefixes: |
||||
|
if bucket_name.startswith(prefix): |
||||
|
raise ValueError("Bucket name must not start with the " |
||||
|
"prefix '{prefix}'") |
||||
|
for suffix in reserved_suffixes: |
||||
|
if bucket_name.endswith(suffix): |
||||
|
raise ValueError("Bucket name must not end with the" |
||||
|
" suffix '{suffix}'") |
||||
|
if '.' in bucket_name: |
||||
|
raise ValueError("Buckets used with Transfer Acceleration can't " |
||||
|
"have dots (.) in their names") |
||||
|
|
||||
|
def action_s3bucket(self): |
||||
|
"""Function to create an S3 bucket in Amazon""" |
||||
|
file = self.env['amazon.fetch.data'].get_file_path() |
||||
|
values = self.env['amazon.dataset'].forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
s3_client = session.client('s3') |
||||
|
bucket_name = self.bucket_name |
||||
|
try: |
||||
|
self._validate_bucket_name(bucket_name) |
||||
|
except ValueError as e: |
||||
|
raise UserError(e) |
||||
|
self.write({ |
||||
|
'file_path': file |
||||
|
}) |
||||
|
s3_client.create_bucket( |
||||
|
Bucket=bucket_name, |
||||
|
CreateBucketConfiguration={'LocationConstraint': 'ap-south-1'} |
||||
|
) |
||||
|
self.write({'state': "push_to_bucket"}) |
||||
|
|
||||
|
def action_s3bucket_push(self): |
||||
|
"""To push the data to Amazon S3 bucket""" |
||||
|
values = self.env['amazon.dataset'].forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
s3_client = session.client('s3') |
||||
|
s3_resource = session.resource('s3') |
||||
|
bucket = s3_resource.Bucket(self.bucket_name) |
||||
|
bucket_name = self.bucket_name |
||||
|
file_path = self.file_path |
||||
|
with open(file_path, 'rb') as file: |
||||
|
s3_client.put_object(Body=file, Bucket=bucket_name, |
||||
|
Key=file_path) |
||||
|
for object_summary in bucket.objects.all(): |
||||
|
object_key = object_summary.key |
||||
|
self.write({ |
||||
|
's3_uri': f"s3://{self.bucket_name}/{object_key}", |
||||
|
}) |
||||
|
self.write({'state': "pushed"}) |
@ -0,0 +1,602 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
import boto3 |
||||
|
import json |
||||
|
import time |
||||
|
from odoo import api, fields, models, _ |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class AmazonDataset(models.Model): |
||||
|
"""Class to create a forecast of stock""" |
||||
|
_name = "amazon.dataset" |
||||
|
_description = "Amazon Dataset" |
||||
|
_rec_name = "table_name" |
||||
|
|
||||
|
table_name = fields.Char(string="Table Name", required=True, |
||||
|
help="Provide the table name.") |
||||
|
role_name = fields.Char(string="Role Name", |
||||
|
help="Provide the Role name.") |
||||
|
policy_name = fields.Char(string="Policy Name", |
||||
|
help="Provide the Policy name.") |
||||
|
dataset_group = fields.Char(string="Dataset Group", |
||||
|
help="Provide the Dataset Group name.") |
||||
|
dataset = fields.Char(string="Dataset", |
||||
|
help="Provide the Dataset name.") |
||||
|
state = fields.Selection([ |
||||
|
('table', "Table"), |
||||
|
('role', "Role"), |
||||
|
('kms', "KMS"), |
||||
|
('policy', "Policy"), |
||||
|
('dataset', "Dataset"), |
||||
|
('import_dataset', "Import Dataset"), |
||||
|
('predictor', "Predictor"), |
||||
|
('forecast', "Forecast"), |
||||
|
('query_forecast', "Query Forecast"), |
||||
|
], default="table", string="State", |
||||
|
help="States of dataset creation.") |
||||
|
table_arn = fields.Char(string="Table ARN", |
||||
|
help="Table arn will be computed based on table" |
||||
|
" name provided after creating the table.") |
||||
|
role_arn = fields.Char(string="Role Arn", help="Role Arn will be computed " |
||||
|
"after Role is created.") |
||||
|
policy_arn = fields.Char(string="Policy Arn", |
||||
|
help="Policy Arn will be created after the " |
||||
|
"policy is created.") |
||||
|
kms_alias = fields.Char(string="KMS Alias Name", |
||||
|
help="Provide an Alias name for KMS.") |
||||
|
kms_arn = fields.Char(string="KMS Arn", help="KMS Arn will be created " |
||||
|
"after KMS key is created.") |
||||
|
dataset_group_arn = fields.Char(string="Dataset Group Arn", |
||||
|
help="Dataset Group Arn will be created " |
||||
|
"after Dataset Group is created.") |
||||
|
dataset_arn = fields.Char(string="Dataset Arn", |
||||
|
help="Dataset Arn will be craeted after" |
||||
|
" Dataset is created.") |
||||
|
forecast_frequency = fields.Selection([ |
||||
|
('D', 'Days'), |
||||
|
('W', 'Weeks'), |
||||
|
('M', 'Months'), |
||||
|
('Y', 'Years'), |
||||
|
('T', 'Minutes'), |
||||
|
('H', 'Hours'), |
||||
|
], default='D', string="Forecast Frequency", help="Choose the frequency" |
||||
|
" for forecasting.") |
||||
|
import_job_name = fields.Char(string="Import Job Name", |
||||
|
help="Provide the import job name.") |
||||
|
import_job_arn = fields.Char(string="Import Job Arn", |
||||
|
help="Import job Arn will be computed after" |
||||
|
" import job is done.") |
||||
|
predictor_name = fields.Char(string="Predictor Name", |
||||
|
help="Provide a name for Predictor function.") |
||||
|
predictor_algorithm = fields.Selection([ |
||||
|
('arn:aws:forecast:::algorithm/ARIMA', 'ARIMA'), |
||||
|
('arn:aws:forecast:::algorithm/CNN-QR', 'CNN-QR'), |
||||
|
('arn:aws:forecast:::algorithm/NPTS', 'NPTS'), |
||||
|
('arn:aws:forecast:::algorithm/MQRNN', 'MQRNN'), |
||||
|
], string="Algorithm", default='arn:aws:forecast:::algorithm/ARIMA', |
||||
|
help="Choose desired Predictor Algorithm.") |
||||
|
predictor_arn = fields.Char(string="Predictor Arn", |
||||
|
help="Predictor Arn will be computed after " |
||||
|
"predictor function is created.") |
||||
|
forecast_name = fields.Char(string="Forecast Name", |
||||
|
help="Provide the name for forecast function.") |
||||
|
forecast_arn = fields.Char(string="Forecast Arn", |
||||
|
help="Forecast Arn will be computed after " |
||||
|
"forecasting is completed.") |
||||
|
item_id = fields.Char(string="Item id", |
||||
|
help="Provide the name of product you want to know " |
||||
|
"the forecasting.") |
||||
|
bucket_id = fields.Many2one('amazon.bucket', string="Bucket", |
||||
|
help="Choose the bucket name " |
||||
|
"from which the data must" |
||||
|
"be taken.") |
||||
|
|
||||
|
def forecast_values(self): |
||||
|
"""To connect with the Amazon Forecast services using credentials""" |
||||
|
amazon_forecast = self.env['ir.config_parameter'].sudo().get_param( |
||||
|
'amazon_forecast_integration.amazon_forecast') |
||||
|
amazon_access_key = self.env['ir.config_parameter'].sudo().get_param( |
||||
|
'amazon_forecast_integration.amazon_access_key') |
||||
|
amazon_secret_access_key = self.env[ |
||||
|
'ir.config_parameter'].sudo().get_param( |
||||
|
'amazon_forecast_integration.amazon_secret_access_key') |
||||
|
amazon_region = self.env['ir.config_parameter'].sudo().get_param( |
||||
|
'amazon_forecast_integration.amazon_region') |
||||
|
return { |
||||
|
'amazon_forecast': amazon_forecast, |
||||
|
'amazon_access_key': amazon_access_key, |
||||
|
'amazon_secret_access_key': amazon_secret_access_key, |
||||
|
'amazon_region': amazon_region, |
||||
|
} |
||||
|
|
||||
|
def action_create_table(self): |
||||
|
"""To create a dynamodb in Amazon Forecast""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
dynamodb_client = session.client('dynamodb') |
||||
|
try: |
||||
|
response = dynamodb_client.create_table( |
||||
|
TableName=self.table_name, |
||||
|
AttributeDefinitions=[ |
||||
|
{ |
||||
|
'AttributeName': 'id', |
||||
|
'AttributeType': 'N' |
||||
|
} |
||||
|
], |
||||
|
KeySchema=[ |
||||
|
{ |
||||
|
'AttributeName': 'id', |
||||
|
'KeyType': 'HASH' |
||||
|
} |
||||
|
], |
||||
|
ProvisionedThroughput={ |
||||
|
'ReadCapacityUnits': 5, |
||||
|
'WriteCapacityUnits': 5 |
||||
|
} |
||||
|
) |
||||
|
self.write({ |
||||
|
'state': 'role', |
||||
|
'table_arn': response['TableDescription']['TableArn'] |
||||
|
}) |
||||
|
except dynamodb_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_create_role(self): |
||||
|
"""To create a Role for Forecasting""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
assume_role_policy_document = { |
||||
|
"Version": "2012-10-17", |
||||
|
"Statement": [ |
||||
|
{ |
||||
|
"Effect": "Allow", |
||||
|
"Principal": { |
||||
|
"Service": "forecast.amazonaws.com" |
||||
|
}, |
||||
|
"Action": "sts:AssumeRole" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
iam_client = session.client('iam') |
||||
|
try: |
||||
|
response = iam_client.create_role( |
||||
|
RoleName=self.role_name, |
||||
|
AssumeRolePolicyDocument=json.dumps( |
||||
|
assume_role_policy_document) |
||||
|
) |
||||
|
self.write({ |
||||
|
'state': 'kms', |
||||
|
'role_arn': response['Role']['Arn'] |
||||
|
}) |
||||
|
except iam_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_create_kms(self): |
||||
|
"""To create a Key Management Service in AWS""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
kms_client = session.client('kms') |
||||
|
response = kms_client.create_key( |
||||
|
Description='KMS Key' |
||||
|
) |
||||
|
key_id = response['KeyMetadata']['KeyId'] |
||||
|
key_response = kms_client.describe_key(KeyId=key_id) |
||||
|
key_arn = key_response['KeyMetadata']['Arn'] |
||||
|
self.write({ |
||||
|
'kms_arn': key_arn, |
||||
|
}) |
||||
|
kms_client.create_alias( |
||||
|
AliasName='alias/'+self.kms_alias, |
||||
|
TargetKeyId=key_id |
||||
|
) |
||||
|
kms_client.create_grant( |
||||
|
KeyId=key_id, |
||||
|
GranteePrincipal=self.role_arn, |
||||
|
Operations=['Encrypt', 'Decrypt'] |
||||
|
) |
||||
|
self.write({ |
||||
|
'state': 'policy' |
||||
|
}) |
||||
|
return key_id |
||||
|
|
||||
|
def action_create_policy(self): |
||||
|
"""To create a policies for the role and attach it with the role""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
iam_client = session.client('iam') |
||||
|
sts_client = session.client('sts') |
||||
|
response = sts_client.get_caller_identity() |
||||
|
account_id = response['Account'] |
||||
|
policy_name = self.policy_name |
||||
|
policy_document = { |
||||
|
"Version": "2012-10-17", |
||||
|
"Statement": [ |
||||
|
{ |
||||
|
"Effect": "Allow", |
||||
|
"Action": [ |
||||
|
"s3:ListBucket" |
||||
|
], |
||||
|
"Resource": "arn:aws:s3:::"+self.bucket_id.bucket_name |
||||
|
}, |
||||
|
{ |
||||
|
"Effect": "Allow", |
||||
|
"Action": [ |
||||
|
"s3:GetObject", |
||||
|
"s3:PutObject" |
||||
|
], |
||||
|
"Resource": |
||||
|
"arn:aws:s3:::"+self.bucket_id.bucket_name+"/*" |
||||
|
}, |
||||
|
{ |
||||
|
"Effect": "Allow", |
||||
|
"Action": [ |
||||
|
"dynamodb:PutItem", |
||||
|
"dynamodb:GetItem" |
||||
|
], |
||||
|
"Resource": self.table_arn |
||||
|
}, |
||||
|
{ |
||||
|
"Effect": "Allow", |
||||
|
"Action": [ |
||||
|
"kms:Encrypt", |
||||
|
"kms:Decrypt", |
||||
|
"kms:ReEncrypt*", |
||||
|
"kms:GenerateDataKey*", |
||||
|
"kms:DescribeKey", |
||||
|
"kms:CreateGrant" |
||||
|
], |
||||
|
"Resource": self.kms_arn |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
response = iam_client.create_policy( |
||||
|
PolicyName=policy_name, |
||||
|
PolicyDocument=json.dumps(policy_document), |
||||
|
) |
||||
|
self.write({ |
||||
|
'policy_arn': response['Policy']['Arn'] |
||||
|
}) |
||||
|
try: |
||||
|
iam_client.put_role_policy( |
||||
|
RoleName=self.role_name, |
||||
|
PolicyName=policy_name, |
||||
|
PolicyDocument=json.dumps(policy_document) |
||||
|
) |
||||
|
except Exception as e: |
||||
|
raise UserError(e) |
||||
|
try: |
||||
|
policy_arn1 = 'arn:aws:iam::aws:policy/AmazonS3FullAccess' |
||||
|
policy_arn2 = ( |
||||
|
'arn:aws:iam::'+account_id+':policy/'+self.policy_name) |
||||
|
iam_client.attach_role_policy( |
||||
|
RoleName=self.role_name, |
||||
|
PolicyArn=policy_arn1 |
||||
|
) |
||||
|
iam_client.attach_role_policy( |
||||
|
RoleName=self.role_name, |
||||
|
PolicyArn=policy_arn2 |
||||
|
) |
||||
|
self.write({ |
||||
|
'state': 'dataset' |
||||
|
}) |
||||
|
except iam_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
except iam_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_create_dataset(self): |
||||
|
"""To create required Dataset for forecasting""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
dataset_group_name = self.dataset_group |
||||
|
forecast_client = session.client('forecast') |
||||
|
try: |
||||
|
dataset_group_response = forecast_client.create_dataset_group( |
||||
|
DatasetGroupName=dataset_group_name, |
||||
|
Domain='RETAIL', |
||||
|
Tags=[ |
||||
|
{'Key': 'Name', 'Value': dataset_group_name} |
||||
|
] |
||||
|
) |
||||
|
self.write({'dataset_group_arn': dataset_group_response[ |
||||
|
'DatasetGroupArn']}) |
||||
|
dataset_name = self.dataset |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
try: |
||||
|
schema = { |
||||
|
"Attributes": [ |
||||
|
{"AttributeName": "item_id", |
||||
|
"AttributeType": "string"}, |
||||
|
{"AttributeName": "timestamp", |
||||
|
"AttributeType": "timestamp"}, |
||||
|
{"AttributeName": "demand", |
||||
|
"AttributeType": "float"}, |
||||
|
{"AttributeName": "id", "AttributeType": "string"}, |
||||
|
{"AttributeName": "reference", |
||||
|
"AttributeType": "string"}, |
||||
|
{"AttributeName": "location_id", |
||||
|
"AttributeType": "string"}, |
||||
|
{"AttributeName": "location_dest_id", |
||||
|
"AttributeType": "string"}, |
||||
|
{"AttributeName": "origin", |
||||
|
"AttributeType": "string"}, |
||||
|
] |
||||
|
} |
||||
|
dataset_response = forecast_client.create_dataset( |
||||
|
DatasetName=dataset_name, |
||||
|
DatasetType='TARGET_TIME_SERIES', |
||||
|
Domain='RETAIL', |
||||
|
DataFrequency=self.forecast_frequency, |
||||
|
Schema=schema, |
||||
|
EncryptionConfig={ |
||||
|
'RoleArn': self.role_arn, |
||||
|
'KMSKeyArn': self.kms_arn, |
||||
|
} |
||||
|
) |
||||
|
self.write({ |
||||
|
'dataset_arn': dataset_response['DatasetArn'] |
||||
|
}) |
||||
|
forecast_client.update_dataset_group( |
||||
|
DatasetGroupArn=self.dataset_group_arn, |
||||
|
DatasetArns=[self.dataset_arn]) |
||||
|
self.write({ |
||||
|
'state': 'import_dataset' |
||||
|
}) |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_import_dataset(self): |
||||
|
"""To import dataset from Amazon S3 bucket""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
forecast_client = session.client('forecast') |
||||
|
try: |
||||
|
response = forecast_client.create_dataset_import_job( |
||||
|
DatasetImportJobName=self.import_job_name, |
||||
|
DatasetArn=self.dataset_arn, |
||||
|
DataSource={ |
||||
|
'S3Config': { |
||||
|
'Path': self.bucket_id.s3_uri, |
||||
|
'RoleArn': self.role_arn |
||||
|
} |
||||
|
} |
||||
|
) |
||||
|
self.write({ |
||||
|
'import_job_arn': response['DatasetImportJobArn'], |
||||
|
'state': 'predictor' |
||||
|
}) |
||||
|
while True: |
||||
|
response = forecast_client.describe_dataset_import_job( |
||||
|
DatasetImportJobArn=self.import_job_arn) |
||||
|
status = response['Status'] |
||||
|
if status == 'ACTIVE': |
||||
|
break |
||||
|
elif status == 'FAILED' or status == 'CREATE_FAILED': |
||||
|
raise UserError(_('Error: Dataset Import Job failed.')) |
||||
|
break |
||||
|
else: |
||||
|
time.sleep(10) |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_create_predictor(self): |
||||
|
"""To create the predictor function for forecasting""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
forecast_client = session.client('forecast') |
||||
|
try: |
||||
|
response_group = forecast_client.describe_dataset_group( |
||||
|
DatasetGroupArn=self.dataset_group_arn) |
||||
|
dataset_arns = response_group['DatasetArns'] |
||||
|
while True: |
||||
|
all_datasets_imported = True |
||||
|
for dataset_arn in dataset_arns: |
||||
|
response = forecast_client.list_dataset_import_jobs( |
||||
|
Filters=[{'Key': 'DatasetArn', |
||||
|
'Value': dataset_arn, |
||||
|
'Condition': 'IS'}]) |
||||
|
import_jobs = response["DatasetImportJobs"] |
||||
|
if (len(import_jobs) == 0 or |
||||
|
import_jobs[0]["Status"] != "ACTIVE"): |
||||
|
all_datasets_imported = False |
||||
|
break |
||||
|
if all_datasets_imported: |
||||
|
break |
||||
|
time.sleep(10) |
||||
|
response = forecast_client.describe_dataset( |
||||
|
DatasetArn=self.dataset_arn) |
||||
|
featurization_config = response['DataFrequency'] |
||||
|
response = forecast_client.create_predictor( |
||||
|
PredictorName=self.predictor_name, |
||||
|
AlgorithmArn=self.predictor_algorithm, |
||||
|
ForecastHorizon=1, |
||||
|
PerformAutoML=False, |
||||
|
PerformHPO=False, |
||||
|
InputDataConfig={ |
||||
|
'DatasetGroupArn': self.dataset_group_arn, |
||||
|
}, |
||||
|
FeaturizationConfig={ |
||||
|
'ForecastFrequency': featurization_config, |
||||
|
} |
||||
|
) |
||||
|
self.write({ |
||||
|
'predictor_arn': response['PredictorArn'], |
||||
|
'state': 'forecast' |
||||
|
}) |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
def action_create_forecast(self): |
||||
|
"""To create the forecast based on our data""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
forecast_client = session.client('forecast') |
||||
|
try: |
||||
|
response = forecast_client.create_forecast( |
||||
|
ForecastName=self.forecast_name, |
||||
|
PredictorArn=self.predictor_arn |
||||
|
) |
||||
|
self.write({ |
||||
|
'forecast_arn': response['ForecastArn'], |
||||
|
'state': 'query_forecast' |
||||
|
}) |
||||
|
while True: |
||||
|
response = forecast_client.describe_forecast( |
||||
|
ForecastArn=response['ForecastArn']) |
||||
|
status = response['Status'] |
||||
|
if status == 'ACTIVE': |
||||
|
break |
||||
|
elif status == 'FAILED': |
||||
|
raise UserError(_('Error: Forecast creation failed.')) |
||||
|
break |
||||
|
else: |
||||
|
time.sleep(10) |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
time.sleep(60) |
||||
|
|
||||
|
def query_forecast(self): |
||||
|
"""To get the forecast result""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
forecast_client = session.client('forecastquery') |
||||
|
try: |
||||
|
forecast_client.query_forecast( |
||||
|
ForecastArn=self.forecast_arn, |
||||
|
Filters={ |
||||
|
'item_id': self.item_id |
||||
|
} |
||||
|
) |
||||
|
return { |
||||
|
'type': 'ir.actions.client', |
||||
|
'tag': 'forecast', |
||||
|
} |
||||
|
except forecast_client.exceptions.ClientError as e: |
||||
|
raise UserError(e) |
||||
|
|
||||
|
@api.model |
||||
|
def get_query_result(self): |
||||
|
"""To get the response from the query forecast""" |
||||
|
values = self.forecast_values() |
||||
|
amazon_forecast = values['amazon_forecast'] |
||||
|
if amazon_forecast: |
||||
|
amazon_access_key = values['amazon_access_key'] |
||||
|
amazon_secret_access_key = values['amazon_secret_access_key'] |
||||
|
amazon_region = values['amazon_region'] |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=amazon_access_key, |
||||
|
aws_secret_access_key=amazon_secret_access_key, |
||||
|
region_name=amazon_region |
||||
|
) |
||||
|
forecast_client = session.client('forecastquery') |
||||
|
data = self.search([], limit=1) |
||||
|
response = forecast_client.query_forecast( |
||||
|
ForecastArn=data.forecast_arn, |
||||
|
Filters={ |
||||
|
'item_id': data.item_id |
||||
|
} |
||||
|
) |
||||
|
forecast_result = response['Forecast']['Predictions'] |
||||
|
return forecast_result |
@ -0,0 +1,86 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
import csv |
||||
|
import xmlrpc.client |
||||
|
from odoo import fields, models, _ |
||||
|
|
||||
|
|
||||
|
class AmazonFetchData(models.Model): |
||||
|
"""Class for fetching the data from our database""" |
||||
|
_name = "amazon.fetch.data" |
||||
|
_description = "To Fetch Data" |
||||
|
_rec_name = "db_name" |
||||
|
|
||||
|
url = fields.Char(string="URL", required=True, |
||||
|
help="Provide the URL in correct format.") |
||||
|
db_name = fields.Char(string="Database Name", required=True, |
||||
|
help="Provide the database name.") |
||||
|
db_username = fields.Char(string="Database Username", required=True, |
||||
|
help="Provide the Username.") |
||||
|
db_password = fields.Char(string="Database Password", required=True, |
||||
|
help="Provide the db password.") |
||||
|
csv_file_path = fields.Char(string="File Path", required=True, |
||||
|
help="Provide the file location path.") |
||||
|
|
||||
|
def action_fetch_data(self): |
||||
|
"""To fetch the data from the database""" |
||||
|
common = xmlrpc.client.ServerProxy(f'{self.url}/xmlrpc/2/common') |
||||
|
uid = common.authenticate(self.db_name, self.db_username, |
||||
|
self.db_password, {}) |
||||
|
model = xmlrpc.client.ServerProxy(f'{self.url}/xmlrpc/2/object') |
||||
|
model_name = 'stock.move' |
||||
|
field = ['product_id', 'date', 'product_uom_qty', 'id', 'reference', |
||||
|
'location_id', 'location_dest_id', 'origin'] |
||||
|
data = model.execute_kw(self.db_name, uid, self.db_password, |
||||
|
model_name, 'search_read', |
||||
|
[[]], {'fields': field}) |
||||
|
for item in data: |
||||
|
item['product_id'] = item['product_id'][1] if item[ |
||||
|
'product_id'] else '' |
||||
|
item['location_id'] = item['location_id'][1] if item[ |
||||
|
'location_id'] else '' |
||||
|
item['location_dest_id'] = item['location_dest_id'][1] if item[ |
||||
|
'location_dest_id'] else '' |
||||
|
for items in data: |
||||
|
items['item_id'] = items.pop('product_id', '') |
||||
|
items['timestamp'] = items.pop('date', '') |
||||
|
items['demand'] = items.pop('product_uom_qty', '') |
||||
|
|
||||
|
new_headers = ['item_id', 'timestamp', 'demand', 'id', 'reference', |
||||
|
'location_id', 'location_dest_id', 'origin'] |
||||
|
with open(self.csv_file_path, 'w', newline='') as csv_file: |
||||
|
writer = csv.DictWriter(csv_file, fieldnames=new_headers) |
||||
|
writer.writeheader() |
||||
|
writer.writerows(data) |
||||
|
return { |
||||
|
'name': _('Push to Bucket'), |
||||
|
'type': 'ir.actions.act_window', |
||||
|
'res_model': 'amazon.bucket', |
||||
|
'view_mode': 'form', |
||||
|
'target': 'current' |
||||
|
} |
||||
|
|
||||
|
def get_file_path(self): |
||||
|
"""To get the file path""" |
||||
|
data = self.search([], limit=1) |
||||
|
file_path = data.csv_file_path |
||||
|
return file_path |
@ -0,0 +1,77 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Shafna K(odoo@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/>. |
||||
|
# |
||||
|
############################################################################### |
||||
|
import boto3 |
||||
|
from botocore.exceptions import ClientError |
||||
|
from odoo import fields, models, _ |
||||
|
from odoo.exceptions import UserError |
||||
|
|
||||
|
|
||||
|
class ResConfigSettings(models.TransientModel): |
||||
|
"""Class inheriting the res config settings for adding fields for the |
||||
|
authentication of AWS with Odoo""" |
||||
|
_inherit = 'res.config.settings' |
||||
|
|
||||
|
amazon_forecast = fields.Boolean( |
||||
|
string="Amazon Forecast", required=True, |
||||
|
config_parameter='amazon_forecast_integration.amazon_forecast', |
||||
|
help="Enable to use Amazon Forecast services.") |
||||
|
amazon_access_key = fields.Char( |
||||
|
string="Access Key", help="Provide the access key.", required=True, |
||||
|
config_parameter='amazon_forecast_integration.amazon_access_key') |
||||
|
amazon_secret_access_key = fields.Char( |
||||
|
string="Secret Access Key", required=True, |
||||
|
config_parameter='amazon_forecast_integration.amazon_secret_access_key', |
||||
|
help="Provide the secret access key.") |
||||
|
amazon_region = fields.Char( |
||||
|
string="Access Key", help="Provide the region.", required=True, |
||||
|
config_parameter='amazon_forecast_integration.amazon_amazon_region') |
||||
|
|
||||
|
def authenticate_amazon_forecast(self): |
||||
|
"""Function to authenticate the AWS connection with Odoo""" |
||||
|
try: |
||||
|
session = boto3.Session( |
||||
|
aws_access_key_id=self.amazon_access_key, |
||||
|
aws_secret_access_key=self.amazon_secret_access_key, |
||||
|
region_name=self.amazon_region |
||||
|
) |
||||
|
iam_client = session.client('iam') |
||||
|
response = iam_client.list_users() |
||||
|
return response |
||||
|
except ClientError: |
||||
|
error_message = _('Invalid Amazon Forecast credentials. Please' |
||||
|
' verify your access key and secret key.') |
||||
|
raise UserError(error_message) |
||||
|
|
||||
|
def set_values(self): |
||||
|
"""To set the values in the corresponding fields""" |
||||
|
super().set_values() |
||||
|
if self.amazon_forecast: |
||||
|
self.authenticate_amazon_forecast() |
||||
|
self.env['ir.config_parameter'].set_param( |
||||
|
'amazon_forecast_integration.amazon_forecast_access_key', |
||||
|
self.amazon_access_key or '') |
||||
|
self.env['ir.config_parameter'].set_param( |
||||
|
'amazon_forecast_integration.amazon_forecast_secret_key', |
||||
|
self.amazon_secret_access_key or '') |
||||
|
self.env['ir.config_parameter'].set_param( |
||||
|
'amazon_forecast_integration.amazon_region', |
||||
|
self.amazon_region or '') |
|
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: 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: 105 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 77 KiB |
After Width: | Height: | Size: 193 KiB |
After Width: | Height: | Size: 148 KiB |
After Width: | Height: | Size: 133 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 63 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 75 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 87 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 117 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 158 KiB |
After Width: | Height: | Size: 172 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 373 KiB |
After Width: | Height: | Size: 99 KiB |
After Width: | Height: | Size: 9.0 KiB |
@ -0,0 +1,657 @@ |
|||||
|
<div style="background-color: #714B67; height: 810px; width: 100%; padding: 15px; position: relative;"> |
||||
|
<!-- TITLE BAR --> |
||||
|
<div class="d-flex align-items-center justify-content-between" |
||||
|
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>Enterprise |
||||
|
</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>Odoo.sh |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
<!-- END OF TITLE BAR --> |
||||
|
<div class="container"> |
||||
|
<div class="row"> |
||||
|
<div class="col-sm-12 col-md-12 col-lg-12"> |
||||
|
<!-- APP HERO --> |
||||
|
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;"> |
||||
|
Amazon Forecast Integration |
||||
|
</h1> |
||||
|
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;"> |
||||
|
This module helps to create Forecast of our Stock based on Demand. |
||||
|
</p> |
||||
|
<!-- END OF APP HERO --> |
||||
|
<img src="assets/screenshots/forecast.gif" class="img-responsive" |
||||
|
style="width: 100%; margin-left: auto; margin-right: auto;"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</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"> |
||||
|
This module helps to get the Forecast Report of our Stock based on the |
||||
|
historical Time series Data we provide. |
||||
|
</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;"> Utilize Amazon Forecast to generate Accurate |
||||
|
Demand Forecasts based on historical data from Odoo.</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;">The Forecasts can be used to optimize Inventory |
||||
|
levels and plan Procurement.</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;">Forecasting helps to prevent Stock outs and |
||||
|
Overstock situations, reducing holding Costs and improving |
||||
|
Customer satisfaction.</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;"> |
||||
|
Login to the Amazon Account and Click on Security |
||||
|
Credentials to Create Access key. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-01.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Note down the Access Key and the Secret Access Key generated |
||||
|
when Creating the Access Key. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-02.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Provide these Access Key, Secret Access Key and the AWS region |
||||
|
in Configuration Settings of Inventory. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-03.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Click on the Menu Item Amazon Forecast and Click on Fetch Data |
||||
|
to Fetch the Data for Forecasting,Note that for Forecasting, a |
||||
|
Historical Data is needed to get the Result. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-04.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Click on the Fetch Data button for Fetching Data. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-05.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Now Click on Amazon Forecast and Click on Bucket to Create |
||||
|
Bucket in Amazon S3.Provide a name for the bucket meeting the |
||||
|
naming format allowed in S3. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-06.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Click on Push to Bucket Button to push the Data to Amazon S3 |
||||
|
Bucket. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-07.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
S3 URI field will be Updated and state is changed to Pushed. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-08.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Next, Click on Forecast Menu and Create a Table in Dynamodb |
||||
|
and then Table Arn wil be Updated. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-09.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Create Role by clicking on Create Role Button after providing |
||||
|
the Role Name. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-10.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Choose the Bucket Name from the Bucket List and Create a KMS |
||||
|
Key by giving KMS Alias Name.KMS Arn will be Updated. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-11.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Create Policy by giving policy name and Policy Arn will be |
||||
|
Updated. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-12.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Create Dataset Group and Dataset by Clicking on Create Dataset |
||||
|
after providing the Dataset Group Name and Dataset Name. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-13.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Import the Dataset from the S3 Bucket by Clicking on Import |
||||
|
Dataset Job After giving the Import Job Name. This requires |
||||
|
some time and ensure that the Data we are using have enough |
||||
|
historical Data. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-14.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
After Importing, now you can Create Predictor function by |
||||
|
giving Predictor Name and then CLicking on Create Predictor |
||||
|
Button. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-15.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
After completing the Predictor function Creation, Create |
||||
|
Forecast by giving the Forecast Name and the Item Id and then |
||||
|
the Forecast Arn will be Updated. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-16.png" class="img-thumbnail"> |
||||
|
</div> |
||||
|
<div style="display: block; margin: 30px auto;"> |
||||
|
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
||||
|
Clicking on the Query Forecast Button, you can get the Forecast |
||||
|
Graph. |
||||
|
</h3> |
||||
|
<img src="assets/screenshots/forecast-17.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/inventory_turnover_report_analysis/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/1.jpg"> |
||||
|
</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/product_brand_inventory/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/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/16.0/low_stocks_product_alert/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/3.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/stock_intercompany_transfer/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/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/16.0/product_deletion/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/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/16.0/quick_stock_movement/" |
||||
|
target="_blank"> |
||||
|
<div style="border-radius:10px"> |
||||
|
<img class="img img-responsive center-block" |
||||
|
style="border-radius: 0px;" |
||||
|
src="assets/modules/6.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 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 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 86068 27707</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,97 @@ |
|||||
|
/** @odoo-module **/ |
||||
|
|
||||
|
/** |
||||
|
* This module defines a custom Odoo web client action 'forecast'. |
||||
|
* The action generates a scatter chart to display forecast data from the |
||||
|
'amazon.dataset' model. |
||||
|
* The chart represents forecast data for three points: p10, p50, and p90. |
||||
|
*/ |
||||
|
import { registry } from '@web/core/registry'; |
||||
|
const actionRegistry = registry.category("actions"); |
||||
|
const { useRef, onMounted, useState } = owl; |
||||
|
import rpc from 'web.rpc'; |
||||
|
import { browser } from '@web/core/browser/browser'; |
||||
|
const { Component } = owl; |
||||
|
|
||||
|
class ForecastReport extends Component{ |
||||
|
/** |
||||
|
* It is responsible for initializing the component and its state. |
||||
|
*/ |
||||
|
setup(){ |
||||
|
super.setup(...arguments); |
||||
|
this.forecastBarGraph = useRef("bubble-chart"); |
||||
|
this.FinalData = useState({ |
||||
|
ForecastData:[], |
||||
|
}) |
||||
|
onMounted(async () =>{ |
||||
|
await this.forecastGraph(); |
||||
|
}); |
||||
|
} |
||||
|
/** |
||||
|
* The 'forecastGraph' function asynchronously retrieves forecast data from the 'amazon.dataset' model using RPC. |
||||
|
* It then generates a scatter chart using the Chart.js library to visualize the data. |
||||
|
*/ |
||||
|
async forecastGraph(){ |
||||
|
let abc = await rpc.query({ |
||||
|
model:'amazon.dataset', |
||||
|
method:'get_query_result', |
||||
|
}).then(val => { |
||||
|
this.FinalData.ForecastData = val |
||||
|
}); |
||||
|
const { p10, p50, p90 } = this.FinalData.ForecastData |
||||
|
function getMonthNameFromDate(dateString) { |
||||
|
const date = new Date(dateString); |
||||
|
const monthName = date.toLocaleString('default', { month: 'long' }); |
||||
|
return monthName; |
||||
|
} |
||||
|
const userDate = p10[0].Timestamp; |
||||
|
const monthName = getMonthNameFromDate(userDate); |
||||
|
let scatterChart = this.forecastBarGraph.el |
||||
|
new Chart(scatterChart, { |
||||
|
type: 'scatter', |
||||
|
data: { |
||||
|
datasets: [{ |
||||
|
label: 'Forecast in '+ monthName, |
||||
|
data: [ |
||||
|
{ 'date': p10[0].Timestamp, 'demand': p10[0].Value }, |
||||
|
{ 'date': p50[0].Timestamp, 'demand': p50[0].Value}, |
||||
|
{ 'date': p90[0].Timestamp, 'demand': p90[0].Value} |
||||
|
], |
||||
|
borderColor: 'rgb(0, 0, 255)', |
||||
|
backgroundColor: 'rgba(0, 0, 255, 0.5)' |
||||
|
}] |
||||
|
}, |
||||
|
options: { |
||||
|
parsing: { |
||||
|
xAxisKey: 'date', |
||||
|
yAxisKey: 'demand' |
||||
|
}, |
||||
|
scales: { |
||||
|
y: { |
||||
|
ticks: { |
||||
|
stepSize: 1 |
||||
|
} |
||||
|
}, |
||||
|
x: { |
||||
|
type: 'time', |
||||
|
time: { |
||||
|
unit: 'day', |
||||
|
}, |
||||
|
ticks: { |
||||
|
autoSkip: false, |
||||
|
stepSize: 1, |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
goBack() { |
||||
|
// this.trigger('back');
|
||||
|
browser.history.go(-1) |
||||
|
|
||||
|
|
||||
|
} |
||||
|
} |
||||
|
ForecastReport.template = "ForecastReport" |
||||
|
actionRegistry.add('forecast', ForecastReport); |
@ -0,0 +1,27 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<!-- QWeb Template for forecast graphical view--> |
||||
|
<templates id="template" xml:space="preserve"> |
||||
|
<t t-name="ForecastReport" owl="1"> |
||||
|
<div> |
||||
|
<center> |
||||
|
<h1> |
||||
|
Forecast Report |
||||
|
</h1> |
||||
|
</center> |
||||
|
</div> |
||||
|
<button class="btn btn-primary" t-on-click="goBack">Go Back</button> |
||||
|
<br/><br/> |
||||
|
<div class="col-md-5" style="margin-left: 20px;"> |
||||
|
<div class="shadow-lg p-3 mb-5 bg-white rounded" |
||||
|
style="height: 700px; width: 1000px;"> |
||||
|
<h2>Forecast Result</h2> |
||||
|
<span>X axis: Demand</span> |
||||
|
<span> Y Axis: Date</span> |
||||
|
<hr/> |
||||
|
<div class="bar"> |
||||
|
<canvas id="bubbleChart" t-ref="bubble-chart"/> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</t> |
||||
|
</templates> |
@ -0,0 +1,40 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!--Tree view of Amazon Bucket lists--> |
||||
|
<record id="amazon_bucket_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">amazon.bucket.view.tree</field> |
||||
|
<field name="model">amazon.bucket</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="bucket_name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Form View of Amazon Bucket--> |
||||
|
<record id="amazon_bucket_view_form" model="ir.ui.view"> |
||||
|
<field name="name">amazon.bucket.view.form</field> |
||||
|
<field name="model">amazon.bucket</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<header> |
||||
|
<button name="action_s3bucket" string="Create Bucket" type="object" class="oe_highlight"/> |
||||
|
<button name="action_s3bucket_push" string="Push To Bucket" type="object" class="oe_highlight"/> |
||||
|
<field name="state" widget="statusbar"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<field name="bucket_name"/> |
||||
|
<field name="file_path" attrs="{'invisible': [('state', 'in', ('create_bucket'))]}"/> |
||||
|
<field name="s3_uri" attrs="{'invisible': [('state', 'in', ('create_bucket', 'push_bucket'))]}"/> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Window action amazon bucket creation--> |
||||
|
<record id="action_amazon_bucket" model="ir.actions.act_window"> |
||||
|
<field name="name">Bucket</field> |
||||
|
<field name="res_model">amazon.bucket</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,66 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!--Tree view of amazon dataset for amazon forecasting--> |
||||
|
<record id="amazon_dataset_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">amazon.dataset.view.tree</field> |
||||
|
<field name="model">amazon.dataset</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="role_name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Form view of amazon dataset for amazon forecasting--> |
||||
|
<record id="amazon_dataset_view_form" model="ir.ui.view"> |
||||
|
<field name="name">amazon.dataset.view.form</field> |
||||
|
<field name="model">amazon.dataset</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<header> |
||||
|
<button name="action_create_table" string="Create Table" type="object" states="table" class="oe_highlight"/> |
||||
|
<button name="action_create_role" string="Create Role" type="object" states="role" class="oe_highlight"/> |
||||
|
<button name="action_create_kms" string="Create KMS" type="object" states="kms" class="oe_highlight"/> |
||||
|
<button name="action_create_policy" string="Create Policy" type="object" states="policy" class="oe_highlight"/> |
||||
|
<button name="action_create_dataset" string="Create Dataset" type="object" states="dataset" class="oe_highlight"/> |
||||
|
<button name="action_import_dataset" string="Import Dataset Job" type="object" states="import_dataset" class="oe_highlight"/> |
||||
|
<button name="action_create_predictor" string="Create Predictor" type="object" class="oe_highlight"/> |
||||
|
<button name="action_create_forecast" string="Create Forecast" type="object" class="oe_highlight"/> |
||||
|
<button name="query_forecast" string="Query Forecast" type="object" class="oe_highlight"/> |
||||
|
<field name="state" widget="statusbar" statusbar_visible="table,import_dataset,query_forecast"/> |
||||
|
</header> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<field name="table_name" /> |
||||
|
<field name="table_arn" /> |
||||
|
<field name="role_name" attrs="{'invisible': [('state', 'in', ('table'))], 'required': [('state', 'not in', ('table'))]}"/> |
||||
|
<field name="role_arn" attrs="{'invisible': [('state', 'in', ('table'))]}"/> |
||||
|
<field name="bucket_id" attrs="{'invisible': [('state', 'in', ('table', 'role'))], 'required': [('state', 'not in', ('table', 'role'))]}"/> |
||||
|
<field name="kms_alias" attrs="{'invisible': [('state', 'in', ('table', 'role'))], 'required': [('state', 'not in', ('table', 'role'))]}"/> |
||||
|
<field name="kms_arn" attrs="{'invisible': [('state', 'in', ('table', 'role'))]}"/> |
||||
|
<field name="policy_name" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms'))], 'required': [('state', 'not in', ('table', 'role', 'kms'))]}"/> |
||||
|
<field name="policy_arn" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms'))]}"/> |
||||
|
<field name="dataset_group" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy'))], 'required': [('state', 'not in', ('table', 'role', 'kms', 'policy'))]}"/> |
||||
|
<field name="dataset_group_arn" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy','dataset'))]}"/> |
||||
|
<field name="dataset" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy'))], 'required': [('state', 'not in', ('table', 'role', 'kms', 'policy'))]}"/> |
||||
|
<field name="dataset_arn" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy','dataset'))]}"/> |
||||
|
<field name="forecast_frequency" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy'))]}"/> |
||||
|
<field name="import_job_name" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset'))]}"/> |
||||
|
<field name="import_job_arn" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset'))]}"/> |
||||
|
<field name="predictor_name" attrs="{'invisible': ['|', ('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset')),('import_job_arn', '=', False)]}"/> |
||||
|
<field name="predictor_algorithm" attrs="{'invisible': ['|', ('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset')), ('import_job_arn', '=', False)]}"/> |
||||
|
<field name="predictor_arn" attrs="{'invisible': ['|', ('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset')), ('import_job_arn', '=', False)]}"/> |
||||
|
<field name="forecast_name" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset', 'import_dataset'))], 'required': [('state', 'not in', ('table', 'role', 'kms', 'policy', 'dataset', 'import_dataset'))]}"/> |
||||
|
<field name="item_id" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset', 'import_dataset', 'predictor'))], 'required': [('state', 'not in', ('table', 'role', 'kms', 'policy', 'dataset', 'import_dataset', 'predictor'))]}"/> |
||||
|
<field name="forecast_arn" attrs="{'invisible': [('state', 'in', ('table', 'role', 'kms', 'policy', 'dataset', 'import_dataset', 'predictor'))]}"/> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Window action for Amazon Dataset --> |
||||
|
<record id="action_amazon_dataset" model="ir.actions.act_window"> |
||||
|
<field name="name">Forecast</field> |
||||
|
<field name="res_model">amazon.dataset</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,38 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!--Tree view for fetching data from database--> |
||||
|
<record id="fetch_data_view_tree" model="ir.ui.view"> |
||||
|
<field name="name">amazon.fetch.data.view.tree</field> |
||||
|
<field name="model">amazon.fetch.data</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<tree> |
||||
|
<field name="db_name"/> |
||||
|
</tree> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Form view for fetching data from database--> |
||||
|
<record id="fetch_data_view_form" model="ir.ui.view"> |
||||
|
<field name="name">amazon.fetch.data.view.form</field> |
||||
|
<field name="model">amazon.fetch.data</field> |
||||
|
<field name="arch" type="xml"> |
||||
|
<form> |
||||
|
<sheet> |
||||
|
<group> |
||||
|
<field name="url"/> |
||||
|
<field name="db_name"/> |
||||
|
<field name="db_username"/> |
||||
|
<field name="db_password" password="True"/> |
||||
|
<field name="csv_file_path"/> |
||||
|
<button name="action_fetch_data" string="Fetch Data" type="object" class="oe_highlight"/> |
||||
|
</group> |
||||
|
</sheet> |
||||
|
</form> |
||||
|
</field> |
||||
|
</record> |
||||
|
<!--Window Action of amazon fetch data--> |
||||
|
<record id="action_amazon_fetch_data" model="ir.actions.act_window"> |
||||
|
<field name="name">Fetch Data</field> |
||||
|
<field name="res_model">amazon.fetch.data</field> |
||||
|
<field name="view_mode">tree,form</field> |
||||
|
</record> |
||||
|
</odoo> |
@ -0,0 +1,20 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8" ?> |
||||
|
<odoo> |
||||
|
<!--Menu items for Amazon forecasting operations--> |
||||
|
<menuitem id="menu_stock_forecast" |
||||
|
name="Amazon Forecast" |
||||
|
parent="stock.menu_stock_root" |
||||
|
sequence="15"/> |
||||
|
<menuitem id="menu_dynamodb" |
||||
|
action="action_amazon_dataset" |
||||
|
parent="menu_stock_forecast" |
||||
|
sequence="11"/> |
||||
|
<menuitem id="menu_s3bucket" |
||||
|
action="action_amazon_bucket" |
||||
|
parent="menu_stock_forecast" |
||||
|
sequence="15"/> |
||||
|
<menuitem id="menu_fetch_data" |
||||
|
action="action_amazon_fetch_data" |
||||
|
parent="menu_stock_forecast" |
||||
|
sequence="14"/> |
||||
|
</odoo> |
@ -0,0 +1,63 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<!--Form view for adding field for the authentication with AWS--> |
||||
|
<record id="res_config_settings_view_form" model="ir.ui.view"> |
||||
|
<field name="name"> |
||||
|
res.config.settings.view.form.inherit.amazon.forecast.integration |
||||
|
</field> |
||||
|
<field name="model">res.config.settings</field> |
||||
|
<field name="inherit_id" ref="stock.res_config_settings_view_form"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<div name="barcode_setting_container" position="after"> |
||||
|
<h2>Forecast</h2> |
||||
|
<div class="row mt16 o_settings_container" |
||||
|
name="forecast_setting_container"> |
||||
|
<div class="col-12 col-lg-6 o_setting_box" |
||||
|
id="forecast_process"> |
||||
|
<div class="o_setting_left_pane"> |
||||
|
<field name="amazon_forecast" widget="upgrade_boolean"/> |
||||
|
</div> |
||||
|
<div class="o_setting_right_pane" |
||||
|
id="forecast_settings"> |
||||
|
<label for="amazon_forecast"/> |
||||
|
<span class="fa fa-lg fa-building-o" |
||||
|
title="Values set here are company-specific." |
||||
|
groups="base.group_multi_company"/> |
||||
|
<div class="text-muted" name="stock_forecast"> |
||||
|
Forecast helps to predict stock and maintain |
||||
|
inventory correctly |
||||
|
</div> |
||||
|
<div class="content-group" |
||||
|
attrs="{'invisible': [('amazon_forecast','=',False)]}"> |
||||
|
<div class="mt16"> |
||||
|
<span class="col-lg-3">Access Key: |
||||
|
<field name="amazon_access_key" |
||||
|
attrs="{'required': [('amazon_forecast', '=', True)]}"/> |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="content-group" |
||||
|
attrs="{'invisible': [('amazon_forecast','=',False)]}"> |
||||
|
<div class="mt16"> |
||||
|
<span class="col-lg-3">Secret Access Key: |
||||
|
<field name="amazon_secret_access_key" password="True" |
||||
|
attrs="{'required': [('amazon_forecast', '=', True)]}"/> |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="content-group" |
||||
|
attrs="{'invisible': [('amazon_forecast','=',False)]}"> |
||||
|
<div class="mt16"> |
||||
|
<span class="col-lg-3">Region: |
||||
|
<field name="amazon_region" |
||||
|
attrs="{'required': [('amazon_forecast', '=', True)]}"/> |
||||
|
</span> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |