BaobabConduit.py 33.4 KB
Newer Older
Kevin Deldycke's avatar
Kevin Deldycke committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
##############################################################################
#
# Copyright (c) 2005 Nexedi SARL and Contributors. All Rights Reserved.
#                    Kevin Deldycke <kevin@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.ERP5SyncML.Conduit.ERP5Conduit import ERP5Conduit
from AccessControl import ClassSecurityInfo
from Products.ERP5Type import Permissions
from Products.ERP5Type.Utils import convertToUpperCase
from Products.CMFCore.utils import getToolByName
from Acquisition import aq_base, aq_inner, aq_chain, aq_acquire

import datetime

from zLOG import LOG



class BaobabConduit(ERP5Conduit):

  global property_map

  # Declarative security
  security = ClassSecurityInfo()


50
  ### This data structure associate a xml property to an ERP5 object property in certain conditions
Kevin Deldycke's avatar
Kevin Deldycke committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
  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'
      , 'erp5_property': 'default_address_street_address'
      , 'conditions'   : [{'erp5_portal_type':'Organisation'}
                         ,{'erp5_portal_type':'Person'}]
      }
    , { 'xml_property' : 'zone_residence'
      , 'erp5_property': 'default_address_region'
      , 'conditions'   : [{'erp5_portal_type':'Organisation'}
                         ,{'erp5_portal_type':'Person'}]
      }
    , { 'xml_property' : 'titre'
      , 'erp5_property': 'prefix'
      , 'conditions'   : {'erp5_portal_type':'Person'}
      }
    , { 'xml_property' : 'telephone'
      , 'erp5_property': 'default_telephone_number'
      , 'conditions'   : [{'erp5_portal_type':'Organisation'}
                         ,{'erp5_portal_type':'Person'}]
      }
    , { 'xml_property' : 'telex'
      , 'erp5_property': 'default_fax_number'
      , 'conditions'   : [{'erp5_portal_type':'Organisation'}
                         ,{'erp5_portal_type':'Person'}]
      }
    , { 'xml_property' : 'prenom'
      , 'erp5_property': 'last_name'
      , 'conditions'   : {'erp5_portal_type':'Person'}
      }
    , { 'xml_property' : 'date_naissance'
      , 'erp5_property': 'birthday'
      , 'conditions'   : {'erp5_portal_type':'Person'}
      }
    , { 'xml_property' : 'code_bic'
      , 'erp5_property': 'bic_code'
      , 'conditions'   : {'erp5_portal_type':'Organisation'}
      }
96

Kevin Deldycke's avatar
Kevin Deldycke committed
97 98 99 100
    , { 'xml_property' : 'intitule'
      , 'erp5_property': 'title'
      , 'conditions'   : {'erp5_portal_type':'Bank Account'}
      }
101

Kevin Deldycke's avatar
Kevin Deldycke committed
102 103 104 105 106 107 108 109
    , { 'xml_property' : 'montant_maxi'
      , 'erp5_property': 'operation_upper_limit'
      , 'conditions'   : {'erp5_portal_type':'Agent Privilege'}
      }
    , { 'xml_property' : 'description'
      , 'erp5_property': 'description'
      , 'conditions'   : {'erp5_portal_type':'Agent Privilege'}
      }
110 111 112

    , { 'xml_property' : 'inventory_title'
      , 'erp5_property': 'title'
113
      , 'conditions'   : {'erp5_portal_type':'Cash Inventory Group'}
114
      }
115 116 117 118 119 120 121 122 123 124

    , { 'xml_property' : 'title'
      , 'erp5_property': 'title'
      , 'conditions'   : {'erp5_portal_type':'Bank Account Inventory'}
      }

    , { 'xml_property' : 'amount'
      , 'erp5_property': 'inventory'
      , 'conditions'   : {'erp5_portal_type':'Bank Account Inventory Line'}
      }
Kevin Deldycke's avatar
Kevin Deldycke committed
125 126
    ]

127 128


