dj-stripe / dj-stripe

dj-stripe automatically syncs your Stripe Data to your local database as pre-implemented Django Models allowing you to use the Django ORM, in your code, to work with the data making it easier and faster.
https://dj-stripe.dev
MIT License
1.56k stars 474 forks source link

Fetching any Upcoming Invoice with a Discount fails #2031

Open Biszu opened 2 months ago

Biszu commented 2 months ago

Describe the bug Fetching an upcoming invoice for a subscription with an applied Discount fails due to AttributeError This is similar to https://github.com/dj-stripe/dj-stripe/issues/1973

Software versions

Steps To Reproduce

  1. Create a new subscription with a Discount.
  2. Try to get an upcoming invoice for the new subscription: Invoice.upcoming(customer=c)

Can you reproduce the issue with the latest version of master? Yes

Expected Behavior Upcoming invoice details are fetched successfully.

Actual Behavior Request fails due to AttributeError: 'str' object has no attribute 'get'

Stacktrace:


In [6]: Invoice.upcoming(customer=c)
[p-3304] [INFO] message='Request to Stripe api' method=get path=https://api.stripe.com/v1/invoices/upcoming?customer=cus_Pa0*****
[p-3304] [INFO] message='Stripe API response' path=https://api.stripe.com/v1/invoices/upcoming?customer=cus_Pa0*****
 response_code=200

DoesNotExist                              Traceback (most recent call last)
File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/base.py:673, in StripeModel._create_from_stripe_object(cls, data, current_ids, pending_relations, save, stripe_account, api_key)
    671     else:
    672         # Raise error on purpose to resume the _create_from_stripe_object flow
--> 673         raise cls.DoesNotExist
    675 except cls.DoesNotExist:
    676     # try to create iff instance doesn't already exist in the DB
    677     # TODO dictionary unpacking will not work if cls has any ManyToManyField

DoesNotExist: 

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
Cell In[6], line 1
----> 1 Invoice.upcoming(customer=c)

File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/billing.py:797, in BaseInvoice.upcoming(cls, api_key, customer, subscription, subscription_plan, **kwargs)
    794 # Workaround for "id" being missing (upcoming invoices don't persist).
    795 upcoming_stripe_invoice["id"] = "upcoming"
--> 797 return UpcomingInvoice._create_from_stripe_object(
    798     upcoming_stripe_invoice,
    799     save=False,
    800     api_key=api_key,
    801 )

File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/base.py:687, in StripeModel._create_from_stripe_object(cls, data, current_ids, pending_relations, save, stripe_account, api_key)
    684     if save:
    685         instance.save()
--> 687     instance._attach_objects_post_save_hook(
    688         cls, data, api_key=api_key, pending_relations=pending_relations
    689     )
    691 return instance

File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/billing.py:991, in UpcomingInvoice._attach_objects_post_save_hook(self, cls, data, api_key, pending_relations)
    984 def _attach_objects_post_save_hook(
    985     self,
    986     cls,
   (...)
    989     pending_relations=None,
    990 ):
--> 991     super()._attach_objects_post_save_hook(
    992         cls, data, api_key=api_key, pending_relations=pending_relations
    993     )
    995     self._default_tax_rates = cls._stripe_object_to_default_tax_rates(
    996         target_cls=TaxRate, data=data, api_key=api_key
    997     )
    999     total_tax_amounts = []

File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/billing.py:835, in BaseInvoice._attach_objects_post_save_hook(self, cls, data, api_key, pending_relations)
    833 # sync every discount
    834 for discount in self.discounts:
--> 835     Discount.sync_from_stripe_data(discount, api_key=api_key)

File ~/Documents/*****/.venv/lib/python3.12/site-packages/djstripe/models/base.py:1043, in StripeModel.sync_from_stripe_data(cls, data, api_key, stripe_version)
   1033 """
   1034 Syncs this object from the stripe data provided.
   1035 
   (...)
   1040 :rtype: cls
   1041 """
   1042 current_ids = set()
-> 1043 data_id = data.get("id")
   1044 stripe_account = getattr(data, "stripe_account", None)
   1046 if data_id:
   1047     # stop nested objects from trying to retrieve this object before
   1048     # initial sync is complete

AttributeError: 'str' object has no attribute 'get'
miguel550 commented 2 months ago

I fixed this by doing Invoice.upcoming(customer=c, expand=('discounts',))