mirror of https://github.com/zulip/zulip.git
corporate: Move void_all_open_invoices to BillingSession.
This commit is contained in:
parent
97d33a4363
commit
4fb564026d
|
@ -758,7 +758,9 @@ class TestSupportEndpoint(ZulipTestCase):
|
|||
with mock.patch(
|
||||
"analytics.views.support.RealmBillingSession.downgrade_now_without_creating_additional_invoices"
|
||||
) as m1:
|
||||
with mock.patch("analytics.views.support.void_all_open_invoices", return_value=1) as m2:
|
||||
with mock.patch(
|
||||
"analytics.views.support.RealmBillingSession.void_all_open_invoices", return_value=1
|
||||
) as m2:
|
||||
result = self.client_post(
|
||||
"/activity/support",
|
||||
{
|
||||
|
@ -767,7 +769,7 @@ class TestSupportEndpoint(ZulipTestCase):
|
|||
},
|
||||
)
|
||||
m1.assert_called_once()
|
||||
m2.assert_called_once_with(get_realm("zulip"))
|
||||
m2.assert_called_once()
|
||||
self.assert_in_success_response(
|
||||
["zulip downgraded and voided 1 open invoices"], result
|
||||
)
|
||||
|
|
|
@ -58,7 +58,6 @@ if settings.BILLING_ENABLED:
|
|||
SupportType,
|
||||
SupportViewRequest,
|
||||
get_latest_seat_count,
|
||||
void_all_open_invoices,
|
||||
)
|
||||
from corporate.lib.support import (
|
||||
get_discount_for_realm,
|
||||
|
@ -263,7 +262,7 @@ def support(
|
|||
] = f"{realm.string_id} downgraded without creating additional invoices"
|
||||
elif modify_plan == "downgrade_now_void_open_invoices":
|
||||
billing_session.downgrade_now_without_creating_additional_invoices()
|
||||
voided_invoices_count = void_all_open_invoices(realm)
|
||||
voided_invoices_count = billing_session.void_all_open_invoices()
|
||||
context[
|
||||
"success_message"
|
||||
] = f"{realm.string_id} downgraded and voided {voided_invoices_count} open invoices"
|
||||
|
|
|
@ -1561,6 +1561,19 @@ class BillingSession(ABC):
|
|||
assert plan is not None
|
||||
do_change_plan_status(plan, CustomerPlan.DOWNGRADE_AT_END_OF_CYCLE)
|
||||
|
||||
def void_all_open_invoices(self) -> int:
|
||||
customer = self.get_customer()
|
||||
if customer is None:
|
||||
return 0
|
||||
invoices = get_all_invoices_for_customer(customer)
|
||||
voided_invoices_count = 0
|
||||
for invoice in invoices:
|
||||
if invoice.status == "open":
|
||||
assert invoice.id is not None
|
||||
stripe.Invoice.void_invoice(invoice.id)
|
||||
voided_invoices_count += 1
|
||||
return voided_invoices_count
|
||||
|
||||
# During realm deactivation we instantly downgrade the plan to Limited.
|
||||
# Extra users added in the final month are not charged. Also used
|
||||
# for the cancellation of Free Trial.
|
||||
|
@ -3151,20 +3164,6 @@ def get_all_invoices_for_customer(customer: Customer) -> Generator[stripe.Invoic
|
|||
)
|
||||
|
||||
|
||||
def void_all_open_invoices(realm: Realm) -> int:
|
||||
customer = get_customer_by_realm(realm)
|
||||
if customer is None:
|
||||
return 0
|
||||
invoices = get_all_invoices_for_customer(customer)
|
||||
voided_invoices_count = 0
|
||||
for invoice in invoices:
|
||||
if invoice.status == "open":
|
||||
assert invoice.id is not None
|
||||
stripe.Invoice.void_invoice(invoice.id)
|
||||
voided_invoices_count += 1
|
||||
return voided_invoices_count
|
||||
|
||||
|
||||
def customer_has_last_n_invoices_open(customer: Customer, n: int) -> bool:
|
||||
if customer.stripe_customer_id is None: # nocoverage
|
||||
return False
|
||||
|
@ -3195,7 +3194,7 @@ def downgrade_small_realms_behind_on_payments_as_needed() -> None:
|
|||
# We've now decided to downgrade this customer and void all invoices, and the below will execute this.
|
||||
billing_session = RealmBillingSession(user=None, realm=realm)
|
||||
billing_session.downgrade_now_without_creating_additional_invoices()
|
||||
void_all_open_invoices(realm)
|
||||
billing_session.void_all_open_invoices()
|
||||
context: Dict[str, Union[str, Realm]] = {
|
||||
"upgrade_url": f"{realm.uri}{reverse('upgrade_page')}",
|
||||
"realm": realm,
|
||||
|
@ -3210,4 +3209,7 @@ def downgrade_small_realms_behind_on_payments_as_needed() -> None:
|
|||
)
|
||||
else:
|
||||
if customer_has_last_n_invoices_open(customer, 1):
|
||||
void_all_open_invoices(realm)
|
||||
# If a small realm, without an active plan, has
|
||||
# the last invoice open, void the open invoices.
|
||||
billing_session = RealmBillingSession(user=None, realm=realm)
|
||||
billing_session.void_all_open_invoices()
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -54,6 +54,7 @@ from corporate.lib.stripe import (
|
|||
catch_stripe_errors,
|
||||
compute_plan_parameters,
|
||||
customer_has_credit_card_as_default_payment_method,
|
||||
customer_has_last_n_invoices_open,
|
||||
do_change_remote_server_plan_type,
|
||||
do_deactivate_remote_server,
|
||||
downgrade_small_realms_behind_on_payments_as_needed,
|
||||
|
@ -72,7 +73,6 @@ from corporate.lib.stripe import (
|
|||
update_license_ledger_for_automanaged_plan,
|
||||
update_license_ledger_for_manual_plan,
|
||||
update_license_ledger_if_needed,
|
||||
void_all_open_invoices,
|
||||
)
|
||||
from corporate.lib.support import get_discount_for_realm, switch_realm_from_standard_to_plus_plan
|
||||
from corporate.models import (
|
||||
|
@ -3610,7 +3610,10 @@ class StripeTest(StripeTestCase):
|
|||
iago = self.example_user("iago")
|
||||
king = self.lear_user("king")
|
||||
|
||||
self.assertEqual(void_all_open_invoices(iago.realm), 0)
|
||||
voided_invoice_count = RealmBillingSession(
|
||||
user=None, realm=iago.realm
|
||||
).void_all_open_invoices()
|
||||
self.assertEqual(voided_invoice_count, 0)
|
||||
|
||||
zulip_customer = RealmBillingSession(iago).update_or_create_stripe_customer()
|
||||
lear_customer = RealmBillingSession(king).update_or_create_stripe_customer()
|
||||
|
@ -3651,7 +3654,10 @@ class StripeTest(StripeTestCase):
|
|||
)
|
||||
stripe.Invoice.finalize_invoice(stripe_invoice)
|
||||
|
||||
self.assertEqual(void_all_open_invoices(iago.realm), 1)
|
||||
voided_invoice_count = RealmBillingSession(
|
||||
user=None, realm=iago.realm
|
||||
).void_all_open_invoices()
|
||||
self.assertEqual(voided_invoice_count, 1)
|
||||
invoices = stripe.Invoice.list(customer=zulip_customer.stripe_customer_id)
|
||||
self.assert_length(invoices, 1)
|
||||
for invoice in invoices:
|
||||
|
@ -3660,11 +3666,17 @@ class StripeTest(StripeTestCase):
|
|||
lear_stripe_customer_id = lear_customer.stripe_customer_id
|
||||
lear_customer.stripe_customer_id = None
|
||||
lear_customer.save(update_fields=["stripe_customer_id"])
|
||||
self.assertEqual(void_all_open_invoices(king.realm), 0)
|
||||
voided_invoice_count = RealmBillingSession(
|
||||
user=None, realm=king.realm
|
||||
).void_all_open_invoices()
|
||||
self.assertEqual(voided_invoice_count, 0)
|
||||
|
||||
lear_customer.stripe_customer_id = lear_stripe_customer_id
|
||||
lear_customer.save(update_fields=["stripe_customer_id"])
|
||||
self.assertEqual(void_all_open_invoices(king.realm), 1)
|
||||
voided_invoice_count = RealmBillingSession(
|
||||
user=None, realm=king.realm
|
||||
).void_all_open_invoices()
|
||||
self.assertEqual(voided_invoice_count, 1)
|
||||
invoices = stripe.Invoice.list(customer=lear_customer.stripe_customer_id)
|
||||
self.assert_length(invoices, 1)
|
||||
for invoice in invoices:
|
||||
|
@ -3740,57 +3752,64 @@ class StripeTest(StripeTestCase):
|
|||
expected_plan_type: int
|
||||
plan: Optional[CustomerPlan]
|
||||
expected_plan_status: Optional[int]
|
||||
void_all_open_invoices_mock_called: bool
|
||||
expected_invoice_count: int
|
||||
email_expected_to_be_sent: bool
|
||||
|
||||
rows: List[Row] = []
|
||||
|
||||
# no stripe customer ID (excluded from query)
|
||||
realm, _, _ = create_realm(
|
||||
users_to_create=1, create_stripe_customer=False, create_plan=False
|
||||
)
|
||||
# To create local Customer object but no Stripe customer.
|
||||
billing_session = RealmBillingSession(
|
||||
user=self.example_user("iago"), realm=realm, support_session=True
|
||||
)
|
||||
billing_session.attach_discount_to_customer(Decimal(20))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, 0, False))
|
||||
|
||||
# no active paid plan or invoices (no action)
|
||||
realm, _, _ = create_realm(
|
||||
users_to_create=1, create_stripe_customer=True, create_plan=False
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, 0, False))
|
||||
|
||||
# no active plan, one unpaid invoice (will be voided, no downgrade or email)
|
||||
realm, _, _ = create_realm(
|
||||
users_to_create=1, create_stripe_customer=True, create_plan=False, num_invoices=1
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, True, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_SELF_HOSTED, None, None, 0, False))
|
||||
|
||||
# active plan, no invoices (no action)
|
||||
realm, plan, _ = create_realm(
|
||||
users_to_create=1, create_stripe_customer=True, create_plan=True
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, 0, False))
|
||||
|
||||
# active plan, only one unpaid invoice (not downgraded or voided)
|
||||
realm, plan, _ = create_realm(
|
||||
users_to_create=1, create_stripe_customer=True, create_plan=True, num_invoices=1
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, 1, False))
|
||||
|
||||
# active plan, two unpaid invoices (will be downgraded, voided and emailed)
|
||||
realm, plan, _ = create_realm(
|
||||
users_to_create=3, create_stripe_customer=True, create_plan=True, num_invoices=2
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_LIMITED, plan, CustomerPlan.ENDED, True, True))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_LIMITED, plan, CustomerPlan.ENDED, 0, True))
|
||||
|
||||
# active plan, two paid invoices (not downgraded)
|
||||
realm, plan, invoices = create_realm(
|
||||
users_to_create=1, create_stripe_customer=True, create_plan=True, num_invoices=2
|
||||
)
|
||||
for invoice in invoices:
|
||||
stripe.Invoice.pay(invoice, paid_out_of_band=True)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, 0, False))
|
||||
|
||||
# not a small realm, two unpaid invoices (not downgraded or voided)
|
||||
realm, plan, _ = create_realm(
|
||||
users_to_create=20, create_stripe_customer=True, create_plan=True, num_invoices=2
|
||||
)
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, False, False))
|
||||
rows.append(Row(realm, Realm.PLAN_TYPE_STANDARD, plan, CustomerPlan.ACTIVE, 2, False))
|
||||
|
||||
# Customer objects without a realm should be excluded from query.
|
||||
remote_server = RemoteZulipServer.objects.create(
|
||||
|
@ -3801,8 +3820,7 @@ class StripeTest(StripeTestCase):
|
|||
)
|
||||
Customer.objects.create(remote_server=remote_server, stripe_customer_id="cus_xxx")
|
||||
|
||||
with patch("corporate.lib.stripe.void_all_open_invoices") as void_all_open_invoices_mock:
|
||||
downgrade_small_realms_behind_on_payments_as_needed()
|
||||
downgrade_small_realms_behind_on_payments_as_needed()
|
||||
|
||||
from django.core.mail import outbox
|
||||
|
||||
|
@ -3812,15 +3830,12 @@ class StripeTest(StripeTestCase):
|
|||
if row.plan is not None:
|
||||
row.plan.refresh_from_db()
|
||||
self.assertEqual(row.plan.status, row.expected_plan_status)
|
||||
if row.void_all_open_invoices_mock_called:
|
||||
void_all_open_invoices_mock.assert_any_call(row.realm)
|
||||
else:
|
||||
try:
|
||||
void_all_open_invoices_mock.assert_any_call(row.realm)
|
||||
except AssertionError:
|
||||
pass
|
||||
else: # nocoverage
|
||||
raise AssertionError("void_all_open_invoices_mock should not be called")
|
||||
customer = get_customer_by_realm(row.realm)
|
||||
if customer is not None and customer.stripe_customer_id is not None:
|
||||
open_invoices = customer_has_last_n_invoices_open(
|
||||
customer, row.expected_invoice_count
|
||||
)
|
||||
self.assertTrue(open_invoices)
|
||||
|
||||
email_found = False
|
||||
for email in outbox:
|
||||
|
|
Loading…
Reference in New Issue