Kevin Deldycke's avatar
Kevin Deldycke committed
129 130 131 132 133 134 135
  """
    Methods below are tools to use the property_map.
  """

  security.declarePrivate('buildConditions')
  def buildConditions(self, object):
    """
136
      Build a condition dictionnary
Kevin Deldycke's avatar
Kevin Deldycke committed
137 138 139 140 141 142 143 144
    """
    dict = {}
    dict['erp5_portal_type'] = object.getPortalType()
    return dict

  security.declarePrivate('findPropertyMapItem')
  def findPropertyMapItem(self, xml_property_name, conditions):
    """
145
      Find the property_map item that match conditions
Kevin Deldycke's avatar
Kevin Deldycke committed
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    """
    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
    return None



  security.declareProtected(Permissions.ModifyPortalContent, 'constructContent')
  def constructContent(self, object, object_id, docid, portal_type):
    """
163 164
      This is a redefinition of the original ERP5Conduit.constructContent function to
      create Baobab objects.
Kevin Deldycke's avatar
Kevin Deldycke committed
165
    """
166 167 168
    erp5_site_path        = object.absolute_url(relative=1)
    person_module         = object.person
    organisation_module   = object.organisation
169 170 171 172 173

    # Modules below are not always required
    #   (it depends of the nature of objects you want to synchronize)
    try:    cash_inventory_module = object.cash_inventory_module
    except: cash_inventory_module = None
174 175
    try:    bank_account_inventory_module = object.bank_account_inventory_module
    except: bank_account_inventory_module = None
176 177
    try:    currency_cash_module  = object.currency_cash_module
    except: currency_cash_module  = None
Kevin Deldycke's avatar
Kevin Deldycke committed
178 179 180

    subobject = None

181 182 183
    # Function to search the parent object where the new content must be construct.
    # Given parameter is the special encoded portal type that represent the path to
    #   the wanted destination.
Kevin Deldycke's avatar
Kevin Deldycke committed
184 185 186 187 188 189
    def findObjectFromSpecialPortalType(special_portal_type):
      source_portal_type = special_portal_type.split('_')[0]
      construction_location = '/'.join(special_portal_type.split('_')[1:][::-1])
      parent_object = None
      for search_folder in ('person', 'organisation'):
        path = '/' + search_folder + '/' + construction_location
190
        parent_object_path = erp5_site_path + path
Kevin Deldycke's avatar
Kevin Deldycke committed
191
        try:
192
          parent_object = object.restrictedTraverse(parent_object_path)
Kevin Deldycke's avatar
Kevin Deldycke committed
193
        except:
194 195 196 197
          LOG( 'BaobabConduit:'
             , 100
             , "parent object of '%s' not found in %s" % (source_portal_type, parent_object_path)
             )
Kevin Deldycke's avatar
Kevin Deldycke committed
198
      if parent_object == None:
199 200 201 202
        LOG( 'BaobabConduit:'
           , 100
           , "parent object of '%s' not found !" % (source_portal_type)
           )
Kevin Deldycke's avatar
Kevin Deldycke committed
203
      else:
204 205 206 207
        LOG( 'BaobabConduit:'
           , 0
           , "parent object of '%s' found (%s)" % (source_portal_type, repr(parent_object))
           )
Kevin Deldycke's avatar
Kevin Deldycke committed
208 209
      return parent_object

210
    ### handle client objects
Kevin Deldycke's avatar
Kevin Deldycke committed
211 212
    if portal_type.startswith('Client'):
      if portal_type[-3:] == 'PER':
213 214 215
        subobject = person_module.newContent( portal_type = 'Person'
                                            , id          = object_id
                                            )
Kevin Deldycke's avatar
Kevin Deldycke committed
216 217
        subobject.setCareerRole('client')
      else:
218 219 220
        subobject = organisation_module.newContent( portal_type = 'Organisation'
                                                  , id          = object_id
                                                  )
Kevin Deldycke's avatar
Kevin Deldycke committed
221 222
        subobject.setRole('client')

