[ADD][WIP]Odoo storage plugin : allow creation of partner / company
This commit is contained in:
parent
2587cfa999
commit
60c6f10392
@ -1,6 +1,6 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Copyright 2018 Fabien Bourgeois <fabien@yaltik.com>
|
# Copyright 2018-2020 Fabien Bourgeois <fabien@yaltik.com>
|
||||||
#
|
#
|
||||||
# This program is free software: you can redistribute it and/or modify
|
# This program is free software: you can redistribute it and/or modify
|
||||||
# it under the terms of the GNU Affero General Public License as
|
# it under the terms of the GNU Affero General Public License as
|
||||||
@ -41,6 +41,8 @@ from radicale import xmlutils
|
|||||||
from radicale.storage import BaseCollection, Item, get_etag, get_uid_from_object
|
from radicale.storage import BaseCollection, Item, get_etag, get_uid_from_object
|
||||||
from radicale_odoo_auth import Auth
|
from radicale_odoo_auth import Auth
|
||||||
|
|
||||||
|
# Application.do_PUT = do_PUT
|
||||||
|
|
||||||
|
|
||||||
class Collection(BaseCollection):
|
class Collection(BaseCollection):
|
||||||
""" BaseCollection implementation for Odoo Radicale Storage """
|
""" BaseCollection implementation for Odoo Radicale Storage """
|
||||||
@ -101,28 +103,43 @@ class Collection(BaseCollection):
|
|||||||
yield
|
yield
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def discover(cls, path, depth="0"):
|
def discover(cls, path, depth='0'):
|
||||||
"""Discover implementation """
|
"""Discover implementation """
|
||||||
attributes = path.strip('/').split('/') or []
|
attributes = path.strip('/').split('/') or []
|
||||||
if path and not cls.user:
|
if path and not cls.user:
|
||||||
cls.user = attributes[0]
|
cls.user = attributes[0]
|
||||||
|
|
||||||
|
depth = depth or '0' # Sometimes depth is '' ?
|
||||||
cls.logger.debug('Discover : %s (path), %s (depth), %s (cls.user), %s (attributes)' %
|
cls.logger.debug('Discover : %s (path), %s (depth), %s (cls.user), %s (attributes)' %
|
||||||
(path, depth, cls.user, attributes))
|
(path, depth, cls.user, attributes))
|
||||||
yield cls(path)
|
|
||||||
if len(attributes) == 1: # Got all if root is needed
|
if len(attributes) == 1: # Root collection (user)
|
||||||
contact_path = '%sodoo-contact' % path
|
if depth == '0':
|
||||||
calendar_own_path = '%sodoo-calendar-own' % path
|
yield cls(path) # Do not ?
|
||||||
calendar_in_path = '%sodoo-calendar-in' % path
|
else: # Need crafted children
|
||||||
calendar_all_path = '%sodoo-calendar-all' % path
|
contact_path = '%sodoo-contact' % path
|
||||||
yield cls(contact_path)
|
calendar_own_path = '%sodoo-calendar-own' % path
|
||||||
yield cls(calendar_own_path)
|
calendar_in_path = '%sodoo-calendar-in' % path
|
||||||
yield cls(calendar_in_path)
|
calendar_all_path = '%sodoo-calendar-all' % path
|
||||||
yield cls(calendar_all_path)
|
yield cls(contact_path)
|
||||||
elif len(attributes) == 2: # Then we need children
|
yield cls(calendar_own_path)
|
||||||
collection = cls(path)
|
yield cls(calendar_in_path)
|
||||||
for item in collection.list():
|
yield cls(calendar_all_path)
|
||||||
yield collection.get(item)
|
elif len(attributes) == 2: # Collection
|
||||||
|
if depth == '0':
|
||||||
|
yield cls(path)
|
||||||
|
else: # We need all items
|
||||||
|
collection = cls(path)
|
||||||
|
yield collection
|
||||||
|
for item in collection.list():
|
||||||
|
yield collection.get(item)
|
||||||
|
elif len(attributes) == 3:
|
||||||
|
if depth =='0': # Read / Update / Create
|
||||||
|
if attributes[-1] in cls.partners: # Read or Update
|
||||||
|
# collection = cls('/'.join(attributes[:-1]))
|
||||||
|
collection = cls(path)
|
||||||
|
yield collection.get(attributes[-1])
|
||||||
|
# FIXME : and depth 1 ?
|
||||||
|
|
||||||
def get_meta(self, key=None):
|
def get_meta(self, key=None):
|
||||||
"""Get metadata value for collection """
|
"""Get metadata value for collection """
|
||||||
@ -134,24 +151,38 @@ class Collection(BaseCollection):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def create_collection(cls, href, collection=None, props=None):
|
def create_collection(cls, href, collection=None, props=None):
|
||||||
""" Create collection implementation : only warns ATM """
|
""" Create collection implementation : only warns ATM """
|
||||||
cls.logger.error('Attemmpt to create a new collection for %s' % href)
|
cls.logger.error('Attempt to create a new collection for %s' % href)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_contacts_from_odoo(cls, extra_domain=False):
|
||||||
|
""" Gets all contacts available from one Odoo login """
|
||||||
|
fields = ['name', 'write_date', 'comment', 'street', 'street2', 'zip',
|
||||||
|
'city', 'phone', 'mobile', 'fax', 'email', 'website',
|
||||||
|
'function', 'comment', 'image', 'ref',
|
||||||
|
'category_id', 'parent_id', 'state_id', 'country_id']
|
||||||
|
domain = [('type', '=', 'contact')]
|
||||||
|
if extra_domain:
|
||||||
|
domain.append(extra_domain)
|
||||||
|
return cls.odoo.execute_kw('res.partner', 'search_read', [domain],
|
||||||
|
{'fields': fields})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_contacts_from_odoo(cls, login):
|
def get_contacts_from_odoo(cls, login):
|
||||||
""" Gets all contacts available from one Odoo login """
|
""" Gets all contacts available from one Odoo login """
|
||||||
cls.logger.info('Get contacts for Odoo user %s' % login)
|
cls.logger.info('Get contacts for Odoo user %s' % login)
|
||||||
fields = ['name', 'write_date', 'comment', 'street', 'street2', 'zip',
|
partners = cls._get_contacts_from_odoo()
|
||||||
'city', 'phone', 'mobile', 'fax', 'email', 'website',
|
cls.partners = {}
|
||||||
'function', 'comment', 'image',
|
for p in partners:
|
||||||
'category_id', 'parent_id', 'state_id', 'country_id']
|
if p.get('ref'):
|
||||||
domain = [('type', '=', 'contact')]
|
cls.partners[p['ref']] = p
|
||||||
partners = cls.odoo.execute_kw('res.partner', 'search_read', [domain],
|
else:
|
||||||
{'fields': fields})
|
cls.partners[str(p['id'])] = p
|
||||||
cls.partners = {p['id']: p for p in partners}
|
# cls.partners = {p['id']: p for p in partners}
|
||||||
categs = cls.odoo.execute_kw('res.partner.category', 'search_read', [[]],
|
categs = cls.odoo.execute_kw('res.partner.category', 'search_read', [[]],
|
||||||
{'fields': ['name']})
|
{'fields': ['name']})
|
||||||
cls.categs = {c['id']: c['name'] for c in categs}
|
cls.categs = {str(c['id']): c['name'] for c in categs}
|
||||||
return ['res.partner:%s' % pid for pid in cls.partners.keys()]
|
return cls.partners.keys()
|
||||||
|
# return ['res.partner:%s' % pid for pid in cls.partners.keys()]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_events_from_odoo(cls, login, path):
|
def get_events_from_odoo(cls, login, path):
|
||||||
@ -200,10 +231,10 @@ class Collection(BaseCollection):
|
|||||||
if self.tag:
|
if self.tag:
|
||||||
if self.tag == 'VADDRESSBOOK':
|
if self.tag == 'VADDRESSBOOK':
|
||||||
for oid in self.get_contacts_from_odoo(self.owner):
|
for oid in self.get_contacts_from_odoo(self.owner):
|
||||||
yield oid + self.content_suffix
|
yield oid
|
||||||
elif self.tag == 'VCALENDAR':
|
elif self.tag == 'VCALENDAR':
|
||||||
for oid in self.get_events_from_odoo(self.owner, self.path):
|
for oid in self.get_events_from_odoo(self.owner, self.path):
|
||||||
yield oid + self.content_suffix
|
yield oid
|
||||||
else:
|
else:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@ -250,7 +281,7 @@ class Collection(BaseCollection):
|
|||||||
if partner.get('comment'):
|
if partner.get('comment'):
|
||||||
vobject_item.add('note').value = partner.get('comment')
|
vobject_item.add('note').value = partner.get('comment')
|
||||||
if partner.get('category_id'):
|
if partner.get('category_id'):
|
||||||
categs = [cls.categs.get(cid) for cid in partner.get('category_id')]
|
categs = [cls.categs.get(str(cid)) for cid in partner.get('category_id')]
|
||||||
vobject_item.add('categories').value = categs
|
vobject_item.add('categories').value = categs
|
||||||
if partner.get('parent_id'):
|
if partner.get('parent_id'):
|
||||||
vobject_item.add('org').value = [partner.get('parent_id')[1]]
|
vobject_item.add('org').value = [partner.get('parent_id')[1]]
|
||||||
@ -300,11 +331,10 @@ class Collection(BaseCollection):
|
|||||||
|
|
||||||
def _get_item_from_vobject(self, href, vobject_item):
|
def _get_item_from_vobject(self, href, vobject_item):
|
||||||
""" Return Item from Vobject and HREF """
|
""" Return Item from Vobject and HREF """
|
||||||
vobject_item.add('uid').value = href
|
vobject_item.add('uid').value = str(href)
|
||||||
tag, start, end = xmlutils.find_tag_and_time_range(vobject_item)
|
tag, start, end = xmlutils.find_tag_and_time_range(vobject_item)
|
||||||
text = vobject_item.serialize()
|
text = vobject_item.serialize()
|
||||||
etag = get_etag(text)
|
etag = get_etag(text)
|
||||||
# uid = get_uid_from_object(vobject_item)
|
|
||||||
return Item(
|
return Item(
|
||||||
self, href=href, last_modified=vobject_item.rev.value, etag=etag,
|
self, href=href, last_modified=vobject_item.rev.value, etag=etag,
|
||||||
text=text, item=vobject_item, uid=href,
|
text=text, item=vobject_item, uid=href,
|
||||||
@ -312,8 +342,8 @@ class Collection(BaseCollection):
|
|||||||
|
|
||||||
def _get_with_metadata(self, href):
|
def _get_with_metadata(self, href):
|
||||||
"""Fetch a single item from Odoo database"""
|
"""Fetch a single item from Odoo database"""
|
||||||
model, database_id = href.split(':')
|
model = self.odoo_model
|
||||||
database_id = int(database_id[:-len(self.content_suffix)])
|
database_id = href
|
||||||
if model == 'res.partner':
|
if model == 'res.partner':
|
||||||
vobject_item = self._generate_vcard_from_odoo(database_id)
|
vobject_item = self._generate_vcard_from_odoo(database_id)
|
||||||
return self._get_item_from_vobject(href, vobject_item)
|
return self._get_item_from_vobject(href, vobject_item)
|
||||||
@ -324,18 +354,124 @@ class Collection(BaseCollection):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def get(self, href, verify_href=True):
|
def get(self, href, verify_href=True):
|
||||||
item, metadata = self._get_with_metadata(href)
|
item, metadata = self._get_with_metadata(str(href))
|
||||||
|
item.href = str(item.href) # fix for etag radicale concatenation
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
def upload(self, href, item):
|
||||||
|
""" Creation or update """
|
||||||
|
self.logger.info('Upload href %s , item %s' % (href, item))
|
||||||
|
# TODO : handle image
|
||||||
|
|
||||||
|
# Ref : important to keep track of creations outside of Odoo
|
||||||
|
contact_data = {'ref': item.uid.value}
|
||||||
|
# Name, remove excessive spaces
|
||||||
|
name = ' '.join(filter(bool, str(item.n.value).split(' ')))
|
||||||
|
if not name:
|
||||||
|
name = item.fn.value
|
||||||
|
contact_data['name'] = name
|
||||||
|
# Address
|
||||||
|
if item.contents.get('adr'): # First only
|
||||||
|
address = item.adr_list[0]
|
||||||
|
|
||||||
|
contact_data.update({
|
||||||
|
'street': address.value.street,
|
||||||
|
'zip': address.value.code,
|
||||||
|
'city': address.value.city
|
||||||
|
})
|
||||||
|
country = address.value.country
|
||||||
|
country_dmn = [('name', 'ilike', country)]
|
||||||
|
country_id = False
|
||||||
|
|
||||||
|
if country:
|
||||||
|
country_id = self.odoo.env['res.country'].search(country_dmn, limit=1)
|
||||||
|
if country_id:
|
||||||
|
country_id = country_id[0]
|
||||||
|
if not country_id:
|
||||||
|
country_id = self.odoo.env.ref('base.fr').id
|
||||||
|
contact_data['country_id'] = country_id
|
||||||
|
|
||||||
|
# Mail
|
||||||
|
if item.contents.get('email'): # First only
|
||||||
|
contact_data['email'] = item.email_list[0].value
|
||||||
|
# Website
|
||||||
|
if item.contents.get('url'): # First only
|
||||||
|
contact_data['website'] = item.url_list[0].value
|
||||||
|
# Phones
|
||||||
|
if item.contents.get('tel'):
|
||||||
|
for tel in item.tel_list:
|
||||||
|
tel_type = tel.params.get('TYPE', [])
|
||||||
|
if 'CELL' in tel_type:
|
||||||
|
contact_data['mobile'] = tel.value
|
||||||
|
elif 'FAX' in tel_type:
|
||||||
|
contact_data['fax'] = tel.value
|
||||||
|
else:
|
||||||
|
contact_data['phone'] = tel.value
|
||||||
|
# Function
|
||||||
|
if item.contents.get('role'):
|
||||||
|
contact_data['function'] = item.role.value
|
||||||
|
# Comment
|
||||||
|
if item.contents.get('note'):
|
||||||
|
contact_data['comment'] = item.note.value
|
||||||
|
# Categories
|
||||||
|
if item.contents.get('categories'):
|
||||||
|
categ_ids = []
|
||||||
|
for categ in item.categories.value:
|
||||||
|
cat_obj = self.odoo.env['res.partner.category']
|
||||||
|
dmn = [('name', '=ilike', categ)]
|
||||||
|
categ_id = cat_obj.search(dmn, limit=1)
|
||||||
|
if not categ_id:
|
||||||
|
categ_id = cat_obj.create({'name': categ})
|
||||||
|
self.categs[str(categ_id)] = categ
|
||||||
|
else:
|
||||||
|
categ_id = categ_id[0]
|
||||||
|
categ_ids.append(categ_id)
|
||||||
|
contact_data['category_id'] = [(6, False, categ_ids)]
|
||||||
|
# Parent company (get, not create)
|
||||||
|
partner_obj = self.odoo.env['res.partner']
|
||||||
|
if item.contents.get('org'):
|
||||||
|
dmn = [('name', '=ilike', item.org.value), ('is_company', '=', True)]
|
||||||
|
parent_id = partner_obj.search(dmn, limit=1)
|
||||||
|
if parent_id:
|
||||||
|
contact_data['parent_id'] = parent_id.id
|
||||||
|
contact_data['is_company'] = False
|
||||||
|
contact_data['company_type'] = 'person'
|
||||||
|
else:
|
||||||
|
contact_data['is_company'] = True
|
||||||
|
contact_data['company_type'] = 'company'
|
||||||
|
# Create or update
|
||||||
|
if href in self.partners.keys():
|
||||||
|
partner = self.partners[href]
|
||||||
|
partner_id = partner['id']
|
||||||
|
partner_obj.write([partner_id], contact_data)
|
||||||
|
else:
|
||||||
|
partner_id = partner_obj.create(contact_data)
|
||||||
|
partner = self._get_contacts_from_odoo(('id', '=', partner_id))[0]
|
||||||
|
self.partners[item.uid.value] = partner
|
||||||
|
|
||||||
|
res_item = self.get(item.uid.value)
|
||||||
|
# print('item.etag : %s // res_item.etag : %s' % (item.etag, res_item.etag))
|
||||||
|
return res_item
|
||||||
|
|
||||||
def delete(self, href=None):
|
def delete(self, href=None):
|
||||||
""" Can not delete collection but item, yes """
|
""" Can not delete collection but item, yes """
|
||||||
|
if self.path and href is None:
|
||||||
|
href = self.path
|
||||||
if href is None:
|
if href is None:
|
||||||
# Delete the collection
|
# Delete the collection
|
||||||
self.logger.error('Attempt to delete collection %s' % self.path)
|
self.logger.error('Attempt to delete collection %s' % self.path)
|
||||||
raise ValueError('Can not delete collection')
|
raise ValueError('Can not delete collection')
|
||||||
else:
|
else:
|
||||||
# Delete an item
|
# Delete an item
|
||||||
raise NotImplementedError
|
if self.odoo_model == 'res.partner':
|
||||||
|
obj = self.odoo.env[self.odoo_model]
|
||||||
|
record_id = href.split('/')[-1]
|
||||||
|
partner_id = self.partners.get(record_id)['id']
|
||||||
|
try:
|
||||||
|
obj.unlink([partner_id])
|
||||||
|
except RPCError: # if not removable, archive it
|
||||||
|
obj.write([partner_id], {'active': False})
|
||||||
|
del self.partners[record_id]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def last_modified(self):
|
def last_modified(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user