fix garanty esh
This commit is contained in:
1 parent
bf78bc2520
commit
739104115a
6 files changed
+382
-78
No files matched your search
+134
-55
@@ -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
|
||||
)
|
||||
|
||||
|
||||
Reference in new issue
Block a user