new: basic email template with csv attachments for the orded and each line
This commit is contained in:
parent
5848134067
commit
0148d193ee
8 changed files with 126 additions and 143 deletions
|
|
@ -9,47 +9,60 @@ class Migration(migrations.Migration):
|
|||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('order', '0001_initial'),
|
||||
("order", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='saleorder',
|
||||
name='state',
|
||||
model_name="saleorder",
|
||||
name="state",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='saleorder',
|
||||
name='sold_at',
|
||||
model_name="saleorder",
|
||||
name="sold_at",
|
||||
field=models.DateTimeField(default=None, max_length=200, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='saleorder',
|
||||
name='sold_to',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.DO_NOTHING, to=settings.AUTH_USER_MODEL),
|
||||
model_name="saleorder",
|
||||
name="sold_to",
|
||||
field=models.ForeignKey(
|
||||
default=None,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='saleorder',
|
||||
name='total',
|
||||
model_name="saleorder",
|
||||
name="total",
|
||||
field=models.FloatField(default=0.0, max_length=200),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='saleorderline',
|
||||
name='order',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='order.saleorder'),
|
||||
model_name="saleorderline",
|
||||
name="order",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
to="order.saleorder",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='saleorderline',
|
||||
name='quantity',
|
||||
model_name="saleorderline",
|
||||
name="quantity",
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='saleorder',
|
||||
name='name',
|
||||
model_name="saleorder",
|
||||
name="name",
|
||||
field=models.CharField(max_length=200, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='saleorderline',
|
||||
name='product',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.DO_NOTHING, to='order.product'),
|
||||
model_name="saleorderline",
|
||||
name="product",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
to="order.product",
|
||||
),
|
||||
),
|
||||
]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ class SaleOrder(BaseModel):
|
|||
readonly = ["sold_to", "sold_at", "total"]
|
||||
|
||||
name = models.CharField(max_length=200, unique=True)
|
||||
sold_to = models.ForeignKey(User, on_delete=models.DO_NOTHING, default=None, null=True)
|
||||
sold_to = models.ForeignKey(
|
||||
User, on_delete=models.DO_NOTHING, default=None, null=True
|
||||
)
|
||||
total = models.FloatField(max_length=200, default=0.0)
|
||||
sold_at = models.DateTimeField(max_length=200, null=True, default=None)
|
||||
|
||||
|
|
@ -37,6 +39,14 @@ class SaleOrder(BaseModel):
|
|||
self.save()
|
||||
return self
|
||||
|
||||
def as_csv(self) -> str:
|
||||
lines_parsed = ""
|
||||
for line in self.saleorderline_set.all():
|
||||
lines_parsed = f"{lines_parsed}::{line.id}/{line.name}"
|
||||
return f"""name,sold_to,total,sold_at,lines
|
||||
{self.name},{self.sold_to.first_name} {self.sold_to.last_name},{self.total},{self.sold_at},{lines_parsed}
|
||||
"""
|
||||
|
||||
|
||||
class Product(models.Model):
|
||||
name = models.CharField(max_length=200)
|
||||
|
|
@ -48,3 +58,8 @@ class SaleOrderLine(models.Model):
|
|||
product = models.ForeignKey(Product, on_delete=models.DO_NOTHING, null=True)
|
||||
order = models.ForeignKey(SaleOrder, on_delete=models.CASCADE, null=True)
|
||||
quantity = models.IntegerField(default=1)
|
||||
|
||||
def as_csv(self) -> str:
|
||||
return f"""name,product,quantity
|
||||
{self.name},{self.product.name},{self.quantity}
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -9,10 +9,18 @@ from order.serializers import SaleOrderSerializer
|
|||
|
||||
class OrderTests(TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.product_1: Product = Product.objects.create(name="Product 1", unit_price=10.0)
|
||||
self.product_2: Product = Product.objects.create(name="Product 2", unit_price=20.0)
|
||||
self.product_3: Product = Product.objects.create(name="Product 3", unit_price=30.0)
|
||||
self.product_4: Product = Product.objects.create(name="Product 4", unit_price=40.0)
|
||||
self.product_1: Product = Product.objects.create(
|
||||
name="Product 1", unit_price=10.0
|
||||
)
|
||||
self.product_2: Product = Product.objects.create(
|
||||
name="Product 2", unit_price=20.0
|
||||
)
|
||||
self.product_3: Product = Product.objects.create(
|
||||
name="Product 3", unit_price=30.0
|
||||
)
|
||||
self.product_4: Product = Product.objects.create(
|
||||
name="Product 4", unit_price=40.0
|
||||
)
|
||||
|
||||
self.order_1: SaleOrder = SaleOrder.objects.create(name="Order 1")
|
||||
self.order_2: SaleOrder = SaleOrder.objects.create(name="Order 2")
|
||||
|
|
@ -21,7 +29,10 @@ class OrderTests(TestCase):
|
|||
name="Line 1/Order1", order=self.order_1, product=self.product_1
|
||||
)
|
||||
self.line_1_2: SaleOrderLine = SaleOrderLine.objects.create(
|
||||
name="Line 2/Order 1", quantity=3, order=self.order_1, product=self.product_2
|
||||
name="Line 2/Order 1",
|
||||
quantity=3,
|
||||
order=self.order_1,
|
||||
product=self.product_2,
|
||||
)
|
||||
|
||||
self.line_2_1: SaleOrderLine = SaleOrderLine.objects.create(
|
||||
|
|
@ -32,14 +43,10 @@ class OrderTests(TestCase):
|
|||
name="Line 2/Order2", order=self.order_2, product=self.product_4
|
||||
)
|
||||
|
||||
self.user_foo: User = User.objects.create(
|
||||
username="foo"
|
||||
)
|
||||
self.user_foo: User = User.objects.create(username="foo")
|
||||
self.user_foo.set_password("foo")
|
||||
self.user_foo.save()
|
||||
self.user_bar = User.objects.create(
|
||||
username="bar", password="bar"
|
||||
)
|
||||
self.user_bar = User.objects.create(username="bar", password="bar")
|
||||
|
||||
self.client = Client()
|
||||
self.client.login(username="foo", password="foo")
|
||||
|
|
@ -73,35 +80,25 @@ class OrderTests(TestCase):
|
|||
def test_view_readonly(self):
|
||||
response = self.client.patch(
|
||||
path="/api/orders/1/",
|
||||
data={
|
||||
"sold_at": "foo"
|
||||
},
|
||||
data={"sold_at": "foo"},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
response = self.client.patch(
|
||||
path="/api/orders/1/",
|
||||
data={
|
||||
"sold_to": self.user_foo.id
|
||||
},
|
||||
content_type="application/json"
|
||||
data={"sold_to": self.user_foo.id},
|
||||
content_type="application/json",
|
||||
)
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def test_view_total(self):
|
||||
response = self.client.get(
|
||||
path="/api/orders/1/total/"
|
||||
)
|
||||
self.assertEqual(json.loads(response.content.decode())['amount_total'], 70.0)
|
||||
response = self.client.get(
|
||||
path="/api/orders/2/total/"
|
||||
)
|
||||
self.assertEqual(json.loads(response.content.decode())['amount_total'], 70.0)
|
||||
response = self.client.get(path="/api/orders/1/total/")
|
||||
self.assertEqual(json.loads(response.content.decode())["amount_total"], 70.0)
|
||||
response = self.client.get(path="/api/orders/2/total/")
|
||||
self.assertEqual(json.loads(response.content.decode())["amount_total"], 70.0)
|
||||
|
||||
def test_view_sell(self):
|
||||
response = self.client.get(
|
||||
path="/api/orders/2/sold/"
|
||||
)
|
||||
response = self.client.get(path="/api/orders/2/sold/")
|
||||
self.order_2.refresh_from_db()
|
||||
self.assertEqual(self.order_2.sold_to, self.user_foo)
|
||||
self.assertEqual(self.order_2.sold_to, self.user_foo)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
from django.core.mail import EmailMessage
|
||||
from django.core.mail import EmailMessage, EmailMultiAlternatives
|
||||
from django.shortcuts import render
|
||||
from django.template.loader import render_to_string
|
||||
from rest_framework import viewsets, permissions, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
|
||||
from bernini import settings
|
||||
from order.models import SaleOrder, SaleOrderLine, Product
|
||||
from order.serializers import (
|
||||
|
|
@ -33,7 +33,7 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
|||
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
||||
return Response(
|
||||
data=f"You can't manually set the field {field}",
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
return super(SaleOrderViewSet, self).update(request, *args, **kwargs)
|
||||
|
|
@ -44,7 +44,7 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
|||
if request.data.get(field) != SaleOrderSerializer(order).data[field]:
|
||||
return Response(
|
||||
data=f"You can't manually set the field {field}",
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
return super(SaleOrderViewSet, self).partial_update(request, *args, **kwargs)
|
||||
|
|
@ -73,30 +73,46 @@ class SaleOrderViewSet(viewsets.ModelViewSet):
|
|||
@action(detail=True)
|
||||
def sent(self, request, pk=None):
|
||||
order: SaleOrder = self.get_object()
|
||||
|
||||
if order.sold_at and order.sold_to.email and settings.EMAIL_HOST_USER:
|
||||
mail = EmailMessage(
|
||||
f'Order {order.name} from Zapatos Bernini',
|
||||
render(
|
||||
'order_sold.html',
|
||||
{
|
||||
'order': order
|
||||
},
|
||||
),
|
||||
settings.EMAIL_HOST_USER,
|
||||
[order.sold_to.email],
|
||||
reply_to=[settings.EMAIL_HOST_USER]
|
||||
mail = EmailMultiAlternatives(
|
||||
subject=f"Order {order.name} from Zapatos Bernini",
|
||||
from_email=settings.EMAIL_HOST_USER,
|
||||
to=[order.sold_to.email],
|
||||
reply_to=[settings.EMAIL_HOST_USER],
|
||||
)
|
||||
mail.attach_alternative(
|
||||
render_to_string(
|
||||
template_name="order_sold.html",
|
||||
context={"order": order},
|
||||
),
|
||||
mimetype="text/html",
|
||||
)
|
||||
mail.attach(
|
||||
filename=f"Bernini order {order.name}.csv",
|
||||
content=order.as_csv(),
|
||||
mimetype="text/csv",
|
||||
)
|
||||
for line in order.saleorderline_set.all():
|
||||
mail.attach(
|
||||
filename=f"Bernini order {order.name}/{line.name}.csv",
|
||||
content=line.as_csv(),
|
||||
mimetype="text/csv",
|
||||
)
|
||||
mail.send()
|
||||
return Response(status=status.HTTP_200_OK,
|
||||
data=f"Email sent! You should receive it at {order.sold_to.email}")
|
||||
return Response(status=status.HTTP_500_INTERNAL_SERVER_ERROR, data="Something went wrong on our end")
|
||||
return Response(
|
||||
status=status.HTTP_200_OK,
|
||||
data=f"Email sent! You should receive it at {order.sold_to.email}",
|
||||
)
|
||||
return Response(
|
||||
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
data="Something went wrong on our end",
|
||||
)
|
||||
|
||||
|
||||
class SaleOrderLineViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
API endpoint that allows orders to be viewed or edited.
|
||||
"""
|
||||
API endpoint that allows orders to be viewed or edited.
|
||||
"""
|
||||
|
||||
queryset = SaleOrderLine.objects.all().order_by("name")
|
||||
serializer_class = SaleOrderLineSerializer
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue