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
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)
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
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