improve sabt factor
This commit is contained in:
1 parent
96dd277f6b
commit
1672de549a
2 files changed
+223
-219
No files matched your search
+216
-219
@@ -1,24 +1,63 @@
|
|||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render, get_object_or_404
|
||||||
from django.urls.base import reverse
|
from django.urls.base import reverse
|
||||||
|
from django.urls import reverse # تکراری در کد اصلی شما بود، اما جهت عدم تغییر حفظ شد
|
||||||
from persiantools.jdatetime import JalaliDate
|
from persiantools.jdatetime import JalaliDate
|
||||||
from persiantools import characters, digits
|
from persiantools import characters, digits
|
||||||
import datetime
|
import datetime
|
||||||
|
from django.utils import timezone # جدید
|
||||||
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
|
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
|
||||||
|
from django.core.exceptions import ValidationError # جدید
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
# Create your views here.
|
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest # بخشBadRequest جدید
|
||||||
from django.http import HttpResponse
|
from django.db import transaction # جدید
|
||||||
from django.urls import reverse
|
from django.db.models import Count, Sum, Q
|
||||||
from django.http import Http404, HttpResponseRedirect
|
|
||||||
from django.db.models import Count, Sum
|
|
||||||
from django.contrib.auth.models import Permission, User
|
from django.contrib.auth.models import Permission, User
|
||||||
from django.contrib.auth import authenticate, login, logout
|
from django.contrib.auth import authenticate, login, logout
|
||||||
from django.contrib.auth.decorators import login_required, permission_required
|
from django.contrib.auth.decorators import login_required, permission_required
|
||||||
import os
|
import os
|
||||||
from django.shortcuts import get_object_or_404
|
import json # جدید
|
||||||
from django.db.models import Q
|
import logging # جدید
|
||||||
|
|
||||||
from .models import *
|
from .models import *
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
class InventoryException(Exception):
|
||||||
|
"""خطای اختصاصی برای کنترل موجودی انبار"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _safe_int(value, default=0):
|
||||||
|
"""تابع امن برای تبدیل مقادیر دریافتی از جاوااسکریپت به عدد"""
|
||||||
|
if value is None:
|
||||||
|
return default
|
||||||
|
try:
|
||||||
|
clean_val = str(value).strip()
|
||||||
|
return int(clean_val) if clean_val else default
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return default
|
||||||
|
|
||||||
|
def _parse_jalali_date(date_str):
|
||||||
|
"""تبدیل امن تاریخ ارسالی از pDatepicker به میلادی"""
|
||||||
|
if not date_str or not str(date_str).strip():
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
clean_date = digits.fa_to_en(digits.ar_to_fa(str(date_str).strip()))
|
||||||
|
y, m, d = map(int, clean_date.split("/"))
|
||||||
|
return JalaliDate(y, m, d).to_gregorian()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Error parsing Jalali date '{date_str}': {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def _parse_timestamp(ts_value):
|
||||||
|
"""تبدیل یونیکس تایماستمپهای ارسالی از جاوااسکریپت"""
|
||||||
|
if not ts_value:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
# تبدیل میلیثانیه جاوااسکریپت به ثانیه پایتون
|
||||||
|
return datetime.datetime.fromtimestamp(int(ts_value) / 1000.0, tz=timezone.get_current_timezone())
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return None
|
||||||
|
|
||||||
def p_login(request):
|
def p_login(request):
|
||||||
username = request.POST.get('username', None )
|
username = request.POST.get('username', None )
|
||||||
password = request.POST.get('password', None )
|
password = request.POST.get('password', None )
|
||||||
@@ -915,233 +954,191 @@ def newFactor(request, c_id):
|
|||||||
|
|
||||||
@login_required(login_url='/panel/p_login/')
|
@login_required(login_url='/panel/p_login/')
|
||||||
def newFactor2(request, c_id):
|
def newFactor2(request, c_id):
|
||||||
import json
|
if request.method != 'POST':
|
||||||
prdsList = request.POST.get('prdsList', '')
|
return HttpResponseBadRequest("متد ارسالی باید POST باشد.")
|
||||||
sarResidDate = request.POST.get('sarResidDate', None)
|
|
||||||
user_op = request.user
|
user_op = request.user
|
||||||
|
|
||||||
|
# ۱. پارس دادههای JSON ارسال شده از فرم (پشتیبانی کامل از ساختار paylist و orderList جاوااسکریپت)
|
||||||
try:
|
try:
|
||||||
sarResidDate = digits.fa_to_en(digits.ar_to_fa(sarResidDate))
|
prds_list = json.loads(request.POST.get('prdsList') or '[]')
|
||||||
#return HttpResponse(sarResidDate)
|
json_pardakht_ha = json.loads(request.POST.get('PardakhtHa') or '{}')
|
||||||
sarResidDate = sarResidDate.split("/")
|
except json.JSONDecodeError:
|
||||||
#return HttpResponse(sarResidDate[2])
|
return HttpResponseBadRequest("فرمت دادههای ارسالی JSON نامعتبر است.")
|
||||||
sarResidDate = JalaliDate(int(sarResidDate[0]), int(sarResidDate[1]), int(sarResidDate[2])).to_gregorian()
|
|
||||||
#return HttpResponse(sarResidDate)
|
|
||||||
except:
|
|
||||||
sarResidDate = None
|
|
||||||
|
|
||||||
#return HttpResponse(sarResidDate[1])
|
factor_type = request.POST.get('FactorType', '')
|
||||||
#sarResidDate = JalaliDate(sarResidDate[0], sarResidDate[1], sarResidDate[2]).to_gregorian()
|
factor_desc = request.POST.get('FactorDesc', '')
|
||||||
|
sar_resid_date = _parse_jalali_date(request.POST.get('sarResidDate'))
|
||||||
|
|
||||||
FactorDate = request.POST.get('FactorDate', '')
|
# ۲. واکشی اطلاعات پایهای
|
||||||
FactorType = request.POST.get('FactorType', '')
|
customer_obj = get_object_or_404(Customer, id=c_id)
|
||||||
FactorDesc = request.POST.get('FactorDesc', '')
|
store_room = get_object_or_404(StoreRoom, user=user_op)
|
||||||
PardakhtHa = request.POST.get('PardakhtHa', '')
|
|
||||||
jsonPardakhtHa = json.loads(PardakhtHa)
|
|
||||||
|
|
||||||
#return HttpResponse(PardakhtHa)
|
# ۳. واکشی دستهجمعی محصولات برای جلوگیری از فشار به دیتابیس
|
||||||
|
product_ids = [item.get('prd_id') for item in prds_list if item.get('prd_id') and item.get('prd_id') != 9999]
|
||||||
|
products_by_id = {p.id: p for p in Product.objects.filter(id__in=product_ids)}
|
||||||
|
|
||||||
#print(datetime.datetime.fromtimestamp(int(jsonPardakhtHa['l_checkPriceSarResid'])/1000.0))
|
# ۴. محاسبه مبلغ کل با نادیده گرفتن محصولاتی که گارانتی/اشانتیون هستند
|
||||||
#return HttpResponse(type(jsonPardakhtHa['l_nesiyeDateTime']))
|
sum_price = sum(
|
||||||
#for pardakhti in jsonPardakhtHa:
|
_safe_int(item.get('prd_count')) * _safe_int(item.get('price'))
|
||||||
#if(jsonPardakhtHa[pardakhti] != None):
|
for item in prds_list
|
||||||
#print(str(pardakhti)+" --> " +str(jsonPardakhtHa[pardakhti]))
|
if not str(item.get('garanty_Esh', '')).strip()
|
||||||
|
)
|
||||||
|
|
||||||
#return HttpResponse(PardakhtHa)
|
|
||||||
|
|
||||||
Factorfail = False
|
|
||||||
customerObj=Customer.objects.get(id=c_id)
|
|
||||||
storeUser = StoreRoom.objects.get(user__id=request.user.id)
|
|
||||||
|
|
||||||
prdsList = json.loads(prdsList)
|
|
||||||
sum_price = 0
|
|
||||||
for prd in prdsList:
|
|
||||||
if prd['garanty_Esh'] == ' ' or prd['garanty_Esh'] == '' or prd['garanty_Esh'] == None:
|
|
||||||
sum_price += prd['prd_count'] * prd['price']
|
|
||||||
|
|
||||||
#return HttpResponse(prd_code)
|
|
||||||
try:
|
try:
|
||||||
newFactor = Factor()
|
# ۵. باز کردن یک تراکنش امن دیتابیس
|
||||||
newFactor.customer= customerObj
|
with transaction.atomic():
|
||||||
newFactor.amount = sum_price
|
|
||||||
|
# ثبت آبجکت فاکتور
|
||||||
|
new_factor = Factor.objects.create(
|
||||||
|
customer=customer_obj,
|
||||||
|
amount=sum_price,
|
||||||
|
FactorType=factor_type,
|
||||||
|
desc=factor_desc,
|
||||||
|
deadline=sar_resid_date,
|
||||||
|
user=user_op
|
||||||
|
)
|
||||||
|
|
||||||
newFactor.FactorType = FactorType
|
# ثبت بدهکاری کلی در سند حسابداری
|
||||||
|
Article.objects.create(
|
||||||
newFactor.desc = FactorDesc
|
debit=sum_price,
|
||||||
|
credit=0,
|
||||||
|
customer=customer_obj,
|
||||||
|
factor=new_factor,
|
||||||
|
desc=f"{customer_obj.name_family} فاکتور شماره: {new_factor.id}",
|
||||||
|
user=user_op
|
||||||
|
)
|
||||||
|
|
||||||
if sarResidDate != None:
|
# ثبت ریزپرداختیها بر اساس دیکشنری paylist جاوااسکریپت
|
||||||
newFactor.deadline = sarResidDate
|
payment_map = {
|
||||||
newFactor.user = User.objects.get(id=request.user.id)
|
'l_naghd': 'نقد',
|
||||||
newFactor.save()
|
'l_kartbekart': 'کارت به کارت',
|
||||||
except:
|
'l_kartKhan': 'کارت خوان',
|
||||||
try:
|
'l_takhfif': 'تخفیف'
|
||||||
Factorfail=True
|
}
|
||||||
newFactor.delete()
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
#ثبت مبالغ پرداختی
|
for key, amount_raw in json_pardakht_ha.items():
|
||||||
if Factorfail != True:
|
amount = _safe_int(amount_raw)
|
||||||
|
if amount <= 0:
|
||||||
#مبلغ کلی فاکتور وارد می شود
|
continue
|
||||||
is_naghd = False
|
|
||||||
'''articleHesabDari = Article()
|
|
||||||
articleHesabDari.debit = newFactor.amount
|
|
||||||
articleHesabDari.credit = 0
|
|
||||||
articleHesabDari.customer = customerObj
|
|
||||||
articleHesabDari.factor = newFactor
|
|
||||||
|
|
||||||
articleHesabDari.desc = str(FactorType) + " " + str(customerObj.name_family) + " طی فاکتور شماره: " + str(newFactor.id)
|
if key in payment_map:
|
||||||
articleHesabDari.save()'''
|
nahve_pardakht = payment_map[key]
|
||||||
#ابتدا مانده حساب بستانکار قبل گرفته میشود از فاکتور کم بشه
|
Article.objects.create(
|
||||||
debit = Article.objects.filter(customer=customerObj).aggregate(Sum('debit'))['debit__sum']
|
debit=0,
|
||||||
credit = Article.objects.filter(customer=customerObj).aggregate(Sum('credit'))['credit__sum']
|
credit=amount,
|
||||||
|
customer=customer_obj,
|
||||||
mandehesabCs = 0
|
factor=new_factor,
|
||||||
try:
|
desc=f"{nahve_pardakht} {customer_obj.name_family} فاکتور شماره: {new_factor.id}",
|
||||||
if (debit - credit) < 0:
|
nahvePardakht=nahve_pardakht,
|
||||||
mandehesabCs = abs(debit - credit)
|
payStatus="true",
|
||||||
except:
|
user=user_op
|
||||||
pass
|
)
|
||||||
|
|
||||||
#کل مبلغ فاکتور بدهکار میشه اول
|
# ثبت اسناد چک و نسیه
|
||||||
# اگر مشتری بستانکار بود ازش کم میشه
|
elif key in ['l_nesiye', 'l_checkPrice']:
|
||||||
articleHesabDari2 = Article()
|
is_nesiye = (key == 'l_nesiye')
|
||||||
articleHesabDari2.debit = (sum_price)
|
type_str = 'nesiye' if is_nesiye else 'check'
|
||||||
articleHesabDari2.credit = 0
|
ts_key = 'l_nesiyeSarResid' if is_nesiye else 'l_checkPriceSarResid'
|
||||||
articleHesabDari2.customer = customerObj
|
|
||||||
articleHesabDari2.factor = newFactor
|
|
||||||
strDes = str(customerObj.name_family) + " فاکتور شماره: " + str(newFactor.id) + ""
|
|
||||||
#if mandehesabCs > 1:
|
|
||||||
# strDes += " مبلغ کل فاکتور" +str(sum_price)+ " - " + str(mandehesabCs)+" بستانکار از قبل "
|
|
||||||
articleHesabDari2.desc = strDes
|
|
||||||
articleHesabDari2.user = user_op
|
|
||||||
articleHesabDari2.save()
|
|
||||||
|
|
||||||
#مبالغ پرداختی وارد می شود
|
|
||||||
for pardakhti in jsonPardakhtHa:
|
|
||||||
|
|
||||||
if(jsonPardakhtHa[pardakhti] != None):
|
|
||||||
#بستانکارها
|
|
||||||
if str(pardakhti) in ['l_naghd', 'l_kartbekart', 'l_kartKhan']:
|
|
||||||
nahvePardakht = ''
|
|
||||||
if str(pardakhti) == 'l_naghd':
|
|
||||||
nahvePardakht = 'نقد'
|
|
||||||
elif str(pardakhti) == 'l_kartbekart':
|
|
||||||
nahvePardakht = 'کارت به کارت'
|
|
||||||
elif str(pardakhti) == 'l_kartKhan':
|
|
||||||
nahvePardakht = 'کارت خوان'
|
|
||||||
elif str(pardakhti) == 'l_takhfif':
|
|
||||||
nahvePardakht = 'تخفیف'
|
|
||||||
|
|
||||||
articleHesabDari2 = Article()
|
|
||||||
articleHesabDari2.debit = 0
|
|
||||||
articleHesabDari2.credit = int(jsonPardakhtHa[pardakhti])
|
|
||||||
articleHesabDari2.customer = customerObj
|
|
||||||
articleHesabDari2.factor = newFactor
|
|
||||||
articleHesabDari2.desc = nahvePardakht + " " + str(customerObj.name_family) + " فاکتور شماره: " + str(newFactor.id)
|
|
||||||
articleHesabDari2.nahvePardakht = nahvePardakht
|
|
||||||
articleHesabDari2.payStatus = "true"
|
|
||||||
articleHesabDari2.user = user_op
|
|
||||||
articleHesabDari2.save()
|
|
||||||
|
|
||||||
elif str(pardakhti) in ['l_takhfif']:
|
|
||||||
if str(pardakhti) == 'l_takhfif':
|
|
||||||
nahvePardakht = 'تخفیف'
|
|
||||||
|
|
||||||
articleHesabDari2 = Article()
|
|
||||||
articleHesabDari2.debit = 0
|
|
||||||
articleHesabDari2.credit = int(jsonPardakhtHa[pardakhti])
|
|
||||||
articleHesabDari2.customer = customerObj
|
|
||||||
articleHesabDari2.factor = newFactor
|
|
||||||
articleHesabDari2.desc = nahvePardakht + " " + str(customerObj.name_family) + " فاکتور شماره: " + str(newFactor.id)
|
|
||||||
articleHesabDari2.nahvePardakht = nahvePardakht
|
|
||||||
articleHesabDari2.payStatus = "true"
|
|
||||||
articleHesabDari2.user = user_op
|
|
||||||
articleHesabDari2.save()
|
|
||||||
|
|
||||||
pass
|
|
||||||
#چک یا نیسیه بدهکار
|
|
||||||
elif str(pardakhti) in ['l_nesiye', 'l_checkPrice']:
|
|
||||||
nahvePardakht = ''
|
|
||||||
sarResidDateTime01 = None
|
|
||||||
if str(pardakhti) == 'l_nesiye':
|
|
||||||
sarResidDateTime01 = datetime.datetime.fromtimestamp(int(jsonPardakhtHa['l_nesiyeSarResid'])/1000.0)
|
|
||||||
nahvePardakht = 'nesiye'
|
|
||||||
elif str(pardakhti) == 'l_checkPrice':
|
|
||||||
sarResidDateTime01 = datetime.datetime.fromtimestamp(int(jsonPardakhtHa['l_checkPriceSarResid'])/1000.0)
|
|
||||||
nahvePardakht = 'check'
|
|
||||||
|
|
||||||
|
|
||||||
sarresidINC = Sarresid()
|
|
||||||
sarresidINC.customer = customerObj
|
|
||||||
sarresidINC.price = int(jsonPardakhtHa[pardakhti])
|
|
||||||
sarresidINC.payStatus = "false"
|
|
||||||
sarresidINC.type = nahvePardakht
|
|
||||||
sarresidINC.user = request.user
|
|
||||||
sarresidINC.factor = newFactor
|
|
||||||
sarresidINC.sarresidDT = sarResidDateTime01
|
|
||||||
sarresidINC.save()
|
|
||||||
|
|
||||||
#articleHesabDari2 = Article()
|
sar_resid_dt = _parse_timestamp(json_pardakht_ha.get(ts_key))
|
||||||
#articleHesabDari2.debit = int(jsonPardakhtHa[pardakhti])
|
|
||||||
#articleHesabDari2.credit = 0
|
|
||||||
#articleHesabDari2.customer = customerObj
|
|
||||||
#articleHesabDari2.factor = newFactor
|
|
||||||
#articleHesabDari2.desc = nahvePardakht + " " + str(customerObj.name_family) + " فاکتور شماره: " + str(newFactor.id)
|
|
||||||
#articleHesabDari2.nahvePardakht = nahvePardakht
|
|
||||||
#articleHesabDari2.payStatus = "false"
|
|
||||||
#articleHesabDari2.sarresid = sarResidDateTime01
|
|
||||||
#print(sarResidDateTime01)
|
|
||||||
#articleHesabDari2.save()
|
|
||||||
|
|
||||||
#ایتم های فاکتور
|
Sarresid.objects.create(
|
||||||
for prd in prdsList:
|
customer=customer_obj,
|
||||||
try:
|
price=amount,
|
||||||
fIteams = FactorIteam()
|
payStatus="false",
|
||||||
fIteams.factor = newFactor
|
type=type_str,
|
||||||
if prd['prd_id'] == 9999:
|
user=user_op,
|
||||||
Gproduct = None
|
factor=new_factor,
|
||||||
fIteams.product = Gproduct
|
sarresidDT=sar_resid_dt
|
||||||
fIteams.product_name = prd['product_name']
|
)
|
||||||
fIteams.num = prd['prd_count']
|
|
||||||
fIteams.amount = int(prd['priceKol'])
|
|
||||||
else:
|
|
||||||
Gproduct = Product.objects.get(id=prd['prd_id'])
|
|
||||||
fIteams.product = Gproduct
|
|
||||||
fIteams.product_name = Gproduct.product_name
|
|
||||||
fIteams.num = prd['prd_count']
|
|
||||||
fIteams.amount = prd['prd_count'] * Gproduct.product_price
|
|
||||||
fIteams.garantyEsh = prd['garanty_Esh']
|
|
||||||
fIteams.save()
|
|
||||||
except:
|
|
||||||
Factorfail = True
|
|
||||||
newFactor.delete()
|
|
||||||
break
|
|
||||||
#کم کردن موجودی انبار
|
|
||||||
if Factorfail != True:
|
|
||||||
for prd in prdsList:
|
|
||||||
if prd['prd_id'] != 9999:
|
|
||||||
pInStore = ProductInStore()
|
|
||||||
sRoom = storeUser
|
|
||||||
productO = Product.objects.get(id=prd['prd_id'])
|
|
||||||
pInStore.StoreRoom = sRoom
|
|
||||||
pInStore.Product = productO
|
|
||||||
pInStore.factor = newFactor
|
|
||||||
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 >= prd['prd_count']:
|
|
||||||
pInStore.s_out = prd['prd_count']
|
|
||||||
if prd['garanty_Esh'] != ' ' and prd['garanty_Esh'] != '' and prd['garanty_Esh'] != None:
|
|
||||||
pInStore.desc = "خروج: " + str(prd['prd_count']) + "#"+str(prd['garanty_Esh'])+ "#" + str(customerObj.name_family) + " * " + str(request.user.first_name) + " " + str(request.user.last_name)
|
|
||||||
else:
|
|
||||||
pInStore.desc = "خروج: " + str(prd['prd_count']) + "#فروش" + "#" + str(customerObj.name_family) + " * " + str(request.user.first_name) + " " + str(request.user.last_name)
|
|
||||||
pInStore.save()
|
|
||||||
else:
|
|
||||||
return HttpResponse("موجودی کافی نیست، موجودی فعلی: "+ str(mojodi_prd))
|
|
||||||
|
|
||||||
return redirect('/panel/customer/'+str(c_id)+'/factors/')
|
# ثبت آیتمهای فاکتور با کارایی بالا (املای کلاس 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_count = _safe_int(item.get('prd_count'))
|
||||||
|
garanty_esh = item.get('garanty_Esh', '')
|
||||||
|
|
||||||
|
if prd_id == 9999:
|
||||||
|
factor_items_pool.append(FactorIteam(
|
||||||
|
factor=new_factor,
|
||||||
|
product=None,
|
||||||
|
product_name=item.get('product_name', ''),
|
||||||
|
num=prd_count,
|
||||||
|
amount=_safe_int(item.get('priceKol')),
|
||||||
|
garantyEsh=garanty_esh
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
product_obj = products_by_id.get(prd_id)
|
||||||
|
if not product_obj:
|
||||||
|
raise ValidationError(f"محصولی با شناسه {prd_id} یافت نشد.")
|
||||||
|
|
||||||
|
factor_items_pool.append(FactorIteam(
|
||||||
|
factor=new_factor,
|
||||||
|
product=product_obj,
|
||||||
|
product_name=product_obj.product_name,
|
||||||
|
num=prd_count,
|
||||||
|
amount=prd_count * _safe_int(product_obj.product_price),
|
||||||
|
garantyEsh=garanty_esh
|
||||||
|
))
|
||||||
|
|
||||||
|
# ثبت یکباره تمام آیتمها
|
||||||
|
FactorIteam.objects.bulk_create(factor_items_pool, batch_size=500)
|
||||||
|
|
||||||
|
# چک کردن موجودی انبار و ثبت کسر کالا
|
||||||
|
for item in prds_list:
|
||||||
|
prd_id = item.get('prd_id')
|
||||||
|
if prd_id == 9999:
|
||||||
|
continue
|
||||||
|
|
||||||
|
prd_count = _safe_int(item.get('prd_count'))
|
||||||
|
garanty_esh = item.get('garanty_Esh', '')
|
||||||
|
product_obj = products_by_id.get(prd_id)
|
||||||
|
|
||||||
|
# قفل کردن ردیف برای اطمینان ۱۰۰ درصدی از عدم تداخل پیش از متد Save مدل
|
||||||
|
stock_records = ProductInStore.objects.select_for_update().filter(
|
||||||
|
Product=product_obj,
|
||||||
|
StoreRoom=store_room,
|
||||||
|
year=1400
|
||||||
|
)
|
||||||
|
list(stock_records)
|
||||||
|
|
||||||
|
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
|
||||||
|
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}"
|
||||||
|
else:
|
||||||
|
desc_text = f"خروج: {prd_count}#فروش#{customer_obj.name_family} * {user_fullname}"
|
||||||
|
|
||||||
|
# این خط بهطور خودکار متد حرفهای Save شما در models.py را فراخوانی میکند
|
||||||
|
# و جداول تاریخچه انبارداری را نیز بروزرسانی مینماید
|
||||||
|
ProductInStore.objects.create(
|
||||||
|
StoreRoom=store_room,
|
||||||
|
Product=product_obj,
|
||||||
|
factor=new_factor,
|
||||||
|
s_out=prd_count,
|
||||||
|
desc=desc_text,
|
||||||
|
year=1400
|
||||||
|
)
|
||||||
|
|
||||||
|
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/')
|
@login_required(login_url='/panel/p_login/')
|
||||||
@permission_required('admin_panel.DelFactor', raise_exception=True)
|
@permission_required('admin_panel.DelFactor', raise_exception=True)
|
||||||
|
|||||||
@@ -4,5 +4,12 @@ DATABASES = {
|
|||||||
'default': {
|
'default': {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
'ENGINE': 'django.db.backends.sqlite3',
|
||||||
'NAME': Path('/app/sqlite/db.sqlite3'),
|
'NAME': Path('/app/sqlite/db.sqlite3'),
|
||||||
|
'OPTIONS': {
|
||||||
|
# ۱. افزایش زمان انتظار (Timeout) به ثانیه
|
||||||
|
# با این کار اگر دیتابیس قفل باشد، کاربر دوم کرش نمیکند بلکه تا ۳۰ ثانیه منتظر باز شدن قفل میماند
|
||||||
|
'timeout': 30,
|
||||||
|
# ۲. فعالسازی حالت WAL برای بالا بردن قدرت پردازش همزمانی در SQLite
|
||||||
|
'init_command': 'PRAGMA journal_mode=WAL;',
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user