diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 0b1ee8d6fc..1d915cba12 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -992,6 +992,17 @@ def void_all_open_invoices(realm: Realm) -> int: return voided_invoices_count +def customer_has_last_n_invoices_open(customer: Customer, n: int) -> bool: + if customer.stripe_customer_id is None: + return False + + open_invoice_count = 0 + for invoice in stripe.Invoice.list(customer=customer.stripe_customer_id, limit=n): + if invoice.status == "open": + open_invoice_count += 1 + return open_invoice_count == n + + def downgrade_small_realms_behind_on_payments_as_needed() -> None: customers = Customer.objects.all() for customer in customers: @@ -1005,13 +1016,8 @@ def downgrade_small_realms_behind_on_payments_as_needed() -> None: if get_current_plan_by_customer(customer) is None: continue - due_invoice_count = 0 - for invoice in stripe.Invoice.list(customer=customer.stripe_customer_id, limit=2): - if invoice.status == "open": - due_invoice_count += 1 - - # Customers with only 1 overdue invoice are ignored. - if due_invoice_count < 2: + # Only customers with last 2 invoices open should be downgraded. + if not customer_has_last_n_invoices_open(customer, 2): continue # We've now decided to downgrade this customer and void all invoices, and the below will execute this. diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 5db2e09f10..b6d48bb591 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -2784,6 +2784,12 @@ class StripeTest(StripeTestCase): invoices.append(invoice) return invoices + realm_0, _, _ = create_realm( + users_to_create=1, create_stripe_customer=False, create_plan=False + ) + # To create local Customer object but no Stripe customer. + attach_discount_to_realm(realm_0, Decimal(20), acting_user=None) + realm_1, _, _ = create_realm( users_to_create=1, create_stripe_customer=True, create_plan=False ) @@ -2822,6 +2828,9 @@ class StripeTest(StripeTestCase): with patch("corporate.lib.stripe.void_all_open_invoices") as void_all_open_invoices_mock: downgrade_small_realms_behind_on_payments_as_needed() + realm_0.refresh_from_db() + self.assertEqual(realm_0.plan_type, Realm.SELF_HOSTED) + realm_1.refresh_from_db() self.assertEqual(realm_1.plan_type, Realm.SELF_HOSTED)