
from django.shortcuts import render
import json
from django.conf import settings
from django.shortcuts import render
from inventory.models import *
from material.models import *
from expenses.models import *
from store.models import *
from company.models import *
from masters.models import *
from supplier.models import *
from django.db import IntegrityError, transaction
import hashlib
import os
from django.conf import settings
from datetime import datetime
from django.utils import timezone
from django.db.models import Max, F, Q,Sum
import base64
from django.shortcuts import render
from io import BytesIO
from django.conf import settings
from django.template.loader import get_template
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse,FileResponse
from django.shortcuts import get_object_or_404
from django.templatetags.static import static
from xhtml2pdf import pisa
from num2words import num2words
from django.contrib.staticfiles import finders  
from common.utils import *
from django.db import connection
import openpyxl
from openpyxl.styles import Font
from django.http import HttpResponse
from django.core.files.storage import default_storage
from sales.models import *
from scheme.models import *
from reports.party_ledger import *
# *********************************************************************************************************************************


def material_outward(request):
    if 'user_id' in request.session:
        user_type = request.session.get('user_type')
        branch_id = request.session.get('branch_id')
        is_ho = request.session.get('is_ho')
        if user_type == 'stores':
            company = select_row(company_table, {'id': 1})  
            category = selectList(category_table, order_by='name')
            if is_ho == 1:
                supplier = selectList(supplier_table, order_by='name' )
            else:
                supplier = selectList(supplier_table, Q(is_global=1), order_by='name' )
   
            branch = selectList(branch_table, order_by='name').exclude(id=branch_id)
            
            # Logic for pending requests from other branches
            pending_requests = []
            with connection.cursor() as cursor:
                cursor.execute("""
                    SELECT DISTINCT b.name, b.id as branch_id, r.id as po_id, r.po_no
                    FROM tm_material_po r
                    INNER JOIN branches b ON r.branch_id = b.id
                    INNER JOIN tx_material_po ri ON r.id = ri.tm_po_id
                    LEFT JOIN (
                        SELECT material_po_id, 
                                SUM(CASE WHEN status = 1 AND is_active = 1 THEN quantity ELSE 0 END) as outward_qty
                        FROM tx_material_out
                        WHERE status = 1 AND is_active = 1
                        GROUP BY material_po_id
                    ) o ON r.id = o.material_po_id
                    WHERE r.supply_branch_id = %s 
                      AND r.status = 1 AND r.is_active = 1
                      AND ri.status = 1 AND ri.is_active = 1
                      AND r.id NOT IN (
                        SELECT DISTINCT txo.material_po_id 
                        FROM tx_material_out txo
                        INNER JOIN tm_material_out tmo ON txo.tm_material_id = tmo.id
                        WHERE tmo.outward_status = 'rejected'
                          AND txo.material_po_id IS NOT NULL
                      )
                    GROUP BY r.id, b.name, b.id, r.po_no
                    HAVING SUM(ri.quantity) > COALESCE(MAX(o.outward_qty), 0)
                """, [branch_id])
                
                rows = cursor.fetchall()
                pending_requests = [
                    {'branch_name': row[0], 'branch_id': row[1], 'po_id': row[2], 'po_no': row[3]} 
                    for row in rows
                ]


            return render(request, 'material_outward/details.html', {
                'company': company,
                'category': category,
                'supplier': supplier,
                'branch': branch,
                'pending_requests': pending_requests
            })
        else:
            return HttpResponseRedirect("/")
    else:
        return HttpResponseRedirect("/")



def get_po_details_by_pu_ids(pu_ids):
    child_rows = (
        child_material_outward_table.objects
        .filter(tm_material_id__in=pu_ids, material_po_id__isnull=False)
        .values('tm_material_id', 'material_po_id')
        .distinct()
    )

    po_ids = {row['material_po_id'] for row in child_rows}
    if not po_ids:
        return {}

    # 2️⃣ Fetch PO details
    po_map = {
        po.id: {
            'po_no': po.po_no,
            'po_date': po.po_date
        }
        for po in tm_material_request_table.objects.filter(id__in=po_ids)
    }

    # 3️⃣ Build PU → PO details
    pu_po_map = {}

    for row in child_rows:
        pu_id = row['tm_material_id']
        po = po_map.get(row['material_po_id'])
        if not po:
            continue

        pu_po_map.setdefault(pu_id, {
            'po_no': set(),
            'po_date': set()
        })

        pu_po_map[pu_id]['po_no'].add(po['po_no'])

        if po['po_date']:
            pu_po_map[pu_id]['po_date'].add(
                format_date_month_year(po['po_date'])
            )

    # 4️⃣ Convert sets → strings
    return {
        pu_id: {
            'po_no': ', '.join(sorted(v['po_no'])),
            'po_date': ', '.join(sorted(v['po_date']))
        }
        for pu_id, v in pu_po_map.items()
    }
from common.utils import *

def ajax_material_outward_view(request):
    role_id = request.session.get('role_id')
    branch_id = request.session.get('branch_id')
    fyf_name = request.session.get('fyf')
    financial_year = calculate_financial_year(fyf_name)
    
    is_ho = request.session.get('is_ho') or 0
    has_access, error_message = check_user_access(role_id, 'transfer_out', "read")

    if not has_access:
        return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to view Transfer Out details'})
    
    supply_branch_id = request.POST.get('supplier')
    from_date = request.POST.get('from_date')
    to_date = request.POST.get('to_date')
    keyword = request.POST.get('keyword_search', '').strip()

    query = Q(status=1,branch_id=branch_id,current_fy=financial_year)

    if supply_branch_id:
        query &= Q(supply_branch_id=supply_branch_id)

        
    if from_date and to_date:
        query &= Q(transfer_date__range=[from_date, to_date])

    if keyword:
        query &= Q(transfer_no__icontains=keyword)
       
    
    
    data = list(selectList(material_outward_table, query).values())
    pu_ids = [item['id'] for item in data]

    # 🔹 Fetch PO numbers ONCE (from child table)
    po_details_map = get_po_details_by_pu_ids(pu_ids)

    formatted = []
    for index, item in enumerate(data):       
        outward_status = str(item['outward_status']).lower() if item['outward_status'] else 'pending'

        # 🔹 Base buttons (always visible)
        action_buttons = (
            f'<button type="button" onclick="edit_data(\'{item["id"]}\')" '
            f'class="btn btn-outline-success btn-xs p-1" title="Edit">'
            f'<i class="fas fa-edit"></i></button> '

            f'<button type="button" onclick="delete_data(\'{item["id"]}\')" '
            f'class="btn btn-outline-danger btn-xs p-1" title="Delete">'
            f'<i class="fas fa-trash-alt"></i></button>'
        )

        # 🔹 If rejected → add info button (do NOT remove others)
        if outward_status == 'rejected':
            action_buttons += (
                f' <button type="button" '
                f'onclick="viewRejectionStatus('
                f'\'{item["transfer_no"]}\', '
                f'\'{getItemNameById(branch_table, item["supply_branch_id"])}\', '
                f'\'{getItemNameById(employee_table, item["rejected_by"]) if item["rejected_by"] else "System"}\', '
                f'\'{format_date_time_month_year(item["rejected_on"]) if item["rejected_on"] else "-"}\', '
                f'\'{item["rejected_remarks"] or "No remarks"}\''
                f')" '
                f'class="btn btn-outline-info btn-xs p-1" title="Rejected Info">'
                f'<i class="fas fa-info-circle"></i></button>'
            )

        po_details = po_details_map.get(item['id'], {})

       


        formatted.append({
            'id': index + 1,
            'action': action_buttons,
            'transfer_request_no':po_details.get('po_no', '-'), 
            'transfer_request_date':po_details.get('po_date', '-'), 
            'po_no': item['transfer_no'] if item['transfer_no'] else '-', 
            'po_date': format_date_month_year(item['transfer_date']), 
            'to_branch': getItemNameById(branch_table, item['supply_branch_id']) if item['supply_branch_id'] else '-', 
            'quantity': item['total_quantity'] if item['total_quantity'] else '-', 
            'amount': format_amount(item['total_amount']),
            'cash': format_amount(item['cash']),
            'bank': format_amount(item['bank']),
            'balance': format_amount(item['balance']),    
            'remarks': item['remarks'] if item['remarks'] else '-', 
            'status': format_badge(
                outward_status,
                mapping={
                    'pending': 'badge bg-label-warning',
                    'approved': 'badge bg-label-success',
                    'rejected': 'badge bg-label-danger',
                    'partial': 'badge bg-label-info'
                },
                label_mapping={
                    'pending': 'Pending',
                    'approved': 'Approved',
                    'rejected': 'Rejected',
                    'partial': 'Partial'
                }
            )
        })

    return JsonResponse({'data': formatted})

from django.http import JsonResponse
from django.db.models import Sum
from datetime import date


def load_material_po(request):
    requesting_branch_id = request.GET.get('supply_branch_id')   # branch that raised request
    our_branch_id = request.session.get('branch_id')             # our branch (supplier)
    edit_id = int(request.GET.get('edit_id') or 0)

    if not requesting_branch_id or not our_branch_id:
        return JsonResponse([], safe=False)

    branch_id = requesting_branch_id
    supply_branch_id = our_branch_id

    # ----------------------------------
    # PO raised BY requesting branch
    # AGAINST our branch
    # ----------------------------------
    po_filter = Q(
        status=1,
        is_active=1,
        branch_id=branch_id,
        supply_branch_id=supply_branch_id
    )

    pos = tm_material_request_table.objects.filter(po_filter)

    valid_pos = []

    for po in pos:
        po_items = tx_material_request_table.objects.filter(
            tm_po_id=po.id,
            status=1,
            is_active=1,
            branch_id=branch_id,
            supply_branch_id=supply_branch_id
        )

        include_po = False

        for item in po_items:
            outward_filter = Q(
                material_po_id=po.id,
                brand_id=item.brand_id,
                model_id=item.model_id,
                variant_id=item.variant_id,
                color_id=item.color_id,
                status=1,
                is_active=1,
                branch_id=supply_branch_id 
            )

            if edit_id:
                outward_filter &= ~Q(id=edit_id)

            outwarded_qty = (
                child_material_outward_table.objects
                .filter(outward_filter)
                .aggregate(total=Sum('quantity'))['total'] or 0
            )

            if outwarded_qty < (item.quantity or 0):
                include_po = True
                break

        if include_po:
            valid_pos.append({
                "id": str(po.id),
                "po_no": po.po_no
            })

    return JsonResponse(valid_pos, safe=False)

from django.http import JsonResponse
from django.db.models import Sum


# def load_transfer_items(request):
#     """
#     Fetches items from a Material PO and calculates remaining quantity 
#     available for outwarding by subtracting already outwarded quantities.
#     """
#     edit_id = int(request.GET.get('edit_id') or 0)
#     material_po_id = request.GET.get('material_po_id')

#     # Convert comma-separated string to list
#     po_ids = [pid.strip() for pid in material_po_id.split(',') if pid.strip()] if material_po_id else []

#     if not po_ids:
#         return JsonResponse({"items": []})

#     # 1. Fetch all items requested in the PO(s)
#     requested_items = tx_material_request_table.objects.filter(
#         tm_po_id__in=po_ids,
#         status=1
#     )

#     items_data = []
#     all_fully_processed = True

#     for req in requested_items:
#         # 2. Calculate how much has already been outwarded for this specific item in this PO
#         outward_qs = child_material_outward_table.objects.filter(
#             material_po_id=req.tm_po_id,
#             subcategory_id=req.subcategory_id,
#             brand_id=req.brand_id,
#             model_id=req.model_id,
#             variant_id=req.variant_id,
#             color_id=req.color_id,
#             status=1
#         )

#         # If editing an existing transaction, exclude current record to show its qty as available
#         if edit_id:
#             outward_qs = outward_qs.exclude(tm_material_id=edit_id)

#         inwarded_qty = outward_qs.aggregate(total=Sum('quantity'))['total'] or 0
#         remaining_qty = (req.quantity or 0) - inwarded_qty

#         # Skip if there is nothing left to outward
#         if remaining_qty <= 0:
#             continue

#         all_fully_processed = False

#         # 3. Resolve Display Names (Assuming getItemNameById is a helper you use)
#         # Replacing with a direct lookup or safe get for reliability
#         subcategory_name = sub_category_table.objects.filter(id=req.subcategory_id).values_list('name', flat=True).first() or ''
#         brand_name = brand_table.objects.filter(id=req.brand_id).values_list('name', flat=True).first() or ''
#         model_name = model_table.objects.filter(id=req.model_id).values_list('name', flat=True).first() or ''
#         variant_name = variant_table.objects.filter(id=req.variant_id).values_list('name', flat=True).first() or ''
#         color_name = color_table.objects.filter(id=req.color_id).values_list('name', flat=True).first() or ''

#         # 4. Prepare Response Data
#         items_data.append({
#             "po_id": req.tm_po_id,
#             "item_id": req.id,  # Transaction ID from request table
#             "ean": req.ean or '',
#             "hsn_code": req.hsn_code or '',
#             "subcategory_id": req.subcategory_id,
#             "subcategory_name": subcategory_name,
#             "brand_id": req.brand_id,
#             "brand_name": brand_name,
#             "model_id": req.model_id,
#             "model_name": model_name,
#             "variant_id": req.variant_id,
#             "variant_name": variant_name,
#             "color_id": req.color_id,   
#             "color_name": color_name,
#             "quantity": remaining_qty,
#             "ordered_qty": req.quantity or 0,
#             "hsn_code": req.hsn_code or '',
#             # Pricing from the Request Table (preserving original PO prices)
#             "rate": float(req.fsp or 0),    
#             "wsp_price": float(req.wsp or 0),
#             "fsp_price": float(req.fsp or 0),
#             "mop_price": float(req.mop or 0),
#             "bop_price": float(req.bop or 0),
#             "db_price": float(req.db_price or 0),
#             "mrp": float(req.mrp or 0),
#             "price_list_id": 0 # Defaulting to 0 unless specifically linked
#         })

#     # 5. Final check if all POs are completed
#     if po_ids and all_fully_processed and edit_id == 0:
#         return JsonResponse({
#             "status": "error",
#             "message": "All items for the selected PO(s) are already fully outwarded.",
#             "items": []
#         })

#     return JsonResponse({"status": "success", "items": items_data})
from django.http import JsonResponse
from django.db import connection
from django.db import connection
from django.db import connection
from django.db import connection

def get_available_imeis_fifo(
    branch_id,
    subcategory_id=None,
    brand_id=None,
    model_id=None,
    variant_id=None,
    color_id=None,
    exclude_tm_material_id=0
):
    print("\n========== FETCH AVAILABLE IMEIS (FIFO + FULL STOCK LOGIC) ==========")

    filters = []
    params = []

    if subcategory_id:
        filters.append("subcategory_id = %s")
        params.append(subcategory_id)

    if brand_id:
        filters.append("brand_id = %s")
        params.append(brand_id)

    if model_id:
        filters.append("model_id = %s")
        params.append(model_id)

    if variant_id:
        filters.append("variant_id = %s")
        params.append(variant_id)

    if color_id:
        filters.append("color_id = %s")
        params.append(color_id)

    common_filter = " AND " + " AND ".join(filters) if filters else ""

    query = f"""
        SELECT 
            incoming.imei_no,
            incoming.db_price,
            incoming.mrp,
            incoming.fsp,
            incoming.wsp,
            incoming.mop,
            incoming.bop
        FROM (
            -- 1️⃣ OPENING STOCK
            SELECT
                os.imei_no,
                os.date AS fifo_date,
                os.db_price, os.mrp, os.fsp, os.wsp, os.mop, os.bop
            FROM opening_stock os
            WHERE os.branch_id = %s
              AND os.status = 1
              AND os.is_active = 1
              {common_filter}

            UNION ALL

            -- 2️⃣ PURCHASE INWARD
            SELECT
                tx_p.imei_no,
                tm_p.pu_date AS fifo_date,
                tx_p.db_price, tx_p.mrp, tx_p.fsp, tx_p.wsp, tx_p.mop, tx_p.bop
            FROM tx_purchase_inward tx_p
            JOIN tm_purchase_inward tm_p ON tm_p.id = tx_p.tm_pu_id
            WHERE tm_p.branch_id = %s
              AND tm_p.status = 1
              AND tx_p.status = 1
              {common_filter}

            UNION ALL

            -- 3️⃣ SALES RETURN
            SELECT
                tx_sr.imei_no,
                tm_sr.sr_date AS fifo_date,
                tx_sr.amount as db_price, tx_sr.mrp, tx_sr.fsp, tx_sr.wsp, tx_sr.mop, tx_sr.bop
            FROM tx_sales_return tx_sr
            JOIN tm_sales_return tm_sr ON tm_sr.id = tx_sr.tm_return_id
            WHERE tm_sr.branch_id = %s
              AND tm_sr.status = 1
              AND tx_sr.status = 1
              {common_filter}

            UNION ALL

            -- 4️⃣ MATERIAL IN
            SELECT
                tx_mi.imei_no,
                tm_mi.transfer_date AS fifo_date,
                tx_mi.db_price, tx_mi.mrp, tx_mi.fsp, tx_mi.wsp, tx_mi.mop, tx_mi.bop
            FROM tx_material_in tx_mi
            JOIN tm_material_in tm_mi ON tm_mi.id = tx_mi.tm_material_id
            WHERE tm_mi.branch_id = %s
              AND tm_mi.status = 1
              AND tx_mi.status = 1
              {common_filter}
        ) incoming

        -- ❌ MATERIAL OUT
        LEFT JOIN tx_material_out txo
          ON txo.imei_no = incoming.imei_no
         AND txo.status = 1
         AND txo.is_active = 1
         AND (%s = 0 OR txo.tm_material_id != %s)
         AND txo.tm_material_id NOT IN (SELECT id FROM tm_material_out WHERE outward_status = 'rejected')

        -- ❌ SALES
        LEFT JOIN tx_sales txs
          ON txs.imei_no = incoming.imei_no
         AND txs.status = 1
         AND txs.is_active = 1

        -- ❌ PURCHASE RETURN
        LEFT JOIN tx_purchase_return txpr
          ON txpr.imei_no = incoming.imei_no
         AND txpr.status = 1
         AND txpr.is_active = 1

        WHERE txo.id IS NULL
          AND txs.id IS NULL
          AND txpr.id IS NULL
          AND incoming.imei_no IS NOT NULL
          AND incoming.imei_no != ''

        ORDER BY incoming.fifo_date ASC, incoming.imei_no ASC
    """

    final_params = (
        [branch_id] + params +
        [branch_id] + params +
        [branch_id] + params +
        [branch_id] + params +
        [exclude_tm_material_id, exclude_tm_material_id]
    )

    with connection.cursor() as cursor:
        cursor.execute(query, final_params)
        rows = cursor.fetchall()

    return [
        {
            "imei_no": r[0],
            "db_price": r[1],
            "mrp": r[2],
            "fsp": r[3],
            "wsp": r[4],
            "mop": r[5],
            "bop": r[6],
        }
        for r in rows
    ]


from django.db.models import Sum
from django.http import JsonResponse
from django.db.models import Sum
from django.http import JsonResponse

from django.db.models import Sum
from django.http import JsonResponse
from django.db.models import Sum
from django.http import JsonResponse
from django.http import JsonResponse
from django.db.models import Sum

def load_transfer_items(request):
    edit_id = int(request.GET.get('edit_id') or 0)
    material_po_id = request.GET.get('material_po_id')
    branch_id = request.session.get('branch_id')

    po_ids = [p.strip() for p in material_po_id.split(',') if p.strip()] if material_po_id else []

    if not po_ids or not branch_id:
        return JsonResponse({"status": "success", "items": [], "stock_data": []})

    # STEP 1: Load requests
    requests = tx_material_request_table.objects.filter(
        tm_po_id__in=po_ids,
        status=1
    ).order_by('id')

    final_items = []
    stock_data = []

    # IMEI POOL CACHE per item combo
    imei_pool_cache = {}

    # STEP 2: Process each request
    for req in requests:
        item_key = (
            req.subcategory_id,
            req.brand_id,
            req.model_id,
            req.variant_id,
            req.color_id
        )

        # Remaining qty
        outward_qs = child_material_outward_table.objects.filter(
            material_po_id=req.tm_po_id,
            subcategory_id=req.subcategory_id,
            brand_id=req.brand_id,
            model_id=req.model_id,
            variant_id=req.variant_id,
            color_id=req.color_id,
            status=1
        )

        if edit_id:
            outward_qs = outward_qs.exclude(tm_material_id=edit_id)

        outwarded = outward_qs.aggregate(q=Sum('quantity'))['q'] or 0
        remaining = (req.quantity or 0) - outwarded

        if remaining <= 0:
            remaining = 0

        # Prepare stock_data entry
        stock_data.append({
            "ean": req.ean or "",
            "remaining_qty": remaining
        })

        if remaining <= 0:
            continue

        # 3. Create placeholder rows for the remaining quantity
        for _ in range(remaining):
            final_items.append({
                "request_id": req.id,
                "po_id": req.tm_po_id,
                "ean": req.ean,
                "hsn_code": req.hsn_code,
                "subcategory_id": req.subcategory_id,
                "subcategory_name": getItemNameById(sub_category_table, req.subcategory_id),
                "brand_id": req.brand_id,
                "brand_name": getItemNameById(brand_table, req.brand_id),
                "model_id": req.model_id,
                "model_name": getItemNameById(model_table, req.model_id),
                "variant_id": req.variant_id,
                "variant_name": getItemNameById(variant_table, req.variant_id),
                "color_id": req.color_id,
                "color_name": getItemNameById(color_table, req.color_id),
                "imei_no": "",
                "quantity": 1,
                "rate": 0,
                "db_price": 0,
                "mrp": 0,
                "wsp_price": 0,
                "fsp_price": 0,
                "mop_price": 0,
                "bop_price": 0,
                'price_id': 0
            })


    return JsonResponse({
        "status": "success",
        "items": final_items,
        "stock_data": stock_data
    })

from django.http import JsonResponse
from django.http import JsonResponse

def get_available_imeis(request):

    print("\n===== 🔍 get_available_imeis CALLED =====")

    # ✅ READ FROM POST (matches JS)
    branch_id       = request.session.get('branch_id')
    brand_id       = request.POST.get('brand_id')
    model_id       = request.POST.get('model_id')
    variant_id     = request.POST.get('variant_id')
    color_id       = request.POST.get('color_id')
    edit_id        = int(request.POST.get('tm_id') or 0)

    is_demo        = int(request.POST.get('is_demo') or 0)
    ignore_is_demo = int(request.POST.get('ignore_is_demo') or 0)

    print(f"🔍 Filters: branch={branch_id}, brand={brand_id}, model={model_id}, variant={variant_id}, color={color_id}")
    print(f"🔍 is_demo={is_demo}, ignore_is_demo={ignore_is_demo}, edit_id={edit_id}")

    # Use Counter-based approach like imei_stock_summary
    from collections import Counter
    
    inwards_count = Counter()
    outwards_count = Counter()
    
    # Base filter for active items at this branch
    base_filter = Q(is_active=1, status=1, branch_id=branch_id)
    
    # Add product filters
    if brand_id:
        base_filter &= Q(brand_id=brand_id)
    if model_id:
        base_filter &= Q(model_id=model_id)
    if variant_id:
        base_filter &= Q(variant_id=variant_id)
    if color_id:
        base_filter &= Q(color_id=color_id)
    
    # Add demo filter
    if ignore_is_demo == 0:
        base_filter &= Q(is_demo=is_demo)
    
    # ========== INWARDS ==========
    
    # 1. Opening Stock
    opening_stock = opening_stock_table.objects.filter(base_filter)\
        .exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in opening_stock:
        inwards_count[imei] += 1
    print(f"📦 Opening Stock: {dict(inwards_count)}")
    
    # 2. Purchase Inward
    purchase_inward = child_purchase_inward_table.objects.filter(base_filter)\
        .exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in purchase_inward:
        inwards_count[imei] += 1
    print(f"📥 After Purchase Inward: {dict(inwards_count)}")
    
    # 3. Material Inward
    material_inward = child_material_inward_table.objects.filter(base_filter)\
        .exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in material_inward:
        inwards_count[imei] += 1
    print(f"📨 After Material Inward: {dict(inwards_count)}")
    
    # 4. Sales Return (no is_demo field)
    sales_return_filter = Q(is_active=1, status=1, branch_id=branch_id)
    if brand_id:
        sales_return_filter &= Q(brand_id=brand_id)
    if model_id:
        sales_return_filter &= Q(model_id=model_id)
    if variant_id:
        sales_return_filter &= Q(variant_id=variant_id)
    if color_id:
        sales_return_filter &= Q(color_id=color_id)
    
    sales_return = child_sales_return_table.objects.filter(sales_return_filter)\
        .exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in sales_return:
        inwards_count[imei] += 1
    print(f"↩️ After Sales Return: {dict(inwards_count)}")
    
    # ========== OUTWARDS ==========
    
    # 5. Sales
    sales = child_sales_order_table.objects.filter(sales_return_filter)\
        .exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in sales:
        outwards_count[imei] += 1
    print(f"🛒 Sales Out: {dict(outwards_count)}")
    
    # 6. Purchase Return (only approved/pending, exclude reversed)
    purchase_return = child_purchase_return_table.objects.filter(
        sales_return_filter,
        tm_return_id__in=purchase_return_table.objects.exclude(
            pr_status__iexact='reversed'
        ).values_list('id', flat=True)
    ).exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in purchase_return:
        outwards_count[imei] += 1
    print(f"📤 After Purchase Return: {dict(outwards_count)}")
    
    # 7. Material Outward (exclude rejected and current edit)
    material_out_qs = child_material_outward_table.objects.filter(sales_return_filter)\
        .exclude(tm_material_id__in=material_outward_table.objects.filter(
            outward_status='rejected'
        ).values_list('id', flat=True))
    
    if edit_id:
        print(f"✏️ Edit mode → excluding tm_material_id = {edit_id}")
        material_out_qs = material_out_qs.exclude(tm_material_id=edit_id)
    
    material_out = material_out_qs.exclude(imei_no__isnull=True).exclude(imei_no='')\
        .values_list('imei_no', flat=True)
    for imei in material_out:
        outwards_count[imei] += 1
    print(f"📮 After Material Outward: {dict(outwards_count)}")
    
    # ========== NET STOCK CALCULATION ==========
    
    available_imeis = []
    all_imeis = set(inwards_count.keys()) | set(outwards_count.keys())
    
    for imei in all_imeis:
        net_stock = inwards_count[imei] - outwards_count[imei]
        if net_stock > 0:
            available_imeis.append(imei)
    
    available_imeis = sorted(available_imeis)
    
    print(f"\n✅ TOTAL INWARDS: {dict(inwards_count)}")
    print(f"❌ TOTAL OUTWARDS: {dict(outwards_count)}")
    print(f"✅ AVAILABLE IMEIs: {available_imeis}")
    print("===== ✅ END =====\n")

    return JsonResponse(available_imeis, safe=False)


from reports.report_utils import get_imei_stock_summary_data

def get_branch_imei_details(request):
    """
    Returns available IMEIs from session branch with FSP price and SKU text.
    Used for the IMEI reference table on material outward add/edit screens.
    """
    branch_id = request.session.get('branch_id')
    try:
        is_ho = int(request.session.get('is_ho', 0))
    except (ValueError, TypeError):
        is_ho = 0
            
    # For edit mode: exclude IMEIs already in current record
    tm_material_id = int(request.POST.get('tm_material_id') or 0)
    
    is_demo = int(request.POST.get('is_demo') or 0)
    ignore_is_demo = int(request.POST.get('ignore_is_demo') or 0)
    
    if not branch_id:
        return JsonResponse([], safe=False)

    # Use report logic to get robust stock data
    # Don't filter by demo status - show all stock (demo + stock) but EXCLUDE Non Open Stock
    filters = {
        'branch_id': branch_id,
        'stock': 'all_except_non_open', # Custom filter to get Stock + Demo but NO Non Open
    }
    
    # 1. Get Base Available Stock
    available_qty, stock_data = get_imei_stock_summary_data(branch_id, is_ho, filters)
    
    result = []
    
    # Helper to format response
    def add_to_result(imei, fsp, brand, model, variant, color, is_demo=0):
        # fsp might be formatted string "1,200.00" or float/decimal
        try:
            fsp_val = float(str(fsp).replace(',', ''))
        except (ValueError, TypeError):
            fsp_val = 0.0

        sku_parts = [brand, model, variant, color]
        sku_text = ' '.join([str(p) for p in sku_parts if p])
        
        # Add demo indicator to IMEI if it's demo stock (in red color)
        imei_display = str(imei)
        if is_demo == 1:
            imei_display += ' <span style="color: red; font-weight: bold;">(DEMO)</span>'
        
        result.append({
            'imei_no': imei_display,
            'raw_imei': str(imei),
            'fsp_price': fsp_val,
            'sku_text': sku_text,
            'is_demo': is_demo
        })

    existing_imeis = set()

    for item in stock_data:
        add_to_result(
            item['imei'], 
            item['fsp'], 
            item['brand'], 
            item['model'], 
            item['variant'], 
            item['color'],
            item.get('is_demo', 0)
        )
        existing_imeis.add(str(item['imei']).upper())
        
    # 2. Re-include items from the current transaction (Edit Mode)
    if tm_material_id:
        current_items = child_material_outward_table.objects.filter(
            tm_material_id=tm_material_id, 
            status=1
        )
        for item in current_items:
            imei = str(item.imei_no).upper()
            
            # Only add if not already in list
            if imei not in existing_imeis:
                 # Fetch names manually since child table has IDs
                brand_name = getItemNameById(brand_table, item.brand_id)
                model_name = getItemNameById(model_table, item.model_id)
                variant_name = getItemNameById(variant_table, item.variant_id)
                color_name = getItemNameById(color_table, item.color_id)
                
                # Determine is_demo status from stock sources
                item_is_demo = 0
                stock_sources = [
                    child_purchase_inward_table,
                    child_material_inward_table,
                    opening_stock_table
                ]
                for source_model in stock_sources:
                    stock_item = source_model.objects.filter(
                        imei_no=item.imei_no,
                        branch_id=branch_id,
                        status=1,
                        is_active=1
                    ).first()
                    if stock_item:
                        item_is_demo = getattr(stock_item, 'is_demo', 0)
                        break
                
                add_to_result(
                    item.imei_no,
                    item.fsp,
                    brand_name,
                    model_name,
                    variant_name,
                    color_name,
                    item_is_demo
                )
    
    return JsonResponse(result, safe=False)


def transfer_outward_add(request):
    if 'user_id' in request.session:
        user_type = request.session.get('user_type')
        branch_id = request.session.get('branch_id')
        is_ho = request.session.get('is_ho')
        if user_type == 'stores':
            company = select_row(company_table, {'id': 1})
            if is_ho == 1:
                supplier = selectList(supplier_table, order_by='name' )
            else:
                supplier = selectList(supplier_table, Q(is_global=1), order_by='name' )
        
            subcategory = selectList(sub_category_table)
            branch = selectList(branch_table, order_by='name').exclude(id=branch_id)
            brand = selectList(brand_table, order_by='name')
            uom = selectList(uom_table)
            item = selectList(item_table)
            employee = employee_list(request)
            fyf_name = request.session.get('fyf')
            financial_year = calculate_financial_year(fyf_name)
            transfer_no = generate_serial_number(
                model=material_outward_table,
                number_field='transfer_no',
                financial_year_field='current_fy',
                financial_year=financial_year,
                branch_id=branch_id
            )
            return render(request, 'material_outward/add.html', {'company': company,'supplier':supplier,'branch':branch,'subcategory':subcategory,'brand':brand,'uom':uom,
                                    'item':item,'employee':employee,'transfer_no':transfer_no})
        else:
            return HttpResponseRedirect("/")
    else:
        return HttpResponseRedirect("/")
    


def prepare_batches(company_id, branch_id, is_ho,  tm_pu_id, items):
    po_type = 'supplier'
    """
    Batch number logic:
    1. First rate of each item shares one common batch.
    2. Second rate of each item (if any) shares another batch.
    3. Third rate (if any) shares next batch, etc.
    4. Sequence increases globally, skipping already used batch numbers.
    """
    branch = select_row(branch_table, {'id':branch_id})
    batch_map = {}

    # Decide prefix
    if is_ho == 1:
        prefix = "BM-"
        existing_batches = child_purchase_inward_table.objects.filter(
            company_id=company_id,
            status=1,
            batch_no__startswith=prefix
        ).values_list("batch_no", flat=True)

    elif po_type == "supplier":
        store_code = str(branch.branch_code).zfill(3)
        prefix = f"{store_code}/OTH-"
        existing_batches = child_purchase_inward_table.objects.filter(
            company_id=company_id,
            branch_id=branch_id,
            status=1,
            batch_no__startswith=prefix
        ).values_list("batch_no", flat=True)
    else:
        return {}

    # Find max sequence used so far globally
    existing_batches = child_purchase_inward_table.objects.filter(
        company_id=company_id,
        branch_id=branch_id,
        status=1,
        batch_no__startswith=prefix
    ).values_list("batch_no", flat=True)

    max_num = 0
    for b in existing_batches:
        try:
            num = int(str(b).split("-")[1])
            max_num = max(max_num, num)
        except:
            print(f"⚠️ Skipping invalid batch {b}")

    # Group items by item_id → collect all unique rates
    item_rates = {}
    for item in items:
        item_id = item["item_id"]
        rate = float(item["rate"])
        item_rates.setdefault(item_id, []).append(rate)

    # Normalize to unique + stable order
    for item_id in item_rates:
        item_rates[item_id] = sorted(set(item_rates[item_id]))

    # Now align by "rate position"
    rate_position_batches = {}  # pos → batch_no
    for item in items:
        item_id = item["item_id"]
        rate = float(item["rate"])
        key = (item_id, rate)

        # find this rate's position for the item
        pos = item_rates[item_id].index(rate)  # 0 = first, 1 = second, etc.

        if pos not in rate_position_batches:
            max_num += 1
            rate_position_batches[pos] = f"{prefix}{max_num}"

        batch_map[key] = rate_position_batches[pos]

    return batch_map




from django.db.models import Max
from django.db import transaction
import json
from django.http import JsonResponse
from django.utils import timezone
import json
from django.db import transaction, IntegrityError
from django.http import JsonResponse
from django.utils import timezone

