{"action":"created","issue":{"url":"https://api.github.com/repos/kavdev/dj-stripe/issues/320","repository_url":"https://api.github.com/repos/kavdev/dj-stripe","labels_url":"https://api.github.com/repos/kavdev/dj-stripe/issues/320/labels{/name}","comments_url":"https://api.github.com/repos/kavdev/dj-stripe/issues/320/comments","events_url":"https://api.github.com/repos/kavdev/dj-stripe/issues/320/events","html_url":"https://github.com/kavdev/dj-stripe/pull/320","id":162306983,"number":320,"title":"Add support for upcoming invoices w/ invoice items","user":{"login":"lskillen","id":2248287,"avatar_url":"https://avatars.githubusercontent.com/u/2248287?v=3","gravatar_id":"","url":"https://api.github.com/users/lskillen","html_url":"https://github.com/lskillen","followers_url":"https://api.github.com/users/lskillen/followers","following_url":"https://api.github.com/users/lskillen/following{/other_user}","gists_url":"https://api.github.com/users/lskillen/gists{/gist_id}","starred_url":"https://api.github.com/users/lskillen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lskillen/subscriptions","organizations_url":"https://api.github.com/users/lskillen/orgs","repos_url":"https://api.github.com/users/lskillen/repos","events_url":"https://api.github.com/users/lskillen/events{/privacy}","received_events_url":"https://api.github.com/users/lskillen/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":3,"created_at":"2016-06-26T00:59:45Z","updated_at":"2016-06-28T20:26:06Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/kavdev/dj-stripe/pulls/320","html_url":"https://github.com/kavdev/dj-stripe/pull/320","diff_url":"https://github.com/kavdev/dj-stripe/pull/320.diff","patch_url":"https://github.com/kavdev/dj-stripe/pull/320.patch"},"body":"## Description\r\n\r\nAdds support for Stripe's API for previewing upcoming invoices, and also adds support for subscription-based invoice items - This was a rather tricky one to implement given the ephemeral nature of the upcoming invoices.  They aren't persisted to the database and their internals differ from the expected \"norm\" (i.e. they don't have a stripe ID).\r\n\r\nAn example of usage (as we have it in our application):\r\n\r\n```\r\ninvoice = djstripe_models.Invoice.upcoming(\r\n    customer=customer, subscription=sub, subscription_plan=plan)\r\n```\r\n\r\nAn additional `plan` property accessor has been added to invoices - As detailed in the code comments, this was necessary for us to provide a consistent view for the invoice history.  Instead of using the plan from the invoice subscription, it is now possible to use the plan for the from available subscription invoice item, and this access is encapsulated in the property.\r\n\r\nThe maximum length for stripe fields was also raised to 255, in accordance with the recommendations in the [Stripe API Upgrades documentation](https://stripe.com/docs/upgrades).  The main reason though was to make room for the longer composite key IDs used for subscription-based invoice items (necessary in order to ensure these remain unique per invoice).  As per the `plan` change above, this also helps to provide a consistent view for the invoice history.\r\n\r\nOne odd thing to note is the use of the `QuerySetMock` for `UpcomingInvoice`.  The reasoning behind this was to ensure that `invoice.invoiceitems()` for upcoming invoices still returned a queryset-like object that supports non-mutation operations such as iteration.  I made a note in the documentation not to expect it to do anything else, so it's a bit like Stripe's own docs for upcoming invoices, they are not mutable.  The main downside is the added dependency, so apologies for that!\r\n\r\nFinally, I guess that the changes for `_get_or_create_from_stripe_object` might be contentious, which were required to handle the slightly different structure for the invoice items within the upcoming invoice, and to support things like not re-fetching the data from Stripe and not saving the created object.  Both of the latter could be useful for some additional optimisations later.\r\n\r\nI'll be happy to discuss any part of the PR during review!\r\n\r\n## Test Output\r\n\r\nRan: `python runtests.py --skip-utc`\r\n\r\n```\r\nWelcome to the dj-stripe test suite.\r\n\r\nStep 1: Running unit tests.\r\n\r\nnosetests . --verbosity=1\r\nCreating test database for alias 'default'...\r\n................................................................................................................................................................................................................S...S.........................................\r\n----------------------------------------------------------------------\r\nRan 254 tests in 5.540s\r\n\r\nOK (SKIP=2)\r\nDestroying test database for alias 'default'...\r\n\r\nStep 2: Generating coverage results.\r\n\r\nName                                             Stmts   Miss Branch BrPart  Cover   Missing\r\n--------------------------------------------------------------------------------------------\r\ndjstripe/context_managers.py                         8      0      0      0   100%\r\ndjstripe/contrib/rest_framework/permissions.py       9      0      0      0   100%\r\ndjstripe/contrib/rest_framework/serializers.py      11      0      0      0   100%\r\ndjstripe/contrib/rest_framework/urls.py              5      0      0      0   100%\r\ndjstripe/contrib/rest_framework/views.py            36      0      2      0   100%\r\ndjstripe/decorators.py                              19      0      4      0   100%\r\ndjstripe/event_handlers.py                          44      0     16      0   100%\r\ndjstripe/exceptions.py                               7      0      0      0   100%\r\ndjstripe/fields.py                                  76      0     18      0   100%\r\ndjstripe/forms.py                                    7      0      0      0   100%\r\ndjstripe/managers.py                                37      0      0      0   100%\r\ndjstripe/middleware.py                              36      0     18      0   100%\r\ndjstripe/mixins.py                                  26      0      2      0   100%\r\ndjstripe/models.py                                 371      0    116      0   100%\r\ndjstripe/settings.py                                43      0     12      0   100%\r\ndjstripe/signals.py                                  3      0      0      0   100%\r\ndjstripe/stripe_objects.py                         485      0     75      0   100%\r\ndjstripe/sync.py                                    29      0      4      0   100%\r\ndjstripe/templatetags/djstripe_tags.py              20      0      4      0   100%\r\ndjstripe/urls.py                                     6      0      0      0   100%\r\ndjstripe/utils.py                                   35      0     14      0   100%\r\ndjstripe/views.py                                  133      0     22      0   100%\r\ndjstripe/webhooks.py                                25      0      8      0   100%\r\n--------------------------------------------------------------------------------------------\r\nTOTAL                                             1471      0    315      0   100%\r\n\r\nStep 3: Checking for pep8 errors.\r\n\r\npep8 errors:\r\n----------------------------------------------------------------------\r\nNone\r\n\r\nTests completed successfully with no errors. Congrats!\r\n```"},"comment":{"url":"https://api.github.com/repos/kavdev/dj-stripe/issues/comments/229171946","html_url":"https://github.com/kavdev/dj-stripe/pull/320#issuecomment-229171946","issue_url":"https://api.github.com/repos/kavdev/dj-stripe/issues/320","id":229171946,"user":{"login":"lskillen","id":2248287,"avatar_url":"https://avatars.githubusercontent.com/u/2248287?v=3","gravatar_id":"","url":"https://api.github.com/users/lskillen","html_url":"https://github.com/lskillen","followers_url":"https://api.github.com/users/lskillen/followers","following_url":"https://api.github.com/users/lskillen/following{/other_user}","gists_url":"https://api.github.com/users/lskillen/gists{/gist_id}","starred_url":"https://api.github.com/users/lskillen/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/lskillen/subscriptions","organizations_url":"https://api.github.com/users/lskillen/orgs","repos_url":"https://api.github.com/users/lskillen/repos","events_url":"https://api.github.com/users/lskillen/events{/privacy}","received_events_url":"https://api.github.com/users/lskillen/received_events","type":"User","site_admin":false},"created_at":"2016-06-28T20:26:05Z","updated_at":"2016-06-28T20:26:05Z","body":"@kavdev I'm sure you're still reviewing (thanks btw, I do appreciate it), but I've added some comments as responses to yours.  These are sometimes easy to miss so just pinging you here so you're aware of it.  Cheers!"}}
{"actor":{"display_login":"lskillen"}}