from datetime import datetime,time,timedelta
from uuid import uuid4
from decimal import Decimal,InvalidOperation
from functools import wraps
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.db import transaction
from django.db.models import F,Q,Sum,Count,DecimalField,Value
from django.db.models.functions import Coalesce
from django.http import Http404,HttpResponseForbidden
from django.shortcuts import render,redirect,get_object_or_404
from django.urls import reverse
from django.utils import timezone
from django.views.decorators.http import require_POST
from .models import Branch,Profile,Category,Product,Stock,StockHistory,PurchaseRequest,RequestItem,Notification,SystemLog
from .forms import UserCreateForm,UserEditForm,BranchForm,CategoryForm,ProductForm
from .jalali import latin,date_str,parse_jalali_date,to_jalali,jalali_to_gregorian
from .exports import make_xlsx


def get_role(user):
    if not user.is_authenticated:return None
    if user.is_superuser:return 'admin'
    try:return user.samshaf_profile.role
    except Profile.DoesNotExist:return None


def allow(*roles):
    def wrap(fn):
        @login_required
        @wraps(fn)
        def inner(request,*args,**kwargs):
            if get_role(request.user) not in roles:return HttpResponseForbidden('شما به این بخش دسترسی ندارید.')
            return fn(request,*args,**kwargs)
        return inner
    return wrap


def user_branch(user):
    try:return user.samshaf_profile.branch
    except Profile.DoesNotExist:return None


def visible_requests(user):
    role=get_role(user)
    qs=PurchaseRequest.objects.select_related('branch','category','requested_by').prefetch_related('items__product')
    if role=='branch':
        branch=user_branch(user)
        return qs.filter(branch=branch) if branch else qs.none()
    if role=='workshop':return qs.filter(Q(category__slug='workshop')|Q(category__parent__slug='workshop'))
    return qs if role in ('admin','buyer') else qs.none()


def log_event(request,event,message,level='INFO'):
    SystemLog.objects.create(level=level,event=event,message=message[:2000],actor=request.user,path=request.path[:240])


def alert_users(users,title,text,url):
    Notification.objects.bulk_create([Notification(recipient=u,title=title,message=text,url=url) for u in users if u.is_active])


def alert_roles(roles,title,text,url,branch=None):
    users=User.objects.filter(is_active=True).filter(Q(is_superuser=True)|Q(samshaf_profile__role__in=roles)).distinct()
    if branch is not None:users=users.filter(Q(is_superuser=True)|Q(samshaf_profile__role__in=[r for r in roles if r!='branch'])|Q(samshaf_profile__role='branch',samshaf_profile__branch=branch))
    alert_users(users,title,text,url)


def parse_nonnegative(raw,limit='9999999999.99'):
    if raw is None or str(raw).strip()=='':return None
    try:
        num=Decimal(latin(raw).replace(',','').strip())
        if not num.is_finite() or num<0 or num>Decimal(limit) or num.as_tuple().exponent < -2:raise ValueError()
        return num
    except (InvalidOperation,ValueError,TypeError):raise ValueError('مقدار باید عدد معتبر و مثبت یا صفر، با حداکثر دو رقم اعشار باشد.')


def home(request):
    if not request.user.is_authenticated:return redirect('login')
    return redirect('dashboard')

@allow('admin','buyer','branch','workshop')
def dashboard(request):
    role=get_role(request.user)
    branch=user_branch(request.user) if role=='branch' else None
    qs=visible_requests(request.user)
    if role=='branch' and branch:
        stocks={s.product_id:s.quantity for s in Stock.objects.filter(branch=branch)}
        low=[(p,stocks.get(p.pk,Decimal(0))) for p in Product.objects.select_related('category').filter(active=True,minimum__gt=0) if stocks.get(p.pk,Decimal(0))<p.minimum]
    else:low=[]
    context={'role':role,'branch':branch,'orders':qs[:10],'open_count':qs.exclude(status__in=['delivered','rejected']).count(), 'waiting_count':qs.filter(status='pending').count(),'low_stock':low[:12], 'low_stock_count':len(low), 'branch_count':Branch.objects.filter(active=True,is_workshop=False).count(), 'product_count':Product.objects.filter(active=True).count()}
    return render(request,'core/dashboard.html',context)


def available_roots():return Category.objects.filter(active=True,parent__isnull=True).order_by('position','id')

@allow('branch','admin')
def inventory(request):
    role=get_role(request.user)
    branches=Branch.objects.filter(active=True,is_workshop=False)
    if role=='branch':
        branch=user_branch(request.user)
        if branch is None or not branch.active or branch.is_workshop:return HttpResponseForbidden('شعبه معتبری برای حساب شما تعریف نشده است.')
    else:
        selected=request.GET.get('branch') or request.POST.get('branch')
        branch=branches.filter(pk=selected).first() if selected else branches.first()
        if branch is None:return HttpResponseForbidden('ابتدا یک شعبه فعال تعریف کنید.')
    roots=available_roots()
    category_id=request.POST.get('category') if request.method=='POST' else request.GET.get('category')
    root=roots.filter(pk=category_id).first() if category_id else roots.first()
    if root is None:raise Http404('دسته‌بندی پیدا نشد')
    products=list(Product.objects.filter(active=True,category__active=True).filter(Q(category=root)|Q(category__parent=root)).select_related('category').order_by('category__position','position','pk'))
    existing={stock.product_id:stock for stock in Stock.objects.filter(branch=branch,product__in=products)}
    submission_token=request.POST.get('submission_token') if request.method=='POST' else str(uuid4())
    if request.method=='POST':
        # هر POST کلید یکتا دارد تا دوبار کلیک/ارسال مجدد سفارش تکراری ایجاد نکند.
        if not submission_token or len(submission_token)!=36:submission_token=str(uuid4())
        edits=[];new_items=[]
        try:
            for p in products:
                qty=parse_nonnegative(request.POST.get(f'stock_{p.pk}'))
                buy=parse_nonnegative(request.POST.get(f'buy_{p.pk}'))
                if qty is not None:edits.append((p,qty))
                if buy is not None and buy>0:new_items.append((p,buy))
            if not edits and not new_items:raise ValueError('حداقل یک موجودی یا تعداد درخواستی وارد کنید.')
        except ValueError as ex:
            messages.error(request,str(ex))
        else:
            with transaction.atomic():
                changed=0
                for p,qty in edits:
                    st,created=Stock.objects.select_for_update().get_or_create(branch=branch,product=p,defaults={'quantity':qty,'updated_by':request.user})
                    if created:
                        StockHistory.objects.create(stock=st,old_qty=Decimal(0),new_qty=qty,actor=request.user);changed+=1
                    elif st.quantity!=qty:
                        previous=st.quantity
                        st.quantity=qty;st.updated_by=request.user;st.save(update_fields=['quantity','updated_by','updated_at'])
                        StockHistory.objects.create(stock=st,old_qty=previous,new_qty=qty,actor=request.user);changed+=1
                order=None
                if new_items:
                    order,created=PurchaseRequest.objects.get_or_create(client_token=submission_token,defaults={'branch':branch,'category':root,'requested_by':request.user,'note':request.POST.get('note','').strip()[:1000]})
                    if order.requested_by_id!=request.user.pk or order.branch_id!=branch.pk:
                        return HttpResponseForbidden('این شناسه درخواست متعلق به حساب شما نیست.')
                    if created:
                        RequestItem.objects.bulk_create([RequestItem(request=order,product=p,quantity=qty,unit_snapshot=p.unit,price_snapshot=p.estimated_price) for p,qty in new_items])
                        link=reverse('order_detail',args=[order.pk])
                        alert_roles(['buyer','admin'],'درخواست خرید جدید',f'{branch.name} / {root.name} / شماره {order.pk}',link)
                        if root.slug=='workshop':alert_roles(['workshop'],'درخواست جدید کارگاه',f'درخواست شماره {order.pk} از {branch.name}',link)
                log_event(request,'inventory_request',f'{branch.name}، {changed} تغییر موجودی'+(f'، درخواست #{order.pk}' if order else ''))
            messages.success(request,'اطلاعات ذخیره شد.'+(f' درخواست خرید شماره {order.pk} ثبت شد.' if order else ''))
            return redirect(f'{reverse("inventory")}?branch={branch.pk}&category={root.pk}')
    for p in products:
        p.stock=existing.get(p.pk)
        p.quantity=p.stock.quantity if p.stock else None
        p.is_low=(p.minimum>0 and p.quantity is not None and p.quantity<p.minimum)
    return render(request,'core/inventory.html',{'roots':roots,'root':root,'products':products,'branch':branch,'branches':branches,'role':role,'submission_token':submission_token})

@allow('admin','buyer','branch','workshop')
def orders(request):
    qs=visible_requests(request.user)
    statuses=dict(PurchaseRequest.STATUSES)
    status=request.GET.get('status','')
    if status and status in statuses:qs=qs.filter(status=status)
    branch_id=request.GET.get('branch','')
    if branch_id and get_role(request.user) in ('admin','buyer'):qs=qs.filter(branch_id=branch_id)
    return render(request,'core/orders.html',{'orders':qs[:250],'status':status,'branches':Branch.objects.filter(is_workshop=False),'branch_id':branch_id,'statuses':PurchaseRequest.STATUSES,'role':get_role(request.user)})

@allow('admin','buyer','branch','workshop')
def order_detail(request,pk):
    order=get_object_or_404(visible_requests(request.user),pk=pk)
    items=list(order.items.all())
    return render(request,'core/order_detail.html',{'order':order,'items':items,'role':get_role(request.user),'allowed_approve':get_role(request.user) in ('admin','buyer') and order.status in ('pending','approved'),'can_receive':get_role(request.user) in ('branch','admin') and order.status in ('dispatched','partial'),'status_options':PurchaseRequest.STATUSES})

@allow('admin','buyer')
@require_POST
def approve_order(request,pk):
    with transaction.atomic():
        order=get_object_or_404(PurchaseRequest.objects.select_for_update(),pk=pk)
        if order.status not in ('pending','approved'):return HttpResponseForbidden('در این وضعیت امکان اصلاح مقادیر وجود ندارد.')
        items=list(order.items.select_for_update().all())
        try:
            new_vals=[(i,parse_nonnegative(request.POST.get(f'approved_{i.pk}'))) for i in items]
            if all(q==0 for i,q in new_vals if q is not None) and all(q is not None for i,q in new_vals):raise ValueError('نمی‌توانید همه مقادیر را صفر تأیید کنید؛ برای رد از دکمه رد درخواست استفاده کنید.')
        except ValueError as ex:
            messages.error(request,str(ex));return redirect('order_detail',pk=pk)
        for item,qty in new_vals:
            if qty is not None:item.approved_qty=qty;item.save(update_fields=['approved_qty'])
        order.status='approved';order.save(update_fields=['status','updated_at'])
        alert_roles(['branch'],'درخواست تأیید شد',f'درخواست #{pk} تأیید یا اصلاح شد.',reverse('order_detail',args=[pk]),branch=order.branch)
        log_event(request,'approve_order',f'درخواست {pk} / {order.branch.name}')
    messages.success(request,'مقادیر تأیید و ذخیره شدند.')
    return redirect('order_detail',pk=pk)

ALLOWED_NEXT={'pending':{'approved','rejected'},'approved':{'preparing','ready','dispatched','rejected'},'preparing':{'ready','dispatched'},'ready':{'dispatched'},'dispatched':set(),'partial':set(),'delivered':set(),'rejected':set()}
@allow('admin','buyer','workshop')
@require_POST
def change_status(request,pk):
    with transaction.atomic():
        order=get_object_or_404(PurchaseRequest.objects.select_for_update(),pk=pk)
        if not visible_requests(request.user).filter(pk=pk).exists():raise Http404('درخواست یافت نشد')
        target=request.POST.get('status','')
        if target not in ALLOWED_NEXT.get(order.status,set()):return HttpResponseForbidden('تغییر وضعیت مجاز نیست.')
        if get_role(request.user)=='workshop' and (target not in ('preparing','ready') or order.category.slug!='workshop'):
            return HttpResponseForbidden('کارگاه فقط می‌تواند وضعیت سفارش‌های تولیدی تأییدشده را تغییر دهد.')
        order.status=target;order.save(update_fields=['status','updated_at'])
        alert_roles(['branch'],'تغییر وضعیت درخواست',f'درخواست #{pk}: {order.get_status_display()}',reverse('order_detail',args=[pk]),branch=order.branch)
        log_event(request,'change_status',f'درخواست {pk}: {target}')
    messages.success(request,'وضعیت درخواست تغییر کرد.')
    return redirect('order_detail',pk=pk)

@allow('branch','admin')
@require_POST
def receive_order(request,pk):
    with transaction.atomic():
        order=get_object_or_404(PurchaseRequest.objects.select_for_update(),pk=pk)
        if not visible_requests(request.user).filter(pk=pk).exists():raise Http404('درخواست یافت نشد')
        if order.status not in ('dispatched','partial'):return HttpResponseForbidden('این سفارش هنوز در مرحله تحویل نیست.')
        items=list(order.items.select_for_update().all())
        try:
            amounts=[]
            for i in items:
                qty=parse_nonnegative(request.POST.get(f'receive_{i.pk}'))
                if qty is None:qty=Decimal(0)
                if qty+i.received_qty>i.approved_or_requested_qty:raise ValueError(f'مقدار تحویل {i.product.name} بیش از مقدار تأییدشده است.')
                amounts.append((i,qty))
            if not any(q>0 for _,q in amounts):raise ValueError('مقدار دریافتی جدیدی وارد نشده است.')
        except ValueError as ex:
            messages.error(request,str(ex));return redirect('order_detail',pk=pk)
        for item,qty in amounts:
            if qty:
                item.received_qty+=qty
                item.save(update_fields=['received_qty'])
        # موجودی در نسخه اول *فقط دستی* ثبت می‌شود؛ دریافت کالا موجودی را خودکار زیاد نمی‌کند.
        order.status='delivered' if all(i.received_qty>=i.approved_or_requested_qty for i in items) else 'partial'
        order.save(update_fields=['status','updated_at'])
        alert_roles(['buyer','admin'],'ثبت دریافت کالا',f'درخواست #{pk} / {order.branch.name} / {order.get_status_display()}',reverse('order_detail',args=[pk]))
        log_event(request,'receive_order',f'درخواست {pk} / {order.branch.name} / {order.status}')
    messages.success(request,'دریافت کالا ثبت شد. برای اصلاح موجودی واقعی، شمارش و ثبت دستی انجام دهید.')
    return redirect('order_detail',pk=pk)

@allow('admin','buyer','branch','workshop')
def order_excel(request,pk):
    order=get_object_or_404(visible_requests(request.user),pk=pk)
    items=list(order.items.select_related('product'))
    rows=[]
    for i in items:
        rows.append([i.product.name,float(i.quantity),float(i.approved_or_requested_qty),i.unit_snapshot,int(i.price_snapshot) if i.price_snapshot else 'بدون قیمت',int(i.line_total) if i.price_snapshot else 'بدون قیمت'])
    rows.append(['جمع قیمت‌های ثبت‌شده','','','','',int(order.estimated_total)])
    rows.append(['توجه: همه قیمت‌ها حدودی هستند؛ اقلام بدون قیمت در جمع لحاظ نشده‌اند.'])
    return make_xlsx(f'سمشف | پیش‌فاکتور حدودی درخواست {order.pk} | {order.branch.name} | {date_str(order.created_at)}',['محصول','درخواست','تأییدشده','واحد','قیمت حدودی واحد (تومان)','جمع حدودی (تومان)'],rows,f'samshaf-invoice-{order.pk}.xlsx')

@allow('admin','buyer','branch','workshop')
def notifications(request):
    qs=Notification.objects.filter(recipient=request.user)
    return render(request,'core/notifications.html',{'notifications':qs[:100]})

@allow('admin','buyer','branch','workshop')
@require_POST
def mark_notifications(request):
    Notification.objects.filter(recipient=request.user,is_read=False).update(is_read=True)
    return redirect('notifications')

@allow('admin')
def catalog(request):
    selected=request.GET.get('category','')
    products=Product.objects.select_related('category').order_by('category__position','position','pk')
    if selected:products=products.filter(Q(category_id=selected)|Q(category__parent_id=selected))
    return render(request,'core/catalog.html',{'products':products[:300],'categories':available_roots(),'selected':selected})

@allow('admin')
@require_POST
def quick_price(request,pk):
    p=get_object_or_404(Product,pk=pk)
    try:
        price=parse_nonnegative(request.POST.get('price'),limit='999999999999999')
        minimum=parse_nonnegative(request.POST.get('minimum'))
        if price is None or minimum is None:raise ValueError('قیمت و حداقل موجودی باید وارد شوند (صفر مجاز است).')
        if price!=price.to_integral_value():raise ValueError('قیمت تومان باید عدد صحیح باشد.')
    except ValueError as ex:
        messages.error(request,str(ex));return redirect('catalog')
    p.estimated_price=price;p.minimum=minimum;p.save(update_fields=['estimated_price','minimum'])
    log_event(request,'update_product_price',f'{p.name} / قیمت: {price} / حداقل: {minimum}')
    messages.success(request,'قیمت و حداقل موجودی اصلاح شد. سفارش‌های قبلی با قیمت زمان ثبت باقی می‌مانند.')
    return redirect(f'{reverse("catalog")}?category={p.category.parent_id or p.category_id}')

@allow('admin')
def product_edit(request,pk=None):
    obj=get_object_or_404(Product,pk=pk) if pk else None
    form=ProductForm(request.POST or None,instance=obj)
    if request.method=='POST' and form.is_valid():
        saved=form.save()
        log_event(request,'edit_product',f'محصول #{saved.pk}: {saved.name}')
        messages.success(request,'محصول ذخیره شد.')
        return redirect('catalog')
    return render(request,'core/edit_form.html',{'form':form,'title':'ویرایش کالا' if obj else 'افزودن کالا','back':'catalog'})

@allow('admin')
def category_edit(request,pk=None):
    obj=get_object_or_404(Category,pk=pk) if pk else None
    form=CategoryForm(request.POST or None,instance=obj)
    if request.method=='POST' and form.is_valid():
        saved=form.save()
        log_event(request,'edit_category',f'دسته #{saved.pk}: {saved.name}')
        messages.success(request,'دسته‌بندی ذخیره شد.')
        return redirect('categories')
    return render(request,'core/edit_form.html',{'form':form,'title':'ویرایش دسته‌بندی' if obj else 'افزودن دسته‌بندی','back':'categories'})

@allow('admin')
def categories(request):
    return render(request,'core/categories.html',{'categories':Category.objects.select_related('parent').all()})

@allow('admin')
def branch_manage(request):
    return render(request,'core/branches.html',{'branches':Branch.objects.all()})

@allow('admin')
def branch_edit(request,pk=None):
    obj=get_object_or_404(Branch,pk=pk) if pk else None
    form=BranchForm(request.POST or None,instance=obj)
    if request.method=='POST' and form.is_valid():
        saved=form.save()
        log_event(request,'edit_branch',f'شعبه #{saved.pk}: {saved.name}')
        messages.success(request,'اطلاعات شعبه ذخیره شد.')
        return redirect('branches')
    return render(request,'core/edit_form.html',{'form':form,'title':'ویرایش شعبه' if obj else 'افزودن شعبه','back':'branches'})

