billing: Fix RealmAuditLog accounting for downgrade.

This commit is contained in:
Rishi Gupta
2018-08-31 21:41:58 -07:00
parent bd0487d503
commit 18b86a9499
2 changed files with 27 additions and 3 deletions

View File

@@ -299,8 +299,21 @@ def process_downgrade(user: UserProfile) -> None:
stripe_customer.account_balance = stripe_customer.account_balance + subscription_balance stripe_customer.account_balance = stripe_customer.account_balance + subscription_balance
stripe_subscription = extract_current_subscription(stripe_customer) stripe_subscription = extract_current_subscription(stripe_customer)
# Wish these two could be transaction.atomic # Wish these two could be transaction.atomic
stripe_subscription.delete() stripe_subscription = stripe_subscription.delete()
stripe_customer.save() stripe_customer.save()
with transaction.atomic():
user.realm.has_seat_based_plan = False
user.realm.save(update_fields=['has_seat_based_plan'])
RealmAuditLog.objects.create(
realm=user.realm,
acting_user=user,
event_type=RealmAuditLog.STRIPE_PLAN_CHANGED,
event_time=timestamp_to_datetime(stripe_subscription.canceled_at),
extra_data=ujson.dumps({'plan': None, 'quantity': stripe_subscription.quantity}))
# Doing this last, since it results in user-visible confirmation (via
# product changes) that the downgrade succeeded.
# Keeping it out of the transaction.atomic block because it will
# eventually have a lot of stuff going on.
do_change_plan_type(user, Realm.LIMITED) do_change_plan_type(user, Realm.LIMITED)
## Process RealmAuditLog ## Process RealmAuditLog
@@ -384,6 +397,9 @@ def run_billing_processor_one_step(processor: BillingProcessor) -> bool:
process_billing_log_entry(processor, log_row) process_billing_log_entry(processor, log_row)
return True return True
except Exception as e: except Exception as e:
# Possible errors include processing subscription quantity entries
# after downgrade, since the downgrade code doesn't check that
# billing processor is up to date
billing_logger.error("Error on log_row.realm=%s, event_type=%s, log_row.id=%s, " billing_logger.error("Error on log_row.realm=%s, event_type=%s, log_row.id=%s, "
"processor.id=%s, processor.realm=%s" % ( "processor.id=%s, processor.realm=%s" % (
processor.log_row.realm.string_id, processor.log_row.event_type, processor.log_row.realm.string_id, processor.log_row.event_type,

View File

@@ -405,17 +405,25 @@ class StripeTest(ZulipTestCase):
def test_downgrade(self, mock_retrieve_customer: mock.Mock, mock_upcoming_invoice: mock.Mock, def test_downgrade(self, mock_retrieve_customer: mock.Mock, mock_upcoming_invoice: mock.Mock,
mock_save_customer: mock.Mock, mock_delete_subscription: mock.Mock) -> None: mock_save_customer: mock.Mock, mock_delete_subscription: mock.Mock) -> None:
realm = get_realm('zulip') realm = get_realm('zulip')
realm.has_seat_based_plan = True
realm.plan_type = Realm.PREMIUM realm.plan_type = Realm.PREMIUM
realm.save(update_fields=['plan_type']) realm.save(update_fields=['has_seat_based_plan', 'plan_type'])
Customer.objects.create( Customer.objects.create(
realm=realm, stripe_customer_id=self.stripe_customer_id, has_billing_relationship=True) realm=realm, stripe_customer_id=self.stripe_customer_id, has_billing_relationship=True)
self.login(self.example_email('iago')) user = self.example_user('iago')
self.login(user.email)
response = self.client_post("/json/billing/downgrade", {}) response = self.client_post("/json/billing/downgrade", {})
self.assert_json_success(response) self.assert_json_success(response)
mock_delete_subscription.assert_called() mock_delete_subscription.assert_called()
mock_save_customer.assert_called() mock_save_customer.assert_called()
realm = get_realm('zulip') realm = get_realm('zulip')
self.assertFalse(realm.has_seat_based_plan)
audit_log_entries = list(RealmAuditLog.objects.filter(acting_user=user)
.values_list('event_type', flat=True).order_by('id'))
# TODO: once we have proper mocks, test for event_time and extra_data in STRIPE_PLAN_CHANGED
self.assertEqual(audit_log_entries, [RealmAuditLog.STRIPE_PLAN_CHANGED,
RealmAuditLog.REALM_PLAN_TYPE_CHANGED])
self.assertEqual(realm.plan_type, Realm.LIMITED) self.assertEqual(realm.plan_type, Realm.LIMITED)
@mock.patch("stripe.Customer.save") @mock.patch("stripe.Customer.save")