diff --git a/backend/order/migrations/0041_shopordermodel_address_shopordermodel_address_city_and_more.py b/backend/order/migrations/0041_shopordermodel_address_shopordermodel_address_city_and_more.py new file mode 100644 index 0000000..5cbd3b8 --- /dev/null +++ b/backend/order/migrations/0041_shopordermodel_address_shopordermodel_address_city_and_more.py @@ -0,0 +1,82 @@ +# Generated by Django 5.1.2 on 2025-12-10 09:24 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0033_shopmodel_commission_percent'), + ('order', '0040_shopordermodel_shoporderitem_shopdailyreport'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='shopordermodel', + name='address', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='account.useraddressmodel', verbose_name='آدرس تحویل'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_city', + field=models.CharField(blank=True, max_length=30, verbose_name='شهر'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_phone', + field=models.CharField(blank=True, max_length=11, verbose_name='شماره تماس تحویل گیرنده'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_postal_code', + field=models.CharField(blank=True, max_length=10, verbose_name='کد پستی'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_province', + field=models.CharField(blank=True, max_length=30, verbose_name='استان'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_recipient_name', + field=models.CharField(blank=True, max_length=100, verbose_name='نام تحویل گیرنده'), + ), + migrations.AddField( + model_name='shopordermodel', + name='address_text', + field=models.TextField(blank=True, verbose_name='آدرس کامل'), + ), + migrations.AddField( + model_name='shopordermodel', + name='customer', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='مشتری'), + ), + migrations.AddField( + model_name='shopordermodel', + name='customer_name', + field=models.CharField(blank=True, max_length=100, verbose_name='نام مشتری'), + ), + migrations.AddField( + model_name='shopordermodel', + name='customer_phone', + field=models.CharField(blank=True, max_length=12, verbose_name='شماره تلفن مشتری'), + ), + migrations.AddField( + model_name='shopordermodel', + name='is_paid', + field=models.BooleanField(default=False, verbose_name='وضعیت پرداخت'), + ), + migrations.AddField( + model_name='shopordermodel', + name='order_created_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='تاریخ ثبت سفارش اصلی'), + ), + migrations.AddField( + model_name='shopordermodel', + name='status', + field=models.CharField(choices=[('ADMIN_PENDING', 'در انتظار تایید'), ('PENDING', 'درحال پردازش'), ('POSTED', 'ارسال شده'), ('RECEIVED', 'تحویل شده'), ('CANCELED', 'لغو شده'), ('REFUNDED', 'مرجوع شده')], default='ADMIN_PENDING', max_length=20, verbose_name='وضعیت سفارش'), + ), + ] diff --git a/backend/order/models.py b/backend/order/models.py index 15cc7fd..45219b0 100644 --- a/backend/order/models.py +++ b/backend/order/models.py @@ -259,6 +259,34 @@ class ShopOrderModel(models.Model): """ order = models.ForeignKey(OrderModel, on_delete=models.CASCADE, related_name='shop_orders') shop = models.ForeignKey('account.ShopModel', on_delete=models.CASCADE, related_name='shop_orders') + + # Customer Information + customer = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='مشتری') + customer_phone = models.CharField(max_length=12, verbose_name='شماره تلفن مشتری', blank=True) + customer_name = models.CharField(max_length=100, verbose_name='نام مشتری', blank=True) + + # Delivery Address (ForeignKey + text backup) + address = models.ForeignKey(UserAddressModel, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='آدرس تحویل') + address_text = models.TextField(verbose_name='آدرس کامل', blank=True) + address_postal_code = models.CharField(max_length=10, verbose_name='کد پستی', blank=True) + address_phone = models.CharField(max_length=11, verbose_name='شماره تماس تحویل گیرنده', blank=True) + address_city = models.CharField(max_length=30, verbose_name='شهر', blank=True) + address_province = models.CharField(max_length=30, verbose_name='استان', blank=True) + address_recipient_name = models.CharField(max_length=100, verbose_name='نام تحویل گیرنده', blank=True) + + # Order Status & Payment + STATUS_CHOICES = [ + ('ADMIN_PENDING', 'در انتظار تایید'), + ('PENDING', 'درحال پردازش'), + ('POSTED', 'ارسال شده'), + ('RECEIVED', 'تحویل شده'), + ('CANCELED', 'لغو شده'), + ('REFUNDED', 'مرجوع شده'), + ] + status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='ADMIN_PENDING', verbose_name='وضعیت سفارش') + is_paid = models.BooleanField(default=False, verbose_name='وضعیت پرداخت') + + # Financial Details subtotal = models.BigIntegerField(verbose_name='جمع جزئیات', default=0) items_count = models.PositiveIntegerField(default=0) discount_amount = models.BigIntegerField(default=0, verbose_name='تخفیف اختصاصی فروشگاه') @@ -268,7 +296,10 @@ class ShopOrderModel(models.Model): tax_amount = models.BigIntegerField(default=0, verbose_name='مالیات') payable_amount = models.BigIntegerField(default=0, verbose_name='قابل پرداخت به فروشگاه') is_settled = models.BooleanField(default=False, verbose_name='تسویه شده') + + # Timestamps created_at = models.DateTimeField(auto_now_add=True) + order_created_at = models.DateTimeField(null=True, blank=True, verbose_name='تاریخ ثبت سفارش اصلی') class Meta: verbose_name = 'سفارش به ازای فروشگاه' diff --git a/backend/order/signals.py b/backend/order/signals.py index dcb9b41..c1062a0 100644 --- a/backend/order/signals.py +++ b/backend/order/signals.py @@ -121,9 +121,41 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg payable = shop_subtotal - allocated_discount - \ allocated_special_discount - commission_amount + allocated_tax + # Prepare customer information + customer_phone = instance.user.phone if instance.user else '' + customer_name = instance.user.full_name if instance.user else '' + + # Prepare address information (with text backups in case address is deleted) + address_text = '' + address_postal_code = '' + address_phone = '' + address_city = '' + address_province = '' + address_recipient_name = '' + + if instance.address: + address_text = instance.address.address + address_postal_code = instance.address.postal_code + address_phone = instance.address.phone + address_city = instance.address.city + address_province = instance.address.province + address_recipient_name = instance.address.name + shop_order = ShopOrderModel.objects.create( order=instance, shop=shop, + customer=instance.user, + customer_phone=customer_phone, + customer_name=customer_name, + address=instance.address, + address_text=address_text, + address_postal_code=address_postal_code, + address_phone=address_phone, + address_city=address_city, + address_province=address_province, + address_recipient_name=address_recipient_name, + status=instance.status, + is_paid=instance.is_paid, subtotal=shop_subtotal, items_count=sum(int(it.quantity) for it in items_list), discount_amount=allocated_discount, @@ -132,6 +164,7 @@ def create_shop_orders_on_payment(sender, instance: OrderModel, created, **kwarg commission_amount=commission_amount, tax_amount=allocated_tax, payable_amount=payable, + order_created_at=instance.created_at, ) # Create ShopOrderItem rows linking to original OrderItemModel