223
    ### handle bank account objects
Kevin Deldycke's avatar
Kevin Deldycke committed
224 225 226 227
    elif portal_type.startswith('Compte'):
      owner = findObjectFromSpecialPortalType(portal_type)
      if owner == None: return None
      subobject = owner.newContent( portal_type = 'Bank Account'
228 229
                                  , id          = object_id
                                  )
Kevin Deldycke's avatar
Kevin Deldycke committed
230 231 232
      # set the bank account owner as agent with no-limit privileges (only for persons)
      if owner.getPortalType() == 'Person':
        new_agent = subobject.newContent( portal_type = 'Agent'
233 234
                                        , id          = 'owner'
                                        )
Kevin Deldycke's avatar
Kevin Deldycke committed
235 236 237 238 239 240 241 242 243 244 245 246
        new_agent.setAgent(owner.getRelativeUrl())
        privileges = ( 'circularization'
                     , 'cash_out'
                     , 'withdrawal_and_payment'
                     , 'account_document_view'
                     , 'signature'
                     , 'treasury'
                     )
        for privilege in privileges:
          new_priv = new_agent.newContent(portal_type = 'Agent Privilege')
          new_priv.setAgentPrivilege(privilege)

247
    ### handle agent objects
Kevin Deldycke's avatar
Kevin Deldycke committed
248 249 250 251
    elif portal_type.startswith('Mandataire'):
      dest = findObjectFromSpecialPortalType(portal_type)
      if dest == None: return None
      subobject = dest.newContent( portal_type = 'Agent'
252 253
                                 , id          = object_id
                                 )
Kevin Deldycke's avatar
Kevin Deldycke committed
254 255 256
      # try to get the agent in the person module
      person = findObjectFromSpecialPortalType('Person_' + object_id)
      if person == None:
257 258 259
        person = person_module.newContent( portal_type = 'Person'
                                         , id          = object_id + 'a'
                                         )
Kevin Deldycke's avatar
Kevin Deldycke committed
260 261
      subobject.setAgent(person.getRelativeUrl())

262
    ### handle privilege objects
Kevin Deldycke's avatar
Kevin Deldycke committed
263 264 265 266
    elif portal_type.startswith('Pouvoir'):
      dest = findObjectFromSpecialPortalType(portal_type)
      if dest == None: return None
      subobject = dest.newContent( portal_type = 'Agent Privilege'
267 268 269
                                 , id          = object_id
                                 )

270
    ### handle inventory objects
271 272
    elif portal_type == 'Cash Inventory':
      if cash_inventory_module == None: return None
273
      subobject = cash_inventory_module.newContent( portal_type = 'Cash Inventory Group'
274 275 276
                                                  , id          = object_id
                                                  )

277
    ### handle inventory details objects
278
    elif portal_type == 'Cash Inventory Detail':
279
      if currency_cash_module == None: return None
280
      # get currency and vault informations by analizing the id
281
      id_items = object_id.split('_')
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
      if len(id_items) != 5:
        LOG( 'BaobabConduit:'
           , 100
           , "Cash Inventory Detail object has a wrong id (%s) !" % (object_id)
           )
        return None
      cell_id        = id_items[0]
      agency_code    = id_items[1]
      inventory_code = id_items[2]
      vault_code     = id_items[3]
      currency_id    = id_items[4]
      # get the path to the vault_code
      vault_path = self.getVaultPathFromCodification( object         = object
                                                    , agency_code    = agency_code
                                                    , inventory_code = inventory_code
                                                    , vault_code     = vault_code
298
                                                    , currency_id    = currency_id
299 300 301 302 303 304
                                                    )
      if vault_path in (None, ''):
        LOG( 'BaobabConduit:'
           , 100
           , "can't find a path to the vault '%s/%s/%s' !" % (agency_code, inventory_code, vault_code)
           )
