Ver 10.4 fix mail to spark

This commit is contained in:
Flatlogic Bot 2026-02-04 17:36:56 +00:00
parent 60ef14cac4
commit 0702dcda68
7 changed files with 212 additions and 18 deletions

Binary file not shown.

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
@page {
size: a4 portrait;
margin: 2cm;
@frame footer_frame { /* Another static Frame */
-pdf-frame-content: footerContent;
bottom: 1cm;
margin-left: 1cm;
margin-right: 1cm;
height: 1cm;
}
}
body { font-family: Helvetica, sans-serif; font-size: 12pt; line-height: 1.5; color: #333; }
.header { text-align: center; border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 20px; }
.title { font-size: 24pt; font-weight: bold; text-transform: uppercase; color: #000; }
.subtitle { font-size: 14pt; color: #666; margin-top: 5px; }
.meta { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; background-color: #f9f9f9; }
.items-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.items-table th { border-bottom: 1px solid #000; padding: 8px; text-align: left; background-color: #eee; font-weight: bold; }
.items-table td { border-bottom: 1px solid #eee; padding: 8px; text-align: left; }
.totals { text-align: right; margin-top: 20px; border-top: 2px solid #333; padding-top: 10px; }
.total-row { font-size: 16pt; font-weight: bold; color: #000; }
.footer { text-align: center; font-size: 10pt; color: #777; }
.positive { color: green; }
.negative { color: red; }
.text-right { text-align: right; }
</style>
</head>
<body>
<div class="header">
<div class="title">Payslip</div>
<div class="subtitle">Reference: #{{ record.id }}</div>
</div>
<div class="meta">
<strong>Worker:</strong> {{ record.worker.name }}<br>
<strong>ID Number:</strong> {{ record.worker.id_no }}<br>
<strong>Date:</strong> {{ record.date }}
</div>
<table class="items-table">
<thead>
<tr>
<th>Description</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
<!-- Base Pay -->
<tr>
<td>Base Pay ({{ logs_count }} days worked)</td>
<td class="text-right">R {{ logs_amount|floatformat:2 }}</td>
</tr>
<!-- Adjustments -->
{% for adj in adjustments %}
<tr>
<td>{{ adj.get_type_display }}: {{ adj.description }}</td>
<td class="text-right">
R {{ adj.amount|floatformat:2 }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="totals">
<p class="total-row">Net Pay: R {{ record.amount|floatformat:2 }}</p>
</div>
<div id="footerContent" class="footer">
Generated by Fox Fitt App for {{ record.worker.name }} | {% now "Y-m-d H:i" %}
</div>
</body>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
@page {
size: a4 portrait;
margin: 2cm;
@frame footer_frame {
-pdf-frame-content: footerContent;
bottom: 1cm;
margin-left: 1cm;
margin-right: 1cm;
height: 1cm;
}
}
body { font-family: Helvetica, sans-serif; font-size: 12pt; line-height: 1.5; color: #333; }
.header { text-align: center; border-bottom: 2px solid #333; padding-bottom: 10px; margin-bottom: 20px; }
.vendor-name { font-size: 24pt; font-weight: bold; text-transform: uppercase; color: #000; }
.sub-header { font-size: 12pt; color: #666; }
.meta { margin-bottom: 20px; padding: 10px; border: 1px solid #ddd; background-color: #f9f9f9; }
.items-table { width: 100%; border-collapse: collapse; margin-bottom: 20px; }
.items-table th { border-bottom: 1px solid #000; padding: 8px; text-align: left; background-color: #eee; font-weight: bold; }
.items-table td { border-bottom: 1px solid #eee; padding: 8px; text-align: left; }
.totals { text-align: right; margin-top: 20px; }
.total-row { font-size: 16pt; font-weight: bold; border-top: 1px solid #000; padding-top: 5px; margin-top: 5px; }
.footer { text-align: center; font-size: 10pt; color: #777; }
.text-right { text-align: right; }
</style>
</head>
<body>
<div class="header">
<div class="sub-header">RECEIPT FROM</div>
<div class="vendor-name">{{ receipt.vendor }}</div>
</div>
<div class="meta">
<strong>Date:</strong> {{ receipt.date }}<br>
<strong>Payment Method:</strong> {{ receipt.get_payment_method_display }}<br>
<strong>Description:</strong> {{ receipt.description|default:"-" }}
</div>
<table class="items-table">
<thead>
<tr>
<th>Item</th>
<th class="text-right">Amount</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.product }}</td>
<td class="text-right">R {{ item.amount|floatformat:2 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="totals">
<p>Subtotal: R {{ receipt.subtotal|floatformat:2 }}</p>
<p>VAT ({{ receipt.get_vat_type_display }}): R {{ receipt.vat_amount|floatformat:2 }}</p>
<p class="total-row">Total: R {{ receipt.total_amount|floatformat:2 }}</p>
</div>
<div id="footerContent" class="footer">
Generated by {{ receipt.user.get_full_name|default:receipt.user.username }} via Fox Fitt App
</div>
</body>
</html>

20
core/utils.py Normal file
View File

@ -0,0 +1,20 @@
from io import BytesIO
from django.template.loader import get_template
from xhtml2pdf import pisa
from django.conf import settings
import os
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
# Enable logging for debugging
# pisa.showLogging()
# Create the PDF
pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result)
if not pdf.err:
return result.getvalue()
return None

View File

@ -8,7 +8,7 @@ from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.contrib.auth.decorators import login_required, user_passes_test
from django.db.models import Sum, Q, Prefetch
from django.core.mail import send_mail
from django.core.mail import send_mail, EmailMultiAlternatives
from django.conf import settings
from django.contrib import messages
from django.http import JsonResponse, HttpResponse
@ -18,6 +18,7 @@ from .models import Worker, Project, Team, WorkLog, PayrollRecord, Loan, Payroll
from .forms import WorkLogForm, ExpenseReceiptForm, ExpenseLineItemFormSet
from datetime import timedelta
from decimal import Decimal
from core.utils import render_to_pdf
def is_staff_or_supervisor(user):
"""Check if user is staff or manages at least one team/project."""
@ -591,26 +592,37 @@ def process_payment(request, worker_id):
# Email Notification
subject = f"Payslip for {worker.name} - {payroll_record.date}"
# Prepare HTML content
# Prepare Context
context = {
'record': payroll_record,
'logs_count': log_count,
'logs_amount': logs_amount,
'adjustments': payroll_record.adjustments.all(),
}
# 1. Render HTML Body
html_message = render_to_string('core/email/payslip_email.html', context)
plain_message = strip_tags(html_message)
# 2. Render PDF Attachment
pdf_content = render_to_pdf('core/pdf/payslip_pdf.html', context)
recipient_list = ['foxfitt-ed9wc+expense@to.sparkreceipt.com']
try:
send_mail(
# Construct Email with Attachment
email = EmailMultiAlternatives(
subject,
plain_message,
settings.DEFAULT_FROM_EMAIL,
recipient_list,
html_message=html_message
)
email.attach_alternative(html_message, "text/html")
if pdf_content:
email.attach(f"Payslip_{worker.id}_{payroll_record.date}.pdf", pdf_content, 'application/pdf')
email.send()
messages.success(request, f"Payment processed for {worker.name}. Net Pay: R {payroll_record.amount}")
except Exception as e:
messages.warning(request, f"Payment processed, but email delivery failed: {str(e)}")
@ -780,21 +792,33 @@ def create_receipt(request):
subject = f"Receipt from {receipt.vendor} - {receipt.date}"
recipient_list = ['foxfitt-ed9wc+expense@to.sparkreceipt.com']
# Prepare HTML content
html_message = render_to_string('core/email/receipt_email.html', {
# Prepare Context
context = {
'receipt': receipt,
'items': line_items,
})
}
# 1. Render HTML Body
html_message = render_to_string('core/email/receipt_email.html', context)
plain_message = strip_tags(html_message)
# 2. Render PDF Attachment
pdf_content = render_to_pdf('core/pdf/receipt_pdf.html', context)
try:
send_mail(
# Construct Email with Attachment
email = EmailMultiAlternatives(
subject,
plain_message,
settings.DEFAULT_FROM_EMAIL,
recipient_list,
html_message=html_message
)
email.attach_alternative(html_message, "text/html")
if pdf_content:
email.attach(f"Receipt_{receipt.id}.pdf", pdf_content, 'application/pdf')
email.send()
messages.success(request, "Receipt created and sent to SparkReceipt.")
return redirect('create_receipt')
except Exception as e:

View File

@ -2,3 +2,4 @@ Django==5.2.7
mysqlclient==2.2.7
python-dotenv==1.1.1
Pillow
xhtml2pdf