from django.shortcuts import render
import pytz

from finance_and_accounting.posting_hooks import post_goods_delivery, post_goods_receipt
from sales_and_marketing.models import ProductBrand, ProductComponent, ProductDiscount, ProductFeaturesAttributes, ProductFeaturesCatalogue, ProductImageCatalogue, ProductPricing, ProductVAT
from sales_and_marketing.serializers import ProductImageCatalogueSerializer
from system_administration.utils import create_notifications, get_empty_products_for_new_categories, get_staff_profile_data, set_current_opening_values, set_initial_opening_values
from access_control.permissions import user_has_permission
from .requisition_helpers import can_approve_purchase_requisition, can_create_purchase_requisition
from .models import *
from .serializers import *
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication
from rest_framework.decorators import api_view, authentication_classes, permission_classes
from rest_framework.response import Response
from django.contrib.auth.models import User
from datetime import datetime, timedelta
import json
import logging
from django.http import JsonResponse
from django.db.models import Count
from django.db.utils import OperationalError, ProgrammingError

logger = logging.getLogger(__name__)

# Create views section


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def create_warehouse(request):
    company_serial_number = request.data["serial_number"]
    company_branch_id = request.data["company_branch_id"]
    warehouse_name = request.data["warehouse_name"]
    warehouse_location = request.data["warehouse_location"]
    warehouse_capacity = request.data["warehouse_capacity"]
    warehouse_contact_phone = request.data["warehouse_contact_phone"]
    warehouse_description = request.data["warehouse_description"]
    active_user = request.user
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.warehouse.add"):
            warehouse_serializer = WarehouseSerializer(
                data={'company_branch': int(company_branch_id), 'warehouse_name': warehouse_name, 'warehouse_location': warehouse_location, 'warehouse_capacity': warehouse_capacity, 'warehouse_contact_phone': warehouse_contact_phone, 'warehouse_description': warehouse_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if warehouse_serializer.is_valid():
                new_warehouse = warehouse_serializer.save()
                all_products = Product.objects.all()
                for product in all_products:
                    if product.recycle_bin != True:
                        new_inventory_serializer = InventorySerializer(
                            data={'product': product.id, 'warehouse': new_warehouse.id, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
                        if new_inventory_serializer.is_valid():
                            new_inventory_serializer.save()
                return Response({"message": "Warehouse created successfully", }, status=200)
            else:
                # print(warehouse_serializer.errors)
                return Response({"message": "Unable to create warehouse", }, status=406)
        else:
            return Response({"message": "You are not authorised to perform this action", }, status=401)
    except:
        return Response({"message": "Error creating warehouse", }, status=200)


# @api_view(['POST'])
# @authentication_classes([TokenAuthentication])
# @permission_classes([IsAuthenticated])
# def create_category(request):
#     company_serial_number = request.data["serial_number"]
#     category_name = request.data["category_name"]
#     category_description = request.data["category_description"]
#     active_user = request.user
#     try:
#         company_profile = CompanyProfile.objects.get(
#             company_serial_number=company_serial_number)
#         staff_profile = StaffProfile.objects.get(user=active_user)
#         if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
#             category_serializer = CategorySerializer(
#                 data={'category_name': category_name, 'category_description': category_description, 'created_by': staff_profile.id,'last_updated_by':staff_profile.id})
#             if category_serializer.is_valid():
#                 category_serializer.save()
#                 return Response({"message": "Product category created successfully", }, status=200)
#             else:
#                 return Response({"message": "Unable to create product category", }, status=406)
#         else:
#             return Response({"message": "You are unauthorised to perform this action", }, status=401)
#     except:
#         return Response({"message": "Error creating product category", }, status=500)


# @api_view(['POST'])
# @authentication_classes([TokenAuthentication])
# @permission_classes([IsAuthenticated])
# def create_product(request):
#     company_serial_number = request.data["serial_number"]
#     category_id = request.data["category_id"]
#     product_name = request.data["product_name"]
#     unit_of_measurement = request.data["unit_of_measurement"]
#     product_description = request.data["product_description"]
#     warehouse_id = request.data["warehouse_id"]
#     active_user = request.user
#     try:
#         company_profile = CompanyProfile.objects.get(
#             company_serial_number=company_serial_number)
#         staff_profile = StaffProfile.objects.get(user=active_user)
#         if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
#             product_serializer = ProductSerializer(
#                 data={'category': int(category_id), 'product_name': product_name, 'unit_of_measurement': unit_of_measurement, 'product_description': product_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
#             if product_serializer.is_valid():
#                 new_product = product_serializer.save()
#                 #create inventory for the product
#                 #get warehouse
#                 warehouse = Warehouse.objects.get(id=int(warehouse_id))
#                 inventory_serializer = InventorySerializer(
#                     data={'product': new_product.id, 'warehouse': warehouse.id, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
#                 if inventory_serializer.is_valid():
#                     inventory_serializer.save()
#                     return Response({"message": "Product created successfully", }, status=200)
#                 else:
#                     return Response({"message": "Error creating product inventory", }, status=406)
#             else:
#                 return Response({"message": "Unable to create product", }, status=406)
#         else:
#             return Response({"message": "You are unauthorised to perform this action", }, status=401)
#     except:
#         return Response({"message": "Error creating product", }, status=500)

# #get all views section

@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def overall_warehouse_management_dashboard(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data.get("serial_number", "")
    active_user = request.user
    payload = {}
    warehouse_list = []
    warehouse_map = {}
    company_profile_map = {}
    company_branches_list = []
    company_branch_map = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    try:
        if not company_serial_number:
            return Response({"message": "serial_number is required", "payload": payload}, status=400)
        try:
            company_profile = CompanyProfile.objects.get(
                company_serial_number=company_serial_number)
        except CompanyProfile.DoesNotExist:
            company_profile = CompanyProfile.objects.order_by("id").first()
            if company_profile is None:
                return Response({"message": "No company found for this serial number", "payload": payload}, status=404)
        all_branches = company_profile.company_branches.all()
        #
        staff_profile = StaffProfile.objects.get(user=active_user)
        # setting up warehouse inventory financial year opening values
        for branch in all_branches:
            set_initial_opening_values("2024", branch)
            current_year = str(datetime.now().year)
            set_current_opening_values(current_year, branch)
        if user_has_permission(staff_profile, "warehouse.dashboard.view"):
            active_staff_profile_data = get_staff_profile_data(active_user)
            company_profile_map["company_id"] = str(company_profile.id)
            company_profile_map["company_name"] = company_profile.company_name
            company_profile_map["company_phone"] = company_profile.company_phone
            # get all product categories
            all_categories = Category.objects.all().count()
            all_products = Product.objects.all().count()
            for branch in all_branches:
                company_branch_map = {}
                company_branch_map["branch_id"] = str(branch.id)
                company_branch_map["branch_name"] = branch.branch_name
                company_branch_map["is_main_branch"] = "true" if branch.main_branch == True else "false"
                all_branch_warehouses = branch.branch_warehouses.all()
                warehouse_list = []
                for warehouse in all_branch_warehouses:
                    if warehouse.recycle_bin != True:
                        warehouse_map = {}
                        warehouse_map["warehouse_id"] = str(warehouse.id)
                        warehouse_map["warehouse_company_branch_id"] = str(
                            warehouse.company_branch.id)
                        warehouse_map["warehouse_company_branch_name"] = warehouse.company_branch.branch_name
                        warehouse_map["warehouse_name"] = warehouse.warehouse_name
                        warehouse_map["warehouse_location"] = warehouse.warehouse_location
                        warehouse_map["warehouse_capacity"] = warehouse.warehouse_capacity
                        warehouse_map["warehouse_contact_phone"] = warehouse.warehouse_contact_phone
                        warehouse_map["warehouse_description"] = warehouse.warehouse_description
                        warehouse_map["all_categories"] = str(all_categories)
                        warehouse_map["all_products"] = str(all_products)
                        #
                        creator_map = {}
                        creator_map["staff_id"] = str(
                            warehouse.created_by.id) if warehouse.created_by is not None else ""
                        creator_map["staff_name"] = f'{warehouse.created_by.first_name} {warehouse.created_by.last_name}' if warehouse.created_by is not None else ""
                        creator_map["staff_position"] = warehouse.created_by.staff_position.position_title if warehouse.created_by is not None and warehouse.created_by.staff_position is not None else ""
                        warehouse_map["created_by"] = creator_map
                        creator_map = {}
                        creator_map["staff_id"] = str(
                            warehouse.last_updated_by.id) if warehouse.last_updated_by is not None else ""
                        creator_map["staff_name"] = f'{warehouse.last_updated_by.first_name} {warehouse.last_updated_by.last_name}' if warehouse.last_updated_by is not None else ""
                        creator_map["staff_position"] = warehouse.last_updated_by.staff_position.position_title if warehouse.last_updated_by is not None and warehouse.last_updated_by.staff_position is not None else ""
                        warehouse_map["last_updated_by"] = creator_map
                        warehouse_map["created_on"] = datetime.strftime(
                            warehouse.created_on.astimezone(target_timezone), date_format) if warehouse.created_on is not None else ""
                        warehouse_map["last_updated_on"] = datetime.strftime(
                            warehouse.last_updated_on.astimezone(target_timezone), date_format) if warehouse.last_updated_on is not None else ""
                        warehouse_equipment = warehouse.warehouse_equipment.all().count()
                        outgoing_warehouse_stock_transactions = warehouse.source_warehouse_stock_transactions.all().count()
                        incoming_warehouse_stock_transactions = warehouse.recipient_warehouse_stock_transactions.all().count()
                        warehouse_purchase_requisitions = warehouse.warehouse_purchase_requisitions.all().count()
                        warehouse_stock_requisitions = warehouse.warehouse_stock_requisitions.count()
                        warehouse_map["warehouse_equipment"] = str(
                            warehouse_equipment)
                        warehouse_map["stock_transactions"] = str(
                            outgoing_warehouse_stock_transactions+incoming_warehouse_stock_transactions)
                        warehouse_map["warehouse_purchase_requisitions"] = str(
                            warehouse_purchase_requisitions)
                        warehouse_map["warehouse_stock_requisitions"] = str(
                            warehouse_stock_requisitions)
                        warehouse_list.append(warehouse_map)
                # and staff_profile.company_branch.main_branch == True:
                if staff_profile.is_head_of_department == True:
                    company_branch_map["branch_warehouses"] = warehouse_list
                    company_branches_list.append(company_branch_map)
                else:
                    if staff_profile.company_branch is not None and staff_profile.company_branch.branch_name == branch.branch_name:
                        company_branch_map["branch_warehouses"] = warehouse_list
                        company_branches_list.append(company_branch_map)
            company_profile_map["company_branches_list"] = company_branches_list
            payload["active_staff_profile_data"] = active_staff_profile_data
            payload["company_profile"] = company_profile_map
            return Response({"message": "true", "payload": payload}, status=200)
        else:
            return Response({"message": "false", "payload": payload}, status=401)
    except StaffProfile.DoesNotExist:
        return Response({"message": "Staff profile not found for active user", "payload": payload}, status=404)
    except Exception as e:
        logger.exception("overall_warehouse_management_dashboard failed")
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_equipment(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    active_user = request.user
    payload = {}
    warehouse_equipment_map = {}
    warehouse_equipment_list = []
    target_timezone = pytz.timezone('Africa/Nairobi')
    try:
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        warehouse_equipment = warehouse_instance.warehouse_equipment.filter(
            recycle_bin=False,
        ).select_related(
            'created_by__staff_position',
            'last_updated_by__staff_position',
        ).order_by("-id")
        for equipment in warehouse_equipment:
            warehouse_equipment_map = {}
            warehouse_equipment_map["equipment_id"] = str(equipment.id)
            warehouse_equipment_map["equipment_name"] = equipment.equipment_name
            warehouse_equipment_map["equipment_serial_number"] = equipment.equipment_serial_number
            warehouse_equipment_map["equipment_description"] = equipment.equipment_description
            warehouse_equipment_map["status"] = equipment.status
            creator_map = {}
            creator_map["staff_id"] = str(
                equipment.created_by.id) if equipment.created_by is not None else ""
            creator_map["staff_name"] = f'{equipment.created_by.first_name} {equipment.created_by.last_name}' if equipment.created_by is not None else ""
            creator_map["staff_position"] = equipment.created_by.staff_position.position_title if equipment.created_by is not None and equipment.created_by.staff_position is not None else ""
            warehouse_equipment_map["created_by"] = creator_map
            creator_map = {}
            creator_map["staff_id"] = str(
                equipment.last_updated_by.id) if equipment.last_updated_by is not None else ""
            creator_map["staff_name"] = f'{equipment.last_updated_by.first_name} {equipment.last_updated_by.last_name}' if equipment.last_updated_by is not None else ""
            creator_map["staff_position"] = equipment.last_updated_by.staff_position.position_title if equipment.last_updated_by.staff_position is not None else ""
            warehouse_equipment_map["last_updated_by"] = creator_map
            warehouse_equipment_map["created_on"] = datetime.strftime(
                equipment.created_on.astimezone(target_timezone), date_format) if equipment.created_on is not None else ""
            warehouse_equipment_map["last_updated_on"] = datetime.strftime(
                equipment.last_updated_on.astimezone(target_timezone), date_format) if equipment.last_updated_on is not None else ""
            warehouse_equipment_list.append(
                warehouse_equipment_map)
        payload["warehouse_equipment_list"] = warehouse_equipment_list
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_product_categories(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    category_map = {}
    category_list = []
    payload = {}
    try:
        all_categories = Category.objects.filter(
            recycle_bin=False,
        ).order_by("-id")
        for category in all_categories:
            category_map = {}
            category_map["category_id"] = str(category.id)
            category_map["category_name"] = category.category_name
            category_map["category_description"] = category.category_description
            category_map["product_count"] = category.category_products.filter(recycle_bin=False).count()
            category_list.append(category_map)
        payload["category_list"] = category_list
        return Response({"message": "true", "payload": payload}, status=200)
    except:
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_product_inventories(request):
    product_map = {}
    product_list = []
    product_image_catalogue_map = {}
    product_image_catalogues_list = []
    payload = {}
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    list_only = str(request.data.get("list_only", "")).lower() in ("1", "true", "yes")
    try:
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        warehouse_product_inventories = warehouse_instance.warehouse_inventories.filter(
            recycle_bin=False,
            product__recycle_bin=False,
        ).select_related(
            'product',
            'product__category',
        ).order_by("-id")
        expire_reservations_for_warehouse(warehouse_instance)
        for inventory in warehouse_product_inventories:
            if inventory.product is not None:
                product_map = {}
                product_map["inventory_id"] = str(inventory.id)
                product_map["quantity"] = inventory.quantity
                product_map["minimum_stock_level"] = inventory.minimum_stock_level
                product_map["inventory_description"] = inventory.inventory_description
                reserved_qty = get_reserved_qty(inventory.product, warehouse_instance)
                available_qty = max(0.0, float(inventory.quantity) - reserved_qty)
                product_map["reserved_quantity"] = str(reserved_qty)
                product_map["available_quantity"] = str(available_qty)
                product_image_catalogues_list = []
                product_map["product_id"] = str(inventory.product.id)
                product_map["category_id"] = str(
                    inventory.product.category.id) if inventory.product.category is not None else ""
                product_map["category_name"] = inventory.product.category.category_name if inventory.product.category is not None else ""
                product_map["product_name"] = inventory.product.product_name
                product_map["stock_keeping_unit"] = inventory.product.stock_keeping_unit
                product_map["unit_of_measurement"] = inventory.product.unit_of_measurement
                product_map["product_description"] = inventory.product.product_description
                if not list_only:
                    product_image_catalogue = inventory.product.product_images.all().order_by("-id")
                    for product_image in product_image_catalogue:
                        product_image_catalogue_map = {}
                        product_image_catalogue_map["product_image_id"] = str(
                            product_image.id)
                        try:
                            product_image_catalogue_map["product_image_url"] = product_image.product_image.url
                        except (ValueError, AttributeError):
                            product_image_catalogue_map["product_image_url"] = ""
                        product_image_catalogue_map["image_attribute"] = product_image.image_attribute
                        product_image_catalogue_map["use_as_main_image"] = "true" if product_image.use_as_main_image == True else "false"
                        product_image_catalogues_list.append(
                            product_image_catalogue_map)
                product_map["product_image_catalogues_list"] = product_image_catalogues_list
                product_list.append(product_map)
        # add default products for new categories
        new_cat_prod = get_empty_products_for_new_categories()
        for new_prod in new_cat_prod:
            product_list.append(new_prod)
        # end
        payload["product_list"] = product_list
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        logger.exception("warehouse_product_inventories failed")
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_stock_transactions_incoming(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    incoming_warehouse_stock_transactions_map = {}
    incoming_warehouse_stock_transactions_list = []
    page_index = request.data["pageIndex"]
    start_index = 0
    last_index = 0

    try:
        if page_index == "1":
            start_index = 0
            last_index = 50
        elif page_index == "2":
            start_index = 50
            last_index = 100
        elif page_index == "3":
            start_index = 100
            last_index = 150
        elif page_index == "4":
            start_index = 150
            last_index = 200
        else:
            start_index = 200
            last_index = 0
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        incoming_warehouse_stock_transactions = []
        incoming_warehouse_stock_transactions_count = str(warehouse_instance.recipient_warehouse_stock_transactions.filter(
            recycle_bin=False).count())
        if last_index != 0:
            incoming_warehouse_stock_transactions = warehouse_instance.recipient_warehouse_stock_transactions.all(
            ).order_by("-id")[start_index:last_index]
        else:
            incoming_warehouse_stock_transactions = warehouse_instance.recipient_warehouse_stock_transactions.all(
            ).order_by("-id")[start_index:]
        for incoming_stock_transaction in incoming_warehouse_stock_transactions:
            if incoming_stock_transaction.recycle_bin != True:
                incoming_warehouse_stock_transactions_map = {}
                incoming_warehouse_stock_transactions_map["incoming_stock_transaction_id"] = str(
                    incoming_stock_transaction.id)
                incoming_warehouse_stock_transactions_map[
                    "stock_transaction_number"] = incoming_stock_transaction.stock_transaction_number
                incoming_warehouse_stock_transactions_map[
                    "source_warehouse_id"] = str(incoming_stock_transaction.source_warehouse.id) if incoming_stock_transaction.source_warehouse is not None else ""
                # print("running!")
                incoming_warehouse_stock_transactions_map[
                    "source_warehouse_name"] = incoming_stock_transaction.source_warehouse.warehouse_name if incoming_stock_transaction.source_warehouse is not None else ""
                incoming_warehouse_stock_transactions_map[
                    "transaction_type"] = incoming_stock_transaction.transaction_type
                incoming_warehouse_stock_transactions_map[
                    "transaction_description"] = incoming_stock_transaction.transaction_description
                incoming_warehouse_stock_transactions_map[
                    "stock_transaction_reference"] = incoming_stock_transaction.stock_transaction_reference
                incoming_warehouse_stock_transactions_map[
                    "stock_transaction_added_to_inventory"] = "true" if incoming_stock_transaction.stock_transaction_added_to_inventory == True else "false"
                creator_map = {}
                creator_map["staff_id"] = str(
                    incoming_stock_transaction.created_by.id) if incoming_stock_transaction.created_by is not None else ""
                creator_map["staff_name"] = f'{incoming_stock_transaction.created_by.first_name} {incoming_stock_transaction.created_by.last_name}' if incoming_stock_transaction.created_by is not None else ""
                creator_map["staff_position"] = incoming_stock_transaction.created_by.staff_position.position_title if incoming_stock_transaction.created_by is not None and incoming_stock_transaction.created_by.staff_position is not None else ""
                incoming_warehouse_stock_transactions_map["created_by"] = creator_map
                creator_map = {}
                creator_map["staff_id"] = str(
                    incoming_stock_transaction.last_updated_by.id) if incoming_stock_transaction.last_updated_by is not None else ""
                creator_map["staff_name"] = f'{incoming_stock_transaction.last_updated_by.first_name} {incoming_stock_transaction.last_updated_by.last_name}' if incoming_stock_transaction.last_updated_by is not None else ""
                creator_map["staff_position"] = incoming_stock_transaction.last_updated_by.staff_position.position_title if incoming_stock_transaction.last_updated_by.staff_position is not None else ""
                incoming_warehouse_stock_transactions_map["last_updated_by"] = creator_map
                incoming_warehouse_stock_transactions_map["created_on"] = datetime.strftime(
                    incoming_stock_transaction.created_on.astimezone(target_timezone), date_format) if incoming_stock_transaction.created_on is not None else ""
                incoming_warehouse_stock_transactions_map["last_updated_on"] = datetime.strftime(
                    incoming_stock_transaction.last_updated_on.astimezone(target_timezone), date_format) if incoming_stock_transaction.last_updated_on is not None else ""
                # getting stock transaction instances
                stock_transaction_instances_list = []
                stock_transaction_instances = incoming_stock_transaction.stock_transaction_instances.all().order_by("-id")
                for stock_transaction_instance in stock_transaction_instances:
                    stock_transaction_instances_map = {}
                    stock_transaction_instances_map["stock_transaction_instance_id"] = str(
                        stock_transaction_instance.id)
                    stock_transaction_instances_map["product_id"] = str(
                        stock_transaction_instance.product.id) if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["product_name"] = stock_transaction_instance.product.product_name if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["unit_of_measurement"] = stock_transaction_instance.product.unit_of_measurement if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["quantity"] = stock_transaction_instance.quantity
                    stock_transaction_instances_list.append(
                        stock_transaction_instances_map)
                incoming_warehouse_stock_transactions_map[
                    "stock_transaction_instances_list"] = stock_transaction_instances_list
                incoming_warehouse_stock_transactions_list.append(
                    incoming_warehouse_stock_transactions_map)
        payload["incoming_warehouse_stock_transactions_list"] = incoming_warehouse_stock_transactions_list
        payload["incoming_warehouse_stock_transactions_count"] = incoming_warehouse_stock_transactions_count
        return Response({"message": "true", "payload": payload}, status=200)
    except:
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_stock_transactions_outgoing(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    outgoing_warehouse_stock_transactions_map = {}
    outgoing_warehouse_stock_transactions_list = []
    page_index = request.data["pageIndex"]
    start_index = 0
    last_index = 0

    try:
        if page_index == "1":
            start_index = 0
            last_index = 50
        elif page_index == "2":
            start_index = 50
            last_index = 100
        elif page_index == "3":
            start_index = 100
            last_index = 150
        elif page_index == "4":
            start_index = 150
            last_index = 200
        else:
            start_index = 200
            last_index = 0
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        outgoing_warehouse_stock_transactions = []
        outgoing_warehouse_stock_transactions_count = str(warehouse_instance.source_warehouse_stock_transactions.filter(
            recycle_bin=False).count())
        if last_index != 0:
            outgoing_warehouse_stock_transactions = warehouse_instance.source_warehouse_stock_transactions.all(
            ).order_by("-id")[start_index:last_index]
        else:
            outgoing_warehouse_stock_transactions = warehouse_instance.source_warehouse_stock_transactions.all(
            ).order_by("-id")[start_index:]
        for outgoing_stock_transaction in outgoing_warehouse_stock_transactions:
            if outgoing_stock_transaction.recycle_bin != True:
                outgoing_warehouse_stock_transactions_map = {}
                outgoing_warehouse_stock_transactions_map["outgoing_stock_transaction_id"] = str(
                    outgoing_stock_transaction.id)
                outgoing_warehouse_stock_transactions_map[
                    "stock_transaction_number"] = outgoing_stock_transaction.stock_transaction_number
                outgoing_warehouse_stock_transactions_map[
                    "recipient_warehouse_id"] = str(outgoing_stock_transaction.recipient_warehouse.id) if outgoing_stock_transaction.recipient_warehouse is not None else ""
                outgoing_warehouse_stock_transactions_map[
                    "recipient_warehouse_name"] = outgoing_stock_transaction.recipient_warehouse.warehouse_name if outgoing_stock_transaction.recipient_warehouse is not None else ""
                outgoing_warehouse_stock_transactions_map[
                    "transaction_type"] = outgoing_stock_transaction.transaction_type
                outgoing_warehouse_stock_transactions_map[
                    "transaction_description"] = outgoing_stock_transaction.transaction_description
                outgoing_warehouse_stock_transactions_map[
                    "stock_transaction_reference"] = outgoing_stock_transaction.stock_transaction_reference
                outgoing_warehouse_stock_transactions_map[
                    "stock_transaction_added_to_inventory"] = "true" if outgoing_stock_transaction.stock_transaction_added_to_inventory == True else "false"
                creator_map = {}
                creator_map["staff_id"] = str(
                    outgoing_stock_transaction.created_by.id) if outgoing_stock_transaction.created_by is not None else ""
                creator_map["staff_name"] = f'{outgoing_stock_transaction.created_by.first_name} {outgoing_stock_transaction.created_by.last_name}' if outgoing_stock_transaction.created_by is not None else ""
                creator_map["staff_position"] = outgoing_stock_transaction.created_by.staff_position.position_title if outgoing_stock_transaction.created_by is not None and outgoing_stock_transaction.created_by.staff_position is not None else ""
                outgoing_warehouse_stock_transactions_map["created_by"] = creator_map
                creator_map = {}
                creator_map["staff_id"] = str(
                    outgoing_stock_transaction.last_updated_by.id) if outgoing_stock_transaction.last_updated_by is not None else ""
                creator_map["staff_name"] = f'{outgoing_stock_transaction.last_updated_by.first_name} {outgoing_stock_transaction.last_updated_by.last_name}' if outgoing_stock_transaction.last_updated_by is not None else ""
                creator_map["staff_position"] = outgoing_stock_transaction.last_updated_by.staff_position.position_title if outgoing_stock_transaction.last_updated_by.staff_position is not None else ""
                outgoing_warehouse_stock_transactions_map["last_updated_by"] = creator_map
                outgoing_warehouse_stock_transactions_map["created_on"] = datetime.strftime(
                    outgoing_stock_transaction.created_on.astimezone(target_timezone), date_format) if outgoing_stock_transaction.created_on is not None else ""
                outgoing_warehouse_stock_transactions_map["last_updated_on"] = datetime.strftime(
                    outgoing_stock_transaction.last_updated_on.astimezone(target_timezone), date_format) if outgoing_stock_transaction.last_updated_on is not None else ""
                # getting stock transaction instances
                stock_transaction_instances_list = []
                stock_transaction_instances = outgoing_stock_transaction.stock_transaction_instances.all().order_by("-id")
                for stock_transaction_instance in stock_transaction_instances:
                    stock_transaction_instances_map = {}
                    stock_transaction_instances_map["stock_transaction_instance_id"] = str(
                        stock_transaction_instance.id)
                    stock_transaction_instances_map["product_id"] = str(
                        stock_transaction_instance.product.id) if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["product_name"] = stock_transaction_instance.product.product_name if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["unit_of_measurement"] = stock_transaction_instance.product.unit_of_measurement if stock_transaction_instance.product is not None else ""
                    stock_transaction_instances_map["quantity"] = stock_transaction_instance.quantity
                    stock_transaction_instances_list.append(
                        stock_transaction_instances_map)
                outgoing_warehouse_stock_transactions_map[
                    "stock_transaction_instances_list"] = stock_transaction_instances_list
                outgoing_warehouse_stock_transactions_list.append(
                    outgoing_warehouse_stock_transactions_map)
                # (outgoing_warehouse_stock_transactions_list)
        payload["outgoing_warehouse_stock_transactions_list"] = outgoing_warehouse_stock_transactions_list
        payload["outgoing_warehouse_stock_transactions_count"] = outgoing_warehouse_stock_transactions_count
        return Response({"message": "true", "payload": payload}, status=200)
    except:
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def view_single_warehouse_product_transactions(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    product_id = request.data["product_id"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    incoming_stock_transactions_list = []
    outgoing_stock_transactions_list = []
    try:
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        product_instance = Product.objects.get(id=int(product_id))
        inventory = Inventory.objects.get(
            product=product_instance, warehouse=warehouse_instance)
        # product_map = {}
        # product_map["inventory_id"] = str(inventory.id)
        # product_map["quantity"] = inventory.quantity
        # product_map["minimum_stock_level"] = inventory.minimum_stock_level
        # product_map["inventory_description"] = inventory.inventory_description
        # product_image_catalogues_list = []
        # product_map["product_id"] = str(inventory.product.id)
        # product_map["category_id"] = str(
        #     inventory.product.category.id) if inventory.product.category is not None else ""
        # product_map["category_name"] = inventory.product.category.category_name if inventory.product.category is not None else ""
        # product_map["product_name"] = inventory.product.product_name
        # product_map["stock_keeping_unit"] = inventory.product.stock_keeping_unit
        # product_map["unit_of_measurement"] = inventory.product.unit_of_measurement
        # product_map["product_description"] = inventory.product.product_description
        # product_image_catalogue = inventory.product.product_images.all().order_by("-id")
        # for product_image in product_image_catalogue:
        #     product_image_catalogue_map = {}
        #     product_image_catalogue_map["product_image_id"] = str(
        #         product_image.id)
        #     product_image_catalogue_map["product_image_url"] = product_image.product_image.url
        #     product_image_catalogue_map["image_attribute"] = product_image.image_attribute
        #     product_image_catalogue_map["use_as_main_image"] = "true" if product_image.use_as_main_image == True else "false"
        #     product_image_catalogues_list.append(
        #         product_image_catalogue_map)
        # product_map["product_image_catalogues_list"] = product_image_catalogues_list
        product_stock_transactions = product_instance.product_stock_transactions.all()
        for stock_transaction in product_stock_transactions:
            if stock_transaction.stock_transaction is not None and stock_transaction.stock_transaction.recycle_bin != True:
                stock_transaction_map = {}
                stock_transaction_map["stock_transaction_number"] = stock_transaction.stock_transaction.stock_transaction_number
                stock_transaction_map["quantity"] = stock_transaction.quantity
                stock_transaction_map["created_on"] = datetime.strftime(
                    stock_transaction.stock_transaction.created_on.astimezone(target_timezone), date_format) if stock_transaction.stock_transaction.created_on is not None else ""
                if stock_transaction.stock_transaction.recipient_warehouse == warehouse_instance:
                    stock_transaction_map["warehouse_name"] = stock_transaction.stock_transaction.recipient_warehouse.warehouse_name
                    incoming_stock_transactions_list.append(
                        stock_transaction_map)
                if stock_transaction.stock_transaction.source_warehouse == warehouse_instance:
                    stock_transaction_map["warehouse_name"] = stock_transaction.stock_transaction.source_warehouse.warehouse_name
                    outgoing_stock_transactions_list.append(
                        stock_transaction_map)
        payload["incoming_stock_transactions_list"] = incoming_stock_transactions_list
        payload["outgoing_stock_transactions_list"] = outgoing_stock_transactions_list
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def get_stock_transaction_creation_items(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    category_list = []
    warehouse_list = []
    try:
        all_categories = Category.objects.filter(
            recycle_bin=False).order_by("-id")
        for category in all_categories:
            if category.category_name != "TRANSPORT FEE":
                category_map = {}
                category_map["category_id"] = str(category.id)
                category_map["category_name"] = category.category_name
                category_products = category.category_products.filter(
                    recycle_bin=False).order_by("-id")
                products_list = []
                for product in category_products:
                    product_map = {}
                    product_map["product_id"] = str(product.id)
                    product_map["product_name"] = product.product_name
                    product_map["stock_keeping_unit"] = product.stock_keeping_unit
                    product_map["unit_of_measurement"] = product.unit_of_measurement
                    products_list.append(product_map)
                category_map["product_list"] = products_list
                category_list.append(category_map)
        all_warehouses = Warehouse.objects.filter(
            recycle_bin=False).order_by("-id")
        for warehouse in all_warehouses:
            warehouse_map = {}
            warehouse_map["warehouse_id"] = str(warehouse.id)
            warehouse_map["warehouse_name"] = warehouse.warehouse_name
            warehouse_map["branch_name"] = warehouse.company_branch.branch_name
            warehouse_list.append(warehouse_map)
        payload["warehouse_list"] = warehouse_list
        payload["category_list"] = category_list
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def get_summary_of_inventory(request):
    warehouse_id = request.data["warehouse_id"]
    category_id = request.data["category_id"]
    payload = {}
    category_container_list = []
    try:
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))

        inventories_qs = Inventory.objects.filter(
            recycle_bin=False,
            warehouse=warehouse_instance,
            product__recycle_bin=False,
            product__category__isnull=False,
        ).exclude(
            product__category__category_name="TRANSPORT FEE",
        ).select_related(
            'product', 'product__category',
        ).order_by('-product__category__id', '-product__id')

        if category_id != "all":
            inventories_qs = inventories_qs.filter(
                product__category_id=int(category_id),
            )

        inventories = list(inventories_qs)
        product_ids = list({inv.product_id for inv in inventories if inv.product_id})

        incoming_counts = {}
        outgoing_counts = {}
        if product_ids:
            incoming_counts = {
                row['product_id']: row['count']
                for row in StockTransactionInstance.objects.filter(
                    product_id__in=product_ids,
                    stock_transaction__isnull=False,
                    stock_transaction__recycle_bin=False,
                    stock_transaction__recipient_warehouse=warehouse_instance,
                ).values('product_id').annotate(count=Count('id'))
            }
            outgoing_counts = {
                row['product_id']: row['count']
                for row in StockTransactionInstance.objects.filter(
                    product_id__in=product_ids,
                    stock_transaction__isnull=False,
                    stock_transaction__recycle_bin=False,
                    stock_transaction__source_warehouse=warehouse_instance,
                ).values('product_id').annotate(count=Count('id'))
            }

        expire_reservations_for_warehouse(warehouse_instance)
        category_map_by_id = {}
        for inventory in inventories:
            product = inventory.product
            if product is None or product.category is None:
                continue
            category = product.category
            cat_id = str(category.id)
            if cat_id not in category_map_by_id:
                category_map_by_id[cat_id] = {
                    "category_id": cat_id,
                    "category_name": category.category_name,
                    "product_list": [],
                }
            reserved_qty = get_reserved_qty(product, warehouse_instance)
            available_qty = max(0.0, float(inventory.quantity) - reserved_qty)
            category_map_by_id[cat_id]["product_list"].append({
                "product_id": str(product.id),
                "product_name": product.product_name,
                "stock_keeping_unit": product.stock_keeping_unit,
                "unit_of_measurement": product.unit_of_measurement,
                "product_description": product.product_description,
                "inventory_id": str(inventory.id),
                "quantity": inventory.quantity,
                "minimum_stock_level": inventory.minimum_stock_level,
                "reserved_quantity": str(reserved_qty),
                "available_quantity": str(available_qty),
                "incoming_stock_transactions_count": incoming_counts.get(product.id, 0),
                "outgoing_stock_transactions_count": outgoing_counts.get(product.id, 0),
            })

        if category_id != "all":
            category = Category.objects.filter(
                id=int(category_id), recycle_bin=False,
            ).first()
            if category and category.category_name != "TRANSPORT FEE":
                cat_id = str(category.id)
                if cat_id not in category_map_by_id:
                    category_map_by_id[cat_id] = {
                        "category_id": cat_id,
                        "category_name": category.category_name,
                        "product_list": [],
                    }

        category_container_list = sorted(
            category_map_by_id.values(),
            key=lambda item: int(item["category_id"]),
            reverse=True,
        )
        payload["category_list"] = category_container_list
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_purchase_requisition(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    purchase_requisition_map = {}
    purchase_requisition_list = []
    purchase_requisition_instances_map = {}
    purchase_requisition_instances_list = []
    page_index = request.data["pageIndex"]
    start_index = 0
    last_index = 0
    try:
        if page_index == "1":
            start_index = 0
            last_index = 50
        elif page_index == "2":
            start_index = 50
            last_index = 100
        elif page_index == "3":
            start_index = 100
            last_index = 150
        elif page_index == "4":
            start_index = 150
            last_index = 200
        else:
            start_index = 200
            last_index = 0
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        warehouse_purchase_requisitions = []
        purchase_requisition_count = str(
            warehouse_instance.warehouse_purchase_requisitions.filter(recycle_bin=False).count())
        if last_index != 0:
            warehouse_purchase_requisitions = warehouse_instance.warehouse_purchase_requisitions.all(
            ).order_by("-id")[start_index:last_index]
        else:
            warehouse_purchase_requisitions = warehouse_instance.warehouse_purchase_requisitions.all(
            ).order_by("-id")[start_index:]
        for purchase_requisition in warehouse_purchase_requisitions:
            if purchase_requisition.recycle_bin != True:
                purchase_requisition_map = {}
                purchase_requisition_instances_list = []
                purchase_requisition_map["purchase_requisition_id"] = str(
                    purchase_requisition.id)
                purchase_requisition_map["purchase_requisition_number"] = purchase_requisition.purchase_requisition_number
                purchase_requisition_map["purchase_requisition_description"] = purchase_requisition.purchase_requisition_description
                purchase_requisition_map["purchase_requisition_approved"] = "true" if purchase_requisition.purchase_requisition_approved == True else "false"
                creator_map = {}
                creator_map["staff_id"] = str(
                    purchase_requisition.created_by.id) if purchase_requisition.created_by is not None else ""
                creator_map["staff_name"] = f'{purchase_requisition.created_by.first_name} {purchase_requisition.created_by.last_name}' if purchase_requisition.created_by is not None else ""
                creator_map["staff_position"] = purchase_requisition.created_by.staff_position.position_title if purchase_requisition.created_by is not None and purchase_requisition.created_by.staff_position is not None else ""
                purchase_requisition_map["created_by"] = creator_map
                creator_map = {}
                creator_map["staff_id"] = str(
                    purchase_requisition.last_updated_by.id) if purchase_requisition.last_updated_by is not None else ""
                creator_map["staff_name"] = f'{purchase_requisition.last_updated_by.first_name} {purchase_requisition.last_updated_by.last_name}' if purchase_requisition.last_updated_by is not None else ""
                creator_map["staff_position"] = purchase_requisition.last_updated_by.staff_position.position_title if purchase_requisition.last_updated_by.staff_position is not None else ""
                purchase_requisition_map["last_updated_by"] = creator_map
                purchase_requisition_map["created_on"] = datetime.strftime(
                    purchase_requisition.created_on.astimezone(target_timezone), date_format) if purchase_requisition.created_on is not None else ""
                purchase_requisition_map["last_updated_on"] = datetime.strftime(
                    purchase_requisition.last_updated_on.astimezone(target_timezone), date_format) if purchase_requisition.last_updated_on is not None else ""
                purchase_requisition_instances = purchase_requisition.purchase_requisition_instances.all().order_by("id")
                for purchase_requisition_instance in purchase_requisition_instances:
                    if purchase_requisition_instance.recycle_bin != True:
                        purchase_requisition_instances_map = {}
                        purchase_requisition_instances_map["purchase_requisition_instance_id"] = str(
                            purchase_requisition_instance.id)
                        purchase_requisition_instances_map["product_id"] = str(
                            purchase_requisition_instance.product.id) if purchase_requisition_instance.product is not None else ""
                        purchase_requisition_instances_map["product_name"] = purchase_requisition_instance.product.product_name if purchase_requisition_instance.product is not None else ""
                        purchase_requisition_instances_map["unit_of_measurement"] = purchase_requisition_instance.product.unit_of_measurement if purchase_requisition_instance.product is not None else ""
                        purchase_requisition_instances_map[
                            "quantity"] = purchase_requisition_instance.quantity
                        purchase_requisition_instances_map[
                            "purchase_requisition_items_purchased"] = "true" if purchase_requisition_instance.purchase_requisition_items_purchased == True else "false"
                        purchase_requisition_instances_list.append(
                            purchase_requisition_instances_map)
                purchase_requisition_map["purchase_requisition_instances_list"] = purchase_requisition_instances_list
                purchase_requisition_list.append(
                    purchase_requisition_map)
        payload["purchase_requisition_list"] = purchase_requisition_list
        payload["purchase_requisition_count"] = purchase_requisition_count
        return Response({"message": "true", "payload": payload}, status=200)
    except:
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_stock_requisition(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    active_user = request.user
    payload = {}
    target_timezone = pytz.timezone('Africa/Nairobi')
    # stock_requisition_map = {}
    stock_requisition_list = []
    stock_requisition_instances_map = {}
    stock_requisition_instances_list = []
    page_index = request.data["pageIndex"]
    start_index = 0
    last_index = 0
    # print(page_index)
    try:
        if page_index == "1":
            start_index = 0
            last_index = 50
        elif page_index == "2":
            start_index = 50
            last_index = 100
        elif page_index == "3":
            start_index = 100
            last_index = 150
        elif page_index == "4":
            start_index = 150
            last_index = 200
        else:
            start_index = 200
            last_index = 0
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        warehouse_stock_requisitions = []
        warehouse_stock_requisitions_count = str(
            warehouse_instance.warehouse_stock_requisitions.filter(recycle_bin=False).count())
        if last_index != 0:
            warehouse_stock_requisitions = warehouse_instance.warehouse_stock_requisitions.all(
            ).order_by("-id")[start_index:last_index]
        else:
            warehouse_stock_requisitions = warehouse_instance.warehouse_stock_requisitions.all(
            ).order_by("-id")[start_index:]
        # print(len(warehouse_stock_requisitions))
        for warehouse_stock_requisition in warehouse_stock_requisitions:
            if warehouse_stock_requisition.recycle_bin != True:
                warehouse_stock_requisitions_map = {}
                stock_requisition_instances_list = []
                warehouse_stock_requisitions_map["warehouse_id"] = str(
                    warehouse_instance.id)
                warehouse_stock_requisitions_map["warehouse_stock_requisition_id"] = str(
                    warehouse_stock_requisition.id)
                warehouse_stock_requisitions_map[
                    "stock_requisition_number"] = warehouse_stock_requisition.stock_requisition_number
                warehouse_stock_requisitions_map[
                    "stock_requisition_description"] = warehouse_stock_requisition.stock_requisition_description
                warehouse_stock_requisitions_map[
                    "stock_requisition_approved"] = "true" if warehouse_stock_requisition.stock_requisition_approved == True else "false"
                creator_map = {}
                creator_map["staff_id"] = str(
                    warehouse_stock_requisition.created_by.id) if warehouse_stock_requisition.created_by is not None else ""
                creator_map["staff_name"] = f'{warehouse_stock_requisition.created_by.first_name} {warehouse_stock_requisition.created_by.last_name}' if warehouse_stock_requisition.created_by is not None else ""
                creator_map["staff_position"] = warehouse_stock_requisition.created_by.staff_position.position_title if warehouse_stock_requisition.created_by is not None and warehouse_stock_requisition.created_by.staff_position is not None else ""
                warehouse_stock_requisitions_map["created_by"] = creator_map
                creator_map = {}
                creator_map["staff_id"] = str(
                    warehouse_stock_requisition.last_updated_by.id) if warehouse_stock_requisition.last_updated_by is not None else ""
                creator_map["staff_name"] = f'{warehouse_stock_requisition.last_updated_by.first_name} {warehouse_stock_requisition.last_updated_by.last_name}' if warehouse_stock_requisition.last_updated_by is not None else ""
                creator_map["staff_position"] = warehouse_stock_requisition.last_updated_by.staff_position.position_title if warehouse_stock_requisition.last_updated_by.staff_position is not None else ""
                warehouse_stock_requisitions_map["last_updated_by"] = creator_map
                warehouse_stock_requisitions_map["created_on"] = datetime.strftime(
                    warehouse_stock_requisition.created_on.astimezone(target_timezone), date_format) if warehouse_stock_requisition.created_on is not None else ""
                warehouse_stock_requisitions_map["last_updated_on"] = datetime.strftime(
                    warehouse_stock_requisition.last_updated_on.astimezone(target_timezone), date_format) if warehouse_stock_requisition.last_updated_on is not None else ""
                stock_requisition_instances = warehouse_stock_requisition.stock_requisition_instances.all().order_by("-id")
                for stock_requisition_instance in stock_requisition_instances:
                    if stock_requisition_instance.recycle_bin != True:
                        stock_requisition_instances_map = {}
                        stock_requisition_instances_map["stock_requisition_instance_id"] = str(
                            stock_requisition_instance.id)
                        stock_requisition_instances_map["product_id"] = str(
                            stock_requisition_instance.product.id) if stock_requisition_instance.product is not None else ""
                        stock_requisition_instances_map[
                            "product_name"] = stock_requisition_instance.product.product_name if stock_requisition_instance.product is not None else ""
                        stock_requisition_instances_map["quantity"] = stock_requisition_instance.quantity
                        stock_requisition_instances_map[
                            "stock_requisition_items_delivered"] = "true" if stock_requisition_instance.stock_requisition_items_delivered else "false"
                        stock_requisition_instances_list.append(
                            stock_requisition_instances_map)
                        # print("running")
                warehouse_stock_requisitions_map[
                    "stock_requisition_instances_list"] = stock_requisition_instances_list
                stock_requisition_list.append(
                    warehouse_stock_requisitions_map)
        payload["warehouse_stock_requisitions_list"] = stock_requisition_list
        payload["warehouse_stock_requisitions_count"] = warehouse_stock_requisitions_count
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def warehouse_management_dashboard(request):
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    active_user = request.user
    payload = {}
    warehouse_list = []
    warehouse_map = {}
    company_profile_map = {}
    company_branches_list = []
    company_branch_map = {}
    category_map = {}
    category_list = []
    product_map = {}
    product_list = []
    product_image_catalogue_map = {}
    product_image_catalogues_list = []
    warehouse_inventory_map = {}
    warehouse_inventory_list = []
    outgoing_warehouse_stock_transactions_map = {}
    outgoing_warehouse_stock_transactions_list = []
    incoming_warehouse_stock_transactions_map = {}
    incoming_warehouse_stock_transactions_list = []
    stock_transaction_instances_map = {}
    stock_transaction_instances_map = {}
    stock_transaction_instances_list = []
    warehouse_equipment_map = {}
    warehouse_equipment_list = []
    purchase_requisition_map = {}
    purchase_requisition_list = []
    purchase_requisition_instances_map = {}
    purchase_requisition_instances_list = []
    warehouse_stock_requisitions_map = {}
    warehouse_stock_requisitions_list = []
    stock_requisition_instances_map = {}
    stock_requisition_instances_list = []
    target_timezone = pytz.timezone('Africa/Nairobi')
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        #
        staff_profile = StaffProfile.objects.select_related(
            "company_department", "staff_position", "company_branch",
        ).get(user=active_user)
        # Permission check FIRST — avoid expensive work for unauthorised users.
        if not user_has_permission(staff_profile, "warehouse.dashboard.view"):
            return Response({"message": "false", "payload": payload}, status=401)
        all_branches = company_profile.company_branches.all()
        if True:  # permission already verified above
            active_staff_profile_data = get_staff_profile_data(active_user)
            company_profile_map["company_id"] = str(company_profile.id)
            company_profile_map["company_name"] = company_profile.company_name
            company_profile_map["company_phone"] = company_profile.company_phone
            # Categories — Vue frontend only needs id/name/description; products are fetched via a separate endpoint.
            all_categories = Category.objects.filter(recycle_bin=False).order_by("-id")
            for category in all_categories:
                category_list.append({
                    "category_id": str(category.id),
                    "category_name": category.category_name,
                    "category_description": category.category_description,
                    "product_count": category.category_products.filter(recycle_bin=False).count(),
                    "product_list": [],  # populated on demand by warehouse-product-categories endpoint
                })

            # Warehouses — lean query: only fetch basic fields needed by Vue dashboard.
            # Detail lists (inventories, transactions, equipment, requisitions) are fetched on
            # demand via their own dedicated endpoints.
            is_main_branch = staff_profile.company_branch is not None and staff_profile.company_branch.main_branch
            warehouses_qs = Warehouse.objects.filter(
                recycle_bin=False,
                company_branch__in=all_branches,
            ).select_related(
                "company_branch",
                "created_by__staff_position",
                "last_updated_by__staff_position",
            ).order_by("-id")

            if not is_main_branch and staff_profile.company_branch is not None:
                warehouses_qs = warehouses_qs.filter(company_branch=staff_profile.company_branch)

            for branch in all_branches:
                company_branch_map = {}
                company_branch_map["branch_id"] = str(branch.id)
                company_branch_map["branch_name"] = branch.branch_name
                company_branch_map["is_main_branch"] = "true" if branch.main_branch == True else "false"
                company_branches_list.append(company_branch_map)
            company_profile_map["company_branches_list"] = company_branches_list

            for warehouse in warehouses_qs:
                if True:  # recycle_bin already filtered in queryset
                        warehouse_map = {}
                        warehouse_map["warehouse_id"] = str(warehouse.id)
                        warehouse_map["warehouse_company_branch_id"] = str(
                            warehouse.company_branch.id) if warehouse.company_branch else ""
                        warehouse_map["warehouse_company_branch_name"] = warehouse.company_branch.branch_name if warehouse.company_branch else ""
                        warehouse_map["warehouse_name"] = warehouse.warehouse_name
                        warehouse_map["warehouse_location"] = warehouse.warehouse_location
                        warehouse_map["warehouse_capacity"] = warehouse.warehouse_capacity
                        warehouse_map["warehouse_contact_phone"] = warehouse.warehouse_contact_phone
                        warehouse_map["warehouse_description"] = warehouse.warehouse_description
                        creator_map = {}
                        creator_map["staff_id"] = str(
                            warehouse.created_by.id) if warehouse.created_by is not None else ""
                        creator_map["staff_name"] = f'{warehouse.created_by.first_name} {warehouse.created_by.last_name}' if warehouse.created_by is not None else ""
                        creator_map["staff_position"] = warehouse.created_by.staff_position.position_title if warehouse.created_by is not None and warehouse.created_by.staff_position is not None else ""
                        warehouse_map["created_by"] = creator_map
                        creator_map = {}
                        creator_map["staff_id"] = str(
                            warehouse.last_updated_by.id) if warehouse.last_updated_by is not None else ""
                        creator_map["staff_name"] = f'{warehouse.last_updated_by.first_name} {warehouse.last_updated_by.last_name}' if warehouse.last_updated_by is not None else ""
                        creator_map["staff_position"] = warehouse.last_updated_by.staff_position.position_title if warehouse.last_updated_by is not None and warehouse.last_updated_by.staff_position is not None else ""
                        warehouse_map["last_updated_by"] = creator_map
                        warehouse_map["created_on"] = datetime.strftime(
                            warehouse.created_on.astimezone(target_timezone), date_format) if warehouse.created_on is not None else ""
                        warehouse_map["last_updated_on"] = datetime.strftime(
                            warehouse.last_updated_on.astimezone(target_timezone), date_format) if warehouse.last_updated_on is not None else ""
                        products_count = warehouse.warehouse_inventories.filter(
                            recycle_bin=False,
                            product__recycle_bin=False,
                        ).count()
                        equipment_count = warehouse.warehouse_equipment.filter(recycle_bin=False).count()
                        incoming_tx_count = warehouse.recipient_warehouse_stock_transactions.filter(recycle_bin=False).count()
                        outgoing_tx_count = warehouse.source_warehouse_stock_transactions.filter(recycle_bin=False).count()
                        warehouse_map["all_products"] = str(products_count)
                        warehouse_map["warehouse_equipment"] = str(equipment_count)
                        warehouse_map["incoming_transactions"] = str(incoming_tx_count)
                        warehouse_map["outgoing_transactions"] = str(outgoing_tx_count)
                        warehouse_map["stock_transactions"] = str(incoming_tx_count + outgoing_tx_count)
                        # Detail lists are returned empty here; they are loaded on demand by
                        warehouse_map["warehouse_inventory_list"] = []
                        warehouse_map["outgoing_warehouse_stock_transactions_list"] = []
                        warehouse_map["incoming_warehouse_stock_transactions_list"] = []
                        warehouse_map["warehouse_equipment_list"] = []
                        warehouse_map["purchase_requisition_list"] = []
                        warehouse_map["warehouse_stock_requisitions_list"] = []
                        warehouse_list.append(warehouse_map)
            payload["active_staff_profile_data"] = active_staff_profile_data
            payload["warehouse_list"] = warehouse_list
            payload["company_profile"] = company_profile_map
            payload["category_list"] = category_list
            return Response({"message": "true", "payload": payload}, status=200)
    except StaffProfile.DoesNotExist:
        return Response({"message": "Staff profile not found for active user", "payload": payload}, status=404)
    except Exception as e:
        logger.exception("warehouse_management_dashboard failed")
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_equipment(request):
    active_user = request.user
    warehouse_id = request.data["warehouse_id"]
    company_serial_number = request.data["serial_number"]
    equipment_name = request.data["equipment_name"]
    equipment_serial_number = request.data["equipment_serial_number"]
    equipment_description = request.data["equipment_description"]
    status = request.data["status"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.equipment.add"):
            equipment_serializer = EquipmentSerializer(
                data={'warehouse': int(warehouse_id), 'equipment_name': equipment_name, 'equipment_serial_number': equipment_serial_number, 'equipment_description': equipment_description, 'status': status, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if equipment_serializer.is_valid():
                equipment_serializer.save()
                return Response({"message": "Equipment added successfully", }, status=200)
            else:
                return Response({"message": "Unable to add equipment", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:
        return Response({"message": "Error adding equipment", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_product_categories(request):
    active_user = request.user
    category_name = request.data["category_name"]
    category_description = request.data["category_description"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product_category.add"):
            category_serializer = CategorySerializer(
                data={'category_name': category_name, 'category_description': category_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if category_serializer.is_valid():
                category_serializer.save()
                return Response({"message": "Product category added successfully", }, status=200)
            else:
                return Response({"message": "Unable to add product category", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error adding product category", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_product(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    category_id = request.data["category_id"]
    product_name = request.data["product_name"]
    stock_keeping_unit = request.data["stock_keeping_unit"]
    unit_of_measurement = request.data["unit_of_measurement"]
    product_description = request.data["product_description"]
    # minimum_stock_level = request.data["minimum_stock_level"]
    # quantity = request.data["quantity"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product.add"):
            product_serializer = ProductSerializer(
                data={'category': int(category_id), 'product_name': product_name, 'unit_of_measurement': unit_of_measurement, 'product_description': product_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if product_serializer.is_valid():
                new_product = product_serializer.save()
                # create inventories on all the warehouses
                all_warehouses = Warehouse.objects.all()
                for warehouse in all_warehouses:
                    if warehouse.recycle_bin != True:
                        new_inventory_serializer = InventorySerializer(
                            data={'product': new_product.id, 'warehouse': warehouse.id, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
                        if new_inventory_serializer.is_valid():
                            new_inventory_serializer.save()
                # create product features and attributes
                for i in range(5):
                    new_feature = ProductFeaturesCatalogue.objects.create(
                        product=new_product)
                    ProductComponent.objects.create(product=new_product)
                    ProductBrand.objects.create(product=new_product)
                    for j in range(5):
                        ProductFeaturesAttributes.objects.create(
                            product_feature_catalogue=new_feature)
                # create product pricing
                ProductPricing.objects.create(product=new_product)
                # create product discount
                ProductDiscount.objects.create(product=new_product)
                # create product vat
                ProductVAT.objects.create(product=new_product)
                if len(stock_keeping_unit) > 0:
                    new_product.stock_keeping_unit = stock_keeping_unit
                    new_product.save()

                    # new_product_inventory = Inventory.objects.get_or_create(product=new_product.id,warehouse=)
                return Response({"message": "Product added successfully", }, status=200)
            else:
                # print(product_serializer.errors)
                return Response({"message": "Unable to add product", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error adding product", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_stock_transaction(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    transaction_type = request.data["transaction_type"]
    recipient_warehouse_id = request.data["recipient_warehouse_id"]
    source_warehouse_id = request.data["source_warehouse_id"]
    try:
        if transaction_type == "in_bound":
            source_warehouse_id = None
            recipient_warehouse_id = int(recipient_warehouse_id) if len(
                recipient_warehouse_id) > 0 else None
        else:
            source_warehouse_id = int(source_warehouse_id) if len(
                source_warehouse_id) > 0 else None
            if recipient_warehouse_id != "sales_order":
                recipient_warehouse_id = int(recipient_warehouse_id) if len(
                    recipient_warehouse_id) > 0 else None
            else:
                recipient_warehouse_id = None
        transaction_description = request.data["transaction_description"]
        stock_transaction_reference = request.data["stock_transaction_reference"]
        stock_transaction_added_to_inventory = True if request.data[
            "stock_transaction_added_to_inventory"] == "true" else False
        stock_transaction_instance_list = request.data.get(
            'stock_transaction_instance_list', [])
        stock_transaction_instance_list = json.loads(
            stock_transaction_instance_list)
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.stock_transaction.add"):
            stock_transaction_serializer = StockTransactionSerializer(
                data={'source_warehouse': source_warehouse_id, 'recipient_warehouse': recipient_warehouse_id, 'transaction_type': transaction_type, 'transaction_description': transaction_description, 'stock_transaction_reference': stock_transaction_reference, 'stock_transaction_added_to_inventory': stock_transaction_added_to_inventory, 'created_by': staff_profile.id,
                      'last_updated_by': staff_profile.id, })
            if stock_transaction_serializer.is_valid():
                new_stock_transaction = stock_transaction_serializer.save()
                affected_product_ids = set()
                for stock_transaction in stock_transaction_instance_list:
                    product_id = int(stock_transaction["product_id"])
                    quantity = stock_transaction["quantity"]
                    affected_product_ids.add(product_id)
                    stock_transaction_instance_serializer = StockTransactionInstanceSerializer(
                        data={'stock_transaction': new_stock_transaction.id, 'product': product_id, 'quantity': quantity, })
                    if stock_transaction_instance_serializer.is_valid():
                        stock_transaction_instance_serializer.save()
                        # update respective inventory
                        if transaction_type == "in_bound":
                            if recipient_warehouse_id is not None:
                                subject_warehouse = Warehouse.objects.get(
                                    id=recipient_warehouse_id)
                                product_instance = Product.objects.get(
                                    id=product_id)
                                subject_inventory = Inventory.objects.get(
                                    warehouse=subject_warehouse, product=product_instance)
                                apply_inventory_delta(
                                    subject_inventory, float(quantity),
                                    movement_type="receipt", staff=staff_profile,
                                    reference=new_stock_transaction.stock_transaction_number,
                                    stock_transaction=new_stock_transaction,
                                    notes=transaction_description)
                        else:
                            if source_warehouse_id is not None:
                                subject_warehouse = Warehouse.objects.get(
                                    id=source_warehouse_id)
                                product_instance = Product.objects.get(
                                    id=product_id)
                                subject_inventory = Inventory.objects.get(
                                    warehouse=subject_warehouse, product=product_instance)
                                apply_inventory_delta(
                                    subject_inventory, -float(quantity),
                                    movement_type="issue", staff=staff_profile,
                                    reference=new_stock_transaction.stock_transaction_number,
                                    stock_transaction=new_stock_transaction,
                                    notes=transaction_description)

                gl_posting = {}
                if transaction_type == "out_bound":
                    gl_posting = post_goods_delivery(
                        staff_profile.company_branch,
                        staff_profile,
                        new_stock_transaction,
                    )
                elif transaction_type == "in_bound" and stock_transaction_added_to_inventory:
                    gl_posting = post_goods_receipt(
                        staff_profile.company_branch,
                        staff_profile,
                        new_stock_transaction,
                    )

                # Inventory levels changed — broadcast new availability to the CRM.
                notify_crm_stock(affected_product_ids)

                return Response({
                    "message": "Stock transaction created successfully",
                    "gl_posting": gl_posting,
                }, status=200)
            else:
                print(stock_transaction_serializer.errors)
                return Response({"message": "Unable to create stock transaction", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except Exception as e:
        print(e)
        return Response({"message": "Error creating stock transaction", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def approve_stock_requisition_and_generate_transaction(request):
    active_user = request.user
    try:
        company_serial_number = request.data["serial_number"]
        transaction_type = "out_bound"
        # recipient_warehouse_id = request.data["recipient_warehouse_id"]
        stock_requisition_id = int(request.data["stock_requisition_id"])
        source_warehouse_id = int(request.data["source_warehouse_id"])
        transaction_description = request.data["transaction_description"]
        stock_transaction_reference = request.data["stock_transaction_reference"]
        stock_transaction_instance_list = request.data.get(
            'stock_transaction_instance_list', [])
        stock_transaction_instance_list = json.loads(
            stock_transaction_instance_list)
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.stock_requisition.approve"):
            stock_transaction_serializer = StockTransactionSerializer(
                data={'source_warehouse': source_warehouse_id, 'transaction_type': transaction_type, 'transaction_description': transaction_description, 'stock_transaction_reference': stock_transaction_reference, 'created_by': staff_profile.id,
                      'last_updated_by': staff_profile.id, })
            if stock_transaction_serializer.is_valid():
                new_stock_transaction = stock_transaction_serializer.save()
                affected_product_ids = set()
                for stock_transaction in stock_transaction_instance_list:
                    product_id = int(stock_transaction["product_id"])
                    quantity = stock_transaction["quantity"]
                    affected_product_ids.add(product_id)
                    stock_transaction_instance_serializer = StockTransactionInstanceSerializer(
                        data={'stock_transaction': new_stock_transaction.id, 'product': product_id, 'quantity': quantity, })
                    if stock_transaction_instance_serializer.is_valid():
                        stock_transaction_instance_serializer.save()
                        subject_warehouse = Warehouse.objects.get(
                            id=source_warehouse_id)
                        product_instance = Product.objects.get(
                            id=product_id)
                        subject_inventory = Inventory.objects.get(
                            warehouse=subject_warehouse, product=product_instance)
                        apply_inventory_delta(
                            subject_inventory, -float(quantity),
                            movement_type="requisition_issue", staff=staff_profile,
                            reference=new_stock_transaction.stock_transaction_number,
                            stock_transaction=new_stock_transaction,
                            notes=transaction_description)
                requisition_instance = StockRequisition.objects.get(
                    id=stock_requisition_id)
                requisition_instance.stock_requisition_approved = True
                requisition_instance.save()
                gl_posting = post_goods_delivery(
                    staff_profile.company_branch,
                    staff_profile,
                    new_stock_transaction,
                )
                # Inventory levels changed — broadcast new availability to the CRM.
                notify_crm_stock(affected_product_ids)
                return Response({
                    "message": "Stock transaction created successfully",
                    "gl_posting": gl_posting,
                }, status=200)
            else:
                print(stock_transaction_serializer.errors)
                return Response({"message": "Unable to create stock transaction", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error creating stock transaction", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def generate_purchase_requisition(request):
    date_format = '%d/%m/%Y'
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    warehouse_id = int(request.data["warehouse_id"])
    # print(settings.TIME_ZONE)
    # print(timezone.localtime(timezone.now()))
    # print(timezone.get_current_timezone())
    payload = {}
    try:
        start_date = datetime.strptime(
            request.data["start_date"], date_format) if len(request.data["start_date"]) > 0 else None
        end_date = datetime.strptime(
            request.data["end_date"], date_format) if len(request.data["end_date"]) > 0 else None
        # utc_tz = timezone('UTC')
        if start_date is not None:
            start_date = start_date.date()
            # start_date = timezone.make_aware(
            #     start_date, timezone.get_default_timezone())
            start_date = datetime.combine(start_date, datetime.min.time())
            # print(start_date)
            start_date = start_date.astimezone(pytz.utc)
        if end_date is not None:
            end_date = end_date.date() + timedelta(days=1)
            # end_date = timezone.make_aware(end_date, timezone.get_default_timezone())
            end_date = datetime.combine(end_date, datetime.min.time())
            end_date = end_date.astimezone(pytz.utc)
        # end_date = end_date + timedelta(days=1)
        # loop through warehouse product inventories and outbound stock transactions
        # get warehouse
        warehouse_instance = Warehouse.objects.get(id=int(warehouse_id))
        # all_outbound_stock_transactions = warehouse_instance.source_warehouse_stock_transactions.filter(created_on__range=(start_date, end_date))
        all_outbound_stock_transactions = warehouse_instance.source_warehouse_stock_transactions.all()
        generated_purchase_requisition_list = []

        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.purchase_requisition.add"):
            for out_bound_stock in all_outbound_stock_transactions:
                # loop through all stock_transaction_instances
                if out_bound_stock.recycle_bin != True and out_bound_stock.created_on >= start_date and out_bound_stock.created_on < end_date:
                    stock_transaction_instances = out_bound_stock.stock_transaction_instances.all()
                    for transaction_instance in stock_transaction_instances:
                        # check to see if the product instance is already added to the purchase requisition list
                        product_exist = False
                        for purchase_requisition in generated_purchase_requisition_list:
                            if transaction_instance.product.id == purchase_requisition["product_id"]:
                                purchase_requisition["quantity"] = str(
                                    float(purchase_requisition["quantity"])+float(transaction_instance.quantity))
                                product_exist = True
                                break
                        if product_exist != True:
                            requisition_map = {}
                            requisition_map["product_id"] = str(
                                transaction_instance.product.id)
                            requisition_map["product_name"] = transaction_instance.product.product_name
                            requisition_map["product_category"] = transaction_instance.product.category.category_name
                            requisition_map["quantity"] = transaction_instance.quantity
                            requisition_map["unit_of_measurement"] = transaction_instance.product.unit_of_measurement
                            generated_purchase_requisition_list.append(
                                requisition_map)
            # finished
            payload["generated_purchase_requisition_list"] = generated_purchase_requisition_list
            return Response({"message": "true", "payload": payload}, status=200)
        else:
            return Response({"message": "false", "payload": payload}, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def generate_reorder_requisition(request):
    """Suggest purchase-requisition lines from REORDER LEVELS — the logical basis
    for a requisition. Returns every product in the warehouse whose available
    stock (on-hand minus active reservations) has fallen to/below its minimum
    stock level, with a suggested quantity to bring it back up to that level.

    Unlike the date-range generator, this answers "what do we actually need to
    re-order right now?" rather than "what left the store between two dates".
    """
    company_serial_number = request.data["serial_number"]
    warehouse_id = int(request.data["warehouse_id"])
    active_user = request.user
    payload = {}
    suggestions = []
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if not user_has_permission(staff_profile, "warehouse.purchase_requisition.add"):
            return Response({"message": "false", "payload": payload}, status=401)

        warehouse_instance = Warehouse.objects.get(id=warehouse_id)
        expire_reservations_for_warehouse(warehouse_instance)
        inventories = warehouse_instance.warehouse_inventories.filter(
            recycle_bin=False).select_related("product", "product__category")
        for inventory in inventories:
            product = inventory.product
            if product is None:
                continue
            try:
                minimum = float(inventory.minimum_stock_level or 0)
            except (TypeError, ValueError):
                minimum = 0.0
            if minimum <= 0:
                # No reorder point configured → can't say it's "needed".
                continue
            try:
                current = float(inventory.quantity or 0)
            except (TypeError, ValueError):
                current = 0.0
            reserved = get_reserved_qty(product, warehouse_instance)
            available = max(0.0, current - reserved)
            if available > minimum:
                continue
            # Reorder enough to climb back to the minimum (at least 1 unit).
            shortfall = minimum - available
            suggested = shortfall if shortfall > 0 else minimum
            suggestions.append({
                "product_id": str(product.id),
                "product_name": product.product_name,
                "product_category": product.category.category_name if product.category else "",
                "unit_of_measurement": product.unit_of_measurement,
                "current_quantity": str(current),
                "reserved_quantity": str(reserved),
                "available_quantity": str(available),
                "minimum_stock_level": str(minimum),
                "suggested_quantity": str(suggested),
                # Kept for backwards-compatible consumers of the old generator.
                "quantity": str(suggested),
            })
        # Most urgent (largest shortfall vs minimum) first.
        suggestions.sort(
            key=lambda s: float(s["minimum_stock_level"]) - float(s["available_quantity"]),
            reverse=True,
        )
        payload["generated_purchase_requisition_list"] = suggestions
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_purchase_requisition(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    warehouse_id = int(request.data["warehouse_id"])
    purchase_requisition_description = request.data["purchase_requisition_description"]
    specifications = request.data.get("specifications", "")
    documents_list_raw = request.data.get("documents_list", "[]")
    purchase_requisition_instance_list = request.data.get(
        'purchase_requisition_instance_list', [])
    purchase_requisition_instance_list = json.loads(
        purchase_requisition_instance_list)
    if isinstance(documents_list_raw, str):
        documents_list = json.loads(documents_list_raw or "[]")
    else:
        documents_list = documents_list_raw or []
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if can_create_purchase_requisition(staff_profile, company_profile):
            purchase_requisition_serializer = PurchaseRequisitionSerializer(
                data={
                    'warehouse': warehouse_id,
                    'purchase_requisition_description': purchase_requisition_description,
                    'specifications': specifications,
                    'created_by': staff_profile.id,
                    'last_updated_by': staff_profile.id,
                })
            if purchase_requisition_serializer.is_valid():
                new_purchase_requisition = purchase_requisition_serializer.save()
                for purchase_requisition_instance in purchase_requisition_instance_list:
                    product_id = int(
                        purchase_requisition_instance["product_id"])
                    quantity = purchase_requisition_instance["quantity"]
                    purchase_requisition_instance_serializer = PurchaseRequisitionInstanceSerializer(
                        data={'purchase_requisition': new_purchase_requisition.id, 'product': product_id, 'quantity': quantity, 'created_by': staff_profile.id,
                              'last_updated_by': staff_profile.id, })
                    if purchase_requisition_instance_serializer.is_valid():
                        purchase_requisition_instance_serializer.save()
                for document in documents_list:
                    PurchaseRequisitionDocument.objects.create(
                        purchase_requisition=new_purchase_requisition,
                        document_name=document.get('document_name', ''),
                        document_type=document.get('document_type', 'supporting'),
                        content_type=document.get('content_type', 'application/octet-stream'),
                        file_data_base64=document.get('file_data_base64', ''),
                        uploaded_by=staff_profile,
                    )
                return Response({"message": "Purchase requisition created successfully", }, status=200)
            else:
                print(purchase_requisition_serializer.errors)
                return Response({"message": "Unable to create purchase requisition", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error creating purchase requisition", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def approve_purchase_requisition(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    purchase_requisition_id = request.data["purchase_requisition_id"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if can_approve_purchase_requisition(staff_profile, company_profile):
            purchase_requisition = PurchaseRequisition.objects.get(
                id=int(purchase_requisition_id), recycle_bin=False)
            purchase_requisition.purchase_requisition_approved = True
            purchase_requisition.purchase_requisition_rejected = False
            purchase_requisition.rejection_reason = ''
            purchase_requisition.approved_by = staff_profile
            purchase_requisition.last_updated_by = staff_profile
            purchase_requisition.save()
            if purchase_requisition.created_by is not None:
                create_notifications(
                    f"Purchase Requisition {purchase_requisition.purchase_requisition_number} Approved",
                    f"Your purchase requisition {purchase_requisition.purchase_requisition_number} has been approved.",
                    [purchase_requisition.created_by.id],
                )
            return Response({"message": "Purchase requisition approved successfully", }, status=200)
        return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except PurchaseRequisition.DoesNotExist:
        return Response({"message": "Purchase requisition not found", }, status=404)
    except:
        return Response({"message": "Error approving purchase requisition", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def reject_purchase_requisition(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    purchase_requisition_id = request.data["purchase_requisition_id"]
    rejection_reason = request.data.get("rejection_reason", "")
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if can_approve_purchase_requisition(staff_profile, company_profile):
            purchase_requisition = PurchaseRequisition.objects.get(
                id=int(purchase_requisition_id), recycle_bin=False)
            purchase_requisition.purchase_requisition_approved = False
            purchase_requisition.purchase_requisition_rejected = True
            purchase_requisition.rejection_reason = rejection_reason
            purchase_requisition.approved_by = None
            purchase_requisition.last_updated_by = staff_profile
            purchase_requisition.save()
            if purchase_requisition.created_by is not None:
                create_notifications(
                    f"Purchase Requisition {purchase_requisition.purchase_requisition_number} Rejected",
                    f"Your purchase requisition {purchase_requisition.purchase_requisition_number} was rejected. Reason: {rejection_reason or 'Not specified'}.",
                    [purchase_requisition.created_by.id],
                )
            return Response({"message": "Purchase requisition rejected successfully", }, status=200)
        return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except PurchaseRequisition.DoesNotExist:
        return Response({"message": "Purchase requisition not found", }, status=404)
    except:
        return Response({"message": "Error rejecting purchase requisition", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_purchase_requisition_document(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    purchase_requisition_id = request.data["purchase_requisition_id"]
    document_name = request.data.get("document_name", "")
    document_type = request.data.get("document_type", "supporting")
    content_type = request.data.get("content_type", "application/octet-stream")
    file_data_base64 = request.data.get("file_data_base64", "")
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if can_create_purchase_requisition(staff_profile, company_profile):
            purchase_requisition = PurchaseRequisition.objects.get(
                id=int(purchase_requisition_id), recycle_bin=False)
            PurchaseRequisitionDocument.objects.create(
                purchase_requisition=purchase_requisition,
                document_name=document_name,
                document_type=document_type,
                content_type=content_type,
                file_data_base64=file_data_base64,
                uploaded_by=staff_profile,
            )
            return Response({"message": "Document uploaded successfully", }, status=200)
        return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except PurchaseRequisition.DoesNotExist:
        return Response({"message": "Purchase requisition not found", }, status=404)
    except:
        return Response({"message": "Error uploading document", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_purchase_requisition_document(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    document_id = request.data["document_id"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if can_create_purchase_requisition(staff_profile, company_profile):
            document = PurchaseRequisitionDocument.objects.get(
                id=int(document_id), recycle_bin=False)
            document.recycle_bin = True
            document.save()
            return Response({"message": "Document deleted successfully", }, status=200)
        return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except PurchaseRequisitionDocument.DoesNotExist:
        return Response({"message": "Document not found", }, status=404)
    except:
        return Response({"message": "Error deleting document", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_stock_requisition(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    warehouse_id = int(request.data["warehouse_id"])
    stock_requisition_description = request.data["stock_requisition_description"]
    stock_requisition_instance_list = request.data.get(
        'stock_requisition_instance_list', [])
    stock_requisition_instance_list = json.loads(
        stock_requisition_instance_list)
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.company_branch.main_branch != True and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.stock_requisition.add"):
            stock_requisition_serializer = StockRequisitionSerializer(
                data={'warehouse': warehouse_id, 'stock_requisition_description': stock_requisition_description, 'created_by': staff_profile.id,
                      'last_updated_by': staff_profile.id, })
            if stock_requisition_serializer.is_valid():
                new_stock_requisition = stock_requisition_serializer.save()
                for stock_requisition_instance in stock_requisition_instance_list:
                    product_id = int(stock_requisition_instance["product_id"])
                    quantity = stock_requisition_instance["quantity"]
                    stock_requisition_instance_serializer = StockRequisitionInstanceSerializer(
                        data={'stock_requisition': new_stock_requisition.id, 'product': product_id, 'quantity': quantity, 'created_by': staff_profile.id,
                              'last_updated_by': staff_profile.id, })
                    if stock_requisition_instance_serializer.is_valid():
                        stock_requisition_instance_serializer.save()
                return Response({"message": "Stock requisition created successfully", }, status=200)
            else:
                return Response({"message": "Unable to create stock requisition", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error creating stock requisition", }, status=500)

# edit views


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def edit_equipment(request):
    active_user = request.user
    warehouse_id = request.data["warehouse_id"]
    company_serial_number = request.data["serial_number"]
    equipment_id_to_edit = request.data["equipment_id_to_edit"]
    equipment_name = request.data["equipment_name"]
    equipment_serial_number = request.data["equipment_serial_number"]
    equipment_description = request.data["equipment_description"]
    status = request.data["status"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.equipment.edit"):
            equipment_instance = Equipment.objects.get(
                id=int(equipment_id_to_edit))
            equipment_serializer = EquipmentSerializer(instance=equipment_instance,
                                                       data={'warehouse': int(warehouse_id), 'equipment_name': equipment_name, 'equipment_serial_number': equipment_serial_number, 'equipment_description': equipment_description, 'status': status, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if equipment_serializer.is_valid():
                equipment_serializer.save()
                return Response({"message": "Equipment edited successfully", }, status=200)
            else:
                return Response({"message": "Unable to edited equipment", }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:
        return Response({"message": "Error editing equipment", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_equipment(request):
    active_user = request.user
    equipment_id_to_edit = request.data["equipment_id_to_edit"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.equipment.delete"):
            equipment_instance = Equipment.objects.get(
                id=int(equipment_id_to_edit))
            if equipment_instance.recycle_bin != True:
                equipment_instance.recycle_bin = True
                equipment_instance.save()
                return Response({"message": "Equipment deleted successfully", }, status=200)
            else:
                equipment_instance.delete()
                return Response({"message": "You have permanently deleted equipment successfully", }, status=200)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error deleting equipment", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def edit_product_categories(request):
    active_user = request.user
    category_id_to_edit = request.data["category_id_to_edit"]
    category_name = request.data["category_name"]
    category_description = request.data["category_description"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.company_branch.main_branch == True and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.product_category.edit"):
            category_instance = Category.objects.get(
                id=int(category_id_to_edit))
            category_serializer = CategorySerializer(instance=category_instance,
                                                     data={'category_name': category_name, 'category_description': category_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if category_serializer.is_valid():
                category_serializer.save()
                return Response({"message": "Product category edited successfully", }, status=200)
            else:
                error_messages = [str(value[0])
                                  for value in category_serializer.errors.values()]
                return Response({"message": error_messages[0], }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error editing product category", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_product_category(request):
    active_user = request.user
    category_id_to_edit = request.data["category_id_to_edit"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product_category.delete"):
            category_instance = Category.objects.get(
                id=int(category_id_to_edit))
            if category_instance.recycle_bin != True:
                category_instance.recycle_bin = True
                category_instance.save()
                return Response({"message": "Product category deleted successfully", }, status=200)
            else:
                category_instance.delete()
                return Response({"message": "You have permanently deleted product category successfully", }, status=200)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error deleting category", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_product(request):
    active_user = request.user
    product_id_to_edit = request.data["product_id_to_edit"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product.delete"):
            product_instance = Product.objects.get(
                id=int(product_id_to_edit))
            product_inventories = product_instance.product_inventories.all()
            if product_instance.recycle_bin != True:
                product_instance.recycle_bin = True
                for product_inventory in product_inventories:
                    if product_inventory.recycle_bin != True:
                        product_inventory.recycle = True
                        product_inventory.save()
                product_instance.save()
                return Response({"message": "Product deleted successfully", }, status=200)
            else:
                for product_inventory in product_inventories:
                    product_inventory.delete()
                product_instance.delete()
                return Response({"message": "You have permanently deleted product successfully", }, status=200)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error deleting product", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_product_image(request):
    active_user = request.user
    image_id = request.data["image_id"]
    company_serial_number = request.data["serial_number"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product.edit"):
            image_to_delete = ProductImageCatalogue.objects.get(
                id=int(image_id))
            image_to_delete.delete()
            return Response({"message": "Product image deleted successfully", }, status=200)
        else:
            return Response({"message": "You are unathorised to perform this action", }, status=401)
    except:
        return Response({"message": "Error deleting product image", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def edit_product(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    product_id_to_edit = request.data["product_id_to_edit"]
    category_id = request.data["category_id"]
    product_name = request.data["product_name"]
    stock_keeping_unit = request.data["stock_keeping_unit"]
    unit_of_measurement = request.data["unit_of_measurement"]
    product_description = request.data["product_description"]
    # minimum_stock_level = request.data["minimum_stock_level"]
    # quantity = request.data["quantity"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.product.edit"):
            product_instance = Product.objects.get(id=int(product_id_to_edit))
            product_serializer = ProductSerializer(instance=product_instance,
                                                   data={'category': int(category_id), 'product_name': product_name, 'unit_of_measurement': unit_of_measurement, 'product_description': product_description, 'created_by': staff_profile.id, 'last_updated_by': staff_profile.id})
            if product_serializer.is_valid():
                new_product = product_serializer.save()

                if len(stock_keeping_unit) > 0:
                    new_product.stock_keeping_unit = stock_keeping_unit
                    new_product.save()

                    # new_product_inventory = Inventory.objects.get_or_create(product=new_product.id,warehouse=)
                return Response({"message": "Product edited successfully", }, status=200)
            else:
                error_messages = [str(value[0])
                                  for value in product_serializer.errors.values()]
                return Response({"message": error_messages[0], }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error editing product", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def edit_inventory(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    inventory_id_to_edit = request.data["inventory_id_to_edit"]
    quantity = request.data["quantity"]
    minimum_stock_level = request.data["minimum_stock_level"]
    inventory_description = request.data["inventory_description"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.inventory.edit"):
            inventory_instance = Inventory.objects.get(
                id=int(inventory_id_to_edit))
            quantity_before = inventory_instance.quantity
            inventory_serializer = InventorySerializer(instance=inventory_instance, data={
                                                       "quantity": quantity, "minimum_stock_level": minimum_stock_level, "inventory_description": inventory_description, "last_updated_by": staff_profile.id})
            if inventory_serializer.is_valid():
                inventory_serializer.save()
                # Log the manual on-hand correction in the stock ledger.
                inventory_instance.refresh_from_db()
                log_inventory_set(
                    inventory_instance, quantity_before,
                    movement_type="adjustment", staff=staff_profile,
                    reference="Manual inventory edit",
                    notes=inventory_description)
                # On-hand quantity changed — broadcast new availability to the CRM.
                if inventory_instance.product_id:
                    notify_crm_stock([inventory_instance.product_id])
                return Response({"message": "Product inventory updated successfully", }, status=200)
            else:
                error_messages = [str(value[0])
                                  for value in inventory_serializer.errors.values()]
                return Response({"message": error_messages[0], }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except:  # Exception as e:
        # print(e)
        return Response({"message": "Error updating product inventory", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def delete_stock_transaction(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    stock_transaction_to_edit = request.data["stock_transaction_to_edit"]
    try:
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if user_has_permission(staff_profile, "warehouse.stock_transaction.delete"):
            stock_transaction_instance = StockTransaction.objects.get(
                id=int(stock_transaction_to_edit))
            if stock_transaction_instance.recycle_bin != True:
                stock_transaction_instance.recycle_bin = True
                stock_transaction_instances = stock_transaction_instance.stock_transaction_instances.all().order_by("-id")
                reversal_reference = f"Reversal of {stock_transaction_instance.stock_transaction_number}"
                for transaction_instance in stock_transaction_instances:
                    if stock_transaction_instance.transaction_type == "in_bound":
                        # get the inventory
                        subject_warehouse = stock_transaction_instance.recipient_warehouse
                        subject_inventory = Inventory.objects.get(
                            warehouse=subject_warehouse, product=transaction_instance.product)
                        apply_inventory_delta(
                            subject_inventory, -float(transaction_instance.quantity),
                            movement_type="reversal", staff=staff_profile,
                            reference=reversal_reference, clamp_zero=True)
                    if stock_transaction_instance.transaction_type == "out_bound":
                        # get the inventory
                        subject_warehouse = stock_transaction_instance.source_warehouse
                        subject_inventory = Inventory.objects.get(
                            warehouse=subject_warehouse, product=transaction_instance.product)
                        apply_inventory_delta(
                            subject_inventory, float(transaction_instance.quantity),
                            movement_type="reversal", staff=staff_profile,
                            reference=reversal_reference)
                stock_transaction_instance.save()
                # Reversing a transaction changed inventory — broadcast to the CRM.
                reverted_product_ids = [
                    ti.product_id
                    for ti in stock_transaction_instance.stock_transaction_instances.all()
                    if ti.product_id
                ]
                notify_crm_stock(reverted_product_ids)
                return Response({"message": "Stock transaction deleted successfully", }, status=200)
            else:
                stock_transaction_instance.delete()
                return Response({"message": "You have permanently deleted stock transaction successfully", }, status=200)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except Exception as e:
        print(e)
        return Response({"message": "Error deleting stock transaction", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def edit_stock_transaction(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    stock_transaction_id_to_edit = request.data["stock_transaction_id_to_edit"]
    transaction_description = request.data["transaction_description"]
    stock_transaction_reference = request.data["stock_transaction_reference"]
    try:
        staff_profile = StaffProfile.objects.get(user=active_user)
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.stock_transaction.edit"):
            stock_transaction_instance = StockTransaction.objects.get(
                id=int(stock_transaction_id_to_edit))
            stock_transaction_serializer = StockTransactionSerializer(instance=stock_transaction_instance,
                                                                      data={'transaction_description': transaction_description, 'stock_transaction_reference': stock_transaction_reference,
                                                                            'last_updated_by': staff_profile.id, })
            if stock_transaction_serializer.is_valid():
                stock_transaction_serializer.save()
                return Response({"message": "Stock transaction updated successfully", }, status=200)
            else:
                error_messages = [str(value[0])
                                  for value in stock_transaction_serializer.errors.values()]
                return Response({"message": error_messages[0], }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except Exception as e:
        print(e)
        return Response({"message": "Error updating product inventory", }, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def add_image(request):
    active_user = request.user
    company_serial_number = request.data["serial_number"]
    product_id = request.data["product_id"]
    image_attribute = request.data["image_attribute"]
    use_as_main_image = request.data["use_as_main_image"]
    product_image = request.data["product_image"]
    try:
        staff_profile = StaffProfile.objects.get(user=active_user)
        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        # if staff_profile.company_department.department_name == "warehouse_management" and staff_profile.is_head_of_department == True and staff_profile.has_read_write_priviledges == True and company_profile:
        if user_has_permission(staff_profile, "warehouse.product.edit"):
            serializer = ProductImageCatalogueSerializer(data={'product': int(
                product_id), 'product_image': product_image, 'image_attribute': image_attribute, 'use_as_main_image': True if use_as_main_image == "true" else False})
            if serializer.is_valid():
                serializer.save()
                return Response({"message": "Product image added successfully", }, status=200)
            else:
                error_messages = [str(value[0])
                                  for value in serializer.errors.values()]
                return Response({"message": error_messages[0], }, status=406)
        else:
            return Response({"message": "You are unauthorised to perform this action", }, status=401)
    except Exception as e:
        print(e)
        return Response({"message": "Error adding product image", }, status=500)


from .stock_availability import (
    expire_reservations_for_warehouse,
    get_inventory_totals,
    get_reserved_qty,
)
from .stock_ledger import apply_inventory_delta, log_inventory_set

from megawatt_api.stock_webhook import notify_crm_stock


def _reservation_product_ids(reservation):
    """Product ids on a reservation, for the CRM stock broadcast."""
    ids = []
    for inst in reservation.reservation_instances.all():
        if inst.product_id:
            ids.append(inst.product_id)
    return ids

@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def create_stock_reservation(request):
    """Sales staff request a product reservation."""
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    reservation_description = request.data.get("reservation_description", "")
    expiry_days = int(request.data.get("expiry_days", 7))
    items = request.data["items"]  # list of {product_id, quantity}
    active_user = request.user
    try:
        from django.utils import timezone as tz
        company_profile = CompanyProfile.objects.get(company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        department_name = (
            staff_profile.company_department.department_name
            if staff_profile.company_department is not None else ''
        )
        if not user_has_permission(staff_profile, "warehouse.stock_reservation.add"):
            return Response({"message": "You are not authorised to perform this action"}, status=401)
        warehouse = Warehouse.objects.get(id=int(warehouse_id))
        expire_reservations_for_warehouse(warehouse)
        # Validate every item has sufficient available stock before creating anything
        for item in items:
            product = Product.objects.get(id=int(item["product_id"]))
            requested = float(item["quantity"])
            try:
                inventory = Inventory.objects.get(product=product, warehouse=warehouse)
                available = max(0.0, float(inventory.quantity) - get_reserved_qty(product, warehouse))
            except Inventory.DoesNotExist:
                return Response({"message": f"No inventory record for '{product.product_name}' in this warehouse."}, status=406)
            if requested > available:
                return Response({
                    "message": (
                        f"Insufficient stock for '{product.product_name}'. "
                        f"Available: {available}, Requested: {requested}"
                    )
                }, status=406)
        expiry_date = tz.now() + timedelta(days=expiry_days)
        reservation = StockReservation.objects.create(
            warehouse=warehouse,
            reservation_description=reservation_description,
            expiry_date=expiry_date,
            requested_by=staff_profile,
            created_by=staff_profile,
            last_updated_by=staff_profile,
        )
        for item in items:
            product = Product.objects.get(id=int(item["product_id"]))
            StockReservationInstance.objects.create(
                stock_reservation=reservation,
                product=product,
                quantity_requested=str(item["quantity"]),
            )
        # Notify the warehouse team so they can review and action the request.
        try:
            _notify_warehouse_team_of_reservation(reservation, company_profile, staff_profile, len(items))
        except Exception as notify_error:
            print(notify_error)
        # A pending reservation does not yet hold stock (only approved ones do),
        # but broadcast so the CRM reflects any concurrent changes consistently.
        notify_crm_stock(_reservation_product_ids(reservation))
        return Response({"message": "Stock reservation created successfully",
                         "reservation_number": reservation.reservation_number}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "Error creating stock reservation"}, status=500)


def _notify_warehouse_team_of_reservation(reservation, company_profile, requester, item_count):
    """Alert the warehouse team that a staff member has requested a reservation."""
    warehouse_staff = StaffProfile.objects.filter(
        company_department__department_name="warehouse_management",
        company_branch__company_profile=company_profile,
        recycle_bin=False,
        is_profile_active=True,
    )
    warehouse_staff_ids = [s.id for s in warehouse_staff]
    if not warehouse_staff_ids:
        return
    requester_name = (
        f"{requester.first_name} {requester.last_name}".strip()
        if requester else "A staff member"
    )
    warehouse_name = reservation.warehouse.warehouse_name if reservation.warehouse else "a warehouse"
    create_notifications(
        f"Stock Reservation Request {reservation.reservation_number}",
        (
            f"{requester_name} has requested a stock reservation of {item_count} item(s) at "
            f"{warehouse_name}. Purpose: {reservation.reservation_description or 'N/A'}. "
            f"Please review and approve or reject it."
        ),
        warehouse_staff_ids,
    )


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def action_on_stock_reservation(request):
    """Warehouse staff approve or reject a pending reservation."""
    company_serial_number = request.data["serial_number"]
    reservation_id = request.data["reservation_id"]
    action = request.data["action"]  # "approve" or "reject"
    rejection_reason = request.data.get("rejection_reason", "")
    active_user = request.user
    try:
        company_profile = CompanyProfile.objects.get(company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        if not user_has_permission(staff_profile, "warehouse.stock_reservation.approve"):
            return Response({"message": "You are not authorised to perform this action"}, status=401)
        reservation = StockReservation.objects.get(id=int(reservation_id), recycle_bin=False)
        if reservation.status != "pending":
            return Response({"message": f"Reservation is already {reservation.status}"}, status=406)
        if action == "approve":
            expire_reservations_for_warehouse(reservation.warehouse)
            for inst in reservation.reservation_instances.all():
                if inst.product is None:
                    continue
                try:
                    inventory = Inventory.objects.get(
                        product=inst.product, warehouse=reservation.warehouse)
                except Inventory.DoesNotExist:
                    return Response({"message": f"No inventory record for {inst.product.product_name}"}, status=406)
                available = float(inventory.quantity) - get_reserved_qty(inst.product, reservation.warehouse)
                if float(inst.quantity_requested) > available:
                    return Response({"message": f"Insufficient stock for {inst.product.product_name}. "
                                                f"Available: {available}, Requested: {inst.quantity_requested}"}, status=406)
            reservation.status = "approved"
            reservation.approved_or_rejected_by = staff_profile
            reservation.last_updated_by = staff_profile
            reservation.save()
            # Close the loop back to the requester.
            if reservation.requested_by is not None:
                try:
                    create_notifications(
                        f"Reservation {reservation.reservation_number} Approved",
                        (
                            f"Your stock reservation {reservation.reservation_number} has been approved. "
                            f"The stock is now held for you until {reservation.expiry_date:%d/%m/%Y %H:%M}."
                            if reservation.expiry_date else
                            f"Your stock reservation {reservation.reservation_number} has been approved."
                        ),
                        [reservation.requested_by.id],
                    )
                except Exception as notify_error:
                    print(notify_error)
            # Approval now holds stock — broadcast the reduced availability.
            notify_crm_stock(_reservation_product_ids(reservation))
            return Response({"message": "Reservation approved successfully"}, status=200)
        elif action == "reject":
            reservation.status = "rejected"
            reservation.rejection_reason = rejection_reason
            reservation.approved_or_rejected_by = staff_profile
            reservation.last_updated_by = staff_profile
            reservation.save()
            # Close the loop back to the requester.
            if reservation.requested_by is not None:
                try:
                    create_notifications(
                        f"Reservation {reservation.reservation_number} Rejected",
                        (
                            f"Your stock reservation {reservation.reservation_number} was rejected. "
                            f"Reason: {rejection_reason or 'Not specified'}."
                        ),
                        [reservation.requested_by.id],
                    )
                except Exception as notify_error:
                    print(notify_error)
            notify_crm_stock(_reservation_product_ids(reservation))
            return Response({"message": "Reservation rejected successfully"}, status=200)
        else:
            return Response({"message": "Invalid action. Use 'approve' or 'reject'"}, status=406)
    except Exception as e:
        print(e)
        return Response({"message": "Error processing reservation action"}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def fulfill_or_release_stock_reservation(request):
    """Mark an approved reservation as fulfilled (stock picked up) or manually released."""
    company_serial_number = request.data["serial_number"]
    reservation_id = request.data["reservation_id"]
    action = request.data["action"]  # "fulfill" or "release"
    active_user = request.user
    try:
        company_profile = CompanyProfile.objects.get(company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        department_name = (
            staff_profile.company_department.department_name
            if staff_profile.company_department is not None else ''
        )
        if not user_has_permission(staff_profile, "warehouse.stock_reservation.edit"):
            return Response({"message": "You are not authorised to perform this action"}, status=401)
        reservation = StockReservation.objects.get(id=int(reservation_id), recycle_bin=False)
        if reservation.status != "approved":
            return Response({"message": f"Only approved reservations can be fulfilled or released"}, status=406)
        if action == "fulfill":
            reservation.status = "fulfilled"
        elif action == "release":
            reservation.status = "released"
        else:
            return Response({"message": "Invalid action. Use 'fulfill' or 'release'"}, status=406)
        reservation.last_updated_by = staff_profile
        reservation.save()
        # Releasing frees stock; fulfilling converts a held reservation — both
        # change effective availability, so broadcast to the CRM.
        notify_crm_stock(_reservation_product_ids(reservation))
        return Response({"message": f"Reservation {action}ed successfully"}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "Error processing reservation"}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def get_stock_movements(request):
    """Stock ledger — paginated audit log of on-hand quantity changes, filterable
    by warehouse, product, and movement type. Every receipt, issue, requisition
    issue, manual adjustment, and reversal is recorded here."""
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data.get("warehouse_id", "")
    product_id = request.data.get("product_id", "")
    movement_type = request.data.get("movement_type", "all")
    page_index = request.data.get("pageIndex", "1")
    active_user = request.user
    target_timezone = pytz.timezone('Africa/Nairobi')
    payload = {}
    movement_list = []
    try:
        from .models import StockMovement

        company_profile = CompanyProfile.objects.get(
            company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)

        qs = StockMovement.objects.select_related(
            "product", "warehouse", "created_by", "stock_transaction")
        if warehouse_id:
            qs = qs.filter(warehouse_id=int(warehouse_id))
        if product_id:
            qs = qs.filter(product_id=int(product_id))
        if movement_type and movement_type != "all":
            qs = qs.filter(movement_type=movement_type)
        qs = qs.order_by("-created_on", "-id")

        try:
            page = max(1, int(page_index))
        except (TypeError, ValueError):
            page = 1
        page_size = 50
        total_count = qs.count()
        start = (page - 1) * page_size
        end = start + page_size

        for mv in qs[start:end]:
            row = {}
            row["movement_id"] = str(mv.id)
            row["movement_type"] = mv.movement_type
            row["movement_type_label"] = mv.get_movement_type_display()
            row["product_id"] = str(mv.product_id) if mv.product_id else ""
            row["product_name"] = mv.product.product_name if mv.product else ""
            row["unit_of_measurement"] = mv.product.unit_of_measurement if mv.product else ""
            row["warehouse_id"] = str(mv.warehouse_id) if mv.warehouse_id else ""
            row["warehouse_name"] = mv.warehouse.warehouse_name if mv.warehouse else ""
            row["quantity_before"] = str(mv.quantity_before)
            row["quantity_change"] = str(mv.quantity_change)
            row["quantity_after"] = str(mv.quantity_after)
            row["reference"] = mv.reference
            row["notes"] = mv.notes
            row["created_by"] = (
                f'{mv.created_by.first_name} {mv.created_by.last_name}'
                if mv.created_by else "")
            row["created_on"] = datetime.strftime(
                mv.created_on.astimezone(target_timezone), date_format) if mv.created_on else ""
            movement_list.append(row)

        payload["movement_list"] = movement_list
        payload["total_count"] = str(total_count)
        payload["page"] = str(page)
        payload["page_size"] = str(page_size)
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def get_stock_reservations(request):
    """List stock reservations for a warehouse with optional status filter."""
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    status_filter = request.data.get("status", "all")
    page_index = request.data.get("pageIndex", "1")
    active_user = request.user
    target_timezone = pytz.timezone('Africa/Nairobi')
    payload = {}
    reservation_list = []
    try:
        warehouse = Warehouse.objects.get(id=int(warehouse_id))
        expire_reservations_for_warehouse(warehouse)
        qs = StockReservation.objects.filter(warehouse=warehouse, recycle_bin=False).order_by("-id")
        if status_filter != "all":
            qs = qs.filter(status=status_filter)
        start_index_map = {"1": (0, 50), "2": (50, 100), "3": (100, 150), "4": (150, 200)}
        start, end = start_index_map.get(str(page_index), (0, 50))
        total_count = qs.count()
        reservations = qs[start:end]
        for res in reservations:
            res_map = {}
            res_map["reservation_id"] = str(res.id)
            res_map["reservation_number"] = res.reservation_number
            res_map["reservation_description"] = res.reservation_description
            res_map["status"] = res.status
            res_map["expiry_date"] = datetime.strftime(
                res.expiry_date.astimezone(target_timezone), date_format) if res.expiry_date else ""
            res_map["rejection_reason"] = res.rejection_reason
            res_map["created_on"] = datetime.strftime(
                res.created_on.astimezone(target_timezone), date_format) if res.created_on else ""
            requested_by_map = {}
            if res.requested_by:
                requested_by_map["staff_id"] = str(res.requested_by.id)
                requested_by_map["staff_name"] = f'{res.requested_by.first_name} {res.requested_by.last_name}'
                requested_by_map["staff_position"] = res.requested_by.staff_position.position_title if res.requested_by.staff_position else ""
            res_map["requested_by"] = requested_by_map
            handled_by_map = {}
            if res.approved_or_rejected_by:
                handled_by_map["staff_id"] = str(res.approved_or_rejected_by.id)
                handled_by_map["staff_name"] = f'{res.approved_or_rejected_by.first_name} {res.approved_or_rejected_by.last_name}'
            res_map["approved_or_rejected_by"] = handled_by_map
            instances_list = []
            for inst in res.reservation_instances.all():
                inst_map = {}
                inst_map["instance_id"] = str(inst.id)
                inst_map["product_id"] = str(inst.product.id) if inst.product else ""
                inst_map["product_name"] = inst.product.product_name if inst.product else ""
                inst_map["unit_of_measurement"] = inst.product.unit_of_measurement if inst.product else ""
                inst_map["quantity_requested"] = inst.quantity_requested
                instances_list.append(inst_map)
            res_map["reservation_instances"] = instances_list
            reservation_list.append(res_map)
        payload["reservation_list"] = reservation_list
        payload["total_count"] = str(total_count)
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def check_available_stock(request):
    """Return current, reserved, and available quantity for a product in a warehouse."""
    company_serial_number = request.data["serial_number"]
    warehouse_id = request.data["warehouse_id"]
    product_id = request.data["product_id"]
    active_user = request.user
    payload = {}
    try:
        warehouse = Warehouse.objects.get(id=int(warehouse_id))
        product = Product.objects.get(id=int(product_id))
        totals = get_inventory_totals(product, warehouse)
        payload["product_id"] = str(product.id)
        payload["product_name"] = product.product_name
        payload["unit_of_measurement"] = product.unit_of_measurement
        payload["warehouse_id"] = str(warehouse.id)
        payload["warehouse_name"] = warehouse.warehouse_name
        payload["current_quantity"] = str(totals["current_quantity"])
        payload["reserved_quantity"] = str(totals["reserved_quantity"])
        payload["available_quantity"] = str(totals["available_quantity"])
        payload["is_available"] = "true" if totals["is_available"] else "false"
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)


def _serialize_stock_reservation(res, target_timezone, date_format):
    res_map = {}
    res_map["reservation_id"] = str(res.id)
    res_map["reservation_number"] = res.reservation_number
    res_map["reservation_description"] = res.reservation_description
    res_map["status"] = res.status
    res_map["warehouse_id"] = str(res.warehouse.id) if res.warehouse is not None else ""
    res_map["warehouse_name"] = res.warehouse.warehouse_name if res.warehouse is not None else ""
    res_map["expiry_date"] = datetime.strftime(
        res.expiry_date.astimezone(target_timezone), date_format) if res.expiry_date else ""
    res_map["rejection_reason"] = res.rejection_reason
    res_map["created_on"] = datetime.strftime(
        res.created_on.astimezone(target_timezone), date_format) if res.created_on else ""
    requested_by_map = {}
    if res.requested_by:
        requested_by_map["staff_id"] = str(res.requested_by.id)
        requested_by_map["staff_name"] = f'{res.requested_by.first_name} {res.requested_by.last_name}'
        requested_by_map["staff_position"] = (
            res.requested_by.staff_position.position_title
            if res.requested_by.staff_position else ""
        )
    res_map["requested_by"] = requested_by_map
    handled_by_map = {}
    if res.approved_or_rejected_by:
        handled_by_map["staff_id"] = str(res.approved_or_rejected_by.id)
        handled_by_map["staff_name"] = (
            f'{res.approved_or_rejected_by.first_name} {res.approved_or_rejected_by.last_name}'
        )
    res_map["approved_or_rejected_by"] = handled_by_map
    instances_list = []
    for inst in res.reservation_instances.all():
        inst_map = {}
        inst_map["instance_id"] = str(inst.id)
        inst_map["product_id"] = str(inst.product.id) if inst.product else ""
        inst_map["product_name"] = inst.product.product_name if inst.product else ""
        inst_map["unit_of_measurement"] = inst.product.unit_of_measurement if inst.product else ""
        inst_map["quantity_requested"] = inst.quantity_requested
        instances_list.append(inst_map)
    res_map["reservation_instances"] = instances_list
    return res_map


@api_view(['POST'])
@authentication_classes([TokenAuthentication])
@permission_classes([IsAuthenticated])
def list_branch_stock_reservations(request):
    """List reservations across all warehouses on the staff member's branch."""
    date_format = '%d/%m/%Y, %H:%M'
    company_serial_number = request.data["serial_number"]
    status_filter = request.data.get("status", "all")
    active_user = request.user
    target_timezone = pytz.timezone('Africa/Nairobi')
    payload = {}
    reservation_list = []
    warehouse_list = []
    try:
        CompanyProfile.objects.get(company_serial_number=company_serial_number)
        staff_profile = StaffProfile.objects.get(user=active_user)
        company_branch = staff_profile.company_branch
        if company_branch is None:
            return Response({"message": "Staff branch not configured", "payload": payload}, status=406)
        department_name = (
            staff_profile.company_department.department_name
            if staff_profile.company_department is not None else ''
        )
        if not user_has_permission(staff_profile, "warehouse.stock_reservation.view"):
            return Response({"message": "You are not authorised to perform this action"}, status=401)
        branch_warehouses = company_branch.branch_warehouses.filter(recycle_bin=False).order_by("id")
        for warehouse in branch_warehouses:
            warehouse_list.append({
                "warehouse_id": str(warehouse.id),
                "warehouse_name": warehouse.warehouse_name,
            })
            expire_reservations_for_warehouse(warehouse)
            qs = StockReservation.objects.filter(
                warehouse=warehouse, recycle_bin=False,
            ).order_by("-id")
            if status_filter != "all":
                qs = qs.filter(status=status_filter)
            for res in qs[:100]:
                reservation_list.append(
                    _serialize_stock_reservation(res, target_timezone, date_format),
                )
        reservation_list.sort(key=lambda row: row.get("created_on", ""), reverse=True)
        payload["reservation_list"] = reservation_list
        payload["warehouse_list"] = warehouse_list
        payload["total_count"] = str(len(reservation_list))
        return Response({"message": "true", "payload": payload}, status=200)
    except Exception as e:
        print(e)
        return Response({"message": "false", "payload": payload}, status=500)
