Skip to content
Snippets Groups Projects
Commit 307033a7 authored by Tom Teichler's avatar Tom Teichler :beers:
Browse files

Merge branch 'mailing' into 'master'

Mailing

See merge request !5
parents b311bcb6 0dfd73bc
No related branches found
No related tags found
1 merge request!5Mailing
Pipeline #59582 failed
......@@ -6,7 +6,7 @@ MENUS = {
"name": _("Payments and Money"),
"url": "#",
"root": True,
"svg_icon": "mdi:piggy_bank",
"svg_icon": "mdi:piggy-bank",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
......
# Generated by Django 3.2.12 on 2022-03-09 20:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('tezor', '0003_manual_invoicing'),
]
operations = [
migrations.AddField(
model_name='client',
name='email',
field=models.EmailField(default='', max_length=254, verbose_name='Email'),
preserve_default=False,
),
]
......@@ -6,6 +6,7 @@ from aleksis.core.mixins import ExtensibleModel
class Client(ExtensibleModel):
name = models.CharField(verbose_name=_("Name"), max_length=255)
email = models.EmailField(verbose_name=_("Email"))
class Meta:
constraints = [
......
......@@ -81,6 +81,11 @@ class Invoice(BasePayment, PureDjangoModel):
)
items = models.ManyToManyField("InvoiceItem", verbose_name=_("Invoice items"))
class Meta:
permissions = (
("send_invoice_email", _("Can send invoice by email")),
)
@classmethod
def get_variant_choices(cls):
choices = []
......@@ -98,10 +103,12 @@ class Invoice(BasePayment, PureDjangoModel):
return self.__class__.STATUS_ICONS[self.status]
def get_purchased_items(self):
for item in self.items.all():
yield item.as_purchased_item()
if self.items.count():
for item in self.items.all():
yield item.as_purchased_item()
else:
return self.for_object.get_purchased_items()
for item in self.for_object.get_purchased_items():
yield item
def get_person(self):
if self.person:
......@@ -122,6 +129,12 @@ class Invoice(BasePayment, PureDjangoModel):
),
]
def get_billing_email_recipients(self):
if hasattr(self.for_object, "get_billing_email_recipients"):
return self.for_object.get_billing_email_recipients()
else:
return [self.billing_email]
@property
def purchased_items_table(self):
items = [i._asdict() for i in self.get_purchased_items()]
......@@ -154,11 +167,14 @@ class Invoice(BasePayment, PureDjangoModel):
return TotalsTable(values)
def get_success_url(self):
def get_absolute_url(self):
return reverse("invoice_by_token", kwargs={"slug": self.token})
def get_success_url(self):
return self.get_absolute_url()
def get_failure_url(self):
return reverse("invoice_by_token", kwargs={"slug": self.token})
return self.get_absolute_url()
class InvoiceItem(ExtensibleModel):
......
......@@ -134,3 +134,11 @@ print_invoice_predicate = (
view_invoice_predicate & display_billing_predicate & display_purchased_items_predicate
)
rules.add_perm("tezor.print_invoice_rule", print_invoice_predicate)
# Send invoice email
send_invoice_email_predicate = (
has_person & is_own_invoice
| has_global_perm("tezor.send_invoice_email")
| has_object_perm("tezor.send_invoice_email")
)
rules.add_perm("tezor.send_invoice_email_rule", send_invoice_email_predicate)
from aleksis.core.celery import app
from aleksis.core.util.email import send_email
from aleksis.core.util.pdf import generate_pdf_from_template
from .models.invoice import Invoice
@app.task
def email_invoice(invoice_token):
context = {}
invoice = Invoice.objects.get(token=invoice_token)
context["invoice"] = invoice
invoice_pdf, result = generate_pdf_from_template(invoice.group.template_name, context)
result.wait(timeout=30, disable_sync_subtasks=False)
invoice_pdf.refresh_from_db()
send_email(
template_name="invoice",
from_email=invoice.group.client.email,
recipient_list=invoice.get_billing_email_recipients(),
context=context,
attachments=[(invoice_pdf.file.name, invoice_pdf.file.read(), "application/pdf")],
)
{% extends "templated_email/base.email" %}
{% load i18n %}
{% block subject_content %}{% trans "Invoice" %} {{ invoice.number }}{% endblock %}
{% block html_content %}
<p>
{% blocktrans with number=invoice.number description=invoice.description %}
Please find attached invoice number {{ number }} for {{ description }}.
Please carefully read the PDF file concerning all payment details.
{% endblocktrans %}
</p>
{% if invoice.status == "waiting" %}
<p>
{% blocktrans %}
Please visit the following link to view and make the payment:
{% endblocktrans %}
</p>
<a href="{{ BASE_URL }}{{ invoice.get_absolute_url }}">{{ BASE_URL }}{{ invoice.get_absolute_url}}</a>
{% endif %}
{% endblock %}
......@@ -12,6 +12,7 @@
{% has_perm 'tezor.display_purchased_items_rule' user object as can_view_purchased_items %}
{% has_perm 'tezor.display_billing_rule' user object as can_view_billing_information %}
{% has_perm 'tezor.print_invoice_rule' user object as can_print_invoice %}
{% has_perm 'tezor.send_invoice_email_rule' user object as can_send_invoice_email %}
<h1>{% trans "Invoice" %} {{ object.number }} — {{ object.created.date }}</h1>
......@@ -21,6 +22,9 @@
{% if can_print_invoice %}
<a class="btn colour-primary waves-effect waves-light" href="{% url 'print_invoice' object.token %}">{% trans "Print" %}</a>
{% endif %}
{% if can_send_invoice_email %}
<a class="btn colour-primary waves-effect waves-light" href="{% url 'send_invoice_by_token' object.token %}">{% trans "Send Email" %}</a>
{% endif %}
<div class="row">
{% if can_view_billing_information %}
......
......@@ -56,4 +56,9 @@ urlpatterns = [
views.InvoiceDetailView.as_view(),
name="invoice_by_token",
),
path(
"invoice/<str:token>/send/",
views.SendInvoiceEmail.as_view(),
name="send_invoice_by_token",
),
]
......@@ -9,14 +9,18 @@ from django.views.generic.detail import DetailView
from django_tables2.views import RequestConfig, SingleTableView
from payments import PaymentStatus, RedirectNeeded, get_payment_model
from rules.contrib.views import PermissionRequiredMixin
from django_tables2.views import SingleTableView, RequestConfig
from templated_email import InlineImage, send_templated_mail
from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
from aleksis.core.util.pdf import generate_pdf_from_template
from aleksis.core.views import RenderPDFView
from .forms import EditClientForm, EditInvoiceGroupForm
from .models.base import Client
from .models.invoice import Invoice, InvoiceGroup
from .tables import ClientsTable, InvoiceGroupsTable, InvoicesTable
from .tasks import email_invoice
class GetInvoicePDF(PermissionRequiredMixin, RenderPDFView):
......@@ -29,7 +33,6 @@ class GetInvoicePDF(PermissionRequiredMixin, RenderPDFView):
self.template_name = invoice.group.template_name
context["invoice"] = invoice
print(invoice.group.__dict__)
return context
......@@ -189,3 +192,17 @@ class InvoiceDetailView(PermissionRequiredMixin, DetailView):
slug_field = "token"
permission_required = "tezor.view_invoice_rule"
template_name = "tezor/invoice/full.html"
class SendInvoiceEmail(PermissionRequiredMixin, View):
permission_required = "tezor.send_invoice_email_rule"
def get(self, request, token):
email_invoice.delay(token)
url = request.META.get("HTTP_REFERRER")
if not url:
url = Invoice.objects.get(token=token).get_absolute_url()
return redirect(url)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment