diff --git a/backend/account/migrations/0034_add_telegram_chat_id_to_shop.py b/backend/account/migrations/0034_add_telegram_chat_id_to_shop.py new file mode 100644 index 0000000..118afb8 --- /dev/null +++ b/backend/account/migrations/0034_add_telegram_chat_id_to_shop.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.2 on 2025-12-28 08:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0033_shopmodel_commission_percent'), + ] + + operations = [ + migrations.AddField( + model_name='shopmodel', + name='telegram_chat_id', + field=models.CharField(blank=True, help_text='برای ارسال خودکار فاکتورها به تلگرام', max_length=100, null=True, verbose_name='شناسه چت تلگرام'), + ), + ] diff --git a/backend/account/models.py b/backend/account/models.py index 34715e9..95f1022 100644 --- a/backend/account/models.py +++ b/backend/account/models.py @@ -143,6 +143,7 @@ class ShopModel(models.Model): shop_name = models.CharField(max_length=100, verbose_name='نام فروشگاه') shop_description = models.TextField(verbose_name='توضیحات فروشگاه') commission_percent = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='درصد کمیسیون') + telegram_chat_id = models.CharField(max_length=100, blank=True, null=True, verbose_name='شناسه چت تلگرام', help_text='برای ارسال خودکار فاکتورها به تلگرام') def __str__(self): return f"{self.user.phone} - {self.shop_name}" diff --git a/backend/dockerfile b/backend/dockerfile index 16ceef1..4a20125 100644 --- a/backend/dockerfile +++ b/backend/dockerfile @@ -5,8 +5,18 @@ ENV PYTHONUNBUFFERED 1 WORKDIR /app +# Install system dependencies for WeasyPrint +RUN apt-get update && apt-get install -y \ + libcairo2 \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + libgdk-pixbuf-2.0-0 \ + libffi-dev \ + shared-mime-info \ + && rm -rf /var/lib/apt/lists/* + COPY requirements.txt /app/ -RUN pip install -r requirements.txt +RUN pip install --no-cache-dir -r requirements.txt COPY . /app/ diff --git a/backend/order/admin.py b/backend/order/admin.py index f60ea14..0726c7d 100644 --- a/backend/order/admin.py +++ b/backend/order/admin.py @@ -12,6 +12,8 @@ from azbankgateways.models.banks import Bank from unfold.decorators import action from django.shortcuts import redirect from .permissons import ShopOrderAdminPermission +from django.urls import reverse +from django.utils.safestring import mark_safe class OrderItemModelInline(StackedInline): model = OrderItemModel @@ -97,6 +99,22 @@ class ShopOrderItemInline(StackedInline): @admin.register(ShopOrderModel) class ShopOrderModelAdmin(ShopOrderAdminPermission, ModelAdmin): inlines = [ShopOrderItemInline] + list_display = ['id', 'shop', 'order', 'customer_name', 'status', 'is_paid', 'is_settled', 'download_invoice_button'] + readonly_fields = ['download_invoice_link'] + + def download_invoice_button(self, obj): + if obj.pk: + url = reverse('download-shop-order-invoice', args=[obj.pk]) + return mark_safe(f'دانلود فاکتور') + return '-' + download_invoice_button.short_description = 'فاکتور' + + def download_invoice_link(self, obj): + if obj.pk: + url = reverse('download-shop-order-invoice', args=[obj.pk]) + return mark_safe(f'دانلود فاکتور PDF') + return '-' + download_invoice_link.short_description = 'دانلود فاکتور' def get_queryset(self, request): @@ -118,8 +136,8 @@ class OrderAdmin(ModelAdmin, ImportExportModelAdmin): search_fields = ['user__phone', 'user__first_name', 'user__last_name', 'user__email'] list_filter = ['is_paid', 'status'] actions_list = ['redirect_to_learn', 'udpate_bank_status'] - list_display = ['order_id', 'user', 'is_paid', 'status', 'discount_code', 'address',] - readonly_fields = ('created_at', 'tax', 'final_price', 'cart_total', 'discount_amount', 'discount_code', 'user', 'address', 'is_paid') + list_display = ['order_id', 'user', 'is_paid', 'status', 'discount_code', 'address', 'download_invoice_button'] + readonly_fields = ('created_at', 'tax', 'final_price', 'cart_total', 'discount_amount', 'discount_code', 'user', 'address', 'is_paid', 'download_invoice_link') compressed_fields = True warn_unsaved_form = True # exclude = ('bank_records',) @@ -129,9 +147,24 @@ class OrderAdmin(ModelAdmin, ImportExportModelAdmin): } } inlines = [OrderItemModelInline, BankRecordInline] + def order_id(self, obj): return f"سفارش {obj.pk + 1000}" order_id.short_description = "شماره سفارش" + + def download_invoice_button(self, obj): + if obj.pk: + url = reverse('download-order-invoice', args=[obj.pk]) + return mark_safe(f'دانلود فاکتور') + return '-' + download_invoice_button.short_description = 'فاکتور' + + def download_invoice_link(self, obj): + if obj.pk: + url = reverse('download-order-invoice', args=[obj.pk]) + return mark_safe(f'دانلود فاکتور PDF') + return '-' + download_invoice_link.short_description = 'دانلود فاکتور' diff --git a/backend/order/invoice_generator.py b/backend/order/invoice_generator.py new file mode 100644 index 0000000..34e8fb7 --- /dev/null +++ b/backend/order/invoice_generator.py @@ -0,0 +1,146 @@ +""" +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 diff --git a/backend/order/signals.py b/backend/order/signals.py index c1062a0..2fcebf0 100644 --- a/backend/order/signals.py +++ b/backend/order/signals.py @@ -6,7 +6,8 @@ from django.dispatch import receiver from .models import OrderModel from account.models import PushSubscription, UserAddressModel import ghasedak_sms -from .tasks import send_change_status_notif, send_change_status_sms +from .tasks import send_change_status_notif, send_change_status_sms, send_shop_order_invoice_telegram_task +from django.conf import settings @receiver(pre_save, sender=OrderModel) @@ -180,3 +181,37 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg special_discount_amount=int( it.special_discount_amount or 0), ) + + +@receiver(post_save, sender=ShopOrderModel) +def send_invoice_to_shop_telegram(sender, instance: ShopOrderModel, created, **kwargs): + """Automatically send invoice to shop's Telegram chat when ShopOrderModel is created. + + This handler triggers when a new ShopOrderModel is created and the shop has a telegram_chat_id configured. + It sends the invoice PDF asynchronously via Celery task. + """ + if not created: + return + + # Check if shop has telegram_chat_id configured + if not instance.shop or not instance.shop.telegram_chat_id: + return + + # Get bot token from settings + bot_token = getattr(settings, 'TELEGRAM_BOT_TOKEN', None) + if not bot_token: + return + + # Send invoice asynchronously + try: + send_shop_order_invoice_telegram_task.delay( + shop_order_id=instance.pk, + chat_id=instance.shop.telegram_chat_id, + bot_token=bot_token + ) + except Exception as e: + send_shop_order_invoice_telegram_task( + shop_order_id=instance.pk, + chat_id=instance.shop.telegram_chat_id, + bot_token=bot_token + ) \ No newline at end of file diff --git a/backend/order/tasks.py b/backend/order/tasks.py index 82818c5..ecd2cdb 100644 --- a/backend/order/tasks.py +++ b/backend/order/tasks.py @@ -119,4 +119,63 @@ def generate_daily_shop_reports(): result = f'Generated reports for {target_date}: {reports_created} created, {reports_updated} updated' logging.info(result) - return result \ No newline at end of file + return result + + +@shared_task +def send_shop_order_invoice_telegram_task(shop_order_id, chat_id, bot_token): + """Send shop order invoice PDF to Telegram chat. + + Args: + shop_order_id: ID of the ShopOrderModel + chat_id: Telegram chat ID to send invoice to + bot_token: Telegram bot token for authentication + + Returns: + Success or error message + """ + import asyncio + import io + from telegram import Bot + from telegram.error import TelegramError + from .invoice_generator import generate_shop_order_invoice + from .models import ShopOrderModel + + try: + # Get the shop order + shop_order = ShopOrderModel.objects.get(pk=shop_order_id) + + # Generate invoice PDF + pdf_buffer = generate_shop_order_invoice(shop_order_id) + + # Reset buffer position + pdf_buffer.seek(0) + + # Send via Telegram + async def send_invoice(): + bot = Bot(token=bot_token) + await bot.send_document( + chat_id=chat_id, + document=pdf_buffer, + filename=f'invoice_shop_order_{shop_order_id}.pdf', + caption=f'فاکتور سفارش #{shop_order_id}\n{shop_order.shop.shop_name}' + ) + + # Run async function + asyncio.run(send_invoice()) + + logging.info(f'Successfully sent shop order invoice {shop_order_id} to Telegram chat {chat_id}') + return f'Invoice sent successfully to chat {chat_id}' + + except ShopOrderModel.DoesNotExist: + error_msg = f'ShopOrderModel with id {shop_order_id} does not exist' + logging.error(error_msg) + return error_msg + except TelegramError as e: + error_msg = f'Telegram error sending invoice {shop_order_id}: {str(e)}' + logging.error(error_msg) + return error_msg + except Exception as e: + error_msg = f'Error sending invoice {shop_order_id} to Telegram: {str(e)}' + logging.error(error_msg) + return error_msg diff --git a/backend/order/urls.py b/backend/order/urls.py index 04c9b69..552bfad 100644 --- a/backend/order/urls.py +++ b/backend/order/urls.py @@ -15,4 +15,8 @@ urlpatterns = [ path('transaction/', CallbackView.as_view(), name='callback-gateway'), path('', OrderGetView.as_view(), name='order-get'), + + # Invoice download endpoints + path('invoice/order//download', download_order_invoice, name='download-order-invoice'), + path('invoice/shop-order//download', download_shop_order_invoice, name='download-shop-order-invoice'), ] diff --git a/backend/order/views.py b/backend/order/views.py index 756baaf..fffdaa1 100644 --- a/backend/order/views.py +++ b/backend/order/views.py @@ -508,3 +508,59 @@ class SetAddressForCartView(APIView): cart_order.address = address_object cart_order.save() return Response({'detail': 'ادرس با موفقیت انتخاب شد'}) + + +from django.http import HttpResponse, HttpResponseForbidden, HttpResponseNotFound +from django.contrib.admin.views.decorators import staff_member_required +from django.contrib.auth.decorators import login_required +from .invoice_generator import generate_order_invoice, generate_shop_order_invoice + + +@login_required +def download_order_invoice(request, order_id): + """ + Download invoice PDF for a specific OrderModel. + Requires superuser permissions. + """ + if not request.user.is_superuser: + return HttpResponseForbidden("شما اجازه دسترسی به این فاکتور را ندارید") + + try: + pdf_file = generate_order_invoice(order_id) + + response = HttpResponse(pdf_file.read(), content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="order_invoice_{order_id + 1000}.pdf"' + + return response + except ValueError as e: + return HttpResponseNotFound(f"خطا: {str(e)}") + except Exception as e: + return HttpResponse(f"خطا در ایجاد فاکتور: {str(e)}", status=500) + + +@login_required +def download_shop_order_invoice(request, shop_order_id): + """ + Download invoice PDF for a specific ShopOrderModel. + Shop owners can only download their own invoices, admins can download all. + """ + from .models import ShopOrderModel + + try: + shop_order = ShopOrderModel.objects.get(pk=shop_order_id) + + # Check permissions + if not request.user.is_staff and not request.user.is_superuser: + if not hasattr(request.user, 'shop') or request.user.shop != shop_order.shop: + return HttpResponseForbidden("شما اجازه دسترسی به این فاکتور را ندارید") + + pdf_file = generate_shop_order_invoice(shop_order_id) + + response = HttpResponse(pdf_file.read(), content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename="shop_order_invoice_{shop_order_id}.pdf"' + + return response + except ShopOrderModel.DoesNotExist: + return HttpResponseNotFound("فاکتور مورد نظر یافت نشد") + except Exception as e: + return HttpResponse(f"خطا در ایجاد فاکتور: {str(e)}", status=500) diff --git a/backend/requirements.txt b/backend/requirements.txt index 90a4e23..1062c77 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -11,6 +11,7 @@ billiard==4.2.1 boto3==1.36.26 botocore==1.36.26 branca==0.8.1 +brotli==1.2.0 celery==5.4.0 certifi==2024.8.30 cffi==1.17.1 @@ -22,6 +23,7 @@ click-repl==0.3.0 colorama==0.4.6 cron-descriptor==1.4.5 cryptography==44.0.1 +cssselect2==0.8.0 defusedxml==0.8.0rc2 diff-match-patch==20230430 distro==1.9.0 @@ -51,6 +53,7 @@ et-xmlfile==1.1.0 factory_boy==3.3.1 Faker==28.4.1 folium==0.19.4 +fonttools==4.61.1 frozenlist==1.4.1 geoip2==4.8.0 ghasedak_sms==1.0.3 @@ -95,7 +98,9 @@ pycparser==2.22 pycryptodome==3.20.0 pydantic==2.10.6 pydantic_core==2.27.2 +pydyf==0.12.1 PyJWT==2.10.1 +pyphen==0.17.2 pyTelegramBotAPI==4.23.0 python-crontab==3.2.0 python-dateutil==2.9.0.post0 @@ -125,6 +130,8 @@ sqlparse==0.5.1 tablib==3.5.0 telebot==0.0.5 text-unidecode==1.3 +tinycss2==1.5.1 +tinyhtml5==2.0.0 tqdm==4.67.1 typing_extensions==4.12.2 tzdata==2024.1 @@ -132,9 +139,12 @@ uritemplate==4.1.1 urllib3==2.2.3 vine==5.1.0 wcwidth==0.2.13 +weasyprint==67.0 +webencodings==0.5.1 whitenoise==6.7.0 xlrd==2.0.1 xlwt==1.3.0 xyzservices==2025.1.0 yarl==1.11.1 zeep==4.2.1 +zopfli==0.4.0 diff --git a/backend/templates/order/invoice_order.html b/backend/templates/order/invoice_order.html new file mode 100644 index 0000000..f2bc8aa --- /dev/null +++ b/backend/templates/order/invoice_order.html @@ -0,0 +1,357 @@ + + + + + + فاکتور سفارش - {{ order_number }} + + + +
+
+

فاکتور فروش

+

شماره سفارش: {{ order_number }}

+
+ +
+
+

اطلاعات مشتری

+ {% if user %} +
+ نام: + {{ user.first_name }} {{ user.last_name }} +
+
+ ایمیل: + {{ user.email|default:"---" }} +
+
+ تلفن: + {{ user.phone }} +
+ {% endif %} +
+ +
+

اطلاعات سفارش

+
+ تاریخ: + {{ created_at_jalali|default:"---" }} +
+
+ وضعیت پرداخت: + + {% if is_paid %} + پرداخت شده + {% else %} + پرداخت نشده + {% endif %} + +
+
+ وضعیت سفارش: + + {{ status }} + +
+
+
+ + {% if address %} +
+
+

آدرس تحویل

+
+ نام گیرنده: + {{ address.name }} +
+
+ آدرس: + {{ address.address }} +
+
+ شهر/استان: + {{ address.city }}, {{ address.province }} +
+
+ کد پستی: + {{ address.postal_code }} +
+
+ تلفن: + {{ address.phone }} +
+
+
+ {% endif %} + + + + + + + + + + + + + + + + {% for item_data in items_with_discount %} + + + + + + + + + + + {% endfor %} + +
ردیفنام محصولتنوعتعدادقیمت اصلیتخفیف محصولتخفیف ویژهقیمت نهایی
{{ forloop.counter }}{{ item_data.item.product.product.name }}{{ item_data.item.product.title }}{{ item_data.item.quantity }}{{ item_data.price_before_discount|floatformat:0 }} تومان + {% if item_data.item.discount_percent > 0 %} + {{ item_data.item.discount_percent }}% ({{ item_data.discount_amount|floatformat:0 }} تومان) + {% else %} + --- + {% endif %} + + {% if item_data.item.special_discount_amount > 0 %} + {{ item_data.item.special_discount_amount|floatformat:0 }} تومان + {% else %} + --- + {% endif %} + {{ item_data.item.price|floatformat:0 }} تومان
+ +
+
+
+ تعداد کل اقلام: + {{ total_items }} +
+
+ جمع کل: + {{ subtotal|floatformat:0 }} تومان +
+ {% if discount_amount > 0 %} +
+ تخفیف کد: + {{ discount_amount|floatformat:0 }} تومان +
+ {% endif %} + {% if special_discount_total > 0 %} +
+ تخفیف ویژه: + {{ special_discount_total|floatformat:0 }} تومان +
+ {% endif %} +
+ مالیات: + {{ tax|floatformat:0 }} تومان +
+
+ مبلغ قابل پرداخت: + {{ final_price|floatformat:0 }} تومان +
+
+
+ + {% if discount_code %} +
+
+
+

کد تخفیف استفاده شده

+
+ کد: + {{ discount_code.code }} +
+
+ درصد: + {{ discount_code.percent }}% +
+
+
+
+ {% endif %} + + +
+ + diff --git a/backend/templates/order/invoice_shop_order.html b/backend/templates/order/invoice_shop_order.html new file mode 100644 index 0000000..1332cc1 --- /dev/null +++ b/backend/templates/order/invoice_shop_order.html @@ -0,0 +1,434 @@ + + + + + + فاکتور فروشگاه - {{ shop_order_id }} + + + +
+
+

فاکتور فروشگاه

+

شماره سفارش اصلی: {{ order_number }} | شماره فاکتور فروشگاه: {{ shop_order_id }}

+
+ +
+

{{ shop.shop_name }}

+

{{ shop.shop_description }}

+

کمیسیون: {{ commission_percent }}%

+
+ +
+
+

اطلاعات مشتری

+
+ نام: + {{ customer_name|default:"---" }} +
+
+ تلفن: + {{ customer_phone|default:"---" }} +
+
+ +
+

اطلاعات سفارش

+
+ تاریخ: + {{ created_at_jalali|default:"---" }} +
+
+ وضعیت پرداخت: + + {% if is_paid %} + پرداخت شده + {% else %} + پرداخت نشده + {% endif %} + +
+
+ وضعیت سفارش: + + {{ status }} + +
+
+ وضعیت تسویه: + + {% if is_settled %} + تسویه شده + {% else %} + تسویه نشده + {% endif %} + +
+
+
+ + {% if address_text %} +
+

آدرس تحویل

+
+ گیرنده: + {{ address_recipient_name }} +
+
+ آدرس: + {{ address_text }} +
+
+ شهر/استان: + {{ address_city }}, {{ address_province }} +
+
+ کد پستی: + {{ address_postal_code }} +
+
+ تلفن: + {{ address_phone }} +
+
+ {% endif %} + + + + + + + + + + + + + + + + {% for item in items %} + + + + + + + + + + + {% endfor %} + +
ردیفنام محصولتنوعتعدادقیمت واحدتخفیف محصولتخفیف ویژهقیمت نهایی
{{ forloop.counter }}{{ item.order_item.product.product.name }}{{ item.order_item.product.title }}{{ item.quantity }}{{ item.unit_price|floatformat:0 }} تومان + {% if item.discount_amount > 0 %} + {{ item.order_item.discount_percent }}% ({{ item.discount_amount|floatformat:0 }} تومان) + {% else %} + --- + {% endif %} + + {% if item.special_discount_amount > 0 %} + {{ item.special_discount_amount|floatformat:0 }} تومان + {% else %} + --- + {% endif %} + {{ item.total_price|floatformat:0 }} تومان
+ +
+
+

خلاصه مالی

+
+ تعداد کل اقلام: + {{ total_items }} +
+
+ جمع کل (Subtotal): + {{ subtotal|floatformat:0 }} تومان +
+ {% if discount_amount > 0 %} +
+ تخفیف معمولی: + {{ discount_amount|floatformat:0 }} تومان +
+ {% endif %} + {% if special_discount_amount > 0 %} +
+ تخفیف ویژه: + {{ special_discount_amount|floatformat:0 }} تومان +
+ {% endif %} +
+ مالیات: + {{ tax_amount|floatformat:0 }} تومان +
+
+ +
+

محاسبات کمیسیون

+
+ درصد کمیسیون: + {{ commission_percent }}% +
+
+ مبلغ کمیسیون: + {{ commission_amount|floatformat:0 }} تومان +
+
+ +
+

مبلغ نهایی

+
+ مبلغ قابل پرداخت به فروشگاه: + {{ payable_amount|floatformat:0 }} تومان +
+
+
+ + +
+ +