def add_material_outward(request):
    if request.method == 'POST':
        try:
            # --- Session Data ---
            company_id = request.session.get('company_id')
            branch_id = request.session.get('branch_id')
            user_id = request.session.get('user_id')
            fyf_name = request.session.get('fyf')
            financial_year = calculate_financial_year(fyf_name)
            role_id = request.session.get('role_id')

            # --- Permission Check ---
            has_access, error_message = check_user_access(role_id, 'transfer_out', "create")
            if not has_access:
                return JsonResponse({
                    'message': 'permission',
                    'error_message': 'You do not have permission to add Transfer Out details'
                })

            # --- Get Main Form Data ---
            po_date = request.POST.get('po_date')
            supply_branch_id = request.POST.get('supply_branch_id') or 0
            remarks = request.POST.get('description')
            total_qty = request.POST.get('total_quantity')
            sub_total = request.POST.get('sub_total')
            round_off = float(request.POST.get('round_off') or 0)
            grand_total = request.POST.get('grand_total')
            transfer_no = request.POST.get('transfer_no') or 0

            
            payment = request.POST.get('payment')
            cash = float(request.POST.get('cash') or 0)
            bank = float(request.POST.get('bank') or 0)
            balance = float(request.POST.get('balance') or 0)
            employee_id = request.POST.get('employee_id') or user_id

            # Parse JSON Items from Frontend
            items = json.loads(request.POST.get('items'))

            # --- Validation ---
            if not supply_branch_id:
                return JsonResponse({'message': 'warning', 'error_message': 'Please select a Supply Branch.'})
            if not items:
                return JsonResponse({'message': 'warning', 'error_message': 'Please add at least one item.'})

            now = timezone.localtime(timezone.now())

            with transaction.atomic():
                # 1. Save Main Entry
                main_obj = material_outward_table.objects.create(
                    company_id=company_id,
                    transfer_date=po_date,
                    transfer_no=transfer_no,
                    num_series=generate_num_series(purchase_inward_table),
                    current_fy=financial_year,
                    branch_id=branch_id,
                    supply_branch_id=supply_branch_id,
                    remarks=remarks,
                    total_quantity=total_qty,
                    sub_total=sub_total,
                    total_amount=grand_total,
                    roundoff=round_off,
                    payment_type=payment,
                    cash=cash,
                    bank=bank,
                    balance=balance,
                    is_active=1,
                    status=1,
                    time=now.strftime('%H:%M:%S'),
                    created_on=now,
                    updated_on=now,
                    created_by=user_id,
                    updated_by=user_id,
                    employee_id=employee_id
                )

                # 2. Save Child Items (One row per IMEI)
                for item in items:
                    rate = float(item['rate'])
                    imei_no = item.get('imei_no', '')
                        
                   

                    child_material_outward_table.objects.create(
                        company_id=company_id,
                        current_fy=financial_year,
                        branch_id=branch_id,
                        supply_branch_id=supply_branch_id,
                        tm_material_id=main_obj.id,
                        material_po_id=int(item.get('po_id') or 0),
                        imei_no=imei_no,
                        subcategory_id=int(item.get('subcategory_id') or 0),
                        brand_id=int(item.get('brand_id') or 0),
                        model_id=int(item.get('model_id') or 0),
                        variant_id=int(item.get('variant_id') or 0),
                        color_id=int(item.get('color_id') or 0),
                        ean=item.get('ean', ''),
                        hsn_code=item.get('hsn_code', ''),
                        rate=rate,
                        quantity=1,           # Quantity is always 1 per row
                        amount=item.get('amount') or 0, # Amount is equal to Rate for Qty 1
                        is_active=1,
                        status=1,
                        created_on=now,
                        updated_on=now,
                        created_by=user_id,
                        updated_by=user_id,
                        mop=float(item.get('mop_price') or 0),
                        bop=float(item.get('bop_price') or 0),
                        fsp=float(item.get('fsp_price') or 0),
                        wsp=float(item.get('wsp_price') or 0),
                        db_price=float(item.get('db_price') or 0),
                        mrp=float(item.get('mrp') or 0),
                        price_list_id=int(item.get('price_list_id') or 0)
                    )

            return JsonResponse({'message': 'success'})

        except IntegrityError as e:
            return JsonResponse({'message': 'exception', 'error': str(e)}, status=200)
        except Exception as e:
            return JsonResponse({'message': 'exception', 'error': f'Unexpected error: {e}'}, status=500)

    return JsonResponse({'message': 'error', 'error_message': 'Invalid Request'}, status=400)


from django.db.models import Q


def check_inward_exists(outward_id):
    """
    Check if any inward exists against the given outward ID.
    Returns True if at least one inward record exists, False otherwise.
    """
    return child_material_inward_table.objects.filter(
        material_outward_id=outward_id,
        status=1
    ).exists()


def material_outward_edit(request):
    if 'user_id' in request.session:
        user_type = request.session.get('user_type')
        branch_id = request.session.get('branch_id')
        is_ho = request.session.get('is_ho')

        if user_type == 'stores':
            encoded_id = request.GET.get('id')
            decoded_id = decode_base64_id(encoded_id)

            if not decoded_id:
                return HttpResponse("ID parameter is missing")

            company = select_row(company_table, {'id': 1})
            purchase_order = select_row(material_outward_table, {'id': decoded_id})

            # Check if inward exists and determine editability
            has_inward = check_inward_exists(decoded_id)
            outward_status = purchase_order.outward_status if purchase_order else 'pending'
            
            # Editable only if: pending AND no inward exists
            is_editable = (outward_status == 'pending' and not has_inward)
            
            # Determine informational message
            info_message = None
            if outward_status == 'approved' or has_inward:
                info_message = "This outward has been approved and inwarded by the destination branch. Editing is not allowed."
            elif outward_status == 'rejected':
                info_message = "This outward has been rejected. Editing is not allowed."

            # base service list for the branch
            

            # rest of your lists
            if is_ho == 1:
                supplier = selectList(supplier_table,  order_by='name')
            else:
                supplier = selectList(supplier_table, Q(is_global=1), order_by='name')

            branch = selectList(branch_table, order_by='name').exclude(id=branch_id)
            item = selectList(item_table)
            employee = employee_list(request)
            subcategory = selectList(sub_category_table)
            brand = selectList(brand_table, order_by='name')
            uom = selectList(uom_table)

            return render(request, 'material_outward/edit.html', {
                'company': company,
                'purchase': purchase_order,
                'id': decoded_id,
                'employee': employee,
                'subcategory': subcategory,
                'brand': brand,
                'uom': uom,
                'supplier': supplier,
                'branch': branch,
                'item': item,
                'outward_status': outward_status,
                'has_inward': has_inward,
                'is_editable': is_editable,
                'info_message': info_message,
            })

        else:
            return HttpResponseRedirect("/")
    else:
        return HttpResponseRedirect("/")




from django.db import connection
from django.http import JsonResponse
from django.http import JsonResponse
from django.db import connection
from django.http import JsonResponse
from django.db import connection
from django.http import JsonResponse
from django.db import connection



def ajax_tx_material_outward_view(request):
    role_id = request.session.get('role_id')
    branch_id = request.session.get('branch_id')
    fyf_name = request.session.get('fyf')
    financial_year = calculate_financial_year(fyf_name)

    has_access, error_message = check_user_access(role_id, 'sales', "read")
    if not has_access:
        return JsonResponse({
            'message': 'permission',
            'error_message': 'You do not have permission to view the sales details'
        })

    tm_material_id = request.POST.get('po_id')

    # ----------------------------  
    # Sales Items
    # ----------------------------
    items_qs = child_material_outward_table.objects.filter(
        tm_material_id=tm_material_id,
        status=1,
        current_fy=financial_year
    ).values()
    material_ids = (
        child_material_outward_table.objects
        .filter(
            tm_material_id=tm_material_id,
            status=1,
            current_fy=financial_year
        )
        .values_list('material_po_id', flat=True)
        .distinct()
    )

    print(list(material_ids))

    print(material_ids)
    items = []
    for index, item in enumerate(items_qs):
        items.append({
            'id': index + 1,
            'ean': item['ean'] or '-',
            'hsn_code': item['hsn_code'] or '-',
            'po_id': item['material_po_id'] or 0,

            'subcategory_id': item['subcategory_id'] or 0,
            'subcategory_name': getItemNameById(sub_category_table, item['subcategory_id']) if item['subcategory_id'] else '-',

            'brand_id': item['brand_id'] or 0,
            'brand_name': getItemNameById(brand_table, item['brand_id']) if item['brand_id'] else '-',

            'model_id': item['model_id'] or 0,
            'model_name': getItemNameById(model_table, item['model_id']) if item['model_id'] else '-',

            'variant_id': item['variant_id'] or 0,
            'variant_name': getItemNameById(variant_table, item['variant_id']) if item['variant_id'] else '-',


            'color_id': item['color_id'] or 0,
            'color_name': getItemNameById(color_table, item['color_id']) if item['color_id'] else '-',

            'imei_no': item['imei_no'] or '-',

            'rate': format_amount(item['rate']) if item['rate'] else '0.00',
            'qty': item['quantity'] or 0,
            'amount': format_amount(item['amount']) if item['amount'] else '0.00',



            'bop_price': format_amount(item['bop']) if item['bop'] else '0.00',
            'mop_price': format_amount(item['mop']) if item['mop'] else '0.00',
            'wsp_price': format_amount(item['wsp']) if item['wsp'] else '0.00',
            'fsp_price': format_amount(item['fsp']) if item['fsp'] else '0.00',
            'db_price': format_amount(item['db_price']) if item['db_price'] else '0.00',
            'mrp': format_amount(item['mrp']) if item['mrp'] else '0.00',
            

             
        })

   

    return JsonResponse({
        'items': items,
        'material_ids': list(material_ids)
    })


def edit_purchase_inward(request):
    if request.headers.get('x-requested-with') == 'XMLHttpRequest' and request.method == "POST":
        data = child_purchase_inward_table.objects.filter(id=request.POST.get('id'))    
    return JsonResponse(data.values()[0])

