diff --git a/amazon_forecast_integration/README.rst b/amazon_forecast_integration/README.rst new file mode 100644 index 000000000..e6ba97805 --- /dev/null +++ b/amazon_forecast_integration/README.rst @@ -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 `__ + +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 `__ + +Further information +------------------- +HTML Description: ``__ diff --git a/amazon_forecast_integration/__init__.py b/amazon_forecast_integration/__init__.py new file mode 100644 index 000000000..7d40135a6 --- /dev/null +++ b/amazon_forecast_integration/__init__.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +from . import models diff --git a/amazon_forecast_integration/__manifest__.py b/amazon_forecast_integration/__manifest__.py new file mode 100644 index 000000000..16b74d03c --- /dev/null +++ b/amazon_forecast_integration/__manifest__.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +{ + '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, +} diff --git a/amazon_forecast_integration/doc/RELEASE_NOTES.md b/amazon_forecast_integration/doc/RELEASE_NOTES.md new file mode 100644 index 000000000..8e2103a56 --- /dev/null +++ b/amazon_forecast_integration/doc/RELEASE_NOTES.md @@ -0,0 +1,7 @@ +## Module + +#### 20.04.2024 +#### Version 16.0.1.0.0 +#### ADD + +- Initial Commit for Amazon Forecast Integration \ No newline at end of file diff --git a/amazon_forecast_integration/models/__init__.py b/amazon_forecast_integration/models/__init__.py new file mode 100644 index 000000000..eae324aea --- /dev/null +++ b/amazon_forecast_integration/models/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +from . import amazon_bucket +from . import amazon_dataset +from . import amazon_fetch_data +from . import res_config_settings diff --git a/amazon_forecast_integration/models/amazon_bucket.py b/amazon_forecast_integration/models/amazon_bucket.py new file mode 100644 index 000000000..14f3db533 --- /dev/null +++ b/amazon_forecast_integration/models/amazon_bucket.py @@ -0,0 +1,136 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +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"}) diff --git a/amazon_forecast_integration/models/amazon_dataset.py b/amazon_forecast_integration/models/amazon_dataset.py new file mode 100644 index 000000000..e9bf2c93a --- /dev/null +++ b/amazon_forecast_integration/models/amazon_dataset.py @@ -0,0 +1,602 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +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 diff --git a/amazon_forecast_integration/models/amazon_fetch_data.py b/amazon_forecast_integration/models/amazon_fetch_data.py new file mode 100644 index 000000000..5319db714 --- /dev/null +++ b/amazon_forecast_integration/models/amazon_fetch_data.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +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 diff --git a/amazon_forecast_integration/models/res_config_settings.py b/amazon_forecast_integration/models/res_config_settings.py new file mode 100644 index 000000000..bc820b666 --- /dev/null +++ b/amazon_forecast_integration/models/res_config_settings.py @@ -0,0 +1,77 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Cybrosys Technologies Pvt. Ltd. +# +# Copyright (C) 2023-TODAY Cybrosys Technologies() +# 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 . +# +############################################################################### +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 '') diff --git a/amazon_forecast_integration/security/ir.model.access.csv b/amazon_forecast_integration/security/ir.model.access.csv new file mode 100644 index 000000000..83d25031b --- /dev/null +++ b/amazon_forecast_integration/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_amazon_dataset,access.amazon.dataset,model_amazon_dataset,base.group_user,1,1,1,1 +access_amazon_bucket,access.amazon.bucket,model_amazon_bucket,base.group_user,1,1,1,1 +access_amazon_fetch_data,access.amazon.fetch.data,model_amazon_fetch_data,base.group_user,1,1,1,1 diff --git a/amazon_forecast_integration/static/description/assets/icons/check.png b/amazon_forecast_integration/static/description/assets/icons/check.png new file mode 100644 index 000000000..c8e85f51d Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/check.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/chevron.png b/amazon_forecast_integration/static/description/assets/icons/chevron.png new file mode 100644 index 000000000..2089293d6 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/chevron.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/cogs.png b/amazon_forecast_integration/static/description/assets/icons/cogs.png new file mode 100644 index 000000000..95d0bad62 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/cogs.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/consultation.png b/amazon_forecast_integration/static/description/assets/icons/consultation.png new file mode 100644 index 000000000..8319d4baa Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/consultation.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/ecom-black.png b/amazon_forecast_integration/static/description/assets/icons/ecom-black.png new file mode 100644 index 000000000..a9385ff13 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/ecom-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/education-black.png b/amazon_forecast_integration/static/description/assets/icons/education-black.png new file mode 100644 index 000000000..3eb09b27b Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/education-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/hotel-black.png b/amazon_forecast_integration/static/description/assets/icons/hotel-black.png new file mode 100644 index 000000000..130f613be Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/hotel-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/license.png b/amazon_forecast_integration/static/description/assets/icons/license.png new file mode 100644 index 000000000..a5869797e Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/license.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/lifebuoy.png b/amazon_forecast_integration/static/description/assets/icons/lifebuoy.png new file mode 100644 index 000000000..658d56ccc Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/lifebuoy.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/manufacturing-black.png b/amazon_forecast_integration/static/description/assets/icons/manufacturing-black.png new file mode 100644 index 000000000..697eb0e9f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/manufacturing-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/pos-black.png b/amazon_forecast_integration/static/description/assets/icons/pos-black.png new file mode 100644 index 000000000..97c0f90c1 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/pos-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/puzzle.png b/amazon_forecast_integration/static/description/assets/icons/puzzle.png new file mode 100644 index 000000000..65cf854e7 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/puzzle.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/restaurant-black.png b/amazon_forecast_integration/static/description/assets/icons/restaurant-black.png new file mode 100644 index 000000000..4a35eb939 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/restaurant-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/service-black.png b/amazon_forecast_integration/static/description/assets/icons/service-black.png new file mode 100644 index 000000000..301ab51cb Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/service-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/trading-black.png b/amazon_forecast_integration/static/description/assets/icons/trading-black.png new file mode 100644 index 000000000..9398ba2f1 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/trading-black.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/training.png b/amazon_forecast_integration/static/description/assets/icons/training.png new file mode 100644 index 000000000..884ca024d Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/training.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/update.png b/amazon_forecast_integration/static/description/assets/icons/update.png new file mode 100644 index 000000000..ecbc5a01a Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/update.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/user.png b/amazon_forecast_integration/static/description/assets/icons/user.png new file mode 100644 index 000000000..6ffb23d9f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/user.png differ diff --git a/amazon_forecast_integration/static/description/assets/icons/wrench.png b/amazon_forecast_integration/static/description/assets/icons/wrench.png new file mode 100644 index 000000000..6c04dea0f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/icons/wrench.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/categories.png b/amazon_forecast_integration/static/description/assets/misc/categories.png new file mode 100644 index 000000000..bedf1e0b1 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/categories.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/check-box.png b/amazon_forecast_integration/static/description/assets/misc/check-box.png new file mode 100644 index 000000000..42caf24b9 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/check-box.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/compass.png b/amazon_forecast_integration/static/description/assets/misc/compass.png new file mode 100644 index 000000000..d5fed8faa Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/compass.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/corporate.png b/amazon_forecast_integration/static/description/assets/misc/corporate.png new file mode 100644 index 000000000..2eb13edbf Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/corporate.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/customer-support.png b/amazon_forecast_integration/static/description/assets/misc/customer-support.png new file mode 100644 index 000000000..79efc72ed Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/customer-support.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/cybrosys-logo.png b/amazon_forecast_integration/static/description/assets/misc/cybrosys-logo.png new file mode 100644 index 000000000..cc3cc0ccf Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/cybrosys-logo.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/features.png b/amazon_forecast_integration/static/description/assets/misc/features.png new file mode 100644 index 000000000..b41769f77 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/features.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/logo.png b/amazon_forecast_integration/static/description/assets/misc/logo.png new file mode 100644 index 000000000..478462d3e Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/logo.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/pictures.png b/amazon_forecast_integration/static/description/assets/misc/pictures.png new file mode 100644 index 000000000..56d255fe9 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/pictures.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/pie-chart.png b/amazon_forecast_integration/static/description/assets/misc/pie-chart.png new file mode 100644 index 000000000..426e05244 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/pie-chart.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/right-arrow.png b/amazon_forecast_integration/static/description/assets/misc/right-arrow.png new file mode 100644 index 000000000..730984a06 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/right-arrow.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/star.png b/amazon_forecast_integration/static/description/assets/misc/star.png new file mode 100644 index 000000000..2eb9ab29f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/star.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/support.png b/amazon_forecast_integration/static/description/assets/misc/support.png new file mode 100644 index 000000000..4f18b8b82 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/support.png differ diff --git a/amazon_forecast_integration/static/description/assets/misc/whatsapp.png b/amazon_forecast_integration/static/description/assets/misc/whatsapp.png new file mode 100644 index 000000000..d513a5356 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/misc/whatsapp.png differ diff --git a/amazon_forecast_integration/static/description/assets/modules/1.jpg b/amazon_forecast_integration/static/description/assets/modules/1.jpg new file mode 100644 index 000000000..c11ba602a Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/1.jpg differ diff --git a/amazon_forecast_integration/static/description/assets/modules/2.png b/amazon_forecast_integration/static/description/assets/modules/2.png new file mode 100644 index 000000000..e281bd660 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/2.png differ diff --git a/amazon_forecast_integration/static/description/assets/modules/3.png b/amazon_forecast_integration/static/description/assets/modules/3.png new file mode 100644 index 000000000..66e5f3dd1 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/3.png differ diff --git a/amazon_forecast_integration/static/description/assets/modules/4.png b/amazon_forecast_integration/static/description/assets/modules/4.png new file mode 100644 index 000000000..d21466f5f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/4.png differ diff --git a/amazon_forecast_integration/static/description/assets/modules/5.png b/amazon_forecast_integration/static/description/assets/modules/5.png new file mode 100644 index 000000000..baaa60084 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/5.png differ diff --git a/amazon_forecast_integration/static/description/assets/modules/6.png b/amazon_forecast_integration/static/description/assets/modules/6.png new file mode 100644 index 000000000..956a7e9c7 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/modules/6.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-01.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-01.png new file mode 100644 index 000000000..0216cd89f Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-01.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-02.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-02.png new file mode 100644 index 000000000..39ee052a1 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-02.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-03.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-03.png new file mode 100644 index 000000000..823166695 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-03.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-04.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-04.png new file mode 100644 index 000000000..c70910395 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-04.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-05.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-05.png new file mode 100644 index 000000000..0f9e3885b Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-05.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-06.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-06.png new file mode 100644 index 000000000..6f2b77637 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-06.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-07.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-07.png new file mode 100644 index 000000000..4840daa65 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-07.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-08.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-08.png new file mode 100644 index 000000000..c86a5ac13 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-08.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-09.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-09.png new file mode 100644 index 000000000..532f6d799 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-09.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-10.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-10.png new file mode 100644 index 000000000..d58de0676 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-10.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-11.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-11.png new file mode 100644 index 000000000..5450f03f6 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-11.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-12.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-12.png new file mode 100644 index 000000000..42cdad6a0 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-12.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-13.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-13.png new file mode 100644 index 000000000..03967c913 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-13.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-14.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-14.png new file mode 100644 index 000000000..2cb11363a Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-14.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-15.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-15.png new file mode 100644 index 000000000..8f3799336 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-15.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-16.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-16.png new file mode 100644 index 000000000..da3ac02b6 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-16.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast-17.png b/amazon_forecast_integration/static/description/assets/screenshots/forecast-17.png new file mode 100644 index 000000000..fe3a2f02e Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast-17.png differ diff --git a/amazon_forecast_integration/static/description/assets/screenshots/forecast.gif b/amazon_forecast_integration/static/description/assets/screenshots/forecast.gif new file mode 100644 index 000000000..c09186b46 Binary files /dev/null and b/amazon_forecast_integration/static/description/assets/screenshots/forecast.gif differ diff --git a/amazon_forecast_integration/static/description/banner.jpg b/amazon_forecast_integration/static/description/banner.jpg new file mode 100644 index 000000000..4261d5381 Binary files /dev/null and b/amazon_forecast_integration/static/description/banner.jpg differ diff --git a/amazon_forecast_integration/static/description/icon.png b/amazon_forecast_integration/static/description/icon.png new file mode 100644 index 000000000..c4c7b7955 Binary files /dev/null and b/amazon_forecast_integration/static/description/icon.png differ diff --git a/amazon_forecast_integration/static/description/index.html b/amazon_forecast_integration/static/description/index.html new file mode 100644 index 000000000..69a4b9efc --- /dev/null +++ b/amazon_forecast_integration/static/description/index.html @@ -0,0 +1,657 @@ +
+ +
+ +
+
+ Enterprise +
+
+ Odoo.sh +
+
+
+ +
+
+
+ +

+ Amazon Forecast Integration +

+

+ This module helps to create Forecast of our Stock based on Demand. +

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

+ Explore This Module

+
+ + + +
+
+ +
+

+ Overview +

+
+
+
+ This module helps to get the Forecast Report of our Stock based on the + historical Time series Data we provide. +
+
+ + +
+
+ +
+

+ Features +

+
+
+
+
+ + Utilize Amazon Forecast to generate Accurate + Demand Forecasts based on historical data from Odoo. +
+
+ + The Forecasts can be used to optimize Inventory + levels and plan Procurement. +
+
+ + Forecasting helps to prevent Stock outs and + Overstock situations, reducing holding Costs and improving + Customer satisfaction. +
+
+
+ + +
+
+ +
+

+ Screenshots +

+
+
+
+
+

+ Login to the Amazon Account and Click on Security + Credentials to Create Access key. +

+ +
+
+

+ Note down the Access Key and the Secret Access Key generated + when Creating the Access Key. +

+ +
+
+

+ Provide these Access Key, Secret Access Key and the AWS region + in Configuration Settings of Inventory. +

+ +
+
+

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

+ +
+
+

+ Click on the Fetch Data button for Fetching Data. +

+ +
+
+

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

+ +
+
+

+ Click on Push to Bucket Button to push the Data to Amazon S3 + Bucket. +

+ +
+
+

+ S3 URI field will be Updated and state is changed to Pushed. +

+ +
+
+

+ Next, Click on Forecast Menu and Create a Table in Dynamodb + and then Table Arn wil be Updated. +

+ +
+
+

+ Create Role by clicking on Create Role Button after providing + the Role Name. +

+ +
+
+

+ Choose the Bucket Name from the Bucket List and Create a KMS + Key by giving KMS Alias Name.KMS Arn will be Updated. +

+ +
+
+

+ Create Policy by giving policy name and Policy Arn will be + Updated. +

+ +
+
+

+ Create Dataset Group and Dataset by Clicking on Create Dataset + after providing the Dataset Group Name and Dataset Name. +

+ +
+
+

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

+ +
+
+

+ After Importing, now you can Create Predictor function by + giving Predictor Name and then CLicking on Create Predictor + Button. +

+ +
+
+

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

+ +
+
+

+ Clicking on the Query Forecast Button, you can get the Forecast + Graph. +

+ +
+
+
+ + +
+
+ +
+

+ Related Products +

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

+ Our Services

+
+
+
+
+
+ +
+
+ Odoo Customization
+
+
+
+ +
+
+ Odoo Implementation
+
+
+
+ +
+
+ Odoo Support
+
+
+
+ +
+
+ Hire Odoo Developer
+
+
+
+ +
+
+ Odoo Integration
+
+
+
+ +
+
+ Odoo Migration
+
+
+
+ +
+
+ Odoo Consultancy
+
+
+
+ +
+
+ Odoo Implementation
+
+
+
+ +
+
+ Odoo Licensing Consultancy
+
+
+
+ + +
+
+ +
+

+ Our Industries +

+
+
+
+
+
+ +
+ Trading
+

+ Easily procure and sell your products

+
+
+
+
+ +
+ POS
+

+ Easy configuration and convivial experience

+
+
+
+
+ +
+ Education
+

+ A platform for educational management

+
+
+
+
+ +
+ Manufacturing
+

+ Plan, track and schedule your operations

+
+
+
+
+ +
+ E-commerce & Website
+

+ Mobile friendly, awe-inspiring product pages

+
+
+
+
+ +
+ Service Management
+

+ Keep track of services and invoice

+
+
+
+
+ +
+ Restaurant
+

+ Run your bar or restaurant methodically

+
+
+
+
+ +
+ Hotel Management
+

+ An all-inclusive hotel management application

+
+
+
+
+ + +
+
+ +
+

+ Support +

+
+
+
+
+
+
+ +
+
+

Need Help?

+

Got questions or need help? + Get in touch.

+ +

+ odoo@cybrosys.com

+
+
+
+
+
+
+
+ +
+
+

WhatsApp

+

Say hi to us on WhatsApp!

+ +

+ +91 86068 27707

+
+
+
+
+
+
+
+ +
+
+
+ diff --git a/amazon_forecast_integration/static/src/js/graphView.js b/amazon_forecast_integration/static/src/js/graphView.js new file mode 100644 index 000000000..e3e530518 --- /dev/null +++ b/amazon_forecast_integration/static/src/js/graphView.js @@ -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); diff --git a/amazon_forecast_integration/static/src/xml/graph_view.xml b/amazon_forecast_integration/static/src/xml/graph_view.xml new file mode 100644 index 000000000..cdff37ce0 --- /dev/null +++ b/amazon_forecast_integration/static/src/xml/graph_view.xml @@ -0,0 +1,27 @@ + + + + +
+
+

