Skip to content

Commit

Permalink
Merge pull request #69 from DeeeeLAN/main
Browse files Browse the repository at this point in the history
Add Discounts and admin updates. Change account_id to be optional on Subscription from_paddle_data
  • Loading branch information
bgervan authored Jun 7, 2024
2 parents 5112b88 + 0641384 commit 6544964
Show file tree
Hide file tree
Showing 9 changed files with 530 additions and 115 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ install:

.PHONY: pre-commit-install
pre-commit-install:
pip install pre-commit
pip install pre-commit
pre-commit install

#* Formatters
Expand Down
5 changes: 3 additions & 2 deletions example/django_billing/django_billing/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django_paddle_billing.urls import urlpatterns as paddle_billing_urls

from billing.urls import urlpatterns as billing_urls
from django.contrib import admin
from django.urls import path

from django_paddle_billing.urls import urlpatterns as paddle_billing_urls

from .api import api

urlpatterns = [
Expand All @@ -27,4 +29,3 @@
]
urlpatterns += billing_urls
urlpatterns += paddle_billing_urls

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ style = [
]
fmt = [
"black {args:.}",
# "ruff --fix {args:.}",
"ruff --fix {args:.}",
"style",
]
all = [
Expand Down
2 changes: 1 addition & 1 deletion src/django_paddle_billing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def __getattr__(self, name):
if name in default_settings:
return default_settings[name]

raise AttributeError("'PADDLE_BILLING' settings object has no attribute '%s'" % name)
raise AttributeError(f"'PADDLE_BILLING' settings object has no attribute '{name}'")


settings = AppSettings()
265 changes: 263 additions & 2 deletions src/django_paddle_billing/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,16 @@
from django.db import models

from django_paddle_billing import settings as app_settings
from django_paddle_billing.models import Address, Business, Customer, Price, Product, Subscription, Transaction
from django_paddle_billing.models import (
Address,
Business,
Customer,
Discount,
Price,
Product,
Subscription,
Transaction,
)

# Check if unfold is in installed apps
if "unfold" in settings.INSTALLED_APPS:
Expand Down Expand Up @@ -53,17 +62,41 @@ class TransactionInline(TabularInline):

@admin.register(Address)
class AddressAdmin(ModelAdmin):
list_display = ["customer_email", "country_code", "postal_code", "status"]
inlines = (SubscriptionInline,)
formfield_overrides: typing.ClassVar = {
models.JSONField: {"widget": app_settings.ADMIN_JSON_EDITOR_WIDGET},
}

def has_change_permission(self, request, obj=None):
return app_settings.ADMIN_READONLY
return not app_settings.ADMIN_READONLY

def customer_email(self, obj=None):
if obj and obj.customer:
return obj.customer.email
if obj:
return obj.id
return ""

def postal_code(self, obj=None):
if obj and obj.data:
return obj.data.get("postal_code", "")
return ""

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")
return ""


@admin.register(Business)
class BusinessAdmin(ModelAdmin):
list_display = [
"name",
"company_number",
"tax_identifier",
"status",
]
inlines = (SubscriptionInline,)
formfield_overrides: typing.ClassVar = {
models.JSONField: {"widget": app_settings.ADMIN_JSON_EDITOR_WIDGET},
Expand All @@ -72,9 +105,36 @@ class BusinessAdmin(ModelAdmin):
def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def name(self, obj=None):
if obj and obj.data:
return obj.data.get("name", obj.id)
if obj:
return obj.id
return ""

def company_number(self, obj=None):
if obj and obj.data:
return obj.data.get("company_number", "")
return ""

def tax_identifier(self, obj=None):
if obj and obj.data:
return obj.data.get("tax_identifier", "")
return ""

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")
return ""


@admin.register(Product)
class ProductAdmin(ModelAdmin):
list_display = [
"name",
"status",
"created_at",
]
search_fields = ["id", "name"]
inlines = (PriceInline,)
formfield_overrides: typing.ClassVar = {
Expand All @@ -87,16 +147,119 @@ def has_change_permission(self, request, obj=None):

@admin.register(Price)
class PriceAdmin(ModelAdmin):
list_display = [
"name",
"unit_price",
"description",
"status",
"trial_period",
"billing_cycle",
]
formfield_overrides: typing.ClassVar = {
models.JSONField: {"widget": app_settings.ADMIN_JSON_EDITOR_WIDGET},
}

def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def billing_cycle(self, obj=None):
if obj and obj.data and obj.data.get("billing_cycle"):
return f'{obj.data["billing_cycle"]["frequency"]} {obj.data["billing_cycle"]["interval"]}'
return ""

def description(self, obj=None):
if obj and obj.data:
return obj.data.get("description", "")

def name(self, obj=None):
if obj and obj.data:
return obj.data.get("name", obj.id)
if obj:
return obj.id
return ""

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")

def trial_period(self, obj=None):
if obj and obj.data and obj.data.get("trial_period"):
return f'{obj.data["trial_period"]["frequency"]} {obj.data["trial_period"]["interval"]}'
return ""

def unit_price(self, obj=None):
if obj and obj.data and obj.data.get("unit_price"):
return f'{int(obj.data["unit_price"]["amount"]) / 100} {obj.data["unit_price"]["currency_code"]}'
return ""


@admin.register(Discount)
class DiscountAdmin(ModelAdmin):
list_display = [
"discount_description",
"amount",
"applies_to",
"status",
"discount_code",
"uses_left",
"expires",
]
formfield_overrides: typing.ClassVar = {
models.JSONField: {"widget": app_settings.ADMIN_JSON_EDITOR_WIDGET},
}

def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def discount_description(self, obj=None):
if obj and obj.data:
return obj.data.get("description", obj.id)
if obj:
return obj.id
return ""

def amount(self, obj=None):
if obj and obj.data:
if obj.data.get("type") == "percentage":
return f"{obj.data.get('amount', '')}%"
return f'{obj.data.get("amount", "")} {obj.data.get("currency_code", "")}'
return ""

def applies_to(self, obj=None):
if obj and obj.data:
return obj.data.get("restrict_to", "")
return ""

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")
return ""

def discount_code(self, obj=None):
if obj and obj.data:
return obj.data.get("code", "")
return ""

def uses_left(self, obj=None):
if obj and obj.data:
return obj.data.get("usage_limit", "")
return ""

def expires(self, obj=None):
if obj and obj.data:
return obj.data.get("expires_at", "")
return ""


@admin.register(Subscription)
class SubscriptionAdmin(ModelAdmin):
list_display = [
"customer_email",
"name",
"price",
"next_payment",
"status",
]
inlines = (
TransactionInline,
ProductInline,
Expand All @@ -110,9 +273,53 @@ class SubscriptionAdmin(ModelAdmin):
def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def customer_email(self, obj=None):
if obj and obj.customer:
return obj.customer.email
if obj:
return obj.id
return ""

def name(self, obj=None):
if obj and obj.data:
try:
return ", ".join([item["price"]["name"] for item in obj.data["items"]])
except Exception:
return ""
return ""

def price(self, obj=None):
if obj and obj.data:
try:
unit_price = [int(item["price"]["unit_price"]["amount"]) / 100 for item in obj.data["items"]]
frequency = [item["price"]["billing_cycle"] for item in obj.data["items"]]
return ", ".join(
[
f"{unit_price[i]}/{frequency[i]['frequency']} {frequency[i]['interval']}"
for i in range(len(unit_price))
]
)
except Exception:
return ""
return ""

def next_payment(self, obj=None):
if obj and obj.data:
try:
return obj.data["next_billed_at"]
except Exception:
return ""
return ""


@admin.register(Customer)
class CustomerAdmin(ModelAdmin):
list_display = [
"email",
"name",
"status",
"created_at",
]
inlines = (
AddressInline,
BusinessInline,
Expand All @@ -126,12 +333,66 @@ class CustomerAdmin(ModelAdmin):
def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")


@admin.register(Transaction)
class TransactionAdmin(ModelAdmin):
list_display = ["customer_email", "payment_amount", "payment_method", "date_paid", "products", "status"]
formfield_overrides: typing.ClassVar = {
models.JSONField: {"widget": app_settings.ADMIN_JSON_EDITOR_WIDGET},
}

def has_change_permission(self, request, obj=None):
return not app_settings.ADMIN_READONLY

def customer_email(self, obj=None):
if obj and obj.customer:
return obj.customer.email
if obj:
return obj.id
return ""

def payment_amount(self, obj=None):
if obj and obj.data:
try:
return int(obj.data["details"]["totals"]["total"]) / 100
except Exception:
return ""
return ""

def payment_method(self, obj=None):
if obj and obj.data:
try:
return (
f'{obj.data["payments"][0]["method_details"]["card"]["type"]} '
f'{obj.data["payments"][0]["method_details"]["card"]["last4"]}'
)
except Exception:
return ""
return ""

def date_paid(self, obj=None):
if obj and obj.data:
try:
return obj.data["payments"][0]["captured_at"]
except Exception:
return ""
return ""

def products(self, obj=None):
if obj and obj.data:
items = obj.data.get("items", [])
# return items
products = Product.objects.filter(id__in=[item["price"]["product_id"] for item in items])
return ", ".join([product.name for product in products])
return ""

products.short_description = "Product(s)"

def status(self, obj=None):
if obj and obj.data:
return obj.data.get("status", "")
return ""
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ def handle(self, *args, **options):

Price.sync_from_paddle()

# ----------------
# Discounts
from django_paddle_billing.models import Discount

Discount.sync_from_paddle()

# ----------------
# Customers
from django_paddle_billing.models import Customer
Expand Down
Loading

0 comments on commit 6544964

Please sign in to comment.