示例#1
0
    def test_get_seat_count(self) -> None:
        realm = get_realm("zulip")
        initial_count = get_seat_count(realm)
        user1 = UserProfile.objects.create(realm=realm, email='*****@*****.**', pointer=-1)
        user2 = UserProfile.objects.create(realm=realm, email='*****@*****.**', pointer=-1)
        self.assertEqual(get_seat_count(realm), initial_count + 2)

        # Test that bots aren't counted
        user1.is_bot = True
        user1.save(update_fields=['is_bot'])
        self.assertEqual(get_seat_count(realm), initial_count + 1)

        # Test that inactive users aren't counted
        do_deactivate_user(user2)
        self.assertEqual(get_seat_count(realm), initial_count)
示例#2
0
    def test_get_seat_count(self) -> None:
        realm = get_realm("zulip")
        initial_count = get_seat_count(realm)
        user1 = UserProfile.objects.create(realm=realm, email='*****@*****.**', pointer=-1)
        user2 = UserProfile.objects.create(realm=realm, email='*****@*****.**', pointer=-1)
        self.assertEqual(get_seat_count(realm), initial_count + 2)

        # Test that bots aren't counted
        user1.is_bot = True
        user1.save(update_fields=['is_bot'])
        self.assertEqual(get_seat_count(realm), initial_count + 1)

        # Test that inactive users aren't counted
        do_deactivate_user(user2)
        self.assertEqual(get_seat_count(realm), initial_count)
示例#3
0
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and CustomerPlan.objects.filter(customer=customer).exists():
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    percent_off = 0
    if customer is not None and customer.default_discount is not None:
        percent_off = customer.default_discount

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'email': user.email,
        'seat_count': seat_count,
        'signed_seat_count': signed_seat_count,
        'salt': salt,
        'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES),
        'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE,
        'plan': "Zulip Standard",
        'page_params': JSONEncoderForHTML().encode({
            'seat_count': seat_count,
            'annual_price': 8000,
            'monthly_price': 800,
            'percent_off': float(percent_off),
        }),
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    return response
示例#4
0
文件: views.py 项目: BakerWang/zulip
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and CustomerPlan.objects.filter(customer=customer).exists():
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    percent_off = 0
    if customer is not None and customer.default_discount is not None:
        percent_off = customer.default_discount

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'email': user.email,
        'seat_count': seat_count,
        'signed_seat_count': signed_seat_count,
        'salt': salt,
        'min_invoiced_licenses': max(seat_count, MIN_INVOICED_LICENSES),
        'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE,
        'plan': "Zulip Standard",
        'page_params': JSONEncoderForHTML().encode({
            'seat_count': seat_count,
            'annual_price': 8000,
            'monthly_price': 800,
            'percent_off': float(percent_off),
        }),
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    return response
示例#5
0
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    error_message = ""
    error_description = ""  # only used in tests

    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and customer.has_billing_relationship:
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    if request.method == 'POST':
        try:
            plan, seat_count = unsign_and_check_upgrade_parameters(
                user, request.POST['plan'], request.POST['signed_seat_count'],
                request.POST['salt'])
            if 'invoiced_seat_count' in request.POST:
                min_required_seat_count = max(seat_count,
                                              MIN_INVOICED_SEAT_COUNT)
                if int(request.POST['invoiced_seat_count']
                       ) < min_required_seat_count:
                    raise BillingError(
                        'lowball seat count',
                        "You must invoice for at least %d users." %
                        (min_required_seat_count, ))
                seat_count = int(request.POST['invoiced_seat_count'])
            process_initial_upgrade(user, plan, seat_count,
                                    request.POST.get('stripeToken', None))
        except BillingError as e:
            error_message = e.message
            error_description = e.description
        except Exception as e:
            billing_logger.exception("Uncaught exception in billing: %s" %
                                     (e, ))
            error_message = BillingError.CONTACT_SUPPORT
            error_description = "uncaught exception during upgrade"
        else:
            return HttpResponseRedirect(
                reverse('corporate.views.billing_home'))

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'email': user.email,
        'seat_count': seat_count,
        'signed_seat_count': signed_seat_count,
        'salt': salt,
        'plan': "Zulip Standard",
        'nickname_monthly': Plan.CLOUD_MONTHLY,
        'nickname_annual': Plan.CLOUD_ANNUAL,
        'error_message': error_message,
        'cloud_monthly_price': 8,
        'cloud_annual_price': 80,
        'cloud_annual_price_per_month': 6.67,
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    response['error_description'] = error_description
    return response
示例#6
0
文件: views.py 项目: zqyoung1/zulip
def billing_home(request: HttpRequest) -> HttpResponse:
    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is None:
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))
    if not CustomerPlan.objects.filter(customer=customer).exists():
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))

    if not user.is_realm_admin and not user.is_billing_admin:
        context = {'admin_access': False}  # type: Dict[str, Any]
        return render(request, 'corporate/billing.html', context=context)
    context = {'admin_access': True}

    stripe_customer = stripe_get_customer(customer.stripe_customer_id)
    plan = get_active_plan(customer)
    if plan is not None:
        plan_name = {
            CustomerPlan.STANDARD: 'Zulip Standard',
            CustomerPlan.PLUS: 'Zulip Plus',
        }[plan.tier]
        now = timezone_now()
        last_ledger_entry = add_plan_renewal_to_license_ledger_if_needed(
            plan, now)
        licenses = last_ledger_entry.licenses
        licenses_used = get_seat_count(user.realm)
        # Should do this in javascript, using the user's timezone
        renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format(
            dt=next_renewal_date(plan, now))
        renewal_cents = renewal_amount(plan, now)
        # TODO: this is the case where the plan doesn't automatically renew
        if renewal_cents is None:  # nocoverage
            renewal_cents = 0
        charge_automatically = plan.charge_automatically
        if charge_automatically:
            payment_method = payment_method_string(stripe_customer)
        else:
            payment_method = 'Billed by invoice'
    # Can only get here by subscribing and then downgrading. We don't support downgrading
    # yet, but keeping this code here since we will soon.
    else:  # nocoverage
        plan_name = "Zulip Free"
        licenses = 0
        renewal_date = ''
        renewal_cents = 0
        payment_method = ''
        charge_automatically = False

    context.update({
        'plan_name': plan_name,
        'licenses': licenses,
        'licenses_used': licenses_used,
        'renewal_date': renewal_date,
        'renewal_amount': '{:,.2f}'.format(renewal_cents / 100.),
        'payment_method': payment_method,
        'charge_automatically': charge_automatically,
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'stripe_email': stripe_customer.email,
    })
    return render(request, 'corporate/billing.html', context=context)
示例#7
0
文件: views.py 项目: BakerWang/zulip
def billing_home(request: HttpRequest) -> HttpResponse:
    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is None:
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))
    if not CustomerPlan.objects.filter(customer=customer).exists():
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))

    if not user.is_realm_admin and not user.is_billing_admin:
        context = {'admin_access': False}  # type: Dict[str, Any]
        return render(request, 'corporate/billing.html', context=context)
    context = {'admin_access': True}

    stripe_customer = stripe_get_customer(customer.stripe_customer_id)
    plan = get_active_plan(customer)
    if plan is not None:
        plan_name = {
            CustomerPlan.STANDARD: 'Zulip Standard',
            CustomerPlan.PLUS: 'Zulip Plus',
        }[plan.tier]
        now = timezone_now()
        last_ledger_entry = add_plan_renewal_to_license_ledger_if_needed(plan, now)
        licenses = last_ledger_entry.licenses
        licenses_used = get_seat_count(user.realm)
        # Should do this in javascript, using the user's timezone
        renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format(dt=next_renewal_date(plan, now))
        renewal_cents = renewal_amount(plan, now)
        # TODO: this is the case where the plan doesn't automatically renew
        if renewal_cents is None:  # nocoverage
            renewal_cents = 0
        charge_automatically = plan.charge_automatically
        if charge_automatically:
            payment_method = payment_method_string(stripe_customer)
        else:
            payment_method = 'Billed by invoice'
    # Can only get here by subscribing and then downgrading. We don't support downgrading
    # yet, but keeping this code here since we will soon.
    else:  # nocoverage
        plan_name = "Zulip Free"
        licenses = 0
        renewal_date = ''
        renewal_cents = 0
        payment_method = ''
        charge_automatically = False

    context.update({
        'plan_name': plan_name,
        'licenses': licenses,
        'licenses_used': licenses_used,
        'renewal_date': renewal_date,
        'renewal_amount': '{:,.2f}'.format(renewal_cents / 100.),
        'payment_method': payment_method,
        'charge_automatically': charge_automatically,
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'stripe_email': stripe_customer.email,
    })
    return render(request, 'corporate/billing.html', context=context)
