# -*- coding: utf-8 -*-
from django.shortcuts import render,redirect
from django.contrib import messages
from django.forms import modelformset_factory
from django.http import JsonResponse
from django.db.models import F, Sum, Max
from django.db import transaction , models, IntegrityError

from django_filters.views import FilterView


from .forms import ReceiptForm, ReceiptItemFormSet, UploadFileForm

from .models import Receipt, ReceiptItem , Stock, Reservation, WriteOff, InvoiceWritingOffMaterial, WritingOffMaterialsItem, InvoiceMovementMaterial, MovementMaterialsItem
from project.models import ProjectItem
from custom_user.models import User
from management.models import StorageFacility, Good, Vendor
from project.models import Project
from own_production.models import OwnProduction

from .filters import GoodFilter, ReceiptItemsFilter

import pandas as pd
import json
from io import StringIO
from utils.utils import remove_spaces_from_string, split_string_into_name_and_units_of_measurement
from action_history.views import user_action_audit, changes_history

from datetime import datetime

def get_actual_availability_good_by_stock(request):
    try:
        if request.method == "POST":
            data = json.loads(request.body)
            stock_name = data.get('stockName', '')
            good_name = data.get('name', '')
            good_units = data.get('unitsOfMeasurement', '')
            if stock_name and good_name and good_units:
                good = Good.objects.get(name = good_name, unit_of_measurement = good_units)
                storage = StorageFacility.objects.get(name = stock_name)
                actual_availability = Stock.objects.filter(good_id=good.id, storage_facility_id = storage).aggregate(Sum('actual_quantity'))['actual_quantity__sum']  or 0
                return JsonResponse({'actual_availability': actual_availability})
    except Exception as error:
        raise error

def get_next_number_invoice_write_off():
    try:
        with transaction.atomic():
            max_invoice = InvoiceWritingOffMaterial.objects.select_for_update().aggregate(Max('invoice'))['invoice__max']
            
            if not max_invoice:
                next_number = 1
            else:
                next_number = int(max_invoice) + 1
            
            return next_number
        
    except Exception as error:
        raise error

def convert_date_format(input_date):
    date_object = datetime.strptime(input_date, '%b %d, %Y')
    formatted_date = date_object.strftime('%Y-%m-%d')
    return formatted_date

def getGoodFactQuantityByName(request):
    if request.method == "POST":
        data = json.loads(request.body)
        name = data.get('name', '')
        good_id = Good.objects.get(name=name)
        total_quantity = ReceiptItem.objects.filter(good_id=good_id).aggregate(Sum('quantity'))['quantity__sum'] or 0
        latest_receipt_item = ReceiptItem.objects.filter(good_id=good_id).order_by('-receipt__date').first()
        latest_price = latest_receipt_item.price if latest_receipt_item else 0
        return JsonResponse({'total_quantity': total_quantity})
    # return total_quantity, latest_price

def home (request):
    if request.method == "POST":    
        data = json.loads(request.body)
        good_name = data['data'].get('good_name')
        unit_of_measurement = data['data'].get('unitsOfMeasurement') 
        good = Good.objects.get(name = good_name, unit_of_measurement = unit_of_measurement)
        # Назва ТМЦ  	Локація	Кількість	Остання ціна	Дата	Інформація
        receipt_items = ReceiptItem.objects.filter(good = good.id).values(
            'good__name',
            'receipt__storage_facility__name',
            'quantity',
            'price',
            'created_at',
            'good',

        )
        receipt_items = list(receipt_items)
        return JsonResponse({
            'message': 'Success',
            'receipt_items':receipt_items, 
            }, safe=False)
    else:

        unique_goods_count = Stock.objects.values('good_id').distinct().count()
        reservation_unique_goods_count = Reservation.objects.values('good_id').distinct().count()
        receipt_items = ReceiptItem.objects.all().order_by('-created_at')[:5]

        receipt_items_filter = GoodFilter(request.POST, queryset=receipt_items)
        return render(request, 'stock/stock.html', context={
                                                    'receipt_items':receipt_items, 
                                                    "receipt_items_filter":receipt_items_filter, 
                                                    "unique_goods_count":unique_goods_count,
                                                    "reservation_unique_goods_count":reservation_unique_goods_count,
                                                    
                                                }) 

def receipt(request):
    try:
        storages = StorageFacility.objects.all()
        goods = Good.objects.all()
        receipts = Receipt.objects.all().order_by('-date')[:10]
        vendors = Vendor.objects.all()
        # if request.method == 'GET' and 'refresh' in request.GET:
        if request.method == 'GET':

            return render(request, 'stock/receipt.html', {"storages": storages, "goods":goods, "receipts":receipts, "vendors":vendors})
        elif request.method == 'POST':
            #### receipt
            vendor = request.POST.get("vendor")
            invoice = request.POST.get("invoice")
            storage_facility = request.POST.get("storage_facility")
            date = request.POST.get("date")
            formatted_date = datetime.strptime(date, "%d-%m-%Y")
            with transaction.atomic():
                is_present = Receipt.objects.filter(vendor = vendor, invoice=invoice)
                if is_present:
                    return JsonResponse({'message': 'error', 'data':'Рахунок вже додано! '})
                receipt = Receipt.objects.create(vendor = vendor, 
                                                    created_by = request.user, 
                                                    invoice=invoice, 
                                                    storage_facility = StorageFacility.objects.get(name = storage_facility ),
                                                    date = formatted_date
                                                )
                table = request.POST.get("table")
                receipt_items = pd.read_html(table)[0]

                add_receipt_items_to_receipt(receipt, receipt_items, request.user)
                add_goods_to_stock(storage_facility, receipt_items, receipt)
                # action_history(request.user, f"Додано закупку № {invoice} від {vendor}", "OK")
                return JsonResponse({'message': 'Success'})
        return render(request, 'stock/receipt.html', {"storages": storages})
    except Exception as error:
        # action_history(request.user, f"Додано закупку № {invoice} від {vendor}", {error})
        return JsonResponse({'message': f'Помилка ==> {error}'})

def delete_receipt(request):
    try:
        data = json.loads(request.body)
        receipt_id = data.get('receipt_id')
        if request.method == 'POST':

            with transaction.atomic():
                receipt_items = ReceiptItem.objects.filter(receipt=receipt_id)
                in_stock_obj = Stock.objects.filter(receipt=receipt_id, good_id=receipt_items.first().good).first()
                receipt_obj = Receipt.objects.filter(id=receipt_id).first()
                if not in_stock_obj or not receipt_obj:
                    return JsonResponse({'message': 'error', 'data': 'Обєкт не знайдено'})

                for receipt_item in receipt_items:
                    if float(in_stock_obj.quantity) > float(in_stock_obj.actual_quantity):
                        project = WritingOffMaterialsItem.objects.get(identifier_write_off=in_stock_obj.id)
                        error_message = f'Товар {project.good.name} вже списано в обєкті з назвою {project.invoice.project.name}'
                        return JsonResponse({'message': 'error', 'data': error_message})

                    receipt_item.delete()

                receipt_obj.delete()

                stock_items = Stock.objects.filter(receipt=receipt_id)
                for stock_item in stock_items:
                    stock_item.delete()

                # action_history(request.user, f"Видаелнно закупку № {receipt_obj.invoice} від {receipt_obj.vendor}", "OK")
                return JsonResponse({'message': 'Success'})

    except Exception as error:
        receipt_obj = Receipt.objects.filter(id=receipt_id).first()
        # action_history(request.user, f"Видаелнно закупку № {receipt_obj.invoice} від {receipt_obj.vendor}", {error})
        return JsonResponse({'message': 'error', 'data': str(error)})

def getConfigVendor(vendor_name):
    try:
        import os
        from django.conf import settings
        project_name = getattr(settings, 'PROJECT_NAME')
        file_config = os.path.abspath('config.json')
        with open(file_config, 'r') as f:
            config = json.load(f)
        return config["CONFIG"][project_name]["VENDORS_PARSE_CONFIG"][vendor_name]
    except Exception as error:
        raise error

def convert_wrong_excel_file(input_file):
    import shutil
    from zipfile import ZipFile
    import os
    from io import BytesIO
    from django.core.files.storage import default_storage

    # Зберігаємо файл на диск
    with default_storage.open('tmp_file.xlsx', 'wb') as tmp_file:
        tmp_file.write(input_file.read())

    # Шлях до збереженого файлу
    tmp_filepath = default_storage.path('tmp_file.xlsx')

    # Створюємо тимчасову папку
    tmp_folder = '/tmp/convert_wrong_excel/'
    os.makedirs(tmp_folder, exist_ok=True)

    # Розпаковуємо excel як zip в нашу тимчасову папку
    with ZipFile(tmp_filepath) as excel_container:
        excel_container.extractall(tmp_folder)

    # Переіменовуємо файл з неправильною назвою
    wrong_file_path = os.path.join(tmp_folder, 'xl', 'SharedStrings.xml')
    correct_file_path = os.path.join(tmp_folder, 'xl', 'sharedStrings.xml')
    os.rename(wrong_file_path, correct_file_path) 

    # Упаковуємо excel знову в zip і перейменовуємо в вихідний файл
    output_filepath = tmp_filepath.replace('.xlsx', '_corrected.xlsx')
    shutil.make_archive(output_filepath.replace('.xlsx', ''), 'zip', tmp_folder)
    os.rename(output_filepath.replace('.xlsx', '.zip'), output_filepath)

    # Зчитуємо вміст виправленого файлу назад у пам'ять
    with open(output_filepath, 'rb') as corrected_file:
        corrected_content = BytesIO(corrected_file.read())

    # Видаляємо тимчасові файли
    os.remove(tmp_filepath)
    os.remove(output_filepath)

    return corrected_content

def load_receipt_from_file(request):
    try:
        if request.method == 'POST':
            vendor = request.POST.get('vendor')
            config_name = None
            vendor_instance = Vendor.objects.filter(name=vendor).exists()
            if vendor_instance:
                vendor_instance = Vendor.objects.get(name=vendor)
                config_name = vendor_instance.config_name
            else:
                config_name = 'default_template'
            config = getConfigVendor(config_name)
            upload_form = UploadFileForm(request.POST, request.FILES)
            if upload_form.is_valid():
                filename = request.FILES['file']
                selected_columns = None
                df = pd.read_excel(filename, skiprows=config["start_row"] )
                if df.empty:
                    return JsonResponse({'message': 'error', 'data':'Шаблон не знайдено'})
                selected_columns = df[
                    [df.columns[config["name"]], 
                    df.columns[config["quantity"]], 
                    df.columns[config["unit_of_measurement"]], 
                    df.columns[config["price"]], 
                    df.columns[config["total_price"]]]
                ]
                if selected_columns.iloc[0].isna().any():
                    return JsonResponse({'message': 'error', 'data':'Структура файлу не співпадає з шаблоном'})
                new_df = pd.DataFrame(columns=['tmc', 'quantity', 'unit_of_measurement', 'price', 'total_price'])
                for record in selected_columns.itertuples():
                    if pd.notna(record[1]) and pd.notna(record[2]):  
                        new_row = {
                            'tmc': record[1],
                            'quantity': record[2],
                            'unit_of_measurement': record[3],
                            'price':float(record[4]),
                            'total_price': float(record[5]) * 1.2,
                        }
                        new_df = pd.concat([new_df, pd.DataFrame([new_row])], ignore_index=True)
                total_sum = new_df['total_price'].sum()
                result = new_df.to_json(orient="records")

                parsed = json.loads(result)
                return JsonResponse({'message': 'Success', 'data': parsed, 'total_sum':total_sum}, safe=False)
        else:
            upload_form = UploadFileForm()
            return render(request, 'stock/load_receipt_from_file.html', {'form':upload_form})
    except Exception as error:
        return JsonResponse({'message': 'error', 'data':str(error)})

def add_receipt_items_to_receipt(receipt, receipt_items, user):
    try:
        for _, row in receipt_items.iterrows():
            good_name = row['Товар']
            quantity = row['Кількість']
            unit_of_measurement = row['Одиниці']
            price = row['Ціна']
            total = row['Сума']       
            receipt_item = ReceiptItem.objects.create(
                receipt=Receipt.objects.get(id = receipt.id),
                good = Good.objects.get(name=good_name, unit_of_measurement = unit_of_measurement),
                quantity=quantity,
                price=float(price) * 1.2,
                total=total,
                created_by = user,
                date = receipt.date
            )
    except Exception as error:
        raise error

def calculate_price(quantity, price):
    quantity = quantity * 100
    price_per_unit = price / 100
    return quantity, price_per_unit 

def add_goods_to_stock(storage_facility_id, receipt_items, receipt):
    try:
        storage_facility = StorageFacility.objects.get(name=storage_facility_id)

        with transaction.atomic():
            for _, row in receipt_items.iterrows():
                good_name = row['Товар']
                quantity = row['Кількість']
                unit_of_measurement = row['Одиниці']
                price = row['Ціна']
                price_with_pdv = float(price)*1.2


                good = Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement)

                is_present = Stock.objects.filter(good_id=good, storage_facility_id=storage_facility, price=price)
                Stock.objects.create(
                    storage_facility_id=storage_facility,
                    good_id=good,
                    quantity=quantity,
                    actual_quantity=quantity,
                    price=price_with_pdv,
                    receipt = receipt
                )

    except IntegrityError as integrity_error:
        raise integrity_error
        # Обробка конфліктів IntegrityError, наприклад, унікальний ключ
    except Exception as error:
        raise error

def test(request):
    return render(request, 'stock/test.html')

def tmc_detail(request, id):
    good = Good.objects.get(id = id)
    # receipt = ReceiptItem.objects.filter(good_id=id).order_by("-created_at")[:5]
    total_quantity = ReceiptItem.objects.filter(good_id=id).aggregate(Sum('quantity'))['quantity__sum'] or 0
    total_sum = ReceiptItem.objects.filter(good_id=id).aggregate(Sum('total'))['total__sum'] or 0
    total_balance = Stock.objects.filter(good_id=id).aggregate(Sum('actual_quantity'))['actual_quantity__sum']  or 0
    total_write_off = WritingOffMaterialsItem.objects.filter(good_id=id).aggregate(Sum('quantity'))['quantity__sum']  or 0
    total_reservation = Reservation.objects.filter(good_id=id).aggregate(Sum('quantity'))['quantity__sum']  or 0
    receipt = ReceiptItem.objects.filter(good_id=id).order_by("-date")
    reservation = Reservation.objects.filter(good_id=id).order_by("-date")
    write_off =  WritingOffMaterialsItem.objects.filter(good_id=id)



    balance = total_balance - total_reservation

    for item in receipt:
        actual_quantity = Stock.objects.filter(good_id = item.good_id, receipt = item.receipt_id,).values_list('actual_quantity') or 0

        item.actual_quantity = actual_quantity[0][0]
        user = User.objects.get(email = item.created_by)  or 'System'
        item.user = user
    for item in reservation:
        user = User.objects.get(email = item.created_by)  or 'System'
        item.user = user
    for item in write_off:
        user = User.objects.get(email = item.invoice.created_by)  or 'System'
        item.user = user
    return render(request, 'stock/tmc_detail.html', {"total_quantity":total_quantity, 
                                                    "total_sum":total_sum, 
                                                    "receipt":receipt,
                                                    "total_balance":total_balance,
                                                    "total_write_off":total_write_off,
                                                    "total_reservation":total_reservation,
                                                    "good":good,
                                                    "reservation":reservation,
                                                    "write_off":write_off,
                                                    "balance":balance
                                                    })

