From 8ec91fc42d92fa4d15207a508377b8d03c7ee2ba Mon Sep 17 00:00:00 2001 From: Rishi Gupta Date: Tue, 11 Dec 2018 22:47:53 -0800 Subject: [PATCH] billing: Remove process_downgrade. --- corporate/lib/stripe.py | 56 +--------- .../downgrade:Customer.create.1.json | Bin 1581 -> 0 bytes .../downgrade:Customer.retrieve.1.json | Bin 4880 -> 0 bytes .../downgrade:Customer.retrieve.2.json | Bin 4884 -> 0 bytes .../downgrade:Customer.retrieve.3.json | Bin 2222 -> 0 bytes .../downgrade:Customer.save.1.json | Bin 1587 -> 0 bytes .../downgrade:Invoice.upcoming.1.json | Bin 6226 -> 0 bytes .../downgrade:Subscription.create.1.json | Bin 2160 -> 0 bytes .../downgrade:Subscription.delete.1.json | Bin 2178 -> 0 bytes .../downgrade:Subscription.retrieve.1.json | Bin 2178 -> 0 bytes .../downgrade:Subscription.save.1.json | Bin 2164 -> 0 bytes .../downgrade:Token.create.1.json | Bin 826 -> 0 bytes ...ade_with_money_owed:Customer.create.1.json | Bin 1581 -> 0 bytes ...e_with_money_owed:Customer.retrieve.1.json | Bin 4880 -> 0 bytes ...e_with_money_owed:Customer.retrieve.2.json | Bin 4880 -> 0 bytes ...e_with_money_owed:Customer.retrieve.3.json | Bin 2217 -> 0 bytes ...grade_with_money_owed:Customer.save.1.json | Bin 4894 -> 0 bytes ...de_with_money_owed:Invoice.upcoming.1.json | Bin 4205 -> 0 bytes ..._with_money_owed:InvoiceItem.create.1.json | Bin 459 -> 0 bytes ...with_money_owed:Subscription.create.1.json | Bin 2160 -> 0 bytes ...with_money_owed:Subscription.delete.1.json | Bin 2174 -> 0 bytes ...th_money_owed:Subscription.retrieve.1.json | Bin 2174 -> 0 bytes ...ngrade_with_money_owed:Token.create.1.json | Bin 826 -> 0 bytes ...ith_no_subscription:Customer.create.1.json | Bin 780 -> 0 bytes ...h_no_subscription:Customer.retrieve.1.json | Bin 780 -> 0 bytes corporate/tests/test_stripe.py | 104 +----------------- corporate/views.py | 2 +- 27 files changed, 7 insertions(+), 155 deletions(-) delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Customer.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Customer.retrieve.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Customer.retrieve.2.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Customer.retrieve.3.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Customer.save.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Invoice.upcoming.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Subscription.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Subscription.delete.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Subscription.retrieve.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Subscription.save.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade:Token.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.retrieve.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.retrieve.2.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.retrieve.3.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.save.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Invoice.upcoming.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:InvoiceItem.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.delete.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.retrieve.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_money_owed:Token.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_no_subscription:Customer.create.1.json delete mode 100644 corporate/tests/stripe_fixtures/downgrade_with_no_subscription:Customer.retrieve.1.json diff --git a/corporate/lib/stripe.py b/corporate/lib/stripe.py index 87c7928ceb..882e16f00a 100644 --- a/corporate/lib/stripe.py +++ b/corporate/lib/stripe.py @@ -103,29 +103,6 @@ def stripe_get_customer(stripe_customer_id: str) -> stripe.Customer: def stripe_get_upcoming_invoice(stripe_customer_id: str) -> stripe.Invoice: return stripe.Invoice.upcoming(customer=stripe_customer_id) -@catch_stripe_errors -def stripe_get_invoice_preview_for_downgrade( - stripe_customer_id: str, stripe_subscription_id: str, - stripe_subscriptionitem_id: str) -> stripe.Invoice: - return stripe.Invoice.upcoming( - customer=stripe_customer_id, subscription=stripe_subscription_id, - subscription_items=[{'id': stripe_subscriptionitem_id, 'quantity': 0}]) - -def preview_invoice_total_for_downgrade(stripe_customer: stripe.Customer) -> int: - stripe_subscription = extract_current_subscription(stripe_customer) - if stripe_subscription is None: - # Most likely situation is: user A goes to billing page, user B - # cancels subscription, user A clicks on "downgrade" or something - # else that calls this function. - billing_logger.error("Trying to extract subscription item that doesn't exist, for Stripe customer %s" - % (stripe_customer.id,)) - raise BillingError('downgrade without subscription', BillingError.TRY_RELOADING) - for item in stripe_subscription['items']: - # There should only be one item, but we can't index into stripe_subscription['items'] - stripe_subscriptionitem_id = item.id - return stripe_get_invoice_preview_for_downgrade( - stripe_customer.id, stripe_subscription.id, stripe_subscriptionitem_id).total - # This allows us to access /billing in tests without having to mock the # whole invoice object def upcoming_invoice_total(stripe_customer_id: str) -> int: @@ -289,34 +266,5 @@ def attach_discount_to_realm(user: UserProfile, percent_off: int) -> None: else: do_replace_coupon(user, coupon) -@catch_stripe_errors -def process_downgrade(user: UserProfile) -> None: - # This is no longer implementing our intended policy. Until that is - # resolved we should use the Stripe dashboard to downgrade. - assert(settings.TEST_SUITE) - stripe_customer = stripe_get_customer( - Customer.objects.filter(realm=user.realm).first().stripe_customer_id) - subscription_balance = preview_invoice_total_for_downgrade(stripe_customer) - # If subscription_balance > 0, they owe us money. This is likely due to - # people they added in the last day, so we can just forgive it. - # Stripe automatically forgives it when we delete the subscription, so nothing we need to do there. - if subscription_balance < 0: - stripe_customer.account_balance = stripe_customer.account_balance + subscription_balance - stripe_subscription = extract_current_subscription(stripe_customer) - # Wish these two could be transaction.atomic - stripe_subscription = stripe.Subscription.delete(stripe_subscription) - stripe.Customer.save(stripe_customer) - 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, Realm.LIMITED) +def process_downgrade(user: UserProfile) -> None: # nocoverage + pass diff --git a/corporate/tests/stripe_fixtures/downgrade:Customer.create.1.json b/corporate/tests/stripe_fixtures/downgrade:Customer.create.1.json deleted file mode 100644 index e244e1335ad6bd135e82acfa3e98fcddfa54a9f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1581 zcmb_cOK%e~5We?Utb78gBF_^iDwP8gFHx045wa%U*=DHMUTklawCaDyR#gI&vb_m%1saS(@wS?tvs^iZfOY&oo0e}-^viqAvL?#KeXpsjUfrbU z5;w22Gx@}O%mP~=%{~OT_FZy(qij1k5T_zk?~hq!E@U>A6w1zFLtC~?+^1QsX@jk; zxjF};Ee5C|Koma2ip846lId0skp|Db&z#p?l+@tE@_BN&?PP0BSVz@@ha_XN ze22hOG@mZUnkEhky3SErq+xImOIPNQVMNoLdKfF-@0!9mzq}!TUEEBl@E6PZaz4fx z??oE(>=4uDW{Qg%c*xu^>sdM4gh*fv7wjDxx(7Z8GGebSw~Z$z7w0j(KZjb0HTYOR zceJW8^8>ynU(y!A2GFt`l39l6UAiKZ&1_QE((|t;mt#^kGR7a)+4rmc?RsHR{{zKz kn`1ivO!0`(o@jurB-7YjNX}anncSe`Ymo|GD*br&2SzKiEdT%j diff --git a/corporate/tests/stripe_fixtures/downgrade:Customer.retrieve.1.json b/corporate/tests/stripe_fixtures/downgrade:Customer.retrieve.1.json deleted file mode 100644 index 1436a0a95e7fae6dde8e94b4a3c735061eaef011..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4880 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 diff --git a/corporate/tests/stripe_fixtures/downgrade:Subscription.create.1.json b/corporate/tests/stripe_fixtures/downgrade:Subscription.create.1.json deleted file mode 100644 index 705c1038cfe8a01b69b5784458d9803877b01d71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2160 zcma)8O^*{X488BKsB%W^R_vjRgb<<}kbp`HClHF2Had8xy0*u&ImQKtc+E0uU?1aA^vP+JJU43y=kc6zPOjbtFxwEKVnd)f&vD&}0LFY9FS8)<>LEPNLs-Yvq=( z<=(+IAKW#hOygS+5Z8LH#NkG;OzTP-N6X+s&77rwJdi#yK)JZ~+o3GEhIE$Fo#Xe<<}BNR zqxS3#Fzh;5tz64Ch80pXHwZItBPZ_n^ZTA~+}qsrWB=1QH1rVkyVywj&QP$ zG}(YCC*{bXFU0oUFfPFf@r}qX?Wl3VF*Pi;i@gWI(CkP)z3EL!Dw}L3CiY)4VtoNM zroMC-PKbt_Q*xo*MLTp*C=G{h#lc42g=9t_q}Y-qsDU9IvcCrpFXJ*!B{lEyb^|11 Ln?0EC&UgO+|5aE> diff --git a/corporate/tests/stripe_fixtures/downgrade:Subscription.delete.1.json b/corporate/tests/stripe_fixtures/downgrade:Subscription.delete.1.json deleted file mode 100644 index 6d51c37e702a77568813f06dfa473647a60e9054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2178 zcma)8OOMkq5WerPsC-83mfgcH5<-Y_KmsZ)oIuE$I&N!loM4Zuma6}qvE#g&hv=ov zygW1C_c=Y^EEWvPQY!(d3d6Tj@={tM4YJn@MdxE2f~}6|H|QOWCPV8?c*no2pFh9*^zr+L6@@LUQ8PF>?hPus4ydQelvQ$LYX@XI1@MoPX-9|^dmkkk~Mn7#J+-) z64NPa?2r_aON3Pad4a5fBv9xq?kI(o8cfq@vVlN#2)jY+Bj%|l zQE|7kaz_Yr?_k$mUmKFAB|g%=+J0J+awqmO54nsOMkp!lG(>UpI|%`iOX3ttc$f9F ztCfYZ_^P-+zCUdKt{EWxyZ>#dyuY*e3z`mOFO>5D?Q|Cjk z1>?4b&8NXdt3cNp4k8;nB~pXqsf-xY)g_IiE1{ugm!*F^kV-K?xqNlsPTV6cImeWi z3SQ#(&nlPg!O?*B3L4g~Rx4NYjbVW_&K1VY+sKOB!}4|@9rw0tdNZPb>Lw1z#z?{j z~GCCAjb*c$r)g0bC^fCk%}6IExk z9XYZ85)vCqs4@-3!*E76-ClHyj!LEC&`=(1;;l$#^g)iSdV!PRf+85gvGa%F X?q$4^Q(M)Wy}k#Mv92G?HMdxE2f~}6|H|QOWCPV8?c*no2pFh9*^zr+L6@@LUQ8PF>?hPus4ydQelvQ$LYX@XI1@MoPX-9|^dmkkk~Mn7#J+-) z64NPa?2r_aON3Pad4a5fBv9xq?kI(o8cfq@vVlN#2)jY+Bj%|l zQE|7kaz_Yr?_k$mUmKFAB|g%=+J0J+awqmO54nsOMkp!lG(>UpI|%`iOX3ttc$f9F ztCfYZ_^P-+zCUdKt{EWxyZ>#dyuY*e3z`mOFO>5D?Q|Cjk z1>?4b&8NXdt3cNp4k8;nB~pXqsf-xY)g_IiE1{ugm!*F^kV-K?xqNlsPTV6cImeWi z3SQ#(&nlPg!O?*B3L4g~Rx4NYjbVW_&K1VY+sKOB!}4|@9rw0tdNZPb>Lw1z#z?{j z~GCCAjb*c$r)g0bC^fCk%}6IExk z9XYZ85)vCqs4@-3!*E76-ClHyj!LEC&`=(1;;l$#^g)iSdV!PRf+85gvGa%F X?q$4^Q(M)Wy}k#Mv92G?HS}U_56BApo8_B_ARni#& zS|3;*#Kl2q3NT_@SvoPlYdKtHL*UhM%#who^_H62C3#eK!v(X#@M(m2fUCI(VwQ@3mmuy0Yas zxM&sVR=q)9L$^d~a6HrzV_H|zI9dj0YUV8cXbG4nRE;&wN`?McVI%}s9x^iS2q2H6lvSdSc8WqIZZ zDC>xm4ajm*kPP}#Y~K#!BAk%li15;u8kZbX!(uzzdk_rGjs(=3-jt}a$!2n5|0N{W zmr!Hsi-+NaY{)q!7}{R6MF*wQaHua2Hu63sGx{LMmL@?D3}K)BMYwwz7ji19d6&0a NAQ{{2!F+SN`3H^2Sp)z8 diff --git a/corporate/tests/stripe_fixtures/downgrade:Token.create.1.json b/corporate/tests/stripe_fixtures/downgrade:Token.create.1.json deleted file mode 100644 index 60d47536e7c17c03b5152a712f27efc6362cb64e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 826 zcmaJe?5488L!s+`eO3a8zWw!@@-X^0(msZ3&4k2b9m7p)-v`{F>^Rw2?$YCk`} z7kfWS5~aZzb(`$NJ^X<&4!zGcaTlWM6*R3$tMMRRx0YQ*EG;pbWWm7bhq#g~PK0M^ z4Z4I2OgzH)A}rSh>WzR~8?R_C;j)0RZCMT!dFg*SwWCH@r*ejjA$O(k%7K-~QSaoT zFV%m%FV4ZOzQSWV3l-_S1kbboBJsP)tJ-oA!qbx{iFF7rgtBznN7rOO(KU`Ni=^k5 zx2O5z!^izyDreFwS+~{%q(HPoL)<4QI+1bbgW5>&m!sFIX4x$3>nmh1zz~@Chm+D^ zOCDX#4af#K*MsZT7wV`p?$-&<^#(2NfT^~5;45m;B-0G*6*-kI2`%Txk)=2Bi=^Tv zj93aLz2H=M)zMhszV1tvw8g46B6tm@$5{L;G>SX6;;xCd&e~P$W9L{mI*fh*S9axJ diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.create.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.create.1.json deleted file mode 100644 index e244e1335ad6bd135e82acfa3e98fcddfa54a9f7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1581 zcmb_cOK%e~5We?Utb78gBF_^iDwP8gFHx045wa%U*=DHMUTklawCaDyR#gI&vb_m%1saS(@wS?tvs^iZfOY&oo0e}-^viqAvL?#KeXpsjUfrbU z5;w22Gx@}O%mP~=%{~OT_FZy(qij1k5T_zk?~hq!E@U>A6w1zFLtC~?+^1QsX@jk; zxjF};Ee5C|Koma2ip846lId0skp|Db&z#p?l+@tE@_BN&?PP0BSVz@@ha_XN ze22hOG@mZUnkEhky3SErq+xImOIPNQVMNoLdKfF-@0!9mzq}!TUEEBl@E6PZaz4fx z??oE(>=4uDW{Qg%c*xu^>sdM4gh*fv7wjDxx(7Z8GGebSw~Z$z7w0j(KZjb0HTYOR zceJW8^8>ynU(y!A2GFt`l39l6UAiKZ&1_QE((|t;mt#^kGR7a)+4rmc?RsHR{{zKz kn`1ivO!0`(o@jurB-7YjNX}anncSe`Ymo|GD*br&2SzKiEdT%j diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.retrieve.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.retrieve.1.json deleted file mode 100644 index 1436a0a95e7fae6dde8e94b4a3c735061eaef011..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4880 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 diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.save.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Customer.save.1.json deleted file mode 100644 index b8c8d9814d2aa09bc4fcad53def37475a97c736c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4894 zcmc&&OK%%D5Wf3Y2t2hw5L>nmPJyBg>K+QTbrB~$BnSwKOG&Kv6_P7hHvHc^GbC4B ziMvuy^&wU`A7_U1_-5pni`k3`DVwf#d?`v%OT~VeUA@6`X_RoPfd9|0;*S?}#;98E zK*Bm(Fgh&MN_3^;w&@Hhc>x`$y(o;bmP_sE!e2t_m6miV6{8D=5T9xdW@ir$tlL9e zscSVy!=LIxnVH+DnN{LV7RKd9$wvgY!e%vHgRt((5?_!wW6^Y8^sHM{c!NKOMnRT0 zxiq52II_RB74a4YJ-qm#KkTe)DrFEXJInvP|M18A-+%l3>m6iZ-lMiMZ>ZSSqSBI= z!n(!q0qUvcRa3hS9N&cZ;@M6Kw03=U6HiyVUMtfYT|0DR;6mcWs;dh?A_OX>&@OtX z>8VnfV(5OXp+raYlP#_n*YSR(oG647p!wxBcnZ(Lgs}I8z``&lf}7<(N|K*wFrK>R zQPuicINj77^lcjS2{R3eCY`KMP)grAMUPyG(ke6s$5tBM;zhLg+?BeWefODu-Kp*O zWPughQu=~Yx~VFmOT44Sy7`$dzLiboFNB!nGs<+mZM0OpHEN}wPzMD)B%GCctE#5J z0PG!2qvWd>+O@2>x73%4KeUc2ikxD72$I;vvZI&Sjc!|?+SnY-x3vWqLSKgreIeVk za60Ise8&$V(_KHnPIvw>G}|FZupt8s#K$Z!5NDX+P(JQLjuGf~jupHhrUp7 zgdIpY&JZNduta};#1tesk1afOgfU2XK5Gy>#~dU``y#LhosTgH36oCPvj|BhnFJB9 z)Bs?UVo`=Pn524qU4cC=0Qbgh(#{9^GnDxO#_RchGaUwj2+px zv+b5Wcb(U$ZCh&TNphuteiTe@;=|bX_$F|FsY~cSPxTvN){2V`9D|!ul;tk4ZVKgc zCrg0Ua?>F73^xeU3T2GzM#+Uk%epDJqAA&P+yJHu0FG)JSyiG2T38Oui3`V-_aFu4*^i{L65uO;cTRJgLL0RX`yrUK6y<5x3J3gvufatkPgNv$eihD@bPJ1(@7*ZF-?3@9g)P)CFHq%B1^uElK#wikDF~Fx z%<6pr@!`e*@Vn;b>6h*D`Tq9Zz5Q?$CC~>uo-$iOj6?jMc}T<@GF56fyQ8E4ur--p zkk?v1MwS+J-GNsbZ}w$CInE$j(-fWG3GpLEVexM9pxnp1_9>^+X=ShjhHV$!XCww% zrwwcj11r8=HKsaMHqK}u{Xlp~4C8jG0J+<>ScMQ_AZH8T$HK-H-Ajg?KVsiH0bE00 zw{8#P!_wwKwHa<081VDWYYtN`n90w&Al#=ZnRr2#oqIu+Ouy7qYIFNjdi3Lz=Sff| zN^`6vlX;z5OiG7k2nN1Lk5J1Qq%uw?=Mu?DGM*%lz|yB^W128}X`U)3GBHIDr-&iR z))6JJ|3C)5&ok{fO#sZDY<+;7ZhH7r3e1~{c28?M-{Nd?U%~G)9nwu$ynssf?7*CO zN*2JYx5-Eg?)&X*$bUf;?V>QS$N#q%57yv*JI43vdetQPr8nal-@^V0f0UwCH~=nQ GFa8BvdHy;8 diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Invoice.upcoming.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Invoice.upcoming.1.json deleted file mode 100644 index 96b2e26d1686d9147bc9a04a25b235349eee0726..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4205 zcmeHKTW=gS6n@XIuq=c?YP%B>l`T?*s-_P}Ku~F43dr)#csHgVd+=q+HtK)p9NRNv z?k~zmdK9}#@?#>9Iyf;C6)&)VoAs3IErg>112QE9HS2QbnF16H~`ob_M z*>W$9W@~}MbWkeE@?P|VXDz-oOG{LN^4DBB5znrqQb{yzZ@AqE#)CIK$Zfe&J6axM zR&ZxD+^1n^O^^2?Kiw=s1 zg+H?+=BSi)M8*h=u^A8Rt&y!@5*cJUjF5ec>##`dzdb0g9ea z4;Jh}SZNT=DIQrph$)b2{3I5H%z186;LM&vQ$9QZ9`@z()`JB=^)|%RnS}Dq#)=K# zmIcQ*B|7ILG3nv*vVsF>l=F|d_D1x0mx5U=Xz7di!MPT4&&S-Ib)S8K2Tmdjm?^+$ z7P~SRx6ik$ch}1&*Y0Kgh}3|iEmx?1_e%p7&uzCZ#7S*bFBls zUWuJS5TjD@25Y*I5Q6_k+b*A6{w(~ftKqhmq43R!^(m8Jk-C`&(*U^oBc_4=i@>Lieh?yK!8j-guY>#`WVOiwHIjRg3NU zBKIG+7Ht9FXj2Dirbijah1+5*&41;TEdC>nJ2b1O(38zvs8`oxBeJ<1h&WV%*&yWh%m|y7Q-4-5B%MfkhZ~Tdu2T@6Y}M DRX$p@ diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:InvoiceItem.create.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:InvoiceItem.create.1.json deleted file mode 100644 index e5b41ca2ff79a53521e8b740129c1ad85d5d93be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 459 zcmZ9JJ!`};42Jjoir~3J)3Mt@I~4i>9lIQgt%8a_a<+VMg!}K6;*e|V&5x}2m0r(X z-x~<2MmCTAaIfc1nXNfvv=^bK7H3viI0eiKZ7s+5kI%Qm>&w^kle7)(jRVF_YLjzV zY^I4RMJdMW{UYe7SoMa{YZK>+3n!-*M0*ESYv-YPQ9LKI+PNuuI%7yq**Eyoh)k>r56euYj(2Had8xy0*u&ImQKtc+E0uU?1aA^vP+JJU43y=kc6zPOjbtFxwEKVnd)f&vD&}0LFY9FS8)<>LEPNLs-Yvq=( z<=(+IAKW#hOygS+5Z8LH#NkG;OzTP-N6X+s&77rwJdi#yK)JZ~+o3GEhIE$Fo#Xe<<}BNR zqxS3#Fzh;5tz64Ch80pXHwZItBPZ_n^ZTA~+}qsrWB=1QH1rVkyVywj&QP$ zG}(YCC*{bXFU0oUFfPFf@r}qX?Wl3VF*Pi;i@gWI(CkP)z3EL!Dw}L3CiY)4VtoNM zroMC-PKbt_Q*xo*MLTp*C=G{h#lc42g=9t_q}Y-qsDU9IvcCrpFXJ*!B{lEyb^|11 Ln?0EC&UgO+|5aE> diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.delete.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.delete.1.json deleted file mode 100644 index 93a4b8419b62aceed2a78d3da83ca10b04c50fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2174 zcma)8O^?$s5WVlOsC-83R_vjRgb<<}kbp`HClIoxj@ud>C)nevWwrmEvE!sk<3#k* zX1+W#?>(naH;V;>y4Fens><-4l)RQ!NQ3P4!g#G;gpjS$TA4kWm^gslOAa2Zl5zxS zeP;b2F3v(zfDwnvQpWPG{lq9i4yid{I7d|lm!_bo9cU+e0kWWwOtzJPG9+A=_103; z!=m#s4#DP*@Qy~4p>-y_<6kz9pWl7@`2EA0=2$kP7I1dl8&sMXUQ%SYsY33+Yviu- zw8eo(IZ;tg-$t`f464G6%=vBhe1)=f6!1jg5IhAS(9@6jv`E(I5fl3gPD)ItsN5kb zq?8D${?l^+DA}v+FDb$>p-CsKsw06yXK_alo~ zSEu*K?cdGnVdEaJ!VF4#1RZLeCD^=|w~Sa0CegH>vMRz1oi4fsOaMZC8zp`lXhsKP zJe@u$dx0#XGVs;5Dtv=y_!(!rf4KiD@!PsS4eOwiHn6WHeb-ua@YMO(Yr(i(Wy@)B z(JIibhJ(m*r$lOSJl7Fpy1JxsbR~4u?6dTb2T~~pC|8$x2b3jOPG_mybNv3<>~(ryCeCGwl^oK-e!AZV*e#0HWpB0 z8cT=ajA+O?Cl|WA=nl;arQy)EIM~D+k<93W6j}4~Cb0!QFoaX@&%)iycpayvnzwp; O2P9)#KbUWxZ~g%R=2=Go diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.retrieve.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Subscription.retrieve.1.json deleted file mode 100644 index 93a4b8419b62aceed2a78d3da83ca10b04c50fa2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2174 zcma)8O^?$s5WVlOsC-83R_vjRgb<<}kbp`HClIoxj@ud>C)nevWwrmEvE!sk<3#k* zX1+W#?>(naH;V;>y4Fens><-4l)RQ!NQ3P4!g#G;gpjS$TA4kWm^gslOAa2Zl5zxS zeP;b2F3v(zfDwnvQpWPG{lq9i4yid{I7d|lm!_bo9cU+e0kWWwOtzJPG9+A=_103; z!=m#s4#DP*@Qy~4p>-y_<6kz9pWl7@`2EA0=2$kP7I1dl8&sMXUQ%SYsY33+Yviu- zw8eo(IZ;tg-$t`f464G6%=vBhe1)=f6!1jg5IhAS(9@6jv`E(I5fl3gPD)ItsN5kb zq?8D${?l^+DA}v+FDb$>p-CsKsw06yXK_alo~ zSEu*K?cdGnVdEaJ!VF4#1RZLeCD^=|w~Sa0CegH>vMRz1oi4fsOaMZC8zp`lXhsKP zJe@u$dx0#XGVs;5Dtv=y_!(!rf4KiD@!PsS4eOwiHn6WHeb-ua@YMO(Yr(i(Wy@)B z(JIibhJ(m*r$lOSJl7Fpy1JxsbR~4u?6dTb2T~~pC|8$x2b3jOPG_mybNv3<>~(ryCeCGwl^oK-e!AZV*e#0HWpB0 z8cT=ajA+O?Cl|WA=nl;arQy)EIM~D+k<93W6j}4~Cb0!QFoaX@&%)iycpayvnzwp; O2P9)#KbUWxZ~g%R=2=Go diff --git a/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Token.create.1.json b/corporate/tests/stripe_fixtures/downgrade_with_money_owed:Token.create.1.json deleted file mode 100644 index 60d47536e7c17c03b5152a712f27efc6362cb64e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 826 zcmaJe?5488L!s+`eO3a8zWw!@@-X^0(msZ3&4k2b9m7p)-v`{F>^Rw2?$YCk`} z7kfWS5~aZzb(`$NJ^X<&4!zGcaTlWM6*R3$tMMRRx0YQ*EG;pbWWm7bhq#g~PK0M^ z4Z4I2OgzH)A}rSh>WzR~8?R_C;j)0RZCMT!dFg*SwWCH@r*ejjA$O(k%7K-~QSaoT zFV%m%FV4ZOzQSWV3l-_S1kbboBJsP)tJ-oA!qbx{iFF7rgtBznN7rOO(KU`Ni=^k5 zx2O5z!^izyDreFwS+~{%q(HPoL)<4QI+1bbgW5>&m!sFIX4x$3>nmh1zz~@Chm+D^ zOCDX#4af#K*MsZT7wV`p?$-&<^#(2NfT^~5;45m;B-0G*6*-kI2`%Txk)=2Bi=^Tv zj93aLz2H=M)zMhszV1tvw8g46B6tm@$5{L;G>SX6;;xCd&e~P$W9L{mI*fh*S9axJ diff --git a/corporate/tests/stripe_fixtures/downgrade_with_no_subscription:Customer.create.1.json b/corporate/tests/stripe_fixtures/downgrade_with_no_subscription:Customer.create.1.json deleted file mode 100644 index 49d7f16c2c88a7ece7841411d8fc46c42ba95b7b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 780 zcmd6l$xg#C5Qgu4iYORW(RyqX}XI3XH`1rdnJjT{?t-4WE~b@hO4m92)t}5fhclT7xY_6@<6Z8J~24eRw!RMb@$>-<__e_59-@)_=Q6lx%qY6w2;Z7#f)R+j=w zytRy8Jioj?-90|6?^g`J9HOSTBvEt@TiT_}oHd3O^{8!=EI5K{4A4LT7WZrm8^gXR zEy|?QcoKbZscVL+Is1^eu$xbmY5fEitVUOG{Fb_o%m+s%yNZGSIBl5y_MTetpe=Zz ztviEc`8Y$754k_9!3I!bv@qW*tI=iu=KXR$0{Qgy$Fup!GA-icX1cY1f_O$Wfd<$K XGOayzcGN_Vj#`p1{Cmmz*RW(RyqX}XI3XH`1rdnJjT{?t-4WE~b@hO4m92)t}5fhclT7xY_6@<6Z8J~24eRw!RMb@$>-<__e_59-@)_=Q6lx%qY6w2;Z7#f)R+j=w zytRy8Jioj?-90|6?^g`J9HOSTBvEt@TiT_}oHd3O^{8!=EI5K{4A4LT7WZrm8^gXR zEy|?QcoKbZscVL+Is1^eu$xbmY5fEitVUOG{Fb_o%m+s%yNZGSIBl5y_MTetpe=Zz ztviEc`8Y$754k_9!3I!bv@qW*tI=iu=KXR$0{Qgy$Fup!GA-icX1cY1f_O$Wfd<$K XGOayzcGN_Vj#`p1{Cmmz* None: @@ -673,86 +656,6 @@ class StripeTest(ZulipTestCase): upcoming_invoice = stripe.Invoice.upcoming(customer=stripe_customer.id) self.assertEqual(upcoming_invoice.amount_due, get_seat_count(user.realm) * 80 * 75) - # Tests upgrade followed by immediate downgrade. Doesn't test the - # calculations for how much credit they should get if they had the - # subscription for more than 0 time. - @mock_stripe(tested_timestamp_fields=["canceled_at"]) - def test_downgrade(self, *mocks: Mock) -> None: - user = self.example_user('iago') - self.login(user.email) - self.upgrade() - realm = get_realm('zulip') - self.assertEqual(realm.has_seat_based_plan, True) - self.assertEqual(realm.plan_type, Realm.STANDARD) - RealmAuditLog.objects.filter(realm=realm).delete() - - stripe_customer = stripe_get_customer(Customer.objects.get(realm=user.realm).stripe_customer_id) - self.assertEqual(stripe_customer.account_balance, 0) - # Change subscription in Stripe, but don't pay for it - stripe_subscription = extract_current_subscription(stripe_customer) - stripe_subscription.quantity = 123 - stripe.Subscription.save(stripe_subscription) - - response = self.client_post("/json/billing/downgrade", {}) - self.assert_json_success(response) - stripe_customer = stripe_get_customer(stripe_customer.id) - self.assertEqual(stripe_customer.account_balance, self.seat_count * -8000) - self.assertIsNone(extract_current_subscription(stripe_customer)) - stripe_subscription = stripe.Subscription.retrieve(stripe_subscription.id) - self.assertEqual(stripe_subscription.status, "canceled") - - realm = get_realm('zulip') - self.assertEqual(realm.plan_type, Realm.LIMITED) - self.assertFalse(realm.has_seat_based_plan) - - audit_log_entries = list(RealmAuditLog.objects.filter(realm=realm) - .values_list('event_type', 'event_time', - 'requires_billing_update').order_by('id')) - self.assertEqual(audit_log_entries, [ - (RealmAuditLog.STRIPE_PLAN_CHANGED, - timestamp_to_datetime(stripe_subscription.canceled_at), False), - (RealmAuditLog.REALM_PLAN_TYPE_CHANGED, Kandra(), False), - ]) - self.assertEqual(ujson.loads(RealmAuditLog.objects.filter( - event_type=RealmAuditLog.STRIPE_PLAN_CHANGED).values_list('extra_data', flat=True).first()), - {'plan': None, 'quantity': 123}) - - @mock_stripe() - def test_downgrade_with_no_subscription(self, *mocks: Mock) -> None: - user = self.example_user("iago") - do_create_customer(user) - self.login(user.email) - with patch("stripe.Customer.save") as mock_save_customer: - with patch("corporate.lib.stripe.billing_logger.error"): - response = self.client_post("/json/billing/downgrade", {}) - mock_save_customer.assert_not_called() - self.assert_json_error_contains(response, 'Please reload') - self.assertEqual(ujson.loads(response.content)['error_description'], 'downgrade without subscription') - - @mock_stripe() - def test_downgrade_with_money_owed(self, *mocks: Mock) -> None: - user = self.example_user('iago') - self.login(user.email) - self.upgrade() - stripe_customer = stripe_get_customer(Customer.objects.get(realm=user.realm).stripe_customer_id) - self.assertEqual(stripe_customer.account_balance, 0) - stripe_subscription = extract_current_subscription(stripe_customer) - # Create a situation where customer net owes us money - stripe.InvoiceItem.create( - amount=100000, - currency='usd', - customer=stripe_customer, - subscription=stripe_subscription) - - response = self.client_post("/json/billing/downgrade", {}) - self.assert_json_success(response) - stripe_customer = stripe_get_customer(stripe_customer.id) - # Check that positive balance was forgiven - self.assertEqual(stripe_customer.account_balance, 0) - self.assertIsNone(extract_current_subscription(stripe_customer)) - stripe_subscription = stripe.Subscription.retrieve(stripe_subscription.id) - self.assertEqual(stripe_subscription.status, "canceled") - @mock_stripe() def test_replace_payment_source(self, *mocks: Mock) -> None: user = self.example_user("hamlet") @@ -856,6 +759,7 @@ class RequiresBillingAccessTest(ZulipTestCase): params = [ ("/json/billing/sources/change", "do_replace_payment_source", {'stripe_token': ujson.dumps('token')}), + # TODO: second argument should be something like "process_downgrade" ("/json/billing/downgrade", "process_downgrade", {}), ] # type: List[Tuple[str, str, Dict[str, Any]]] diff --git a/corporate/views.py b/corporate/views.py index 382a4651a1..5d8bf90d7b 100644 --- a/corporate/views.py +++ b/corporate/views.py @@ -194,7 +194,7 @@ def billing_home(request: HttpRequest) -> HttpResponse: return render(request, 'corporate/billing.html', context=context) @require_billing_access -def downgrade(request: HttpRequest, user: UserProfile) -> HttpResponse: +def downgrade(request: HttpRequest, user: UserProfile) -> HttpResponse: # nocoverage try: process_downgrade(user) except BillingError as e: