def test_sync_from_stripe_data(self): invoice = Invoice.sync_from_stripe_data(FAKE_INVOICE) self.assertEqual("in_xxxxxxxxxxxxxxx", invoice.stripe_id) self.assertEqual(False, invoice.attempted) self.assertEqual(False, invoice.closed) self.assertEqual(False, invoice.paid) self.assertEqual(Decimal("9.95"), invoice.subtotal) self.assertEqual(Decimal("9.95"), invoice.total) self.assertEqual("", invoice.charge) self.assertEqual(1, invoice.items.count()) invoice_item = invoice.items.all()[0] self.assertEqual("sub_xxxxxxxxxxxxxxx", invoice_item.stripe_id) self.assertEqual(Decimal("9.95"), invoice_item.amount) self.assertEqual("usd", invoice_item.currency) self.assertEqual(False, invoice_item.proration) self.assertEqual("", invoice_item.description) self.assertEqual("subscription", invoice_item.line_type) self.assertEqual("test", invoice_item.plan) self.assertEqual(1, invoice_item.quantity) # period_end is determined by latest invoice_item self.assertEqual(invoice_item.period_end, invoice.period_end) # Update invoice Invoice.sync_from_stripe_data(FAKE_INVOICE)
def test_sync_from_stripe_data_with_charge_no_receipt(self, record_charge_mock, send_receipt_mock): record_charge_mock.return_value = Charge(customer=self.customer) FAKE_INVOICE_WITH_CHARGE = deepcopy(FAKE_INVOICE) FAKE_INVOICE_WITH_CHARGE["id"] = "in_zzzzzzzzzzzzzzz1" FAKE_INVOICE_WITH_CHARGE["subscription"] = "sub_zzzzzzzzzzzzzzz1" FAKE_INVOICE_WITH_CHARGE["lines"]["data"][0]["id"] = "sub_zzzzzzzzzzzzzzz1" FAKE_INVOICE_WITH_CHARGE["charge"] = "taco1" Invoice.sync_from_stripe_data(FAKE_INVOICE_WITH_CHARGE, send_receipt=False) record_charge_mock.assert_called_once_with("taco1") self.assertFalse(send_receipt_mock.called)
def test_invoice_without_plan(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"][0]["plan"] = None invoice_data["subscription"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNone(invoice.plan)
def test_status_open(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"paid": False, "closed": False}) invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(Invoice.STATUS_OPEN, invoice.status)
def test_invoice_plan_from_subscription(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"][0]["plan"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice.plan) # retrieved from subscription self.assertEqual(FAKE_PLAN["id"], invoice.plan.stripe_id)
def sync_invoices(apps, schema_editor): # This is okay, since we're only doing a forward migration. from djstripe.models import Invoice from djstripe.context_managers import stripe_temporary_api_version with stripe_temporary_api_version("2016-03-07"): if Invoice.objects.count(): print("syncing invoices. This may take a while.") for invoice in tqdm(iterable=Invoice.objects.all(), desc="Sync", unit=" invoices"): try: Invoice.sync_from_stripe_data(invoice.api_retrieve()) except InvalidRequestError: tqdm.write("There was an error while syncing invoice \ ({invoice_id}).".format(invoice_id=invoice.stripe_id)) print("Invoice sync complete.")
def test_invoice_with_non_subscription_invoice_items(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II)) invoice_data["lines"]["total_count"] += 1 invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice) self.assertEqual(2, len(invoice.invoiceitems.all()))
def test_invoice_with_subscription_invoice_items(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice = Invoice.sync_from_stripe_data(invoice_data) items = invoice.invoiceitems.all() self.assertEqual(1, len(items)) item_id = "{invoice_id}-{subscription_id}".format(invoice_id=invoice.stripe_id, subscription_id=FAKE_SUBSCRIPTION["id"]) self.assertEqual(item_id, items[0].stripe_id)
def test_retry_false(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock, invoice_retrieve_mock): default_account_mock.return_value = self.account fake_invoice = deepcopy(FAKE_INVOICE) invoice_retrieve_mock.return_value = fake_invoice invoice = Invoice.sync_from_stripe_data(fake_invoice) return_value = invoice.retry() self.assertFalse(invoice_retrieve_mock.called) self.assertFalse(return_value)
def test_sync_from_stripe_data_no_plan(self): FAKE_INVOICE_NO_PLAN = deepcopy(FAKE_INVOICE) FAKE_INVOICE_NO_PLAN["id"] = "in_yyyyyyyyyyyyyyy" FAKE_INVOICE_NO_PLAN["subscription"] = "sub_yyyyyyyyyyyyyyy" FAKE_INVOICE_NO_PLAN["lines"]["data"][0]["id"] = "sub_yyyyyyyyyyyyyyy" FAKE_INVOICE_NO_PLAN["lines"]["data"][0]["plan"] = None invoice = Invoice.sync_from_stripe_data(FAKE_INVOICE_NO_PLAN) self.assertEqual(1, invoice.items.count()) invoice_item = invoice.items.all()[0] self.assertEqual("", invoice_item.plan)
def test_sync_no_subscription(self, charge_retrieve_mock, subscription_retrieve_mock, plan_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"subscription": None}) invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(None, invoice.subscription) charge_retrieve_mock.assert_called_once_with(api_key=ANY, expand=ANY, id=FAKE_CHARGE["id"]) plan_retrieve_mock.assert_called_once_with(api_key=ANY, expand=ANY, id=FAKE_PLAN["id"]) subscription_retrieve_mock.assert_not_called()
def test_retry_true(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock, invoice_retrieve_mock): default_account_mock.return_value = self.account fake_invoice = deepcopy(FAKE_INVOICE) fake_invoice.update({"paid": False, "closed": False}) invoice_retrieve_mock.return_value = fake_invoice invoice = Invoice.sync_from_stripe_data(fake_invoice) return_value = invoice.retry() invoice_retrieve_mock.assert_called_once_with(id=invoice.stripe_id, api_key=settings.STRIPE_SECRET_KEY, expand=None) self.assertTrue(return_value)
def test_str(self, charge_retrieve_mock, subscription_retrive_mock, default_account_mock): default_account_mock.return_value = self.account invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual(invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url()) self.assertEqual( "<amount_due={amount_due}, date={date}, status={status}, stripe_id={stripe_id}>".format( amount_due=invoice.amount_due, date=invoice.date, status=invoice.status, stripe_id=invoice.stripe_id ), str(invoice) )
def test_status_forgiven_default( self, product_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): # forgiven parameter deprecated in API 2018-11-08 # see https://stripe.com/docs/upgrades#2018-11-08 default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"paid": False, "closed": False}) invoice_data.pop("forgiven", None) # TODO remove invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(Invoice.STATUS_OPEN, invoice.status)
def test_status_closed_deprecated( self, product_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): # closed parameter deprecated in API 2018-11-08 - see https://stripe.com/docs/upgrades#2018-11-08 default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"paid": False}) invoice_data["auto_advance"] = False invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(Invoice.STATUS_CLOSED, invoice.status) self.assertEqual(invoice.auto_advance, invoice_data["auto_advance"])
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 invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual(invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url()) self.assertEqual(str(invoice), "Invoice #{}".format(FAKE_INVOICE["number"])) self.assertGreater(len(invoice.status_transitions.keys()), 1) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_status_void( self, product_retrieve_mock, paymentmethod_card_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"paid": False, "status": "void"}) invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(InvoiceStatus.void, invoice.status) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_invoice_plan_from_subscription( self, product_retrieve_mock, paymentmethod_card_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"][0]["plan"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice.plan) # retrieved from subscription self.assertEqual(FAKE_PLAN["id"], invoice.plan.id) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_retry_true( self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock, invoice_retrieve_mock, ): default_account_mock.return_value = self.account fake_invoice = deepcopy(FAKE_INVOICE) fake_invoice.update({"paid": False, "closed": False}) invoice_retrieve_mock.return_value = fake_invoice invoice = Invoice.sync_from_stripe_data(fake_invoice) return_value = invoice.retry() invoice_retrieve_mock.assert_called_once_with( id=invoice.id, api_key=STRIPE_SECRET_KEY, expand=[]) self.assertTrue(return_value)
def test_status_closed(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"paid": False}) invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(Invoice.STATUS_CLOSED, invoice.status) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.Plan.product", }, )
def invoice_paid_to_slack(event, **kwargs): """Post paid invoices to Slack.""" data = event.data["object"] # This is a little over-engineered # However, by fetching from Stripe and then syncing, # we ensure there isn't a race condition between the webhook and this handler # Also, this will work in test-mode as well as production invoice_id = data["id"] invoice = Invoice.sync_from_stripe_data( stripe.Invoice.retrieve(invoice_id)) log.debug("Stripe invoice %s is paid. Posting to Slack...", invoice) slack_message( "adserver/slack/invoice-paid.slack", { "customer": invoice.customer, "invoice": invoice, }, )
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 invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual( invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url() ) self.assertEqual(str(invoice), "Invoice #{}".format(FAKE_INVOICE["number"])) self.assertGreater(len(invoice.status_transitions.keys()), 1) self.assertTrue(bool(invoice.account_country)) self.assertTrue(bool(invoice.account_name)) self.assertTrue(bool(invoice.collection_method)) with self.assertWarns(DeprecationWarning): self.assertEqual(invoice.billing, invoice.collection_method) self.assertEqual(invoice.default_tax_rates.count(), 1) self.assertEqual( invoice.default_tax_rates.first().id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"] ) self.assertEqual(invoice.total_tax_amounts.count(), 1) first_tax_amount = invoice.total_tax_amounts.first() self.assertEqual( first_tax_amount.tax_rate.id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"] ) self.assertEqual( first_tax_amount.inclusive, FAKE_TAX_RATE_EXAMPLE_1_VAT["inclusive"] ) self.assertEqual(first_tax_amount.amount, 261) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_invoice_plan_from_subscription(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"][0]["plan"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice.plan) # retrieved from subscription self.assertEqual(FAKE_PLAN["id"], invoice.plan.id) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.Plan.product", }, )
def test_invoice_with_non_subscription_invoice_items( self, product_retrieve_mock, paymentmethod_card_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II)) invoice_data["lines"]["total_count"] += 1 invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice) self.assertEqual(2, len(invoice.invoiceitems.all())) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_sync_no_subscription( self, product_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, plan_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data.update({"subscription": None}) invoice_data["lines"]["data"][0]["subscription"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertEqual(None, invoice.subscription) self.assertEqual(FAKE_CHARGE["id"], invoice.charge.id) self.assertEqual(FAKE_PLAN["id"], invoice.plan.id) # charge_retrieve_mock.assert_not_called() plan_retrieve_mock.assert_not_called() subscription_retrieve_mock.assert_not_called() self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.branding_logo", "djstripe.Account.branding_icon", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.Invoice.subscription", "djstripe.PaymentIntent.on_behalf_of", "djstripe.PaymentIntent.payment_method", "djstripe.Subscription.pending_setup_intent", }, )
def test_invoice_without_plan( self, charge_retrieve_mock, payment_intent_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"][0]["plan"] = None invoice_data["lines"]["data"][0]["subscription"] = None invoice_data["subscription"] = None invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNone(invoice.plan) self.assert_fks( invoice, expected_blank_fks=self.default_expected_blank_fks | {"djstripe.Invoice.subscription"}, )
def test_str( self, product_retrieve_mock, charge_retrieve_mock, subscription_retrive_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual(invoice.get_stripe_dashboard_url(), self.customer.get_stripe_dashboard_url()) self.assertEqual(str(invoice), "Invoice #XXXXXXX-0001") self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", }, )
def test_status_paid( self, product_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual(Invoice.STATUS_PAID, invoice.status) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", }, )
def test_invoice_with_subscription_invoice_items( self, product_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, ): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice = Invoice.sync_from_stripe_data(invoice_data) items = invoice.invoiceitems.all() self.assertEqual(1, len(items)) # Previously the test asserted item_id="{invoice_id}-{subscription_id}", # but this doesn't match what I'm seeing from Stripe # I'm not sure if it's possible to predict the whole item id now, # sli seems to not reference anything item_id_prefix = "{invoice_id}-sli_".format(invoice_id=invoice.id) self.assertTrue(items[0].id.startswith(item_id_prefix)) self.assertEqual(items[0].subscription.id, FAKE_SUBSCRIPTION["id"]) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.branding_logo", "djstripe.Account.branding_icon", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.PaymentIntent.on_behalf_of", "djstripe.PaymentIntent.payment_method", "djstripe.Subscription.pending_setup_intent", }, )
def test_retry_false( self, product_retrieve_mock, paymentmethod_card_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, invoice_retrieve_mock, ): default_account_mock.return_value = self.account fake_invoice = deepcopy(FAKE_INVOICE) invoice_retrieve_mock.return_value = fake_invoice invoice = Invoice.sync_from_stripe_data(fake_invoice) return_value = invoice.retry() self.assertFalse(invoice_retrieve_mock.called) self.assertFalse(return_value) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks)
def test_invoice_with_non_subscription_invoice_items( self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice_data["lines"]["data"].append(deepcopy(FAKE_INVOICEITEM_II)) invoice_data["lines"]["total_count"] += 1 invoice = Invoice.sync_from_stripe_data(invoice_data) self.assertIsNotNone(invoice) self.assertEqual(2, len(invoice.invoiceitems.all())) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.Plan.product", }, )
def test_retry_true( self, product_retrieve_mock, payment_intent_retrieve_mock, charge_retrieve_mock, subscription_retrieve_mock, balance_transaction_retrieve_mock, default_account_mock, invoice_retrieve_mock, ): default_account_mock.return_value = self.account fake_invoice = deepcopy(FAKE_INVOICE) fake_invoice.update({"paid": False, "closed": False}) invoice_retrieve_mock.return_value = fake_invoice invoice = Invoice.sync_from_stripe_data(fake_invoice) return_value = invoice.retry() invoice_retrieve_mock.assert_called_once_with( id=invoice.id, api_key=STRIPE_SECRET_KEY, expand=[] ) self.assertTrue(return_value) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.branding_logo", "djstripe.Account.branding_icon", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.PaymentIntent.on_behalf_of", "djstripe.PaymentIntent.payment_method", "djstripe.Subscription.pending_setup_intent", }, )
def test_invoice_with_subscription_invoice_items( self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice_data = deepcopy(FAKE_INVOICE) invoice = Invoice.sync_from_stripe_data(invoice_data) items = invoice.invoiceitems.all() self.assertEqual(1, len(items)) item_id = "{invoice_id}-{subscription_id}".format( invoice_id=invoice.id, subscription_id=FAKE_SUBSCRIPTION["id"]) self.assertEqual(item_id, items[0].id) self.assert_fks( invoice, expected_blank_fks={ "djstripe.Account.business_logo", "djstripe.Charge.dispute", "djstripe.Charge.transfer", "djstripe.Customer.coupon", "djstripe.Plan.product", }, )
def test_sync_from_stripe_data_default_payment_method( 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 fake_invoice = deepcopy(FAKE_INVOICE) fake_invoice["default_payment_method"] = deepcopy(FAKE_CARD_AS_PAYMENT_METHOD) invoice = Invoice.sync_from_stripe_data(fake_invoice) self.assertEqual( invoice.default_payment_method.id, FAKE_CARD_AS_PAYMENT_METHOD["id"] ) self.assert_fks( invoice, expected_blank_fks=self.default_expected_blank_fks - {"djstripe.Invoice.default_payment_method"}, )
def test_status_paid(self, charge_retrieve_mock, subscription_retrieve_mock, default_account_mock): default_account_mock.return_value = self.account invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) self.assertEqual(Invoice.STATUS_PAID, invoice.status)
def action_create_draft_invoice(self, request, queryset): """ Create a draft invoice for selected flights with metadata attached. This fails with a message if the flights aren't all from the same advertiser. """ # TODO: convert to using djstripe and tie FK to flights if not settings.STRIPE_ENABLED: messages.add_message( request, messages.ERROR, _("Stripe is not configured. Please set the envvar STRIPE_SECRET_KEY." ), ) return flights = list( queryset.select_related("campaign", "campaign__advertiser")) if not flights: # Django actually doesn't take this path and instead shows its own error message return # pragma: no cover if len({f.campaign.advertiser_id for f in flights}) > 1: messages.add_message( request, messages.ERROR, _("All selected flights must be from a single advertiser."), ) return earliest_start_date = min([f.start_date for f in flights]) latest_end_date = max([f.end_date for f in flights]) advertiser = [f.campaign.advertiser for f in flights][0] if not advertiser.djstripe_customer: messages.add_message( request, messages.ERROR, _("No Stripe customer ID for {}".format(advertiser)), ) return for flight in flights: message_components = ["Advertising", flight.name] unit_amount = 0 quantity = 1 if flight.cpc: message_components.append("per click") unit_amount = int(flight.cpc * 100) # Convert to US cents quantity = flight.sold_clicks elif flight.cpm: message_components.append("per 1k impressions") unit_amount = int(flight.cpm * 100) # Convert to US cents quantity = flight.sold_impressions // 1000 # Amounts, prices, and description can be customized before sending stripe.InvoiceItem.create( customer=advertiser.djstripe_customer.id, description=" - ".join(message_components), quantity=quantity, unit_amount=unit_amount, # in US cents currency="USD", metadata={ "Advertiser": advertiser.slug, "Flight": flight.slug, "Flight Start": flight.start_date.strftime("%Y-%m-%d"), "Flight End": flight.end_date.strftime("%Y-%m-%d"), }, ) # https://stripe.com/docs/api/invoices/create inv = stripe.Invoice.create( customer=advertiser.djstripe_customer.id, auto_advance=False, # Draft invoice collection_method="send_invoice", custom_fields=[ { "name": "Advertiser", "value": advertiser.slug }, { "name": "Estimated Start", "value": earliest_start_date.strftime("%Y-%m-%d"), }, { "name": "Estimated End", "value": latest_end_date.strftime("%Y-%m-%d"), }, ], days_until_due=30, ) invoice = Invoice.sync_from_stripe_data(inv) # Attach Stripe invoices to flights # There isn't a good way to mock invoices for tests so this check is to mock the invoice if invoice.pk: for flight in flights: flight.invoices.add(invoice) messages.add_message( request, messages.SUCCESS, _("New Stripe invoice for {}: {}".format( advertiser, invoice.get_stripe_dashboard_url())), )
def test_sync_from_stripe_data_update_total_tax_amounts( 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 invoice = Invoice.sync_from_stripe_data(deepcopy(FAKE_INVOICE)) # as per basic sync test self.assertEqual(invoice.default_tax_rates.count(), 1) self.assertEqual( invoice.default_tax_rates.first().id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"] ) self.assertEqual(invoice.total_tax_amounts.count(), 1) first_tax_amount = invoice.total_tax_amounts.first() self.assertEqual( first_tax_amount.tax_rate.id, FAKE_TAX_RATE_EXAMPLE_1_VAT["id"] ) self.assertEqual( first_tax_amount.inclusive, FAKE_TAX_RATE_EXAMPLE_1_VAT["inclusive"] ) self.assertEqual(first_tax_amount.amount, 261) self.assert_fks(invoice, expected_blank_fks=self.default_expected_blank_fks) # Now update with a different tax rate # TODO - should update tax rate in invoice items etc as well, # but here we're mainly testing that invoice.total_tax_rates is # correctly updated fake_updated_invoice = deepcopy(FAKE_INVOICE) fake_tax_rate_2 = deepcopy(FAKE_TAX_RATE_EXAMPLE_2_SALES) new_tax_amount = int( fake_updated_invoice["total"] * fake_tax_rate_2["percentage"] / 100 ) fake_updated_invoice.update( { "default_tax_rates": [fake_tax_rate_2], "tax": new_tax_amount, "total": fake_updated_invoice["total"] + new_tax_amount, "total_tax_amounts": [ { "amount": new_tax_amount, "inclusive": False, "tax_rate": fake_tax_rate_2["id"], } ], } ) invoice_updated = Invoice.sync_from_stripe_data(fake_updated_invoice) self.assertEqual(invoice_updated.default_tax_rates.count(), 1) self.assertEqual( invoice_updated.default_tax_rates.first().id, fake_tax_rate_2["id"] ) self.assertEqual(invoice_updated.total_tax_amounts.count(), 1) first_tax_amount = invoice_updated.total_tax_amounts.first() self.assertEqual(first_tax_amount.tax_rate.id, fake_tax_rate_2["id"]) self.assertEqual(first_tax_amount.inclusive, fake_tax_rate_2["inclusive"]) self.assertEqual(first_tax_amount.amount, new_tax_amount) self.assert_fks( invoice_updated, expected_blank_fks=self.default_expected_blank_fks )