Beispiel #1
0
def _create_investment_agreement(amount, currency, token, token_count_float,
                                 username, version, app_user, **kwargs):
    tff_profile = get_tff_profile(username)
    if tff_profile.kyc.status != KYCStatus.VERIFIED:
        raise HttpBadRequestException('cannot_invest_not_kyc_verified')
    applicant = get_applicant(tff_profile.kyc.applicant_id)
    name = '%s %s ' % (applicant.first_name, applicant.last_name)
    address = '%s %s' % (applicant.addresses[0].street,
                         applicant.addresses[0].building_number)
    address += '\n%s %s' % (applicant.addresses[0].postcode,
                            applicant.addresses[0].town)
    country = filter(lambda c: c['value'] == applicant.addresses[0].country,
                     country_choices)[0]['label']
    address += '\n%s' % country
    precision = 2
    reference = user_code(username)
    agreement = InvestmentAgreement(creation_time=now(),
                                    app_user=app_user,
                                    token=token,
                                    amount=amount,
                                    token_count=long(token_count_float *
                                                     pow(10, precision)),
                                    token_precision=precision,
                                    currency=currency,
                                    name=name,
                                    address=address,
                                    version=version,
                                    reference=reference,
                                    **kwargs)
    return agreement
Beispiel #2
0
def put_investment_agreement(agreement_id, agreement, admin_user):
    admin_app_user = create_app_user_by_email(
        admin_user.email(),
        get_config(NAMESPACE).rogerthat.app_id)
    # type: (long, InvestmentAgreement, users.User) -> InvestmentAgreement
    agreement_model = InvestmentAgreement.get_by_id(
        agreement_id)  # type: InvestmentAgreement
    if not agreement_model:
        raise HttpNotFoundException('investment_agreement_not_found')
    if agreement_model.status == InvestmentAgreement.STATUS_CANCELED:
        raise HttpBadRequestException('order_canceled')
    if agreement.status not in (InvestmentAgreement.STATUS_SIGNED,
                                InvestmentAgreement.STATUS_CANCELED):
        raise HttpBadRequestException('invalid_status')
    # Only support updating the status for now
    agreement_model.status = agreement.status
    if agreement_model.status == InvestmentAgreement.STATUS_CANCELED:
        agreement_model.cancel_time = now()
    elif agreement_model.status == InvestmentAgreement.STATUS_SIGNED:
        agreement_model.paid_time = now()
        if agreement.currency == 'BTC':
            _set_token_count(agreement_model, agreement.token_count_float)
        deferred.defer(_send_ito_agreement_to_admin, agreement_model.key,
                       admin_app_user)
    agreement_model.put()
    return agreement_model
Beispiel #3
0
def invest_complete(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key, tag,
                    result_key, flush_id, flush_message_flow_id, service_identity, user_details, flow_params):
    email = user_details[0].email
    app_id = user_details[0].app_id
    if 'confirm' in end_id:
        agreement_key = InvestmentAgreement.create_key(json.loads(tag)['investment_id'])
        deferred.defer(_invest, agreement_key, email, app_id, 0)
Beispiel #4
0
def create_investment_agreement(agreement):
    # type: (CreateInvestmentAgreementTO) -> InvestmentAgreement
    app_user = users.User(agreement.app_user)
    username = get_iyo_username(app_user)
    token_count_float = get_token_count(agreement.currency, agreement.amount)
    agreement_model = _create_investment_agreement(
        agreement.amount,
        agreement.currency,
        agreement.token,
        token_count_float,
        username,
        'manually_created',
        app_user,
        status=agreement.status,
        paid_time=agreement.paid_time,
        sign_time=agreement.sign_time)
    prefix, doc_content_base64 = agreement.document.split(',')
    content_type = prefix.split(';')[0].replace('data:', '')
    doc_content = base64.b64decode(doc_content_base64)
    agreement_model.put()
    pdf_name = InvestmentAgreement.filename(agreement_model.id)
    pdf_url = upload_to_gcs(pdf_name, doc_content, content_type)
    deferred.defer(_create_investment_agreement_iyo_see_doc,
                   agreement_model.key,
                   app_user,
                   pdf_url,
                   content_type,
                   send_sign_message=False)
    return agreement_model
Beispiel #5
0
 def trans():
     agreement = InvestmentAgreement.create_key(
         tag_dict['agreement_id']).get()  # type: InvestmentAgreement
     if answer_id != FormTO.POSITIVE:
         logging.info('Investment agreement sign aborted')
         return
     if agreement.status == InvestmentAgreement.STATUS_PAID:
         logging.warn(
             'Ignoring request to set InvestmentAgreement %s as paid because it is already paid',
             agreement.id)
         return
     agreement.status = InvestmentAgreement.STATUS_PAID
     agreement.paid_time = now()
     agreement.put()
     user_email, app_id, = get_app_user_tuple(agreement.app_user)
     deferred.defer(transfer_genesis_coins_to_user,
                    agreement.app_user,
                    TokenType.I,
                    long(agreement.token_count_float * 100),
                    _transactional=True)
     deferred.defer(update_investor_progress,
                    user_email.email(),
                    app_id,
                    INVESTMENT_TODO_MAPPING[agreement.status],
                    _transactional=True)
     deferred.defer(_send_tokens_assigned_message,
                    agreement.app_user,
                    _transactional=True)
Beispiel #6
0
def _move_investment_agreement_pdf_to_gcs(investment_agreement_key):
    investment_agreement = get_investment_agreement_details(
        investment_agreement_key.id())
    if not investment_agreement.see_document:
        return
    original_url = investment_agreement.see_document.versions[0].link
    _move_pdf(InvestmentAgreement.filename(investment_agreement.id),
              original_url)
Beispiel #7
0
def invest_complete(status, answer_id, received_timestamp, member, message_key,
                    tag, acked_timestamp, parent_message_key, service_identity,
                    user_details):
    email = user_details[0].email
    app_id = user_details[0].app_id
    if answer_id == u'confirm':
        agreement_key = InvestmentAgreement.create_key(
            json.loads(tag)['investment_id'])
        deferred.defer(_invest, agreement_key, email, app_id, 0)
Beispiel #8
0
def get_total_investment_value(app_user):
    statuses = (InvestmentAgreement.STATUS_PAID, InvestmentAgreement.STATUS_SIGNED)
    total_token_count = get_total_token_count(app_user, InvestmentAgreement.list_by_status_and_user(app_user, statuses))

    tokens = total_token_count.keys()
    stats = dict(zip(tokens, ndb.get_multi([GlobalStats.create_key(token) for token in tokens])))
    total_usd = 0
    for token, token_count in total_token_count.iteritems():
        total_usd += token_count * stats[token].value
    logging.debug('The tokens of %s are worth $%s', app_user, total_usd)
    return total_usd
Beispiel #9
0
def migrate(dry_run=False):
    investments = InvestmentAgreement.query().fetch(1000)  # type: list[InvestmentAgreement]
    updates = {}
    for investment in investments:
        new_status = INVESTMENT_TODO_MAPPING[investment.status]
        if investment.app_user not in updates or updates[investment.app_user] < new_status:
            updates[investment.app_user] = INVESTMENT_TODO_MAPPING[investment.status]
    if dry_run:
        return updates
    for app_user, step in updates.iteritems():
        email, app_id = get_app_user_tuple(app_user)
        deferred.defer(update_investor_progress, email.email(), app_id, step)
