stripe: Link InvoiceItem to Invoice.

This is required for upgrading to the basil version of stripe.
Changes haven't been tested in this commit. It is just for easy
verification of changes.
This commit is contained in:
Aman Agrawal
2025-05-10 13:25:17 +05:30
committed by Tim Abbott
parent 6fb7a21cef
commit 3cdda91afe

View File

@@ -3216,25 +3216,32 @@ class BillingSession(ABC):
licenses_base = plan.invoiced_through.licenses licenses_base = plan.invoiced_through.licenses
invoiced_through_id = plan.invoiced_through.id invoiced_through_id = plan.invoiced_through.id
# Invoice Variables
stripe_invoice: stripe.Invoice | None = None
need_to_invoice = False
# Track if we added renewal invoice item which is possibly eligible for discount. # Track if we added renewal invoice item which is possibly eligible for discount.
renewal_invoice_period: stripe.InvoiceItem.CreateParamsPeriod | None = None renewal_invoice_period: stripe.InvoiceItem.CreateParamsPeriod | None = None
for ledger_entry in LicenseLedger.objects.filter( for ledger_entry in LicenseLedger.objects.filter(
plan=plan, id__gt=invoiced_through_id, event_time__lte=event_time plan=plan, id__gt=invoiced_through_id, event_time__lte=event_time
).order_by("id"): ).order_by("id"):
price_args: PriceArgs = {} # InvoiceItem variables.
invoice_item_params = stripe.InvoiceItem.CreateParams(
customer=plan.customer.stripe_customer_id
)
if ledger_entry.is_renewal: if ledger_entry.is_renewal:
if plan.fixed_price is not None: if plan.fixed_price is not None:
amount_due = get_amount_due_fixed_price_plan( amount_due = get_amount_due_fixed_price_plan(
plan.fixed_price, plan.billing_schedule plan.fixed_price, plan.billing_schedule
) )
price_args = {"amount": amount_due} invoice_item_params["amount"] = amount_due
else: else:
assert plan.price_per_license is not None # needed for mypy assert plan.price_per_license is not None # needed for mypy
price_args = { invoice_item_params["unit_amount"] = plan.price_per_license
"unit_amount": plan.price_per_license, invoice_item_params["quantity"] = ledger_entry.licenses
"quantity": ledger_entry.licenses, invoice_item_params["description"] = f"{plan.name} - renewal"
} need_to_invoice = True
description = f"{plan.name} - renewal"
elif ( elif (
plan.fixed_price is None plan.fixed_price is None
and licenses_base is not None and licenses_base is not None
@@ -3260,19 +3267,34 @@ class BillingSession(ABC):
plan_renewal_or_end_date - ledger_entry.event_time plan_renewal_or_end_date - ledger_entry.event_time
) / (billing_period_end - last_renewal) ) / (billing_period_end - last_renewal)
unit_amount = int(plan.price_per_license * proration_fraction + 0.5) unit_amount = int(plan.price_per_license * proration_fraction + 0.5)
price_args = { invoice_item_params["unit_amount"] = unit_amount
"unit_amount": unit_amount, invoice_item_params["quantity"] = ledger_entry.licenses - licenses_base
"quantity": ledger_entry.licenses - licenses_base, invoice_item_params["description"] = "Additional license ({} - {})".format(
}
description = "Additional license ({} - {})".format(
ledger_entry.event_time.strftime("%b %-d, %Y"), ledger_entry.event_time.strftime("%b %-d, %Y"),
plan_renewal_or_end_date.strftime("%b %-d, %Y"), plan_renewal_or_end_date.strftime("%b %-d, %Y"),
) )
need_to_invoice = True
if price_args: if stripe_invoice is None and need_to_invoice:
plan.invoiced_through = ledger_entry if plan.charge_automatically:
plan.invoicing_status = CustomerPlan.INVOICING_STATUS_STARTED collection_method: Literal["charge_automatically", "send_invoice"] = (
plan.save(update_fields=["invoicing_status", "invoiced_through"]) "charge_automatically"
)
days_until_due = None
else:
collection_method = "send_invoice"
days_until_due = DEFAULT_INVOICE_DAYS_UNTIL_DUE
invoice_params = stripe.Invoice.CreateParams(
auto_advance=True,
collection_method=collection_method,
customer=plan.customer.stripe_customer_id,
statement_descriptor=plan.name,
)
if days_until_due is not None:
invoice_params["days_until_due"] = days_until_due
stripe_invoice = stripe.Invoice.create(**invoice_params)
if invoice_item_params.get("description") is not None:
invoice_period = stripe.InvoiceItem.CreateParamsPeriod( invoice_period = stripe.InvoiceItem.CreateParamsPeriod(
start=datetime_to_timestamp(ledger_entry.event_time), start=datetime_to_timestamp(ledger_entry.event_time),
end=datetime_to_timestamp( end=datetime_to_timestamp(
@@ -3283,22 +3305,28 @@ class BillingSession(ABC):
if ledger_entry.is_renewal: if ledger_entry.is_renewal:
renewal_invoice_period = invoice_period renewal_invoice_period = invoice_period
stripe.InvoiceItem.create( invoice_item_params["currency"] = "usd"
currency="usd", invoice_item_params["discountable"] = False
customer=plan.customer.stripe_customer_id, invoice_item_params["period"] = invoice_period
description=description, invoice_item_params["idempotency_key"] = get_idempotency_key(ledger_entry)
discountable=False, assert stripe_invoice is not None
period=invoice_period, assert stripe_invoice.id is not None
idempotency_key=get_idempotency_key(ledger_entry), invoice_item_params["invoice"] = stripe_invoice.id
**price_args, stripe.InvoiceItem.create(**invoice_item_params)
)
# Update license base per ledger_entry. # Update license base per ledger_entry.
licenses_base = ledger_entry.licenses licenses_base = ledger_entry.licenses
plan.invoiced_through = ledger_entry plan.invoiced_through = ledger_entry
if renewal_invoice_period is not None: flat_discount, flat_discounted_months = self.get_flat_discount_info(plan.customer)
flat_discount, flat_discounted_months = self.get_flat_discount_info(plan.customer) if stripe_invoice is not None:
if plan.fixed_price is None and flat_discounted_months > 0: # Only apply discount if this invoice contains renewal of the plan.
if (
renewal_invoice_period is not None
and plan.fixed_price is None
and flat_discounted_months > 0
):
assert stripe_invoice.id is not None
num_months = ( num_months = (
12 if plan.billing_schedule == CustomerPlan.BILLING_SCHEDULE_ANNUAL else 1 12 if plan.billing_schedule == CustomerPlan.BILLING_SCHEDULE_ANNUAL else 1
) )
@@ -3307,6 +3335,7 @@ class BillingSession(ABC):
plan.customer.flat_discounted_months -= flat_discounted_months plan.customer.flat_discounted_months -= flat_discounted_months
plan.customer.save(update_fields=["flat_discounted_months"]) plan.customer.save(update_fields=["flat_discounted_months"])
stripe.InvoiceItem.create( stripe.InvoiceItem.create(
invoice=stripe_invoice.id,
currency="usd", currency="usd",
customer=plan.customer.stripe_customer_id, customer=plan.customer.stripe_customer_id,
description=f"${cents_to_dollar_string(flat_discount)}/month new customer discount", description=f"${cents_to_dollar_string(flat_discount)}/month new customer discount",
@@ -3315,23 +3344,6 @@ class BillingSession(ABC):
period=renewal_invoice_period, period=renewal_invoice_period,
) )
if plan.charge_automatically:
collection_method: Literal["charge_automatically", "send_invoice"] = (
"charge_automatically"
)
days_until_due = None
else:
collection_method = "send_invoice"
days_until_due = DEFAULT_INVOICE_DAYS_UNTIL_DUE
invoice_params = stripe.Invoice.CreateParams(
auto_advance=True,
collection_method=collection_method,
customer=plan.customer.stripe_customer_id,
statement_descriptor=plan.name,
)
if days_until_due is not None:
invoice_params["days_until_due"] = days_until_due
stripe_invoice = stripe.Invoice.create(**invoice_params)
stripe.Invoice.finalize_invoice(stripe_invoice) stripe.Invoice.finalize_invoice(stripe_invoice)
plan.invoicing_status = CustomerPlan.INVOICING_STATUS_DONE plan.invoicing_status = CustomerPlan.INVOICING_STATUS_DONE