305
        return None
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
      # try to find an existing inventory with the same price currency and vault
      inventory_list = object.contentValues(filter={'portal_type': 'Cash Inventory'})
      new_inventory = None
      for inventory in inventory_list:
        inventory_currency = inventory.getPriceCurrencyId()
        inventory_vault    = inventory.getDestination()
        if inventory_currency not in (None, '') and \
           inventory_vault    not in (None, '') and \
           inventory_currency == currency_id    and \
           inventory_vault    == vault_path     :
          new_inventory = inventory
          LOG( 'BaobabConduit:'
             , 0
             , "previous Cash Inventory found (%s) !" % (repr(new_inventory))
             )
          break
      # no previous inventory found, create one
      if new_inventory == None:
        new_inventory = object.newContent(portal_type = 'Cash Inventory')
        new_inventory.setPriceCurrency('currency/' + currency_id)
        new_inventory.setDestination(vault_path)
      subobject = new_inventory

329 330 331 332 333 334 335 336 337 338 339 340 341
    ### handle bank account inventory objects
    elif portal_type == 'Bank Account Inventory':
      if bank_account_inventory_module == None: return None
      subobject = bank_account_inventory_module.newContent( portal_type = 'Bank Account Inventory'
                                                          , id          = object_id
                                                          )

    ### handle bank account inventory line objects
    elif portal_type == 'Bank Account Inventory Line':
      subobject = object.newContent( portal_type = 'Bank Account Inventory Line'
                                   , id          = object_id
                                   )

342 343 344
    return subobject


345

346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
  security.declareProtected(Permissions.ModifyPortalContent, 'editDocument')
  def editDocument(self, object=None, **kw):
    """
      This function transfer datas from the dictionary to the baobab document
      object given in parameters.
    """

    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
                                                    , agency_code    = kw['agency_code']
                                                    , inventory_code = kw['inventory_code']
                                                    )
      object.setDestination(vault_path)

    ### Cash Inventory Detail objects needs all properties to create and update the cell matrix
    if object.getPortalType() == 'Cash Inventory':
      quantity      = None
      cell_id       = None
      resource_type = None
      base_price    = None
      currency_name = None
      for k,v in kw.items():
        if k == 'quantity'     : quantity      = float(v)
        if k == 'cell_id'      : cell_id       = v
        if k == 'currency_type': resource_type = v
        if k == 'price'        : base_price    = float(v)
        if k == 'currency'     : currency_name = v
      # try to find an existing line with the same resource as the current cell
382 383 384 385 386
      if resource_type in ['BIL']:
        currency_portal_type = 'Banknote'
      elif resource_type in ['MON']:
        currency_portal_type = 'Coin'
      else:
387 388 389 390
        LOG( 'BaobabConduit:'
           , 100
           , "Cash Inventory Detail resource type can't be guess (%s) !" % (resource_type)
           )
391 392 393
        return None
      # get the list of existing currency to find the currency of the line
      line_currency_cash = None
394
      currency_cash_list = object.currency_cash_module.contentValues(filter={'portal_type': currency_portal_type})
395
      for currency_cash in currency_cash_list:
396 397 398 399 400 401
        if base_price    not in (None, '')                    and \
           currency_name not in (None, '')                    and \
           currency_cash.getBasePrice()       == base_price   and \
           currency_cash.getPriceCurrencyId() == currency_name:
          line_currency_cash = currency_cash
          break
402 403
      # no currency found
      if line_currency_cash == None:
404 405 406 407
        LOG( 'BaobabConduit:'
           , 100
           , "Currency '%s %s' not found for the Cash Inventory Detail !" % (base_price, currency_name)
           )
408
        return None
409
      # search for lines
410 411 412 413 414 415 416 417
      inventory_lines = object.contentValues(filter={'portal_type': 'Cash Inventory Line'})
      new_line = None
      for line in inventory_lines:
        if line.getResourceValue() == line_currency_cash:
          new_line = line
          break
      # no previous line found, create one
      if new_line == None:
418
        new_line = object.newContent(portal_type = 'Cash Inventory Line')
419 420
        new_line.setResourceValue(line_currency_cash)
        new_line.setPrice(line_currency_cash.getBasePrice())
421
      # get matrix variation values
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
      category_list = []
      base_cat_map = { 'variation'  : 'variation'
                     , 'letter_code': 'emission_letter'
                     , 'status_code': 'cash_status'
                     }
      for base_key in base_cat_map.keys():
        if base_key in kw.keys() and kw[base_key] not in ('', None):
          if base_key == 'status_code':
            status_table = { 'TVA' : 'valid'
                           , 'NEE' : 'new_emitted'
                           , 'NEU' : 'new_not_emitted'
                           , 'RTC' : 'retired'
                           , 'ATR' : 'to_sort'
                           , 'MUT' : 'mutilated'
                           , 'EAV' : 'to_ventilate'
                           }
            category = status_table[kw[base_key]]
          else:
            category = kw[base_key]
        else:
          category = 'not_defined'
        category_list.append(base_cat_map[base_key] + '/' + category)
444 445
      # update the matrix with this cell
      self.updateCashInventoryMatrix( line               = new_line
446 447
                                    , cell_category_list = category_list
                                    , quantity           = quantity
448
                                    , cell_uid           = cell_id
449 450
                                    )

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
    ### Bank Account Inventory Line objects needs two properties to get the right bank account object
    if object.getPortalType() == 'Bank Account Inventory Line':
      currency_id         = None
      bank_account_number = None
      for k,v in kw.items():
        if k == 'currency'      : currency_id         = v
        if k == 'account_number': bank_account_number = v
      # try to find the bank account
      if bank_account_number != None:
        customer_list = object.person.contentValues(filter={'portal_type': 'Person'}) + \
                        object.organisation.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
        if bank_account_object != None:
          object.setDestinationValue(bank_account_object)
          if currency_id != None:
            # verify or add the currency
            current_currency_id = bank_account_object.getPriceCurrencyId()
            if current_currency_id in (None, ''):
              bank_account_object.setPriceCurrency('currency/' + currency_id)
            elif current_currency_id != currency_id:
              LOG( 'BaobabConduit inconsistency:'
                 , 200
                 , 'found bank account has not the same currency as expected'
                 )

484 485 486 487 488 489 490 491 492

    """
      Here we use 2 generic way to update object properties :
        1. We try to use the property_map mapping to migrate a value from a property
             to another;
        2. If the latter fail, we try to find a method with a pre-defined name in
             this script to handle the value.
    """

Kevin Deldycke's avatar
Kevin Deldycke committed
493 494 495 496 497 498
    # Set properties of the destination baobab object
    for k,v in kw.items():
      # Try to find a translation rule in the property_map
      cond = self.buildConditions(object)
      map_item = self.findPropertyMapItem(k, cond)

499 500
      ### There is a translation rule, so call the right setProperty() method
      if map_item != None:
Kevin Deldycke's avatar
Kevin Deldycke committed
501
        method_id = "set" + convertToUpperCase(map_item['erp5_property'])
502 503 504 505
        LOG( 'BaobabConduit:'
           , 0
           , "try to call object method %s on %s" % (repr(method_id), repr(object))
           )
Kevin Deldycke's avatar
Kevin Deldycke committed
506 507 508 509 510
        if v not in ('', None):
          if hasattr(object, method_id):
            method = getattr(object, method_id)
            method(v)
          else:
511 512 513 514 515 516 517
            LOG( 'BaobabConduit:'
               , 100
               , 'property map item don\'t match object properties'
               )

      ### No translation rule found, try to find a hard-coded translation method in the conduit
      else:
518
        method_id = "edit%s%s" % (kw['type'].replace(' ', ''), convertToUpperCase(k))
519 520 521 522 523 524 525 526 527 528 529 530 531 532
        LOG( 'BaobabConduit:'
           , 0
           , "try to call conduit method %s on %s" % (repr(method_id), repr(object))
           )
        if v not in ('', None):
          if hasattr(self, method_id):
            method = getattr(self, method_id)
            method(object, v)
          else:
            LOG( 'BaobabConduit:'
               , 100
               , "there is no method to handle <%s>%s</%s> data" % (k,repr(v),k)
               )