Beispiel #10
0
def investment_agreement_signed(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key,
                                tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details,
                                flow_params):
    try:
        user_detail = user_details[0]
        tag_dict = json.loads(tag)
        agreement = InvestmentAgreement.create_key(tag_dict['agreement_id']).get()  # type: InvestmentAgreement

        last_step = steps[-1]
        assert isinstance(last_step, FormFlowStepTO)
        if last_step.answer_id != FormTO.POSITIVE:
            logging.info('Investment agreement was canceled')
            agreement.status = InvestmentAgreement.STATUS_CANCELED
            agreement.cancel_time = now()
            agreement.put()
            return None

        logging.info('Received signature for Investment Agreement')

        sign_result = last_step.form_result.result.get_value()
        assert isinstance(sign_result, SignWidgetResultTO)
        iyo_username = get_iyo_username(user_detail)
        sign_see_document(iyo_username, agreement.iyo_see_id, sign_result, user_detail)

        logging.debug('Storing signature in DB')
        agreement.populate(status=InvestmentAgreement.STATUS_SIGNED,
                           signature=sign_result.payload_signature,
                           sign_time=now())
        agreement.put_async()

        deferred.defer(add_user_to_role, user_detail, RogerthatRoles.INVESTOR)
        intercom_tags = get_intercom_tags_for_investment(agreement)
        if intercom_tags:
            for i_tag in intercom_tags:
                deferred.defer(intercom_helpers.tag_intercom_users, i_tag, [iyo_username])
        deferred.defer(update_investor_progress, user_detail.email, user_detail.app_id,
                       INVESTMENT_TODO_MAPPING[agreement.status])
        deferred.defer(_inform_support_of_new_investment, iyo_username, agreement.id, agreement.token_count_float)
        if needs_utility_bill(agreement):
            logging.debug('Sending "utility bill received" message')
            deferred.defer(_send_utility_bill_received, agreement.app_user)
        else:
            logging.debug('Sending confirmation message')
            deferred.defer(send_payment_instructions, agreement.app_user, agreement.id, '')
            deferred.defer(send_hoster_reminder, agreement.app_user, _countdown=1)
        result = FlowCallbackResultTypeTO(flow=FLOW_INVESTMENT_CONFIRMED,
                                          tag=None,
                                          force_language=None,
                                          flow_params=json.dumps({'reference': agreement.reference}))
        return FlowMemberResultCallbackResultTO(type=TYPE_FLOW, value=result)
    except:
        logging.exception('An unexpected error occurred')
        return create_error_message()
Beispiel #11
0
def fix_investments():
    investments = InvestmentAgreement.query().filter(
        InvestmentAgreement.status > InvestmentAgreement.STATUS_CANCELED)
    to_put = []
    to_fix = []
    for agreement in investments:  # type: InvestmentAgreement
        if not agreement.token_count:
            if not agreement.iyo_see_id:
                _set_token_count(agreement)
                to_put.append(agreement)
            elif agreement.currency != 'BTC':
                to_fix.append(agreement.id)
    ndb.put_multi(to_put)
    logging.warn('These investment agreements need manual migration: %s',
                 to_fix)
Beispiel #12
0
def manual_fix():
    to_fix = [5667908084563968]
    mapping = {5667908084563968: 0.85}  # Incomplete
    to_put = []
    agreements = ndb.get_multi(
        [InvestmentAgreement.create_key(id) for id in to_fix])
    for agreement in agreements:  # type: InvestmentAgreement
        if agreement.currency == 'USD':
            conversion = 1
        else:
            conversion = mapping[agreement.id]
        token_count_float = agreement.amount / (conversion * 5)
        precision = 8 if agreement.currency == 'BTC' else 2
        agreement.token_count = long(token_count_float * pow(10, precision))
        agreement.token_precision = precision
        to_put.append(agreement)
    ndb.put_multi(to_put)
def search_investment_agreements(query=None, page_size=20, cursor=None):
    # type: (unicode, int, unicode) -> tuple[list[InvestmentAgreement], search.Cursor, bool]
    options = search.QueryOptions(
        limit=page_size,
        cursor=search.Cursor(cursor),
        ids_only=True,
        sort_options=search.SortOptions(expressions=[
            SortExpression(expression='creation_time',
                           direction=SortExpression.DESCENDING)
        ]))
    search_results = INVESTMENT_INDEX.search(
        search.Query(query, options=options))  # type: search.SearchResults
    results = search_results.results  # type: list[search.ScoredDocument]
    investment_agreements = ndb.get_multi([
        InvestmentAgreement.create_key(long(result.doc_id))
        for result in results
    ])
    return investment_agreements, search_results.cursor, search_results.cursor is not None
