diff --git a/backend/commerce/models.py b/backend/commerce/models.py index fc84954..1e58abf 100644 --- a/backend/commerce/models.py +++ b/backend/commerce/models.py @@ -270,6 +270,15 @@ class Order(models.Model): def save(self, *args, **kwargs): is_new = self.pk is None + old_status = None + + # Track old status for change detection + if not is_new: + try: + old_instance = Order.objects.get(pk=self.pk) + old_status = old_instance.status + except Order.DoesNotExist: + pass # CRITICAL: Set currency from site configuration ONLY at creation time # Once set, currency should NEVER change to maintain order integrity @@ -285,9 +294,19 @@ class Order(models.Model): super().save(*args, **kwargs) # Send email notification for new orders - if is_new and self.user: + if is_new: from .tasks import notify_order_successfuly_created - notify_order_successfuly_created.delay(order=self, user=self.user) + notify_order_successfuly_created.delay(order=self) + + # Send email notification when status changes to CANCELLED + if not is_new and old_status != self.OrderStatus.CANCELLED and self.status == self.OrderStatus.CANCELLED: + from .tasks import notify_order_cancelled + notify_order_cancelled.delay(order=self) + + # Send email notification when status changes to COMPLETED + if not is_new and old_status != self.OrderStatus.COMPLETED and self.status == self.OrderStatus.COMPLETED: + from .tasks import notify_order_completed + notify_order_completed.delay(order=self) def cancel_order(self): """Cancel the order if possible""" @@ -352,7 +371,7 @@ class Carrier(models.Model): self.shipping_method == self.SHIPPING.STORE): if hasattr(self, 'order') and self.order: - notify_Ready_to_pickup.delay(order=self.order, user=self.order.user) + notify_Ready_to_pickup.delay(order=self.order) def get_price(self, order=None): if self.shipping_method == self.SHIPPING.ZASILKOVNA: @@ -381,7 +400,7 @@ class Carrier(models.Model): self.returning = False self.save() - notify_zasilkovna_sended.delay(order=self.order, user=self.order.user) + notify_zasilkovna_sended.delay(order=self.order) elif self.shipping_method == self.SHIPPING.DEUTSCHEPOST: # Import here to avoid circular imports @@ -400,7 +419,7 @@ class Carrier(models.Model): self.state = self.STATE.READY_TO_PICKUP self.save() - notify_Ready_to_pickup.delay(order=self.order, user=self.order.user) + notify_Ready_to_pickup.delay(order=self.order) else: raise ValidationError("Tato metoda dopravy nepodporuje objednání přepravy.") @@ -707,7 +726,7 @@ class Refund(models.Model): self.order.save(update_fields=["status", "updated_at"]) - notify_refund_accepted.delay(order=self.order, user=self.order.user) + notify_refund_accepted.delay(order=self.order) def generate_refund_pdf_for_customer(self): diff --git a/backend/commerce/tasks.py b/backend/commerce/tasks.py index a7199bb..78d013f 100644 --- a/backend/commerce/tasks.py +++ b/backend/commerce/tasks.py @@ -22,147 +22,161 @@ def delete_expired_orders(): # Zásilkovna @shared_task -def notify_zasilkovna_sended(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_zasilkovna_sended(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: - print("Additional kwargs received in notify_order_sended:", kwargs) + print("Additional kwargs received in notify_zasilkovna_sended:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your order has been shipped", - template_path="email/order_sended.html", + template_path="email/shipping/zasilkovna/zasilkovna_sended.html", context={ - "user": user, "order": order, }) - - pass # Shop @shared_task -def notify_Ready_to_pickup(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_Ready_to_pickup(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: - print("Additional kwargs received in notify_order_sended:", kwargs) + print("Additional kwargs received in notify_Ready_to_pickup:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your order is ready for pickup", - template_path="email/order_ready_pickup.html", + template_path="email/shipping/ready_to_pickup/ready_to_pickup.html", context={ - "user": user, "order": order, }) - - pass # -- NOTIFICATIONS ORDER -- @shared_task -def notify_order_successfuly_created(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_order_successfuly_created(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: print("Additional kwargs received in notify_order_successfuly_created:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your order has been successfully created", template_path="email/order_created.html", context={ - "user": user, "order": order, }) - - pass + @shared_task -def notify_order_payed(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_order_payed(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: - print("Additional kwargs received in notify_order_paid:", kwargs) + print("Additional kwargs received in notify_order_payed:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your order has been paid", template_path="email/order_paid.html", context={ - "user": user, "order": order, }) - - pass + @shared_task -def notify_about_missing_payment(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_about_missing_payment(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: print("Additional kwargs received in notify_about_missing_payment:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Payment missing for your order", template_path="email/order_missing_payment.html", context={ - "user": user, "order": order, }) - - pass # -- NOTIFICATIONS REFUND -- @shared_task -def notify_refund_items_arrived(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_refund_items_arrived(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: print("Additional kwargs received in notify_refund_items_arrived:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your refund items have arrived", template_path="email/order_refund_items_arrived.html", context={ - "user": user, "order": order, }) - - pass -# Refund accepted, retuning money +# Refund accepted, returning money @shared_task -def notify_refund_accepted(order = None, user = None, **kwargs): - if not order or not user: - raise ValueError("Order and User must be provided for notification.") +def notify_refund_accepted(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") if kwargs: print("Additional kwargs received in notify_refund_accepted:", kwargs) send_email_with_context( - recipients=user.email, + recipients=order.email, subject="Your refund has been accepted", template_path="email/order_refund_accepted.html", context={ - "user": user, "order": order, }) + + +# -- NOTIFICATIONS ORDER STATUS -- + +@shared_task +def notify_order_cancelled(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") - pass + if kwargs: + print("Additional kwargs received in notify_order_cancelled:", kwargs) + + send_email_with_context( + recipients=order.email, + subject="Your order has been cancelled", + template_path="email/order_cancelled.html", + context={ + "order": order, + }) -# \ No newline at end of file +@shared_task +def notify_order_completed(order = None, **kwargs): + if not order: + raise ValueError("Order must be provided for notification.") + + if kwargs: + print("Additional kwargs received in notify_order_completed:", kwargs) + + send_email_with_context( + recipients=order.email, + subject="Your order has been completed", + template_path="email/order_completed.html", + context={ + "order": order, + }) diff --git a/backend/commerce/templates/email/order_cancelled.html b/backend/commerce/templates/email/order_cancelled.html new file mode 100644 index 0000000..4fa53df --- /dev/null +++ b/backend/commerce/templates/email/order_cancelled.html @@ -0,0 +1,50 @@ +

