diff --git a/core/apps/finance/admin/__init__.py b/core/apps/finance/admin/__init__.py index b69fa20..2feea35 100644 --- a/core/apps/finance/admin/__init__.py +++ b/core/apps/finance/admin/__init__.py @@ -1,2 +1,3 @@ from .cash_transaction import * -from .payment_type import * \ No newline at end of file +from .payment_type import * +from .type_income import * \ No newline at end of file diff --git a/core/apps/finance/admin/type_income.py b/core/apps/finance/admin/type_income.py new file mode 100644 index 0000000..13256a8 --- /dev/null +++ b/core/apps/finance/admin/type_income.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from core.apps.finance.models import TypeIncome + + +@admin.register(TypeIncome) +class TypeIncomeAdmin(admin.ModelAdmin): + list_display = ['id', 'name'] + diff --git a/core/apps/finance/migrations/0006_typeincome.py b/core/apps/finance/migrations/0006_typeincome.py new file mode 100644 index 0000000..37618b0 --- /dev/null +++ b/core/apps/finance/migrations/0006_typeincome.py @@ -0,0 +1,30 @@ +# Generated by Django 5.2.4 on 2025-09-08 15:35 + +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0005_rename_expence_balance_cashtransaction_expence_balance_usd_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='TypeIncome', + 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)), + ('name', models.CharField(max_length=200)), + ('category', models.CharField(choices=[('OTHERS', 'boshqalar'), ('CONTANT_INCOME', 'doimiy daromad')], max_length=20)), + ('activity', models.CharField(choices=[('FINANCIAL', 'moliyaviy'), ('CAPITAL', 'sarmoya')], max_length=20)), + ('comment', models.CharField(max_length=200)), + ('status', models.BooleanField(default=True)), + ], + options={ + 'verbose_name': 'Daromad turlari', + }, + ), + ] diff --git a/core/apps/finance/models/__init__.py b/core/apps/finance/models/__init__.py index 85454e1..caa3f73 100644 --- a/core/apps/finance/models/__init__.py +++ b/core/apps/finance/models/__init__.py @@ -1,2 +1,4 @@ from .cash_transaction import * -from .payment_type import * \ No newline at end of file +from .payment_type import * +from .type_income import * +from .income import * \ No newline at end of file diff --git a/core/apps/finance/models/income.py b/core/apps/finance/models/income.py new file mode 100644 index 0000000..ee4f6f0 --- /dev/null +++ b/core/apps/finance/models/income.py @@ -0,0 +1,41 @@ +from django.db import models + +from core.apps.shared.models import BaseModel +from core.apps.finance.models import CashTransaction, PaymentType, TypeIncome +from core.apps.counterparty.models import Counterparty + + +class Income(BaseModel): + cash_transaction = models.ForeignKey( + CashTransaction, on_delete=models.CASCADE, related_name='incomes' + ) + payment_type = models.ForeignKey(PaymentType, on_delete=models.CASCADE, related_name='incomes') + project_folder = models.ForeignKey( + 'projects.ProjectFolder', on_delete=models.CASCADE, related_name='incomes' + ) + project = models.ForeignKey( + 'projects.Project', on_delete=models.SET_NULL, related_name='incomes', null=True + ) + counterparty = models.ForeignKey( + Counterparty, on_delete=models.SET_NULL, related_name='incomes', null=True + ) + type_income = models.ForeignKey( + TypeIncome, on_delete=models.SET_NULL, related_name='incomes', null=True + ) + + currency = models.CharField(choices=[('uzs', 'uzs'),('usd', 'usd')], max_length=3) + price = models.PositiveBigIntegerField() + exchange_rate = models.PositiveBigIntegerField(default=0) + date = models.DateField() + comment = models.TextField(null=True, blank=True) + file = models.FileField(upload_to='finance/income/file/', null=True, blank=True) + audit = models.CharField(max_length=200) + + def __str__(self): + return f'{self.cash_transaction} kassa uchun kirim {self.price}' + + class Meta: + verbose_name = 'kirim' + verbose_name_plural = 'kirimlar' + + diff --git a/core/apps/finance/models/type_income.py b/core/apps/finance/models/type_income.py new file mode 100644 index 0000000..20bd793 --- /dev/null +++ b/core/apps/finance/models/type_income.py @@ -0,0 +1,23 @@ +from django.db import models + +from core.apps.shared.models import BaseModel + + +class TypeIncome(BaseModel): + name = models.CharField(max_length=200) + category = models.CharField( + choices=[('OTHERS', 'boshqalar'), ('CONTANT_INCOME', 'doimiy daromad')], max_length=20 + ) + activity = models.CharField( + max_length=20, choices=[('FINANCIAL', 'moliyaviy'), ('CAPITAL', 'sarmoya')] + ) + comment = models.CharField(max_length=200) + status = models.BooleanField(default=True) + + def __str__(self): + return self.name + + class Meta: + verbose_name = 'Daromad turi' + verbose_name = 'Daromad turlari' + \ No newline at end of file diff --git a/core/apps/finance/serializers/type_income.py b/core/apps/finance/serializers/type_income.py new file mode 100644 index 0000000..b0b81d3 --- /dev/null +++ b/core/apps/finance/serializers/type_income.py @@ -0,0 +1,11 @@ +from rest_framework import serializers + +from core.apps.finance.models import TypeIncome + + +class TypeIncomeSerializer(serializers.ModelSerializer): + class Meta: + model = TypeIncome + fields = [ + 'id', 'name', 'category', 'activity', 'comment', 'status' + ] \ No newline at end of file diff --git a/core/apps/finance/urls.py b/core/apps/finance/urls.py index ed9dd2d..d00c14a 100644 --- a/core/apps/finance/urls.py +++ b/core/apps/finance/urls.py @@ -3,6 +3,7 @@ from django.urls import path, include from core.apps.finance.views import cash_transaction as cash_views from core.apps.finance.views import cash_transaction_folder as folder_views from core.apps.finance.views import payment_type as pt_views +from core.apps.finance.views import type_income as ti_views urlpatterns = [ @@ -29,5 +30,13 @@ urlpatterns = [ path('/delete/', pt_views.PaymentDeleteApiView.as_view()), path('/update/', pt_views.PaymentUpdateApiView.as_view()), ] + )), + path('type_income/', include( + [ + path('create/', ti_views.TypeIncomeCreateApiView.as_view()), + path('list/', ti_views.TypeIncomeListApiView.as_view()), + path('/update/', ti_views.TypeIncomeUpdateApiView.as_view()), + path('/delete/', ti_views.TypeIncomeDeleteApiView.as_view()), + ] )) ] \ No newline at end of file diff --git a/core/apps/finance/views/type_income.py b/core/apps/finance/views/type_income.py new file mode 100644 index 0000000..a2ee862 --- /dev/null +++ b/core/apps/finance/views/type_income.py @@ -0,0 +1,84 @@ +from django.shortcuts import get_object_or_404 + +from rest_framework import generics, views, status +from rest_framework.response import Response + +from core.apps.finance.models import TypeIncome +from core.apps.finance.serializers.type_income import TypeIncomeSerializer +from core.apps.accounts.permissions.permissions import HasRolePermission + + +class TypeIncomeCreateApiView(generics.GenericAPIView): + serializer_class = TypeIncomeSerializer + queryset = TypeIncome.objects.all() + permission_classes = [HasRolePermission] + + def post(self, request): + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + { + 'success': True, + 'message': 'Type income created' + }, + status=201 + ) + return Response( + { + 'success': False, + 'message': 'Type income not created', + 'error': serializer.errors, + }, + status=400 + ) + + +class TypeIncomeListApiView(generics.GenericAPIView): + permission_classes = [HasRolePermission] + queryset = TypeIncome.objects.all() + serializer_class = TypeIncomeSerializer + + def get(self, request): + page = self.paginate_queryset(self.queryset) + if page is not None: + ser = self.serializer_class(page, many=True) + return self.get_paginated_response(ser.data) + + +class TypeIncomeUpdateApiView(generics.GenericAPIView): + serializer_class = TypeIncomeSerializer + queryset = TypeIncome.objects.all() + permission_classes = [HasRolePermission] + + def patch(self, request, id): + obj = get_object_or_404(TypeIncome, id=id) + ser = self.serializer_class(data=request.data, instance=obj, partial=True) + if ser.is_valid(raise_exception=True): + ser.save() + return Response( + { + 'success': True, + 'message': 'updated' + }, + status=200 + ) + return Response( + { + 'success': False, + 'message': 'error', + 'error': ser.errors, + }, + status=400 + ) + + +class TypeIncomeDeleteApiView(views.APIView): + permission_classes = [HasRolePermission] + + def delete(self, request, id): + obj = get_object_or_404(TypeIncome, id=id) + obj.delete() + return Response( + {'success': True, 'message': 'deleted'}, status=204 + ) diff --git a/core/apps/projects/models/project.py b/core/apps/projects/models/project.py index 1550e6c..d01cab6 100644 --- a/core/apps/projects/models/project.py +++ b/core/apps/projects/models/project.py @@ -5,7 +5,7 @@ from core.apps.shared.models import BaseModel, Region, District from core.apps.projects.models.builder import Builder from core.apps.accounts.models.user import User from core.apps.wherehouse.models.wherehouse import WhereHouse -from core.apps.finance.models import CashTransaction +from core.apps.finance.models.cash_transaction import CashTransaction class ProjectFolder(BaseModel):