# -*- coding: utf-8 -*-
"""Handles DB operations

.. module:: testenv.db_handler
   :platform: Unix
   :synopsis: Handles DB operations
.. moduleauthor:: Petr Rašek <bowman@hydratk.org>

"""

from hydratk.core.masterhead import MasterHead
from hydratk.lib.network.dbi.client import DBClient
from hydratk.extensions.testenv.entities import Customer, Payer, Subscriber, Service
from hydratk.extensions.testenv.entities import Contact, ContactRole, Address, AddressRole
from os import path

class DbHandler(object):
    """Class DbHandler
    """
    
    _mh = None    
    _db_file = None
    _client = None
    
    def __init__(self):
        """Class constructor
           
        Called when the object is initialized 
        
        Args:  
           none 
           
        """           
        
        self._mh = MasterHead.get_head()
        self._db_file = path.join(self._mh.cfg['Extensions']['TestEnv']['ext_dir'], 
                                  self._mh.cfg['Extensions']['TestEnv']['db_file'])
        self._client = DBClient('SQLITE')
        
    def connect(self):
        """Method connects to database
        
        Args:
           none            
             
        Returns:
           bool: result
                
        """        
        
        result = self._client.connect(self._db_file)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_connected'), self._mh.fromhere())
        return result       

    def disconnect(self):
        """Method disconnects from database
        
        Args:  
           none          
             
        Returns:
           bool: result
                
        """        
        
        result = self._client.disconnect()
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_disconnected'), self._mh.fromhere())
        return result      
        
    def read_customer(self, id):
        """Method reads customer
        
        Args:
           id (int): customer id           
             
        Returns:
           obj: crm_entities.Customer
                
        """        
        
        msg = 'id:{0}'.format(id)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_customer', msg), 
                       self._mh.fromhere())
            
        query = 'SELECT a.id, a.name, b.title AS status, a.segment, a.birth_no, a.reg_no, a.tax_no ' + \
                'FROM customer a, lov_status b WHERE a.id = ? AND a.status = b.id'
        res, row = self._client.exec_query(query, [id], True)
          
        if (row == None):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'customer', id), 
                          self._mh.fromhere())
            return None

        customer = Customer(row['id'], row['name'], row['status'], row['segment'], 
                            row['birth_no'], row['reg_no'], row['tax_no'])
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'customer', customer),
                      self._mh.fromhere())
                                            
        return customer                                             
       
    def create_customer(self, name, segment, status='active', birth_no=None, reg_no=None, tax_no=None):
        """Method creates customer
        
        Args:
           name (str): name
           segment (int): segment id, lov_segment.id
           status (str): status, lov_status.title, default active
           birth_no (str): birth number
           reg_no (str): registration number
           tax_no (str): tax identification number      
             
        Returns:
           int: created customer id
                
        """           
        
        msg = 'name:{0}, status:{1}, segment:{2}, birth_no:{3}, reg_no:{4}, tax_no:{5}'.format(
               name, status, segment, birth_no, reg_no, tax_no)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_customer', msg), 
                      self._mh.fromhere())         
                           
        query = 'SELECT id FROM lov_status WHERE title = ?'
        res, row = self._client.exec_query(query, [status], True)
        
        status_id = row['id'] if (row != None) else None
            
        query = 'INSERT INTO customer (name, status, segment, birth_no, reg_no, tax_no, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'                
        self._client.exec_query(query, [name, status_id, segment, birth_no, reg_no, tax_no]) 
                                           
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)
        id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|status:{2}|segment:{3}'.format(id, name, status, segment) + \
              u'|birth_no:{0}|reg_no:{1}|tax_no:{2}'.format(birth_no, reg_no, tax_no)
                  
        self._client.exec_query(query, ['customer', id, 'create_customer', log])            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'customer', id), 
                      self._mh.fromhere())
            
        return id
        
        
    def change_customer(self, id, name=None, status=None, segment=None, birth_no=None, reg_no=None, tax_no=None):
        """Method changes customer
        
        Args:
           id (int): customer id
           name (str): name
           status (str): status, lov_status.title
           segment (int): segment id, lov_segment.id           
           birth_no (str): birth number
           reg_no (str): registration number
           tax_no (str): tax identification number      
             
        Returns: 
           bool: result
                
        """          
        
        msg = 'id:{0}, name:{1}, status:{2}, segment:{3}, birth_no:{4}, reg_no:{5}, tax_no:{6}'. format(
              id, name, status, segment, birth_no, reg_no, tax_no)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_customer', msg), 
                      self._mh.fromhere())
                        
        query = 'SELECT COUNT(*) FROM customer WHERE id = ?'
        res, row = self._client.exec_query(query, [id], True)
        
        if (row[0] == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'customer', id), 
                              self._mh.fromhere())
            return False         
                        
        query = 'UPDATE customer SET {0}, modify_date = CURRENT_TIMESTAMP WHERE id = ?'  
                          
        if (name != None):
            self._client.exec_query(query.format('name = ?'), [name, id])
        if (status != None):
            query2 = 'SELECT id FROM lov_status WHERE title = ?'                
            res, row = self._client.exec_query(query2, [status], True)
            status_id = row['id'] if (row != None) else None             
                
            self._client.exec_query(query.format('status = ?'), [status_id, id])
        if (segment != None):
            self._client.exec_query(query.format('segment = ?'), [segment, id])
        if (birth_no != None):
            self._client.exec_query(query.format('birth_no = ?'), [birth_no, id])
        if (reg_no != None):
            self._client.exec_query(query.format('reg_no = ?'), [reg_no, id])
        if (tax_no != None):
            self._client.exec_query(query.format('tax_no = ?'), [tax_no, id])  
                
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|status:{2}|segment:{3}'.format(id, name, status, segment) + \
              u'|birth_no:{0}|reg_no:{1}|tax_no:{2}'.format(birth_no, reg_no, tax_no)
                  
        res, rows = self._client.exec_query(query, ('customer', id, 'change_customer', log))   
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'customer', id), 
                      self._mh.fromhere())                                                                         
            
        return res
        
    def read_payer(self, id):
        """Method reads payer
        
        Args:
           id (int): payer id          
             
        Returns:
           obj: crm_entities.Payer
                
        """           
        
        msg = 'id:{0}'.format(id)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_Payer', msg), 
                      self._mh.fromhere())          
            
        query = 'SELECT a.id, a.name, b.title AS status, a.billcycle, a.bank_account, a.customer ' + \
                'FROM payer a, lov_status b WHERE a.id = ? AND a.status = b.id'
        res, row = self._client.exec_query(query, [id], True)
            
        if (row == None):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'payer', id), 
                          self._mh.fromhere())              
            return None

        payer = Payer(row['id'], row['name'], row['status'], row['billcycle'], 
                      row['customer'], row['bank_account'])
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'payer', payer),
                      self._mh.fromhere())  
                                         
        return payer                                             
        
    def create_payer(self, name, billcycle, customer, status='active', bank_account=None):
        """Method creates payer
        
        Args:
           name (str): name
           billcycle (int): billcycle, lov_billcycle.id
           customer (int): assigned customer id
           status (str): status, lov_status.title, default active
           bank_account (str): banking account            
             
        Returns:
           int: created payer id
                
        """          
        
        msg = 'name:{0}, status:{1}, billcycle:{2}, bank_account:{3}, customer:{4}'.format(
              name, status, billcycle, bank_account, customer)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_payer', msg), 
                      self._mh.fromhere())
                                   
        query = 'SELECT id FROM lov_status WHERE title = ?'
        res, row = self._client.exec_query(query, [status], True)
        status_id = row['id'] if (row != None) else None
        
        query = 'INSERT INTO payer (name, status, billcycle, bank_account, customer, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'                
        self._client.exec_query(query, [name, status_id, billcycle, bank_account, customer]) 
                                           
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)
        id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|status:{2}|billcycle:{3}'.format(id, name, status, billcycle) + \
              u'|bank_account:{0}|customer:{1}'.format(bank_account, customer)
                  
        self._client.exec_query(query, ['payer', id, 'create_payer', log])            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'payer', id), 
                      self._mh.fromhere())
            
        return id
        
    def change_payer(self, id, name=None, status=None, billcycle=None, bank_account=None, customer=None):
        """Method changes payer
        
        Args:
           id (int): payer id
           name (str): name
           status (str): status, lov_status.title
           billcycle (int): billcycle id, lov_billcycle.id
           bank_account (str): banking account 
           customer (int): assigned customer id                    
             
        Returns:
           bool: result
                
        """         
        
        msg = 'id:{0}, name:{1}, status:{2}, billcycle:{3}, bank_account:{4}, customer:{5}'.format(
              id, name, status, billcycle, bank_account, customer)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_payer', msg), 
                      self._mh.fromhere())  
            
        query = 'SELECT COUNT(*) FROM payer WHERE id = ?'
        res, row = self._client.exec_query(query, [id], True
                                           )
        if (row[0] == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'payer', id), 
                          self._mh.fromhere())
            return False               
                                       
        query = 'UPDATE payer SET {0}, modify_date = CURRENT_TIMESTAMP WHERE id = ?'
            
        if (name != None):
            self._client.exec_query(query.format('name = ?'), [name, id])
        if (status != None):
            query2 = 'SELECT id FROM lov_status WHERE title = ?'
            res, row = self._client.exec_query(query2, [status], True)
            status_id = row[0] if (row != None) else None              
                
            self._client.exec_query(query.format('status = ?'), [status_id, id])
        if (billcycle != None):
            self._client.exec_query(query.format('billcycle = ?'), [billcycle, id])
        if (bank_account != None):
            self._client.exec_query(query.format('bank_account = ?'), [bank_account, id])
        if (customer != None):
            self._client.exec_query(query.format('customer = ?'), [customer, id])
                
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|status:{2}|billcycle:{3}'.format(id, name, status, billcycle) + \
              u'|bank_account:{0}|customer:{1}'.format(bank_account, customer)
                  
        res, rows = self._client.exec_query(query, ['payer', id, 'change_payer', log]) 
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'payer', id), 
                      self._mh.fromhere())                                                                                 
            
        return res
        
    def read_subscriber(self, id):
        """Method reads subscriber
        
        Args:
           id (int): subscriber id           
             
        Returns:
           obj: crm_entities.Subscriber
                
        """           
        
        msg = 'id:{0}'.format(id)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_subscriber', msg), 
                      self._mh.fromhere())
                       
        query = 'SELECT a.id, a.name, a.msisdn, b.title AS status, a.market, a.tariff, a.customer, a.payer ' + \
                'FROM subscriber a, lov_status b WHERE a.id = ? AND a.status = b.id'
        res, row = self._client.exec_query(query, [id], True)
            
        if (row == None):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'subscriber', id), 
                          self._mh.fromhere())               
            return None

        subscriber = Subscriber(row['id'], row['name'], row['msisdn'], row['status'], 
                                row['market'], row['tariff'], row['customer'], row['payer'])
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'subscriber', subscriber),
                      self._mh.fromhere()) 
                                            
        return subscriber                                             
        
    def create_subscriber(self, name, msisdn, market, tariff, customer, payer, status='active'):
        """Method creates subscriber
        
        Args:
           name (str): name
           msisdn (str): MSISDN
           market (int): market id, lov_market.id
           tariff (int): tariff id, lov_tariff.id
           customer (int): assigned customer id
           payer (int): assigned payer id
           status (str): status, lov_status.title, default active                              
             
        Returns:
           int: created subscriber id
                
        """           
        
        msg = 'name:{0}, msisdn:{1}, status:{2}, market:{3}, tariff:{4}, customer:{5}, payer:{6}'.format(
              name, msisdn, status, market, tariff, customer, payer)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_subscriber', msg), 
                      self._mh.fromhere())                       
            
        query = 'SELECT id FROM lov_status WHERE title = ?'
        res, row = self._client.exec_query(query, [status], True)
        status_id = row['id'] if (row != None) else None
        
        query = 'INSERT INTO subscriber (name, msisdn, status, market, tariff, customer, payer, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'                
        self._client.exec_query(query, [name, msisdn, status_id, market, tariff, customer, payer])
                                                               
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)
        id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|msisdn:{2}|status:{3}|market:{4}'.format(id, name, msisdn, status, market) + \
              u'|tariff:{0}|customer:{1}|payer:{2}'.format(tariff, customer, payer)
                  
        self._client.exec_query(query, ['subscriber', id, 'create_subscriber', log])            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'subscriber', id), 
                      self._mh.fromhere())
            
        return id
        
    def change_subscriber(self, id, name=None, msisdn=None, status=None, market=None, tariff=None, customer=None, payer=None):
        """Method changes subscriber
        
        Args:
           id (int): subscriber id
           name (str): name
           msisdn (str): MSISDN
           status (str): status, lov_status.title           
           market (int): market id, lov_market.id
           tariff (int): tariff id, lov_tariff.id
           customer (int): assigned customer id
           payer (int): assigned payer id                     
             
        Returns:
           result: bool
        
        """             
        
        msg = 'id:{0}, name:{1}, msisdn:{2}, status:{3}, market:{4}, tariff:{5}, customer:{6}, payer:{7}'.format(
              id, name, msisdn, status, market, tariff, customer, payer)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_subscriber', msg), 
                      self._mh.fromhere())
              
        query = 'SELECT COUNT(*) FROM subscriber WHERE id = ?'
        res, row = self._client.exec_query(query, [id], True)
        
        if (row[0] == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'subscriber', id), 
                          self._mh.fromhere())
            return False               
                                      
        query = 'UPDATE subscriber SET {0}, modify_date = CURRENT_TIMESTAMP WHERE id = ?'
            
        if (name != None):
            self._client.exec_query(query.format('name = ?'), [name, id])
        if (msisdn != None):
            self._client.exec_query(query.format('msisdn = ?'), [msisdn, id])                
        if (status != None):
            query2 = 'SELECT id FROM lov_status WHERE title = ?'
            res, row = self._client.exec_query(query2, [status], True)
            status_id = row[0] if (row != None) else None              
                
            self._client.exec_query(query.format('status = ?'), [status_id, id])
        if (market != None):
            self._client.exec_query(query.format('market = ?'), [market, id])
        if (tariff != None):
            self._client.exec_query(query.format('tariff = ?'), [tariff, id])
        if (customer != None):
            self._client.exec_query(query.format('customer = ?'), [customer, id])
        if (payer != None):
            self._client.exec_query(query.format('payer = ?'), [payer, id])  
                
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|msisdn:{2}|status:{3}|market:{4}'.format(id, name, msisdn, status, market) + \
              u'|tariff:{0}|customer:{1}|payer:{2}'.format(tariff, customer, payer)
                  
        res, rows = self._client.exec_query(query, ['subscriber', id, 'change_subscriber', log])
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'subscriber', id), 
                      self._mh.fromhere())                                                                           
            
        return res
        
    def read_contact(self, id):
        """Method reads contact
        
        Args:
           id (int): contact id         
             
        Returns:
           obj: crm_entities.Contact
                
        """           
        
        msg = 'id:{0}'.format(id)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_contact', msg), 
                      self._mh.fromhere())
                      
        query = 'SELECT a.id, a.name, a.phone, a.email, ' + \
                'c.title, b.customer, b.payer, b.subscriber ' + \
                'FROM contact a LEFT JOIN contact_role b ON a.id = b.contact ' + \
                'LEFT JOIN lov_contact_role c ON b.contact_role = c.id ' + \
                'WHERE a.id = ?'                
        res, rows = self._client.exec_query(query, [id])
            
        if (len(rows) == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'contact', id), 
                          self._mh.fromhere())              
            return None
            
        first = True
        id = None
        name = None
        phone = None
        email = None
        roles = []
            
        for row in rows:
                           
            if (first):
                    
                id = row['id']
                name = row['name']
                phone = row['phone']
                email = row['email']
                first = False 
                    
            if (row['title'] != None):
                roles.append(ContactRole(row['id'], row['title'], row['customer'],
                                         row['payer'], row['subscriber']))   

        contact = Contact(id, name, phone, email, roles)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'contact', contact),
                      self._mh.fromhere())                    
                                             
        return contact                                             
        
    def create_contact(self, name, phone=None, email=None):
        """Method creates contact
        
        Args:
           name (str): name
           phone (str): phone number
           email (str): email         
             
        Returns:
           int: created contact id
                
        """             
        
        msg = 'name:{0}, phone:{1}, email:{2}'.format(name, phone, email)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_contact', msg), 
                      self._mh.fromhere())          
        
        query = 'INSERT INTO contact (name, phone, email, create_date) ' + \
                'VALUES (?, ?, ?, CURRENT_TIMESTAMP)'                  
        self._client.exec_query(query, [name, phone, email])                                

        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)
        id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|phone:{2}|email:{3}'.format(id, name, phone, email)
                  
        self._client.exec_query(query, ['contact', id, 'create_contact', log])            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'contact', id), 
                      self._mh.fromhere())
            
        return id
        
    def change_contact(self, id, name=None, phone=None, email=None):
        """Method changes contact
        
        Args:
           id (int): contact id
           name (str): name
           phone (str): phone number
           email (str): email         
             
        Returns:
           bool: result
                
        """             
        
        msg = 'id:{0}, name:{1}, phone:{2}, email:{3}'.format(id, name, phone, email)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_contact', msg), 
                      self._mh.fromhere())           
            
        query = 'SELECT COUNT(*) FROM contact WHERE id = ?'
        res, row = self._client.exec_query(query, [id], True)
        
        if (row[0] == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'contact', id), 
                          self._mh.fromhere())
            return False             
            
        query = 'UPDATE contact SET {0}, modify_date = CURRENT_TIMESTAMP WHERE id = ?'
            
        if (name != None):
            self._client.exec_query(query.format('name = ?'), [name, id])
        if (phone != None):
            self._client.exec_query(query.format('phone = ?'), (phone, id))
        if (email != None):
            self._client.exec_query(query.format('email = ?'), (email, id))
                
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|name:{1}|phone:{2}|email:{3}'.format(id, name, phone, email)
                  
        res, row = self._client.exec_query(query, ['contact', id, 'change_contact', log])    
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'contact', id), 
                      self._mh.fromhere())                                                                             
            
        return res 
        
    def assign_contact_role(self, id, role, customer=None, payer=None, subscriber=None):
        """Method assigns contact role
        
        Args:
           id (int): contact id
           role (str): role title, lov_contact_role.title
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id     
             
        Returns:
           bool: result
                
        """             
        
        msg = 'id:{0}, role:{1}, customer:{2}, payer:{3}, subscriber:{4}'.format(
              id, role, customer, payer, subscriber)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'assign_contact_role', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT id FROM lov_contact_role WHERE title = ?'            
        res, row = self._client.exec_query(query, [role], True)
        
        role_id = row['id'] if (row != None) else None
        if (role_id == None):
            return False
            
        query = 'INSERT INTO contact_role (contact_role, contact, customer, payer, subscriber, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'                
        self._client.exec_query(query, [role_id, id, customer, payer, subscriber])            
                       
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)                 
        rec_id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|role:{1}|customer:{2}|payer:{3}'.format(id, role, customer, payer) + \
              u'|subscriber:{0}'.format(subscriber)
                  
        res, row = self._client.exec_query(query, ['contact_role', rec_id, 'assign_contact_role', log])   
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_role_assigned', 'contact'), 
                      self._mh.fromhere())        
            
        return res    
        
    def revoke_contact_role(self, id, role, customer=None, payer=None, subscriber=None):
        """Method revokes contact role
        
        Args:
           id (int): contact id
           role (str): role title, lov_contact_role.title
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id     
             
        Returns:
           bool: result
                
        """          
        
        msg = 'id:{0}, role:{1}, customer:{2}, payer:{3}, subscriber:{4}'.format(
              id, role, customer, payer, subscriber)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'revoke_contact_role', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT id FROM lov_contact_role WHERE title = ?'            
        res, row = self._client.exec_query(query, [role], True)
            
        role_id = row[0] if (row != None) else None
        if (role_id == None):
            return False
            
        query = 'SELECT id FROM contact_role WHERE contact_role = ? AND contact = ? {0}' 
        if (customer != None):
            res, row = self._client.exec_query(query.format('AND customer = ?'), [role_id, id, customer], True)
        elif (payer != None):
            res, row = self._client.exec_query(query.format('AND payer = ?'), [role_id, id, payer], True)
        elif (subscriber != None):
            res, row = self._client.exec_query(query.format('AND subscriber = ?'), [role_id, id, subscriber], True)
            
        rec_id = row[0] if (row != None) else None
        if (rec_id == None):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_role'), self._mh.fromhere())  
            return False
            
        query = 'DELETE FROM contact_role where id = ?'
        self._client.exec_query(query, [rec_id])
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|role:{1}|customer:{2}|payer:{3}'.format(id, role, customer, payer) + \
              u'|subscriber:{0}'.format(subscriber)
                  
        res, row = self._client.exec_query(query, ['contact_role', rec_id, 'revoke_contact_role', log])   
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_role_revoke', 'contact'), 
                      self._mh.fromhere())         
            
        return res             
        
    def read_address(self, id):
        """Method reads address
        
        Args:
           id (int): address id         
             
        Returns:
           obj: crm_entities.Address 
                
        """           
        
        msg = 'id:{0}'.format(id)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_address', msg), 
                      self._mh.fromhere())
                      
        query = 'SELECT a.id, a.street, a.street_no, a.city, a.zip, ' + \
                'c.title, b.contact, b.customer, b.payer, b.subscriber ' + \
                'FROM address a LEFT JOIN address_role b ON a.id = b.address ' + \
                'LEFT JOIN lov_address_role c ON b.address_role = c.id ' + \
                'WHERE a.id = ?'
        res, rows = self._client.exec_query(query, [id])
            
        if (len(rows) == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'address', id), 
                          self._mh.fromhere())            
            return None
            
        first = True
        id = None
        street = None
        street_no = None
        city = None
        zip = None
        roles = []
            
        for row in rows:
                
            if (first):
                    
                id = row['id']
                street = row['street']
                street_no = row['street_no']
                city = row['city']
                zip = row['zip']
                first = False 
                    
            if (row['title'] != None):
                roles.append(AddressRole(row['id'], row['title'], row['contact'], 
                                         row['customer'], row['payer'], row['subscriber']))

        address = Address(id, street, street_no, city, zip, roles)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'address', address),
                      self._mh.fromhere())
                                             
        return address                                             
        
    def create_address(self, street, street_no, city, zip):
        """Method creates address
        
        Args:
           street (str): street
           street_no (str): street number
           city (str): city
           zip (int): zip code       
             
        Returns:
           int: created address id
                
        """          
        
        msg = 'street:{0}, street_no:{1}, city:{2}, zip:{3}'.format(street, street_no, city, zip)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_address', msg), 
                      self._mh.fromhere())         
        
        query = 'INSERT INTO address (street, street_no, city, zip, create_date) ' + \
                'VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)'
        self._client.exec_query(query, [street, street_no, city, zip])                                             
            
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)
        id = row[0] 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|street:{1}|street_no:{2}|city:{3}'.format(id, street, street_no, city) + \
              u'|zip:{0}'.format(zip)
                  
        self._client.exec_query(query, ['address', id, 'create_address', log])            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'address', id), 
                      self._mh.fromhere())
            
        return id
        
    def change_address(self, id, street=None, street_no=None, city=None, zip=None):
        """Method changes address
        
        Args:
           id (int): address id
           street (str): street
           street_no (str): street number
           city (str): city
           zip (int): zip code    
             
        Returns:
           bool: result
                
        """             
        
        msg = 'id:{0}, street:{1}, street_no:{2}, city:{3}, zip:{4}'.format(id, street, street_no, city, zip)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_address', msg), 
                      self._mh.fromhere())            
            
        query = 'SELECT COUNT(*) FROM address WHERE id = ?'
        res, row = self._client.exec_query(query, [id], True)
            
        if (row[0] == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'address', id), 
                          self._mh.fromhere())
            return False             
            
        query = 'UPDATE address SET {0}, modify_date = CURRENT_TIMESTAMP WHERE id = ?'
            
        if (street != None):
            self._client.exec_query(query.format('street = ?'), [street, id])
        if (street_no != None):
            self._client.exec_query(query.format('street_no = ?'), [street_no, id])
        if (city != None):
            self._client.exec_query(query.format('city = ?'), [city, id])
        if (zip != None):
            self._client.exec_query(query.format('zip = ?'), [zip, id])                
                
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|street:{1}|street_no:{2}|city:{3}'.format(id, street, street_no, city) + \
              u'|zip:{0}'.format(zip)
                  
        res, rows = self._client.exec_query(query, ['address', id, 'change_address', log])                  
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'address', id), 
                      self._mh.fromhere())                                                                
            
        return res               
        
    def assign_address_role(self, id, role, contact=None, customer=None, payer=None, subscriber=None):
        """Method assigns address role
        
        Args:
           id (int): address id
           role (str): role title, lov_address_role.title
           contact (int): assigned contact id
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id  
            
        Returns:
           bool: result
                
        """             
        
        msg = 'id:{0}, role:{1}, contact:{2}, customer:{3}, payer:{4}, subscriber:{5}'.format(
              id, role, contact, customer, payer, subscriber)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'assign_address_role', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT id FROM lov_address_role WHERE title = ?'
        res, row = self._client.exec_query(query, [role], True)
        
        role_id = row[0] if (row != None) else None 
        if (role_id == None):
            return False           
            
        query = 'INSERT INTO address_role (address_role, address, contact, customer, payer, subscriber, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'                
        self._client.exec_query(query, [role_id, id, contact, customer, payer, subscriber])            
                     
        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)   
        rec_id = row[0]                 
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|role:{1}|contact:{2}|customer:{3}'.format(id, role, contact, customer) + \
              u'|payer:{0}|subscriber:{1}'.format(payer, subscriber)
                  
        res, row = self._client.exec_query(query, ['address_role', rec_id, 'assign_address_role', log]) 
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_role_assigned', 'address'), 
                      self._mh.fromhere())         
            
        return res    
        
    def revoke_address_role(self, id, role, contact=None, customer=None, payer=None, subscriber=None):
        """Method revokes address role
        
        Args:
           id (int): address id
           role (str): role title, lov_address_role.title
           contact (int): assigned contact id
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id  
             
        Returns:
           bool: result
                
        """         
        
        msg = 'id:{0}, role:{1}, contact:{2}, customer:{3}, payer:{4}, subscriber:{5}'.format(
              id, role, contact, customer, payer, subscriber)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'revoke_address_role', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT id FROM lov_address_role WHERE title = ?'
        res, row = self._client.exec_query(query, [role], True)
        
        role_id = row[0] if (row != None) else None
        if (role_id == None):
            return False
            
        query = 'SELECT id FROM address_role WHERE address_role = ? AND address = ? {0}' 
        if (contact != None):
            res, row = self._client.exec_query(query.format('AND contact = ?'), [role_id, id, contact], True)            
        elif (customer != None):
            res, row = self._client.exec_query(query.format('AND customer = ?'), [role_id, id, customer], True)
        elif (payer != None):
            res, row = self._client.exec_query(query.format('AND payer = ?'), [role_id, id, payer], True)
        elif (subscriber != None):
            res, row = self._client.exec_query(query.format('AND subscriber = ?'), [role_id, id, subscriber], True)
                
        rec_id = row[0] if (row != None) else None
        if (rec_id == None):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_role'), self._mh.fromhere())  
            return False
            
        query = 'DELETE FROM address_role where id = ?'
        self._client.exec_query(query, [rec_id])
            
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'
        log = u'id:{0}|role:{1}|contact:{2}|customer:{3}'.format(id, role, contact, customer) + \
              u'|payer:{0}|subscriber:{1}'.format(payer, subscriber)
                  
        self._client.exec_query(query, ('address_role', rec_id, 'revoke_address_role', log))
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_role_revoked', 'address'), 
                      self._mh.fromhere())            
            
        return res
        
    def read_services(self, customer=None, payer=None, subscriber=None, service=None):
        """Method read services
        
        Args:
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id
           service (int): service id, lov_service.id, default empty, read all services for entity     
             
        Returns:
           list: list of crm_entities.Service
                
        """         
        
        msg = 'customer:{0}, payer:{1}, subscriber:{2}, service:{3}'.format(
              customer, payer, subscriber, service)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'read_services', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT a.service, c.title, d.title AS status, b.param, b.value ' + \
                'FROM service a LEFT JOIN service_params b ON a.id = b.service ' + \
                'JOIN lov_service c ON a.service = c.id JOIN lov_status d ON a.status = d.id ' + \
                'WHERE '                    
                    
        entity = None
        if (customer != None):
            query += 'a.customer = ?'
            entity = customer
        elif (payer != None):
            query += 'a.payer = ?'
            entity = payer
        elif (subscriber != None):
            query += 'a.subscriber = ?'
            entity = subscriber  
                
        if (service == None):
            query += ''
            res, rows = self._client.exec_query(query, [entity])
        else:
            query += ' AND a.service = ?'
            res, rows = self._client.exec_query(query, [entity, service])                             
            
        if (len(rows) > 0):

            curr = None
            services = []
            id = None
            title = None
            status = None
            params = None               

            for row in rows:
                    
                id = row['service']
                
                # store service
                if (curr != None and curr != id):
                    services.append(Service(curr, title, status, params))
                        
                # new service
                if (curr == None or curr != id):
                    curr = id
                    title = row['title']
                    status = row['status']
                    params = {} 
                    
                # store param
                if (row['param'] != None):
                    params[row['param']] = row['value']
                    
            # last service
            if (id != None):
                services.append(Service(id, title, status, params))  
                
            for service in services:    
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_found', 'service', service),
                              self._mh.fromhere())
                    
            return services 
            
        else:
            return None              
        
    def create_service(self, service, customer=None, payer=None, subscriber=None, status='active', params={}):  
        """Method creates service
        
        Args: 
           service (int): service id, lov_service.id
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id
           status (str): status, lov_status.title, default active
           params (dict): key (int), lov_service_param.id, value (str)  
             
        Returns:
           bool: result
                
        """           
        
        msg = 'service:{0}, customer:{1}, payer:{2}, subscriber:{3}, status:{4}, params:{5}'.format(
              service, customer, payer, subscriber, status, params)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'create_service', msg), 
                      self._mh.fromhere())
            
        query = 'SELECT customer, payer, subscriber FROM lov_service WHERE id = ?'
        res, row = self._client.exec_query(query, [service], True)            
                            
        if (len(row) == 0):
            self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'service', service),
                          self._mh.fromhere())
            return False
            
        if (customer != None):
                
            if (row['customer'] != 1):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_srv_forbidden', service, 'customer'),
                              self._mh.fromhere())
                return False
                
            query = 'SELECT id FROM service WHERE service = ? AND customer = ?'
            res, rows = self._client.exec_query(query, [service, customer])
            if (len(rows) > 0):
                self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_srv_assigned', service), 
                              self._mh.fromhere())
                return False
                
        elif (payer != None):
                
            if (row['payer'] != 1):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_srv_forbidden', service, 'payer'),
                                  self._mh.fromhere())
                return False
                
            query = 'SELECT id FROM service WHERE service = ? AND payer = ?'
            res, rows = self._client.exec_query(query, [service, payer])
            if (len(rows) > 0):
                self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_srv_assigned', service), 
                              self._mh.fromhere())
                return False
                
        elif (subscriber != None):
                
            if (row['subscriber'] != 1):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_srv_forbidden', service, 'subscriber'),
                                self._mh.fromhere())
                return False
                
            query = 'SELECT id FROM service WHERE service = ? AND subscriber = ?'
            res, rows = self._client.exec_query(query, [service, subscriber])
            if (len(rows) > 0):
                self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_srv_assigned', service), 
                              self._mh.fromhere())
                return False   
                
        query = 'SELECT id FROM lov_status WHERE title = ?'
        res, row = self._client.exec_query(query, [status], True)
        
        status_id = row[0] if (row != None) else None

        query = 'INSERT INTO service (service, status, customer, payer, subscriber, create_date) ' + \
                'VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)'  
        self._client.exec_query(query, [service, status_id, customer, payer, subscriber])

        res, row = self._client.exec_query('SELECT last_insert_rowid()', fetch_one=True)   
        rec_id = row[0]     

        log = u'service:{0}|status:{1}|customer:{2}|payer:{3}'.format(service, status, customer, payer) +\
              u'|subscriber:{0}|params#'.format(subscriber) 
            
        if (len(params) > 0):
            for key, value in params.items():
                           
                query = 'SELECT mandatory, default_value from lov_service_param WHERE service = ? AND id = ?'
                res, row = self._client.exec_query(query, [service, key], True)
                    
                if (len(row) == 0):
                    self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_unknown_srv_param', service, key), 
                                  self._mh.fromhere())
                    return False
                elif (row['mandatory'] == 1 and value == None):
                    self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_mandatory_param'), key, 
                                  self._mh.fromhere())
                    return False 
                elif (row['mandatory'] == 0 and value == None):                            
                    query = 'INSERT INTO service_params (param, value, service, create_date) ' + \
                            'VALUES (?, ?, ?, CURRENT_TIMESTAMP)'
                    self._client.exec_query(query, [key, row['default_value'], rec_id])
                    log += u'id:{0}|value:{1}'.format(key, row['default_value'])
                else:
                    query = 'INSERT INTO service_params (param, value, service, create_date) ' + \
                            'VALUES (?, ?, ?, CURRENT_TIMESTAMP)' 
                    self._client.exec_query(query, [key, value, rec_id])  
                log += u'id:{0}|value:{1}'.format(key, value) 
                        
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'                  
        res, row = self._client.exec_query(query, ['service', rec_id, 'create_service', log])                                                                           
            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_created', 'service', id), 
                      self._mh.fromhere())
            
        return res           
        
    def change_service(self, service, customer=None, payer=None, subscriber=None, status=None, params={}): 
        """Method changes service
        
        Args: 
           service (int): service id, lov_service.id
           customer (int): assigned customer id
           payer (int): assigned payer id
           subscriber (int): assigned subscriber id
           status (str): status, lov_status.title, default active
           params (dict): key (int), lov_service_param.id, value (str)  
             
        Returns:
           bool: result
                
        """            
        
        msg = 'service:{0}, customer:{1}, payer:{2}, subscriber:{3}, status:{4}, params:{5}'.format(
              service, customer, payer, subscriber, status, params)
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_func', 'change_service', msg), 
                      self._mh.fromhere())
            
        id = None      
            
        if (customer != None):
                
            query = 'SELECT id FROM service WHERE service = ? AND customer = ?'
            res, row = self._client.exec_query(query, [service, customer], True)
                
            if (len(row) == 0):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'service', id), 
                              self._mh.fromhere())
                return False
                
        elif (payer != None):
                
            query = 'SELECT id FROM service WHERE service = ? AND payer = ?'
            res, row = self._client.exec_query(query, [service, payer])
                
            if (len(row) == 0):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'service', id), 
                              self._mh.fromhere())
                return False              
                
        elif (subscriber != None):
                
            query = 'SELECT id FROM service WHERE service = ? AND subscriber = ?'
            res, row = self._client.exec_query(query, [service, subscriber], True)
                
            if (len(row) == 0):
                self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_unknown_entity', 'service', id), 
                              self._mh.fromhere())
                return False   
                
        id = row[0]                
                
        if (status != None):
                    
            query = 'SELECT id FROM lov_status WHERE title = ?'
            res, row = self._client.exec_query(query, [status], True)
            
            status_id = row[0] if (row != None) else None
            
            query = 'UPDATE service SET status = ?, modify_date = CURRENT_TIMESTAMP WHERE id = ?'  
            self._client.exec_query(query, [status_id, id])
             
        log = u'service:{0}|status:{1}|customer:{2}|payer:{3}'.format(service, status, customer, payer) +\
              u'|subscriber:{0}|params#'.format(subscriber) 
            
        if (len(params) > 0):
            for key, value in params.items():
                           
                query = 'SELECT a.id, b.mandatory FROM service_params a, lov_service_param b ' + \
                        'WHERE a.service = ? AND a.param = ? AND a.param = b.id'
                res, row = self._client.exec_query(query, [id, key], True)
                    
                if (len(row) == 0):
                    self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_param_not_assigned', key),
                                  self._mh.fromhere())
                    return False
                elif (row['mandatory'] == 1 and value == None):
                    self._mh.dmsg('htk_on_extension_error', self._mh._trn.msg('te_db_mandatory_param', key), 
                                  self._mh.fromhere())
                    return False 
                else:
                    query = 'UPDATE service_params SET value = ?, modify_date = CURRENT_TIMESTAMP ' + \
                            'WHERE id = ?'
                    self._client.exec_query(query, (value, row['id']))  
                log += u'id:{0}|value:{1}'.format(key, value) 
                        
        query = 'INSERT INTO history (event_date, table_name, table_id, event, log) ' + \
                'VALUES (CURRENT_TIMESTAMP, ?, ?, ?, ?)'                  
        self._client.exec_query(query, ['service', id, 'change_service', log])                                                                           
            
        self._mh.dmsg('htk_on_debug_info', self._mh._trn.msg('te_db_entity_changed', 'service', id), 
                      self._mh.fromhere())      
            
        return True
    