Order Cancelled

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Your order has been cancelled.

+ +

Order Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Total Amount:{{ order.total_price }} {{ order.get_currency }}
Cancellation Date:{{ order.updated_at|date:"d.m.Y H:i" }}
+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +{% if order.payment.status == 'paid' %} +

Refund Information

+

Since your order was already paid, you will receive a refund of {{ order.total_price }} {{ order.get_currency }}. The refund will be processed within 3-5 business days.

+{% endif %} + +

+ If you have any questions, please contact our support team. +

diff --git a/backend/commerce/templates/email/order_completed.html b/backend/commerce/templates/email/order_completed.html new file mode 100644 index 0000000..9845677 --- /dev/null +++ b/backend/commerce/templates/email/order_completed.html @@ -0,0 +1,49 @@ +

✓ Order Completed

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Great news! Your order has been completed and delivered. Thank you for your purchase!

+ +

Order Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Total Amount:{{ order.total_price }} {{ order.get_currency }}
Completed:{{ order.updated_at|date:"d.m.Y H:i" }}
+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ We hope you enjoyed your purchase! If you have any feedback or need to return an item, please let us know. +

+ +

+ Thank you for shopping with us! +

diff --git a/backend/commerce/templates/email/order_created.html b/backend/commerce/templates/email/order_created.html new file mode 100644 index 0000000..5811efd --- /dev/null +++ b/backend/commerce/templates/email/order_created.html @@ -0,0 +1,50 @@ +

Order Confirmation

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Thank you for your order! Your order has been successfully created and is being prepared for shipment.

+ +

Order Details

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

Order Summary

+ + + + + +
Subtotal:{{ order.total_price }} {{ order.get_currency }}
+ +

Shipping Address

+

+ {{ order.first_name }} {{ order.last_name }}
+ {{ order.address }}
+ {{ order.postal_code }} {{ order.city }}
+ {{ order.country }} +

+ +{% if order.note %} +

Special Instructions

+

{{ order.note }}

+{% endif %} + +

+ We will notify you as soon as your order ships. If you have any questions, please contact us. +

diff --git a/backend/commerce/templates/email/order_missing_payment.html b/backend/commerce/templates/email/order_missing_payment.html new file mode 100644 index 0000000..de66f61 --- /dev/null +++ b/backend/commerce/templates/email/order_missing_payment.html @@ -0,0 +1,50 @@ +

⚠ Payment Reminder

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

We haven't received payment for your order yet. Your order is being held and may be cancelled if payment is not completed soon.

+ +

Order Details

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Amount Due:{{ order.total_price }} {{ order.get_currency }}
Created:{{ order.created_at|date:"d.m.Y H:i" }}
+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ Please complete your payment as soon as possible to avoid order cancellation. + If you have questions or need assistance, contact us right away. +

+ +

+ Thank you for your business! +

diff --git a/backend/commerce/templates/email/order_paid.html b/backend/commerce/templates/email/order_paid.html new file mode 100644 index 0000000..8620920 --- /dev/null +++ b/backend/commerce/templates/email/order_paid.html @@ -0,0 +1,45 @@ +

