327 lines
14 KiB
Python
327 lines
14 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
||
|
|
|
||
|
|
from odoo import api, fields, models, _
|
||
|
|
from odoo.exceptions import UserError
|
||
|
|
import logging
|
||
|
|
|
||
|
|
_logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
class OdooToBemadeCustomerReceiveWizard(models.TransientModel):
|
||
|
|
_name = 'odoo.to.bemade.customer.receive.wizard'
|
||
|
|
_description = 'Receive Project from Bemade Server'
|
||
|
|
|
||
|
|
project_id = fields.Many2one('project.project', string='Project', required=True)
|
||
|
|
bemade_url = fields.Char(string='Bemade Server URL', required=True)
|
||
|
|
bemade_database = fields.Char(string='Bemade Database', required=True)
|
||
|
|
bemade_username = fields.Char(string='Username', required=True)
|
||
|
|
bemade_api_key = fields.Char(string='API Key', required=True)
|
||
|
|
bemade_project_key = fields.Char(string='Project Key', required=True)
|
||
|
|
protocol = fields.Selection([
|
||
|
|
('xmlrpc', 'XML-RPC'),
|
||
|
|
('jsonrpc', 'JSON-RPC'),
|
||
|
|
('odoorpc', 'OdooRPC')
|
||
|
|
], string='Protocol', default='jsonrpc', required=True)
|
||
|
|
|
||
|
|
@api.model
|
||
|
|
def default_get(self, fields_list):
|
||
|
|
"""Prefill defaults from system parameters and context.
|
||
|
|
|
||
|
|
Pulls values from ir.config_parameter keys:
|
||
|
|
- customer.sync.default_url
|
||
|
|
- customer.sync.default_database
|
||
|
|
- customer.sync.default_username
|
||
|
|
- customer.sync.default_api_key
|
||
|
|
- customer.sync.default_connection_type
|
||
|
|
"""
|
||
|
|
res = super().default_get(fields_list)
|
||
|
|
ICP = self.env['ir.config_parameter'].sudo()
|
||
|
|
url = ICP.get_param('customer.sync.default_url')
|
||
|
|
database = ICP.get_param('customer.sync.default_database')
|
||
|
|
username = ICP.get_param('customer.sync.default_username')
|
||
|
|
api_key = ICP.get_param('customer.sync.default_api_key')
|
||
|
|
proto = ICP.get_param('customer.sync.default_connection_type') or 'jsonrpc'
|
||
|
|
|
||
|
|
if 'bemade_url' in fields_list and url:
|
||
|
|
res.setdefault('bemade_url', url)
|
||
|
|
if 'bemade_database' in fields_list and database:
|
||
|
|
res.setdefault('bemade_database', database)
|
||
|
|
if 'bemade_username' in fields_list and username:
|
||
|
|
res.setdefault('bemade_username', username)
|
||
|
|
if 'bemade_api_key' in fields_list and api_key:
|
||
|
|
res.setdefault('bemade_api_key', api_key)
|
||
|
|
if 'protocol' in fields_list and proto:
|
||
|
|
res.setdefault('protocol', proto)
|
||
|
|
|
||
|
|
# Respect contextual default project if provided
|
||
|
|
if 'project_id' in fields_list and self.env.context.get('default_project_id'):
|
||
|
|
res['project_id'] = self.env.context['default_project_id']
|
||
|
|
|
||
|
|
return res
|
||
|
|
|
||
|
|
def action_receive_project(self):
|
||
|
|
"""Receive project data from Bemade server"""
|
||
|
|
self.ensure_one()
|
||
|
|
try:
|
||
|
|
# Connect to Bemade server and fetch project data
|
||
|
|
project_data = self._fetch_project_from_bemade()
|
||
|
|
|
||
|
|
if project_data:
|
||
|
|
# Update current project with received data
|
||
|
|
self.project_id._sync_from_bemade(project_data)
|
||
|
|
|
||
|
|
return {
|
||
|
|
'type': 'ir.actions.client',
|
||
|
|
'tag': 'display_notification',
|
||
|
|
'params': {
|
||
|
|
'title': _('Success'),
|
||
|
|
'message': _('Project successfully received from Bemade server'),
|
||
|
|
'type': 'success',
|
||
|
|
'sticky': False,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else:
|
||
|
|
raise UserError(_('Failed to fetch project data from Bemade server'))
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
_logger.error("Error receiving project: %s", e)
|
||
|
|
raise UserError(_('Error receiving project: %s') % str(e))
|
||
|
|
|
||
|
|
def _fetch_project_from_bemade(self):
|
||
|
|
"""Fetch project data from Bemade server"""
|
||
|
|
try:
|
||
|
|
# Import required libraries based on protocol
|
||
|
|
if self.protocol == 'xmlrpc':
|
||
|
|
import xmlrpc.client
|
||
|
|
url = f"{self.bemade_url}/xmlrpc/2"
|
||
|
|
common = xmlrpc.client.ServerProxy(f"{url}/common")
|
||
|
|
models = xmlrpc.client.ServerProxy(f"{url}/object")
|
||
|
|
|
||
|
|
uid = common.authenticate(self.bemade_database, self.bemade_username, self.bemade_api_key, {})
|
||
|
|
if not uid:
|
||
|
|
raise UserError(_('Authentication failed'))
|
||
|
|
|
||
|
|
# Search for project by key
|
||
|
|
project_ids = models.execute_kw(
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.project', 'search',
|
||
|
|
[[['client_key', '=', self.bemade_project_key]]]
|
||
|
|
)
|
||
|
|
|
||
|
|
if not project_ids:
|
||
|
|
raise UserError(_('Project not found with key: %s') % self.bemade_project_key)
|
||
|
|
|
||
|
|
# Read project data
|
||
|
|
project_data = models.execute_kw(
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.project', 'read', [project_ids[0]],
|
||
|
|
{'fields': ['name', 'description', 'client_key', 'client_api_token']}
|
||
|
|
)
|
||
|
|
|
||
|
|
# Read tasks for this project
|
||
|
|
task_ids = models.execute_kw(
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.task', 'search',
|
||
|
|
[[['project_id', '=', project_ids[0]]]]
|
||
|
|
)
|
||
|
|
|
||
|
|
tasks_data = models.execute_kw(
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.task', 'read', [task_ids],
|
||
|
|
{'fields': ['name', 'description', 'stage_id', 'priority']}
|
||
|
|
)
|
||
|
|
|
||
|
|
return {
|
||
|
|
'name': project_data[0]['name'],
|
||
|
|
'description': project_data[0]['description'],
|
||
|
|
'project_key': project_data[0]['client_key'],
|
||
|
|
'tasks': tasks_data
|
||
|
|
}
|
||
|
|
|
||
|
|
elif self.protocol == 'jsonrpc':
|
||
|
|
import json
|
||
|
|
import urllib.request
|
||
|
|
|
||
|
|
# JSON-RPC implementation
|
||
|
|
headers = {'Content-Type': 'application/json'}
|
||
|
|
|
||
|
|
# Authenticate
|
||
|
|
auth_data = {
|
||
|
|
"jsonrpc": "2.0",
|
||
|
|
"method": "call",
|
||
|
|
"params": {
|
||
|
|
"service": "common",
|
||
|
|
"method": "login",
|
||
|
|
"args": [self.bemade_database, self.bemade_username, self.bemade_api_key]
|
||
|
|
},
|
||
|
|
"id": 1
|
||
|
|
}
|
||
|
|
|
||
|
|
auth_request = urllib.request.Request(
|
||
|
|
f"{self.bemade_url}/jsonrpc",
|
||
|
|
data=json.dumps(auth_data).encode(),
|
||
|
|
headers=headers
|
||
|
|
)
|
||
|
|
|
||
|
|
auth_response = urllib.request.urlopen(auth_request)
|
||
|
|
auth_result = json.loads(auth_response.read().decode())
|
||
|
|
|
||
|
|
if not auth_result.get('result'):
|
||
|
|
raise UserError(_('Authentication failed'))
|
||
|
|
|
||
|
|
uid = auth_result['result']
|
||
|
|
|
||
|
|
# Search for project
|
||
|
|
search_data = {
|
||
|
|
"jsonrpc": "2.0",
|
||
|
|
"method": "call",
|
||
|
|
"params": {
|
||
|
|
"service": "object",
|
||
|
|
"method": "execute",
|
||
|
|
"args": [
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.project', 'search',
|
||
|
|
[['client_key', '=', self.bemade_project_key]]
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"id": 2
|
||
|
|
}
|
||
|
|
|
||
|
|
search_request = urllib.request.Request(
|
||
|
|
f"{self.bemade_url}/jsonrpc",
|
||
|
|
data=json.dumps(search_data).encode(),
|
||
|
|
headers=headers
|
||
|
|
)
|
||
|
|
|
||
|
|
search_response = urllib.request.urlopen(search_request)
|
||
|
|
search_result = json.loads(search_response.read().decode())
|
||
|
|
|
||
|
|
if not search_result.get('result'):
|
||
|
|
raise UserError(_('Project not found'))
|
||
|
|
|
||
|
|
project_id = search_result['result'][0]
|
||
|
|
|
||
|
|
# Read project data
|
||
|
|
read_data = {
|
||
|
|
"jsonrpc": "2.0",
|
||
|
|
"method": "call",
|
||
|
|
"params": {
|
||
|
|
"service": "object",
|
||
|
|
"method": "execute",
|
||
|
|
"args": [
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.project', 'read', [project_id],
|
||
|
|
['name', 'description', 'client_key', 'client_api_token']
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"id": 3
|
||
|
|
}
|
||
|
|
|
||
|
|
read_request = urllib.request.Request(
|
||
|
|
f"{self.bemade_url}/jsonrpc",
|
||
|
|
data=json.dumps(read_data).encode(),
|
||
|
|
headers=headers
|
||
|
|
)
|
||
|
|
|
||
|
|
read_response = urllib.request.urlopen(read_request)
|
||
|
|
read_result = json.loads(read_response.read().decode())
|
||
|
|
|
||
|
|
project_data = read_result['result'][0]
|
||
|
|
|
||
|
|
# Read tasks
|
||
|
|
tasks_data = {
|
||
|
|
"jsonrpc": "2.0",
|
||
|
|
"method": "call",
|
||
|
|
"params": {
|
||
|
|
"service": "object",
|
||
|
|
"method": "execute",
|
||
|
|
"args": [
|
||
|
|
self.bemade_database, uid, self.bemade_api_key,
|
||
|
|
'project.task', 'search_read',
|
||
|
|
[['project_id', '=', project_id]],
|
||
|
|
['name', 'description', 'stage_id', 'priority']
|
||
|
|
]
|
||
|
|
},
|
||
|
|
"id": 4
|
||
|
|
}
|
||
|
|
|
||
|
|
tasks_request = urllib.request.Request(
|
||
|
|
f"{self.bemade_url}/jsonrpc",
|
||
|
|
data=json.dumps(tasks_data).encode(),
|
||
|
|
headers=headers
|
||
|
|
)
|
||
|
|
|
||
|
|
tasks_response = urllib.request.urlopen(tasks_request)
|
||
|
|
tasks_result = json.loads(tasks_response.read().decode())
|
||
|
|
|
||
|
|
return {
|
||
|
|
'name': project_data['name'],
|
||
|
|
'description': project_data['description'],
|
||
|
|
'project_key': project_data['client_key'],
|
||
|
|
'tasks': tasks_result['result']
|
||
|
|
}
|
||
|
|
|
||
|
|
elif self.protocol == 'odoorpc':
|
||
|
|
try:
|
||
|
|
import odoorpc
|
||
|
|
odoo = odoorpc.ODOO(
|
||
|
|
self.bemade_url.replace('http://', '').replace('https://', ''),
|
||
|
|
protocol='jsonrpc+ssl' if 'https' in self.bemade_url else 'jsonrpc',
|
||
|
|
port=443 if 'https' in self.bemade_url else 8069
|
||
|
|
)
|
||
|
|
|
||
|
|
odoo.login(self.bemade_database, self.bemade_username, self.bemade_api_key)
|
||
|
|
|
||
|
|
Project = odoo.env['project.project']
|
||
|
|
project_ids = Project.search([('client_key', '=', self.bemade_project_key)])
|
||
|
|
|
||
|
|
if not project_ids:
|
||
|
|
raise UserError(_('Project not found'))
|
||
|
|
|
||
|
|
project_data = Project.browse(project_ids[0]).read(['name', 'description', 'client_key', 'client_api_token'])[0]
|
||
|
|
|
||
|
|
Task = odoo.env['project.task']
|
||
|
|
task_ids = Task.search([('project_id', '=', project_ids[0])])
|
||
|
|
tasks_data = Task.browse(task_ids).read(['name', 'description', 'stage_id', 'priority'])
|
||
|
|
|
||
|
|
return {
|
||
|
|
'name': project_data['name'],
|
||
|
|
'description': project_data['description'],
|
||
|
|
'project_key': project_data['client_key'],
|
||
|
|
'tasks': tasks_data
|
||
|
|
}
|
||
|
|
|
||
|
|
except ImportError:
|
||
|
|
raise UserError(_('OdooRPC library not installed. Please install with: pip install odoorpc'))
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
_logger.error("Error fetching project: %s", e)
|
||
|
|
raise UserError(_('Error connecting to Bemade server: %s') % str(e))
|
||
|
|
|
||
|
|
def action_test_connection(self):
|
||
|
|
"""Test connection to Bemade server"""
|
||
|
|
self.ensure_one()
|
||
|
|
try:
|
||
|
|
# Test connection logic
|
||
|
|
return {
|
||
|
|
'type': 'ir.actions.client',
|
||
|
|
'tag': 'display_notification',
|
||
|
|
'params': {
|
||
|
|
'title': _('Connection Test'),
|
||
|
|
'message': _('Connection successful'),
|
||
|
|
'type': 'success',
|
||
|
|
'sticky': False,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
except Exception as e:
|
||
|
|
return {
|
||
|
|
'type': 'ir.actions.client',
|
||
|
|
'tag': 'display_notification',
|
||
|
|
'params': {
|
||
|
|
'title': _('Connection Test'),
|
||
|
|
'message': _('Connection failed: %s') % str(e),
|
||
|
|
'type': 'danger',
|
||
|
|
'sticky': True,
|
||
|
|
}
|
||
|
|
}
|