def add_receipt_items_to_reservation(request):
    try:
        if request.method == 'POST':
            project = request.POST.get("project_id")
            items = request.POST.get("items")
            _items = pd.read_html(items)[0]
        with transaction.atomic():
            for _, row in _items.iterrows():
                good_name = row['Товар']
                quantity = row['Кількість']
                unit_of_measurement = row['Одиниці']
                good = Good.objects.get(name=good_name, unit_of_measurement = unit_of_measurement)
                items = Stock.objects.filter(good_id=good.id).order_by('date')
                total_quantity = sum(item.quantity for item in items)
                if total_quantity < quantity:
                    # raise ValueError(f"Недосататьня кількість {good_name} для списання")  
                    continue  

                is_reserved = Reservation.objects.filter(good =Good.objects.get(name=good_name, unit_of_measurement = unit_of_measurement), quantity=quantity, project = project)
                if is_reserved:
                    continue

                item = Reservation.objects.create(
                    project = Project.objects.get(id = project),
                    good = Good.objects.get(name=good_name, unit_of_measurement = unit_of_measurement),
                    quantity=quantity,
                    created_by = request.user,
                )
            return JsonResponse({'message': 'Success','data':''})
    except Exception as error:
        return JsonResponse({'message': 'error','data':str(error)})

def delete_items_from_reservation(request):
    try:
        if request.method == 'POST':
            project = request.POST.get("project_id")
        with transaction.atomic():
            items = Reservation.objects.filter(project = project)
            for item in items:
                item.delete()
            return JsonResponse({'message': 'Success','data':''})
    except Exception as error:
        return JsonResponse({'message': 'error','data':str(error)})

def delete_items_from_reservation_after_write_off_from_object(good_id, quantity, project_id):
    try:
        with transaction.atomic():
            items = Reservation.objects.filter(good=good_id, quantity=quantity, project = project_id)
            for item in items:
                item.delete()
    except Exception as error:
        raise error

def write_off_tmc_from_stock(good, quantity):
    """ Функція для списання матеріалів зі складу зі сторінки об'єкта /stock/write-off-tmc/"""
    try:
        # quantity = 20
        items = Stock.objects.filter(good_id=good.id, actual_quantity__gt=0).order_by('date')
        total_quantity = sum(item.actual_quantity for item in items)
        if total_quantity < quantity:
            return "False"

        remaining_quantity = quantity
        # price = 0.0
        write_off_info_list = []  # Зберігатиме інформацію про списані кількості та ідентифікатори

        for item in items:
            if item.actual_quantity >= remaining_quantity:
                item.actual_quantity = F('actual_quantity') - remaining_quantity
                # price += remaining_quantity * item.price
                write_off_info_list.append((str(item.id), remaining_quantity))
                remaining_quantity = 0
                item.save()
                break
            else:
                remaining_quantity -= item.actual_quantity
                # price += remaining_quantity * item.price
                write_off_info_list.append((str(item.id), item.actual_quantity))
                item.actual_quantity = 0
                item.save()

        return  write_off_info_list
    except Exception as error:
        raise error

def write_data_to_writing_off_materials_item_table(request, invoice_write_of, items):
    try:
        for _, row in items.iterrows():
                    good_name = row['Товар']
                    quantity = row['Кількість']     
                    item = WritingOffMaterialsItem.objects.create(
                        invoice = invoice_write_of,
                        good = Good.objects.get(name=good_name),
                        quantity=quantity
                    )
                    write_off_tmc_from_stock(Good.objects.get(name=good_name), quantity)
    except Exception as error:
        raise error

def manual_write_off_tmc_from_stock(good, quantity):
    """ Функція для списання матеріалів зі складу вручну через кнопку списати на сторінці /stock/write-off-tmc/"""
    try:

        items = Stock.objects.filter(good_id=good.id, actual_quantity__gt=0).order_by('date')

        total_quantity = sum(item.actual_quantity for item in items)
        if total_quantity < quantity:
            return False

        remaining_quantity = quantity
        price = 0.0

        for item in items:
            if item.actual_quantity >= remaining_quantity:
                item.actual_quantity = F('actual_quantity') - remaining_quantity
                price += remaining_quantity * item.price
                item.save()
                break
            else:
                remaining_quantity -= item.actual_quantity
                price += item.actual_quantity * item.price
                item.actual_quantity = 0
                item.save()

        return price  
    except Exception as error:

        raise error

def manual_write_off_tmc(request):
    """ Функція обробки натискання кнопки списати на сторінці /stock/write-off-tmc/"""
    try:
        data = {}
        if request.method == 'GET':
            next_number = get_next_number_invoice_write_off()
            goods = Good.objects.all()
            return render(request, 'stock/write_off_tmc.html', {"goods":goods, "next_number":next_number})
        if request.method == 'POST':
            data = json.loads(request.body)
            nested_data = data.get("data", {})

            

            reason = nested_data.get("reason")
            reason = remove_spaces_from_string(reason)
            # date = nested_data.get("date")
            date = datetime.strptime(nested_data.get("date"), "%d-%m-%Y")
            table = nested_data.get("table")
            invoice = get_next_number_invoice_write_off()
            html_buffer = StringIO(table)
            items = pd.read_html(html_buffer)[0]

            total_price = 0.0
            with transaction.atomic():
                invoice_write_of = InvoiceWritingOffMaterial.objects.create(
                    invoice = invoice,
                    date = date,
                    reason = reason,
                    created_by = request.user
                )
                
                for _, row in items.iterrows():
                    good_name = row['Товар']
                    unit_of_measurement = row['Одиниці']
                    quantity = row['Кількість'] 
                    price = manual_write_off_tmc_from_stock(Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement), quantity) 
                    if not price:
                        return JsonResponse({'message': 'error','data':str('Недостатня кількість для списанн')})
                    item = WritingOffMaterialsItem.objects.create(
                        invoice = invoice_write_of,
                        good = Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement),
                        quantity=quantity,
                        price = price
                    )
                    total_price += price
                invoice = InvoiceWritingOffMaterial.objects.get(id = invoice_write_of.id)
                invoice.total_sum = total_price
                invoice.save()

                return JsonResponse({'message': 'Success'})
    except Exception as error:
        return JsonResponse({'message': 'error','data':str(error)})

