예제 #1
0
def test_fetch_exchange_rate(mocker, test_client, init_database,
                             initialised_blockchain_network, user_type,
                             preferred_language, exchange_text, limit_text):
    user = UserFactory(preferred_language=preferred_language, phone=phone())
    if user_type == "group":
        user.set_held_role('GROUP_ACCOUNT', 'grassroots_group_account')
        user.is_phone_verified = True
        kyc = KycApplication(type='INDIVIDUAL')
        kyc.user = user
        kyc.kyc_status = 'VERIFIED'

    token1 = Token.query.filter_by(symbol="SM1").first()
    create_transfer_account_for_user(user, token1, 20000)

    def mock_convert(exchange_contract, from_token, to_token, from_amount):
        return from_amount * 1.2

    mocker.patch('server.bt.get_conversion_amount', mock_convert)

    def mock_send_message(phone, message):
        assert exchange_text in message
        assert limit_text in message

    mocker.patch('server.utils.phone.send_message', mock_send_message)
    TokenProcessor.fetch_exchange_rate(user)
def test_get_vendor_payout_with_withdrawal_limit(test_client,
                                                 authed_sempo_admin_user,
                                                 create_transfer_account_user):
    authed_sempo_admin_user.organisations[
        0].minimum_vendor_payout_withdrawal = 200
    auth = get_complete_auth_token(authed_sempo_admin_user)
    user = create_transfer_account_user
    user.transfer_account.is_vendor = True
    user.set_held_role('VENDOR', 'supervendor')
    user.transfer_account.approve_and_disburse()
    user.transfer_account.organisation = authed_sempo_admin_user.organisations[
        0]
    user.transfer_account.set_balance_offset(100)
    user.is_phone_verified = True
    kyc = KycApplication(type='INDIVIDUAL')
    user.kyc_applications = [kyc]
    user.kyc_applications[0].kyc_status = 'VERIFIED'
    db.session.commit()

    response = test_client.post(f"/api/v1/get_vendor_payout/",
                                headers=dict(
                                    Authorization=auth,
                                    Accept='application/json',
                                ),
                                data=json.dumps(dict({})))
    assert len(user.transfer_account.credit_sends) == 0
예제 #3
0
def test_send_balance_sms(mocker, test_client, init_database,
                          initialised_blockchain_network, user_type, limit,
                          preferred_language, sample_text):
    user = UserFactory(preferred_language=preferred_language, phone=phone())
    if user_type == "group":
        user.set_held_role('GROUP_ACCOUNT', 'grassroots_group_account')
        user.is_phone_verified = True
        kyc = KycApplication(type='INDIVIDUAL')
        kyc.user = user
        kyc.kyc_status = 'VERIFIED'

    token1 = Token.query.filter_by(symbol="SM1").first()
    token2 = Token.query.filter_by(symbol="SM2").first()
    token3 = Token.query.filter_by(symbol="SM3").first()
    create_transfer_account_for_user(user, token1, 20000)
    create_transfer_account_for_user(user, token2, 35000, is_default=False)
    # this one should not show up in balance
    create_transfer_account_for_user(user,
                                     token3,
                                     0,
                                     is_default=False,
                                     is_ghost=True)

    def mock_convert(exchange_contract, from_token, to_token, from_amount):
        if from_token.symbol == "SM1":
            return from_amount * 1.4124333344353534
        else:
            return from_amount * 0.8398339289133232

    mocker.patch('server.bt.get_conversion_amount', mock_convert)

    def mock_send_message(phone, message):
        assert sample_text in message
        assert "SM1 200" in message
        assert "SM2 350" in message
        assert "SM3" not in message
        if limit:
            assert "{:.2f} SM1 (1 SM1 = 1.41 AUD)".format(limit *
                                                          200) in message
            assert "{:.2f} SM2 (1 SM2 = 0.84 AUD)".format(limit *
                                                          350) in message

    mocker.patch('server.utils.phone.send_message', mock_send_message)
    TokenProcessor.send_balance_sms(user)
