# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2009 Nexedi SA and Contributors. All Rights Reserved. # Hervé Poulain <herve@nexedi.com> # # WARNING: This program as such is intended to be used by professional # programmers who take the whole responsability of assessing all potential # consequences resulting from its eventual inadequacies and bugs # End users who are looking for a ready-to-use solution with commercial # garantees and support are strongly adviced to contract a Free Software # Service Company # # This program is Free Software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # ############################################################################## from Products.ERP5TioSafe.Conduit.TioSafeBaseConduit import TioSafeBaseConduit from Products.ERP5SyncML.SyncMLConstant import XUPDATE_INSERT_OR_ADD_LIST from base64 import b16encode from zLOG import LOG, WARNING from copy import deepcopy class ERP5TransactionConduit(TioSafeBaseConduit): """ This is the conduit use to synchonize tiosafe sale orders and ERP5 """ def __init__(self): # Define the object_tag element to add object self.xml_object_tag = 'transaction' def getObjectAsXML(self, object, domain): return object.Transaction_asTioSafeXML(context_document=domain) def updateNode(self, xml=None, object=None, previous_xml=None, force=0, simulate=0, **kw): raise Exception('updateNode: Impossible to update transaction') def deleteNode(self, xml=None, object=None, previous_xml=None, force=0, simulate=0, **kw): raise Exception('deleteNode: Impossible to delete transaction') def _createContent(self, xml=None, object=None, object_id=None, sub_object=None, reset_local_roles=0, reset_workflow=0, simulate=0, **kw): """ This is the method calling to create an object """ if object_id is None: object_id = self.getAttribute(xml, 'id') if object_id is not None: if sub_object is None: try: sub_object = object._getOb(object_id) except (AttributeError, KeyError, TypeError): sub_object = None if sub_object is None: # If so, it doesn't exist portal_type = '' if xml.xpath('local-name()') == self.xml_object_tag: portal_type = self.getObjectType(xml) elif xml.xpath('name()') in XUPDATE_INSERT_OR_ADD_LIST: # Deprecated ??? portal_type = self.getXupdateContentType(xml) # Deprecated ??? sub_object, reset_local_roles, reset_workflow = self.constructContent( object, object_id, portal_type, ) # Define the trade condition by using the one defined on IS integration_site = self.getIntegrationSite(kw.get('domain')) if portal_type == "Sale Order": sub_object.setSpecialise(integration_site.getSourceTrade()) sub_object.SaleOrder_applySaleTradeCondition() # Mapping between tag and element node_dict = { 'arrow': self.visitArrow, 'movement': self.visitMovement, } # if exist namespace retrieve only the tag index = 0 if xml.nsmap not in [None, {}]: index = -1 # Browse the list of arrows and movements for node in xml.getchildren(): # Only works on right tags, and no on the comments, ... if type(node.tag) is not str: continue # Build the slipt list of a tag to test split_tag = node.tag.split('}') # Build the tag (without is Namespace) tag = node.tag.split('}')[index] # Treat sub-element if len(node.getchildren()): if tag in node_dict: node_dict[tag](document=sub_object, xml=node, **kw) else: raise ValueError, "This is an unknown sub-element %s on %s" %(tag, sub_object.getPath()) if tag == 'currency': link_object = object.portal_catalog.getResultValue( portal_type='Currency', reference=node.text.encode('utf-8'), ) sub_object.setPriceCurrencyValue(link_object) elif tag in ['start_date', 'stop_date']: if not node.text: node.text = None elif tag == "payment_mode": sub_object.setPaymentConditionPaymentMode(node.text) # Build the content of new Sale Order self.newObject( object=sub_object, xml=xml, simulate=simulate, reset_local_roles=reset_local_roles, reset_workflow=reset_workflow, ) return sub_object def afterNewObject(self, object): """ Confirm the sale order and, add the grants on this one. """ if object.getPortalType() in ['Sale Order',]: object.confirm() object.updateLocalRolesOnSecurityGroups() def visitArrow(self, document=None, xml=None, **kw): """ Manage the addition of sources and destination in the Sale Orders. """ # if exist namespace retrieve only the tag index = 0 if xml.nsmap not in [None, {}]: index = -1 # retrieve the integration site domain = kw.get('domain') integration_site = self.getIntegrationSite(domain ) # use the setters of source and destination in terms of the category sync_object_list = self.getSynchronizationObjectListForType(kw.get('domain'), 'Person', 'publication') + \ self.getSynchronizationObjectListForType(kw.get('domain'), 'Organisation', 'publication') category = xml.get('type') if category != "": if category == 'Ownership': arrow_dict = { 'source': { 'synchronization': sync_object_list, 'setter': document.setSourceSectionValue, 'free_text_setter': document.setSourceSectionFreeText}, 'destination': { 'synchronization': sync_object_list, 'setter': document.setDestinationSectionValue, 'free_text_setter': document.setDestinationSectionFreeText}, } elif category == 'Payment': arrow_dict = { 'source': { 'synchronization': sync_object_list, 'setter': document.setSourcePaymentValue, 'free_text_setter': document.setSourcePaymentFreeText}, 'destination': { 'synchronization': sync_object_list, 'setter': document.setDestinationPaymentValue, 'free_text_setter': document.setDestinationPaymentFreeText}, } elif category == 'Administration': arrow_dict = { 'source': { 'synchronization': sync_object_list, 'setter': document.setSourceAdministrationValue, 'free_text_setter': document.setSourceAdministrationFreeText}, 'destination': { 'synchronization': sync_object_list, 'setter': document.setDestinationAdministrationValue, 'free_text_setter': document.setDestinationAdministrationFreeText}, } elif category == 'Decision': arrow_dict = { 'source': { 'synchronization': sync_object_list, 'setter': document.setSourceDecisionValue, 'free_text_setter': document.setSourceDecisionFreeText}, 'destination': { 'synchronization': sync_object_list, 'setter': document.setDestinationDecisionValue, 'free_text_setter': document.setDestinationDecisionFreeText}, } else: raise Exception('visitArrow: Unexpected Category %s' %(category)) else: arrow_dict = { 'source': { 'synchronization': sync_object_list, 'setter': document.setSourceValue, 'free_text_setter': document.setSourceFreeText}, 'destination': { 'synchronization': sync_object_list, 'setter': document.setDestinationValue, 'free_text_setter': document.setDestinationFreeText}, } # browse the xml and save the source and destination in the sale order integration_site = self.getIntegrationSite(kw.get('domain')) default_source = integration_site.getSourceAdministrationValue() default_org_gid = integration_site.organisation_module.getSourceSectionValue().getGidFromObject(default_source, encoded=False) default_node = integration_site.getDestinationValue() default_node_gid = integration_site.person_module.getSourceSectionValue().getGidFromObject(default_node, encoded=False) for subnode in xml.getchildren(): # only works on tags, no on the comments or other kind of tag if type(subnode.tag) is not str: continue tag = subnode.tag.split('}')[index] if tag in arrow_dict: # retrieve the corresponding synchronization_list = arrow_dict[tag]['synchronization'] link_object = None link_gid = subnode.text.encode('utf-8') if link_gid == default_org_gid: link_object = default_source else: for synchronization in synchronization_list: # encode to the output type if getattr(synchronization, 'getObjectFromGid', None) is not None: link_object = synchronization.getObjectFromGid(b16encode(link_gid)) #LOG("trying to get %s from %s, got %s" %(link_gid, synchronization.getPath(), link_object), 300, "This is for category type %s" %(category)) if link_object is not None: break if link_object is None: arrow_dict[tag]['free_text_setter'](link_gid) link_object = default_node # set person to the sale order arrow_dict[tag]['setter'](link_object) def visitMovement(self, document=None, xml=None, **kw): """ Manage the addition of the Sale Order Line. """ # dictionary of the value of a movement movement_dict_value = {'category': []} # marker for checking property existency MARKER = object() # if exist namespace retrieve only the tag index = 0 if xml.nsmap not in [None, {}]: index = -1 integration_site = self.getIntegrationSite(kw.get('domain')) default_resource = integration_site.getResourceValue() default_resource_gid = integration_site.product_module.getSourceSectionValue().getGidFromObject(default_resource, encoded=False) # browse the xml and save the sale order line values for subnode in xml.getchildren(): # only works on tags, no on the comments or other kind of tag if type(subnode.tag) is not str: continue tag = subnode.tag.split('}')[index] if tag == 'resource': # encode to the output type link_gid = subnode.text #.encode('utf-8') if link_gid == ' Service Discount': link_object = integration_site.getSourceCarrierValue() elif link_gid == ' Service Delivery': link_object = integration_site.getDestinationCarrierValue() elif link_gid == default_resource_gid: link_object = default_resource else: synchronization_list = self.getSynchronizationObjectListForType(kw.get('domain'), 'Product', 'publication') for synchronization in synchronization_list: link_object = synchronization.getObjectFromGid(b16encode(link_gid)) if link_object is not None: break # in the worse case save the line with the unknown product if link_object is None: raise ValueError, "Impossible to find related resource for gid %s" %(link_gid) # set the resource in the dict movement_dict_value[tag] = link_object elif tag == "VAT": vat_category = document.portal_categories.base_amount.trade.base.taxable['vat'] vat = vat_category[subnode.text] movement_dict_value['vat'] = vat elif tag == 'category': # set categories in the list if subnode.text is not None: movement_dict_value[tag].append(subnode.text) else: # set line values in the dict if subnode.text is not None: movement_dict_value[tag] = subnode.text#.encode('utf-8') LOG("visitMovement", 300, "movement_dict_value = %s" %(movement_dict_value)) if 'quantity' not in movement_dict_value: return # no variations will be use for the unknown product if movement_dict_value['resource'] == default_resource: movement_dict_value['category'] = [] # Create the new Sale Order Line sale_order_line = document.newContent(portal_type='Sale Order Line') # define the setters of the sale order line mapping_of_setter = { 'title': sale_order_line.setTitle, 'reference': sale_order_line.setReference, 'resource': sale_order_line.setResourceValue, 'description': sale_order_line.setDescription, 'vat': sale_order_line.setBaseContributionValue, 'quantity': sale_order_line.setQuantity, 'price': sale_order_line.setPrice, } # second, specific work on sale order line or on cell if len(movement_dict_value['category']): # set the resource on the sale order line mapping_of_setter['resource'](movement_dict_value['resource']) # work on variations variation_category_list = list(set( deepcopy(movement_dict_value['category']) )) #variation_category_list.sort() # XXX: The set remove the order sale_order_line.setVariationCategoryList(variation_category_list) variation_category_list = sale_order_line.getVariationCategoryList() # create the cell or raise if it's not possible cell = sale_order_line.newCell( base_id='movement', portal_type='Sale Order Cell', *variation_category_list ) # set values on the cell cell.setCategoryList(variation_category_list) cell.setMembershipCriterionCategoryList(variation_category_list) cell.setMembershipCriterionBaseCategoryList( sale_order_line.getVariationBaseCategoryList(), ) cell.setMappedValuePropertyList(['price', 'quantity']) mapping_of_setter['quantity'] = cell.setQuantity mapping_of_setter['price'] = cell.setPrice # set on the sale order line or on cell the values for key in movement_dict_value: if key in mapping_of_setter: mapping_of_setter[key](movement_dict_value[key]) def editDocument(self, object=None, **kw): """ This is the default editDocument method. This method can easily be overwritten. """ # Mapping of the PropertySheet mapping = { 'title': 'title', 'start_date': 'start_date', 'stop_date': 'stop_date', 'reference': 'reference', 'causality': 'comment', } property = {} # Translate kw with the good PropertySheet for k, v in kw.items(): k = mapping.get(k, k) property[k] = v object._edit(**property)