diff --git a/admin_panel/views.py b/admin_panel/views.py index dbc748f..ec8add1 100644 --- a/admin_panel/views.py +++ b/admin_panel/views.py @@ -951,7 +951,6 @@ def newFactor(request, c_id): return redirect('/panel/customer/'+str(c_id)+'/factors/') - @login_required(login_url='/panel/p_login/') def newFactor2(request, c_id): if request.method != 'POST': @@ -959,7 +958,6 @@ def newFactor2(request, c_id): user_op = request.user - # ۱. پارس داده‌های JSON ارسال شده از فرم (پشتیبانی کامل از ساختار paylist و orderList جاوااسکریپت) try: prds_list = json.loads(request.POST.get('prdsList') or '[]') json_pardakht_ha = json.loads(request.POST.get('PardakhtHa') or '{}') @@ -970,15 +968,18 @@ def newFactor2(request, c_id): factor_desc = request.POST.get('FactorDesc', '') sar_resid_date = _parse_jalali_date(request.POST.get('sarResidDate')) - # ۲. واکشی اطلاعات پایه‌ای customer_obj = get_object_or_404(Customer, id=c_id) store_room = get_object_or_404(StoreRoom, user=user_op) - # ۳. واکشی دسته‌جمعی محصولات برای جلوگیری از فشار به دیتابیس - product_ids = [item.get('prd_id') for item in prds_list if item.get('prd_id') and item.get('prd_id') != 9999] + # رفع مشکل متنی/عددی در استخراج شناسه کالاها + product_ids = [] + for item in prds_list: + p_id = item.get('prd_id') + if p_id and _safe_int(p_id) != 9999: + product_ids.append(_safe_int(p_id)) + products_by_id = {p.id: p for p in Product.objects.filter(id__in=product_ids)} - # ۴. محاسبه مبلغ کل با نادیده گرفتن محصولاتی که گارانتی/اشانتیون هستند sum_price = sum( _safe_int(item.get('prd_count')) * _safe_int(item.get('price')) for item in prds_list @@ -986,10 +987,8 @@ def newFactor2(request, c_id): ) try: - # ۵. باز کردن یک تراکنش امن دیتابیس with transaction.atomic(): - # ثبت آبجکت فاکتور new_factor = Factor.objects.create( customer=customer_obj, amount=sum_price, @@ -999,7 +998,6 @@ def newFactor2(request, c_id): user=user_op ) - # ثبت بدهکاری کلی در سند حسابداری Article.objects.create( debit=sum_price, credit=0, @@ -1009,7 +1007,6 @@ def newFactor2(request, c_id): user=user_op ) - # ثبت ریزپرداختی‌ها بر اساس دیکشنری paylist جاوااسکریپت payment_map = { 'l_naghd': 'نقد', 'l_kartbekart': 'کارت به کارت', @@ -1035,7 +1032,6 @@ def newFactor2(request, c_id): user=user_op ) - # ثبت اسناد چک و نسیه elif key in ['l_nesiye', 'l_checkPrice']: is_nesiye = (key == 'l_nesiye') type_str = 'nesiye' if is_nesiye else 'check' @@ -1053,12 +1049,12 @@ def newFactor2(request, c_id): sarresidDT=sar_resid_dt ) - # ثبت آیتم‌های فاکتور با کارایی بالا (املای کلاس FactorIteam دقیقا منطبق بر models.py) factor_items_pool = [] user_fullname = f"{user_op.first_name} {user_op.last_name}".strip() or user_op.username for item in prds_list: - prd_id = item.get('prd_id') + # تبدیل امن شناسه به عدد در همینجا برای جلوگیری از خطای یافت نشدن دیکشنری + prd_id = _safe_int(item.get('prd_id')) prd_count = _safe_int(item.get('prd_count')) garanty_esh = item.get('garanty_Esh', '') @@ -1073,8 +1069,10 @@ def newFactor2(request, c_id): )) else: product_obj = products_by_id.get(prd_id) + + # خطای کاربرپسند در صورت نبود محصول if not product_obj: - raise ValidationError(f"محصولی با شناسه {prd_id} یافت نشد.") + raise InventoryException(f"خطا در دریافت محصول: محصولی با شناسه {prd_id} در سیستم وجود ندارد.") factor_items_pool.append(FactorIteam( factor=new_factor, @@ -1085,12 +1083,10 @@ def newFactor2(request, c_id): garantyEsh=garanty_esh )) - # ثبت یکباره تمام آیتم‌ها FactorIteam.objects.bulk_create(factor_items_pool, batch_size=500) - # چک کردن موجودی انبار و ثبت کسر کالا for item in prds_list: - prd_id = item.get('prd_id') + prd_id = _safe_int(item.get('prd_id')) if prd_id == 9999: continue @@ -1098,7 +1094,9 @@ def newFactor2(request, c_id): garanty_esh = item.get('garanty_Esh', '') product_obj = products_by_id.get(prd_id) - # قفل کردن ردیف برای اطمینان ۱۰۰ درصدی از عدم تداخل پیش از متد Save مدل + if not product_obj: + continue + stock_records = ProductInStore.objects.select_for_update().filter( Product=product_obj, StoreRoom=store_room, @@ -1119,8 +1117,6 @@ def newFactor2(request, c_id): else: desc_text = f"خروج: {prd_count}#فروش#{customer_obj.name_family} * {user_fullname}" - # این خط به‌طور خودکار متد حرفه‌ای Save شما در models.py را فراخوانی می‌کند - # و جداول تاریخچه انبارداری را نیز بروزرسانی می‌نماید ProductInStore.objects.create( StoreRoom=store_room, Product=product_obj, @@ -1131,13 +1127,11 @@ def newFactor2(request, c_id): ) except InventoryException as e: - # اگر خطا دهد دیتابیس Rollback می‌شود و فرم در قالب همان پیغام HTML به کاربر برمی‌گردد return HttpResponse(str(e), status=400) except Exception as e: logger.error(f"Error creating factor for customer {c_id}: {str(e)}", exc_info=True) return HttpResponse("خطایی غیرمنتظره در سرور رخ داد. اطلاعات ذخیره نشد.", status=500) - # بازگشت موفقیت‌آمیز دقیقاً به همان آدرس فایل urls.py return redirect(f'/panel/customer/{c_id}/factors/') @login_required(login_url='/panel/p_login/')