diff --git a/backend/admin.py b/backend/admin.py index 5cecd24cd..f60925484 100644 --- a/backend/admin.py +++ b/backend/admin.py @@ -24,7 +24,6 @@ FileStorageFile, MultiFileUpload, ) - from backend.finance.models import ( Invoice, InvoiceURL, @@ -36,7 +35,7 @@ ReceiptDownloadToken, ) -from backend.clients.models import Client +from backend.clients.models import Client, DefaultValues from settings.settings import BILLING_ENABLED @@ -68,6 +67,7 @@ InvoiceRecurringProfile, FileStorageFile, MultiFileUpload, + DefaultValues, ] ) diff --git a/backend/core/api/public/permissions.py b/backend/core/api/public/permissions.py index 69beaa4b2..9893a41cc 100644 --- a/backend/core/api/public/permissions.py +++ b/backend/core/api/public/permissions.py @@ -21,6 +21,7 @@ "team_permissions:write", "team:invite", "team:kick", + "account_defaults:write", "email_templates:read", "email_templates:write", } @@ -40,6 +41,7 @@ "team:kick": {"team:kick", "team:invite"}, "email_templates:read": {"email_templates:read"}, "email_templates:write": {"email_templates:read", "email_templates:write"}, + "account_defaults:write": {"account_defaults:write"}, } SCOPE_DESCRIPTIONS = { @@ -50,6 +52,7 @@ "team_permissions": {"description": "Access team permissions", "options": {"read": "Read only", "write": "Read and write"}}, "team": {"description": "Invite team members", "options": {"invite": "Invite members"}}, "email_templates": {"description": "Access email templates", "options": {"read": "Read only", "write": "Read and write"}}, + "account_defaults": {"description": "Modify account defaults", "options": {"write": "Read and write"}}, } if settings.BILLING_ENABLED: diff --git a/backend/core/api/settings/email_templates.py b/backend/core/api/settings/email_templates.py index 83e3b2edf..97458738d 100644 --- a/backend/core/api/settings/email_templates.py +++ b/backend/core/api/settings/email_templates.py @@ -1,9 +1,32 @@ -from django.views.decorators.http import require_GET +from django.contrib import messages +from django.shortcuts import render +from django.views.decorators.http import require_POST +from backend.core.service.defaults.get import get_account_defaults from backend.decorators import web_require_scopes from backend.core.types.requests import WebRequest -@require_GET -@web_require_scopes("email_templates:read") -def get_current_email_template(request: WebRequest): ... +@require_POST +@web_require_scopes(["email_templates:write", "account_defaults:write"], True, True) +def save_email_template(request: WebRequest, template: str): + template = template.lower().strip() + content = request.POST.get("content") + + if template not in ["invoice_created", "invoice_overdue", "invoice_cancelled"]: + messages.error(request, f"Invalid template: {template}") + return render(request, "base/toast.html") + + if content is None: + messages.error(request, f"Missing content for template: {template}") + return render(request, "base/toast.html") + + acc_defaults = get_account_defaults(request.actor) + + setattr(acc_defaults, f"email_template_recurring_invoices_{template}", content) + + acc_defaults.save() + + messages.success(request, f"Email template '{template}' saved successfully") + + return render(request, "base/toast.html") diff --git a/backend/core/api/settings/urls.py b/backend/core/api/settings/urls.py index d448f8133..56e54a7aa 100644 --- a/backend/core/api/settings/urls.py +++ b/backend/core/api/settings/urls.py @@ -3,6 +3,7 @@ from . import change_name, profile_picture, preferences from .api_keys import generate_api_key_endpoint, revoke_api_key_endpoint from .defaults import handle_client_defaults_endpoints, remove_client_default_logo_endpoint +from .email_templates import save_email_template urlpatterns = [ path( @@ -22,6 +23,7 @@ path("client_defaults/", handle_client_defaults_endpoints, name="client_defaults without client"), path("client_defaults/remove_default_logo/", remove_client_default_logo_endpoint, name="client_defaults remove logo without client"), path("client_defaults/remove_default_logo/", remove_client_default_logo_endpoint, name="client_defaults remove logo"), + path("email_templates//save/", save_email_template, name="email_template save"), ] app_name = "settings" diff --git a/backend/core/service/invoices/common/emails/on_create.py b/backend/core/service/invoices/common/emails/on_create.py index 7c43400c3..227bd6291 100644 --- a/backend/core/service/invoices/common/emails/on_create.py +++ b/backend/core/service/invoices/common/emails/on_create.py @@ -36,7 +36,7 @@ def on_create_invoice_email_service(users_email: str, invoice: Invoice) -> OnCre "first_name": invoice.client_to.name.split(" ")[0] if invoice.client_to else invoice.client_name, "invoice_id": invoice.id, "invoice_ref": invoice.reference or invoice.invoice_number or invoice.id, - "due_date": invoice.date_due.strftime("%a %m %Y"), + "due_date": invoice.date_due.strftime("%A, %B %d, %Y"), "amount_due": invoice.get_total_price(), "currency": invoice.currency, "currency_symbol": invoice.get_currency_symbol(), diff --git a/frontend/templates/pages/settings/settings/email_templates/tabs.html b/frontend/templates/pages/settings/settings/email_templates/tabs.html index 1259e0a3e..2a098f1ee 100644 --- a/frontend/templates/pages/settings/settings/email_templates/tabs.html +++ b/frontend/templates/pages/settings/settings/email_templates/tabs.html @@ -30,16 +30,16 @@ {% for template in 'invoice_created,invoice_overdue,invoice_cancelled'|split:"," %} -

{{ template|split:"_"|join:" "|title }}

- {% spaceless %} - - {% endspaceless %} + {% with text=email_templates.recurring_invoices|dict_get:template %} + + {% endwith %} -
+ {% endfor %}