@allow('admin')
def user_manage(request):
    profiles={p.user_id:p for p in Profile.objects.select_related('branch')}
    users=list(User.objects.all().order_by('username'))
    for u in users:u.role_profile=profiles.get(u.pk)
    return render(request,'core/users.html',{'users':users})

@allow('admin')
def user_create(request):
    form=UserCreateForm(request.POST or None)
    if request.method=='POST' and form.is_valid():
        u=form.save()
        log_event(request,'create_user',f'کاربر #{u.pk}: {u.username}')
        messages.success(request,'کاربر جدید ساخته شد. اکنون می‌تواند وارد سامانه شود.')
        return redirect('users')
    return render(request,'core/edit_form.html',{'form':form,'title':'ایجاد کاربر جدید','back':'users'})

@allow('admin')
def user_edit(request,pk):
    target=get_object_or_404(User,pk=pk)
    if target.is_superuser and not request.user.is_superuser:
        return HttpResponseForbidden('حساب مدیر فنی فقط توسط سوپریوزر ویرایش می‌شود.')
    form=UserEditForm(request.POST or None,user_obj=target)
    if request.method=='POST' and form.is_valid():
        if target.pk==request.user.pk and not form.cleaned_data['is_active']:
            messages.error(request,'نمی‌توانید حساب فعال خودتان را غیرفعال کنید.')
        else:
            form.save()
            log_event(request,'edit_user',f'کاربر #{target.pk}: {target.username}')
            messages.success(request,'اطلاعات کاربر ذخیره شد.')
            return redirect('users')
    return render(request,'core/edit_form.html',{'form':form,'title':f'ویرایش کاربر {target.username}','back':'users'})

@allow('admin')
@require_POST
def user_toggle(request,pk):
    user=get_object_or_404(User,pk=pk)
    if user.pk==request.user.pk or user.is_superuser:return HttpResponseForbidden('غیرفعال‌کردن این حساب مجاز نیست.')
    user.is_active=not user.is_active;user.save(update_fields=['is_active'])
    log_event(request,'toggle_user',f'{user.username}: {user.is_active}')
    messages.success(request,'وضعیت کاربر تغییر کرد.')
    return redirect('users')


def get_range(request):
    period=request.GET.get('period','jalali_month')
    allowed=('7','30','90','365','jalali_month','jalali_previous_month','jalali_quarter','jalali_year','custom')
    if period not in allowed:period='jalali_month'
    today=timezone.localdate()
    year,month,_=to_jalali(today)
    if period=='custom':
        try:
            start=parse_jalali_date(request.GET['from'])
            end=parse_jalali_date(request.GET['to'])
            if start>end or (end-start).days>730:raise ValueError()
        except (ValueError,KeyError,TypeError):
            raise ValueError('بازه شمسی نامعتبر است یا بیشتر از دو سال انتخاب شده.')
    elif period=='jalali_month':
        start=jalali_to_gregorian(year,month,1);end=today
    elif period=='jalali_previous_month':
        prev_y,prev_m=(year-1,12) if month==1 else (year,month-1)
        start=jalali_to_gregorian(prev_y,prev_m,1)
        end=jalali_to_gregorian(year,month,1)-timedelta(days=1)
    elif period=='jalali_quarter':
        start=jalali_to_gregorian(year,((month-1)//3)*3+1,1);end=today
    elif period=='jalali_year':
        start=jalali_to_gregorian(year,1,1);end=today
    else:
        end=today
        start=today-timedelta(days=int(period)-1)
    return period,start,end

@allow('admin')
def reports(request):
    try:period,start,end=get_range(request)
    except ValueError as ex:
        messages.error(request,str(ex))
        return redirect('reports')
    branch_id=request.GET.get('branch','')
    qs=PurchaseRequest.objects.filter(created_at__date__gte=start,created_at__date__lte=end)
    if branch_id:qs=qs.filter(branch_id=branch_id)
    order_count=qs.count()
    lines=RequestItem.objects.filter(request__in=qs)
    amount_expr=Coalesce(F('approved_qty'),F('quantity'))*F('price_snapshot')
    sums=list(lines.values('request__branch_id','request__branch__name').annotate(total=Coalesce(Sum(amount_expr),Value(Decimal('0')),output_field=DecimalField()),orders=Count('request',distinct=True)).order_by('request__branch__name'))
    products=list(lines.values('product__name','unit_snapshot').annotate(qty=Coalesce(Sum('quantity'),Value(Decimal('0')),output_field=DecimalField()),received=Coalesce(Sum('received_qty'),Value(Decimal('0')),output_field=DecimalField()),cost=Coalesce(Sum(amount_expr),Value(Decimal('0')),output_field=DecimalField())).order_by('-qty')[:200])
    total=sum((s['total'] for s in sums),Decimal('0'))
    unpriced=lines.filter(price_snapshot=0).count()
    # تنها کاهش شمارش‌های دستی ثبت می‌شود؛ این معیار لزوماً معادل مصرف نیست.
    changes=StockHistory.objects.select_related('stock__branch','stock__product').filter(changed_at__date__gte=start,changed_at__date__lte=end)
    if branch_id:changes=changes.filter(stock__branch_id=branch_id)
    decreases={}
    for history in changes.iterator(chunk_size=300):
        diff=history.old_qty-history.new_qty
        if diff>0:
            key=(history.stock.branch.name,history.stock.product.name,history.stock.product.unit)
            decreases[key]=decreases.get(key,Decimal('0'))+diff
    declines=sorted([{'branch':key[0],'name':key[1],'unit':key[2],'qty':qty} for key,qty in decreases.items()],key=lambda x:x['qty'],reverse=True)[:100]
    context={'period':period,'start_shamsi':date_str(start),'end_shamsi':date_str(end),'start_input':date_str(start).translate(str.maketrans('۰۱۲۳۴۵۶۷۸۹','0123456789')),'end_input':date_str(end).translate(str.maketrans('۰۱۲۳۴۵۶۷۸۹','0123456789')),'branches':Branch.objects.filter(is_workshop=False),'branch_id':branch_id,'order_count':order_count,'sums':sums,'products':products,'total_estimate':total,'unpriced':unpriced,'declines':declines}
    return render(request,'core/reports.html',context)

@allow('admin')
def reports_excel(request):
    try:period,start,end=get_range(request)
    except ValueError:return HttpResponseForbidden('بازه گزارش نامعتبر است.')
    qs=RequestItem.objects.select_related('request__branch','product').filter(request__created_at__date__gte=start,request__created_at__date__lte=end)
    branch=request.GET.get('branch')
    if branch:qs=qs.filter(request__branch_id=branch)
    rows=[]
    for i in qs.iterator(chunk_size=300):
        rows.append([str(i.request_id),date_str(i.request.created_at),i.request.branch.name,i.product.name,float(i.quantity),float(i.approved_or_requested_qty),float(i.received_qty),i.unit_snapshot,int(i.price_snapshot) if i.price_snapshot else 'بدون قیمت',int(i.line_total) if i.price_snapshot else 'بدون قیمت',i.request.get_status_display()])
        if len(rows)>=25000:break
    return make_xlsx(f'سمشف | گزارش حدودی سفارش‌ها | {date_str(start)} تا {date_str(end)}',['درخواست','تاریخ شمسی','شعبه','کالا','مقدار درخواست','مقدار تأیید','مقدار تحویل','واحد','قیمت حدودی واحد','مبلغ حدودی','وضعیت'],rows,'samshaf-report.xlsx')
