Browse Source

Sep 13: [FIX] Bug fixed 'onedrive_integration_odoo'

16.0
Risvana Cybro 1 day ago
parent
commit
c5c5de0fe5
  1. 2
      onedrive_integration_odoo/__manifest__.py
  2. 5
      onedrive_integration_odoo/doc/RELEASE_NOTES.md
  3. 2
      onedrive_integration_odoo/hooks.py
  4. 53
      onedrive_integration_odoo/models/onedrive_dashboard.py
  5. 30
      onedrive_integration_odoo/models/res_config_settings.py
  6. BIN
      onedrive_integration_odoo/static/description/assets/screenshots/06.png
  7. BIN
      onedrive_integration_odoo/static/description/assets/screenshots/5.png
  8. 6
      onedrive_integration_odoo/static/description/index.html
  9. 1
      onedrive_integration_odoo/static/src/js/onedrive.js
  10. 34
      onedrive_integration_odoo/views/res_config_settings_views.xml
  11. 50
      onedrive_integration_odoo/wizard/upload_file.py

2
onedrive_integration_odoo/__manifest__.py

@ -21,7 +21,7 @@
###############################################################################
{
'name': "Onedrive Integration",
'version': "16.0.1.0.0'",
'version': "16.0.1.0.2'",
'category': "Document Management",
'summary': """Upload and download files in Onedrive using odoo """,
'description': """This module was developed to upload files to Onedrive as

5
onedrive_integration_odoo/doc/RELEASE_NOTES.md

@ -8,3 +8,8 @@
#### Version 16.0.1.0.1
#### UPDT
- Added modules field and changed view
#### 12.09.2025
#### Version 16.0.1.0.2
##### BUG FIX
- Updated the module workflow to remove the dependency on manually fetching the folder ID. Instead, a new Folder Name field was introduced in the Odoo settings, and the system now automatically retrieves the corresponding Folder ID from the Microsoft Graph API and stores it internally.

2
onedrive_integration_odoo/hooks.py

@ -35,3 +35,5 @@ def uninstall_hook(cr, registry):
[('key', '=', 'onedrive_integration_odoo.folder_id')]).unlink()
env['ir.config_parameter'].sudo().search(
[('key', '=', 'onedrive_integration_odoo.onedrive_button')]).unlink()
env['ir.config_parameter'].sudo().search(
[('key', '=', 'onedrive_integration_odoo.folder_id')]).unlink()

53
onedrive_integration_odoo/models/onedrive_dashboard.py

@ -53,9 +53,7 @@ class OneDriveDashboard(models.Model):
help="Binary field to store the uploaded file.")
def get_tokens(self, authorize_code):
"""
Generate onedrive tokens from authorization code
"""
""" Generate onedrive tokens from authorization code """
data = {
'code': authorize_code,
'client_id': self.env['ir.config_parameter'].get_param(
@ -76,12 +74,26 @@ class OneDriveDashboard(models.Model):
response = res.content and res.json() or {}
if response:
expires_in = response.get('expires_in')
self.env['onedrive.dashboard'].create({
record = self.env['onedrive.dashboard'].create({
'onedrive_access_token': response.get('access_token'),
'onedrive_refresh_token': response.get('refresh_token'),
'token_expiry_date': fields.Datetime.now() + timedelta(
seconds=expires_in) if expires_in else False,
})
folder_name = self.env['ir.config_parameter'].get_param(
'onedrive_integration_odoo.folder_name', '')
if folder_name:
url = f"https://graph.microsoft.com/v1.0/me/drive/root:/{folder_name}"
res_folder = requests.get(url, headers={
'Authorization': f'Bearer {response.get("access_token")}'
}).json()
if "id" in res_folder:
self.env['ir.config_parameter'].set_param(
'onedrive_integration_odoo.folder_id',
res_folder["id"]
)
except requests.HTTPError as error:
_logger.exception("Bad microsoft onedrive request : %s !",
error.response.content)
@ -131,20 +143,35 @@ class OneDriveDashboard(models.Model):
return False
if record.token_expiry_date <= str(fields.Datetime.now()):
record.generate_onedrive_refresh_token()
folder = self.env['ir.config_parameter'].get_param(
'onedrive_integration_odoo.folder_id', '')
if not folder: return False
url = "https://graph.microsoft.com/v1.0/me/drive/items/%s/children" \
"?Content-Type=application/json" % folder
response = requests.request("GET", url, headers={
'Authorization': 'Bearer "' + record.onedrive_access_token + '"'},
data={})
'onedrive_integration_odoo.folder_id', ''
)
if not folder:
url = f"https://graph.microsoft.com/v1.0/me/drive/root:/{folder_name}"
response = requests.get(url, headers={
'Authorization': f'Bearer {record.onedrive_access_token}'
})
folder_data = response.json()
if "id" in folder_data:
folder = folder_data["id"]
self.env['ir.config_parameter'].set_param(
'onedrive_integration_odoo.folder_id', folder
)
else:
return ['error', 'itemNotFound', 'Folder not found in OneDrive']
url = f"https://graph.microsoft.com/v1.0/me/drive/items/{folder}/children"
response = requests.get(url, headers={
'Authorization': f'Bearer {record.onedrive_access_token}'
})
message = json.loads(response.content)
if 'error' in message:
return ['error', message['error']['code'],
message['error']['message']]
files = {}
for file in response.json().get('value'):
if list(file.keys())[0] == '@microsoft.graph.downloadUrl':
for file in response.json().get('value', []):
if '@microsoft.graph.downloadUrl' in file:
files[file['name']] = file['@microsoft.graph.downloadUrl']
return files

30
onedrive_integration_odoo/models/res_config_settings.py

@ -31,12 +31,6 @@ _logger = logging.getLogger(__name__)
class ResConfigSettings(models.TransientModel):
"""
This model represents the configuration settings for the OneDrive
integration in Odoo.It allows users to configure various parameters for
OneDrive integration, including client ID, client secret, access token,
and folder ID.
"""
_inherit = 'res.config.settings'
onedrive_client = fields.Char(
@ -53,22 +47,29 @@ class ResConfigSettings(models.TransientModel):
onedrive_tenant_id = fields.Char(
string="Onedrive Tenant Id",
config_parameter='onedrive_integration_odoo.tenant_id',
help="Director (tenant) id for accessing OneDrive API")
help="Directory (tenant) id for accessing OneDrive API")
onedrive_refresh_token = fields.Char(
string='Onedrive Refresh Token',
help="Refresh Token for refreshing the access token")
onedrive_folder = fields.Char(
string='Folder ID', help="ID of the folder in OneDrive",
config_parameter='onedrive_integration_odoo.folder_id')
onedrive_folder_name = fields.Char(
string='Folder Name',
help="Name of the folder in OneDrive (ex: ODOO16_onedrive_integration_odoo)",
config_parameter='onedrive_integration_odoo.folder_name'
)
onedrive_folder_id = fields.Char(
string='Folder ID',
help="Fetched Folder ID from OneDrive API",
config_parameter='onedrive_integration_odoo.folder_id'
)
is_onedrive_enabled = fields.Boolean(
string="Synchronize Onedrive with odoo",
string="Synchronize Onedrive with Odoo",
config_parameter='onedrive_integration_odoo.onedrive_button',
help="Enable/Disable OneDrive integration")
def action_get_onedrive_auth_code(self):
"""
Generate onedrive authorization code
"""
""" Generate onedrive authorization code """
data = {
'client_id': self.env['ir.config_parameter'].get_param(
'onedrive_integration_odoo.client_id', ''),
@ -117,3 +118,4 @@ class ResConfigSettings(models.TransientModel):
'target': 'self',
'url': "%s?%s" % (authority, encoded_params),
}

BIN
onedrive_integration_odoo/static/description/assets/screenshots/06.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

After

Width:  |  Height:  |  Size: 53 KiB

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 315 KiB

After

Width:  |  Height:  |  Size: 120 KiB

6
onedrive_integration_odoo/static/description/index.html

@ -191,10 +191,10 @@
</div>
<div style="display: block; margin: 30px auto;">
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">
Onedrive Folder id
Onedrive Folder Name
</h3>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
The Onedrive folder id is presented on url of that folder itself.</p>
Use the Onedrive folder name for odoo setup.</p>
<img src="assets/screenshots/5.png" class="img-thumbnail">
</div>
@ -203,7 +203,7 @@
Token setup
</h3>
<p style="font-family: 'Roboto', sans-serif !important; font-weight: 400 !important; color: #282F33 !important; font-size: 1rem !important;">
Token setup is available in configuration settings. Setup Tokens button will redirect to an authorization page.
Token setup is available in configuration settings.After token setup system now automatically retrieves the corresponding Folder ID using Folder Name and stores it internally. Setup Tokens button will redirect to an authorization page.
Goto --> Settngs-->Integrations</p>
<img src="assets/screenshots/06.png" class="img-thumbnail">
</div>

1
onedrive_integration_odoo/static/src/js/onedrive.js

@ -59,7 +59,6 @@ odoo.define('onedrive_integration_odoo.dashboard', function (require) {
}
});
} else if (result[0] === 'error') {
console.log(result);
if (result[1] === 'itemNotFound') {
// Display a notification if the folder is not found
self.do_action({

34
onedrive_integration_odoo/views/res_config_settings_views.xml

@ -18,6 +18,8 @@
Synchronize with OneDrive
</div>
</div>
<!-- Client ID -->
<div class="o_setting_right_pane">
<span attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
Client ID:
@ -25,6 +27,8 @@
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}"/>
</span>
</div>
<!-- Client Secret -->
<div class="o_setting_right_pane">
<span attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
Client Secret:
@ -32,23 +36,43 @@
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}"/>
</span>
</div>
<!-- Tenant ID -->
<div class="o_setting_right_pane">
<span invisible="'is_onedrive_enabled'== False">
<span attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
Tenant Id:
<field name="onedrive_tenant_id"
invisible="'is_onedrive_enabled'== False"/>
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}"/>
</span>
</div>
<!-- 🔹 Folder Name (user configurable) -->
<div class="o_setting_right_pane">
<span attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
Folder Name:
<field name="onedrive_folder_name"
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}"/>
</span>
</div>
<!-- Setup Token Button -->
<button class="o_setting_right_pane btn btn-link"
name="action_get_onedrive_auth_code" type="object"
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
<i class="fa fa-arrow-right"/> Setup Token
</button>
<!-- Folder ID (auto-fetched) -->
<div class="o_setting_right_pane">
<span attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}">
Folder ID:
<field name="onedrive_folder"
attrs="{'invisible': [('is_onedrive_enabled', '=', False)]}"/>
Folder ID (auto-fetched):
<!-- wrapper constrains width and applies ellipsis -->
<div style="display:block; max-width:520px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; border:1px solid #e6e6e6; padding:6px 8px; border-radius:4px;">
<!-- nolabel keeps UI compact; readonly ensures no editing -->
<field name="onedrive_folder_id" readonly="1" nolabel="1"
widget="char"
style="display:inline-block; width:100%; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;"/>
</div>
</span>
</div>
</div>

50
onedrive_integration_odoo/wizard/upload_file.py

@ -42,35 +42,62 @@ class UploadFile(models.TransientModel):
attachment = self.env["ir.attachment"].search(
['|', ('res_field', '!=', False), ('res_field', '=', False),
('res_id', '=', self.id),
('res_model', '=', 'upload.file')])
('res_model', '=', 'upload.file')]
)
token = self.env['onedrive.dashboard'].search([], order='id desc',
limit=1)
folder = self.env['ir.config_parameter'].get_param(
'onedrive_integration_odoo.folder_id', '')
'onedrive_integration_odoo.folder_id', ''
)
if not token or not folder:
raise exceptions.UserError(
_('Please setup Access Token and Folder Id.'))
_('Please setup Access Token and Folder Id.')
)
if token.token_expiry_date <= str(fields.Datetime.now()):
token.generate_onedrive_refresh_token()
try:
url = "http://graph.microsoft.com/v1.0/me/drive/items/%s:/%s:/" \
"createUploadSession" % (folder, self.file_name)
url = f"https://graph.microsoft.com/v1.0/me/drive/items/{folder}:/{self.file_name}:/createUploadSession"
upload_session = requests.post(url, headers={
'Content-Type': 'application/json',
'Authorization': 'Bearer "' + token.onedrive_access_token + '"'
})
requests.put(upload_session.json().get('uploadUrl'), data=open(
(attachment._full_path(attachment.store_fname)), 'rb'))
'Authorization': f'Bearer {token.onedrive_access_token}'
}).json()
if "uploadUrl" not in upload_session:
raise exceptions.UserError(
_('Failed to create upload session: %s' % upload_session))
upload_url = upload_session.get('uploadUrl')
file_path = attachment._full_path(attachment.store_fname)
with open(file_path, 'rb') as f:
file_data = f.read()
file_size = len(file_data)
headers = {
'Content-Length': str(file_size),
'Content-Range': f'bytes 0-{file_size - 1}/{file_size}'
}
response = requests.put(upload_url, headers=headers, data=file_data)
if response.status_code in (200, 201):
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'type': 'success',
'message': 'File has been uploaded successfully. '
'Please refresh.',
'message': 'File uploaded successfully to OneDrive.',
'next': {'type': 'ir.actions.act_window_close'},
}
}
else:
raise exceptions.UserError(
_('Upload failed: %s' % response.content)
)
except Exception as error:
return {
'type': 'ir.actions.client',
@ -81,3 +108,4 @@ class UploadFile(models.TransientModel):
'next': {'type': 'ir.actions.act_window_close'},
}
}

Loading…
Cancel
Save