def write_off_tmc_from_object(request):
    """
    write off items from project 
    """
    try:
        if request.method == 'POST':
            data = json.loads(request.body)
            nested_data = data.get("data", {})
            project_id = nested_data.get("project_id")
            reason = nested_data.get("reason")
            reason = remove_spaces_from_string(reason)
            table = nested_data.get("table")
            html_buffer = StringIO(table)
            items = pd.read_html(html_buffer)[0]
            print(f"{items}")
            total_price = 0.0
            with transaction.atomic():
                is_invoice_present = InvoiceWritingOffMaterial.objects.filter(project = project_id)
                if not is_invoice_present:
                    next_invoice_number = get_next_number_invoice_write_off()
                    invoice_write_of = InvoiceWritingOffMaterial.objects.create(
                        invoice = next_invoice_number,
                        reason = reason,
                        created_by = request.user,
                        project = Project.objects.get(id=project_id),

                    )
                    for _, row in items.iterrows():
                        good_name = row['Товар']
                        quantity = row['Кількість'] 
                        unit_of_measurement = row['Одиниці'] 

                        print(f" good_name ==> {good_name} == quantity ==>{quantity} == unit_of_measuremen ==> {unit_of_measurement} ")
                        identifier_write_off = write_off_tmc_from_stock(Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement), quantity) 

                        for i in identifier_write_off:
                            price = Stock.objects.filter(id = i[0]).values_list('price')
                            item_price = float(price[0][0])

                            if str(identifier_write_off) == "False":
                                continue   

                            item = WritingOffMaterialsItem.objects.create(
                                invoice = invoice_write_of,
                                good = Good.objects.get(name=good_name),
                                quantity=i[1],
                                price = item_price,
                                identifier_write_off = i[0]
                            )
                            total_price += item_price * quantity

                            set_write_off_true = ProjectItem.objects.filter(good = Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement), quantity = quantity, project = project_id)
                            if set_write_off_true:
                                set_write_off_true.update(is_write_off=True)

                        invoice = InvoiceWritingOffMaterial.objects.get(id = invoice_write_of.id)
                        invoice.total_sum = F('total_sum') + total_price
                        invoice.save()
                        delete_items_from_reservation_after_write_off_from_object(Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement), quantity, project_id)
                    return JsonResponse({'message': 'Success'})
                else:
                    is_invoice_present = InvoiceWritingOffMaterial.objects.get(project = project_id)
                    for _, row in items.iterrows():
                        good_name = row['Товар']
                        quantity = row['Кількість'] 
                        unit_of_measurement = row['Одиниці'] 
                        identifier_write_off = write_off_tmc_from_stock(Good.objects.get(name=good_name,unit_of_measurement=unit_of_measurement), quantity) 
                        for i in identifier_write_off:

                            price = Stock.objects.filter(id = i[0]).values_list('price')

                            item_price = float(price[0][0])

                            if str(identifier_write_off) == "False":
                                continue   
                            item = WritingOffMaterialsItem.objects.create(
                                invoice = is_invoice_present,
                                good = Good.objects.get(name=good_name,unit_of_measurement=unit_of_measurement),
                                quantity=i[1],
                                price = item_price,
                                identifier_write_off = i[0]
                            )
                            total_price += item_price * float(quantity)
                            is_invoice_present.total_sum = F('total_sum') + total_price
                            is_invoice_present.save()
                            set_write_off_true = ProjectItem.objects.filter(good = Good.objects.get(name=good_name,unit_of_measurement=unit_of_measurement), quantity = quantity, project = project_id)
                            if set_write_off_true:
                                set_write_off_true.update(is_write_off=True)
                            delete_items_from_reservation_after_write_off_from_object(Good.objects.get(name=good_name, unit_of_measurement=unit_of_measurement), quantity, project_id)
                    return JsonResponse({'message': 'Success'})

    except Exception as error:
        return JsonResponse({'message': 'error','data':str('Недостатня кількість')})

def add_item_quantity_to_stock(good_id, quantity):
    item = Stock.objects.filter(good_id=good_id).first()
    item.actual_quantity = F('actual_quantity') + quantity
    item.save()

def delete_items_from_write_off(request):
    try:
        if request.method == 'POST':
            project = request.POST.get("project_id")
            project_name = Project.objects.get(id = project)
            invoice = InvoiceWritingOffMaterial.objects.get(reason = project_name.name)
        with transaction.atomic():
            items = WritingOffMaterialsItem.objects.filter(invoice = invoice)
            for item in items:
                add_item_quantity_to_stock(item.good, item.quantity)
                item.delete()

            invoice.delete()

            ProjectItem.objects.filter(project=project).update(is_write_off=False)

            return JsonResponse({'message': 'Success','data':''})
    except Exception as error:
        return JsonResponse({'message': 'error','data':str(error)})

def movement_between_stocks(request):
    storages = StorageFacility.objects.all()
    goods =  Good.objects.all().values('name')
    return render(request, 'stock/movement_between_stocks.html', context={"storages":storages, "goods":goods})

def get_next_number_invoice_movement():
    try:
        with transaction.atomic():
            max_invoice = InvoiceMovementMaterial.objects.select_for_update().aggregate(Max('invoice'))['invoice__max']
            
            if not max_invoice:
                next_number = 1
            else:
                next_number = int(max_invoice) + 1
            
            return next_number
        
    except Exception as error:
        raise error