예제 #4
0
def test_get_vendor_payout(test_client, authed_sempo_admin_user,
                           create_transfer_account_user):
    auth = get_complete_auth_token(authed_sempo_admin_user)
    user = create_transfer_account_user
    user.transfer_account.is_vendor = True
    user.set_held_role('VENDOR', 'supervendor')
    user.transfer_account.approve_and_disburse()
    user.transfer_account.organisation = authed_sempo_admin_user.organisations[
        0]
    user.transfer_account.set_balance_offset(1000)
    user.is_phone_verified = True
    kyc = KycApplication(type='INDIVIDUAL')
    user.kyc_applications = [kyc]
    user.kyc_applications[0].kyc_status = 'VERIFIED'
    db.session.commit()

    response = test_client.post(f"/api/v1/get_vendor_payout/",
                                headers=dict(
                                    Authorization=auth,
                                    Accept='application/json',
                                ),
                                data=json.dumps(dict({})))
    resp = response.data.decode('ascii')

    f = StringIO(resp)
    reader = csv.reader(f)
    formatted_results = list(reader)
    formatted_results[1][9] = 'SOME DATE'
    formatted_results[1][10] = 'SOME DATE'
    assert formatted_results == [[
        'Vendor Account ID', 'Phone', 'ContactName', 'Current Balance',
        'Total Sent', 'Total Received', 'Approved', 'Beneficiary', 'Vendor',
        'InvoiceDate', 'DueDate', 'Transfer ID', 'UnitAmount',
        'Payment Has Been Made', 'Bank Payment Date'
    ],
                                 [
                                     '4', user.phone, 'Transfer User', '0',
                                     '10', '0', 'False', 'False', 'True',
                                     'SOME DATE', 'SOME DATE', '1', '10', '',
                                     ''
                                 ]]
    transfer = user.transfer_account.credit_sends[0]
    assert transfer.transfer_type == TransferTypeEnum.WITHDRAWAL
    assert transfer.sender_transfer_account == user.transfer_account
    assert transfer.recipient_transfer_account == user.transfer_account.token.float_account
    assert transfer.transfer_status == TransferStatusEnum.PENDING
예제 #5
0
def test_new_credit_transfer_check_sender_transfer_limits(new_credit_transfer):
    """
    GIVEN a CreditTransfer model
    WHEN a new credit transfer is created
    THEN check the correct check_sender_transfer_limits apply
    """
    from server.utils.transfer_limits import LIMIT_IMPLEMENTATIONS
    from server.models.kyc_application import KycApplication
    from server.models import token

    # Sempo Level 0 LIMITS (payment only)
    assert new_credit_transfer.check_sender_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'Sempo Level 0: P' in limit.name
    ]

    # Check Sempo Level 1 LIMITS (payment only)
    new_credit_transfer.sender_user.is_phone_verified = True
    assert new_credit_transfer.check_sender_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'Sempo Level 1: P' in limit.name
    ]

    # Check Sempo Level 2 LIMITS (payment only)
    kyc = KycApplication(type='INDIVIDUAL')
    kyc.user = new_credit_transfer.sender_user
    kyc.kyc_status = 'VERIFIED'
    assert new_credit_transfer.check_sender_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'Sempo Level 2: P' in limit.name
    ]

    # Check Sempo Level 3 LIMITS for business (payment only)
    kyc.type = 'BUSINESS'
    assert new_credit_transfer.check_sender_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'Sempo Level 3: P' in limit.name
    ]

    # Check Sempo Level 3 LIMITS for multiple verified (payment only)
    kyc.type = 'INDIVIDUAL'
    kyc.multiple_documents_verified = True
    assert new_credit_transfer.check_sender_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'Sempo Level 3: P' in limit.name
    ]

    # Check additional GE LIMITS for Liquid Token (withdrawal)
    new_credit_transfer.token.token_type = token.TokenType.LIQUID
    new_credit_transfer.transfer_type = TransferTypeEnum.WITHDRAWAL
    new_credit_transfer.sender_transfer_account.balance = 10000
    assert new_credit_transfer.get_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'GE Liquid Token - Standard User' == limit.name
        or 'Sempo Level 3: WD' in limit.name
    ]

    # Check additional GE LIMITS for Liquid Token (payment, Agent Out Subtype)
    new_credit_transfer.token.token_type = token.TokenType.LIQUID
    new_credit_transfer.transfer_type = TransferTypeEnum.PAYMENT
    new_credit_transfer.transfer_subtype = TransferSubTypeEnum.AGENT_OUT
    new_credit_transfer.sender_transfer_account.balance = 10000
    assert new_credit_transfer.get_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'GE Liquid Token - Standard User' in limit.name
        or 'Sempo Level 3: P' in limit.name
    ]

    # Check additional GE LIMITS for Liquid Token and Group Account (payment, Agent Out Subtype)
    new_credit_transfer.sender_user.set_held_role('GROUP_ACCOUNT',
                                                  'grassroots_group_account')
    new_credit_transfer.token.token_type = token.TokenType.LIQUID
    new_credit_transfer.transfer_type = TransferTypeEnum.PAYMENT
    new_credit_transfer.transfer_subtype = TransferSubTypeEnum.AGENT_OUT
    new_credit_transfer.sender_transfer_account.balance = 10000
    assert new_credit_transfer.get_transfer_limits() == [
        limit for limit in LIMIT_IMPLEMENTATIONS
        if 'GE Liquid Token - Group Account User' in limit.name
        or 'Sempo Level 3: P' in limit.name
    ]

    new_credit_transfer.exclude_from_limit_calcs = True
    def post(self, kyc_application_id):
        post_data = request.get_json()

        is_mobile = post_data.get('is_mobile')
        user_id = post_data.get(
            'user_id'
        )  # should only be defined when an admin is adding user KYC data (not their own)

        type = post_data.get('account_type', 'BUSINESS').upper()
        first_name = post_data.get('first_name')
        last_name = post_data.get('last_name')
        phone = post_data.get('phone')
        business_legal_name = post_data.get('business_legal_name')
        business_type = post_data.get('business_type')
        tax_id = post_data.get('tax_id')
        website = post_data.get('website')
        date_established = post_data.get('date_established')
        country = post_data.get('country')
        street_address = post_data.get('street_address')
        street_address_2 = post_data.get('street_address_2')
        city = post_data.get('city')
        region = post_data.get('region')
        postal_code = post_data.get('postal_code')
        beneficial_owners = post_data.get('beneficial_owners')

        document_type = post_data.get('document_type')
        document_country = post_data.get('document_country')
        document_front_base64 = post_data.get('document_front_base64')  # image
        document_back_base64 = post_data.get('document_back_base64')  # image
        selfie_base64 = post_data.get('selfie_base64')  # image

        if is_mobile or user_id:

            # creation logic is handled after kyc object creation.
            kyc_details = KycApplication.query.filter_by(
                user_id=user_id or g.user.id).first()
            if kyc_details is not None:
                return make_response(
                    jsonify({'message': 'KYC details already exist'})), 400

            if not user_id and (document_type is None
                                or document_country is None
                                or document_front_base64 is None
                                or selfie_base64 is None):
                return make_response(
                    jsonify({'message':
                             'Must provide correct parameters'})), 400

        if not is_mobile and type == 'BUSINESS':

            if user_id and AccessControl.has_suffient_role(
                    g.user.roles, {'ADMIN': 'subadmin'}) is not True:
                return make_response(
                    jsonify({
                        'message':
                        'Must be superadmin to create any KYC profile'
                    })), 401

            elif AccessControl.has_suffient_role(
                    g.user.roles, {'ADMIN': 'superadmin'}) is not True:
                return make_response(
                    jsonify({
                        'message':
                        'Must be superadmin to create org business KYC profile'
                    })), 401

            # check for existing business based on Legal Name and Tax ID.
            business_details = KycApplication.query.filter_by(
                business_legal_name=business_legal_name,
                tax_id=tax_id).first()

            if business_details is not None:
                response_object = {
                    'message':
                    'Business Verification profile already exists for business name: {} and tax ID: {}'
                    .format(business_legal_name, tax_id)
                }

                return make_response(jsonify(response_object)), 400

        if not is_mobile and (business_legal_name is None
                              and tax_id is None) and not user_id:
            # not mobile, not a org profile, thus user_id cannot be None
            return make_response(
                jsonify({
                    'message':
                    'Must provide a user id to create a user KYC profile'
                })), 400

        if beneficial_owners is not None:
            # filter empty beneficial owners
            beneficial_owners = [
                owner for owner in beneficial_owners
                if (owner['full_name'].strip(' ', ) != '')
            ]

        create_kyc_application = KycApplication(
            type=type,
            user=g.user,
            first_name=first_name or g.user.first_name,
            last_name=last_name or g.user.last_name,
            phone=phone or g.user.phone,
            business_legal_name=business_legal_name,
            business_type=business_type,
            tax_id=tax_id,
            website=website,
            date_established=date_established,
            country=country or document_country,
            street_address=street_address,
            street_address_2=street_address_2,
            city=city,
            region=region,
            postal_code=postal_code,
            beneficial_owners=beneficial_owners,
        )

        if user_id:
            user = User.query.get(user_id)
            create_kyc_application.user = user or g.user

        if not is_mobile:
            # not mobile
            if not user_id and type == 'BUSINESS':
                # not a admin applying for another user
                # ngo organisation
                create_kyc_application.organisation = g.active_organisation
            else:
                # admin applying for another user (individual or business)
                create_kyc_application.kyc_status = 'INCOMPLETE'

        db.session.add(create_kyc_application)
        db.session.flush()  # need this to create an ID

        if is_mobile:
            # handle document upload to s3
            handle_kyc_documents(data=post_data,
                                 document_country=document_country,
                                 document_type=document_type,
                                 kyc_details=create_kyc_application)

            # Post verification message to slack
            post_verification_message(user=g.user)

        response_object = {
            'message': 'KYC Application created',
            'data': {
                'kyc_application':
                kyc_application_state_schema.dump(create_kyc_application).data
            }
        }

        return make_response(jsonify(response_object)), 201