Skip to content

Commit

Permalink
Update Refund.reason Enum, fix Refund.status definition (dj-stripe#1076)
Browse files Browse the repository at this point in the history
* Included stripe "expired_uncaptured_charge" refund reason to RefundReason enum
* Fixed refund.status (was the wrong enum)
  • Loading branch information
jcobacho authored and therefromhere committed Dec 13, 2019
1 parent f5b1e2e commit 1b414f2
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 1 deletion.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ History
- Updated ``StripeQuantumCurrencyAmountField`` and ``StripeDecimalCurrencyAmountField`` to support Stripe Large Charges (#1045).
- Update event handling so ``customer.subscription.deleted`` updates subscriptions to ``status="canceled"`` instead of
deleting it from our database, to match Stripe's behaviour (#599).
- Added missing ``Refund.reason`` value, increases field width (#1075).
- Fixed ``Refund.status`` definition, reduces field width (#1076).

Warning about safe uninstall of jsonfield on upgrade
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions djstripe/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,6 +535,7 @@ class RefundReason(Enum):
duplicate = _("Duplicate charge")
fraudulent = _("Fraudulent")
requested_by_customer = _("Requested by customer")
expired_uncaptured_charge = _("Expired uncaptured charge")


class RefundStatus(Enum):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -866,6 +866,7 @@ def __init__(self, *args, **kwargs):
field=models.ForeignKey(
help_text="If the refund failed, this balance transaction describes the adjustment made on your account balance that reverses the initial balance transaction.",
null=True,
blank=True,
on_delete=django.db.models.deletion.SET_NULL,
to="djstripe.BalanceTransaction",
related_name="failure_refunds",
Expand Down
37 changes: 37 additions & 0 deletions djstripe/migrations/0008_auto_20191212_1434.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Generated by Django 2.2 on 2019-12-12 14:34

from django.db import migrations

import djstripe.enums
import djstripe.fields


class Migration(migrations.Migration):

dependencies = [
("djstripe", "0007_auto_20191107_0118"),
]

operations = [
migrations.AlterField(
model_name="refund",
name="reason",
field=djstripe.fields.StripeEnumField(
blank=True,
default="",
enum=djstripe.enums.RefundReason,
help_text="Reason for the refund.",
max_length=25,
),
),
migrations.AlterField(
model_name="refund",
name="status",
field=djstripe.fields.StripeEnumField(
blank=True,
enum=djstripe.enums.RefundStatus,
help_text="Status of the refund.",
max_length=9,
),
),
]
3 changes: 2 additions & 1 deletion djstripe/models/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1966,6 +1966,7 @@ class Refund(StripeModel):
on_delete=models.SET_NULL,
related_name="failure_refunds",
null=True,
blank=True,
help_text="If the refund failed, this balance transaction describes the "
"adjustment made on your account balance that reverses the initial "
"balance transaction.",
Expand All @@ -1990,7 +1991,7 @@ class Refund(StripeModel):
"for this charge.",
)
status = StripeEnumField(
enum=enums.RefundFailureReason, help_text="Status of the refund."
blank=True, enum=enums.RefundStatus, help_text="Status of the refund."
)

def get_stripe_dashboard_url(self):
Expand Down
231 changes: 231 additions & 0 deletions tests/test_refund.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
"""
dj-stripe Charge Model Tests.
"""
from copy import deepcopy
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.test.testcases import TestCase

from djstripe.models import Invoice, Refund

from . import (
FAKE_BALANCE_TRANSACTION,
FAKE_BALANCE_TRANSACTION_REFUND,
FAKE_CARD_AS_PAYMENT_METHOD,
FAKE_CHARGE,
FAKE_CUSTOMER,
FAKE_INVOICE,
FAKE_PAYMENT_INTENT_I,
FAKE_PRODUCT,
FAKE_REFUND,
FAKE_SUBSCRIPTION,
IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
AssertStripeFksMixin,
default_account,
)


class RefundTest(AssertStripeFksMixin, TestCase):
def setUp(self):
self.account = default_account()
self.user = get_user_model().objects.create_user(
username="pydanny", email="pydanny@gmail.com"
)
self.customer = FAKE_CUSTOMER.create_for_user(self.user)

self.default_expected_blank_fks = {
"djstripe.Account.branding_logo",
"djstripe.Account.branding_icon",
"djstripe.Charge.dispute",
"djstripe.Charge.transfer",
"djstripe.Customer.coupon",
"djstripe.Customer.default_payment_method",
"djstripe.Invoice.default_payment_method",
"djstripe.PaymentIntent.on_behalf_of",
"djstripe.PaymentIntent.payment_method",
"djstripe.Subscription.pending_setup_intent",
"djstripe.Refund.failure_balance_transaction",
}

@patch(
"djstripe.models.Account.get_default_account",
autospec=IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
)
@patch(
"stripe.BalanceTransaction.retrieve",
return_value=deepcopy(FAKE_BALANCE_TRANSACTION),
autospec=True,
)
@patch(
"stripe.Subscription.retrieve",
return_value=deepcopy(FAKE_SUBSCRIPTION),
autospec=True,
)
@patch("stripe.Charge.retrieve", return_value=deepcopy(FAKE_CHARGE), autospec=True)
@patch(
"stripe.PaymentMethod.retrieve",
return_value=deepcopy(FAKE_CARD_AS_PAYMENT_METHOD),
autospec=True,
)
@patch(
"stripe.PaymentIntent.retrieve",
return_value=deepcopy(FAKE_PAYMENT_INTENT_I),
autospec=True,
)
@patch(
"stripe.Product.retrieve", return_value=deepcopy(FAKE_PRODUCT), autospec=True
)
def test_sync_from_stripe_data(
self,
product_retrieve_mock,
payment_intent_retrieve_mock,
paymentmethod_card_retrieve_mock,
charge_retrieve_mock,
subscription_retrieve_mock,
balance_transaction_retrieve_mock,
default_account_mock,
):
default_account_mock.return_value = self.account
# TODO - remove invoice sync
Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

fake_refund = deepcopy(FAKE_REFUND)

balance_transaction_retrieve_mock.return_value = deepcopy(
FAKE_BALANCE_TRANSACTION_REFUND
)

refund = Refund.sync_from_stripe_data(fake_refund)

self.assert_fks(refund, expected_blank_fks=self.default_expected_blank_fks)

@patch(
"djstripe.models.Account.get_default_account",
autospec=IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
)
@patch(
"stripe.BalanceTransaction.retrieve",
return_value=deepcopy(FAKE_BALANCE_TRANSACTION),
autospec=True,
)
@patch(
"stripe.Subscription.retrieve",
return_value=deepcopy(FAKE_SUBSCRIPTION),
autospec=True,
)
@patch("stripe.Charge.retrieve", return_value=deepcopy(FAKE_CHARGE), autospec=True)
@patch(
"stripe.PaymentMethod.retrieve",
return_value=deepcopy(FAKE_CARD_AS_PAYMENT_METHOD),
autospec=True,
)
@patch(
"stripe.PaymentIntent.retrieve",
return_value=deepcopy(FAKE_PAYMENT_INTENT_I),
autospec=True,
)
@patch(
"stripe.Product.retrieve", return_value=deepcopy(FAKE_PRODUCT), autospec=True
)
def test_reason_enum(
self,
product_retrieve_mock,
payment_intent_retrieve_mock,
paymentmethod_card_retrieve_mock,
charge_retrieve_mock,
subscription_retrieve_mock,
balance_transaction_retrieve_mock,
default_account_mock,
):
default_account_mock.return_value = self.account
# TODO - remove invoice sync
Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

balance_transaction_retrieve_mock.return_value = deepcopy(
FAKE_BALANCE_TRANSACTION_REFUND
)

fake_refund = deepcopy(FAKE_REFUND)

for reason in (
"duplicate",
"fraudulent",
"requested_by_customer",
"expired_uncaptured_charge",
):
fake_refund["reason"] = reason

refund = Refund.sync_from_stripe_data(fake_refund)

self.assertEqual(refund.reason, reason)

# trigger model field validation (including enum value choices check)
refund.full_clean()

self.assert_fks(refund, expected_blank_fks=self.default_expected_blank_fks)

@patch(
"djstripe.models.Account.get_default_account",
autospec=IS_STATICMETHOD_AUTOSPEC_SUPPORTED,
)
@patch(
"stripe.BalanceTransaction.retrieve",
return_value=deepcopy(FAKE_BALANCE_TRANSACTION),
autospec=True,
)
@patch(
"stripe.Subscription.retrieve",
return_value=deepcopy(FAKE_SUBSCRIPTION),
autospec=True,
)
@patch("stripe.Charge.retrieve", return_value=deepcopy(FAKE_CHARGE), autospec=True)
@patch(
"stripe.PaymentMethod.retrieve",
return_value=deepcopy(FAKE_CARD_AS_PAYMENT_METHOD),
autospec=True,
)
@patch(
"stripe.PaymentIntent.retrieve",
return_value=deepcopy(FAKE_PAYMENT_INTENT_I),
autospec=True,
)
@patch(
"stripe.Product.retrieve", return_value=deepcopy(FAKE_PRODUCT), autospec=True
)
def test_status_enum(
self,
product_retrieve_mock,
payment_intent_retrieve_mock,
paymentmethod_card_retrieve_mock,
charge_retrieve_mock,
subscription_retrieve_mock,
balance_transaction_retrieve_mock,
default_account_mock,
):
default_account_mock.return_value = self.account
# TODO - remove invoice sync
Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE))

balance_transaction_retrieve_mock.return_value = deepcopy(
FAKE_BALANCE_TRANSACTION_REFUND
)

fake_refund = deepcopy(FAKE_REFUND)

for status in (
"pending",
"succeeded",
"failed",
"canceled",
):
fake_refund["status"] = status

refund = Refund.sync_from_stripe_data(fake_refund)

self.assertEqual(refund.status, status)

# trigger model field validation (including enum value choices check)
refund.full_clean()

self.assert_fks(refund, expected_blank_fks=self.default_expected_blank_fks)

0 comments on commit 1b414f2

Please sign in to comment.