HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux spn-python 5.15.0-89-generic #99-Ubuntu SMP Mon Oct 30 20:42:41 UTC 2023 x86_64
User: arjun (1000)
PHP: 8.1.2-1ubuntu2.20
Disabled: NONE
Upload Files
File: //proc/thread-self/root/home/arjun/projects/buyercall/buyercall/blueprints/partnership/models.py
import uuid
import math
import logging
import traceback
from flask import jsonify
from datetime import datetime, timedelta, date as datetime_date
import pytz
from buyercall.blueprints.sms.models import Message
from buyercall.blueprints.user.models import User
from buyercall.blueprints.contacts.models import Contact, CreditReports
from buyercall.blueprints.widgets.models import Widget
from buyercall.blueprints.agents.models import Agent
from buyercall.blueprints.issue.models import Issue
<<<<<<< HEAD
from buyercall.blueprints.appointments.models import Appointment
=======
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
from buyercall.blueprints.activity.models import ActivityLogs
from buyercall.lib.util_sqlalchemy import ResourceMixin
from buyercall.lib.util_datetime import convert_to_deltatime
from sqlalchemy.sql.expression import update
from buyercall.blueprints.billing.models.invoice import Invoice
from buyercall.blueprints.leads.models import Lead
from buyercall.extensions import db
from sqlalchemy import or_, and_, extract
from collections import OrderedDict
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.sql import text
<<<<<<< HEAD
from dateutil.relativedelta import relativedelta
from sqlalchemy.dialects.postgresql import UUID, ARRAY
=======
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
from buyercall.blueprints.filters import format_phone_number

log = logging.getLogger(__name__)

BILLING_TYPE = OrderedDict([
    ('partnership', 'Bill Partnership'),
    ('account', 'Bill Account'),
    ('invoice', 'By Invoice')
])

BUSINESS_TYPE = OrderedDict([
    ('automotive', 'Automotive'),
    ('general', 'General'),
    ('healthcare', 'Healthcare')
])

CREDIT_PROVIDER = OrderedDict([
    ('', ''),
    ('offerlogix', 'OfferLogix')
])


class ApiToken(ResourceMixin, db.Model):
    __tablename__ = 'api_tokens'

    id = db.Column(db.Integer, primary_key=True)

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')

    api_token_hash = db.Column(db.String(64), nullable=False)

    @classmethod
    def create(cls):
        """
        Create a new api token

        :return: int / id
        """
        from buyercall.lib.util_crypto import SHA
        token = str(uuid.uuid4())
        new_api_token = ApiToken(
            active=False,
            api_token_hash=SHA.encrypt(token)
        )

        db.session.add(new_api_token)
        db.session.flush()
        result_api_id = new_api_token.id
        db.session.commit()

        return result_api_id

    @classmethod
    def check_token(cls, token):
        """
        Check to see if REST API token is valid, return the id of the token
        """
        found_token_id = -1

        from buyercall.lib.util_crypto import SHA
        token_hash = SHA.encrypt(token)

        found_token_id = db.session.query(ApiToken.id) \
            .filter(and_(ApiToken.active == True, ApiToken.api_token_hash == token_hash)) \
            .first()

        if found_token_id:
            if len(found_token_id) >= 1:
                found_token_id = found_token_id[0]
                if found_token_id >= 1:
                    return found_token_id

        return None

    @classmethod
    def regenerate_token(cls, id):
        """
        Regenerate the token for a specific token entry. The new token is returned.
        """
        token_hash = db.session.query(ApiToken) \
            .filter(ApiToken.id == id) \
            .first()

        if token_hash is not None:
            new_token = str(uuid.uuid4())

            from buyercall.lib.util_crypto import SHA
            api_token_hash = SHA.encrypt(new_token)

            token_hash.api_token_hash = api_token_hash
            token_hash.save()
            db.session.commit()

            return new_token
        else:
            return None


class ExternalApiServiceProviders(ResourceMixin, db.Model):
    __tablename__ = 'external_api_service_providers'

    id = db.Column(db.Integer, primary_key=True)

    name = db.Column(db.String(128), index=True, nullable=False,
                     server_default='')

    @classmethod
    def get_provider_name(cls, pa_external_api_service_provider_id):
        """
        Returns the API service provider name. If one does not exist, an empty string is returned.

        :return: external api service provider name
        """
        result = ''
        service_provider = db.session.query(ExternalApiServiceProviders) \
            .filter(ExternalApiServiceProviders.id == pa_external_api_service_provider_id) \
            .first()

        if service_provider is not None:
            result = service_provider.name

        return result


class ExternalApiServiceProvidersPartnershipAccountTie(ResourceMixin, db.Model):
    __tablename__ = 'external_api_service_providers_partnership_account_tie'

    external_api_service_provider_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'external_api_service_providers.id',
            name='partnership_account_external_api_tie_sp_id_fkey'
        ),
        primary_key=True,
        index=True
    )

    partnership_account_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_accounts.id',
            name='partnership_account_external_api_tie_pa_fkey'
        ),
        primary_key=True
    )

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')

    username = db.Column(db.String(128), nullable=False, server_default='')

    password = db.Column(db.String(128), nullable=False, server_default='')

    url = db.Column(db.String(128), nullable=False, server_default='')

    token_url = db.Column(db.String(128), nullable=False, server_default='')

    client_id = db.Column(db.String(128), nullable=False, server_default='')

    secret = db.Column(db.String(128), nullable=False, server_default='')

    source = db.Column(db.String(128), nullable=False, server_default='')

    app_source = db.Column(db.String(128), nullable=False, server_default='')

    @classmethod
    def delete(cls, pa_external_api_service_provider_id, pa_partnership_account_id):
        """
        Delete the partnership account's external api service provider details.

        :return: bool
        """
        result = False
        partnership_provider_tie = db.session.query(ExternalApiServiceProvidersPartnershipAccountTie).filter(
            and_(ExternalApiServiceProvidersPartnershipAccountTie.partnership_account_id == pa_partnership_account_id,
                 ExternalApiServiceProvidersPartnershipAccountTie.external_api_service_provider_id ==
                 pa_external_api_service_provider_id)) \
            .first()

        if partnership_provider_tie is not None:
            db.session.delete(partnership_provider_tie)
            db.session.commit()
            result = True

        return result

    @classmethod
    def update(cls, pa_external_api_service_provider_id, pa_partnership_account_id, pa_client_id,
               pa_username, pa_password, pa_url, pa_token_url, pa_source, pa_app_source, pa_secret):
        """
        Update the partnership account's external api service provider details.

        :return: bool
        """
        partnership_provider_tie = db.session.query(ExternalApiServiceProvidersPartnershipAccountTie) \
            .filter(
            ExternalApiServiceProvidersPartnershipAccountTie.partnership_account_id == pa_partnership_account_id,
            ExternalApiServiceProvidersPartnershipAccountTie.external_api_service_provider_id == pa_external_api_service_provider_id
        ).first()

        if partnership_provider_tie is not None:
            partnership_provider_tie.external_api_service_provider_id = pa_external_api_service_provider_id
            partnership_provider_tie.username = pa_username
            partnership_provider_tie.password = pa_password
            partnership_provider_tie.url = pa_url
            partnership_provider_tie.secret = pa_secret
            partnership_provider_tie.client_id = pa_client_id
            partnership_provider_tie.token_url = pa_token_url
            partnership_provider_tie.source = pa_source
            partnership_provider_tie.app_source = pa_app_source

            db.session.commit()
            return True
        elif partnership_provider_tie is None:
            partnership_provider_tie = ExternalApiServiceProvidersPartnershipAccountTie(
                external_api_service_provider_id=pa_external_api_service_provider_id,
                partnership_account_id=pa_partnership_account_id,
                client_id=pa_client_id,
                token_url=pa_token_url,
                username=pa_username,
                password=pa_password,
                url=pa_url,
                app_source=pa_app_source,
                source=pa_source,
                secret=pa_secret)

            db.session.add(partnership_provider_tie)
            db.session.flush()
            db.session.commit()
            return True
        else:
            return False

    @classmethod
    def exists(cls, pa_partnership_account_id, pa_service_provider_id):
        """
        Check whether the partnership accounts is assigned to provided external api service provider.

        :return: external api service provider id
        """

        partnership_provider_tie = db.session.query(ExternalApiServiceProvidersPartnershipAccountTie) \
            .filter(
            ExternalApiServiceProvidersPartnershipAccountTie.partnership_account_id == pa_partnership_account_id,
            ExternalApiServiceProvidersPartnershipAccountTie.external_api_service_provider_id == pa_service_provider_id
        ).first()

        if partnership_provider_tie is not None:
            return partnership_provider_tie.external_api_service_provider_id
        return -1

    @classmethod
    def service_provider_name(cls, pa_partnership_account_id):
        """
        Returns the API service provider name. If one does not exist, an empty string is returned.

        :return: external api service provider name
        """
        result = ''

        partnership_provider_tie = db.session.query(ExternalApiServiceProvidersPartnershipAccountTie) \
            .filter(
            ExternalApiServiceProvidersPartnershipAccountTie.partnership_account_id == pa_partnership_account_id
        ).first()

        if partnership_provider_tie is not None:
            result = ExternalApiServiceProviders.get_provider_name(
                partnership_provider_tie.external_api_service_provider_id
            )

        return result

    @classmethod
    def exists_for_service_provider(cls, partnership_account_id, service_provider_name):
        """
        Check whether the partnership accounts is assigned to provided external api service provider.

        :return: external api service provider object
        """
        partnership_provider_tie = db.session \
            .query(ExternalApiServiceProvidersPartnershipAccountTie) \
            .join(ExternalApiServiceProviders) \
            .filter(ExternalApiServiceProvidersPartnershipAccountTie.partnership_account_id == partnership_account_id,
                    ExternalApiServiceProvidersPartnershipAccountTie.active) \
            .filter(ExternalApiServiceProviders.name == service_provider_name) \
            .first()

        return partnership_provider_tie


class PartnershipAccountGroupTie(ResourceMixin, db.Model):
    __tablename__ = 'partnership_account_group_tie'

    partnership_account_group_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_account_groups.id',
            name='partnership_account_group_account_tie_id_fkey'
        ),
        primary_key=False, index=True
    )

    partnership_account_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_accounts.id',
            name='partnership_account_group_id_tie_fkey'
        ),
        primary_key=True
    )

    @classmethod
    def update(cls, pa_partnership_account_group_id, pa_partnership_account_id):
        """
        Update the partnership account's group details.

        :return: bool
        """
        # account_group = db.session.query(PartnershipAccountGroupTie)\
        #    .filter(PartnershipAccountGroupTie.partnership_account_id == pa_partnership_account_id).first()
        #
        # if account_group is not None:
        #    account_group.partnership_account_group_id = pa_partnership_account_group_id
        #    db.session.commit()
        #    return True
        # elif account_group is None:
        #    account_group = PartnershipAccountGroupTie(partnership_account_group_id = pa_partnership_account_group_id,
        #    partnership_account_id = pa_partnership_account_id)
        #    db.session.add(account_group)
        #    db.session.flush()
        #    db.session.commit()
        #    return True
        # else:
        return False

    @classmethod
    def exists(cls, partnership_account_group_id, partnership_account_id):
        """
        Check whether the two partnership accounts are in the same group.

        :return: bool
        """

        account_group = db.session.query(PartnershipAccountGroupTie) \
            .filter(and_(PartnershipAccountGroupTie.partnership_account_group_id == partnership_account_group_id,
                         PartnershipAccountGroupTie.partnership_account_id == partnership_account_id)).first()

        if account_group is not None:
            return True
        else:
            return False


<<<<<<< HEAD

class PartnershipAccountCreditTie(ResourceMixin, db.Model):
    __tablename__ = 'partnership_account_credit_tie'

    id = db.Column(db.Integer, primary_key=True)

    service_provider = db.Column(db.String(128), index=True, nullable=False, server_default='')

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')

    partnership_account_id = db.Column(db.Integer, db.ForeignKey('partnership_accounts.id',
                                                                 onupdate='CASCADE',
                                                                 ondelete='CASCADE'),
                                       index=True, nullable=False)

    product_type = db.Column(db.String(128), index=True, nullable=False, server_default='')

    api_account = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_username = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_password = db.Column(db.String(256), index=True, nullable=False, server_default='')

    experian_enabled = db.Column('experian_enabled', db.Boolean(), nullable=False, server_default='0')

    transunion_enabled = db.Column('transunion_enabled', db.Boolean(), nullable=False, server_default='0')

    equifax_enabled = db.Column('equifax_enabled', db.Boolean(), nullable=False, server_default='0')

    @classmethod
    def partner_account_seven_hundred_credit_info(cls, paid, product_type):
        """
        Find and return the partnership account's 700 credit credentials.
        """
        seven_hundred_credit_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_account_id == paid,
                    cls.service_provider == '700Credit',
                    cls.product_type == product_type,
                    cls.active.is_(True)).first()

        if seven_hundred_credit_as_provider is not None:
            return seven_hundred_credit_as_provider

        else:
            return None

    @classmethod
    def partner_account_finserv_credit_info(cls, paid, product_type):
        """
        Find and return the partnership account's Finserv credit credentials.
        """
        finserv_credit_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_account_id == paid,
                    cls.service_provider == 'Finserv',
                    cls.product_type == product_type,
                    cls.active.is_(True)).first()

        if finserv_credit_as_provider is not None:
            return finserv_credit_as_provider

        else:
            return None


=======
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
class PartnershipAccountGroup(ResourceMixin, db.Model):
    __tablename__ = 'partnership_account_groups'

    id = db.Column(db.Integer, primary_key=True)

    name = db.Column(db.String(128), index=True, nullable=False,
                     server_default='')


<<<<<<< HEAD
=======
class PartnershipAccountCreditTie(ResourceMixin, db.Model):
    __tablename__ = 'partnership_account_credit_tie'

    id = db.Column(db.Integer, primary_key=True)

    service_provider = db.Column(db.String(128), index=True, nullable=False, server_default='')

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')

    partnership_account_id = db.Column(db.Integer, db.ForeignKey('partnership_accounts.id',
                                                                 onupdate='CASCADE',
                                                                 ondelete='CASCADE'),
                                       index=True, nullable=False)

    product_type = db.Column(db.String(128), index=True, nullable=False, server_default='')

    api_account = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_username = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_password = db.Column(db.String(256), index=True, nullable=False, server_default='')

    experian_enabled = db.Column('experian_enabled', db.Boolean(), nullable=False, server_default='0')

    transunion_enabled = db.Column('transunion_enabled', db.Boolean(), nullable=False, server_default='0')

    equifax_enabled = db.Column('equifax_enabled', db.Boolean(), nullable=False, server_default='0')

    def __init__(self, *args, **kwargs):
        super(PartnershipAccountCreditTie, self).__init__(*args, **kwargs)

    @hybrid_property
    def is_active(self):
        return self.active

    @is_active.expression
    def is_active(cls):
        return cls.active

    @classmethod
    def partner_account_seven_hundred_credit_info(cls, paid, product_type):
        """
        Find and return a partnership account's 700 credit credentials.
        """
        seven_hundred_credit_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_account_id == paid,
                    cls.service_provider == '700Credit',
                    cls.product_type == product_type,
                    cls.active.is_(True)).first()

        if seven_hundred_credit_as_provider is not None:
            return seven_hundred_credit_as_provider

        else:
            return None

    @classmethod
    def partner_account_finserv_credit_info(cls, paid, product_type):
        """
        Find and return a partnership account's Finserv credit credentials.
        """
        finserv_credit_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_account_id == paid,
                    cls.service_provider == 'Finserv',
                    cls.product_type == product_type,
                    cls.active.is_(True)).first()

        if finserv_credit_as_provider is not None:
            return finserv_credit_as_provider

        else:
            return None


>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
class PartnershipAccount(ResourceMixin, db.Model):
    __tablename__ = 'partnership_accounts'

    # Serializing fields
    serialize_only = ('sid', 'name', 'active', 'billing_type', 'business_type', 'appointment_enabled', 'email',
                      'description', 'safety_information', 'timezone')

    id = db.Column(db.Integer, primary_key=True)

    sid = db.Column(UUID(as_uuid=True), unique=True, default=uuid.uuid4, index=True)

    name = db.Column(db.String(255), index=True, nullable=False,
                     server_default='')

    partnership_id = db.Column(db.Integer, db.ForeignKey('partnerships.id',
                                                         onupdate='CASCADE',
                                                         ondelete='CASCADE'),
                               index=True, nullable=True)

    subscription_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'subscriptions.id',
            onupdate='CASCADE',
            ondelete='CASCADE'),
        index=True, nullable=True
    )

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')

    email = db.Column(db.String(320), index=True, nullable=False, server_default='')

    adf_assigned_email = db.Column(db.String(255), nullable=True)

    users = db.relationship(User, backref="partnership_account")

    agents = db.relationship(Agent, backref="partnership_account")

    issues = db.relationship(Issue, backref="partnership_account")

    import_leads = db.Column('import_leads', db.Boolean(), nullable=False, server_default='0')

    invoices = db.relationship(Invoice, backref='partnership_account')

    leads = db.relationship(Lead, backref='partnership_account', passive_deletes=True)

    appointments = db.relationship(Appointment, backref="partnership_account_appointments",
                                   lazy='dynamic', passive_deletes=True)

    messages = db.relationship(Message, backref="partnership_account", passive_deletes=True)

    contacts = db.relationship(Contact, backref="partnership_account", uselist=False)

    widgets = db.relationship(Widget, backref="partnership_account")

    activity_logs = db.relationship(ActivityLogs, backref="partnership_account")

    credit_provider = db.relationship(PartnershipAccountCreditTie, backref="partnership_account")

    credit_reports = db.relationship(CreditReports, backref="partnership_account", uselist=False)

    billing_type = db.Column(
        db.Enum(*BILLING_TYPE, name='billing_types'),
        index=True,
        nullable=False,
        server_default='partnership'
    )

    api_token_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'api_tokens.id',
            onupdate='CASCADE',
            ondelete='CASCADE'
        ),
        index=True,
        nullable=True
    )
    partner_account_code = db.Column(
        db.String(64),
        index=True,
        nullable=False,
        server_default=''
    )

    business_type = db.Column(
        db.String(128), nullable=False, server_default='general',
        index=True
    )

    is_2fa_email_enabled = db.Column(db.Boolean(), nullable=False,
                             server_default='0')

    is_2fa_sms_enabled = db.Column(db.Boolean(), nullable=False,
                           server_default='1')

    partnership_account_group = db.relationship(PartnershipAccountGroupTie, backref='partnership_account')

    description = db.Column(db.String(1024), nullable=False, server_default='')

    safety_information = db.Column(db.String(1024), nullable=False, server_default='')

    address_1 = db.Column(db.String(128), nullable=False, server_default='')

    address_2 = db.Column(db.String(128), nullable=False, server_default='')

    city = db.Column(db.String(64), nullable=False, server_default='')

    state = db.Column(db.String(64), nullable=False, server_default='')

    zip_code = db.Column(db.String(10), nullable=False, server_default='')

    country = db.Column(db.String(64), nullable=False, server_default='united states')

    timezone = db.Column(db.String(64), nullable=False, server_default='US/Eastern')

    operating_hours = db.relationship('PartnershipAccountOperatingHours', backref='partnership_account',
                                      cascade='all, delete-orphan', lazy='dynamic',
                                      order_by='asc(PartnershipAccountOperatingHours.day)')
    # Indicates if appointments are enabled for the account
    appointment_enabled = db.Column(db.Boolean, nullable=False, server_default='1')
    # The time interval in minutes for how long appointments are
    appointment_interval = db.Column(db.Integer, nullable=False, default=30)
    # The general appointment slot count to determine how many a specific day slot can be booked
    appointment_general_slot_count = db.Column(db.Integer, nullable=False, default=1)
    # The service appointment slot count to determine how many a specific day slot can be booked
    appointment_service_slot_count = db.Column(db.Integer, nullable=False, default=1)

    # Whether the appointments all hours
    all_hours = db.Column(db.Boolean, nullable=False, server_default='0')

    # account invitation token
    account_invitation_url_token = db.Column(
        db.String(255),
        index=True,
        nullable=False,
        server_default=''
    )

    # partnership_account_group_id = db.Column(db.Integerdb.ForeignKey('partnership_account_group_tie.partnerid',
    #                                                  onupdate='CASCADE'),
    #                         index=True, nullable=True, server_default=None)

    # REST API authentication token
    # api_token_hash = db.Column(db.String(60), nullable=True)

<<<<<<< HEAD
    # @classmethod
    # def create(cls, name, partnership_id, active, billing_type, email):
    #     """
    #     Create a partnership account.
    #
    #     :return: bool
    #     """
    #     try:
    #         new_account = PartnershipAccount(
    #             name=name,
    #             partnership_id=partnership_id,
    #             active=active,
    #             billing_type=billing_type,
    #             email=email
    #         )
    #
    #         api_token_id = ApiToken.create()
    #
    #         if api_token_id is not None and api_token_id > 0:
    #             new_account.api_token_id = api_token_id
    #
    #         db.session.add(new_account)
    #         db.session.commit()
    #     except Exception as e:
    #         print(e)
    #     return True

    @classmethod
    def get_by_email(cls, email):
        return cls.query.filter(cls.email == email).first()

    @classmethod
    def get_name(cls, pid):
        obj = cls.query.filter(cls.id == pid).first()
        return obj.name if obj else None

    @classmethod
    def get_by_invitation_token(cls, token=None):
        return cls.query.filter(cls.account_invitation_url_token == token).first() if token else None

    @classmethod
    def create_partial(cls, name, partnership_id, active, partner_account_code, description,
                       safety_information, address_1, address_2, city, state, zip_code, country, timezone,
                       business_type):
=======
    @hybrid_property
    def credit_sp(self):
        for i in self.credit_provider:
            return i.service_provider

    @classmethod
    def create(cls, name, partnership_id, active, billing_type):
        """
        Create a partnership account.

        :return: bool
        """
        new_account = PartnershipAccount(
            name=name,
            partnership_id=partnership_id,
            active=active,
            billing_type=billing_type
        )

        api_token_id = ApiToken.create()

        if api_token_id is not None and api_token_id > 0:
            new_account.api_token_id = api_token_id

        db.session.add(new_account)
        db.session.commit()
        return True

    @classmethod
    def create_partial(cls, name, partnership_id, active, partner_account_code, business_type=None):
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
        """
        Create a partnership account.

        :return: bool
        """
        try:
            subscription_id = Partnership.query \
                .with_entities(Partnership.subscription_id) \
                .filter(Partnership.id == partnership_id).first()

            if business_type:
                bt = business_type
            else:
                bt = 'general'
            new_account = PartnershipAccount(
                name=name,
                partnership_id=partnership_id,
                partner_account_code=partner_account_code,
                subscription_id=subscription_id,
                active=active,
<<<<<<< HEAD
                billing_type='partnership',
                description=description,
                safety_information=safety_information,
                address_1=address_1,
                address_2=address_2,
                city=city,
                state=state,
                zip_code=zip_code,
                country=country,
                timezone=timezone,
                business_type=bt
=======
                business_type=bt,
                billing_type='partnership',
                import_leads=False

>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
            )

            api_token_id = ApiToken.create()

            if api_token_id is not None and api_token_id > 0:
                new_account.api_token_id = api_token_id

            db.session.add(new_account)
            db.session.flush()
            partnership_account_id = new_account.id
            db.session.commit()

            return partnership_account_id

        except Exception as e:
            log.error('Error creating partnership account. Error: {}'.format(e))

            return -1

    @classmethod
    def update(cls, id, name, billing_type):
        """
        Update the partnership account's details.

        :return: bool
        """
        account = PartnershipAccount.query.filter(PartnershipAccount.id == id).first()

        if account is not None:
            account.name = name

            if billing_type is not None and billing_type in ['partnership', 'account', 'invoice']:
                account.billing_type = billing_type
            db.session.commit()

            return True
        else:
            return False

    @classmethod
<<<<<<< HEAD
    def api_update(cls, id, partnership_id, name, is_active, partner_account_code, description,
                   safety_information, address_1, address_2, city, state, zip_code, country, timezone,
                   business_type, users=None):
=======
    def api_update(cls, id, partnership_id, name, is_active, partner_account_code,
                   partner_account_credit_service_provider, users=None):
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
        """
        Update the partnership account's details.

        :return: bool
        """
        account = PartnershipAccount.query \
            .filter(and_(PartnershipAccount.id == id, PartnershipAccount.partnership_id == partnership_id)).first()

        if account is not None:

            if name is not None and name != '':
                account.name = name

            if is_active is not None:
                account.active = is_active

            if partner_account_code is not None and partner_account_code != '':
                account.partner_account_code = partner_account_code

<<<<<<< HEAD
            if description is not None:
                account.description = description

            if safety_information is not None:
                account.safety_information = safety_information

            if address_1 is not None:
                account.address_1 = address_1

            if address_2 is not None:
                account.address_2 = address_2

            if city is not None:
                account.city = city

            if state is not None:
                account.state = state

            if zip_code is not None:
                account.zip_code = zip_code

            if country is not None:
                account.country = country

            if timezone is not None:
                account.timezone = timezone

            if business_type is not None:
                account.business_type = business_type
=======
            if partner_account_credit_service_provider is not None and partner_account_credit_service_provider != '':
                partner_account_credit_service_provider_lower = partner_account_credit_service_provider.lower()
                existing_credit_tie = (PartnershipAccountCreditTie.query
                                       .filter(and_(PartnershipAccountCreditTie.partnership_account_id == id,
                                                    PartnershipAccountCreditTie.service_provider ==
                                                    partner_account_credit_service_provider_lower)).first())
                if not existing_credit_tie:
                    new_credit_sp = PartnershipAccountCreditTie(
                        service_provider=partner_account_credit_service_provider_lower,
                        active=True,
                        partnership_account_id=id,
                        product_type='prequalify',
                        equifax_enabled=True
                    )
                    db.session.add(new_credit_sp)
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b

            if users is not None:
                for usr in users:
                    new_user = User(
                        firstname=usr['firstname'],
                        lastname=usr['lastname'],
                        email=usr['email'].lower(),
                        phonenumber=format_phone_number(str(usr['phonenumber'])),
                        password=usr['password'],
                        role=usr['role'],
                        partnership_account_id=account.id,
                        partnership_id=account.partnership_id,
                        company=account.name,
                        tos_agreement=False
                    )
                    db.session.add(new_user)
<<<<<<< HEAD
=======

>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
            db.session.flush()
            db.session.commit()

            return True
        else:
            return False

    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return text('')

        search_query = '%{0}%'.format(query)
        search_chain = (PartnershipAccount.name.ilike(search_query))

        # return or_(*search_chain)
        return search_chain

    @classmethod
    def get_partnership_account_by_token(cls, api_token_id):
        """
        Find a PartnershipAccount by their REST API token id.
        """
        partnership_account = db.session.query(PartnershipAccount) \
            .filter(and_(PartnershipAccount.api_token_id == api_token_id, PartnershipAccount.active == True)) \
            .first()

        if partnership_account:
            return partnership_account
        return None

    @classmethod
    def regenerate_api_token(cls, id):
        """
        Regenerates the PartnershipAccount REST API token.
        :return: api token
        """
        partnership_account = db.session.query(PartnershipAccount) \
            .filter(PartnershipAccount.id == id) \
            .first()

        if partnership_account is not None:

            # Check if api token id already exists, if so, just regenerate
            if partnership_account.api_token_id is not None and partnership_account.api_token_id > 0:
                return ApiToken.regenerate_token(partnership_account.api_token_id)
            # API token id does not exist, need to create a new one and regenerate the key
            else:
                new_token = ApiToken.create()

                if new_token is not None and new_token > 0:
                    partnership_account.api_token_id = new_token
                    partnership_account.save()
                    db.session.commit()

                    return ApiToken.regenerate_token(new_token)
                else:
                    return None
        else:
            # No partnership account found
            return None

    @classmethod
    def find_by_token(cls, token):
        """
        Find a PartnershipAccount by their REST API token.
        """
        # token_hashes = db.session.query(PartnershipAccount.id, PartnershipAccount.api_token_hash).filter(
        #    PartnershipAccount.api_token_hash.isnot(None)
        # ).all()
        # for id, hash in token_hashes:
        #    if bcrypt.check_password_hash(hash, token):
        #        return PartnershipAccount.query.filter(PartnershipAccount.id == id).first()
        return None

    @classmethod
    def total_calls_count(cls, paid):
        total_calls = Lead.query.filter(Lead.partnership_account_id == paid).count()

        return total_calls

    @classmethod
    # TODO: Review me
    def total_inbound_calls_count(cls, paid):
        total_calls = Lead.query.filter(and_(Lead.partnership_account_id == paid,
                                             Lead.call_type == 'inbound')).count()

        return total_calls

    @classmethod
    # TODO: Review me
    def total_inbound_calls_count_for_phone(cls, paid, pnid):
        total_calls = Lead \
            .query \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'inbound')) \
            .filter(Lead.inbound_id == pnid) \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def monthly_total_inbound_calls_count(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        total_calls = Lead.query.with_entities(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'inbound')) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month) \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def previous_total_inbound_calls_count(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        if month == 1:
            year = now.year - 1
            month = 12
        else:
            month = month - 1

        total_calls = Lead.query.with_entities(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'inbound')) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month) \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def total_outbound_calls_count(cls, paid):
        total_calls = db.session.query(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'outbound')) \
            .distinct() \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def total_outbound_calls_count_for_phone(cls, paid, pnid):
        total_calls = db.session.query(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'outbound')) \
            .filter(Lead.inbound_id == pnid) \
            .distinct() \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def monthly_total_outbound_calls_count(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        total_calls = db.session.query(Lead).with_entities(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'outbound')) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month) \
            .distinct() \
            .count()

        return total_calls

    @classmethod
    # TODO: Review me
    def previous_total_outbound_calls_count(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        if month == 1:
            year = now.year - 1
            month = 12
        else:
            month = month - 1

        total_calls = Lead.query.with_entities(Lead.id) \
            .filter(and_(Lead.partnership_account_id == paid, Lead.call_type == 'outbound')) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month) \
            .count()

        return total_calls

    @classmethod
    def monthly_minute_usage(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        partnership_account_month_minutes = Lead.query.with_entities(Lead.duration) \
            .filter(Lead.partnership_account_id == paid) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month) \
            .all()

        new_call_duration = [i.duration for i in partnership_account_month_minutes]
        call_duration_list = list()
        for duration in new_call_duration:
            if not duration:
                duration_zero = float(0)
                call_duration_list.append(duration_zero)
            else:
                float_duration = math.ceil(float(duration) / 60.0)
                call_duration_list.append(float_duration)

        month_minutes = int(sum(call_duration_list))
        return month_minutes

    @classmethod
    def previous_month_minute_usage(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        if month == 1:
            year = now.year - 1
            month = 12
        else:
            month = month - 1

        partnership_account_previous_month_minutes = Lead.query.with_entities(Lead.duration) \
            .filter(Lead.partnership_account_id == paid) \
            .filter(extract('year', Lead.created_on) == year) \
            .filter(extract('month', Lead.created_on) == month).all()

        previous_new_call_duration = [i.duration for i in partnership_account_previous_month_minutes]
        previous_call_duration_list = list()
        for previous_duration in previous_new_call_duration:
            if not previous_duration:
                previous_duration_zero = float(0)
                previous_call_duration_list.append(previous_duration_zero)
            else:
                previous_float_duration = math.ceil(float(previous_duration) / 60.0)
                previous_call_duration_list.append(previous_float_duration)

        previous_month_minutes = int(sum(previous_call_duration_list))

        return previous_month_minutes

    @classmethod
    def total_minute_usage(cls, paid):
        partnership_account_total_minutes = Lead.query.with_entities(Lead.duration) \
            .filter(Lead.partnership_account_id == paid).all()

        new_total_duration = [i.duration for i in partnership_account_total_minutes]
        total_duration_list = list()
        for total in new_total_duration:
            if not total:
                total_zero = float(0)
                total_duration_list.append(total_zero)
            else:
                total_float_duration = math.ceil(float(total) / 60.0)
                total_duration_list.append(total_float_duration)

        total_minutes = int(sum(total_duration_list))

        return total_minutes

    @classmethod
    # TODO: Review me
    def total_active_phonenumbers(cls, paid):
        from buyercall.blueprints.phonenumbers.models import Phone

        total_phonenumbers = Phone.query.filter(
            and_(Phone.partnership_account_id == paid, Phone.is_deactivated == False)
        ).count()

        return total_phonenumbers

    @classmethod
    # TODO: Review me
    def total_priority_phonenumbers(cls, paid):
        from buyercall.blueprints.phonenumbers.models import Phone

        total_phonenumbers = Phone.query.filter(
            Phone.partnership_account_id == paid
        ).filter(Phone.type == 'priority', Phone.is_deactivated == '0').count()

        return total_phonenumbers

    @classmethod
    # TODO: Review me
    def total_tracking_phonenumbers(cls, paid):
        from buyercall.blueprints.phonenumbers.models import Phone

        total_phonenumbers = Phone.query.filter(
            Phone.partnership_account_id == paid
        ).filter(Phone.type == 'tracking', Phone.is_deactivated == '0').count()

        return total_phonenumbers

    @classmethod
    # TODO: Review me
    def total_mobile_phonenumbers(cls, paid):
        from buyercall.blueprints.phonenumbers.models import Phone

        total_phonenumbers = Phone.query.filter(
            Phone.partnership_account_id == paid
        ).filter(Phone.type == 'mobile', Phone.is_deactivated == '0').count()

        return total_phonenumbers

    @classmethod
    # TODO: Review me
    def total_messages(cls, paid):

        total_messages_count = Message.query.filter(Message.partnership_account_id == paid) \
            .distinct() \
            .count()

        return total_messages_count

    @classmethod
    # TODO: Review me
    def total_inbound_messages(cls, paid):

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'in', Message.direction == 'inbound')) \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def total_inbound_messages_for_phone(cls, paid, pnid):

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'in', Message.direction == 'inbound')) \
            .filter(Message.inbound_id == pnid) \
            .distinct() \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def total_monthly_inbound_messages(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'in', Message.direction == 'inbound')) \
            .filter(extract('year', Message.created_on) == year) \
            .filter(extract('month', Message.created_on) == month) \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def previous_total_monthly_inbound_messages(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        if month == 1:
            year = now.year - 1
            month = 12
        else:
            month = month - 1

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'in', Message.direction == 'inbound')) \
            .filter(extract('year', Message.created_on) == year) \
            .filter(extract('month', Message.created_on) == month) \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def total_outbound_messages(cls, paid):

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'out', Message.direction == 'outbound')) \
            .distinct() \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def total_outbound_messages_for_phone(cls, paid, pnid):

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'out', Message.direction == 'outbound')) \
            .filter(Message.inbound_id == pnid) \
            .distinct() \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def total_monthly_outbound_messages(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'out', Message.direction == 'outbound')) \
            .filter(extract('year', Message.created_on) == year) \
            .filter(extract('month', Message.created_on) == month) \
            .distinct() \
            .count()

        return total_messages

    @classmethod
    # TODO: Review me
    def previous_total_monthly_outbound_messages(cls, paid):
        now = datetime.now()
        year = now.year
        month = now.month

        if month == 1:
            year = now.year - 1
            month = 12
        else:
            month = month - 1

        total_messages = Message.query \
            .filter(Message.partnership_account_id == paid) \
            .filter(or_(Message.direction == 'out', Message.direction == 'outbound')) \
            .filter(extract('year', Message.created_on) == year) \
            .filter(extract('month', Message.created_on) == month) \
            .distinct() \
            .count()

        return total_messages

    @classmethod
    def partnership_account_appointment_availability(cls, partnership_account_id, appointment_type, date, start_time):
        """
        Check if a partnership account is available for a specific date and time

        :param partnership_account_id: Account id
        :param appointment_type: The request appointment type
        :param date: The request appointment date
        :param start_time: The request appointment start time
        :type partnership_account_id: int
        :type appointment_type: string
        :type date: string
        :type start_time: string
        :return: True
        """
        # Retrieve the user record
        account = cls.query.filter(cls.id == partnership_account_id).first()
        format_date = datetime.strptime(date, "%Y-%m-%d")
        date_weekday = (format_date.weekday() + 1) % 7
        start_time = convert_to_deltatime(start_time)
        end_time = start_time + timedelta(minutes=account.appointment_interval)
        format_start_time = datetime.strptime(str(start_time), "%H:%M:%S")
        format_end_time = datetime.strptime(str(end_time), "%H:%M:%S")
        final_start_time = format_start_time.time()
        final_end_time = format_end_time.time()
        requested_appointment = {'date': str(format_date),
                                 'weekday': date_weekday,
                                 'start_time': str(final_start_time)[:-3],
                                 'end_time': str(final_end_time)[:-3]}
        available_slots = cls.partnership_account_available_appointment_slots(account.id, appointment_type, date)
        if requested_appointment in available_slots:
            return True
        else:
            return False

    @classmethod
    def partnership_account_available_appointment_slots(cls, partnership_account_id, appointment_type, date=None):
        """
        Retrieve a list of start and end time appointment slots for a specific date a partnership account

        :param partnership_account_id: Account id
        :param appointment_type: The appointment type in question
        :param date: The date in question
        :type partnership_account_id: int
        :type appointment_type: string
        :type date: string
        :return: list of appointment slot dictionaries for a specific date and partnership account
        """
        # Find the user based on user id provided
        account = cls.query.filter(and_(cls.id == partnership_account_id,
                                        cls.active.is_(True),
                                        cls.appointment_enabled.is_(True))).first()
        # Set general value for appointment_type when not provided
        if appointment_type is None:
            appointment_type = 'general'
        # Set the appointment type variable
        if appointment_type == "service":
            appointment_type_count = account.appointment_service_slot_count
        elif appointment_type == 'general':
            appointment_type_count = account.appointment_general_slot_count
        else:
            appointment_type_count = 0
        # Set or create the date parameters
        date_today = datetime.strptime(str(datetime_date.today()), "%Y-%m-%d")
        account_timezone = account.timezone
        tz = pytz.timezone(account_timezone)
        time_now = datetime.now(tz)
        account_time_now = time_now.strftime('%H:%M:%S')
        if date:
            format_date = datetime.strptime(date, "%Y-%m-%d")
            future_date = str(format_date + relativedelta(months=+2))[:-9]
            format_future_date = datetime.strptime(future_date, "%Y-%m-%d")
        else:
            # If no date is provided use today's date
            format_date = date_today
            future_date = str(format_date + relativedelta(months=+2))[:-9]
            format_future_date = datetime.strptime(future_date, "%Y-%m-%d")
        try:
            if account:
                # Return all the dates between request/current date and the future date allow with its day of the week
                dates_with_weekdays = []
                start_date = format_date
                while start_date <= format_future_date:
                    weekday = (start_date.weekday() + 1) % 7
                    date_and_day = {'date': start_date, 'weekday': weekday}
                    dates_with_weekdays.append(date_and_day)
                    start_date += relativedelta(days=1)
                # Return all user available dates and time availability based on their schedule weekdays
                dates_with_schedules = []
                # Check to verify if the user is using a schedule and use start and end times from schedule
                if account.operating_hours.count() != 0:
                    for date_and_day in dates_with_weekdays:
                        for var_day in account.operating_hours:
                            if var_day.day == date_and_day['weekday'] and var_day.is_active \
                                    and var_day.operating_type == appointment_type:
                                schedule_start_time = var_day.available_from
                                schedule_end_time = var_day.available_to
                                available_days = {'date': date_and_day['date'],
                                                  'weekday': date_and_day['weekday'],
                                                  'schedule_start_time': schedule_start_time,
                                                  'schedule_end_time': schedule_end_time}
                                dates_with_schedules.append(available_days)
                # Else if the user schedule is not set than use all dates with all hours in day
                else:
                    for date_and_day in dates_with_weekdays:
                        schedule_start_time = '00:00:00'
                        schedule_end_time = '23:00:00'
                        available_days = {'date': date_and_day['date'],
                                          'weekday': date_and_day['weekday'],
                                          'schedule_start_time': schedule_start_time,
                                          'schedule_end_time': schedule_end_time}
                        dates_with_schedules.append(available_days)
                # Get current list of appointments for an account between a specific date and 2 months out
                appointments = Appointment.query \
                    .filter(and_(Appointment.partnership_account_id == account.id,
                                 Appointment.is_deactivated.is_(False),
                                 Appointment.appointment_type == appointment_type,
                                 Appointment.appointment_date >= format_date,
                                 Appointment.appointment_date <= format_future_date)).all()
                # Identify time slots for appointments that's already booked
                unavailable_slots = []
                for appointment in appointments:
                    for appointment_slot in appointment.appointment_slot:
                        appointment_time_slot = {'date': str(appointment.appointment_date),
                                                 'weekday': (appointment.appointment_date.weekday() + 1) % 7,
                                                 'start_time': appointment_slot.start_time,
                                                 'end_time': appointment_slot.end_time}
                        # Get the count for an appointment for a specific date and time
                        appointment_count = Appointment.appointment_count(account.id,
                                                                          str(appointment.appointment_date),
                                                                          appointment_slot.start_time,
                                                                          appointment.appointment_type)
                        if appointment_count >= appointment_type_count \
                                and appointment_time_slot not in unavailable_slots:
                            unavailable_slots.append(appointment_time_slot)
                # Return all available time slots for the date range 3 months out ignoring already booked slots
                available_slots = []
                for schedule_date in dates_with_schedules:
                    schedule_start_time = schedule_date['schedule_start_time']
                    while schedule_start_time < schedule_date['schedule_end_time'] \
                            and schedule_start_time < '23:00:00':
                        start_time = convert_to_deltatime(schedule_start_time[:-3])
                        end_time = start_time + timedelta(minutes=account.appointment_interval)
                        format_start_time = datetime.strptime(str(start_time), "%H:%M:%S")
                        format_end_time = datetime.strptime(str(end_time), "%H:%M:%S")
                        final_start_time = format_start_time.time()
                        final_end_time = format_end_time.time()
                        slot = {'date': str(schedule_date['date']),
                                'weekday': schedule_date['weekday'],
                                'start_time': str(final_start_time)[:-3],
                                'end_time': str(final_end_time)[:-3]}
                        if slot not in unavailable_slots:
                            if str(slot['date']) == str(date_today) \
                                    and slot['start_time'] < account_time_now:
                                pass
                            else:
                                available_slots.append(slot)
                        schedule_start_time = str(final_end_time)
                return available_slots
        except Exception:
            log.error('User available appointment slots error message: {}'.format(traceback.format_exc()))

    @hybrid_property
    def api_token_hash(self):
        """
        Returns the REST API token for this partnership account.
        """
        token = db.session.query(ApiToken.api_token_hash) \
            .filter(ApiToken.id == self.api_token_id).first()

        if token is not None:
            return token
        else:
            return None

    @hybrid_property
    def has_active_subscription(self):
        if not self.subscription:
            return False
        return self.subscription.status == 'active'

    @has_active_subscription.expression
    def has_active_subscription(cls):
        if not cls.subscription:
            return False
        return cls.subscription.status == 'active'

    @property
    def created_datetime(self):
        """ Return the date/time this partnership account occurred
        """
        return self.created_on.strftime('%Y-%m-%d %H:%M:%S')

    @property
    def updated_datetime(self):
        """ Return the date/time this partnership account record was updated
        """
        return self.updated_on.strftime('%Y-%m-%d %H:%M:%S')

    @property
    def is_active(self):
        """ Return the status of partnership account
        """
        return self.active

    @classmethod
    def phone_number_usage(cls, paid, pnid):

        try:
            inbound_call_count_result = cls.total_inbound_calls_count_for_phone(paid, pnid)
            outbound_call_count_result = cls.total_outbound_calls_count_for_phone(paid, pnid)
            inbound_message_count_result = cls.total_inbound_messages_for_phone(paid, pnid)
            outbound_message_count_result = cls.total_outbound_messages_for_phone(paid, pnid)

            return jsonify(
                inbound_call_count=inbound_call_count_result,
                outbound_call_count=outbound_call_count_result,
                inbound_message_count=inbound_message_count_result,
                outbound_message_count=outbound_message_count_result
            )

        except Exception as e:
            log.error(traceback.format_exc())

            return ''


