add: income-expence delete api

This commit is contained in:
behruz-dev
2025-09-25 16:37:41 +05:00
parent e854e5a7fe
commit 7ee8531aee
13 changed files with 214 additions and 7 deletions

View File

@@ -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']

View File

@@ -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']

View 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",
},
),
]

View 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),
),
]

View 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",
},
),
]

View 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),
),
]

View File

@@ -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"

View File

@@ -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"

View File

@@ -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()

View File

@@ -131,3 +131,7 @@ 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()

View File

@@ -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(

View File

@@ -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
@@ -108,3 +108,38 @@ class ChangeExpenceStatusApiView(views.APIView):
}, },
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
)

View File

@@ -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
@@ -77,3 +77,37 @@ class CounterpartyIncomeListApiView(generics.GenericAPIView):
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
)