def process_movement_good_beatween_stocks(good, quantity, storage_from, storage_to, user_email):
    """ Функція для списання матеріалів зі складу зі сторінки об'єкта /stock/write-off-tmc/"""
    try:
        movement_invoice = InvoiceMovementMaterial.objects.create(
            invoice = get_next_number_invoice_movement(),
            created_by = user_email,
            storage_from = storage_from.id,
            storage_to = storage_to.id
        )
        items = Stock.objects.filter(good_id=good.id, actual_quantity__gt=0, storage_facility_id = storage_from.id).order_by('date')
        remaining_quantity = quantity
        movement_info_list = []  # Зберігатиме інформацію про списані кількості та ідентифікатори
        for item in items:
            if item.actual_quantity >= remaining_quantity:
                item.actual_quantity = F('actual_quantity') - remaining_quantity
                movement_info_list.append((str(item.id), remaining_quantity))
                MovementMaterialsItem.objects.create(
                    invoice = movement_invoice,
                    good = good,
                    quantity = remaining_quantity,
                    price = item.price,
                    identifier_write_off = item.id
                )
                Stock.objects.create(
                    storage_facility_id=storage_to,
                    good_id=good,
                    quantity=remaining_quantity,
                    actual_quantity=remaining_quantity,
                    price=item.price,
                    receipt = Receipt.objects.get(id = item.receipt.id)
                )
                item.save()
                remaining_quantity = 0
                break
            else:
                remaining_quantity -= item.actual_quantity
                movement_info_list.append((str(item.id), item.actual_quantity))
                MovementMaterialsItem.objects.create(
                    invoice = movement_invoice,
                    good = good,
                    quantity = item.actual_quantity,
                    price = item.price,
                    identifier_write_off = item.id
                )
                Stock.objects.create(
                    storage_facility_id=storage_to,
                    good_id=good,
                    quantity=item.actual_quantity,
                    actual_quantity=item.actual_quantity,
                    price=item.price,
                    receipt = Receipt.objects.get(id = item.receipt.id)
                )
                item.actual_quantity = 0
                item.save()
        return  movement_info_list
    except Exception as error:
        raise error

def save_movement_between_stocks(request):
    try:
        with transaction.atomic():
            data = json.loads(request.body)
            table =  data['data']
            storage_facility_from =  data['storage_facility_from']
            storage_facility_to =  data['storage_facility_to']
            for row in table:
                if row[0] == "Товар(ТМЦ)" or  row[0] == None:
                    continue
                needed_move_quantity = row[2]
                good_name, good_units = split_string_into_name_and_units_of_measurement(row[0])
                good = Good.objects.get(name = good_name, unit_of_measurement = good_units)
                storage_from  = StorageFacility.objects.get(name = storage_facility_from)
                storage_to  = StorageFacility.objects.get(name = storage_facility_to)
                actual_availability = Stock.objects.filter(good_id=good.id, storage_facility_id = storage_from).aggregate(Sum('actual_quantity'))['actual_quantity__sum']  or 0
                if actual_availability < needed_move_quantity:
                    return JsonResponse({'message': 'error','data':str('Недостатня кількість для переміщення')})

                process_movement_good_beatween_stocks(good, needed_move_quantity, storage_from, storage_to, request.user.email)
                return JsonResponse({'message': 'Success','data':''})

    except Exception as error:
        raise error

def full_report_about_good(request, id):
    good = Good.objects.get(id = id)
    receipt = ReceiptItem.objects.filter(good_id=id).order_by("-created_at")
    reservation = Reservation.objects.filter(good_id=id).order_by("-date")
    write_off =  WriteOff.objects.filter(good_id=id).order_by("-date")
    for item in receipt:
        user = User.objects.get(email = item.created_by)  or 'System'
        item.user = user
    for item in reservation:
        user = User.objects.get(email = item.created_by)  or 'System'
        item.user = user
    for item in write_off:
        user = User.objects.get(email = item.created_by)  or 'System'
        item.user = user
    return render(request, 'stock/full_report_about_good.html', context={
        "good":good,
        "receipt":receipt,
        "reservation":reservation,
        "write_off":write_off
    })