Beispiel #14
0
def token_value_addendum_signed(message_flow_run_id, member, steps, end_id, end_message_flow_id, parent_message_key,
                                tag, result_key, flush_id, flush_message_flow_id, service_identity, user_details,
                                flow_params):
    parsed_tag = json.loads(tag)
    document_key = Document.create_key(parsed_tag['document_id'])
    last_step = steps[-1]
    assert isinstance(last_step, FormFlowStepTO)
    if last_step.answer_id != FormTO.POSITIVE:
        logging.error('User pressed cancel in the %s flow', FLOW_SIGN_TOKEN_VALUE_ADDENDUM)
        return None

    logging.info('Received signature for Document')

    sign_result = last_step.form_result.result.get_value()
    assert isinstance(sign_result, SignWidgetResultTO)
    user_detail = user_details[0]
    app_user = create_app_user_by_email(user_detail.email, user_detail.app_id)
    investment_keys = InvestmentAgreement.list_by_user(app_user).fetch(keys_only=True)
    multiply_agreements_tokens(document_key, sign_result, user_details[0], investment_keys)
Beispiel #15
0
def _invest(agreement_key, email, app_id, retry_count):
    # type: (ndb.Key, unicode, unicode, long, list[int]) -> None
    from plugins.tff_backend.bizz.agreements import create_token_agreement_pdf
    app_user = create_app_user_by_email(email, app_id)
    logging.debug('Creating Token agreement')
    agreement = get_investment_agreement(agreement_key.id())
    _set_token_count(agreement)
    agreement.put()
    currency_full = _get_currency_name(agreement.currency)
    pdf_name = InvestmentAgreement.filename(agreement_key.id())
    username = get_iyo_username(app_user)
    has_verified_utility_bill = get_tff_profile(username).kyc.utility_bill_verified
    pdf_contents = create_token_agreement_pdf(agreement.name, agreement.address, agreement.amount, currency_full,
                                              agreement.currency, agreement.token, agreement.payment_info,
                                              has_verified_utility_bill)
    pdf_url = upload_to_gcs(pdf_name, pdf_contents, 'application/pdf')
    logging.debug('Storing Investment Agreement in the datastore')
    pdf_size = len(pdf_contents)

    deferred.defer(_create_investment_agreement_iyo_see_doc, agreement_key, app_user, pdf_url, pdf_size=pdf_size)
    deferred.defer(update_investor_progress, email, app_id, INVESTMENT_TODO_MAPPING[agreement.status])
Beispiel #16
0
def create_itft_amendment_1_pdf(app_user):
    from plugins.tff_backend.bizz.investor import get_total_token_count
    agreements = InvestmentAgreement.list_by_status_and_user(app_user, (InvestmentAgreement.STATUS_PAID,
                                                                        InvestmentAgreement.STATUS_SIGNED))
    azzert(agreements)
    agreements.sort(key=lambda a: a.sign_time)
    purchase_amounts = ''
    sign_dates = ''
    for i, agreement in enumerate(agreements):
        if i:
            purchase_amounts += '<br>'
            sign_dates += '<br>'
        purchase_amounts += '%s %s' % (agreement.amount, agreement.currency)
        sign_dates += _get_effective_date(agreement.sign_time)

    old_count = get_total_token_count(app_user, agreements)[TOKEN_ITFT]
    new_count = old_count * 100.0
    purchase_amount_in_usd = old_count * 5.0

    fmt = lambda x: '{:.2f}'.format(x)
    template_variables = {
        'logo_path': 'assets/logo.jpg',
        'agreement': _get_effective_date,
        'full_name': agreements[0].name,
        'purchase_amounts': purchase_amounts,
        'sign_dates': sign_dates,
        'old_count': fmt(old_count),
        'new_count': fmt(new_count),
        'purchase_amount_in_usd': fmt(purchase_amount_in_usd),
        'title': 'iTFT Purchase Agreement - Amendment I<br>iTFT Token Price & Volume Adjustment'
    }

    md = JINJA_ENVIRONMENT.get_template('itft_amendment_1.md').render(template_variables)
    markdown_to_html = markdown.markdown(md, extensions=['markdown.extensions.tables'])
    template_variables['markdown_to_html'] = markdown_to_html.replace('<th', '<td')
    return _render_pdf_from_html('token_itft.html', template_variables)
Beispiel #17
0
def create_investment_agreement(agreement):
    # type: (CreateInvestmentAgreementTO) -> InvestmentAgreement
    app_user = users.User(agreement.app_user)
    username = get_iyo_username(app_user)
    tff_profile = get_tff_profile(username)
    if tff_profile.kyc.status != KYCStatus.VERIFIED:
        raise HttpBadRequestException('cannot_invest_not_kyc_verified')

    token_count_float = get_token_count(agreement.currency, agreement.amount)
    agreement_model = _create_investment_agreement(agreement.amount, agreement.currency, agreement.token,
                                                   token_count_float, username, 'manually_created', app_user,
                                                   status=agreement.status, paid_time=agreement.paid_time,
                                                   sign_time=agreement.sign_time)
    prefix, doc_content_base64 = agreement.document.split(',')
    content_type = prefix.split(';')[0].replace('data:', '')
    doc_content = base64.b64decode(doc_content_base64)
    agreement_model.put()

    pdf_name = InvestmentAgreement.filename(agreement_model.id)
    pdf_url = upload_to_gcs(pdf_name, doc_content, content_type)
    deferred.defer(_create_investment_agreement_iyo_see_doc, agreement_model.key, app_user, pdf_url,
                   content_type, send_sign_message=False, pdf_size=len(doc_content))

    return agreement_model
def _create_token_value_agreement_if_needed(profile_key):
    profile = profile_key.get()  # type: TffProfile
    investments = [
        i for i in InvestmentAgreement.list_by_user(profile.app_user)
        if PaymentInfo.HAS_MULTIPLIED_TOKENS not in i.payment_info
        and i.creation_time <= FF_ENDED_TIMESTAMP
    ]
    statuses = [
        InvestmentAgreement.STATUS_PAID, InvestmentAgreement.STATUS_SIGNED
    ]
    canceled_or_started_investments = [
        i for i in investments if i.status not in statuses
    ]
    to_put, token_count = multiply_tokens_for_agreements(
        canceled_or_started_investments)
    azzert(token_count == 0, 'Expected token_count to be 0')
    logging.info('Updated %s agreements for user %s', len(to_put),
                 profile.username)
    if to_put:
        ndb.put_multi(to_put)
    has_document = any(d.type == DocumentType.TOKEN_VALUE_ADDENDUM.value
                       for d in Document.list_by_username(profile.username))
    if any(i.status in statuses for i in investments) and not has_document:
        create_token_value_agreement(profile.username)
def _get_investment_agreements():
    return InvestmentAgreement.query()
Beispiel #20
0
def send_signed_investments_messages(app_user):
    agreements = InvestmentAgreement.list_by_status_and_user(app_user, InvestmentAgreement.STATUS_SIGNED)
    for agreement in agreements:
        deferred.defer(send_payment_instructions, app_user, agreement.id, '')
Beispiel #21
0
def _order_node(order_key, user_email, app_id, steps):
    logging.info('Receiving order of Zero-Node')
    app_user = create_app_user_by_email(user_email, app_id)

    overview_step = get_step(steps, 'message_overview')
    if overview_step and overview_step.answer_id == u"button_use":
        api_key = get_rogerthat_api_key()
        user_data_keys = [
            'name', 'email', 'phone', 'billing_address', 'address',
            'shipping_name', 'shipping_email', 'shipping_phone',
            'shipping_address'
        ]
        user_data = system.get_user_data(api_key, user_email, app_id,
                                         user_data_keys)
        billing_info = ContactInfo(name=user_data['name'],
                                   email=user_data['email'],
                                   phone=user_data['phone'],
                                   address=user_data['billing_address']
                                   or user_data['address'])

        if user_data['shipping_name']:
            shipping_info = ContactInfo(name=user_data['shipping_name'],
                                        email=user_data['shipping_email'],
                                        phone=user_data['shipping_phone'],
                                        address=user_data['shipping_address'])
        else:
            shipping_info = billing_info

        updated_user_data = None
    else:
        name = get_step_value(steps, 'message_name')
        email = get_step_value(steps, 'message_email')
        phone = get_step_value(steps, 'message_phone')
        billing_address = get_step_value(steps, 'message_billing_address')
        updated_user_data = {
            'name': name,
            'email': email,
            'phone': phone,
            'billing_address': billing_address,
        }

        billing_info = ContactInfo(name=name,
                                   email=email,
                                   phone=phone,
                                   address=billing_address)

        same_shipping_info_step = get_step(steps,
                                           'message_choose_shipping_info')
        if same_shipping_info_step and same_shipping_info_step.answer_id == u"button_yes":
            shipping_info = billing_info
        else:
            shipping_name = get_step_value(steps, 'message_shipping_name')
            shipping_email = get_step_value(steps, 'message_shipping_email')
            shipping_phone = get_step_value(steps, 'message_shipping_phone')
            shipping_address = get_step_value(steps,
                                              'message_shipping_address')
            updated_user_data.update({
                'shipping_name': shipping_name,
                'shipping_email': shipping_email,
                'shipping_phone': shipping_phone,
                'shipping_address': shipping_address,
            })

            shipping_info = ContactInfo(name=shipping_name,
                                        email=shipping_email,
                                        phone=shipping_phone,
                                        address=shipping_address)
    socket_step = get_step(steps, 'message_socket')
    socket = socket_step and socket_step.answer_id.replace('button_', '')

    # Only one node is allowed per user, and one per location
    if NodeOrder.has_order_for_user_or_location(
            app_user, billing_info.address) and not DEBUG:
        logging.info('User already has a node order, sending abort message')
        msg = u'Dear ThreeFold Member, we sadly cannot grant your request to host an additional ThreeFold Node:' \
              u' We are currently only allowing one Node to be hosted per ThreeFold Member and location.' \
              u' This will allow us to build a bigger base and a more diverse Grid.'
        subject = u'Your ThreeFold Node request'
        send_message_and_email(app_user, msg, subject)
        return

    # Check if user has invested >= 120 tokens
    paid_orders = InvestmentAgreement.list_by_status_and_user(
        app_user, InvestmentAgreement.STATUS_PAID)
    total_tokens = sum([o.token_count_float for o in paid_orders])
    can_host = total_tokens >= REQUIRED_TOKEN_COUNT_TO_HOST

    def trans():
        logging.debug('Storing order in the database')
        order = NodeOrder(key=order_key,
                          app_user=app_user,
                          tos_iyo_see_id=None,
                          billing_info=billing_info,
                          shipping_info=shipping_info,
                          order_time=now(),
                          status=NodeOrderStatus.APPROVED
                          if can_host else NodeOrderStatus.WAITING_APPROVAL,
                          socket=socket)
        order.put()
        if can_host:
            logging.info(
                'User has invested more than %s tokens, immediately creating node order PDF.',
                REQUIRED_TOKEN_COUNT_TO_HOST)
            deferred.defer(_create_node_order_pdf,
                           order_key.id(),
                           _transactional=True)
        else:
            logging.info(
                'User has not invested more than %s tokens, an admin needs to approve this order manually.',
                REQUIRED_TOKEN_COUNT_TO_HOST)
            deferred.defer(_inform_support_of_new_node_order,
                           order_key.id(),
                           _transactional=True)
        deferred.defer(set_hoster_status_in_user_data,
                       order.app_user,
                       False,
                       _transactional=True)
        if updated_user_data:
            deferred.defer(put_user_data,
                           app_user,
                           updated_user_data,
                           _transactional=True)

    ndb.transaction(trans)
Beispiel #22
0
def investment_agreement_signed(status, form_result, answer_id, member,
                                message_key, tag, received_timestamp,
                                acked_timestamp, parent_message_key,
                                result_key, service_identity, user_details):
    """
    Args:
        status (int)
        form_result (FormResultTO)
        answer_id (unicode)
        member (unicode)
        message_key (unicode)
        tag (unicode)
        received_timestamp (int)
        acked_timestamp (int)
        parent_message_key (unicode)
        result_key (unicode)
        service_identity (unicode)
        user_details(list[UserDetailsTO])

    Returns:
        FormAcknowledgedCallbackResultTO
    """
    try:
        user_detail = user_details[0]
        tag_dict = json.loads(tag)
        agreement = InvestmentAgreement.create_key(
            tag_dict['agreement_id']).get()  # type: InvestmentAgreement

        if answer_id != FormTO.POSITIVE:
            logging.info('Investment agreement was canceled')
            agreement.status = InvestmentAgreement.STATUS_CANCELED
            agreement.cancel_time = now()
            agreement.put()
            return None

        logging.info('Received signature for Investment Agreement')

        sign_result = form_result.result.get_value()
        assert isinstance(sign_result, SignWidgetResultTO)
        payload_signature = sign_result.payload_signature

        iyo_organization_id = get_iyo_organization_id()
        iyo_username = get_iyo_username(user_detail)

        logging.debug('Getting IYO SEE document %s', agreement.iyo_see_id)
        doc = get_see_document(iyo_organization_id, iyo_username,
                               agreement.iyo_see_id)
        doc_view = IYOSeeDocumentView(username=doc.username,
                                      globalid=doc.globalid,
                                      uniqueid=doc.uniqueid,
                                      **serialize_complex_value(
                                          doc.versions[-1],
                                          IYOSeeDocumenVersion, False))
        doc_view.signature = payload_signature
        keystore_label = get_publickey_label(sign_result.public_key.public_key,
                                             user_detail)
        if not keystore_label:
            return create_error_message(FormAcknowledgedCallbackResultTO())
        doc_view.keystore_label = keystore_label
        logging.debug('Signing IYO SEE document')
        sign_see_document(iyo_organization_id, iyo_username, doc_view)

        logging.debug('Storing signature in DB')
        agreement.populate(status=InvestmentAgreement.STATUS_SIGNED,
                           signature=payload_signature,
                           sign_time=now())
        agreement.put_async()

        deferred.defer(add_user_to_role, user_detail, RogerthatRoles.INVESTOR)
        intercom_tags = get_intercom_tags_for_investment(agreement)
        if intercom_tags:
            for i_tag in intercom_tags:
                deferred.defer(intercom_helpers.tag_intercom_users, i_tag,
                               [iyo_username])
        deferred.defer(update_investor_progress, user_detail.email,
                       user_detail.app_id,
                       INVESTMENT_TODO_MAPPING[agreement.status])
        deferred.defer(_inform_support_of_new_investment, iyo_username,
                       agreement.id, agreement.token_count_float)
        logging.debug('Sending confirmation message')
        prefix_message = u'Thank you. We successfully received your digital signature.' \
                         u' We have stored a copy of this agreement in your ThreeFold Documents.' \
                         u' We will contact you again when we have received your payment.' \
                         u' Thanks again for your purchase and your support of the ThreeFold Foundation!' \
                         u'\n\nWe would like to take this opportunity to remind you once again to keep a back-up of' \
                         u' your wallet in a safe place, by writing down the 29 words that can be used to restore it' \
                         u' to a different device.' \
                         u' As usual, if you have any questions, don\'t hesitate to contact us.\n\n'
        msg = u'%sReference: %s' % (prefix_message, agreement.reference)
        deferred.defer(send_payment_instructions, agreement.app_user,
                       agreement.id, prefix_message)

        message = MessageCallbackResultTypeTO()
        message.alert_flags = Message.ALERT_FLAG_VIBRATE
        message.answers = []
        message.branding = get_main_branding_hash()
        message.dismiss_button_ui_flags = 0
        message.flags = Message.FLAG_ALLOW_DISMISS | Message.FLAG_AUTO_LOCK
        message.message = msg
        message.step_id = u'investment_agreement_accepted'
        message.tag = None

        result = FormAcknowledgedCallbackResultTO()
        result.type = TYPE_MESSAGE
        result.value = message
        return result
    except:
        logging.exception('An unexpected error occurred')
        return create_error_message(FormAcknowledgedCallbackResultTO())
def get_investment_agreement(agreement_id):
    # type: (long) -> InvestmentAgreement
    agreement = InvestmentAgreement.get_by_id(agreement_id)
    if not agreement:
        raise HttpNotFoundException('investment_agreement_not_found')
    return agreement