def prepare_batches_for_edit(company_id, branch_id, is_ho, po_type, tm_id, items):
    """
    Handle batch assignment logic when updating a purchase (edit screen).
    Rules:
    1. If same item_id + same rate already exists in this tm_id → reuse that batch.
    2. If same item_id but new rate → assign new batch.
    3. If completely new item_id → assign new batch.
    4. Ensure new batch numbers are unique and sequential globally.
    """
    branch = select_row(branch_table, {'id': branch_id})
    batch_map = {}

    # Prefix logic
    if is_ho == 1:
        prefix = "BM-"
    elif po_type == "supplier":
        store_code = str(branch.branch_code).zfill(3)
        prefix = f"{store_code}/OTH-"
    else:
        return {}

    # Collect all existing batches globally (for unique numbering)
    existing_batches = child_purchase_inward_table.objects.filter(
        company_id=company_id,
        branch_id=branch_id,
        status=1,
        batch_no__startswith=prefix
    ).values_list("batch_no", flat=True)

    max_num = 0
    for b in existing_batches:
        try:
            num = int(str(b).split("-")[1])
            max_num = max(max_num, num)
        except:
            print(f"⚠️ Skipping invalid batch {b}")

    # Collect existing rows for this tm_id
    existing_rows = child_purchase_inward_table.objects.filter(
        company_id=company_id,
        branch_id=branch_id,
        tm_pu_id=tm_id,
        status=1
    )

    existing_map = {}  # (item_id, rate) → batch_no
    item_batches = {}  # item_id → list of batch_no
    for row in existing_rows:
        existing_map[(row.item_id, float(row.rate))] = row.batch_no
        item_batches.setdefault(row.item_id, []).append(row.batch_no)


    # Now handle new items
    for item in items:
        item_id = item["item_id"]
        rate = float(item["rate"])
        key = (item_id, rate)

        if key in existing_map:
            # Case 1: exact match found
            batch_map[key] = existing_map[key]

        elif item_id in item_batches:
            # Case 2: same item different rate → check if already exists
            matching = [bn for (iid, r), bn in existing_map.items() if iid == item_id and r == rate]
            if matching:
                batch_map[key] = matching[0]
            else:
                max_num += 1
                new_batch = f"{prefix}{max_num}"
                batch_map[key] = new_batch

        else:
            # Case 3: completely new item
            if existing_map:
                # Reuse the first batch_no already linked to this tm_id
                first_existing_batch = next(iter(existing_map.values()))
                batch_map[key] = first_existing_batch
            else:
                # No existing rows at all → must create new batch
                max_num += 1
                new_batch = f"{prefix}{max_num}"
                batch_map[key] = new_batch


    return batch_map

from django.http import JsonResponse
from django.utils import timezone
import json
import json
from django.http import JsonResponse
from django.utils import timezone
from django.db import transaction
import json
from django.http import JsonResponse
from django.utils import timezone
from django.db import transaction
@transaction.atomic
def update_material_outward(request):
    if request.method != 'POST':
        return JsonResponse({'success': False, 'message': 'Invalid request'})

    try:
        # 1. Environment & Request Setup
        user_id = request.session.get('user_id')
        company_id = request.session.get('company_id')
        branch_id = request.session.get('branch_id')
        fyf_name = request.session.get('fyf')
        financial_year = calculate_financial_year(fyf_name)
        
        # Use a consistent variable name for time
        current_time = timezone.localtime(timezone.now())

        data = request.POST
        items_json = data.get('items')
        if not items_json:
            return JsonResponse({'success': False, 'message': 'No items provided'})
            
        items = json.loads(items_json)
        tm_id_parent = int(data.get('tm_material_id') or 0)
        
        # Get parent record
        purchase = select_row(material_outward_table, {'id': tm_id_parent})
        if not purchase:
             return JsonResponse({'success': False, 'message': 'Parent record not found'})

        if purchase.outward_status == 'rejected':
             return JsonResponse({'success': False, 'message': 'This transfer is rejected and cannot be modified.'})

        # 2. Soft delete existing children
        child_material_outward_table.objects.filter(
            tm_material_id=tm_id_parent, 
            status=1
        ).update(status=0, updated_on=current_time, updated_by=user_id)

        # 3. Process and Insert Rows
        for item in items:
            rate = float(item.get('rate') or 0)
            imei_no = item.get('imei_no', '')
            
            
                # Duplicate check within active records
            if child_material_outward_table.objects.filter(
                imei_no=imei_no, status=1
            ).exists():
                return JsonResponse({'success': False, 'message': f"Duplicate IMEI detected: {imei_no}"})

            child_material_outward_table.objects.create(
                company_id=company_id,
                current_fy=financial_year,
                branch_id=branch_id,
                supply_branch_id=purchase.supply_branch_id,
                tm_material_id=purchase.id,
                material_po_id=int(item.get('po_id') or 0),
                imei_no=imei_no,
                subcategory_id=int(item.get('subcategory_id') or 0),
                brand_id=int(item.get('brand_id') or 0),
                model_id=int(item.get('model_id') or 0),
                variant_id=int(item.get('variant_id') or 0),
                color_id=int(item.get('color_id') or 0),
                ean=item.get('ean', ''),
                hsn_code=item.get('hsn_code', ''),
                rate=rate,
                quantity=1,           # Quantity is always 1 per row
                amount=item.get('amount') or 0, # Amount is equal to Rate for Qty 1
                is_active=1,
                status=1,
                created_on=current_time,
                updated_on=current_time,
                created_by=user_id,
                updated_by=user_id,
                mop=float(item.get('mop_price') or 0),
                bop=float(item.get('bop_price') or 0),
                fsp=float(item.get('fsp_price') or 0),
                wsp=float(item.get('wsp_price') or 0),
                db_price=float(item.get('db_price') or 0),
                mrp=float(item.get('mrp') or 0),
                price_list_id=int(item.get('price_list_id') or 0)
            )

        # 4. Update Parent Totals
        purchase.total_quantity = int(data.get('total_quantity') or 0)
        purchase.sub_total = float(data.get('sub_total') or 0)
        purchase.total_amount = float(data.get('grand_total') or 0)
        purchase.cash = float(data.get('cash') or 0)
        purchase.bank = float(data.get('bank') or 0)
        purchase.balance = float(data.get('balance') or 0)
        purchase.updated_on = current_time
        purchase.updated_by = user_id
        purchase.save()

        return JsonResponse({'success': True, 'message': 'success'})

    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)})
    
def delete_tm_pu(request):
    if request.method != 'POST':
        return JsonResponse({'status': 'error', 'error_message': 'Invalid request method'})

    data_id = request.POST.get('id')
    role_id = request.session.get('role_id')
    branch_id = request.session.get('branch_id')
    tm_purchase = select_row(purchase_inward_table, {'id': data_id})

    has_access, error_message = check_user_access(role_id, 'transfer_out', "delete")
    if not has_access:
        return JsonResponse({'status': 'permission_denied', 'error_message': 'You do not have permission to delete Transfer Out details'})

    

    try:
        if tm_purchase.inward_type == 'service':
            service_rows = child_purchase_inward_table.objects.filter(
                tm_pu_id=data_id,
                inward_type='service',
                status=1,
                branch_id=branch_id
            ).values_list('service_id', flat=True)

            
            if tm_purchase.inward_type == 'accessory':       
            # 🔍 1. Check if purchase return exists
                return_exists = child_purchase_return_table.objects.filter(
                    pu_id=data_id,
                    status=1,
                    branch_id=branch_id
                ).exists()

            if return_exists:
                return JsonResponse({
                    'message': 'warning',
                    'error_message': 'There was a purchase return against this inward, so it cannot be deleted.'
                })

            # 🔍 2. Get all batch numbers from this inward
            inward_batches = child_purchase_inward_table.objects.filter(
                tm_pu_id=data_id,
                status=1,
                branch_id=branch_id
            ).values_list("batch_no", flat=True)

            if inward_batches:
                sales_exists = child_sales_order_table.objects.filter(
                    branch_id=branch_id,
                    status=1,
                    batch_no__in=inward_batches
                ).exists()

            if sales_exists:
                return JsonResponse({
                    'message': 'warning',
                    'error_message': 'There was sales against this purchase inward, so it cannot be deleted.'
                })

            # 🔍 2b. Check sales returns
            sales_return_exists = child_sales_return_table.objects.filter(
                branch_id=branch_id,
                status=1,
                batch_no__in=inward_batches
            ).exists()

            if sales_return_exists:
                return JsonResponse({
                    'message': 'warning',
                    'error_message': 'There was a sales and sales return against this inward, so it cannot be deleted.'
                })

        # ✅ Mark purchase inward and child rows as inactive
        purchase_inward_table.objects.filter(id=data_id).update(status=0, is_active=0)
        child_purchase_inward_table.objects.filter(tm_pu_id=data_id).update(status=0, is_active=0)

        return JsonResponse({'message': 'yes'})

    except Exception as e:
        return JsonResponse({'status': 'error', 'error_message': str(e)})




def edit_tm_inward(request):
    if request.headers.get('x-requested-with') == 'XMLHttpRequest' and request.method == "POST":
        data  = purchase_inward_table.objects.filter(id=request.POST.get('id'))    
    return JsonResponse(data.values()[0])



def ajax_tx_data(request):  
    role_id = request.session.get('role_id')
    financial_year = request.session.get('financial_year')
    branch_id = request.session.get('branch_id')
    is_ho = request.session.get('is_ho') or 0  # 1 for HO

    has_access, error_message = check_user_access(role_id, 'transfer_out', "read")
    if not has_access:
        return JsonResponse({'message': 'permission', 'error_message': 'You do not have permission to view Transfer Out details'})

    po_id = request.POST.get('po_id')
    data = list(selectList(child_purchase_inward_table, {'tm_pu_id': po_id}).values())
    formatted = []
    for index, item in enumerate(data):
        item_obj = item_table.objects.filter(id=item['item_id'], status=1).values('ean').first() if item['item_id'] else None
        action = ''
        if str(is_ho) == '1':
            action = f'<button type="button" onclick="barcode_data(\'{item["id"]}\')" class="btn btn-outline-success p-1">Barcode</i></button>'

        formatted.append({
            'id': index + 1,
            'action': action,
            'stock': item['imei_no'], 
            'item': item['ean'] if item_obj else '-',  # ✅ With SKU
            'qty': item['quantity'] if item.get('quantity') else '-', 
        })

    return JsonResponse({'data': formatted})


def purchase_barcode_view(request):
    if request.headers.get('x-requested-with') == 'XMLHttpRequest' and request.method == "POST":
        data = child_purchase_inward_table.objects.filter(id=request.POST.get('id'))    
    return JsonResponse(data.values()[0])



def load_items(request):
    if request.method == 'POST':        
        inward_type = request.POST.get('inward_type')

        if not inward_type:
            return JsonResponse({'items': []})

        # Filter items only by inward_type
        items = item_table.objects.filter(
            item_type=inward_type,
            is_active=1,
            status=1
        ).values('id', 'sku', 'name', 'item_type')

        return JsonResponse({'items': list(items)})

    return JsonResponse({'error': 'Invalid request'}, status=400)


def get_supplier_details(request):
    inward_type = request.GET.get('inward_type')

    if inward_type not in ['accessories', 'service']:
        return JsonResponse({'suppliers': []})

    if inward_type == 'accessories':
        suppliers = supplier_table.objects.filter(is_accessory=1, status=1, is_active=1)
    else:  # service
        suppliers = supplier_table.objects.filter(is_service=1, status=1, is_active=1)

    data = list(suppliers.values('id', 'name'))
    return JsonResponse({'suppliers': data})

from django.db.models import Sum
from django.utils import timezone
from django.http import JsonResponse

def delete_purchase_item(request):
    if request.method != 'POST':
        return JsonResponse({'success': False, 'message': 'Invalid request'})

    try:
        tx_id = request.POST.get('tx_id')
        branch_id = request.session.get('branch_id')
        user_id = request.session.get('user_id')

        item_row = child_purchase_inward_table.objects.filter(
            id=tx_id, status=1
        ).first()

        if not item_row:
            return JsonResponse({'success': False, 'message': 'Item not found or already deleted.'})

        tm_obj = purchase_inward_table.objects.filter(
            id=item_row.tm_pu_id, status=1
        ).first()

        if not tm_obj:
            return JsonResponse({'success': False, 'message': 'Purchase master not found.'})

        # 🛑 SERVICE CHECK
        
        # 🛑 PURCHASE RETURN CHECK
        if child_purchase_return_table.objects.filter(
            pu_id=item_row.tm_pu_id,
            item_id=item_row.item_id,
            status=1,
            branch_id=branch_id
        ).exists():
            return JsonResponse({
                'success': False,
                'message': 'Purchase return exists for this item. Cannot delete.'
            })

        # 🛑 SALES CHECK
        if child_sales_order_table.objects.filter(
            branch_id=branch_id,
            status=1,
            batch_no=item_row.batch_no,
            item_id=item_row.item_id
        ).exists():
            return JsonResponse({
                'success': False,
                'message': 'Sales exist for this item. Cannot delete.'
            })

        # 🛑 SALES RETURN CHECK
        if child_sales_return_table.objects.filter(
            branch_id=branch_id,
            status=1,
            batch_no=item_row.batch_no
        ).exists():
            return JsonResponse({
                'success': False,
                'message': 'Sales return exists for this item. Cannot delete.'
            })

        # ✅ RECALCULATE TM TOTALS (EXCLUDING THIS TX)
        totals = child_purchase_inward_table.objects.filter(
            tm_pu_id=item_row.tm_pu_id,
            status=1
        ).exclude(id=tx_id).aggregate(
            total_qty=Sum('quantity'),
            total_amt=Sum('total_amount'),
            sub_total=Sum('sub_total')
        )

        total_qty = totals['total_qty'] or 0
        total_amt = totals['total_amt'] or 0
        sub_total = totals['sub_total'] or 0

        cash = tm_obj.cash or 0
        bank = tm_obj.bank or 0

        new_balance = round(total_amt - (cash + bank), 2)

        # 🛑 BALANCE SAFETY CHECK
        if round(tm_obj.balance or 0, 2) != new_balance:
            return JsonResponse({
                'success': False,
                'message': 'Balance mismatch. Please adjust Cash/Bank before deleting.'
            })

        # ✅ SOFT DELETE CHILD
        item_row.status = 0
        item_row.is_active = 0
        item_row.updated_on = timezone.now()
        item_row.updated_by = user_id
        item_row.save(update_fields=['status', 'is_active', 'updated_on', 'updated_by'])

        # ✅ UPDATE TM TABLE
        tm_obj.total_quantity = total_qty
        tm_obj.total_amount = total_amt
        tm_obj.sub_total = sub_total
        tm_obj.balance = new_balance
        tm_obj.updated_on = timezone.now()
        tm_obj.updated_by = user_id
        tm_obj.save(update_fields=[
            'total_quantity', 'total_amount', 'sub_total',
            'balance', 'updated_on', 'updated_by'
        ])

        return JsonResponse({'success': True, 'message': 'Item deleted and purchase values updated successfully.'})

    except Exception as e:
        return JsonResponse({'success': False, 'message': str(e)})




from django.http import JsonResponse
from django.http import JsonResponse

def check_imei_exists(request):
    imeis = request.POST.getlist('imeis[]') or request.POST.getlist('imeis')
    edit_id_str = request.POST.get('edit_id') # This is now "101,102,103"

    imeis = [i.strip().upper() for i in imeis if i.strip()]
    if not imeis:
        return JsonResponse({"exists": False, "duplicate_imeis": []})

    qs = child_purchase_inward_table.objects.filter(imei_no__in=imeis, status=1)

    # 🔥 Exclude all IDs belonging to the group being edited
    if edit_id_str and edit_id_str != '0':
        edit_ids = edit_id_str.split(',')
        qs = qs.exclude(id__in=edit_ids)

    duplicate_imeis = list(qs.values_list('imei_no', flat=True))

    return JsonResponse({
        "exists": bool(duplicate_imeis),
        "duplicate_imeis": duplicate_imeis
    })


from datetime import date
def load_material_details(request):

    branch_id = request.session.get('branch_id')

    brand_id   = request.GET.get('brand_id')
    model_id   = request.GET.get('model_id')
    variant_id = request.GET.get('variant_id')
    color_id   = request.GET.get('color_id')
    imei       = request.GET.get('imei')
    # 🆕 Receive is_demo from GET
    is_demo_req = int(request.GET.get('is_demo') or 0)
    ignore_is_demo = int(request.GET.get('ignore_is_demo') or 0)

    if not all([brand_id, model_id, variant_id, color_id, imei]):
        return JsonResponse({'status': False, 'message': 'Missing parameters'})

    # -------------------------------------------------
    # 1. FIND ITEM MASTER
    # -------------------------------------------------
    item = item_table.objects.filter(
        brand_id=brand_id,
        model_id=model_id,
        variant_id=variant_id,
        color_id=color_id,
        status=1,
        is_active=1
    ).first()

    if not item:
        return JsonResponse({'status': False, 'message': 'Item not found'})

    # -------------------------------------------------
    # 2. LOCATE IMEI IN STOCK (WITH IS_DEMO FILTER ❗)
    # -------------------------------------------------
    
    # Base filter dictionary
    filter_kwargs = {
        'branch_id': branch_id,
        'imei_no': imei,
        'status': 1,
        'is_active': 1
    }
    
    # Only sort by is_demo if NOT ignored
    if ignore_is_demo == 0:
        filter_kwargs['is_demo'] = is_demo_req

    stock_row = (
        child_purchase_inward_table.objects.filter(**filter_kwargs).first()
        or
        child_material_inward_table.objects.filter(**filter_kwargs).first()
        or
        opening_stock_table.objects.filter(**filter_kwargs).first()
    )

    if not stock_row:
        msg = 'IMEI not found in demo stock.' if is_demo_req == 1 else 'IMEI not available in live stock.'
        return JsonResponse({
            'status': False,
            'message': msg
        })

    # 🚨 ROBUST AVAILABILITY CHECK (Exclude Sold/Returned/Transferred)
    # -------------------------------------------------
    # a. Check Sales
    if child_sales_order_table.objects.filter(imei_no=imei, status=1,branch_id=branch_id).exists():
        # But check if it was returned
        if not child_sales_return_table.objects.filter(imei_no=imei, status=1,branch_id=branch_id).exists():
            return JsonResponse({'status': False, 'message': 'Item is already sold'})

    # b. Check Purchase Return
    if child_purchase_return_table.objects.filter(
        imei_no=imei, 
        status=1,
        branch_id=branch_id,
        tm_return_id__in=purchase_return_table.objects.exclude(pr_status__iexact='reversed').values_list('id', flat=True)
    ).exists():
        return JsonResponse({'status': False, 'message': 'Item is already purchase returned or pending return'})

    # c. Check Material Transfer Status (NET STOCK CALCULATION)
    # An IMEI may have been transferred out and back multiple times.
    # We need to calculate net stock to determine current location.
    
    # Count ALL inwards for this IMEI at this branch
    # 1. Purchase inwards
    purchase_inwards = child_purchase_inward_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1
    ).count()
    
    # 2. Material inwards (transfers received)
    material_inwards = child_material_inward_table.objects.filter(
        imei_no=imei, 
        branch_id=branch_id,
        status=1,
        is_active=1
    ).count()
    
    # 3. Opening stock
    opening_stock = opening_stock_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1
    ).count()
    
    # 4. Sales returns (IMEI came back to branch)
    sales_returns = child_sales_return_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1
    ).count()
    
    # Count ALL outwards for this IMEI at this branch
    # 1. Sales (sold to customers)
    sales_out = child_sales_order_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1
    ).count()
    
    # 2. Purchase returns (returned to supplier) - Exclude reversed ones
    purchase_returns_out = child_purchase_return_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1,
        tm_return_id__in=purchase_return_table.objects.exclude(
            pr_status__iexact='reversed'
        ).values_list('id', flat=True)
    ).count()
    
    # 3. Material outwards (excluding rejected transfers)
    material_outwards = child_material_outward_table.objects.filter(
        imei_no=imei,
        branch_id=branch_id,
        status=1,
        is_active=1
    ).exclude(
        tm_material_id__in=material_outward_table.objects.filter(
            outward_status='rejected'
        ).values_list('id', flat=True)
    ).count()
    
    # Calculate net stock: total_inwards - total_outwards
    total_inwards = purchase_inwards + material_inwards + opening_stock + sales_returns
    total_outwards = sales_out + purchase_returns_out + material_outwards
    net_stock = total_inwards - total_outwards
    
    # If net stock <= 0, IMEI is not currently at this branch
    if net_stock <= 0:
        return JsonResponse({
            'status': False, 
            'message': 'Item is not available at this branch (transferred out)'
        })


    # -------------------------------------------------
    # 3. BASE PRICE FROM STOCK
    # -------------------------------------------------
    base_prices = {
        'fsp': getattr(stock_row, 'fsp', 0),
        'wsp': getattr(stock_row, 'wsp', 0),
        'mop': getattr(stock_row, 'mop', 0),
        'bop': getattr(stock_row, 'bop', 0),
        'db':  getattr(stock_row, 'base_rate', getattr(stock_row, 'db_price', 0)),
        'mrp': getattr(stock_row, 'mrp', 0),
    }

    # -------------------------------------------------
    # 4. APPLY IMEI PRICE LOGIC
    # -------------------------------------------------
    fsp, wsp, mop, bop = get_final_price_for_imei(
        branch_id=branch_id,
        subcategory_id=getattr(stock_row, 'subcategory_id', item.sub_category_id),
        brand_id=item.brand_id,
        model_id=item.model_id,
        variant_id=item.variant_id,
        imei_no=imei,
        fsp=base_prices['fsp'],
        wsp=base_prices['wsp'],
        mop=base_prices['mop'],
        bop=base_prices['bop']
    )
  

    # -------------------------------------------------
    # 5. RESPONSE
    # -------------------------------------------------
    return JsonResponse({
        'status': True,
        'item_id': item.id,
        'ean': item.ean,
        'hsn_code': item.hsn_code or '',
        'fsp_price': fsp,
        'wsp_price': wsp,
        'mop_price': mop,
        'bop_price': bop,
        'db_price': base_prices['db'],
        'mrp_price': base_prices['mrp'],
        'price_id': 0,
        'price_source': stock_row.__class__.__name__,
    })


