finish payment logic

This commit is contained in:
Parsa Nazer
2025-09-23 11:25:20 +03:30
parent 4047352399
commit 0ce765c2df
6 changed files with 142 additions and 36 deletions
+12 -1
View File
@@ -42,10 +42,21 @@ class BankRecordInline(StackedInline):
return [field.name for field in self.model._meta.fields]
class CartItemInline(StackedInline):
model = CartItem
extra = 0
max_num = 0
def has_delete_permission(self, request, obj=None):
return False
def has_add_permission(self, request, obj=None):
return False
@admin.register(Cart)
class CartAdmin(ModelAdmin):
pass
inlines = [CartItemInline]
@@ -0,0 +1,19 @@
# Generated by Django 5.1.2 on 2025-09-23 07:24
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('order', '0035_remove_cartitem_discount'),
]
operations = [
migrations.AddField(
model_name='ordermodel',
name='cart',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='order.cart'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 5.1.2 on 2025-09-23 07:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('order', '0036_ordermodel_cart'),
]
operations = [
migrations.AddField(
model_name='ordermodel',
name='is_stock_rolled_back',
field=models.BooleanField(default=False, verbose_name='موجودی برگردانده شده'),
),
]
+38
View File
@@ -55,6 +55,11 @@ class Cart(models.Model):
def __str__(self):
return f"Cart for {self.user.email}"
def clear_cart(self):
self.items.all().delete()
self.discount_code = None
self.save()
@property
def discount_code_amount(self):
if self.discount_code:
@@ -151,6 +156,11 @@ class OrderModel(models.Model):
cart_total = models.BigIntegerField(
null=True, blank=True, verbose_name='کل سبد خرید')
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, null=True, blank=True)
is_stock_rolled_back = models.BooleanField(
default=False, verbose_name="موجودی برگردانده شده")
def __str__(self):
return f'سفارش: {self.pk + 1000}'
@@ -158,6 +168,34 @@ class OrderModel(models.Model):
verbose_name = 'سفارش'
verbose_name_plural = 'سفارشات'
def rollback_stock(self):
"""
Rollback stock quantities for all items in this order
Returns True if successful, False otherwise
"""
if self.is_stock_rolled_back:
return False
# if not self.cart:
# return False
try:
# Get all cart items and rollback their stock
for order_item in self.items.all():
product = order_item.product
# Add back the quantity to stock
product.stock_quantity += order_item.quantity
product.save()
# Mark as rolled back
self.is_stock_rolled_back = True
self.save(update_fields=['is_stock_rolled_back'])
return True
except Exception as e:
# Log the error if you have logging setup
# logger.error(f"Failed to rollback stock for order {self.pk}: {e}")
return False
class OrderItemModel(models.Model):
order = models.ForeignKey(
+4 -1
View File
@@ -23,8 +23,11 @@ def udpate_bank_status():
bank.verify(item.tracking_code)
bank_record = bank_models.Bank.objects.get(tracking_code=item.tracking_code)
if bank_record.is_success:
bank_record.order.cart.clear_cart()
logging.debug("This record is verify now.", extra={"pk": bank_record.pk})
else:
order = bank_record.order
order.rollback_stock()
return 'update bank record is done'
+49 -32
View File
@@ -205,7 +205,7 @@ class PaymentView(APIView):
tags=['order payment']
)
def post(self, request):
print(request.data.get('gateway_type'))
# Get user's cart
cart = get_object_or_404(Cart, user=request.user)
@@ -226,39 +226,55 @@ class PaymentView(APIView):
# Validate product variant quantities
insufficient_stock_items = []
adjusted_items = []
for cart_item in cart.items.all():
if cart_item.product_variant.in_stock < cart_item.quantity:
available_stock = cart_item.product_variant.in_stock
# Store info about insufficient stock
insufficient_stock_items.append({
'product': cart_item.product_variant.product.name,
'variant': str(cart_item.product_variant),
'requested': cart_item.quantity,
'available': cart_item.product_variant.in_stock
'available': available_stock
})
# Auto-adjust the cart item quantity
if available_stock > 0:
# Reduce quantity to available stock
cart_item.quantity = available_stock
cart_item.save()
adjusted_items.append({
'product': cart_item.product_variant.product.name,
'variant': str(cart_item.product_variant),
'new_quantity': available_stock
})
else:
# Remove item if no stock available
product_name = cart_item.product_variant.product.name
variant_name = str(cart_item.product_variant)
cart_item.delete()
adjusted_items.append({
'product': product_name,
'variant': variant_name,
'new_quantity': 0,
'removed': True
})
if insufficient_stock_items:
# Create error message with product names
product_names = [item['product'] for item in insufficient_stock_items]
product_list = '، '.join(product_names)
return Response({
'error': 'موجودی برخی محصولات کافی نیست',
'insufficient_items': insufficient_stock_items
'detail': f'موجودی محصولات زیر کافی نیست: {product_list}',
'message': 'تعداد محصولات به صورت خودکار تنظیم شد',
'insufficient_items': insufficient_stock_items,
'adjusted_items': adjusted_items
}, status=status.HTTP_400_BAD_REQUEST)
# Validate discount code if present
if cart.discount_code and not cart.discount_code.is_valid():
return Response({
'error': cart.discount_code.not_valid_reason()
}, status=status.HTTP_400_BAD_REQUEST)
# Calculate order totals
cart_total = cart.cart_total
discount_amount = 0
if cart.discount_code:
discount_amount = int((cart_total * cart.discount_code.percent) / 100)
# Calculate tax (assuming 9% tax rate, adjust as needed)
tax_rate = 9 # percent
subtotal = cart_total - discount_amount
tax = int((subtotal * tax_rate) / 100)
final_price = subtotal + tax
try:
with transaction.atomic():
@@ -268,11 +284,12 @@ class PaymentView(APIView):
address=cart.address,
created_at=timezone.now().date(),
discount_code=cart.discount_code,
discount_amount=discount_amount,
tax=tax,
final_price=final_price,
cart_total=cart_total,
status='ADMIN_PENDING'
discount_amount=cart.discount_code_amount,
tax=cart.tax_amount,
final_price=cart.final_price,
cart_total=cart.cart_total,
status='ADMIN_PENDING',
cart=cart
)
# Create order items and reduce product variant quantities
@@ -300,7 +317,7 @@ class PaymentView(APIView):
bank = factory.create(bank_models.BankType.ZIBAL)
bank.set_request(request)
bank.set_amount(final_price) # Use final_price instead of hardcoded amount
bank.set_amount(cart.final_price) # Use final_price instead of hardcoded amount
bank.set_client_callback_url('http://localhost:3000/transaction')
bank.set_mobile_number(user_mobile_number)
@@ -309,10 +326,7 @@ class PaymentView(APIView):
bank_record.order = order
bank_record.save()
# Clear cart after successful order creation
# cart.items.all().delete()
# cart.discount_code = None
# cart.save()
return Response({
'url': bank.get_gateway()['url'],
})
@@ -398,8 +412,11 @@ class CallbackView(APIView):
return Response({'detail': 'کد تریسکد معتبر نمیباشد.'}, status=status.HTTP_404_NOT_FOUND)
if bank_record.is_success:
bank_record.order.cart.clear_cart()
return Response({"detail" : "پرداخت با موفقیت انجام شد.", "bank_result": bank_record_ser.data}, status=status.HTTP_200_OK)
else:
order = bank_record.order
order.rollback_stock()
return Response(
{
"detail": "پرداخت ناموفق بود. در صورت کسر وجه، مبلغ حداکثر تا ۴۸ ساعت آینده به حساب شما بازگردانده می‌شود.",