Sanjay Gohel (Open ERP) has proposed merging
lp:~openerp-dev/openobject-addons/trunk-import-outlook-pst-backlog-2-sgo into
lp:~openerp-dev/openobject-addons/trunk-import-outlook-pst.
Requested reviews:
Atul Patel(OpenERP) (atp-openerp)
For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-import-outlook-pst-backlog-2-sgo/+merge/66782
hello,
I have make all the correction
kindly check it
Thanks
--
https://code.launchpad.net/~openerp-dev/openobject-addons/trunk-import-outlook-pst-backlog-2-sgo/+merge/66782
Your team OpenERP R&D Team is subscribed to branch
lp:~openerp-dev/openobject-addons/trunk-import-outlook-pst.
=== modified file 'crm/crm_installer.py'
--- crm/crm_installer.py 2011-01-14 00:11:01 +0000
+++ crm/crm_installer.py 2011-07-04 12:17:26 +0000
@@ -34,6 +34,7 @@
'crm_caldav': fields.boolean('Calendar Synchronizing', help="Helps you to synchronize the meetings with other calendar clients and mobiles."),
'sale_crm': fields.boolean('Opportunity to Quotation', help="This module relates sale from opportunity cases in the CRM."),
'fetchmail': fields.boolean('Fetch Emails', help="Allows you to receive E-Mails from POP/IMAP server."),
+ 'import_outlook_pst':fields.boolean('Import Outlook Pst',help='Helps you to Integration of outlook Pst file to OpenERP modules'),
'thunderbird': fields.boolean('Thunderbird', help="Allows you to link your e-mail to OpenERP's documents. You can attach it to any existing one in OpenERP or create a new one."),
'outlook': fields.boolean('MS-Outlook', help="Allows you to link your e-mail to OpenERP's documents. You can attach it to any existing one in OpenERP or create a new one."),
'wiki_sale_faq': fields.boolean('Sale FAQ', help="Helps you manage wiki pages for Frequently Asked Questions on Sales Application."),
=== modified file 'crm/crm_installer_view.xml'
--- crm/crm_installer_view.xml 2011-01-14 00:11:01 +0000
+++ crm/crm_installer_view.xml 2011-07-04 12:17:26 +0000
@@ -37,6 +37,7 @@
<separator string="Synchronization" colspan="4" />
<field name="crm_caldav" />
<field name="fetchmail" />
+ <field name="import_outlook_pst"/>
</group>
<group colspan="2" col="2">
<separator string="Plug-In" colspan="4" />
=== modified file 'import_base/__openerp__.py'
--- import_base/__openerp__.py 2011-05-12 08:49:08 +0000
+++ import_base/__openerp__.py 2011-07-04 12:17:26 +0000
@@ -31,7 +31,7 @@
'website': 'http://www.openerp.com',
'depends': ['base'],
'init_xml': [],
- 'update_xml': [],
+ 'update_xml': ["import_base_view.xml"],
'demo_xml': [],
'test': [], #TODO provide test
'installable': True,
=== added file 'import_base/import_base_view.xml'
--- import_base/import_base_view.xml 1970-01-01 00:00:00 +0000
+++ import_base/import_base_view.xml 2011-07-04 12:17:26 +0000
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<openerp>
+ <data>
+ <menuitem name="Import" id="menu_import_crm" parent="base.menu_base_partner"/>
+ </data>
+</openerp>
\ No newline at end of file
=== modified file 'import_base/import_framework.py'
--- import_base/import_framework.py 2011-05-31 14:25:45 +0000
+++ import_base/import_framework.py 2011-07-04 12:17:26 +0000
@@ -46,8 +46,11 @@
"""
DO_NOT_FIND_DOMAIN = [('id', '=', 0)]
+ #TODO don't use context to pass credential parameters
def __init__(self, obj, cr, uid, instance_name, module_name, email_to_notify=False, context=None):
Thread.__init__(self)
+ #change this value to set another default field for unique ID in the external table
+ self.external_id_field = 'id'
self.obj = obj
self.cr = cr
self.uid = uid
@@ -58,8 +61,6 @@
self.table_list = []
self.initialize()
-
-
"""
Abstract Method to be implemented in
the real instance
@@ -78,6 +79,22 @@
"""
return [{}]
+ def get_link(self, from_table, ids, to_table):
+ """
+ @return: a dictionaries that contains the association between the id (from_table)
+ and the list (to table) of id linked
+ """
+ return {}
+
+ def get_external_id(self, data):
+ """
+ @return the external id
+ the default implementation return self.external_id_field (that has 'id') by default
+ if the name of id field is different, you can overwrite this method or change the value
+ of self.external_id_field
+ """
+ return data[self.external_id_field]
+
def get_mapping(self):
"""
@return: { TABLE_NAME : {
@@ -154,7 +171,7 @@
for k, field_name in self_dependencies:
data[k] = data.get(field_name) and self._generate_xml_id(data.get(field_name), table)
- data['id_new'] = self._generate_xml_id(data['id'], table)
+ data['id_new'] = self._generate_xml_id(self.get_external_id(data), table)
fields, values = self._fields_mapp(data, mapping, table)
res.append(values)
@@ -214,7 +231,6 @@
fields.append(key)
value = val(dict(dict_sugar))
data_lst.append(value)
-
return fields, data_lst
def _generate_xml_id(self, name, table):
@@ -238,6 +254,7 @@
Check if the external id exist in the openerp database
in order to check if the id exist the table where it come from
should be provide
+ @return the xml_id generated if the external_id exist in the database or false
"""
if not external_id:
return False
@@ -297,7 +314,6 @@
domain_search = not domain_search and [('name', 'ilike', name)] or domain_search
obj = self.obj.pool.get(model)
xml_id = self._generate_xml_id(name, table)
-
xml_ref = self.mapped_id_if_exist(model, domain_search, table, name)
fields.append('id')
data.append(xml_id)
@@ -342,7 +358,6 @@
"""
-
self.data_started = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
self.cr = pooler.get_db(self.cr.dbname).cursor()
try:
@@ -385,9 +400,9 @@
def _send_notification_email(self, result):
if not self.email:
- return
+ return False
tools.email_send(
- '[email protected]',
+ '[email protected]',
self.email,
self.get_email_subject(result),
self.get_email_body(result),
@@ -431,4 +446,20 @@
"""
return "The import of data \n instance name : %s \n" % self.instance_name
-#For example of use see import_sugarcrm
\ No newline at end of file
+
+ #TODO documentation test
+ def run_test(self):
+ back_get_data = self.get_data
+ back_get_link = self.get_link
+ self.get_data = self.get_data_test
+ self.get_link = self.get_link_test
+ self.run()
+ self.get_data = back_get_data
+ self.get_link = back_get_link
+
+
+ def get_data_test(self, table):
+ return [{}]
+
+ def get_link_test(self, from_table, ids, to_table):
+ return {}
=== modified file 'import_base/mapper.py'
--- import_base/mapper.py 2011-05-12 08:49:08 +0000
+++ import_base/mapper.py 2011-07-04 12:17:26 +0000
@@ -57,7 +57,7 @@
self.delimiter = delimiter and delimiter.get('delimiter', ' ') or ' '
def __call__(self, external_values):
- return self.delimiter.join(map(lambda x : external_values.get(x,''), self.arg))
+ return self.delimiter.join(map(lambda x : str(external_values.get(x,'')), self.arg))
class ppconcat(mapper):
"""
@@ -71,7 +71,7 @@
self.delimiter = delimiter and delimiter.get('delimiter', ' ') or '\n\n'
def __call__(self, external_values):
- return self.delimiter.join(map(lambda x : x + ": " + external_values.get(x,''), self.arg))
+ return self.delimiter.join(map(lambda x : x + ": " + str(external_values.get(x,'')), self.arg))
class const(mapper):
"""
@@ -94,12 +94,16 @@
and don't care about the name of the field
call(self.method, value('field1'))
"""
- def __init__(self, val, default=''):
+ def __init__(self, val, default='', fallback=False):
self.val = val
self.default = default
+ self.fallback = fallback
def __call__(self, external_values):
- return external_values.get(self.val, self.default)
+ val = external_values.get(self.val, self.default)
+ if self.fallback and (not val or val == self.default):
+ val = external_values.get(self.fallback, self.default)
+ return val
class map_val(mapper):
"""
@@ -163,5 +167,3 @@
else:
args.append(arg)
return self.fun(external_values, *args)
-
-
\ No newline at end of file
=== modified file 'import_outlook_pst/__openerp__.py'
--- import_outlook_pst/__openerp__.py 2011-05-23 08:54:23 +0000
+++ import_outlook_pst/__openerp__.py 2011-07-04 12:17:26 +0000
@@ -26,7 +26,7 @@
'description': """This Module read data from Outlook PST.""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
- 'depends': ['base'],
+ 'depends': ['import_base','base','crm'],
'init_xml': [],
'update_xml': ['import_outlook_pst_view.xml'],
'demo_xml': [],
=== removed file 'import_outlook_pst/demo/contact.pst'
Binary files import_outlook_pst/demo/contact.pst 2011-05-23 08:54:23 +0000 and import_outlook_pst/demo/contact.pst 1970-01-01 00:00:00 +0000 differ
=== added file 'import_outlook_pst/demo/contacts.pst'
Binary files import_outlook_pst/demo/contacts.pst 1970-01-01 00:00:00 +0000 and import_outlook_pst/demo/contacts.pst 2011-07-04 12:17:26 +0000 differ
=== added file 'import_outlook_pst/demo/meeting.pst'
Binary files import_outlook_pst/demo/meeting.pst 1970-01-01 00:00:00 +0000 and import_outlook_pst/demo/meeting.pst 2011-07-04 12:17:26 +0000 differ
=== added file 'import_outlook_pst/extract.py'
--- import_outlook_pst/extract.py 1970-01-01 00:00:00 +0000
+++ import_outlook_pst/extract.py 2011-07-04 12:17:26 +0000
@@ -0,0 +1,190 @@
+from osv import fields, osv
+import base64
+import subprocess
+import mailbox
+import os, os.path,stat
+import vobject
+import tempfile
+import time
+from datetime import *
+from dateutil.rrule import *
+from dateutil.parser import *
+from import_base.mapper import *
+from import_base.import_framework import *
+
+
+def read_pst(file):
+ parent_directory = tempfile.mkdtemp(prefix='outlook_', suffix='_pst')
+ outputdirectory = os.path.join(parent_directory, 'exctract_pst')
+ os.mkdir(outputdirectory)
+ pst_file = base64.decodestring(file)
+ att_folder_path = os.path.abspath(os.path.dirname("%rule_type%\\"))
+ att_path = os.path.join(att_folder_path,'exctract_file.pst')
+ file_pst = open(att_path, "w")
+ file_pst.write(pst_file)
+ file_pst.close()
+ try:
+ subprocess.call(['readpst', '-o', outputdirectory, '-r', att_path])
+ except:
+ raise osv.except_osv(_('readpst lib Error!'), _('Please install readpst lib for reading pst file using sudo apt-get install readpst.'))
+ name = False
+ directories = [outputdirectory]
+ for directory in directories:
+ for name in os.listdir(directory):
+ fullpath = os.path.join(directory,name)
+ if os.path.isfile(fullpath):
+ continue
+ elif os.path.isdir(fullpath):
+ directories.append(fullpath)
+ return name,fullpath
+
+def import_calendar_data(read_file):
+ list_dict = []
+ for reader in vobject.readComponents(read_file):
+ row = {}
+ for contents in reader.contents:
+ for address in reader.contents.get(contents):
+ if contents == 'dtend' or contents == 'dtstart' or contents == 'dtstamp' or contents == 'created':
+ cal_date = str(address.value)
+ change_date = datetime.datetime.strptime(cal_date, "%Y-%m-%d %H:%M:%S+00:00")
+ address.value = str(change_date)
+ if contents == 'last-mod':
+ continue
+ if contents == 'rrule':
+ recurrency = '1'
+ row['recurrency'] = recurrency
+ val = address.value
+ splt = val.split(';')
+ rule_type = splt[0].split('=')
+ for rrule in rule_type:
+ rrule_type = rrule.title()
+ row['rrule_type'] = rrule_type
+ if rule_type[1] == 'MONTHLY':
+ for content in splt:
+ monthday = content.split('=')
+ if monthday[0] == 'BYMONTHDAY':
+ day = monthday[1]
+ row['day'] = day
+ row['select1'] = 'date'
+ if monthday[0] == 'BYSETPOS':
+ byday = monthday[1]
+ row['byday'] = byday
+ row['select1'] = 'day'
+ if monthday[0] == 'BYDAY':
+ week_list = monthday[1]
+ row['week_list'] = week_list
+ if rule_type[1] == 'WEEKLY':
+ totaldays = val.split('=')[-1]
+ days = totaldays.split(';')
+ for weekday in days:
+ row[weekday] = weekday
+ if len(splt) != 1:
+ intrvl = splt[1].split('=')
+ if intrvl[0] == 'INTERVAL':
+ interval = intrvl[1]
+ row['interval'] = interval
+ if rule_type[1] == 'WEEKLY' and intrvl[0] == 'COUNT':
+ count = intrvl[1]
+ date_end = datetime.datetime.now() + datetime.timedelta(weeks=+int(count)-1)
+ row['end_type'] = 'end_date'
+ row['end_date'] = date_end
+ if rule_type[1] != 'WEEKLY' and intrvl[0] == 'COUNT':
+ if rule_type[1] == 'MONTHLY' and len(monthday[0]) < 3:
+ continue
+ if rule_type[1] == 'DAILY' and len(splt) > 5:
+ continue
+ rr = list(rrulestr(val))
+ date_end = rr[-1]
+ row['end_type'] = 'end_date'
+ row['end_date'] = date_end
+ row[contents] = eval('reader.' + contents + '.value')
+ result = row.get('summary') + row.get("status")
+ row['id'] = result
+ list_dict.append(row)
+ return list_dict
+
+def get_address_type(row,address,list_dict, type, phone, fax):
+ row.update({'city': address.value.city,
+ 'street' : address.value.street,
+ 'country' : address.value.country,
+ 'zip' : address.value.code,
+ 'state' : address.value.region,
+ 'type' : type,
+ 'phone' : phone,
+ 'fax' : fax,
+ })
+ result = row.get('fn')+row.get('type')
+ row['id'] = result
+ list_dict.append(row)
+
+def import_contact_data(read_file):
+ list_dict = []
+ email = False
+ mobile = False
+ fn = False
+ org = False
+ prefix = False
+ title = False
+ work_voice = False
+ work_fax = False
+ home_voice = False
+ home_fax = False
+ other_voice = False
+ other_fax = False
+ for reader in vobject.readComponents(read_file):
+ if reader.contents.has_key('fn') :
+ fn = eval('reader.' + unicode('fn') + '.value')
+ if reader.contents.has_key('email'):
+ email = eval('reader.' + unicode('email') + '.value')
+ if reader.contents.has_key('org') :
+ org = eval('reader.' + unicode('org') + '.value')
+ if reader.contents.has_key('title') :
+ title = eval('reader.' + unicode('title') + '.value')
+ if reader.contents.has_key('n') :
+ fname = eval('reader.' + unicode('n') + '.value')
+ prefix = fname.prefix
+ if reader.contents.has_key('tel'):
+ for adr in reader.contents.get('tel'):
+ tel_type = adr.params.get('TYPE')
+ if tel_type[0] == 'work' and tel_type[1] == 'voice':
+ work_voice = adr.value
+ if tel_type[0] == 'work' and tel_type[1] == 'fax':
+ work_fax = adr.value
+ if tel_type[0] == 'home' and tel_type[1] == 'voice':
+ home_voice = adr.value
+ if tel_type[0] == 'home' and tel_type[1] == 'fax':
+ home_fax = adr.value
+ if tel_type[0] == 'cell':
+ mobile = adr.value
+ if tel_type[0] == 'msg':
+ other_voice = adr.value
+ if tel_type[0] == 'fax':
+ other_fax = adr.value
+ if fn and reader.contents.has_key('adr'):
+ for address in reader.contents.get('adr'):
+ row = {
+ 'fn': fn,
+ 'prefix' : prefix,
+ 'email' : email ,
+ 'title' : title,
+ 'org' : org,
+ 'mobile': mobile,
+ }
+ type = address.params.get('TYPE')[0]
+ if type == 'work':
+ get_address_type(row,address,list_dict, 'invoice',work_voice,work_fax)
+ if type == 'home':
+ get_address_type(row,address,list_dict, 'contact',home_voice,home_fax)
+ if type == 'postal':
+ get_address_type(row,address,list_dict, 'other',other_voice,other_fax)
+ return list_dict
+
+def get_pst(fullpath,name):
+ list_dict = []
+ fp = open(fullpath)
+ s = "".join(fp.readlines())
+ if name == 'calendar':
+ list_dict = import_calendar_data(s)
+ elif name == 'contacts':
+ list_dict = import_contact_data(s)
+ return list_dict
=== modified file 'import_outlook_pst/import_outlook_pst.py'
--- import_outlook_pst/import_outlook_pst.py 2011-05-23 08:54:23 +0000
+++ import_outlook_pst/import_outlook_pst.py 2011-07-04 12:17:26 +0000
@@ -19,39 +19,155 @@
#
##############################################################################
from osv import fields, osv
-import base64
-import subprocess
-import os, os.path
-import tempfile
-from tools.translate import _
+from import_base.mapper import *
+from import_base.import_framework import *
+import extract
+
+
+class outlook_import(import_framework):
+
+ FILE_CONTACT = 'contacts'
+ FILE_MEETING = 'calendar'
+
+ def initialize(self):
+ fullpth,directo = extract.read_pst(self.context.get('file'))
+ self.context['name'] = directo
+ self.context['fullpath'] = fullpth
+
+ def get_data(self,name):
+ """
+ @return: a list of dictionaries
+ each dictionnaries contains the list of pair external_field_name : value
+ """
+ directy = extract.get_pst(self.context.get('name'),self.context.get('fullpath'))
+ return directy
+
+ meeting_state = {
+ 'CONFIRMED': 'open',
+ }
+
+ def get_category(self, val, model, name):
+ fields = ['name', 'object_id']
+ nam = name[0].title()
+ name = nam.replace('-',' ')
+ data = [name, model]
+ return self.import_object(fields, data, 'crm.case.categ', 'crm_categ', nam, [('object_id.model','=',model), ('name', 'like', name)])
+
+ def get_meeting_mapping(self):
+ return {
+ 'model' : 'crm.meeting',
+ 'map' : {
+ 'name': 'summary',
+ 'date': 'dtstart',
+ 'date_deadline' : 'dtend',
+ 'location': 'location',
+ 'description' : 'description',
+ 'recurrency': 'recurrency',
+ 'state' : map_val('status', self.meeting_state),
+ 'categ_id/id' : call(self.get_category,'crm.meeting', value('categories')),
+ 'rrule_type': 'rrule_type',
+ 'interval' : 'interval',
+ 'end_type': 'end_type',
+ 'end_date' : 'end_date',
+ 'mo' : 'MO',
+ 'tu' : 'TU',
+ 'we' : 'WE',
+ 'th' : 'TH',
+ 'fr' : 'FR',
+ 'sa' : 'SA',
+ 'su' : 'SU',
+ 'select1' : 'select1',
+ 'day' : 'day',
+ 'byday' : 'byday',
+ 'week_list' : 'week_list'
+ }
+ }
+
+ def import_contact(self, val):
+ if val.get('country'):
+ country_id = self.get_all_countries(val.get('country'))
+ states = self.get_all_states(val.get('state'), country_id)
+ val['country_id/id'] = country_id
+ val['state_id/id'] = states
+ return val
+
+ def get_company_id(self, dict, val):
+ if val:
+ fields = ['name']
+ data = val
+ return self.import_object(fields, data, 'res.company', 'cmpny', val and val[0] or False)
+
+ def get_title(self, dict, prefix):
+ fields = ['shortcut', 'name', 'domain']
+ if prefix:
+ data = [prefix, prefix, 'Contact']
+ return self.import_object(fields, data, 'res.partner.title', 'contact_title', prefix, [('shortcut', '=',prefix)])
+
+
+ def get_contact_mapping(self):
+ return {
+ 'model' : 'res.partner.address',
+ 'hook' : self.import_contact,
+ 'map' : {
+ 'name':'fn',
+ 'function': 'title',
+ 'street': 'street',
+ 'phone' : 'phone',
+ 'fax' : 'fax',
+ 'city': 'city',
+ 'zip' : 'zip',
+ 'mobile' : 'mobile',
+ 'email' : 'email' ,
+ 'state_id/id' : 'state_id/id',
+ 'company_id/id' : call(self.get_company_id, value('org')),
+ 'title/id' : call(self.get_title, value('prefix')),
+ 'country_id/id': 'country_id/id',
+ 'type':'type'
+ }
+ }
+
+ def get_mapping(self):
+
+ return {
+ 'contacts': self.get_contact_mapping(),
+ 'calendar' : self.get_meeting_mapping()
+ }
+
+ def get_all_states(self, external_val, country_id):
+ """Get states or create new state unless country_id is False"""
+ state_code = external_val[0:3] #take the tree first char
+ fields = ['country_id/id', 'name', 'code']
+ data = [country_id, external_val, state_code]
+ if country_id:
+ return self.import_object(fields, data, 'res.country.state', 'state_id', external_val)
+ return False
+
+ def get_all_countries(self, val):
+ """Get Country, if no country match do not create anything, to avoid duplicate country code"""
+ return self.mapped_id_if_exist('res.country', [('name', 'ilike', val)], 'country', val)
class import_outlook_pst(osv.osv):
"""Import Outlook Pst"""
_name = "import.outlook.pst"
_description = __doc__
_columns ={
- 'file': fields.binary('Upload pst File',filters='*.pst', required=True),
+ 'file': fields.binary('Upload pst File',filters='*.pst', required=True),
+ 'email': fields.char('Notify End Of Import To:', size=128),
+ 'contacts': fields.boolean('Contacts'),
+ 'calendar': fields.boolean('Meetings'),
+ 'task': fields.boolean('Todo')
}
def import_pst(self, cr, uid, ids, context=None):
- """Extract pst file"""
if not context:
- context = {}
+ context={}
+ email=False
for current in self.browse(cr, uid, ids):
- parent_directory = tempfile.mkdtemp(prefix='outlook_', suffix='_pst')
- outputdirectory = os.path.join(parent_directory, 'exctract_pst')
- os.mkdir(outputdirectory)
- pst_file = base64.decodestring(current.file)
- att_folder_path = os.path.abspath(os.path.dirname("%temp%\\"))
- att_path = os.path.join(att_folder_path,'exctract_file.pst')
- file_pst = open(att_path, "w")
- file_pst.write(pst_file)
- file_pst.close()
- try:
- subprocess.call(['readpst', '-o', outputdirectory, '-r', att_path])
- except:
- raise osv.except_osv(_('readpst lib Error!'), _('Please install readpst lib for reading pst file using sudo apt-get install readpst.'))
- directories = [outputdirectory]
- return {'type': 'ir.actions.act_window_close'}
+ context.update({'file': current.file})
+ email = current.email
+ imp = outlook_import(self, cr, uid, "outlook", "import_outlook_pst", email_to_notify=email, context=context)
+ imp.set_table_list([imp.context.get('fullpath')])
+ imp.start()
+ return {}
import_outlook_pst()
=== modified file 'import_outlook_pst/import_outlook_pst_view.xml'
--- import_outlook_pst/import_outlook_pst_view.xml 2011-05-23 08:54:23 +0000
+++ import_outlook_pst/import_outlook_pst_view.xml 2011-07-04 12:17:26 +0000
@@ -10,6 +10,16 @@
<group colspan="4">
<separator string="Upload Your File:" colspan="4"/>
<field name="file"/>
+ <separator string="" colspan="4"/>
+ <group colspan="4" col='6'>
+ <field name="contacts"/>
+ <field name="calendar"/>
+ <field name="task"/>
+ </group>
+ <group colspan="4">
+ <separator string="Email Notification When Import is finished" colspan="4"/>
+ <field name="email" widget="email" string="Email Address to Notify"/>
+ </group>
<separator string="" colspan="4" />
<group colspan="4" >
<label string="" colspan="2"/>
@@ -31,8 +41,8 @@
<field name="target">new</field>
</record>
- <menuitem name="Outlook_PST" id="menu_outlook_pst" parent="base.menu_base_partner"/>
- <menuitem name="Import Outlook Pst" id="menu_pst_contact" parent="menu_outlook_pst" action="action_import_outlook_pst" icon="STOCK_EXECUTE"/>
+
+ <menuitem name="Import Outlook Pst" id="menu_pst_contact" parent="import_base.menu_import_crm" action="action_import_outlook_pst" icon="STOCK_EXECUTE"/>
</data>
</openerp>
_______________________________________________
Mailing list: https://launchpad.net/~openerp-dev-gtk
Post to : [email protected]
Unsubscribe : https://launchpad.net/~openerp-dev-gtk
More help : https://help.launchpad.net/ListHelp