Kevin Deldycke's avatar
Kevin Deldycke committed
533 534 535 536



  """
537 538 539 540
    All functions below are defined to set a document's property to a value
    given in parameters.
    The name of those functions are chosen to help the transfert of datas
    from a given XML format to standard Baobab objects.
Kevin Deldycke's avatar
Kevin Deldycke committed
541 542
  """

543 544
  ### Client-related-properties functions

Kevin Deldycke's avatar
Kevin Deldycke committed
545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
  def editClientCategorie(self, document, value):
    if document.getPortalType() == 'Organisation':
      id_table = { 'BIF': 'institution/world/bank'
                 , 'PFR': 'institution/world/institution'
                 , 'ICU': 'institution/local/common'
                 , 'BET': 'institution/local/institution'
                 , 'ETF': 'institution/local/bank'
                 , 'BTR': 'treasury/national'
                 , 'ORP': 'treasury/other'
                 , 'ORI': 'organism/international'
                 , 'ORR': 'organism/local'
                 , 'COR': 'intermediaries'
                 , 'DIV': 'depositories/various'
                 , 'DER': 'depositories/savings'
                 , 'DAU': 'depositories/other'
                 }
      document.setActivity('banking_finance/' + id_table[value])
    else:
563
      LOG('BaobabConduit:', 0, 'Person\'s category ignored')
Kevin Deldycke's avatar
Kevin Deldycke committed
564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579

  def editClientNatureEconomique(self, document, value):
    if document.getPortalType() == 'Organisation':
      # build the economical class category path
      c = ''
      path = ''
      for i in value[1:]:
        c += i
        if c == '13':
          path += '/S13'
          if value != 'S13':
            path += '/' + value
          break
        path += '/S' + c
      document.setEconomicalClass(path)
    else:
580 581 582 583
      LOG( 'BaobabConduit inconsistency:'
         , 200
         , 'a non-Organisation client can\'t have an economical class'
         )
Kevin Deldycke's avatar
Kevin Deldycke committed
584 585 586 587 588 589 590 591 592 593

  def editClientSituationMatrimoniale(self, document, value):
    if document.getPortalType() == 'Person':
      id_table = { 'VEU' : 'widowed'
                 , 'DIV' : 'divorced'
                 , 'MAR' : 'married'
                 , 'CEL' : 'never_married'
                 }
      document.setMaritalStatus(id_table[value])
    else:
594 595 596 597
      LOG( 'BaobabConduit inconsistency:'
         , 200
         , 'a non-Person client can\'t have a marital status'
         )
Kevin Deldycke's avatar
Kevin Deldycke committed
598 599 600



601 602
  ### BankAccount-related-properties functions

Kevin Deldycke's avatar
Kevin Deldycke committed
603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622
  def editCompteDevise(self, document, value):
    document.setPriceCurrency('currency/' + value)

  def editCompteDateOuverture(self, document, value):
    if document.getStopDate() in ('', None):
      document.setStopDate(str(datetime.datetime.max))
    document.setStartDate(value)

  def editCompteDateFermeture(self, document, value):
    if document.getStartDate() in ('', None):
      document.setStartDate(str(datetime.datetime.min))
    document.setStopDate(value)

  def editCompteNumero(self, document, value):
    document.setBankCode(value[0])
    document.setBranch(value[1:3])
    document.setBankAccountNumber(value)



623 624
  ### Agent-related-properties functions

Kevin Deldycke's avatar
Kevin Deldycke committed
625 626 627 628
  def editMandataireNom(self, document, value):
    old_value = document.getAgentValue().getFirstName()
    new_value = value
    if old_value != new_value:
629 630 631 632
      LOG( 'BaobabConduit:'
         , 200
         , 'old value of agent first name (%s) was replaced by a new one (%s)' % (old_value, new_value)
         )
Kevin Deldycke's avatar
Kevin Deldycke committed
633 634 635 636 637 638
      document.getAgentValue().setFirstName(new_value)

  def editMandatairePrenom(self, document, value):
    old_value = document.getAgentValue().getLastName()
    new_value = value
    if old_value != new_value:
639 640 641 642
      LOG( 'BaobabConduit:'
         , 200
         , 'old value of agent last name (%s) was replaced by a new one (%s)' % (old_value, new_value)
         )
Kevin Deldycke's avatar
Kevin Deldycke committed
643 644 645 646
      document.getAgentValue().setLastName(new_value)

  def editMandataireService(self, document, value):
    assignment = document.getAgentValue().newContent( portal_type = 'Assignment'
647 648
                                                    , id          = 'service'
                                                    )
Kevin Deldycke's avatar
Kevin Deldycke committed
649 650 651 652 653 654 655 656 657 658 659
    assignment.setGroup(value)
    return

  def editMandataireFonction(self, document, value):
    document.getAgentValue().setDefaultCareerGrade(value)
    return

  def editMandataireTelephone(self, document, value):
    old_value = document.getAgentValue().getDefaultTelephoneNumber()
    new_value = value
    if old_value != new_value:
660 661 662 663
      LOG( 'BaobabConduit:'
         , 200
         , "old value of agent's telephone (%s) was replaced by a new one (%s)" % (old_value, new_value)
         )
Kevin Deldycke's avatar
Kevin Deldycke committed
664 665 666 667 668 669 670 671 672
      document.getAgentValue().setDefaultTelephoneNumber(new_value)

  def editMandataireDateCreation(self, document, value):
    if document.getStopDate() in ('', None):
      document.setStopDate(str(datetime.datetime.max))
    document.setStartDate(value)



673 674
  ### AgentPrivilege-related-properties functions

Kevin Deldycke's avatar
Kevin Deldycke committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
  def editPouvoirCategorie(self, document, value):
    id_table = { 'COM' : 'clearing'
               , 'CIR' : 'circularization'
               , 'REM' : 'cash_out'
               , 'RET' : 'withdrawal_and_payment'
               , 'RTE' : 'account_document_view'
               , 'SIG' : 'signature'
               , 'TRE' : 'treasury'
               }
    document.setAgentPrivilege(id_table[value])

  def editPouvoirDateDebut(self, document, value):
    if document.getStopDate() in ('', None):
      document.setStopDate(str(datetime.datetime.max))
    document.setStartDate(value)

  def editPouvoirDateFin(self, document, value):
    if document.getStartDate() in ('', None):
      document.setStartDate(str(datetime.datetime.min))
694 695 696 697
    document.setStopDate(value)



698 699
  ### CashInventory-related-properties functions

700
  def editCashInventoryInventoryDate(self, document, value):
701 702 703 704 705 706 707 708 709
    if value in ('', None):
      date = str(datetime.datetime.max)
    else:
      # Convert french date to strandard date
      date_items = value.split('/')
      day   = date_items[0]
      month = date_items[1]
      year  = date_items[2]
      date  = '/'.join([year, month, day])
710 711
    document.setStopDate(date)

712
  def getVaultPathFromCodification( self, object, agency_code=None, inventory_code=None, vault_code=None, currency_id=None):
713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
    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:]:
      site_path = site_item[1]
      site_object = category_tool.resolveCategory(site_path)
      if site_object.getPortalType() == 'Category':
        site_code = site_object.getCodification()
        if site_code not in (None, '') and site_code.upper() == agency_code.upper():
          agency_path = site_path
          break
    if inventory_code in (None, ''):
      return agency_path
    # 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:]:
      agency_sub_item_path   = agency_sub_item[1]
      agency_sub_item_object = category_tool.resolveCategory(agency_sub_item_path)