示例#8
0
def billing_home(request: HttpRequest) -> HttpResponse:
    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is None:
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))
    if not CustomerPlan.objects.filter(customer=customer).exists():
        return HttpResponseRedirect(reverse('corporate.views.initial_upgrade'))

    if not user.is_realm_admin and not user.is_billing_admin:
        context = {'admin_access': False}  # type: Dict[str, Any]
        return render(request, 'corporate/billing.html', context=context)
    context = {'admin_access': True}

    plan_name = "Zulip Free"
    licenses = 0
    renewal_date = ''
    renewal_cents = 0
    payment_method = ''
    charge_automatically = False

    stripe_customer = stripe_get_customer(customer.stripe_customer_id)
    plan = get_current_plan(customer)
    if plan is not None:
        plan_name = {
            CustomerPlan.STANDARD: 'Zulip Standard',
            CustomerPlan.PLUS: 'Zulip Plus',
        }[plan.tier]
        now = timezone_now()
        last_ledger_entry = make_end_of_cycle_updates_if_needed(plan, now)
        if last_ledger_entry is not None:
            licenses = last_ledger_entry.licenses
            licenses_used = get_seat_count(user.realm)
            # Should do this in javascript, using the user's timezone
            renewal_date = '{dt:%B} {dt.day}, {dt.year}'.format(
                dt=start_of_next_billing_cycle(plan, now))
            renewal_cents = renewal_amount(plan, now)
            charge_automatically = plan.charge_automatically
            if charge_automatically:
                payment_method = payment_method_string(stripe_customer)
            else:
                payment_method = 'Billed by invoice'

    context.update({
        'plan_name': plan_name,
        'licenses': licenses,
        'licenses_used': licenses_used,
        'renewal_date': renewal_date,
        'renewal_amount': '{:,.2f}'.format(renewal_cents / 100.),
        'payment_method': payment_method,
        'charge_automatically': charge_automatically,
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'stripe_email': stripe_customer.email,
    })
    return render(request, 'corporate/billing.html', context=context)
示例#9
0
文件: views.py 项目: brainwane/zulip
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    error_message = ""
    error_description = ""  # only used in tests

    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and customer.has_billing_relationship:
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    if request.method == 'POST':
        try:
            plan, seat_count = unsign_and_check_upgrade_parameters(
                user, request.POST['plan'], request.POST['signed_seat_count'], request.POST['salt'])
            if 'invoiced_seat_count' in request.POST:
                min_required_seat_count = max(seat_count, MIN_INVOICED_SEAT_COUNT)
                if int(request.POST['invoiced_seat_count']) < min_required_seat_count:
                    raise BillingError(
                        'lowball seat count',
                        "You must invoice for at least %d users." % (min_required_seat_count,))
                seat_count = int(request.POST['invoiced_seat_count'])
            process_initial_upgrade(user, plan, seat_count, request.POST.get('stripeToken', None))
        except BillingError as e:
            error_message = e.message
            error_description = e.description
        except Exception as e:
            billing_logger.exception("Uncaught exception in billing: %s" % (e,))
            error_message = BillingError.CONTACT_SUPPORT
            error_description = "uncaught exception during upgrade"
        else:
            return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'email': user.email,
        'seat_count': seat_count,
        'signed_seat_count': signed_seat_count,
        'salt': salt,
        'plan': "Zulip Standard",
        'nickname_monthly': Plan.CLOUD_MONTHLY,
        'nickname_annual': Plan.CLOUD_ANNUAL,
        'error_message': error_message,
        'cloud_monthly_price': 8,
        'cloud_annual_price': 80,
        'cloud_annual_price_per_month': 6.67,
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    response['error_description'] = error_description
    return response
