From 9d83ccd7ed9b8c3db42be7254802c0fb3c6b7cd8 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Fri, 31 Oct 2025 22:49:14 +0500 Subject: [PATCH] finance: fucking income expence --- core/apps/finance/serializers/expence.py | 220 +++++++++++++++------ core/apps/finance/serializers/income.py | 231 +++++++++++++++-------- core/apps/finance/views/expence.py | 88 +++++++++ core/apps/finance/views/income.py | 109 ++++++++--- core/apps/orders/serializers/party.py | 182 ++++++++++-------- 5 files changed, 589 insertions(+), 241 deletions(-) diff --git a/core/apps/finance/serializers/expence.py b/core/apps/finance/serializers/expence.py index ff77c46..4c836be 100644 --- a/core/apps/finance/serializers/expence.py +++ b/core/apps/finance/serializers/expence.py @@ -14,8 +14,29 @@ class ExpenceCreateSerializer(serializers.ModelSerializer): 'counterparty', 'price', 'exchange_rate', 'currency', 'date', 'comment', 'audit', 'file' ] + def validate(self, data): + """Balanslari tekshirish""" + cash_transaction = data.get('cash_transaction') + payment_type = data.get('payment_type') + price = data.get('price') + exchange_rate = data.get('exchange_rate', 1) + currency = data.get('currency', 'uzs') + + final_price = price * exchange_rate if exchange_rate else price + + # Payment type balansi tekshiruvi + if currency == 'uzs' and payment_type.total_uzs < final_price: + raise serializers.ValidationError(f"Yetarli UZS balansi yo'q. Mavjud: {payment_type.total_uzs}") + elif currency == 'usd' and payment_type.total_usd < final_price: + raise serializers.ValidationError(f"Yetarli USD balansi yo'q. Mavjud: {payment_type.total_usd}") + + return data + def create(self, validated_data): with transaction.atomic(): + exchange_rate = validated_data.get('exchange_rate') or 1 + final_price = validated_data.get('price') * exchange_rate + expence = Expence.objects.create( user=self.context.get('user'), cash_transaction=validated_data.get('cash_transaction'), @@ -24,61 +45,83 @@ class ExpenceCreateSerializer(serializers.ModelSerializer): project=validated_data.get('project'), expence_type=validated_data.get('expence_type'), counterparty=validated_data.get('counterparty'), - price=validated_data.get('price') * validated_data.get('exchange_rate') if validated_data.get('exchange_rate') else validated_data.get('price'), - exchange_rate=validated_data.get('exchange_rate'), + price=final_price, + exchange_rate=exchange_rate, currency=validated_data.get('currency'), date=validated_data.get('date'), comment=validated_data.get('comment'), audit=validated_data.get('audit'), file=validated_data.get('file'), ) + cash_transaction = expence.cash_transaction payment_type = expence.payment_type + currency = validated_data.get('currency', 'uzs').lower() user = self.context.get('user') - if validated_data.get('currency') == 'uzs': + + if currency == 'uzs': + # Cash transaction balansini yangilash cash_transaction.expence_balance_uzs += expence.price cash_transaction.total_balance_uzs = cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs - if payment_type.total_uzs > expence.price: - payment_type.total_uzs -= expence.price - + + # Payment type balansini kamayitish + payment_type.total_uzs -= expence.price + + # Kontrapartiya hisobini yangilash if expence.counterparty: - if expence.counterparty.kredit_uzs != 0: - expence.counterparty.kredit_uzs -= expence.price + if expence.counterparty.kredit_uzs > 0: + # Kredit bo'lsa, uni qisqartirish + expence.counterparty.kredit_uzs -= expence.price expence.counterparty.total_kredit -= expence.price - - expence.counterparty.debit_uzs += expence.counterparty.kredit_uzs - expence.price - expence.counterparty.total_debit += expence.price - else: + + # Debit oshirish expence.counterparty.debit_uzs += expence.price expence.counterparty.total_debit += expence.price + else: + # Kredit yo'q bo'lsa, to'g'ridan-to'g'ri debit oshirish + expence.counterparty.debit_uzs += expence.price + expence.counterparty.total_debit += expence.price + expence.counterparty.save() - elif validated_data.get('currency') == 'usd': + elif currency == 'usd': + # Cash transaction balansini yangilash cash_transaction.expence_balance_usd += expence.price cash_transaction.total_balance_usd = cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd - if payment_type.total_usd > expence.price: - payment_type.total_usd -= expence.price - + + # Payment type balansini kamayitish + payment_type.total_usd -= expence.price + + # Kontrapartiya hisobini yangilash if expence.counterparty: - if expence.counterparty.kredit_usd != 0: - expence.counterparty.kredit_usd -= validated_data.get('price') + if expence.counterparty.kredit_usd > 0: + # Kredit bo'lsa, uni qisqartirish + expence.counterparty.kredit_usd -= expence.price # ✅ TUZATILDI expence.counterparty.total_kredit -= expence.price - - expence.counterparty.debit_usd += expence.counterparty.kredit_usd - validated_data.get('price') + + # Debit oshirish + expence.counterparty.debit_usd += expence.price # ✅ TUZATILDI expence.counterparty.total_debit += expence.price else: - expence.counterparty.debit_usd += validated_data.get('price') + # Kredit yo'q bo'lsa, to'g'ridan-to'g'ri debit oshirish + expence.counterparty.debit_usd += expence.price # ✅ TUZATILDI expence.counterparty.total_debit += expence.price + expence.counterparty.save() + + # Bildirishnoma yuborish body = f"""{user.full_name} {expence.price} {expence.currency.upper()}... \n screen: /monitoring""" data = { "screen": "/monitoring", "type": "expence", } notify_user(user=user, title="Tasdiqlang yoki rad eting", body=body, data=data) + cash_transaction.save() payment_type.save() + return expence + class ExpenceListSerializer(serializers.ModelSerializer): @@ -156,40 +199,103 @@ class ExpenceUpdateSerializer(serializers.ModelSerializer): 'price': {'required': False}, } + def validate_price(self, value): + """Narxi manfiy bo'lmasligi tekshiruvi""" + if value and value < 0: + raise serializers.ValidationError("Narxi manfiy bo'lishi mumkin emas") + return value + def update(self, instance, validated_data): - old_price = instance.price - instance.project_folder = validated_data.get('project_folder', instance.project_folder) - instance.project = validated_data.get('project', instance.project) - instance.price = validated_data.get('price', instance.price) - instance.expence_type = validated_data.get('expence_type', instance.expence_type) - instance.counterparty = validated_data.get('counterparty', instance.counterparty) - instance.date = validated_data.get('date', instance.date) - instance.comment = validated_data.get('comment', instance.comment) - instance.audit = validated_data.get('audit', instance.audit) - instance.file = validated_data.get('file', instance.file) - if validated_data.get('price'): - cash_transaction = instance.cash_transaction - payment_type = instance.payment_type - if old_price > validated_data.get('price'): - if instance.currency == 'uzs': - cash_transaction.expence_balance_uzs -= (old_price - validated_data.get('price')) - cash_transaction.total_balance_uzs -= (old_price - validated_data.get('price')) - payment_type.total_uzs -= (old_price - validated_data.get('price')) if payment_type.total_uzs > (old_price - validated_data.get('price')) else 0 - else: - cash_transaction.expence_balance_usd -= (old_price - validated_data.get('price')) - cash_transaction.total_balance_usd -= (old_price - validated_data.get('price')) - payment_type.total_usd -= (old_price - validated_data.get('price')) if payment_type.total_usd > (old_price - validated_data.get('price')) else 0 - - elif old_price < validated_data.get('price'): - if instance.currency == 'uzs': - cash_transaction.expence_balance_uzs += (old_price - validated_data.get('price')) - cash_transaction.total_balance_uzs += (old_price - validated_data.get('price')) - payment_type.total_uzs += (old_price - validated_data.get('price')) - else: - cash_transaction.expence_balance_usd += (old_price - validated_data.get('price')) - cash_transaction.total_balance_usd += (old_price - validated_data.get('price')) - payment_type.total_usd += (old_price - validated_data.get('price')) - cash_transaction.save() - payment_type.save() - instance.save() - return instance \ No newline at end of file + with transaction.atomic(): + old_price = instance.price + old_counterparty = instance.counterparty + new_price = validated_data.get('price', instance.price) + new_counterparty = validated_data.get('counterparty', instance.counterparty) + currency = instance.currency.lower() + + # Asosiy ma'lumotlarni yangilash + instance.project_folder = validated_data.get('project_folder', instance.project_folder) + instance.project = validated_data.get('project', instance.project) + instance.price = new_price + instance.expence_type = validated_data.get('expence_type', instance.expence_type) + instance.counterparty = new_counterparty + instance.date = validated_data.get('date', instance.date) + instance.comment = validated_data.get('comment', instance.comment) + instance.audit = validated_data.get('audit', instance.audit) + instance.file = validated_data.get('file', instance.file) + + # Agar narxi o'zgarsa, balanslarni yangilash + if validated_data.get('price') and old_price != new_price: + price_difference = new_price - old_price # ✅ To'g'ri farq + cash_transaction = instance.cash_transaction + payment_type = instance.payment_type + + if currency == 'uzs': + # Cash transaction balansini yangilash + cash_transaction.expence_balance_uzs += price_difference + cash_transaction.total_balance_uzs = ( + cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs + ) + + # Payment type balansini yangilash + payment_type.total_uzs -= price_difference # ✅ Narx oshsa, balans kamayadi + + elif currency == 'usd': + # Cash transaction balansini yangilash + cash_transaction.expence_balance_usd += price_difference + cash_transaction.total_balance_usd = ( + cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd + ) + + # Payment type balansini yangilash + payment_type.total_usd -= price_difference # ✅ Narx oshsa, balans kamayadi + + cash_transaction.save() + payment_type.save() + + # Agar kontrapartiya o'zgarsa, uning hisobini yangilash + if new_counterparty != old_counterparty: + # Eski kontrapartyani qaytarish + if old_counterparty: + if currency == 'uzs': + if old_counterparty.debit_uzs > 0: + old_counterparty.debit_uzs -= old_price + old_counterparty.total_debit -= old_price + else: + old_counterparty.kredit_uzs += old_price + old_counterparty.total_kredit += old_price + else: + if old_counterparty.debit_usd > 0: + old_counterparty.debit_usd -= old_price + old_counterparty.total_debit -= old_price + else: + old_counterparty.kredit_usd += old_price + old_counterparty.total_kredit += old_price + + old_counterparty.save() + + # Yangi kontrapartyani yangilash + if new_counterparty: + if currency == 'uzs': + if new_counterparty.kredit_uzs > 0: + new_counterparty.kredit_uzs -= new_price + new_counterparty.total_kredit -= new_price + new_counterparty.debit_uzs += new_price + new_counterparty.total_debit += new_price + else: + new_counterparty.debit_uzs += new_price + new_counterparty.total_debit += new_price + else: + if new_counterparty.kredit_usd > 0: + new_counterparty.kredit_usd -= new_price + new_counterparty.total_kredit -= new_price + new_counterparty.debit_usd += new_price + new_counterparty.total_debit += new_price + else: + new_counterparty.debit_usd += new_price + new_counterparty.total_debit += new_price + + new_counterparty.save() + + instance.save() + return instance diff --git a/core/apps/finance/serializers/income.py b/core/apps/finance/serializers/income.py index 14fbe16..1f0bebd 100644 --- a/core/apps/finance/serializers/income.py +++ b/core/apps/finance/serializers/income.py @@ -97,8 +97,25 @@ class IncomeCreateSerializer(serializers.ModelSerializer): "audit", ] + def validate(self, data): + """Validasiya qilish""" + price = data.get('price') + exchange_rate = data.get('exchange_rate') + + if price and price < 0: + raise serializers.ValidationError("Narxi manfiy bo'lishi mumkin emas") + + if exchange_rate and exchange_rate < 0: + raise serializers.ValidationError("Kurs manfiy bo'lishi mumkin emas") + + return data + def create(self, validated_data): with transaction.atomic(): + # Exchange rate va final price hisoblash + exchange_rate = validated_data.get("exchange_rate") or 1 + final_price = validated_data.get("price") * exchange_rate + income = Income.objects.create( user=self.context.get("user"), cash_transaction=validated_data["cash_transaction"], @@ -108,61 +125,72 @@ class IncomeCreateSerializer(serializers.ModelSerializer): counterparty=validated_data.get("counterparty"), type_income=validated_data.get("type_income"), currency=validated_data.get("currency"), - price=( - validated_data.get("price") * validated_data.get("exchange_rate") - if validated_data.get("exchange_rate") - else validated_data.get("price") - ), - exchange_rate=validated_data.get("exchange_rate"), + price=final_price, + exchange_rate=exchange_rate, date=validated_data.get("date"), comment=validated_data.get("comment"), file=validated_data.get("file"), audit=validated_data.get("audit"), ) + cash_transaction = income.cash_transaction payment_type = income.payment_type + currency = validated_data.get("currency", "uzs").lower() - if validated_data.get("currency") == "uzs": + if currency == "uzs": + # Cash transaction balansini yangilash cash_transaction.income_balance_uzs += income.price cash_transaction.total_balance_uzs = ( cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs ) + + # Payment type balansini yangilash payment_type.total_uzs += income.price + # Kontrapartiya hisobini yangilash if income.counterparty: - if income.counterparty.debit_uzs != 0: + if income.counterparty.debit_uzs > 0: + # Debit bo'lsa, uni qisqartirish income.counterparty.debit_uzs -= income.price income.counterparty.total_debit -= income.price - - income.counterparty.kredit_uzs += ( - income.counterparty.debit_uzs - income.price - ) + + # Kredit oshirish + income.counterparty.kredit_uzs += income.price # ✅ TUZATILDI income.counterparty.total_kredit += income.price else: + # Debit yo'q bo'lsa, to'g'ridan-to'g'ri kredit oshirish income.counterparty.kredit_uzs += income.price income.counterparty.total_kredit += income.price + income.counterparty.save() - elif validated_data.get("currency") == "usd": + elif currency == "usd": + # Cash transaction balansini yangilash cash_transaction.income_balance_usd += income.price cash_transaction.total_balance_usd = ( cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd ) + + # Payment type balansini yangilash payment_type.total_usd += income.price + + # Kontrapartiya hisobini yangilash if income.counterparty: - if income.counterparty.debit_usd != 0: - income.counterparty.debit_usd -= validated_data.get("price") + if income.counterparty.debit_usd > 0: + # Debit bo'lsa, uni qisqartirish + income.counterparty.debit_usd -= income.price # ✅ TUZATILDI income.counterparty.total_debit -= income.price - income.counterparty.kredit_usd += ( - income.counterparty.debit_usd - validated_data.get("price") - ) + # Kredit oshirish + income.counterparty.kredit_usd += income.price # ✅ TUZATILDI income.counterparty.total_kredit += income.price else: - income.counterparty.kredit_usd += validated_data.get("price") + # Debit yo'q bo'lsa, to'g'ridan-to'g'ri kredit oshirish + income.counterparty.kredit_usd += income.price # ✅ TUZATILDI income.counterparty.total_kredit += income.price + income.counterparty.save() cash_transaction.save() @@ -192,70 +220,107 @@ class IncomeUpdateSerializer(serializers.ModelSerializer): "price": {"required": False}, } - def update(self, instance, validated_data): - old_price = instance.price - instance.project_folder = validated_data.get( - "project_folder", instance.project_folder - ) - instance.project = validated_data.get("project", instance.project) - instance.price = validated_data.get("price", instance.price) - instance.type_income = validated_data.get("type_income", instance.type_income) - instance.counterparty = validated_data.get( - "counterparty", instance.counterparty - ) - instance.date = validated_data.get("date", instance.date) - instance.comment = validated_data.get("comment", instance.comment) - instance.audit = validated_data.get("audit", instance.audit) - instance.file = validated_data.get("file", instance.file) - if validated_data.get("price"): - cash_transaction = instance.cash_transaction - payment_type = instance.payment_type - if old_price > validated_data.get("price"): - if instance.currency == "uzs": - cash_transaction.income_balance_uzs -= ( - old_price - validated_data.get("price") - ) - cash_transaction.total_balance_uzs -= ( - old_price - validated_data.get("price") - ) - payment_type.total_uzs -= ( - (old_price - validated_data.get("price")) - if payment_type.total_uzs - > (old_price - validated_data.get("price")) - else 0 - ) - else: - cash_transaction.income_balance_usd -= ( - old_price - validated_data.get("price") - ) - cash_transaction.total_balance_usd -= ( - old_price - validated_data.get("price") - ) - payment_type.total_usd -= ( - (old_price - validated_data.get("price")) - if payment_type.total_usd - > (old_price - validated_data.get("price")) - else 0 - ) + def validate_price(self, value): + """Narxi manfiy bo'lmasligi tekshiruvi""" + if value and value < 0: + raise serializers.ValidationError("Narxi manfiy bo'lishi mumkin emas") + return value - elif old_price < validated_data.get("price"): - if instance.currency == "uzs": - cash_transaction.income_balance_uzs += ( - old_price - validated_data.get("price") + def update(self, instance, validated_data): + with transaction.atomic(): + old_price = instance.price + old_counterparty = instance.counterparty + new_price = validated_data.get("price", instance.price) + new_counterparty = validated_data.get("counterparty", instance.counterparty) + currency = instance.currency.lower() + + # Asosiy ma'lumotlarni yangilash + instance.project_folder = validated_data.get( + "project_folder", instance.project_folder + ) + instance.project = validated_data.get("project", instance.project) + instance.price = new_price + instance.type_income = validated_data.get("type_income", instance.type_income) + instance.counterparty = new_counterparty + instance.date = validated_data.get("date", instance.date) + instance.comment = validated_data.get("comment", instance.comment) + instance.audit = validated_data.get("audit", instance.audit) + instance.file = validated_data.get("file", instance.file) + + # Agar narxi o'zgarsa, balanslarni yangilash + if validated_data.get("price") and old_price != new_price: + price_difference = new_price - old_price # ✅ To'g'ri farq + cash_transaction = instance.cash_transaction + payment_type = instance.payment_type + + if currency == "uzs": + # Cash transaction balansini yangilash + cash_transaction.income_balance_uzs += price_difference + cash_transaction.total_balance_uzs = ( + cash_transaction.income_balance_uzs + - cash_transaction.expence_balance_uzs ) - cash_transaction.total_balance_uzs += ( - old_price - validated_data.get("price") + + # Payment type balansini yangilash + payment_type.total_uzs += price_difference # ✅ Narx oshsa, balans oshadi + + elif currency == "usd": + # Cash transaction balansini yangilash + cash_transaction.income_balance_usd += price_difference + cash_transaction.total_balance_usd = ( + cash_transaction.income_balance_usd + - cash_transaction.expence_balance_usd ) - payment_type.total_uzs += old_price - validated_data.get("price") - else: - cash_transaction.income_balance_usd += ( - old_price - validated_data.get("price") - ) - cash_transaction.total_balance_usd += ( - old_price - validated_data.get("price") - ) - payment_type.total_usd += old_price - validated_data.get("price") - cash_transaction.save() - payment_type.save() - instance.save() - return instance + + # Payment type balansini yangilash + payment_type.total_usd += price_difference # ✅ Narx oshsa, balans oshadi + + cash_transaction.save() + payment_type.save() + + # Agar kontrapartiya o'zgarsa, uning hisobini yangilash + if new_counterparty != old_counterparty: + # Eski kontrapartyani qaytarish + if old_counterparty: + if currency == "uzs": + if old_counterparty.kredit_uzs > 0: + old_counterparty.kredit_uzs -= old_price + old_counterparty.total_kredit -= old_price + else: + old_counterparty.debit_uzs += old_price + old_counterparty.total_debit += old_price + else: + if old_counterparty.kredit_usd > 0: + old_counterparty.kredit_usd -= old_price + old_counterparty.total_kredit -= old_price + else: + old_counterparty.debit_usd += old_price + old_counterparty.total_debit += old_price + + old_counterparty.save() + + # Yangi kontrapartyani yangilash + if new_counterparty: + if currency == "uzs": + if new_counterparty.debit_uzs > 0: + new_counterparty.debit_uzs -= new_price + new_counterparty.total_debit -= new_price + new_counterparty.kredit_uzs += new_price + new_counterparty.total_kredit += new_price + else: + new_counterparty.kredit_uzs += new_price + new_counterparty.total_kredit += new_price + else: + if new_counterparty.debit_usd > 0: + new_counterparty.debit_usd -= new_price + new_counterparty.total_debit -= new_price + new_counterparty.kredit_usd += new_price + new_counterparty.total_kredit += new_price + else: + new_counterparty.kredit_usd += new_price + new_counterparty.total_kredit += new_price + + new_counterparty.save() + + instance.save() + return instance diff --git a/core/apps/finance/views/expence.py b/core/apps/finance/views/expence.py index c196722..cb8870f 100644 --- a/core/apps/finance/views/expence.py +++ b/core/apps/finance/views/expence.py @@ -1,4 +1,5 @@ from django.shortcuts import get_object_or_404 +from django.db import transaction from rest_framework import generics, views, parsers, filters from rest_framework.response import Response @@ -116,6 +117,93 @@ class ExpenceDeleteApiView(generics.GenericAPIView): permission_classes = [HasRolePermission] def post(self, request, id): + expence = get_object_or_404(Expence, id=id, is_deleted=False) + serializer = self.serializer_class(data=request.data) + + if serializer.is_valid(raise_exception=True): + with transaction.atomic(): + comment = serializer.validated_data.get('comment') + currency = expence.currency.lower() + + # Deleted record yaratish + DeletedExpence.objects.create( + expence=expence, + comment=comment + ) + + cash_transaction = expence.cash_transaction + payment_type = expence.payment_type + counterparty = expence.counterparty + + if currency == 'uzs': + # Balanslarni qaytarish (o'chirilgani teskari qilish) + cash_transaction.expence_balance_uzs -= expence.price # ✅ KAMAYISHI KERAK + cash_transaction.total_balance_uzs = ( + cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs + ) + payment_type.total_uzs += expence.price # ✅ QAYTARISH + + # Kontrapartiya hisobini qaytarish + if counterparty: + if counterparty.debit_uzs > 0: + # Agar debit bo'lsa, uni oshirish (teskari) + counterparty.debit_uzs += expence.price + counterparty.total_debit += expence.price + + counterparty.kredit_uzs -= expence.price + counterparty.total_kredit -= expence.price + else: + # Agar kredit bo'lsa, uni kamayitish (teskari) + counterparty.kredit_uzs -= expence.price + counterparty.total_kredit -= expence.price + + counterparty.save() + + elif currency == 'usd': + # Balanslarni qaytarish (o'chirilgani teskari qilish) + cash_transaction.expence_balance_usd -= expence.price # ✅ KAMAYISHI KERAK + cash_transaction.total_balance_usd = ( + cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd + ) + payment_type.total_usd += expence.price # ✅ QAYTARISH + + # Kontrapartiya hisobini qaytarish + if counterparty: + if counterparty.debit_usd > 0: + # Agar debit bo'lsa, uni oshirish (teskari) + counterparty.debit_usd += expence.price + counterparty.total_debit += expence.price + + counterparty.kredit_usd -= expence.price + counterparty.total_kredit -= expence.price + else: + # Agar kredit bo'lsa, uni kamayitish (teskari) + counterparty.kredit_usd -= expence.price + counterparty.total_kredit -= expence.price + + counterparty.save() + + # is_deleted = True qilish (hard delete emas, soft delete) + expence.is_deleted = True + + # Barcha o'zgarishlari saqlash + cash_transaction.save() + payment_type.save() + expence.save() + + return Response( + { + 'success': True, + 'message': 'Expence o\'chirildi', + 'data': { + 'expence_id': expence.id, + 'price': expence.price, + 'currency': expence.currency + } + }, + status=200 + ) + expence = get_object_or_404(Expence, id=id) serializer = self.serializer_class(data=request.data) if serializer.is_valid(raise_exception=True): diff --git a/core/apps/finance/views/income.py b/core/apps/finance/views/income.py index 930479d..62853f8 100644 --- a/core/apps/finance/views/income.py +++ b/core/apps/finance/views/income.py @@ -1,4 +1,5 @@ from django.shortcuts import get_object_or_404 +from django.db import transaction from rest_framework import generics, views, parsers, filters from rest_framework.response import Response @@ -84,33 +85,93 @@ class IncomeDeleteApiView(generics.GenericAPIView): permission_classes = [HasRolePermission] def post(self, request, id): - income = get_object_or_404(Income, id=id) + income = get_object_or_404(Income, id=id, is_deleted=False) serializer = self.serializer_class(data=request.data) + if serializer.is_valid(raise_exception=True): - comment = serializer.validated_data.get('comment') - DeletedIncome.objects.create( - income=income, - comment=comment - ) - income.is_deleted = True - if income.currency == 'uzs': - income.cash_transaction.expence_balance_uzs -= income.price - income.cash_transaction.total_balance_uzs -= income.price - income.payment_type.total_uzs -= income.price if income.payment_type.total_uzs > income.price else 0 - else: - income.cash_transaction.expence_balance_usd -= income.price - income.cash_transaction.total_balance_usd -= income.price - income.payment_type.total_usd -= income.price if income.payment_type.total_usd > income.price else 0 + with transaction.atomic(): + comment = serializer.validated_data.get('comment') + currency = income.currency.lower() + + # Deleted record yaratish + DeletedIncome.objects.create( + income=income, + comment=comment + ) + + cash_transaction = income.cash_transaction + payment_type = income.payment_type + counterparty = income.counterparty + + if currency == 'uzs': + # Balanslarni qaytarish (o'chirilgani teskari qilish) + cash_transaction.income_balance_uzs -= income.price # ✅ INCOME BALANCE KAMAYISHI KERAK + cash_transaction.total_balance_uzs = ( + cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs + ) + payment_type.total_uzs -= income.price # ✅ QAYTARISH + + # Kontrapartiya hisobini qaytarish + if counterparty: + if counterparty.kredit_uzs > 0: + # Agar kredit bo'lsa, uni kamayitish (teskari) + counterparty.kredit_uzs -= income.price + counterparty.total_kredit -= income.price + + counterparty.debit_uzs += income.price + counterparty.total_debit += income.price + else: + # Agar debit bo'lsa, uni oshirish (teskari) + counterparty.debit_uzs += income.price + counterparty.total_debit += income.price + + counterparty.save() + + elif currency == 'usd': + # Balanslarni qaytarish (o'chirilgani teskari qilish) + cash_transaction.income_balance_usd -= income.price # ✅ INCOME BALANCE KAMAYISHI KERAK + cash_transaction.total_balance_usd = ( + cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd + ) + payment_type.total_usd -= income.price # ✅ QAYTARISH + + # Kontrapartiya hisobini qaytarish + if counterparty: + if counterparty.kredit_usd > 0: + # Agar kredit bo'lsa, uni kamayitish (teskari) + counterparty.kredit_usd -= income.price + counterparty.total_kredit -= income.price + + counterparty.debit_usd += income.price + counterparty.total_debit += income.price + else: + # Agar debit bo'lsa, uni oshirish (teskari) + counterparty.debit_usd += income.price + counterparty.total_debit += income.price + + counterparty.save() + + # is_deleted = True qilish (hard delete emas, soft delete) + income.is_deleted = True + + # Barcha o'zgarishlari saqlash + cash_transaction.save() + payment_type.save() + income.save() + + return Response( + { + 'success': True, + 'message': 'Income o\'chirildi', + 'data': { + 'income_id': income.id, + 'price': income.price, + 'currency': income.currency + } + }, + status=200 + ) - income.cash_transaction.save() - income.payment_type.save() - income.save() - return Response( - { - 'success': True, - 'message': 'Income deleted', - }, status=200 - ) class IncomeUpdateApiView(generics.GenericAPIView): diff --git a/core/apps/orders/serializers/party.py b/core/apps/orders/serializers/party.py index a1bc1f2..9a486b2 100644 --- a/core/apps/orders/serializers/party.py +++ b/core/apps/orders/serializers/party.py @@ -381,126 +381,154 @@ class PartyExpenceCreateSerializer(serializers.ModelSerializer): "party", ] + def validate(self, data): + """Validasiya qilish""" + price = data.get('price') + exchange_rate = data.get('exchange_rate') + party = data.get('party') + currency = data.get('currency', 'uzs').lower() + + if price and price < 0: + raise serializers.ValidationError("Narxi manfiy bo'lishi mumkin emas") + + if exchange_rate and exchange_rate < 0: + raise serializers.ValidationError("Kurs manfiy bo'lishi mumkin emas") + + if not party: + raise serializers.ValidationError("Party talab qilinadi") + + # USD kursi tekshiruvi + try: + usd_course = UsdCourse.objects.first() + if not usd_course or not usd_course.value: + raise serializers.ValidationError("USD kursi topilmadi") + except UsdCourse.DoesNotExist: + raise serializers.ValidationError("USD kursi topilmadi") + + return data + def create(self, validated_data): with transaction.atomic(): - usd_value = UsdCourse.objects.first().value + # USD kursi olish + usd_course = UsdCourse.objects.first() + if not usd_course: + raise serializers.ValidationError("USD kursi topilmadi") + + usd_value = usd_course.value + + # Exchange rate va final price hisoblash + exchange_rate = validated_data.get("exchange_rate") or 1 + final_price = validated_data.get("price") * exchange_rate + currency = validated_data.get("currency", "uzs").lower() + expence = Expence.objects.create( cash_transaction=validated_data.get("cash_transaction"), payment_type=validated_data.get("payment_type"), project_folder=validated_data.get("project_folder"), project=validated_data.get("project"), counterparty=validated_data.get("counterparty"), - price=( - validated_data.get("price") * validated_data.get("exchange_rate") - if validated_data.get("exchange_rate") - else validated_data.get("price") - ), - exchange_rate=validated_data.get("exchange_rate"), - currency=validated_data.get("currency"), + price=final_price, + exchange_rate=exchange_rate, + currency=currency, date=validated_data.get("date"), comment=validated_data.get("comment"), party=validated_data.get("party"), user=self.context.get('user'), ) + cash_transaction = expence.cash_transaction payment_type = expence.payment_type - - if validated_data.get("currency") == "uzs": + party = expence.party + + if currency == "uzs": + # Cash transaction balansini yangilash cash_transaction.expence_balance_uzs += expence.price cash_transaction.total_balance_uzs = ( cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs ) + + # Payment type balansini yangilash if payment_type.total_uzs > expence.price: payment_type.total_uzs -= expence.price - + + # Kontrapartiya hisobini yangilash if expence.counterparty: - if expence.counterparty.kredit_uzs != 0: + if expence.counterparty.kredit_uzs > 0: expence.counterparty.kredit_uzs -= expence.price expence.counterparty.total_kredit -= expence.price - - expence.counterparty.debit_uzs += ( - expence.counterparty.kredit_uzs - expence.price - ) + + # ✅ TUZATILDI: Debit to'g'ridan-to'g'ri oshadi + expence.counterparty.debit_uzs += expence.price expence.counterparty.total_debit += expence.price else: expence.counterparty.debit_uzs += expence.price expence.counterparty.total_debit += expence.price + expence.counterparty.save() - if expence.party.currency == "uzs": - expence.party.party_amount.payment_amount -= expence.price - expence.party.party_amount.paid_amount += expence.price - expence.party.party_amount.save() - expence.party.save() - elif expence.party.currency == 'usd': - expence.party.party_amount.payment_amount -= round(expence.price / usd_value) - expence.party.party_amount.paid_amount += round(expence.price / usd_value) - expence.party.save() - elif validated_data.get("currency") == "usd": + + # Party hisobini yangilash + if party: + if party.currency == "uzs": + # ✅ Birjani to'g'ri hisoblash + party.party_amount.payment_amount -= expence.price + party.party_amount.paid_amount += expence.price + party.party_amount.save() + elif party.currency == 'usd': + # ✅ USD kursi bilan to'g'ri konvertatsiya + converted_price = round(expence.price / usd_value) + party.party_amount.payment_amount -= converted_price + party.party_amount.paid_amount += converted_price + party.party_amount.save() + + party.save() + + elif currency == "usd": + # Cash transaction balansini yangilash cash_transaction.expence_balance_usd += expence.price cash_transaction.total_balance_usd = ( cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd ) + + # Payment type balansini yangilash if payment_type.total_usd > expence.price: payment_type.total_usd -= expence.price - + + # Kontrapartiya hisobini yangilash if expence.counterparty: - if expence.counterparty.kredit_usd != 0: - expence.counterparty.kredit_usd -= validated_data.get("price") + if expence.counterparty.kredit_usd > 0: + # ✅ TUZATILDI: expence.price ishlatish + expence.counterparty.kredit_usd -= expence.price expence.counterparty.total_kredit -= expence.price - - expence.counterparty.debit_usd += ( - expence.counterparty.kredit_usd - - validated_data.get("price") - ) + + # ✅ TUZATILDI: Debit to'g'ridan-to'g'ri oshadi + expence.counterparty.debit_usd += expence.price expence.counterparty.total_debit += expence.price else: - expence.counterparty.debit_usd += validated_data.get("price") + expence.counterparty.debit_usd += expence.price expence.counterparty.total_debit += expence.price + expence.counterparty.save() - - if expence.party.currency == "usd": - expence.party.party_amount.payment_amount -= validated_data.get( - "price" - ) - expence.party.party_amount.paid_amount += validated_data.get( - "price" - ) - expence.party.save() - elif expence.party.currency == "uzs": - expence.party.party_amount.payment_amount -= validated_data.get( - "price" - ) * usd_value - expence.party.party_amount.paid_amount += validated_data.get( - "price" - ) * usd_value - expence.party.save() - expence.party.party_amount.save() + + # Party hisobini yangilash + if party: + if party.currency == "usd": + # ✅ TUZATILDI: expence.price ishlatish + party.party_amount.payment_amount -= expence.price + party.party_amount.paid_amount += expence.price + party.party_amount.save() + elif party.currency == "uzs": + # ✅ TUZATILDI: expence.price bilan konvertatsiya + converted_price = expence.price * usd_value + party.party_amount.payment_amount -= converted_price + party.party_amount.paid_amount += converted_price + party.party_amount.save() + + party.save() + + # Barcha o'zgarishlari saqlash cash_transaction.save() payment_type.save() + return expence - - -class PartyPaymentHistorySerializer(serializers.ModelSerializer): - counterparty = serializers.SerializerMethodField(method_name='get_counterparty') - user = serializers.SerializerMethodField(method_name='get_user') - - class Meta: - model = Expence - fields = [ - 'id', 'counterparty', 'price', 'created_at', 'date', 'user' - ] - - def get_counterparty(self, obj): - return { - 'id': obj.counterparty.id, - 'name': obj.counterparty.name, - } if obj.counterparty else None - - def get_user(self, obj): - return { - 'id': obj.user.id, - 'full_name': obj.user.full_name, - 'profile_image': obj.user.profile_image.url if obj.user.profile_image else None - } if obj.user else None \ No newline at end of file