735 736 737 738 739 740 741 742
      agency_sub_item_vault  = agency_sub_item_object.getVaultType()
      if agency_sub_item_vault not in (None, ''):
        vault_type_path        = 'vault_type/' + agency_sub_item_vault
        vault_type_object      = category_tool.resolveCategory(vault_type_path)
        vault_type_code        = vault_type_object.getCodification()
        if vault_type_code not in (None, '') and vault_type_code.upper() == inventory_code.upper():
          inventory_path = agency_sub_item_path
          break
743 744 745 746 747 748 749 750 751 752 753 754
    if vault_code in (None, ''):
      return inventory_path
    # 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:]:
      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()
      if vault_sub_item_code not in (None, '') and vault_sub_item_code.upper() == vault_code.upper():
        vault_path = vault_sub_item_path
        break
755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771
    if currency_id in (None, ''):
      return vault_path
    # Get the site path corresponding to the currency-related-subvault
    currency_object = category_tool.currency[currency_id]
    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:]:
      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()
      if currency_vault_item_title not in (None, '') and currency_vault_item_title.upper() == currency_title.upper():
        currency_vault_path = currency_vault_item_path
        break
    if currency_vault_path == None:
      return vault_path
    return currency_vault_path
772 773 774 775



  ### CashInventoryDetail-related-properties functions
776

777
  def updateCashInventoryMatrix(self, line, cell_category_list, quantity, cell_uid):
778
    base_id = 'movement'
779
    base_category_list = [ 'emission_letter'
780
                         , 'variation'
781
                         , 'cash_status'
782
                         ]
783 784 785 786 787 788 789 790 791 792

    old_line_category_list   = line.getVariationCategoryList()
    messy_line_category_list = cell_category_list + old_line_category_list

    sorted_line_base_category_list = []
    sorted_line_category_list = []
    sorted_cell_category_list = []
    sorted_cell_range = []

    # cell_category_list must have the same base category order of cell_range base category
793
    for base_category in base_category_list:
794 795 796 797 798 799 800

      # generate the sorted line categories
      for category in messy_line_category_list:
        if category.startswith(base_category + '/') and category not in sorted_line_category_list:
          sorted_line_category_list.append(category)

      # generate the sorted cell range
801
      base_group = []
802
      for category in messy_line_category_list:
803 804
        if category.startswith(base_category + '/') and category not in base_group:
          base_group.append(category)
805 806
      sorted_cell_range.append(base_group)
      # generate the sorted base category
807
      if len(base_group) > 0:
808 809 810 811 812 813 814 815 816 817 818
        sorted_line_base_category_list.append(base_category)

      # generate the sorted cell variation categories
      for category in cell_category_list:
        if category.startswith(base_category + '/') and category not in sorted_cell_category_list:
          sorted_cell_category_list.append(category)

    # update line variation categories
    line.setVariationBaseCategoryList(sorted_line_base_category_list)
    line.setVariationCategoryList(sorted_line_category_list)
    line.setCellRange(base_id = base_id, *sorted_cell_range)
819 820 821 822
    # create the cell
    kwd = { 'base_id'    : base_id
          , 'portal_type': 'Cash Inventory Cell'
          }
823
    new_cell = line.newCell(*sorted_cell_category_list, **kwd)
824 825 826
    new_cell.edit( mapped_value_property_list         = ('price', 'inventory')
                 , force_update                       = 1
                 , inventory                          = quantity
827 828
                 , membership_criterion_category_list = sorted_cell_category_list
                 , category_list                      = sorted_cell_category_list
829
                 , title                              = cell_uid
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852
                 )



  ### BankAccountInventory-related-properties functions

  def editBankAccountInventoryAgencyCode(self, document, value):
    agency_path = self.getVaultPathFromCodification( object      = document
                                                   , agency_code = value
                                                   )
    document.setDestination(agency_path)

  def editBankAccountInventoryDate(self, document, value):
    if value in ('', None):
      date = str(datetime.datetime.max)
    else:
      # Convert french date to strandard date
      date_items = value.split('/')
      day   = date_items[0]
      month = date_items[1]
      year  = date_items[2]
      date  = '/'.join([year, month, day])
    document.setStopDate(date)