✓ Payment Received

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Thank you! Your payment has been successfully received and processed. Your order is now confirmed and will be prepared for shipment.

+ +

Order Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Amount Paid:{{ order.total_price }} {{ order.get_currency }}
Payment Date:{{ order.payment.created_at|date:"d.m.Y H:i" }}
+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ Your order will be prepared and shipped as soon as possible. You will receive a shipping notification with tracking details. +

diff --git a/backend/commerce/templates/email/order_refund_accepted.html b/backend/commerce/templates/email/order_refund_accepted.html new file mode 100644 index 0000000..8b74bf2 --- /dev/null +++ b/backend/commerce/templates/email/order_refund_accepted.html @@ -0,0 +1,53 @@ +

✓ Refund Processed

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Excellent! Your refund has been approved and processed. The funds will appear in your account within 3-5 business days, depending on your financial institution.

+ +

Refund Details

+ + + + + + + + + + + + + + + + + +
Original Order ID:{{ order.id }}
Refund Amount:{{ order.total_price }} {{ order.get_currency }}
Processing Date:{{ order.updated_at|date:"d.m.Y H:i" }}
Status:✓ Completed
+ +

Refunded Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyRefund
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ Timeline: Your refund should appear in your account within 3-5 business days. Some banks may take longer during weekends or holidays. +

+ +

+ Thank you for giving us the opportunity to serve you. If you need anything else, please don't hesitate to contact us. +

diff --git a/backend/commerce/templates/email/order_refund_items_arrived.html b/backend/commerce/templates/email/order_refund_items_arrived.html new file mode 100644 index 0000000..253c8d4 --- /dev/null +++ b/backend/commerce/templates/email/order_refund_items_arrived.html @@ -0,0 +1,49 @@ +

Return Items Received

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Thank you! We have received your returned items from order #{{ order.id }}. Our team is now inspecting the items and processing your refund.

+ +

Order Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Total Refund Amount:{{ order.total_price }} {{ order.get_currency }}
Received Date:{{ order.updated_at|date:"d.m.Y H:i" }}
+ +

Returned Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyRefund
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ What's Next? We'll inspect the items and confirm the refund within 2-3 business days. You'll receive another confirmation email when your refund has been processed. +

+ +

+ If you have any questions about your return, please contact us. +

diff --git a/backend/commerce/templates/email/shipping/ready_to_pickup/ready_to_pickup.html b/backend/commerce/templates/email/shipping/ready_to_pickup/ready_to_pickup.html index e69de29..0c03d40 100644 --- a/backend/commerce/templates/email/shipping/ready_to_pickup/ready_to_pickup.html +++ b/backend/commerce/templates/email/shipping/ready_to_pickup/ready_to_pickup.html @@ -0,0 +1,49 @@ +

✓ Your Order is Ready for Pickup!

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Excellent news! Your order is now ready for pickup. You can collect your package at your convenience during store hours.

+ +

Pickup Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Ready Since:{{ order.carrier.updated_at|date:"d.m.Y H:i" }}
Pickup Location:Our Store
+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ What to Bring: Please bring a valid ID and your order confirmation (this email). Your package is being held for you and will be released upon presentation of these documents. +

+ +

+ Thank you for your business! If you have any questions, please don't hesitate to contact us. +

diff --git a/backend/commerce/templates/email/shipping/zasilkovna/zasilkovna_sended.html b/backend/commerce/templates/email/shipping/zasilkovna/zasilkovna_sended.html index e69de29..12eb46b 100644 --- a/backend/commerce/templates/email/shipping/zasilkovna/zasilkovna_sended.html +++ b/backend/commerce/templates/email/shipping/zasilkovna/zasilkovna_sended.html @@ -0,0 +1,55 @@ +

📦 Your Package is on its Way!

+ +

Dear {{ order.first_name }} {{ order.last_name }},

+ +

Great news! Your order has been shipped via Zásilkovna and is on its way to you.

+ +

Shipping Information

+ + + + + + + + + + + + + +
Order ID:{{ order.id }}
Carrier:Zásilkovna
Shipped Date:{{ order.carrier.updated_at|date:"d.m.Y H:i" }}
+ +

Delivery Instructions

+

Your package will be delivered to your selected Zásilkovna pickup point. You will receive an SMS/email notification from Zásilkovna when the package arrives at the pickup point.

+ +

Order Items

+ + + + + + + + + + {% for item in order.items.all %} + + + + + + {% endfor %} + +
ProductQtyPrice
{{ item.product.name }}{{ item.quantity }}{{ item.get_total_price }} {{ order.get_currency }}
+ +

+ Delivery Address:
+ {{ order.first_name }} {{ order.last_name }}
+ {{ order.address }}
+ {{ order.postal_code }} {{ order.city }} +

+ +

+ You can track your package on the Zásilkovna website. If you have any questions, please contact us. +