fix garanty esh

This commit is contained in:
javad committed 2026-06-24 18:22:34 +03:30
1 parent bf78bc2520
commit 739104115a
6 files changed
+382 -78

No files matched your search

+134 -55
View File
@@ -9,7 +9,7 @@ from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.core.exceptions import ValidationError # جدید
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest # بخشBadRequest جدید
from django.db import transaction # جدید
from django.db import transaction, connection
from django.db.models import Count, Sum, Q
from django.contrib.auth.models import Permission, User
from django.contrib.auth import authenticate, login, logout
@@ -470,13 +470,20 @@ def CustomerJson(request, c_id):
@login_required(login_url='/panel/p_login/')
def productMojodi(request, p_id):
storeUser = StoreRoom.objects.get(user__id=request.user.id)
productS_in = ProductInStore.objects.filter(Product__id=p_id, StoreRoom=storeUser, year=1400).aggregate(Sum('s_in'))['s_in__sum']
productS_out = ProductInStore.objects.filter(Product__id=p_id, StoreRoom=storeUser, year=1400).aggregate(Sum('s_out'))['s_out__sum']
#return HttpResponse(productS_out)
try:
mojodi_prd = productS_in - productS_out
except:
mojodi_prd = 0
# ۱. تجمیع ورودی و خروجی در یک کوئری بهینه برای افزایش سرعت دیتابیس
totals = ProductInStore.objects.filter(
Product__id=p_id, StoreRoom=storeUser, year=1400
).aggregate(total_in=Sum('s_in'), total_out=Sum('s_out'))
# ۲. رفع باگ قطعی NoneType: اگر رکوردی نباشد، مقدار None را به 0 تبدیل می‌کند
total_in = totals['total_in'] or 0
total_out = totals['total_out'] or 0
# ۳. محاسبه دقیق موجودی بدون ریسک بروز خطای ریاضی پایتون
mojodi_prd = total_in - total_out
# ۴. بازگشت خروجی دقیقاً متناسب با ساختار متنی فرانت‌اَند شما
return HttpResponse(str(mojodi_prd))
@login_required(login_url='/panel/p_login/')
@@ -726,6 +733,7 @@ def productAdd(request):
prd_in_store.StoreRoom = StoreRoom.objects.get(id=storeroom)
prd_in_store.s_in = mojodiAval
prd_in_store.desc = "موجودی اولیه: "+str(mojodiAval)
prd_in_store.movement_type = ProductInStore.MovementType.INITIAL
prd_in_store.save()
#return HttpResponse(installDate)
@@ -758,51 +766,105 @@ def productDel(request, p_id, s_id):
@login_required(login_url='/panel/p_login/')
@permission_required('admin_panel.ViewStore', raise_exception=True)
def operation(request):
prd_id = request.POST.get('prd_id', '')
if request.method != 'POST':
return HttpResponseBadRequest("متد ارسالی نامعتبر است.")
# استفاده از توابع امن اختصاصی پروژه شما برای جداسازی مقادیر عددی
prd_id = _safe_int(request.POST.get('prd_id'))
actionType = request.POST.get('actionType', '')
tedad = int(request.POST.get('tedad', ''))
azanbar = request.POST.get('azanbar', '')
beanbar = request.POST.get('beanbar', '')
tedad = _safe_int(request.POST.get('tedad'))
azanbar = _safe_int(request.POST.get('azanbar'))
beanbar = _safe_int(request.POST.get('beanbar'))
desc = request.POST.get('desc', '')
pInStore = ProductInStore()
sRoom = StoreRoom.objects.get(id=azanbar)
productO = Product.objects.get(id=prd_id)
pInStore.StoreRoom = sRoom
pInStore.Product = productO
if actionType == 'vorod':
pInStore.s_in = tedad
pInStore.desc = "ورود: "+str(tedad)+" به انبار " + str(sRoom.store_name) + "#" + str(desc)
elif actionType == 'khoroj':
productS_in = ProductInStore.objects.filter(Product=productO, StoreRoom=sRoom, year=1400).aggregate(Sum('s_in'))['s_in__sum']
productS_out = ProductInStore.objects.filter(Product=productO, StoreRoom=sRoom, year=1400).aggregate(Sum('s_out'))['s_out__sum']
mojodi_prd = productS_in - productS_out
if mojodi_prd >= tedad:
pInStore.s_out = tedad
pInStore.desc = "خروج: "+str(tedad)+" از انبار " + str(sRoom.store_name) + "#" + str(desc)
else:
return HttpResponse("موجودی کافی نیست، موجودی فعلی: "+ str(mojodi_prd))
elif actionType == 'transfer':
beanbar = int(beanbar)
beanbarOjb = StoreRoom.objects.get(id=beanbar)
productS_in = ProductInStore.objects.filter(Product=productO, StoreRoom=sRoom, year=1400).aggregate(Sum('s_in'))['s_in__sum']
productS_out = ProductInStore.objects.filter(Product=productO, StoreRoom=sRoom, year=1400).aggregate(Sum('s_out'))['s_out__sum']
mojodi_prd = productS_in - productS_out
if mojodi_prd >= tedad:
pInStore.s_out = tedad
pInStore.desc = "خروج: "+str(tedad)+" از انبار " + str(sRoom.store_name) + " جهت انتقال به انبار " + str(beanbarOjb.store_name) + "#" + str(desc)
else:
return HttpResponse("موجودی کافی نیست، موجودی فعلی: "+ str(mojodi_prd))
if tedad <= 0:
return HttpResponseBadRequest("تعداد وارد شده باید بیشتر از صفر باشد.")
pInStore.save()
sRoom = get_object_or_404(StoreRoom, id=azanbar)
productO = get_object_or_404(Product, id=prd_id)
if actionType == 'transfer':
pInStore = ProductInStore()
pInStore.StoreRoom = beanbarOjb
pInStore.s_in = tedad
pInStore.Product = productO
pInStore.desc = "ورود: "+str(tedad)+" از انبار " + str(sRoom.store_name) + "#" + str(desc)
pInStore.save()
try:
with transaction.atomic():
# ساخت کوئری پایه برای دریافت تراکنش‌های سال جاری کالا
qs = ProductInStore.objects.filter(
Product=productO, StoreRoom=sRoom, year=1400
)
# بررسی هوشمند پشتیبانی دیتابیس از قابلیت قفل ردیف (سازگار با SQLite و PostgreSQL)
if connection.features.has_select_for_update:
qs = qs.select_for_update()
# بارگذاری امن تراکنش‌ها در حافظه (RAM) جهت جلوگیری از خطای توابع تجمیعی همزمان با قفل دیتابیس
locked_records = list(qs)
# --- عملیات ورود دستی ---
if actionType == 'vorod':
pInStore = ProductInStore(
StoreRoom=sRoom,
Product=productO,
year=1400
)
pInStore.s_in = tedad
pInStore.desc = "ورود: "+str(tedad)+" به انبار " + str(sRoom.store_name) + "#" + str(desc)
pInStore.movement_type = ProductInStore.MovementType.DIRECT_IN
pInStore.save()
# --- عملیات خروج دستی یا انتقال بین انبارها ---
elif actionType in ['khoroj', 'transfer']:
# محاسبات دقیق و سریع موجودی کالا در سطح پایتون بدون درگیر کردن دیسک
total_in = sum(item.s_in for item in locked_records if item.s_in)
total_out = sum(item.s_out for item in locked_records if item.s_out)
mojodi_prd = total_in - total_out
if mojodi_prd < tedad:
# تطابق ۱۰۰ درصدی پاسخ کدهای قدیمی با فرانت‌اند شما
return HttpResponse("موجودی کافی نیست، موجودی فعلی: "+ str(mojodi_prd))
# الف) خروج دستی ساده
if actionType == 'khoroj':
pInStore = ProductInStore(
StoreRoom=sRoom,
Product=productO,
year=1400
)
pInStore.s_out = tedad
pInStore.desc = "خروج: "+str(tedad)+" از انبار " + str(sRoom.store_name) + "#" + str(desc)
pInStore.movement_type = ProductInStore.MovementType.DIRECT_OUT
pInStore.save()
# ب) انتقال بین دو انبار مجزا
elif actionType == 'transfer':
if not beanbar:
return HttpResponse("انبار مقصد مشخص نشده است.")
beanbarOjb = get_object_or_404(StoreRoom, id=_safe_int(beanbar))
# ردیف اول: ثبت تراکنش خروج از انبار مبدأ
pInStore = ProductInStore(
StoreRoom=sRoom,
Product=productO,
year=1400
)
pInStore.s_out = tedad
pInStore.desc = "خروج: "+str(tedad)+" از انبار " + str(sRoom.store_name) + " جهت انتقال به انبار " + str(beanbarOjb.store_name) + "#" + str(desc)
pInStore.movement_type = ProductInStore.MovementType.TRANSFER
pInStore.save()
# ردیف دوم: ثبت تراکنش ورود به انبار مقصد (محافظت شده در بلاک اتمیک)
ProductInStore.objects.create(
StoreRoom=beanbarOjb,
Product=productO,
s_in=tedad,
year=1400,
desc="ورود: "+str(tedad)+" از انبار " + str(sRoom.store_name) + "#" + str(desc),
movement_type=ProductInStore.MovementType.TRANSFER
)
except Exception as e:
logger.error(f"Error in store operation view: {str(e)}", exc_info=True)
return HttpResponse("خطای سیستمی رخ داد. تراکنش لغو شد.")
return redirect('/panel/store/'+str(sRoom.id))
@@ -1053,7 +1115,7 @@ def newFactor2(request, c_id):
user_fullname = f"{user_op.first_name} {user_op.last_name}".strip() or user_op.username
for item in prds_list:
# تبدیل امن شناسه به عدد در همینجا برای جلوگیری از خطای یافت نشدن دیکشنری
# تبدیل امن شناسه به عدد
prd_id = _safe_int(item.get('prd_id'))
prd_count = _safe_int(item.get('prd_count'))
garanty_esh = item.get('garanty_Esh', '')
@@ -1070,7 +1132,6 @@ def newFactor2(request, c_id):
else:
product_obj = products_by_id.get(prd_id)
# خطای کاربرپسند در صورت نبود محصول
if not product_obj:
raise InventoryException(f"خطا در دریافت محصول: محصولی با شناسه {prd_id} در سیستم وجود ندارد.")
@@ -1085,6 +1146,7 @@ def newFactor2(request, c_id):
FactorIteam.objects.bulk_create(factor_items_pool, batch_size=500)
# فرآیند کسر از انبار (Enterprise Level)
for item in prds_list:
prd_id = _safe_int(item.get('prd_id'))
if prd_id == 9999:
@@ -1097,32 +1159,49 @@ def newFactor2(request, c_id):
if not product_obj:
continue
stock_records = ProductInStore.objects.select_for_update().filter(
# ۱. کوئری پایه
qs = ProductInStore.objects.filter(
Product=product_obj,
StoreRoom=store_room,
year=1400
)
list(stock_records)
# ۲. قفل دیتابیس هوشمند و سازگار با تمام پایگاه‌های داده
if connection.features.has_select_for_update:
qs = qs.select_for_update()
# ۳. بارگذاری امن در رم برای جلوگیری از خطای Aggregate در دیتابیس قفل‌شده
locked_records = list(qs)
totals = stock_records.aggregate(total_in=Sum('s_in'), total_out=Sum('s_out'))
total_in = totals['total_in'] or 0
total_out = totals['total_out'] or 0
# ۴. محاسبه دقیق و پرسرعت در پایتون
total_in = sum(rec.s_in for rec in locked_records if rec.s_in)
total_out = sum(rec.s_out for rec in locked_records if rec.s_out)
mojodi_prd = total_in - total_out
if mojodi_prd < prd_count:
# استفاده از متن دقیق خطای قبلی جهت حفظ سازگاری با فرانت‌اند
raise InventoryException(f"موجودی کافی نیست، موجودی فعلی {product_obj.product_name}: {mojodi_prd}")
# ۵. تشخیص نوع تراکنش برای سیستم گزارش‌گیری جدید انبار
if garanty_esh and str(garanty_esh).strip():
desc_text = f"خروج: {prd_count}#{garanty_esh}#{customer_obj.name_family} * {user_fullname}"
if "اشانتیون" in str(garanty_esh):
m_type = ProductInStore.MovementType.PROMOTION
else:
m_type = ProductInStore.MovementType.WARRANTY
else:
desc_text = f"خروج: {prd_count}#فروش#{customer_obj.name_family} * {user_fullname}"
m_type = ProductInStore.MovementType.SALE
# ۶. ثبت تراکنش خروجی انبار
ProductInStore.objects.create(
StoreRoom=store_room,
Product=product_obj,
factor=new_factor,
s_out=prd_count,
desc=desc_text,
movement_type=m_type, # ثبت ساختاریافته نوع خروج برای سرعت در گزارش‌گیری
year=1400
)