From 9962f2622bf5f47468144fa7cbcb228566b00acf Mon Sep 17 00:00:00 2001 From: Benjamin Bach Date: Mon, 12 Aug 2024 22:22:11 +0200 Subject: [PATCH] Send order emails via admin action --- src/accounting/admin.py | 27 ++++++++++++++++--- src/membership/emails.py | 16 +++++++++++ .../templates/membership/emails/base.txt | 2 +- .../templates/membership/emails/order.txt | 6 +++++ src/project/templates/index.html | 4 +++ src/project/views.py | 7 ++++- 6 files changed, 56 insertions(+), 6 deletions(-) create mode 100644 src/membership/templates/membership/emails/order.txt diff --git a/src/accounting/admin.py b/src/accounting/admin.py index 11d2219..1b09077 100644 --- a/src/accounting/admin.py +++ b/src/accounting/admin.py @@ -2,7 +2,11 @@ from django import forms 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 membership.emails import OrderEmail from . import models @@ -26,7 +30,7 @@ class OrderAdminForm(forms.ModelForm): model = models.Order exclude = () # noqa: DJ006 - def clean(self): # noqa: D102, ANN201 + def clean(self): # noqa: ANN201 cd = super().clean() if not cd["account"] and cd["member"]: try: @@ -43,10 +47,25 @@ class OrderAdmin(admin.ModelAdmin): inlines = (OrderProductInline,) form = OrderAdminForm + actions = ("send_order",) + list_display = ("member", "description", "created", "is_paid", "total_with_vat") search_fields = ("member__email", "membership__membership_type__name", "description") 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) class PaymentAdmin(admin.ModelAdmin): @@ -61,15 +80,15 @@ class PaymentAdmin(admin.ModelAdmin): @admin.register(models.Product) -class ProductAdmin(admin.ModelAdmin): # noqa: D101 +class ProductAdmin(admin.ModelAdmin): list_display = ("name", "price", "vat") -class TransactionInline(admin.TabularInline): # noqa: D101 +class TransactionInline(admin.TabularInline): model = models.Transaction @admin.register(models.Account) -class AccountAdmin(admin.ModelAdmin): # noqa: D101 +class AccountAdmin(admin.ModelAdmin): list_display = ("owner", "balance") inlines = (TransactionInline,) diff --git a/src/membership/emails.py b/src/membership/emails.py index f1d96fc..6a57718 100644 --- a/src/membership/emails.py +++ b/src/membership/emails.py @@ -5,6 +5,7 @@ * 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.auth.tokens import default_token_generator 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["referral_code"] = self.membership.referral_code 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 diff --git a/src/membership/templates/membership/emails/base.txt b/src/membership/templates/membership/emails/base.txt index 9a86384..636aee0 100644 --- a/src/membership/templates/membership/emails/base.txt +++ b/src/membership/templates/membership/emails/base.txt @@ -3,7 +3,7 @@ {% block content %}{% endblock %} -Regards, +{% trans "Cooperatively yours," %} {{ site_name }} {{ protocol }}://{{ domain }} diff --git a/src/membership/templates/membership/emails/order.txt b/src/membership/templates/membership/emails/order.txt new file mode 100644 index 0000000..e976444 --- /dev/null +++ b/src/membership/templates/membership/emails/order.txt @@ -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 %} diff --git a/src/project/templates/index.html b/src/project/templates/index.html index 2cc3490..1cc49e6 100644 --- a/src/project/templates/index.html +++ b/src/project/templates/index.html @@ -14,6 +14,10 @@ It is very much under construction.

+ {% for order in unpaid_orders %} +

You have an unpaid order: View Order ID {{ order.id }}

+ {% endfor %} + {% comment %}

diff --git a/src/project/views.py b/src/project/views.py index 7fbde77..affd1e7 100644 --- a/src/project/views.py +++ b/src/project/views.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING +from accounting.models import Order from django_view_decorator import view from utils.view_utils import render @@ -19,7 +20,11 @@ if TYPE_CHECKING: ) def index(request: HttpRequest) -> HttpResponse: """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(