finish payment logic
This commit is contained in:
+12
-1
@@ -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='موجودی برگردانده شده'),
|
||||
),
|
||||
]
|
||||
@@ -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(
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
|
||||
+51
-34
@@ -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": "پرداخت ناموفق بود. در صورت کسر وجه، مبلغ حداکثر تا ۴۸ ساعت آینده به حساب شما بازگردانده میشود.",
|
||||
|
||||
Reference in New Issue
Block a user