Files
hossein-por-shop/backend/order/invoice_generator.py
T
Parsa Nazer 34715994ce feat: Add Telegram chat ID to ShopModel for automatic invoice sending
chore: Update Dockerfile to install WeasyPrint dependencies

feat: Enhance ShopOrderModelAdmin with invoice download buttons

feat: Implement invoice generation for OrderModel and ShopOrderModel

feat: Send invoice to shop's Telegram chat upon ShopOrderModel creation

feat: Create Celery task to send shop order invoice via Telegram

feat: Add invoice download endpoints for OrderModel and ShopOrderModel

feat: Implement views for downloading order and shop order invoices

chore: Update requirements.txt to include necessary packages for PDF generation

feat: Create HTML templates for order and shop order invoices
2025-12-28 11:43:33 +03:30

147 lines
5.3 KiB
Python

"""
Invoice generation utilities for OrderModel and ShopOrderModel.
These functions generate PDF invoices from HTML templates.
"""
from io import BytesIO
from datetime import datetime
from django.template.loader import render_to_string
from weasyprint import HTML
from django.conf import settings
import jdatetime
def generate_order_invoice(order_id):
"""
Generate a PDF invoice for an OrderModel instance.
Args:
order_id: The ID of the OrderModel instance
Returns:
BytesIO: A BytesIO object containing the PDF data
"""
from .models import OrderModel
try:
order = OrderModel.objects.select_related(
'user', 'address', 'discount_code'
).prefetch_related('items__product__product').get(pk=order_id)
except OrderModel.DoesNotExist:
raise ValueError(f"Order with ID {order_id} does not exist")
# Prepare items with calculated discount amounts
items = order.items.all()
items_with_discount = []
for item in items:
# Calculate the discount amount from the discount_percent
# price is after discount, so we need to calculate original price
# original_price = price / (1 - discount_percent/100)
# discount_amount = original_price - price
if item.discount_percent > 0:
discount_multiplier = (100 - item.discount_percent) / 100
price_after_discount = item.price
price_before_discount = int(price_after_discount / discount_multiplier) if discount_multiplier > 0 else price_after_discount
item_discount_amount = (price_before_discount - price_after_discount)
else:
item_discount_amount = 0
price_before_discount = item.price
items_with_discount.append({
'item': item,
'discount_amount': item_discount_amount,
'price_before_discount': price_before_discount
})
# Prepare context for the template
context = {
'order': order,
'order_number': order.pk + 1000,
'items': items,
'items_with_discount': items_with_discount,
'user': order.user,
'address': order.address,
'discount_code': order.discount_code,
'created_at_jalali': jdatetime.datetime.fromgregorian(datetime=order.created_at) if order.created_at else None,
'total_items': sum(item.quantity for item in items),
'subtotal': order.cart_total,
'discount_amount': order.discount_amount or 0,
'special_discount_total': order.special_discount_total or 0,
'tax': order.tax or 0,
'final_price': order.final_price,
'is_paid': order.is_paid,
'status': order.get_status_display(),
}
# Render HTML template
html_string = render_to_string('order/invoice_order.html', context)
# Generate PDF
html = HTML(string=html_string, base_url=settings.STATIC_URL)
pdf_file = BytesIO()
html.write_pdf(pdf_file)
pdf_file.seek(0)
return pdf_file
def generate_shop_order_invoice(shop_order_id):
"""
Generate a PDF invoice for a ShopOrderModel instance.
Args:
shop_order_id: The ID of the ShopOrderModel instance
Returns:
BytesIO: A BytesIO object containing the PDF data
"""
from .models import ShopOrderModel
try:
shop_order = ShopOrderModel.objects.select_related(
'order', 'shop', 'shop__user', 'customer', 'address'
).prefetch_related('items__order_item__product__product').get(pk=shop_order_id)
except ShopOrderModel.DoesNotExist:
raise ValueError(f"ShopOrder with ID {shop_order_id} does not exist")
# Prepare context for the template
context = {
'shop_order': shop_order,
'order_number': shop_order.order.pk + 1000,
'shop_order_id': shop_order.pk,
'shop': shop_order.shop,
'customer': shop_order.customer,
'customer_name': shop_order.customer_name,
'customer_phone': shop_order.customer_phone,
'address_text': shop_order.address_text,
'address_postal_code': shop_order.address_postal_code,
'address_phone': shop_order.address_phone,
'address_city': shop_order.address_city,
'address_province': shop_order.address_province,
'address_recipient_name': shop_order.address_recipient_name,
'items': shop_order.items.all(),
'created_at_jalali': jdatetime.datetime.fromgregorian(datetime=shop_order.order_created_at) if shop_order.order_created_at else None,
'total_items': shop_order.items_count,
'subtotal': shop_order.subtotal,
'discount_amount': shop_order.discount_amount,
'special_discount_amount': shop_order.special_discount_amount,
'tax_amount': shop_order.tax_amount,
'commission_percent': shop_order.commission_percent,
'commission_amount': shop_order.commission_amount,
'payable_amount': shop_order.payable_amount,
'is_paid': shop_order.is_paid,
'status': shop_order.get_status_display(),
'is_settled': shop_order.is_settled,
}
# Render HTML template
html_string = render_to_string('order/invoice_shop_order.html', context)
# Generate PDF
html = HTML(string=html_string, base_url=settings.STATIC_URL)
pdf_file = BytesIO()
html.write_pdf(pdf_file)
pdf_file.seek(0)
return pdf_file