add: income-expence delete api
This commit is contained in:
@@ -1,9 +1,14 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from core.apps.finance.models import Expence
|
from core.apps.finance.models import Expence, DeletedExpence
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Expence)
|
@admin.register(Expence)
|
||||||
class ExpenceAdmin(admin.ModelAdmin):
|
class ExpenceAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'price', 'cash_transaction', 'status']
|
list_display = ['id', 'price', 'cash_transaction', 'status']
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(DeletedExpence)
|
||||||
|
class DeletedExpenceAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['id', 'comment', 'expence']
|
||||||
|
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from core.apps.finance.models import Income
|
from core.apps.finance.models import Income, DeletedIncome
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Income)
|
@admin.register(Income)
|
||||||
class IncomeAdmin(admin.ModelAdmin):
|
class IncomeAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'price', 'cash_transaction']
|
list_display = ['id', 'price', 'cash_transaction']
|
||||||
list_filter = ['cash_transaction', 'payment_type']
|
list_filter = ['cash_transaction', 'payment_type']
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(DeletedIncome)
|
||||||
|
class DeletedIncomeAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ['id', 'comment', 'income']
|
||||||
|
|
||||||
29
core/apps/finance/migrations/0025_deletedexpence.py
Normal file
29
core/apps/finance/migrations/0025_deletedexpence.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-09-25 16:10
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('finance', '0024_expence_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DeletedExpence',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('comment', models.CharField(max_length=200)),
|
||||||
|
('expence', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deleted_expences', to='finance.expence')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': "O'chirilgan chiqim",
|
||||||
|
'verbose_name_plural': "O'chirilgan chiqim",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
18
core/apps/finance/migrations/0026_expence_is_deleted.py
Normal file
18
core/apps/finance/migrations/0026_expence_is_deleted.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-09-25 16:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('finance', '0025_deletedexpence'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='expence',
|
||||||
|
name='is_deleted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
29
core/apps/finance/migrations/0027_deletedincome.py
Normal file
29
core/apps/finance/migrations/0027_deletedincome.py
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-09-25 16:30
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('finance', '0026_expence_is_deleted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='DeletedIncome',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('comment', models.CharField(max_length=200)),
|
||||||
|
('income', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='deleted_incomes', to='finance.income')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': "O'chirilgan kirim",
|
||||||
|
'verbose_name_plural': "O'chirilgan kirimlar",
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
18
core/apps/finance/migrations/0028_income_is_deleted.py
Normal file
18
core/apps/finance/migrations/0028_income_is_deleted.py
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-09-25 16:36
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('finance', '0027_deletedincome'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='income',
|
||||||
|
name='is_deleted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -41,6 +41,7 @@ class Expence(BaseModel):
|
|||||||
audit = models.CharField(max_length=200, null=True, blank=True)
|
audit = models.CharField(max_length=200, null=True, blank=True)
|
||||||
file = models.FileField(null=True, blank=True, upload_to='finance/expence/files/')
|
file = models.FileField(null=True, blank=True, upload_to='finance/expence/files/')
|
||||||
status = models.CharField(max_length=20, choices=STATUS, default='PENDING', null=True, blank=True)
|
status = models.CharField(max_length=20, choices=STATUS, default='PENDING', null=True, blank=True)
|
||||||
|
is_deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.cash_transaction} kassa uchun chiqim {self.price}'
|
return f'{self.cash_transaction} kassa uchun chiqim {self.price}'
|
||||||
@@ -49,3 +50,14 @@ class Expence(BaseModel):
|
|||||||
verbose_name = 'chiqim'
|
verbose_name = 'chiqim'
|
||||||
verbose_name_plural = 'chiqimlar'
|
verbose_name_plural = 'chiqimlar'
|
||||||
|
|
||||||
|
|
||||||
|
class DeletedExpence(BaseModel):
|
||||||
|
expence = models.ForeignKey(Expence, on_delete=models.CASCADE, related_name='deleted_expences')
|
||||||
|
comment = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.expence} is deleted'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "O'chirilgan chiqim"
|
||||||
|
verbose_name_plural = "O'chirilgan chiqim"
|
||||||
@@ -32,6 +32,7 @@ class Income(BaseModel):
|
|||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
file = models.FileField(upload_to='finance/income/file/', null=True, blank=True)
|
file = models.FileField(upload_to='finance/income/file/', null=True, blank=True)
|
||||||
audit = models.CharField(max_length=200, null=True, blank=True)
|
audit = models.CharField(max_length=200, null=True, blank=True)
|
||||||
|
is_deleted = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.cash_transaction} kassa uchun kirim {self.price}'
|
return f'{self.cash_transaction} kassa uchun kirim {self.price}'
|
||||||
@@ -41,3 +42,13 @@ class Income(BaseModel):
|
|||||||
verbose_name_plural = 'kirimlar'
|
verbose_name_plural = 'kirimlar'
|
||||||
|
|
||||||
|
|
||||||
|
class DeletedIncome(BaseModel):
|
||||||
|
income = models.ForeignKey(Income, on_delete=models.CASCADE, related_name='deleted_incomes')
|
||||||
|
comment = models.CharField(max_length=200)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'{self.income} is deleted'
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "O'chirilgan kirim"
|
||||||
|
verbose_name_plural = "O'chirilgan kirimlar"
|
||||||
@@ -133,3 +133,7 @@ class ExpenceListSerializer(serializers.ModelSerializer):
|
|||||||
'id': obj.expence_type.id,
|
'id': obj.expence_type.id,
|
||||||
'name': obj.expence_type.name
|
'name': obj.expence_type.name
|
||||||
} if obj.expence_type else None
|
} if obj.expence_type else None
|
||||||
|
|
||||||
|
|
||||||
|
class ExpenceDeleteSerializer(serializers.Serializer):
|
||||||
|
comment = serializers.CharField()
|
||||||
@@ -130,4 +130,8 @@ class IncomeCreateSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
cash_transaction.save()
|
cash_transaction.save()
|
||||||
payment_type.save()
|
payment_type.save()
|
||||||
return income
|
return income
|
||||||
|
|
||||||
|
|
||||||
|
class IncomeDeleteSerializer(serializers.Serializer):
|
||||||
|
comment = serializers.CharField()
|
||||||
@@ -50,6 +50,7 @@ urlpatterns = [
|
|||||||
path('list/', income_views.IncomeListApiView.as_view()),
|
path('list/', income_views.IncomeListApiView.as_view()),
|
||||||
path('create/', income_views.IncomeCreateApiView.as_view()),
|
path('create/', income_views.IncomeCreateApiView.as_view()),
|
||||||
path('<uuid:counterparty_id>/list/', income_views.CounterpartyIncomeListApiView.as_view()),
|
path('<uuid:counterparty_id>/list/', income_views.CounterpartyIncomeListApiView.as_view()),
|
||||||
|
path('<uuid:id>/delete/', income_views.IncomeDeleteApiView.as_view()),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
path('expence_type/', include(
|
path('expence_type/', include(
|
||||||
@@ -66,6 +67,7 @@ urlpatterns = [
|
|||||||
path('create/', expence_views.ExpenceCreateApiView.as_view()),
|
path('create/', expence_views.ExpenceCreateApiView.as_view()),
|
||||||
path('<uuid:counterparty_id>/list/', expence_views.CounterpartyExpenceListApiView.as_view()),
|
path('<uuid:counterparty_id>/list/', expence_views.CounterpartyExpenceListApiView.as_view()),
|
||||||
path('<uuid:id>/change-status/', expence_views.ChangeExpenceStatusApiView.as_view()),
|
path('<uuid:id>/change-status/', expence_views.ChangeExpenceStatusApiView.as_view()),
|
||||||
|
path('<uuid:id>/delete/', expence_views.ExpenceDeleteApiView.as_view()),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
path('income_contract/', include(
|
path('income_contract/', include(
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from rest_framework.response import Response
|
|||||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||||
|
|
||||||
from core.apps.accounts.permissions.permissions import HasRolePermission
|
from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||||
from core.apps.finance.models import Expence
|
from core.apps.finance.models import Expence, DeletedExpence
|
||||||
from core.apps.finance.serializers import expence as serializers
|
from core.apps.finance.serializers import expence as serializers
|
||||||
from core.apps.finance.filters.expence import ExpenceFilter
|
from core.apps.finance.filters.expence import ExpenceFilter
|
||||||
from core.apps.counterparty.models import Counterparty
|
from core.apps.counterparty.models import Counterparty
|
||||||
@@ -107,4 +107,39 @@ class ChangeExpenceStatusApiView(views.APIView):
|
|||||||
'message': 'expence status successfully updated',
|
'message': 'expence status successfully updated',
|
||||||
},
|
},
|
||||||
status=200
|
status=200
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ExpenceDeleteApiView(generics.GenericAPIView):
|
||||||
|
serializer_class = serializers.ExpenceDeleteSerializer
|
||||||
|
queryset = Expence.objects.all()
|
||||||
|
permission_classes = [HasRolePermission]
|
||||||
|
|
||||||
|
def post(self, request, id):
|
||||||
|
expence = get_object_or_404(Expence, id=id)
|
||||||
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
if serializer.is_valid(raise_exception=True):
|
||||||
|
comment = serializer.validated_data.get('comment')
|
||||||
|
DeletedExpence.objects.create(
|
||||||
|
expence=expence,
|
||||||
|
comment=comment
|
||||||
|
)
|
||||||
|
expence.is_deleted = True
|
||||||
|
if expence.currency == 'uzs':
|
||||||
|
expence.cash_transaction.expence_balance_uzs += expence.price
|
||||||
|
expence.cash_transaction.total_balance_uzs += expence.price
|
||||||
|
expence.payment_type.total_uzs += expence.price
|
||||||
|
else:
|
||||||
|
expence.cash_transaction.expence_balance_usd += expence.price
|
||||||
|
expence.cash_transaction.total_balance_usd += expence.price
|
||||||
|
expence.payment_type.total_usd += expence.price
|
||||||
|
|
||||||
|
expence.cash_transaction.save()
|
||||||
|
expence.payment_type.save()
|
||||||
|
expence.save()
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
'success': True,
|
||||||
|
'message': 'Expence deleted',
|
||||||
|
}, status=200
|
||||||
|
)
|
||||||
@@ -5,7 +5,7 @@ from rest_framework.response import Response
|
|||||||
|
|
||||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||||
|
|
||||||
from core.apps.finance.models import Income
|
from core.apps.finance.models import Income, DeletedIncome
|
||||||
from core.apps.finance.serializers import income as serializers
|
from core.apps.finance.serializers import income as serializers
|
||||||
from core.apps.accounts.permissions.permissions import HasRolePermission
|
from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||||
from core.apps.finance.filters.income import IncomeFilter
|
from core.apps.finance.filters.income import IncomeFilter
|
||||||
@@ -76,4 +76,38 @@ class CounterpartyIncomeListApiView(generics.GenericAPIView):
|
|||||||
if page is not None:
|
if page is not None:
|
||||||
ser = self.serializer_class(page, many=True)
|
ser = self.serializer_class(page, many=True)
|
||||||
return self.get_paginated_response(ser.data)
|
return self.get_paginated_response(ser.data)
|
||||||
|
|
||||||
|
|
||||||
|
class IncomeDeleteApiView(generics.GenericAPIView):
|
||||||
|
serializer_class = serializers.IncomeDeleteSerializer
|
||||||
|
queryset = Income.objects.all()
|
||||||
|
permission_classes = [HasRolePermission]
|
||||||
|
|
||||||
|
def post(self, request, id):
|
||||||
|
income = get_object_or_404(Income, id=id)
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
income.cash_transaction.expence_balance_usd -= income.price
|
||||||
|
income.cash_transaction.total_balance_usd -= income.price
|
||||||
|
income.payment_type.total_usd -= income.price
|
||||||
|
|
||||||
|
income.cash_transaction.save()
|
||||||
|
income.payment_type.save()
|
||||||
|
income.save()
|
||||||
|
return Response(
|
||||||
|
{
|
||||||
|
'success': True,
|
||||||
|
'message': 'Income deleted',
|
||||||
|
}, status=200
|
||||||
|
)
|
||||||
Reference in New Issue
Block a user