Membership invitations and order emails #47

Merged
valberg merged 10 commits from benjaoming/membersystem:membership-invite into main 2024-08-14 09:17:30 +00:00
6 changed files with 56 additions and 6 deletions
Showing only changes of commit 9962f2622b - Show all commits

View file

@ -2,7 +2,11 @@
from django import forms from django import forms
from django.contrib import admin from django.contrib import admin
from django.contrib import messages
from django.db.models import QuerySet
from django.http import HttpRequest
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from membership.emails import OrderEmail
from . import models from . import models
@ -26,7 +30,7 @@ class OrderAdminForm(forms.ModelForm):
model = models.Order model = models.Order
exclude = () # noqa: DJ006 exclude = () # noqa: DJ006
def clean(self): # noqa: D102, ANN201 def clean(self): # noqa: ANN201
cd = super().clean() cd = super().clean()
if not cd["account"] and cd["member"]: if not cd["account"] and cd["member"]:
try: try:
@ -43,10 +47,25 @@ class OrderAdmin(admin.ModelAdmin):
inlines = (OrderProductInline,) inlines = (OrderProductInline,)
form = OrderAdminForm form = OrderAdminForm
actions = ("send_order",)
list_display = ("member", "description", "created", "is_paid", "total_with_vat") list_display = ("member", "description", "created", "is_paid", "total_with_vat")
search_fields = ("member__email", "membership__membership_type__name", "description") search_fields = ("member__email", "membership__membership_type__name", "description")
list_filter = ("is_paid", "membership__membership_type") list_filter = ("is_paid", "membership__membership_type")
@admin.action(description="Send order link to selected unpaid orders")
def send_order(self, request: HttpRequest, queryset: QuerySet[models.Order]) -> None:
for order in queryset:
if order.is_paid:
messages.error(
request,
f"Order pk={order.id} is already marked paid, not sending email to: {order.member.email}",
)
continue
email = OrderEmail(order, request)
email.send()
messages.success(request, f"Sent an order for order pk={order.id} link to: {order.member.email}")
@admin.register(models.Payment) @admin.register(models.Payment)
class PaymentAdmin(admin.ModelAdmin): class PaymentAdmin(admin.ModelAdmin):
@ -61,15 +80,15 @@ class PaymentAdmin(admin.ModelAdmin):
@admin.register(models.Product) @admin.register(models.Product)
class ProductAdmin(admin.ModelAdmin): # noqa: D101 class ProductAdmin(admin.ModelAdmin):
list_display = ("name", "price", "vat") list_display = ("name", "price", "vat")
class TransactionInline(admin.TabularInline): # noqa: D101 class TransactionInline(admin.TabularInline):
model = models.Transaction model = models.Transaction
@admin.register(models.Account) @admin.register(models.Account)
class AccountAdmin(admin.ModelAdmin): # noqa: D101 class AccountAdmin(admin.ModelAdmin):
list_display = ("owner", "balance") list_display = ("owner", "balance")
inlines = (TransactionInline,) inlines = (TransactionInline,)

View file

@ -5,6 +5,7 @@
* Generally, an email consists of templates (for body and subject) and a get_context() method. * Generally, an email consists of templates (for body and subject) and a get_context() method.
""" """
from accounting.models import Order
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.tokens import default_token_generator from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site from django.contrib.sites.shortcuts import get_current_site
@ -108,3 +109,18 @@ class InviteEmail(BaseEmail):
c["token"] = default_token_generator.make_token(self.membership.user) c["token"] = default_token_generator.make_token(self.membership.user)
c["referral_code"] = self.membership.referral_code c["referral_code"] = self.membership.referral_code
return c return c
class OrderEmail(BaseEmail):
template = "membership/emails/order.txt"
default_subject = _("Your data.coop order and payment")
def __init__(self, order: Order, request: HttpRequest, *args, **kwargs) -> None:
self.order = order
kwargs["user"] = order.member
super().__init__(request, *args, **kwargs)
def get_context_data(self) -> dict:
c = super().get_context_data()
c["order"] = self.order
return c

View file

@ -3,7 +3,7 @@
{% block content %}{% endblock %} {% block content %}{% endblock %}
Regards, {% trans "Cooperatively yours," %}
{{ site_name }} {{ site_name }}
{{ protocol }}://{{ domain }} {{ protocol }}://{{ domain }}

View file

@ -0,0 +1,6 @@
{% extends "membership/emails/base.txt" %}{% load i18n %}
{% block content %}{% url 'order:detail' order_id=order.id as order_url %}{% blocktrans %}You have an order in our system, you can pay it here:
{{ protocol }}://{{ domain }}{{ order_url }}
{% endblocktrans %}{% endblock %}

View file

@ -14,6 +14,10 @@
It is very much under construction. It is very much under construction.
</p> </p>
{% for order in unpaid_orders %}
<p>You have an unpaid order: <a href="{% url "order:detail" order_id=order.id %}">View Order ID {{ order.id }}</a></p>
{% endfor %}
{% comment %} {% comment %}
<hr> <hr>
<br> <br>

View file

@ -4,6 +4,7 @@ from __future__ import annotations
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from accounting.models import Order
from django_view_decorator import view from django_view_decorator import view
from utils.view_utils import render from utils.view_utils import render
@ -19,7 +20,11 @@ if TYPE_CHECKING:
) )
def index(request: HttpRequest) -> HttpResponse: def index(request: HttpRequest) -> HttpResponse:
"""View to show the index page.""" """View to show the index page."""
return render(request, "index.html") unpaid_orders = Order.objects.filter(member=request.user, is_paid=False)
context = {"unpaid_orders": list(unpaid_orders)}
return render(request, "index.html", context=context)
@view( @view(