Skip to content

Commit

Permalink
Add TaxRate model (dj-stripe#1027)
Browse files Browse the repository at this point in the history
Also Invoice.default_tax_rates, InvoiceItem.tax_rates, Invoice.total_tax_amounts, Subscription.default_tax_rates, SubscriptionItem.tax_rates
  • Loading branch information
therefromhere authored Dec 14, 2019
1 parent 7ea0953 commit adda5fb
Show file tree
Hide file tree
Showing 20 changed files with 1,026 additions and 18 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ History
- Dropped previously-deprecated enum ``PaymentMethodType`` (use ``DjstripePaymentMethodType`` instead)
- Renamed ``Invoice.billing`` to ``.collection_method`` (added deprecated property for the old name).
- Updated ``Invoice`` model to add missing fields.
- Added ``TaxRate`` model, and ``Invoice.default_tax_rates``, ``InvoiceItem.tax_rates``,
``Invoice.total_tax_amounts``, ``Subscription.default_tax_rates``, ``SubscriptionItem.tax_rates`` (#1027).
- Change urls.py to use the new style urls.
- Update forward relation fields in the admin to be raw id fields.
- Updated ``StripeQuantumCurrencyAmountField`` and ``StripeDecimalCurrencyAmountField`` to support Stripe Large Charges (#1045).
Expand Down
6 changes: 6 additions & 0 deletions djstripe/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,12 @@ def cancel_subscription(self, request, queryset):
actions = (cancel_subscription,)


@admin.register(models.TaxRate)
class TaxRateAdmin(StripeModelAdmin):
list_display = ("active", "display_name", "inclusive", "jurisdiction", "percentage")
list_filter = ("active", "inclusive", "jurisdiction")


@admin.register(models.Transfer)
class TransferAdmin(StripeModelAdmin):
list_display = ("amount", "description")
2 changes: 1 addition & 1 deletion djstripe/contrib/rest_framework/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Meta:
"""Model class options."""

model = Subscription
fields = "__all__"
exclude = ["default_tax_rates"]


class CreateSubscriptionSerializer(serializers.Serializer):
Expand Down
234 changes: 234 additions & 0 deletions djstripe/migrations/0011_auto_20191214_0453.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# Generated by Django 3.0 on 2019-12-14 04:53

import django.core.validators
import django.db.models.deletion
from django.db import migrations, models

import djstripe.fields


class Migration(migrations.Migration):

dependencies = [
("djstripe", "0010_upcominginvoice"),
]

operations = [
migrations.CreateModel(
name="TaxRate",
fields=[
(
"djstripe_id",
models.BigAutoField(
primary_key=True, serialize=False, verbose_name="ID"
),
),
("id", djstripe.fields.StripeIdField(max_length=255, unique=True)),
(
"livemode",
models.NullBooleanField(
default=None,
help_text="Null here indicates that the livemode status is unknown or was previously unrecorded. Otherwise, this field indicates whether this record comes from Stripe test mode or live mode operation.",
),
),
(
"created",
djstripe.fields.StripeDateTimeField(
blank=True,
help_text="The datetime this object was created in stripe.",
null=True,
),
),
(
"metadata",
djstripe.fields.JSONField(
blank=True,
help_text="A set of key/value pairs that you can attach to an object. It can be useful for storing additional information about an object in a structured format.",
null=True,
),
),
(
"description",
models.TextField(
blank=True, help_text="A description of this object.", null=True
),
),
("djstripe_created", models.DateTimeField(auto_now_add=True)),
("djstripe_updated", models.DateTimeField(auto_now=True)),
(
"active",
models.BooleanField(
default=True,
help_text="Defaults to true. When set to false, this tax rate cannot be applied to objects in the API, but will still be applied to subscriptions and invoices that already have it set.",
),
),
(
"display_name",
models.CharField(
blank=True,
default="",
help_text="The display name of the tax rates as it will appear to your customer on their receipt email, PDF, and the hosted invoice page.",
max_length=50,
),
),
(
"inclusive",
models.BooleanField(
help_text="This specifies if the tax rate is inclusive or exclusive."
),
),
(
"jurisdiction",
models.CharField(
blank=True,
default="",
help_text="The jurisdiction for the tax rate.",
max_length=50,
),
),
(
"percentage",
djstripe.fields.StripePercentField(
decimal_places=2,
help_text="This represents the tax rate percent out of 100.",
max_digits=5,
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(100),
],
),
),
],
options={"get_latest_by": "created", "abstract": False,},
),
migrations.AddField(
model_name="invoice",
name="default_tax_rates",
field=models.ManyToManyField(
blank=True,
db_table="djstripe_djstripeinvoicedefaulttaxrate",
help_text="The tax rates applied to this invoice, if any.",
related_name="_invoice_default_tax_rates_+",
to="djstripe.TaxRate",
),
),
migrations.AddField(
model_name="invoiceitem",
name="tax_rates",
field=models.ManyToManyField(
blank=True,
db_table="djstripe_djstripeinvoiceitemtaxrate",
help_text="The tax rates which apply to this invoice item. When set, the default_tax_rates on the invoice do not apply to this invoice item.",
related_name="_invoiceitem_tax_rates_+",
to="djstripe.TaxRate",
),
),
migrations.AddField(
model_name="subscription",
name="default_tax_rates",
field=models.ManyToManyField(
blank=True,
db_table="djstripe_djstripesubscriptiondefaulttaxrate",
help_text="The tax rates that will apply to any subscription item that does not have tax_rates set. Invoices created will have their default_tax_rates populated from the subscription.",
related_name="_subscription_default_tax_rates_+",
to="djstripe.TaxRate",
),
),
migrations.AddField(
model_name="subscriptionitem",
name="tax_rates",
field=models.ManyToManyField(
blank=True,
db_table="djstripe_djstripesubscriptionitemtaxrate",
help_text="The tax rates which apply to this subscription_item. When set, the default_tax_rates on the subscription do not apply to this subscription_item.",
related_name="_subscriptionitem_tax_rates_+",
to="djstripe.TaxRate",
),
),
migrations.CreateModel(
name="DjstripeUpcomingInvoiceTotalTaxAmount",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"amount",
djstripe.fields.StripeQuantumCurrencyAmountField(
help_text="The amount, in cents, of the tax."
),
),
(
"inclusive",
models.BooleanField(
help_text="Whether this tax amount is inclusive or exclusive."
),
),
(
"invoice",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="+",
to="djstripe.UpcomingInvoice",
),
),
(
"tax_rate",
models.ForeignKey(
help_text="The tax rate that was applied to get this tax amount.",
on_delete=django.db.models.deletion.CASCADE,
to="djstripe.TaxRate",
),
),
],
options={"unique_together": {("invoice", "tax_rate")},},
),
migrations.CreateModel(
name="DjstripeInvoiceTotalTaxAmount",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"amount",
djstripe.fields.StripeQuantumCurrencyAmountField(
help_text="The amount, in cents, of the tax."
),
),
(
"inclusive",
models.BooleanField(
help_text="Whether this tax amount is inclusive or exclusive."
),
),
(
"invoice",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="total_tax_amounts",
to="djstripe.Invoice",
),
),
(
"tax_rate",
models.ForeignKey(
help_text="The tax rate that was applied to get this tax amount.",
on_delete=django.db.models.deletion.CASCADE,
to="djstripe.TaxRate",
),
),
],
options={"unique_together": {("invoice", "tax_rate")},},
),
]
2 changes: 2 additions & 0 deletions djstripe/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
Plan,
Subscription,
SubscriptionItem,
TaxRate,
UpcomingInvoice,
UsageRecord,
)
Expand Down Expand Up @@ -72,6 +73,7 @@
"StripeModel",
"Subscription",
"SubscriptionItem",
"TaxRate",
"Transfer",
"TransferReversal",
"UpcomingInvoice",
Expand Down
74 changes: 74 additions & 0 deletions djstripe/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,80 @@ def _stripe_object_to_customer(cls, target_cls, data):
if "customer" in data and data["customer"]:
return target_cls._get_or_create_from_stripe_object(data, "customer")[0]