from django.http import JsonResponse
from django.db.models import Q

def get_ean_details(request):
    ean = request.POST.get('ean')

    if not ean:
        return JsonResponse({
            'status': False,
            'message': 'EAN missing'
        })

    item = item_table.objects.filter(
        ean__iexact=ean,
        status=1,
        is_active=1
    ).values(
        'id',
        'brand_id',
        'model_id',
        'variant_id',
        'color_id'
    ).first()

    if not item:
        return JsonResponse({
            'status': False,
            'message': 'Item not found'
        })

    return JsonResponse({
        'status': True,
        'item_id': item['id'],
        'brand_id': item['brand_id'],
        'model_id': item['model_id'],
        'variant_id': item['variant_id'],
        'color_id': item['color_id'],
    })



def check_outward_imei_exists(request):
    """
    Check if IMEIs are available for material outward.
    Uses net stock calculation to handle IMEIs that have been transferred out and back.
    """
    branch_id = request.session.get("branch_id")
    imeis = request.POST.getlist('imeis[]') or request.POST.getlist('imeis')
    edit_id_str = request.POST.get('tm_id')  # Current outward being edited

    imeis = [i.strip().upper() for i in imeis if i.strip()]
    if not imeis:
        return JsonResponse({"exists": False, "duplicate_imeis": []})

    # Check each IMEI's net stock to determine if it's available
    unavailable_imeis = []
    
    for imei in imeis:
        # Count ALL inwards for this IMEI at this branch
        purchase_inwards = child_purchase_inward_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1
        ).count()
        
        material_inwards = child_material_inward_table.objects.filter(
            imei_no=imei, 
            branch_id=branch_id,
            status=1,
            is_active=1
        ).count()
        
        opening_stock = opening_stock_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1
        ).count()
        
        sales_returns = child_sales_return_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1
        ).count()
        
        # Count ALL outwards for this IMEI at this branch
        sales_out = child_sales_order_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1
        ).count()
        
        # 2. Purchase returns (returned to supplier) - Exclude reversed ones
        purchase_returns_out = child_purchase_return_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1,
            tm_return_id__in=purchase_return_table.objects.exclude(
                pr_status__iexact='reversed'
            ).values_list('id', flat=True)
        ).count()
        
        # Material outwards (excluding rejected and current edit)
        material_out_qs = child_material_outward_table.objects.filter(
            imei_no=imei,
            branch_id=branch_id,
            status=1,
            is_active=1
        ).exclude(
            tm_material_id__in=material_outward_table.objects.filter(
                outward_status='rejected'
            ).values_list('id', flat=True)
        )
        
        # Exclude current outward being edited
        if edit_id_str and edit_id_str != '0':
            material_out_qs = material_out_qs.exclude(tm_material_id=edit_id_str)
        
        material_outwards = material_out_qs.count()
        
        # Calculate net stock
        total_inwards = purchase_inwards + material_inwards + opening_stock + sales_returns
        total_outwards = sales_out + purchase_returns_out + material_outwards
        net_stock = total_inwards - total_outwards
        
        # If net stock <= 0, IMEI is not available
        if net_stock <= 0:
            unavailable_imeis.append(imei)

    return JsonResponse({
        "exists": bool(unavailable_imeis),
        "duplicate_imeis": unavailable_imeis
    })

from financial_year.models import *
from decimal import Decimal
from reports.supply_branch_ledger import *

def get_branch_outstanding(request):
    supply_branch_id = request.POST.get("supply_branch_id")
    branch_id = request.session.get("branch_id")

    fyf = request.session.get("fyf")
    financial_year = calculate_financial_year(fyf)

    running_outstanding = Decimal("0.00")

    if supply_branch_id and int(supply_branch_id) != 0:
        # Get ledger rows including opening + material in/out
        res_branch = outstanding_supply_branch(branch_id, int(supply_branch_id), financial_year)

        # Sum opening balance
        running_outstanding += Decimal(res_branch["opening_outstanding"] or 0)

        # Sum Material In (debit → increases outstanding)
        for row in res_branch["rows"]:
            running_outstanding += Decimal(row["debit"] or 0)
            running_outstanding -= Decimal(row["credit"] or 0)

    # Determine DR/CR
    total_type = "DR" if running_outstanding >= 0 else "CR"
    outstanding = {running_outstanding}
    total_outstanding = f"{abs(running_outstanding):,.2f} ({total_type})"

    return JsonResponse({"total_outstanding": total_outstanding})


def delete_material_outward(request):
    """
    Delete Material Outward with strict validation.
    
    Deletion is BLOCKED if:
    - Outward status is 'approved'
    - Outward status is 'rejected'
    - Any inward exists against the outward
    
    Deletion is ALLOWED only if:
    - Outward status is 'pending' AND no inward exists
    """
    if request.method != 'POST':
        return JsonResponse({'status': 'error', 'error_message': 'Invalid request method'})

    data_id = request.POST.get('id')
    role_id = request.session.get('role_id')
    branch_id = request.session.get('branch_id')

    # Permission check
    has_access, error_message = check_user_access(role_id, 'transfer_out', "delete")
    if not has_access:
        return JsonResponse({
            'message': 'permission',
            'error_message': 'You do not have permission to delete transfer outward details'
        })

    try:
        # Get outward record
        outward = select_row(material_outward_table, {'id': data_id})
        
        if not outward:
            return JsonResponse({
                'message': 'error',
                'error_message': 'Outward record not found'
            })

        outward_status = outward.outward_status if outward.outward_status else 'pending'

        # ❌ BLOCK: Approved outward
        if outward_status == 'approved':
            return JsonResponse({
                'message': 'infos',
                'error_message': 'This outward has already been approved. Deletion is not allowed.'
            })

        # ❌ BLOCK: Rejected outward
        if outward_status == 'rejected':
            return JsonResponse({
                'message': 'infos',
                'error_message': 'Rejected outward records cannot be deleted.'
            })

        # ❌ BLOCK: Inward exists
        has_inward = check_inward_exists(data_id)
        if has_inward:
            return JsonResponse({
                'message': 'infos',
                'error_message': 'Inward has been created against this outward. Deletion is blocked.'
            })

        # ✅ ALLOW: Pending + No inward
        material_outward_table.objects.filter(id=data_id).update(status=0, is_active=0)
        child_material_outward_table.objects.filter(tm_material_id=data_id).update(status=0, is_active=0)

        return JsonResponse({'message': 'yes'})

    except Exception as e:
        return JsonResponse({'status': 'error', 'error_message': str(e)})
