[Erp5-report] r6399 - /erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py
nobody at svn.erp5.org
nobody at svn.erp5.org
Fri Mar 31 10:49:42 CEST 2006
Author: seb
Date: Fri Mar 31 10:49:40 2006
New Revision: 6399
URL: http://svn.erp5.org?rev=6399&view=rev
Log:
added many comments, also optimize some parts
Modified:
erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py
Modified: erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py
URL: http://svn.erp5.org/erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py?rev=6399&r1=6398&r2=6399&view=diff
==============================================================================
--- erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py (original)
+++ erp5/trunk/products/ERP5SyncML/Conduit/BaobabConduit.py Fri Mar 31 10:49:40 2006
@@ -41,6 +41,28 @@
class BaobabConduit(ERP5Conduit):
+ """
+ A conduit is in charge to read data from a particular structure,
+ and then to save this data in another structure.
+
+ In baobab, the data is read from some sql tables and it will stored
+ with ERP5 objects. The difficult parts are :
+ - for one sql table we have several kind of objects in ERP5
+ - for each properties that comes from sql, we have one ore more
+ properties in ERP5
+
+ Most importants method defined here are :
+ - constructContent : it is used when a new set of data comes from
+ the sql table, then constructContent must decide
+ wich kind of object must be created in ERP5
+ - editDocument : after constructContent, editDocument is called with
+ all properties that comes from the set of data from
+ sql. Each property must be converted to ERP5 property.
+
+ If you need to handle a new property, the most important thing to know
+ is what will be the property used in ERP5. Then you have to enter a
+ new dictionary in the property_map variable.
+ """
global property_map
@@ -49,81 +71,80 @@
### This data structure associate a xml property to an ERP5 object property in certain conditions
- property_map = \
- [ { 'xml_property' : 'nom'
- , 'erp5_property': 'first_name'
- , 'conditions' : {'erp5_portal_type':'Person'}
- }
- , { 'xml_property' : 'nom'
- , 'erp5_property': 'title'
- , 'conditions' : {'erp5_portal_type':'Organisation'}
- }
- , { 'xml_property' : 'adresse'
+ property_map = {
+ # For example, in the sql export, we use for the first name of a person the
+ # property 'nom', in ERP5 we use the property first_name
+ 'nom':[{
+ , 'erp5_property': 'first_name'
+ , 'conditions' : {'erp5_portal_type':'Person'}
+ }
+ , { 'xml_property' : 'nom'
+ , 'erp5_property': 'title'
+ , 'conditions' : {'erp5_portal_type':'Organisation'}
+ }],
+ # For example, in the sql export, we use for the name of an organisation the
+ # property 'nom', in ERP5 we use the property title
+ 'adresse': [{
, 'erp5_property': 'default_address_street_address'
, 'conditions' : [{'erp5_portal_type':'Organisation'}
,{'erp5_portal_type':'Person'}]
- }
- , { 'xml_property' : 'zone_residence'
+ }],
+ 'zone_residence': [{
, 'erp5_property': 'default_address_region'
, 'conditions' : [{'erp5_portal_type':'Organisation'}
,{'erp5_portal_type':'Person'}]
- }
- , { 'xml_property' : 'titre'
+ }],
+ 'titre': [{
, 'erp5_property': 'prefix'
, 'conditions' : {'erp5_portal_type':'Person'}
- }
- , { 'xml_property' : 'telephone'
+ }],
+ 'telephone': [{
, 'erp5_property': 'default_telephone_number'
, 'conditions' : [{'erp5_portal_type':'Organisation'}
,{'erp5_portal_type':'Person'}]
- }
- , { 'xml_property' : 'telex'
+ }],
+ 'telex': [{
, 'erp5_property': 'default_fax_number'
, 'conditions' : [{'erp5_portal_type':'Organisation'}
,{'erp5_portal_type':'Person'}]
- }
- , { 'xml_property' : 'prenom'
+ }],
+ 'prenom': [{
, 'erp5_property': 'last_name'
, 'conditions' : {'erp5_portal_type':'Person'}
- }
- , { 'xml_property' : 'date_naissance'
+ }],
+ 'date_naissance': [{
, 'erp5_property': 'birthday'
, 'conditions' : {'erp5_portal_type':'Person'}
- }
- , { 'xml_property' : 'code_bic'
+ }],
+ 'code_bic': [{
, 'erp5_property': 'bic_code'
, 'conditions' : {'erp5_portal_type':'Organisation'}
- }
-
- , { 'xml_property' : 'intitule'
+ }],
+ 'intitule': [{
, 'erp5_property': 'title'
, 'conditions' : {'erp5_portal_type':'Bank Account'}
- }
-
- , { 'xml_property' : 'montant_maxi'
+ }],
+ 'montant_maxi': [{
, 'erp5_property': 'operation_upper_limit'
, 'conditions' : {'erp5_portal_type':'Agent Privilege'}
- }
- , { 'xml_property' : 'description'
+ }],
+ 'description': [{
, 'erp5_property': 'description'
, 'conditions' : {'erp5_portal_type':'Agent Privilege'}
- }
-
- , { 'xml_property' : 'inventory_title'
+ }],
+ 'inventory_title': [{
, 'erp5_property': 'title'
, 'conditions' : {'erp5_portal_type':'Cash Inventory Group'}
- }
-
- , { 'xml_property' : 'title'
+ }],
+ 'title': [{
, 'erp5_property': 'title'
, 'conditions' : {'erp5_portal_type':'Bank Account Inventory'}
- }
-
- , { 'xml_property' : 'amount'
+ }],
+ 'amount': [{
, 'erp5_property': 'inventory'
, 'conditions' : {'erp5_portal_type':'Bank Account Inventory Line'}
- }
- ]
+ }],
+ }
@@ -134,7 +155,9 @@
security.declarePrivate('buildConditions')
def buildConditions(self, object):
"""
- Build a condition dictionnary
+ Build a condition dictionnary based on the portal type.
+ For example it will returns :
+ {'erp5_portal_type':'Agent Privilege'}
"""
dict = {}
dict['erp5_portal_type'] = object.getPortalType()
@@ -144,16 +167,19 @@
def findPropertyMapItem(self, xml_property_name, conditions):
"""
Find the property_map item that match conditions
- """
- for item in property_map:
- if item['xml_property'] == xml_property_name:
- c = item['conditions']
- if type(c) == type([]):
- if conditions in c:
- return item
- else:
- if conditions == c:
- return item
+ It will returns for example :
+ { 'xml_property' : 'nom'
+ , 'erp5_property': 'first_name'
+ , 'conditions' : {'erp5_portal_type':'Person'} }
+ """
+ for item in property_map[xml_property_name]:
+ c = item['conditions']
+ if type(c) == type([]):
+ if conditions in c:
+ return item
+ else:
+ if conditions == c:
+ return item
return None
@@ -163,13 +189,17 @@
"""
This is a redefinition of the original ERP5Conduit.constructContent function to
create Baobab objects.
- """
+
+ This method is in charge to create a new object.
+ """
+ # Register some path in some variables
erp5_site_path = object.absolute_url(relative=1)
person_module_object = object.person_module
organisation_module_object = object.organisation_module
# Modules below are not always required
# (it depends of the nature of objects you want to synchronize)
+ # So if a module to not exist, we set the value to None
try: cash_inventory_module = object.cash_inventory_module
except AttributeError: cash_inventory_module = None
try: bank_account_inventory_module = object.bank_account_inventory_module
@@ -183,13 +213,21 @@
# Given parameter is the special encoded portal type that represent the path to
# the wanted destination.
def findObjectFromSpecialPortalType(special_portal_type):
+ # The first part or portal type, for example "Mandataire"
source_portal_type = special_portal_type.split('_')[0]
+ # The place where we should build,
+ # [1:] is used to takes the full list except the first element
+ # [::-1] is used in order to reverse the order
+ # construction_location will be for example 40/Z000900001
+ # (person with id 40 and account with id Z000900001
construction_location = '/'.join(special_portal_type.split('_')[1:][::-1])
parent_object = None
for search_folder in ('person_module', 'organisation_module'):
+ # full path : /person_module/40/Z000900001
path = '/' + search_folder + '/' + construction_location
parent_object_path = erp5_site_path + path
try:
+ # Get the object with the path
parent_object = object.restrictedTraverse(parent_object_path)
except ConflictError:
raise
@@ -215,12 +253,13 @@
### handle client objects
if portal_type.startswith('Client'):
+ # This is a person object
if portal_type[-3:] == 'PER':
subobject = person_module_object.newContent( portal_type = 'Person'
, id = object_id
)
subobject.setCareerRole('client')
- else:
+ else: # This is an organisation object
subobject = organisation_module_object.newContent( portal_type = 'Organisation'
, id = object_id
)
@@ -252,6 +291,7 @@
### handle agent objects
elif portal_type.startswith('Mandataire'):
+ # Get the person or organisation thanks to the portal_type
dest = findObjectFromSpecialPortalType(portal_type)
if dest == None: return None
subobject = dest.newContent( portal_type = 'Agent'
@@ -267,6 +307,7 @@
### handle privilege objects
elif portal_type.startswith('Pouvoir'):
+ # Get the person or organisation thanks to the portal_type
dest = findObjectFromSpecialPortalType(portal_type)
if dest == None: return None
subobject = dest.newContent( portal_type = 'Agent Privilege'
@@ -358,11 +399,6 @@
if object == None: return
- """
- Write here the code that require to combine more than one property from
- the **kw dictionnary in order to put the right value in object attributes.
- """
-
### Cash Inventory objects needs two properties to generate the vault path
if object.getPortalType() == 'Cash Inventory Group':
vault_path = self.getVaultPathFromCodification( object = object
@@ -372,7 +408,9 @@
object.setDestination(vault_path)
### Cash Inventory Detail objects needs all properties to create and update the cell matrix
+ # This part is only usefull for cash inventory, it is not used for most portal types
if object.getPortalType() == 'Cash Inventory':
+ # Make sure all variables will be defined
quantity = None
cell_id = None
resource_type = None
@@ -399,6 +437,8 @@
line_currency_cash = None
currency_cash_list = object.currency_cash_module.contentValues(filter={'portal_type': currency_portal_type})
for currency_cash in currency_cash_list:
+ # Check the price_currency_id and the base price to make sure
+ # we have the right currency
if base_price not in (None, '') and \
currency_name not in (None, '') and \
currency_cash.getBasePrice() == base_price and \
@@ -412,10 +452,13 @@
, "Currency '%s %s' not found for the Cash Inventory Detail !" % (base_price, currency_name)
)
return None
- # search for lines
+ # We are looking for an existing line
inventory_lines = object.contentValues(filter={'portal_type': 'Cash Inventory Line'})
new_line = None
for line in inventory_lines:
+ # getResourceValue returns the currency_cash, so if it is
+ # equivalent to the currency_cash we have found, then we can
+ # update the line
if line.getResourceValue() == line_currency_cash:
new_line = line
break
@@ -426,6 +469,7 @@
new_line.setPrice(line_currency_cash.getBasePrice())
# get matrix variation values
category_list = []
+ # This is the 3 variation axes of the matrix
base_cat_map = { 'variation' : 'variation'
, 'letter_code': 'emission_letter'
, 'status_code': 'cash_status'
@@ -446,6 +490,7 @@
category = kw[base_key]
else:
category = 'not_defined'
+ # We must have at least a category for each axis
category_list.append(base_cat_map[base_key] + '/' + category)
# update the matrix with this cell
self.updateCashInventoryMatrix( line = new_line
@@ -455,7 +500,9 @@
)
### Bank Account Inventory Line objects needs two properties to get the right bank account object
+ # This part is only usefull for bank account inventory line, it is not used for most portal types
if object.getPortalType() == 'Bank Account Inventory Line':
+ # Make sure variables will be defined
currency_id = None
bank_account_number = None
for k,v in kw.items():
@@ -463,22 +510,25 @@
if k == 'account_number': bank_account_number = v
# try to find the bank account
if bank_account_number != None:
- customer_list = object.person_module.contentValues(filter={'portal_type': 'Person'}) + \
- object.organisation_module.contentValues(filter={'portal_type': 'Organisation'})
bank_account_object = None
- for customer in customer_list:
- for bank_account in customer.contentValues(filter={'portal_type': 'Bank Account'}):
- if bank_account.getBankAccountNumber() == bank_account_number:
- # found !
- bank_account_object = bank_account
- break
- if bank_account_object != None:
- break
+ # We use here the catalog in order to find very quickly
+ # all bank with a particular reference, so most of the time
+ # we should get only 1 bank account
+ bank_account_list = [x.getObject() for x in object.portal_catalog(
+ portal_type=('Bank Account'),
+ reference='%%%s%%' % bank_account_number)]
+ # Make sure we have found the right bank account
+ for bank_account in bank_account_list:
+ if bank_account.getBankAccountNumber() == bank_account_number:
+ bank_account_object = bank_account
+ break
if bank_account_object != None:
+ # Se the right account on the inventory line
object.setDestinationValue(bank_account_object)
if currency_id != None:
# verify or add the currency
current_currency_id = bank_account_object.getPriceCurrencyId()
+ # Make sure that the bank account will have a currency defined
if current_currency_id in (None, ''):
bank_account_object.setPriceCurrency('currency_module/' + currency_id)
elif current_currency_id != currency_id:
@@ -486,6 +536,11 @@
, 200
, 'found bank account has not the same currency as expected'
)
+ else:
+ LOG( 'BaobabConduit inconsistency:'
+ , 200
+ , 'no bank account found'
+ )
"""
@@ -504,14 +559,19 @@
### There is a translation rule, so call the right setProperty() method
if map_item != None:
+ # The method id can be for example 'setTitle'
method_id = "set" + convertToUpperCase(map_item['erp5_property'])
LOG( 'BaobabConduit:'
, 0
, "try to call object method %s on %s" % (repr(method_id), repr(object))
)
if v not in ('', None):
+ # We look if the method exist
if hasattr(object, method_id):
+ # get the method itself
method = getattr(object, method_id)
+ # This call the method, this exactly the same thing
+ # as calling directly : object.setTitle(v)
method(v)
else:
LOG( 'BaobabConduit:'
@@ -521,6 +581,13 @@
### No translation rule found, try to find a hard-coded translation method in the conduit
else:
+ # The method is generated with the type of the document and with the
+ # name of the property. If the type of the document is 'Client' and the
+ # property is nature_economique, then it will try to find a method
+ # defined in this conduit 'editClientNatureEconomique'. This is very
+ # usefull if we must do some particular conversion or some calculation
+ # before editing an object. This is used when there is no simple
+ # equivalent between sql table and ERP5.
method_id = "edit%s%s" % (kw['type'].replace(' ', ''), convertToUpperCase(k))
LOG( 'BaobabConduit:'
, 0
@@ -528,7 +595,10 @@
)
if v not in ('', None):
if hasattr(self, method_id):
+ # get the method itself
method = getattr(self, method_id)
+ # This call the method, this exactly the same thing
+ # as calling directly : self.editClientNatureEconomique(object,v)
method(object, v)
else:
LOG( 'BaobabConduit:'
@@ -589,6 +659,10 @@
)
def editClientSituationMatrimoniale(self, document, value):
+ """
+ Here we can convert data from sql to data in ERP5 thanks
+ to a simple dictionnary: the id_table.
+ """
if document.getPortalType() == 'Person':
id_table = { 'VEU' : 'widowed'
, 'DIV' : 'divorced'
@@ -607,19 +681,24 @@
### BankAccount-related-properties functions
def editCompteDevise(self, document, value):
+ # Convert compte_devise to price_currency
document.setPriceCurrency('currency_module/' + value)
def editCompteDateOuverture(self, document, value):
+ # Convert date_ouverture to start_date and stop_date
if document.getStopDate() in ('', None):
document.setStopDate(str(datetime.datetime.max))
document.setStartDate(value)
def editCompteDateFermeture(self, document, value):
+ # Convert date_firemeture to start_date and stop_date
if document.getStartDate() in ('', None):
document.setStartDate(str(datetime.datetime.min))
document.setStopDate(value)
def editCompteNumero(self, document, value):
+ # Here we have several properties in ERP5 for only
+ # one property in sql, so we need this particular method.
document.setBankCode(value[0])
document.setBranch(value[1:3])
document.setBankAccountNumber(value)
@@ -629,6 +708,7 @@
### Agent-related-properties functions
def editMandataireNom(self, document, value):
+ # Convert mandataire_nom to first_name
old_value = document.getAgentValue().getFirstName()
new_value = value
if old_value != new_value:
@@ -639,6 +719,7 @@
document.getAgentValue().setFirstName(new_value)
def editMandatairePrenom(self, document, value):
+ # Convert mandataire_prenom to last_name
old_value = document.getAgentValue().getLastName()
new_value = value
if old_value != new_value:
@@ -649,6 +730,7 @@
document.getAgentValue().setLastName(new_value)
def editMandataireService(self, document, value):
+ # Convert mandataire_service to an assignment
assignment = document.getAgentValue().newContent( portal_type = 'Assignment'
, id = 'service'
)
@@ -656,10 +738,12 @@
return
def editMandataireFonction(self, document, value):
+ # Convert mandataire_function to a career grade
document.getAgentValue().setCareerGrade(value)
return
def editMandataireTelephone(self, document, value):
+ # Convert mandataire_telephone to default_telephone_number
old_value = document.getAgentValue().getDefaultTelephoneNumber()
new_value = value
if old_value != new_value:
@@ -670,6 +754,7 @@
document.getAgentValue().setDefaultTelephoneNumber(new_value)
def editMandataireDateCreation(self, document, value):
+ # Convert mandataire_date_creation to stop_date and start_date
if document.getStopDate() in ('', None):
document.setStopDate(str(datetime.datetime.max))
document.setStartDate(value)
@@ -679,6 +764,7 @@
### AgentPrivilege-related-properties functions
def editPouvoirCategorie(self, document, value):
+ # Convert pouvoir_categorie to agent_privilege property
id_table = { 'COM' : 'clearing'
, 'CIR' : 'circularization'
, 'REM' : 'cash_out'
@@ -690,11 +776,13 @@
document.setAgentPrivilege(id_table[value])
def editPouvoirDateDebut(self, document, value):
+ # Convert pouvoir_date_debut to start_date and stop_date properties
if document.getStopDate() in ('', None):
document.setStopDate(str(datetime.datetime.max))
document.setStartDate(value)
def editPouvoirDateFin(self, document, value):
+ # Convert pouvoir_date_fin to start_date and stop_date properties
if document.getStartDate() in ('', None):
document.setStartDate(str(datetime.datetime.min))
document.setStopDate(value)
@@ -704,6 +792,7 @@
### CashInventory-related-properties functions
def editCashInventoryInventoryDate(self, document, value):
+ # Convert cash_inventory_inventory_date to stop_date property
if value in ('', None):
date = str(datetime.datetime.max)
else:
@@ -716,13 +805,26 @@
document.setStopDate(date)
def getVaultPathFromCodification( self, object, agency_code=None, inventory_code=None, vault_code=None, currency_id=None):
+ """
+ This method get many parameters and try to find a category
+ corresponding with parameters.
+
+ For example if agency_code=A00, this function will returns
+ site/aaa/bbb/ccc
+ """
if agency_code in (None, ''):
return None
category_tool = object.portal_categories
# Get the site path to agency
agency_path = None
site_base_object = category_tool.resolveCategory('site')
- for site_item in site_base_object.getCategoryChildLogicalPathItemList(base=1)[1:]:
+ # XXX Warning, we should use the catalog in order to retrieve this
+ # first level. It will go faster. But we need the codification in
+ # the catalog table
+
+ # Parse the category tree in order to find the category corresponding
+ # to the agency
+ for site_item in site_base_object.getCategoryChildItemList(base=1)[1:]:
site_path = site_item[1]
site_object = category_tool.resolveCategory(site_path)
if site_object.getPortalType() == 'Category':
@@ -735,7 +837,9 @@
# Get the site path corresponding to the inventory type
inventory_path = None
agency_site_object = site_object
- for agency_sub_item in agency_site_object.getCategoryChildLogicalPathItemList(base=1)[1:]:
+ # Parse the category tree (from the level of the agency) in order to
+ # find the category corresponding to the inventory
+ for agency_sub_item in agency_site_object.getCategoryChildItemList(base=1)[1:]:
agency_sub_item_path = agency_sub_item[1]
agency_sub_item_object = category_tool.resolveCategory(agency_sub_item_path)
agency_sub_item_vault = agency_sub_item_object.getVaultType()
@@ -751,7 +855,9 @@
# Get the site path corresponding to the vault code
vault_path = None
vault_site_object = agency_sub_item_object
- for vault_sub_item in vault_site_object.getCategoryChildLogicalPathItemList(base=1)[1:]:
+ # Parse the category tree (from the level of the inventory) in order to
+ # find the category corresponding to the vault
+ for vault_sub_item in vault_site_object.getCategoryChildtemList(base=1)[1:]:
vault_sub_item_path = vault_sub_item[1]
vault_sub_item_object = category_tool.resolveCategory(vault_sub_item_path)
vault_sub_item_code = vault_sub_item_object.getCodification()
@@ -765,7 +871,9 @@
currency_title = currency_object.getTitle()
currency_vault_path = None
vault_object = vault_sub_item_object
- for currency_vault_item in vault_object.getCategoryChildLogicalPathItemList(base=1)[1:]:
+ # Parse the category tree (from the level of the vault) in order to
+ # find the category corresponding to the currency
+ for currency_vault_item in vault_object.getCategoryChildItemList(base=1)[1:]:
currency_vault_item_path = currency_vault_item[1]
currency_vault_item_object = category_tool.resolveCategory(currency_vault_item_path)
currency_vault_item_title = currency_vault_item_object.getTitle()
@@ -840,12 +948,14 @@
### BankAccountInventory-related-properties functions
def editBankAccountInventoryAgencyCode(self, document, value):
+ # Convert bank_account_inventory_agency_code to a destination
agency_path = self.getVaultPathFromCodification( object = document
, agency_code = value
)
document.setDestination(agency_path)
def editBankAccountInventoryDate(self, document, value):
+ # Convert bank_account_inventory_date to stop_date property
if value in ('', None):
date = str(datetime.datetime.max)
else:
More information about the Erp5-report
mailing list