@classmethod
def _stripe_object_to_default_tax_rates(cls, target_cls, data):
"""
Retrieves TaxRates for a Subscription or Invoice
:param target_cls:
:param data:
:param instance:
:type instance: Union[djstripe.models.Invoice, djstripe.models.Subscription]
:return:
"""
tax_rates = []

for tax_rate_data in data.get("default_tax_rates", []):
tax_rate, _ = target_cls._get_or_create_from_stripe_object(
tax_rate_data, refetch=False
)
tax_rates.append(tax_rate)

return tax_rates

@classmethod
def _stripe_object_to_tax_rates(cls, target_cls, data):
"""
Retrieves TaxRates for a SubscriptionItem or InvoiceItem
:param target_cls:
:param data:
:param instance:
:type instance: Union[djstripe.models.InvoiceItem,
djstripe.models.SubscriptionItem]
:return:
"""
tax_rates = []

for tax_rate_data in data.get("tax_rates", []):
tax_rate, _ = target_cls._get_or_create_from_stripe_object(
tax_rate_data, refetch=False
)
tax_rates.append(tax_rate)

return tax_rates

@classmethod
def _stripe_object_set_total_tax_amounts(cls, target_cls, data, instance):
"""
Set total tax amounts on Invoice instance
:param target_cls:
:param data:
:param instance:
:type instance: djstripe.models.Invoice
:return:
"""
from .billing import TaxRate

pks = []

for tax_amount_data in data.get("total_tax_amounts", []):
tax_rate_data = tax_amount_data["tax_rate"]
if isinstance(tax_rate_data, str):
tax_rate_data = {"tax_rate": tax_rate_data}

tax_rate, _ = TaxRate._get_or_create_from_stripe_object(
tax_rate_data, field_name="tax_rate", refetch=True
)
tax_amount, _ = target_cls.objects.update_or_create(
invoice=instance,
amount=tax_amount_data["amount"],
inclusive=tax_amount_data["inclusive"],
tax_rate=tax_rate,
)

pks.append(tax_amount.pk)

instance.total_tax_amounts.exclude(pk__in=pks).delete()

@classmethod
def _stripe_object_to_invoice_items(cls, target_cls, data, invoice):
"""
Expand Down
Loading

0 comments on commit adda5fb

Please sign in to comment.