示例#10
0
 def setUp(self, *mocks: Mock) -> None:
     # TODO
     # Unfortunately this test suite is likely not robust to users being
     # added in populate_db. A quick hack for now to ensure get_seat_count is 8
     # for these tests (8, since that's what it was when the tests were written).
     realm = get_realm('zulip')
     seat_count = get_seat_count(get_realm('zulip'))
     assert(seat_count >= 8)
     if seat_count > 8:  # nocoverage
         for user in UserProfile.objects.filter(realm=realm, is_active=True, is_bot=False) \
                                        .exclude(email__in=[
                                            self.example_email('hamlet'),
                                            self.example_email('iago')])[6:]:
             user.is_active = False
             user.save(update_fields=['is_active'])
     self.assertEqual(get_seat_count(get_realm('zulip')), 8)
     self.seat_count = 8
     self.signed_seat_count, self.salt = sign_string(str(self.seat_count))
     # Choosing dates with corresponding timestamps below 1500000000 so that they are
     # not caught by our timestamp normalization regex in normalize_fixture_data
     self.now = datetime(2012, 1, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
     self.next_month = datetime(2012, 2, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
     self.next_year = datetime(2013, 1, 2, 3, 4, 5).replace(tzinfo=timezone_utc)
示例#11
0
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and customer.has_billing_relationship:
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    percent_off = 0
    if customer is not None:
        stripe_customer = stripe_get_customer(customer.stripe_customer_id)
        if stripe_customer.discount is not None:
            percent_off = stripe_customer.discount.coupon.percent_off

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key':
        STRIPE_PUBLISHABLE_KEY,
        'email':
        user.email,
        'seat_count':
        seat_count,
        'signed_seat_count':
        signed_seat_count,
        'salt':
        salt,
        'min_seat_count_for_invoice':
        max(seat_count, MIN_INVOICED_SEAT_COUNT),
        'default_invoice_days_until_due':
        DEFAULT_INVOICE_DAYS_UNTIL_DUE,
        'plan':
        "Zulip Standard",
        'nickname_monthly':
        Plan.CLOUD_MONTHLY,
        'nickname_annual':
        Plan.CLOUD_ANNUAL,
        'page_params':
        JSONEncoderForHTML().encode({
            'seat_count': seat_count,
            'nickname_annual': Plan.CLOUD_ANNUAL,
            'nickname_monthly': Plan.CLOUD_MONTHLY,
            'annual_price': 8000,
            'monthly_price': 800,
            'percent_off': percent_off,
        }),
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    return response
示例#12
0
def initial_upgrade(request: HttpRequest) -> HttpResponse:
    if not settings.BILLING_ENABLED:
        return render(request, "404.html")

    user = request.user
    error_message = ""
    error_description = ""  # only used in tests

    customer = Customer.objects.filter(realm=user.realm).first()
    if customer is not None and customer.has_billing_relationship:
        return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    percent_off = 0
    if customer is not None:
        stripe_customer = stripe_get_customer(customer.stripe_customer_id)
        if stripe_customer.discount is not None:
            percent_off = stripe_customer.discount.coupon.percent_off

    if request.method == 'POST':
        try:
            plan, seat_count = unsign_and_check_upgrade_parameters(
                user, request.POST['plan'], request.POST['signed_seat_count'], request.POST['salt'],
                request.POST['billing_modality'])
            if request.POST['billing_modality'] == 'send_invoice':
                try:
                    invoiced_seat_count = int(request.POST['invoiced_seat_count'])
                except (KeyError, ValueError):
                    invoiced_seat_count = -1
                min_required_seat_count = max(seat_count, MIN_INVOICED_SEAT_COUNT)
                if invoiced_seat_count < min_required_seat_count:
                    raise BillingError(
                        'lowball seat count',
                        "You must invoice for at least %d users." % (min_required_seat_count,))
                seat_count = invoiced_seat_count
            process_initial_upgrade(user, plan, seat_count, request.POST.get('stripe_token', None))
        except BillingError as e:
            error_message = e.message
            error_description = e.description
        except Exception as e:
            billing_logger.exception("Uncaught exception in billing: %s" % (e,))
            error_message = BillingError.CONTACT_SUPPORT
            error_description = "uncaught exception during upgrade"
        else:
            return HttpResponseRedirect(reverse('corporate.views.billing_home'))

    seat_count = get_seat_count(user.realm)
    signed_seat_count, salt = sign_string(str(seat_count))
    context = {
        'publishable_key': STRIPE_PUBLISHABLE_KEY,
        'email': user.email,
        'seat_count': seat_count,
        'signed_seat_count': signed_seat_count,
        'salt': salt,
        'min_seat_count_for_invoice': max(seat_count, MIN_INVOICED_SEAT_COUNT),
        'default_invoice_days_until_due': DEFAULT_INVOICE_DAYS_UNTIL_DUE,
        'plan': "Zulip Standard",
        'nickname_monthly': Plan.CLOUD_MONTHLY,
        'nickname_annual': Plan.CLOUD_ANNUAL,
        'error_message': error_message,
        'page_params': JSONEncoderForHTML().encode({
            'seat_count': seat_count,
            'nickname_annual': Plan.CLOUD_ANNUAL,
            'nickname_monthly': Plan.CLOUD_MONTHLY,
            'annual_price': 8000,
            'monthly_price': 800,
            'percent_off': percent_off,
        }),
    }  # type: Dict[str, Any]
    response = render(request, 'corporate/upgrade.html', context=context)
    response['error_description'] = error_description
    return response