+ Forecast Report +

+
+
+ +

+
+
+

Forecast Result

+ X axis: Demand + Y Axis: Date +
+
+ +
+
+
+
+
diff --git a/amazon_forecast_integration/views/amazon_bucket_views.xml b/amazon_forecast_integration/views/amazon_bucket_views.xml new file mode 100644 index 000000000..85052716e --- /dev/null +++ b/amazon_forecast_integration/views/amazon_bucket_views.xml @@ -0,0 +1,40 @@ + + + + + amazon.bucket.view.tree + amazon.bucket + + + + + + + + + amazon.bucket.view.form + amazon.bucket + +
+
+
+ + + + + + + +
+
+
+ + + Bucket + amazon.bucket + tree,form + +
diff --git a/amazon_forecast_integration/views/amazon_dataset_views.xml b/amazon_forecast_integration/views/amazon_dataset_views.xml new file mode 100644 index 000000000..c956ff7aa --- /dev/null +++ b/amazon_forecast_integration/views/amazon_dataset_views.xml @@ -0,0 +1,66 @@ + + + + + amazon.dataset.view.tree + amazon.dataset + + + + + + + + + amazon.dataset.view.form + amazon.dataset + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + + Forecast + amazon.dataset + tree,form + +
diff --git a/amazon_forecast_integration/views/amazon_fetch_data_views.xml b/amazon_forecast_integration/views/amazon_fetch_data_views.xml new file mode 100644 index 000000000..45379641d --- /dev/null +++ b/amazon_forecast_integration/views/amazon_fetch_data_views.xml @@ -0,0 +1,38 @@ + + + + + amazon.fetch.data.view.tree + amazon.fetch.data + + + + + + + + + amazon.fetch.data.view.form + amazon.fetch.data + +
+ + + + + + + +