Skip to content

Commit

Permalink
feat: include subscription period in the invoice
Browse files Browse the repository at this point in the history
  • Loading branch information
nijel committed Oct 24, 2024
1 parent 18f861b commit 08416b8
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 9 deletions.
29 changes: 22 additions & 7 deletions wlhosted/integrations/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#

from __future__ import annotations
from django.utils import timezone

import datetime
from django import forms
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from weblate.billing.models import Billing, Plan

from wlhosted.integrations.utils import get_origin
from wlhosted.payments.models import Customer, Payment
from wlhosted.payments.models import Customer, Payment, get_period_delta, date_format


class ChooseBillingForm(forms.Form):
Expand Down Expand Up @@ -73,16 +77,27 @@ def create_payment(self, user):

plan = self.cleaned_data["plan"]
period = self.cleaned_data["period"]
description = "Weblate hosting ({}, {})".format(
plan.name, "Monthly" if period == "m" else "Yearly"
)
extra = {
"plan": plan.pk,
"period": period,
}
if billing := self.cleaned_data["billing"]:
extra["billing"] = billing.pk
try:
invoice = billing.get_last_invoice_object()
except IndexError:
start_date = timezone.now()
else:
start_date = invoice.end + datetime.timedelta(days=1)
else:
start_date = timezone.now()
end_date = start_date + get_period_delta(period)

description = f"Weblate hosting ({plan.name}) [{date_format(start_date)} - {date_format(end_date)}]"
amount = plan.price if period == "m" else plan.yearly_price
if self.cleaned_data["extra_domain"]:
amount += 100
description += " + Custom domain"
extra = {"plan": plan.pk, "period": period}
if self.cleaned_data["billing"]:
extra["billing"] = self.cleaned_data["billing"].pk
return Payment.objects.create(
amount=amount,
description=description,
Expand Down
7 changes: 7 additions & 0 deletions wlhosted/integrations/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from wlhosted.integrations.models import handle_received_payment
from wlhosted.integrations.utils import get_origin
from wlhosted.payments.models import Payment
from wlhosted.payments.models import get_period_delta, date_format


@app.task
Expand Down Expand Up @@ -74,10 +75,16 @@ def recurring_payments():

original = Payment.objects.get(pk=billing.payment["recurring"])

start_date = last_invoice.end + timedelta(days=1)
end_date = start_date + get_period_delta(original.extra["period"])

description = f"Weblate hosting ({billing.plan.name}) [{date_format(start_date)} - {date_format(end_date)}]"

repeated = original.repeat_payment(
amount=billing.plan.price
if original.extra["period"] == "m"
else billing.plan.yearly_price,
description=description,
billing=billing.pk,
)
if not repeated:
Expand Down
16 changes: 14 additions & 2 deletions wlhosted/payments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import requests
from appconf import AppConf
from dateutil.relativedelta import relativedelta
from datetime import datetime
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.serializers.json import DjangoJSONEncoder
Expand Down Expand Up @@ -273,7 +274,11 @@ def get_payment_url(self):
return settings.PAYMENT_REDIRECT_URL.format(language=language, uuid=self.uuid)

def repeat_payment(
self, skip_previous: bool = False, amount: int | None = None, **kwargs
self,
skip_previous: bool = False,
amount: int | None = None,
description: str | None = None,
**kwargs,
):
# Check if backend is still valid
from wlhosted.payments.backends import get_backend
Expand All @@ -283,6 +288,9 @@ def repeat_payment(
except KeyError:
return False

if description is None:
description = self.description

with transaction.atomic(using="payments_db"):
# Check for failed payments
previous = Payment.objects.filter(repeat=self)
Expand All @@ -305,7 +313,7 @@ def repeat_payment(
return Payment.objects.create(
amount=self.amount if amount is None else amount,
backend=self.backend,
description=self.description,
description=description,
recurring="",
customer=self.customer,
amount_fixed=self.amount_fixed,
Expand Down Expand Up @@ -346,3 +354,7 @@ def get_period_delta(period):
if period == "m":
return relativedelta(months=1) - relativedelta(days=1)
raise ValueError(f"Invalid payment period {period!r}!")


def date_format(value: datetime) -> str:
return value.strftime("%-d %b %Y")

0 comments on commit 08416b8

Please sign in to comment.