Earlier, we were using 'timezone_now' (the time cron job runs)
as the event_time while invoicing plans in 'invoice_plan'.
This is not accurate as it will lead to invoicing ledger entries
created after 'next_invoice_date' and before 'timezone_now'.
We should only invoice the ledger entries created till
next_invoice_date. It should be independent of the time
at which cron job runs.
This commit updates the logic to use next_invoice_date
as the event_time while invoicing via cron.
Earlier, at few places in test_stripe we were doing
incorrect time travel. For example:
A plan was invoiced till self.next_year & while checking
the billing page, we were using the self.now datetime.
To correctly check the billing page state after plan is
invoiced, we should use a datetime greater than or equal
to the time at which plan was invoiced.
This commit fixes such logically incorrect time-travel.
While creating a LicenseLedger entry in 'create_customer_and_plan',
we should set the 'event_time' to the same time at which the plan
is created.
Earlier, the 'event_time' for ledger entry & 'billing_cycle_anchor'
of the plan were set to different values, which is not the
correct behavior.
It's best for these to just be consistent. Therefore:
1. The .../not-configured/ error page endpoint should be restricted to
.has_billing_access users only.
2. For consistency, self_hosting_auth_view_common is tweaked to also do
the .has_billing_access check as the first thing, to avoid revealing
configuration information via its redirect/error-handling behavior.
The revealed configuration information seems super harmless, but it's
simpler to not have to worry about it and just be consistent.
Just shows a config error page if the bouncer is not enabled. Uses a new
endpoint for this so that it can work nicely for both browser and
desktop app clients.
It's necessary, because the desktop app expects to get a json response
with either an error or billing_access_url to redirect to. Showing a
nice config error page can't be done via the json error mechanism, so
instead we just serve a redirect to the new error page, which the app
will open in the browser in a new window or tab.
The remote support view now returns results for deactivated remote
servers with those results sorted to the end and formatted to
visually stand out.
Forms to change sponsorship and discount fields on the customer
for the remote server or realm are not shown, but the data stored
on the customer object is shown, including any sponsorship request
information (if the customer had a sponsorship request pending when
it was deactivated).
Forms to schedule a plan are also not shown for deactivated servers
and their associated remote realms.
Forms and information for any current plan or scheduled plan, for
either the deactivated remote server or its associated remote
realms, are shown so that support staff can update those plans if
necessary.
Earlier, the 'next_invoice_date', 'invoiced_through', and
'invoicing_status' fields in 'do_change_schedule_after_free_trial'
were not set correctly.
It resulted in invoicing the ENDED plan and the ledger we create
in this function.
Multiple invoices were created depending on the number of times the
billing frequency was changed for customers who started free-trial
& changed their billing frequency.
This commit sets those fields correctly leading to create only one
invoice.
Because the remote support page now supports searching by UUID,
the support URL for remote billing entities, which is used for
sponsorship request emails and overdue invoice emails, can now
use the remote server or realm UUID.
Adds the remote realm UUID to the remote support view information.
Earlier, for fixed-price plans we were showing the generic
next payment info on billing page which stated that plan
will automatically renew on end_date. It is no longer correct
for fixed-price plans.
This commit fixes the next payment info for fixed-price plans.
When the next_billing_cycle is the end_date, we inform the customer
that their plan will end on end_date and zulip sales will contact
them a couple of month ago before the end_date for renewal.
Adds the information returned by get_push_status_for_remote_request
for remote billing users to the support page. Note that getting
the current push status data will result in some duplicate database
queries (getting customer, plan, current billed users, next billing
cycle) when generating the remote support view.
We no longer want to migrate Legacy plans from server to realms, since
Legacy plans are not really a thing in the original sense anymore, since
February 15th.
Now they're just a tool to give temporary extensions of access to the
push notification service for users, when needed. And as such, it makes
no sense to migrate like that.
The remaining code in this function is for migrating (any) plan from the
server object to the realm object, if the server has just a single
realm.
For fixed-price plans, we send a reminder email to
sales@zulip.com, 2 months before the end date, to review
the pricing and configure a new fixed-price plan accordingly.
Earlier, we were not configuring the end_date while creating
a fixed-price plan which would result in the automatic renewal
of the plan at the end of billing cycle.
Our plan is to re-evaluate the pricing when we are close to the
end date, then:
1. Schedule a new fixed-price plan with the updated or same price.
2. or Don't do anything - the plan will end on the end_date.
Now, we add an end_date of 1 year from the start date when
creating a fixed-price plan.
The logic in the case where there's only one realm and the function
tries to migrate the server's plan to it, had two main unhandled edge
cases that would throw exceptions:
1.
```
remote_realm = RemoteRealm.objects.get(
uuid=realm_uuids[0], plan_type=RemoteRealm.PLAN_TYPE_SELF_MANAGED
)
```
This could throw an exception if the RemoteRealm exists, but has an
active e.g. Legacy plan. Then there'd be no object matching the
plan_type in the query, raising RemoteRealm.DoesNotExist.
2. If the RemoteRealm had e.g. a Legacy plan in the past, that's now
expired, then it'd have a Customer object. Meaning that the attempt
to move the server's customer to the realm:
`server_plan.customer = remote_realm_customer`
would trigger an IntegrityError since a RemoteRealm can't have two
Customer objects.
In simple cases the situation in (2) can still be easily migrated, by
moving the plan from the server's customer to the realm's customer.
This is kind of too specific, allowing testing for only one single error
when accessing signed_auth_url. Instead, this should use a general
pattern, which will allow other tests to use this to assert other kinds
of error responses that may be returned.
It doesn't make sense to run a loop over "all" query results, when those
results are just two, and each of them has its own distinct asserts.
That for loop is there probably due to copying the structure of the
earlier test_transfer_legacy_plan_from_server_to_all_realms test, for
which the loop does make sense.
Earlier, on extending end_date for legacy plans, next_invoice_date
was not extended which resulted in invoice_plan() run for such
legacy plans before the end_date.
The intended behaviour is that the legacy plan should be invoiced
only once on the end_date to downgrade or switch to a new tier.
This commit adds logic to update next_invoice_date when end_date
is extended via /support.
Expands the main query for remote servers to get the audit log
event datetime for when the server was created/registered.
The remote realm object has a field for when the remote realm
was created on the remote server.
Adds a link on the upgrade and billing pages that opens a stripe
billing portal for the customer to update their name and address
that will appear on invoices and receipts.
On the billing page, updating the credit card information will
no longer update the customer billing address, since they can
now do this directly through the billing portal. To be consistent
with the credit card form on the upgrade page, we still require
inputting a billing address for the card.
Note that, once an invoice is paid/complete, then changes to the
customer's name and address will not be applied to those invoices.
Instead of charging the customer using the attached payment
method and then creating the invoice, we create an invoice and
force an immediate payment for the invoice via the attached
payment method.
Earlier, when a fixed-price plan for a customer with
no current plan was configured via /support, the next plan
info was missing on support page.
It was because we were considering next plan only if the
customer had a current plan.
This commit fixes the incorrect behaviour.
View functions in `analytics/views/support.py` are moved to
`corporate/views/support.py`.
Shared activity functions in `analytics/views/activity_common.py`
are moved to `corporate/lib/activity.py`, which was also renamed
from `corporate/lib/analytics.py`.
Earlier, the next_invoicing_date and invoicing_status
for new plan weren't set correctly, resulting in the
scheduled switching of legacy plan to a new plan not
working as expected.
This commit fixes the incorrect behaviour.
Earlier, in 'migrate_customer_to_legacy_plan`, we set
'next_invoice_date' to None for legacy plans.
This will result in legacy plans not getting invoiced.
We need to invoice legacy plans on their end date to
either downgrade them or switch to a new plan.
We set next_invoice_date for legacy plans to end_date.
When you click "Plan management", the desktop app opens
/self-hosted-billing/ in your browser immediately. So that works badly
if you're already logged into another account in the browser, since that
session will be used and it may be for a different user account than in
the desktop app, causing unintended behavior.
The solution is to replace the on click behavior for "Plan management"
in the desktop app case, to instead make a request to a new endpoint
/json/self-hosted-billing, which provides the billing access url in a
json response. The desktop app takes that URL and window.open()s it (in
the browser). And so a remote billing session for the intended user will
be obtained.