<<<<<<< HEAD
class PartnershipAccountOperatingHours(ResourceMixin, db.Model):
    __tablename__ = 'partnership_account_operating_hours'
    id = db.Column(db.Integer, primary_key=True)

    sid = db.Column(UUID(as_uuid=True), unique=True, default=uuid.uuid4, index=True)

    partnership_account_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'partnership_accounts.id',
            onupdate='CASCADE',
            ondelete='CASCADE'),
        index=True, nullable=False
    )

    operating_type = db.Column(db.String(64), nullable=False, server_default='general')

    is_active = db.Column(db.Boolean, nullable=False)

    day = db.Column(db.Integer, index=True, nullable=False)

    available_from = db.Column(
        db.String(8), nullable=False, server_default='08:00:00'
    )

    available_to = db.Column(
        db.String(8), nullable=False, server_default='17:00:00'
    )

    is_all_hours = db.Column(db.Boolean, nullable=True)

    def __init__(self, day, available_from, available_to, is_active, partnership_account_id, operating_type, is_all_hours):
        self.day = day
        self.available_from = available_from
        self.available_to = available_to
        self.is_active = is_active
        self.partnership_account_id = partnership_account_id
        self.operating_type = operating_type
        self.is_all_hours = is_all_hours

    @classmethod
    def create(cls, *days):
        """
        Return whether the partnership account operating hours was created successfully.
        :return: bool
        """
        for day in days:
            db.session.add(PartnershipAccountOperatingHours(**day))
        db.session.commit()

        return True

    @classmethod
    def update(cls, operating_id, available_from, available_to, is_active,
               partnership_account_id, operating_type='general'):
        db.session.query(PartnershipAccountOperatingHours).filter_by(id=operating_id).update(
            {"is_active": is_active,
             "available_from": available_from,
             "available_to": available_to
             })
        db.session.commit()

    @classmethod
    def api_update(cls, operating_id, available_from, available_to, is_active,
                   partnership_account_id):
        try:

            operating_hours = PartnershipAccountOperatingHours.query.filter(
                and_(PartnershipAccountOperatingHours.id == operating_id,
                     PartnershipAccountOperatingHours.partnership_account_id == partnership_account_id
                     )
            ).first()

            if operating_hours is not None:
                if available_from is not None:
                    operating_hours.available_from = available_from

                if available_to is not None:
                    operating_hours.available_to = available_to

                if is_active is not None:
                    operating_hours.is_active = is_active

                db.session.commit()

                return True
            else:
                return False

        except Exception as e:
            log.error('Error updating partnership account operating hours. Error: {}'.format(e.message))
            return False

    @classmethod
    def deactivate(cls, partnership_account_id):
        account_found = db.session.query(PartnershipAccount) \
            .filter(PartnershipAccount.id == partnership_account_id).first()

        if account_found is not None:
            operating_hours = db.session.query(PartnershipAccountOperatingHours) \
                .filter(PartnershipAccountOperatingHours.partnership_account_id == account_found.id).all()

            for operating_hour in operating_hours:
                operating_hour.is_active = False
            db.session.commit()

            return True
        else:
            return False

    @classmethod
    def get_by_account_id(cls, partnership_account_id):
        operating_hours = db.session.query(PartnershipAccountOperatingHours) \
            .filter(PartnershipAccountOperatingHours.partnership_account_id == partnership_account_id).all()
        return operating_hours if operating_hours else []

    @classmethod
    def get_general_by_account(cls, partnership_account_id):
        operating_hours = db.session.query(cls) \
            .filter(cls.partnership_account_id == partnership_account_id, cls.operating_type == 'general').all()
        return operating_hours if operating_hours else []

    @classmethod
    def get_service_by_account(cls, partnership_account_id):
        operating_hours = db.session.query(cls) \
            .filter(cls.partnership_account_id == partnership_account_id, cls.operating_type == 'service').all()
        return operating_hours if operating_hours else []


=======
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
class Partnership(ResourceMixin, db.Model):
    __tablename__ = 'partnerships'

    id = db.Column(db.Integer, primary_key=True)

    sid = db.Column(UUID(as_uuid=True), unique=True, default=uuid.uuid4, index=True)

    name = db.Column(db.String(255), index=True, nullable=False,
                     server_default='')

    active = db.Column('is_active', db.Boolean(), nullable=False,
                       server_default='1')

    account_invitation_url_token = db.Column(
        db.String(255),
        index=True,
        nullable=False,
        server_default=''
    )

    subscription_id = db.Column(db.Integer, db.ForeignKey('subscriptions.id',
                                                          onupdate='CASCADE',
                                                          ondelete='CASCADE'),
                                index=True, nullable=True)

    users = db.relationship(User, backref="partnership")

    partnership_accounts = db.relationship(PartnershipAccount, backref="partnership")

    issues = db.relationship(Issue, backref="partnership")

    invoices = db.relationship(Invoice, backref='partnership')

    logo = db.Column(db.String(2000), index=True, nullable=False,
                     server_default='')

    alternative_logo = db.Column(db.String(512), index=True, nullable=False,
                                 server_default='')

    logo_small = db.Column(db.String(2000), index=True, nullable=False,
                           server_default='')

    alternative_logo_small = db.Column(db.String(512), index=True, nullable=False,
                                       server_default='')

    default_billing_type = db.Column(db.Enum(*BILLING_TYPE, name='billing_types'),
                                     index=True, nullable=False, server_default='partnership')

    default_provider = db.Column(
        db.String(10), nullable=False, server_default='twilio',
        index=True,
    )

    api_token_id = db.Column(
        db.Integer,
        db.ForeignKey(
            'api_tokens.id',
            onupdate='CASCADE',
            ondelete='CASCADE'
        ),
        index=True, nullable=True
    )

    partner_url = db.Column(
        db.String(256), nullable=False, server_default='',
        index=True,
    )

    email_sender = db.Column(
        db.String(64), nullable=False, server_default='',
        index=True,
    )

    partner_type = db.Column(
        db.String(256), nullable=False, server_default='general',
        index=True,
    )

    business_type = db.Column(
        db.String(128), nullable=False, server_default='general',
        index=True
    )

<<<<<<< HEAD
    primary_color = db.Column(
        db.String(10), nullable=False, server_default='#2a3042'
    )

    is_expanded_menu = db.Column('is_expanded_menu', db.Boolean(), nullable=False,
                                 server_default='1')

    dkim_records = db.Column(ARRAY(db.String(128)))

    domain_record = db.Column(
        db.String(128), nullable=False, server_default=''
    )

=======
>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
    operational_number = db.Column(
        db.String(32), nullable=False, server_default='',
        index=False,
    )

    custom_styles = db.Column(
        db.String(128), nullable=False, server_default='',
<<<<<<< HEAD
        index=False
    )

=======
        index=False,
    )

    is_2fa_email_enabled = db.Column(db.Boolean(), nullable=False,
                                     server_default='0')

    is_2fa_sms_enabled = db.Column(db.Boolean(), nullable=False,
                                   server_default='1')

    is_2fa_enforced = db.Column(db.Boolean(), nullable=False,
                                   server_default='0')

>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b
    @classmethod
    def search(cls, query):
        """
        Search a resource by 1 or more fields.

        :param query: Search query
        :type query: str
        :return: SQLAlchemy filter
        """
        if not query:
            return text('')

        search_query = '%{0}%'.format(query)
        search_chain = (Partnership.name.ilike(search_query))

        # return or_(*search_chain)
        return search_chain

    @classmethod
    def get_name(cls, pid):
        obj = cls.query.filter(cls.id == pid).first()
        return obj.name if obj else None

    @classmethod
    def get_by_invitation_token(cls, token=None):
        return cls.query.filter(cls.account_invitation_url_token == token).first() if token else None

    @classmethod
    def get_partnership_by_token(cls, api_token_id):
        """
        Find a Partnership by their REST API token id.
        """
        partnership = db.session.query(Partnership) \
            .filter(and_(Partnership.api_token_id == api_token_id, Partnership.active == True)) \
            .first()

        if partnership is not None:
            return partnership
        else:
            return None

    @classmethod
    def regenerate_api_token(cls, id):
        """
        Regenerates the Partnership REST API token.
        :return: api token
        """
        partnership = db.session.query(Partnership) \
            .filter(Partnership.id == id) \
            .first()

        if partnership is not None:

            # Check if api token id already exists, if so, just regenerate
            if partnership.api_token_id is not None and partnership.api_token_id > 0:
                return ApiToken.regenerate_token(partnership.api_token_id)
            # API token id does not exist, need to create a new one and regenerate the key
            else:
                new_token = ApiToken.create()

                if new_token is not None and new_token > 0:
                    partnership.api_token_id = new_token
                    partnership.save()
                    db.session.commit()

                    return ApiToken.regenerate_token(new_token)
                else:
                    return None
        else:
            # No partnership found
            return None

    @classmethod
    def get_partnership_accounts(cls):
        """
        Find and return a list of all the partnership accounts linked to this partnership.
        """
        partnership_accounts = (db.session.query(PartnershipAccount)) \
            .filter(and_(PartnershipAccount.partnership_id == cls.id,
                         PartnershipAccount.active == True)) \
            .order_by(PartnershipAccount.name) \
            .all()

        return partnership_accounts

    @classmethod
    def get_partnership_account(cls, paid):
        """
        Find and return a partnership accounts linked to this partnership based on partnership account id.
        """
        partnership_account = (db.session.query(PartnershipAccount)) \
            .filter(and_(PartnershipAccount.partnership_id == cls.id,
                         PartnershipAccount.active == True,
                         PartnershipAccount.id == paid)).first()

        return partnership_account

    @hybrid_property
    def api_token_hash(self):
        """
        Returns the REST API token for this partnership account.
        """
        token = db.session.query(ApiToken.api_token_hash) \
            .filter(ApiToken.id == self.api_token_id) \
            .first()

        if token is not None:
            return token
        else:
            return None

    @hybrid_property
    def has_active_subscription(self):
        if not self.subscription:
            return False
        return self.subscription.status == 'active'

    @hybrid_property
    def billing_type(self):
        return self.default_billing_type

    @classmethod
    def update_business_type(cls, partnership_id, business_type):
        """
        Updates the busines type of all related partnership accounts.
        """
        if partnership_id and business_type:
            update_stmt = update(PartnershipAccount) \
                .where(PartnershipAccount.partnership_id == partnership_id) \
                .values(business_type=business_type)
            db.session.execute(update_stmt)
            db.session.commit()

    @classmethod
    def close_account(cls, pid):
        """
        Deactivates the account and it's accounts and users
        """
        try:
            partnership = cls.query.filter(cls.sid == pid).first()
            partnership.active = False
            partnership.save()

            users = partnership.users
            for u in users:
                u.active = False
                u.is_deactivated = True
                u.deactivated_on = datetime.now()
                u.save()

            partnership_accounts = partnership.partnership_accounts
            for pa in partnership_accounts:
                pa.active = False
                pa.save()

            return True
        except Exception as e:
            print(e)
            return False

    @classmethod
    def get_by_partner_url(cls, purl):
        return cls.query.filter(cls.partner_url == purl).first()

    @classmethod
    def get_by_email(cls, email):
        return cls.query.filter(cls.email_sender == email).first()


class PartnershipCpaasProviders(ResourceMixin, db.Model):
    __tablename__ = 'partnership_cpaas_providers'

    id = db.Column(db.Integer, primary_key=True)

    cpaas_provider_name = db.Column(db.String(32), index=True, nullable=False, server_default='')

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')

    partnership_id = db.Column(db.Integer, db.ForeignKey('partnerships.id',
                                                         onupdate='CASCADE',
                                                         ondelete='CASCADE'), index=True, nullable=False)

    cpaas_default_provider = db.Column('is_default_provider', db.Boolean(), nullable=False, server_default='0')

    cpaas_account_id = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_api_token = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_api_secret = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_user_id = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_api_username = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_api_password = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_site_id = db.Column(db.String(128), index=True, nullable=False, server_default='')

    cpaas_location_id = db.Column(db.String(64), index=True, nullable=False, server_default='')

    cpaas_sms_application_id = db.Column(db.String(64), index=True, nullable=False, server_default='')

    cpaas_voice_application_id = db.Column(db.String(64), index=True, nullable=False, server_default='')

    cpaas_caller_id = db.Column(db.String(12), index=True, nullable=False, server_default='')

    cpaas_cnam_password = db.Column(db.String(64), index=True, nullable=False, server_default='')

    @classmethod
    def partnership_twilio_credentials(cls, paid):
        """
        Find and return the partnership CPaaS twilio provider otherwise return default provider.
        """
        twilio_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_id == paid, cls.cpaas_provider_name == 'twilio', cls.active.is_(True)).first()

        if twilio_as_provider is not None:
            return twilio_as_provider

        else:
            default_twilio_as_provider = (db.session.query(cls)) \
                .filter(cls.cpaas_default_provider.is_(True), cls.cpaas_provider_name == 'twilio',
                        cls.active.is_(True)).first()
            return default_twilio_as_provider

    @classmethod
    def partnership_bandwidth_credentials(cls, paid):
        """
        Find and return the partnership CPaaS bandwidth provider otherwise return default provider.
        """
        twilio_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_id == paid, cls.cpaas_provider_name == 'bandwidth', cls.active.is_(True)).first()

        if twilio_as_provider is not None:
            return twilio_as_provider

        else:
            default_twilio_as_provider = (db.session.query(cls)) \
                .filter(cls.cpaas_default_provider.is_(True), cls.cpaas_provider_name == 'bandwidth',
                        cls.active.is_(True)).first()
            return default_twilio_as_provider


class PartnershipCpaasPhoneNumberSubscriptions(ResourceMixin, db.Model):
    __tablename__ = 'partnership_cpaas_phonenumber_subscriptions'

    id = db.Column(db.Integer, primary_key=True)

    cpaas_provider_name = db.Column(db.String(32), index=True, nullable=False, server_default='')

    partnership_id = db.Column(db.Integer, db.ForeignKey('partnerships.id',
                                                         onupdate='CASCADE',
                                                         ondelete='CASCADE'), index=True, nullable=False)

    order_type = db.Column(db.String(32), index=True, nullable=False, server_default='')

    subscription_id = db.Column(db.String(128), index=True, nullable=False, server_default='')

    callback_url = db.Column(db.String(128), index=True, nullable=False, server_default='')

    @classmethod
    def partnership_bandwidth_phonenumber_subscription(cls, paid):
        """
        Find and return the subscription for bandwidth phone number order
        """
        partnership_id = paid

        return partnership_id


class PartnershipCreditTie(ResourceMixin, db.Model):
    __tablename__ = 'partnership_credit_tie'

    id = db.Column(db.Integer, primary_key=True)

    service_provider = db.Column(db.String(128), index=True, nullable=False, server_default='')

    active = db.Column('is_active', db.Boolean(), nullable=False, server_default='1')

    partnership_id = db.Column(db.Integer, db.ForeignKey('partnerships.id',
                                                         onupdate='CASCADE',
                                                         ondelete='CASCADE'), index=True, nullable=False)

    product_type = db.Column(db.String(128), index=True, nullable=False, server_default='')

    api_account = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_username = db.Column(db.String(256), index=True, nullable=False, server_default='')

    api_password = db.Column(db.String(256), index=True, nullable=False, server_default='')

    experian_enabled = db.Column('experian_enabled', db.Boolean(), nullable=False, server_default='0')

    transunion_enabled = db.Column('transunion_enabled', db.Boolean(), nullable=False, server_default='0')

    equifax_enabled = db.Column('equifax_enabled', db.Boolean(), nullable=False, server_default='0')

    @classmethod
    def partner_finserv_credit_info(cls, pid, product_type):
        """
        Find and return the partnership account's Finserv credit credentials.
        """
        finserv_credit_as_provider = (db.session.query(cls)) \
            .filter(cls.partnership_id == pid,
                    cls.service_provider == 'Finserv',
                    cls.product_type == product_type,
                    cls.active.is_(True)).first()

        if finserv_credit_as_provider is not None:
            return finserv_credit_as_provider

        else:
            return None


class PartnershipAccountCounts(ResourceMixin, db.Model):
<<<<<<< HEAD

    __tablename__ = 'partnership_account_counts'

    partnership_account_id = db.Column(db.Integer, db.ForeignKey('partnership_accounts.id'),
                                       primary_key=True, index=True, nullable=False)

    contact_count = db.Column(db.Integer, nullable=True)

    call_count = db.Column(db.Integer, nullable=True)

    form_lead_count = db.Column(db.Integer, nullable=True)

    message_count = db.Column(db.Integer, nullable=True)
=======
    __tablename__ = 'partnership_account_counts'

    partnership_account_id = db.Column(db.Integer, db.ForeignKey('partnership_accounts.id'),
                                       primary_key=True, index=True, nullable=False)

    contact_count = db.Column(db.Integer, nullable=True)

    call_count = db.Column(db.Integer, nullable=True)

    form_lead_count = db.Column(db.Integer, nullable=True)

    message_count = db.Column(db.Integer, nullable=True)

>>>>>>> 2d4ba41568adf348959bbf7b64ffa061df87d07b