add: add income model and income create and list apig
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
from .cash_transaction import *
|
||||
from .payment_type import *
|
||||
from .type_income import *
|
||||
from .income import *
|
||||
9
core/apps/finance/admin/income.py
Normal file
9
core/apps/finance/admin/income.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from core.apps.finance.models import Income
|
||||
|
||||
|
||||
@admin.register(Income)
|
||||
class IncomeAdmin(admin.ModelAdmin):
|
||||
list_display = ['id', 'price', 'cash_transaction']
|
||||
list_filter = ['cash_transaction', 'payment_type']
|
||||
42
core/apps/finance/migrations/0007_income.py
Normal file
42
core/apps/finance/migrations/0007_income.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 5.2.4 on 2025-09-08 16:18
|
||||
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('counterparty', '0004_counterparty_status'),
|
||||
('finance', '0006_typeincome'),
|
||||
('projects', '0016_estimatework_employee_estimatework_end_date_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Income',
|
||||
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)),
|
||||
('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(blank=True, null=True)),
|
||||
('file', models.FileField(blank=True, null=True, upload_to='finance/income/file/')),
|
||||
('audit', models.CharField(max_length=200)),
|
||||
('cash_transaction', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='incomes', to='finance.cashtransaction')),
|
||||
('counterparty', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='counterparty.counterparty')),
|
||||
('payment_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='incomes', to='finance.paymenttype')),
|
||||
('project', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='projects.project')),
|
||||
('project_folder', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='incomes', to='projects.projectfolder')),
|
||||
('type_income', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='finance.typeincome')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'kirim',
|
||||
'verbose_name_plural': 'kirimlar',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Generated by Django 5.2.4 on 2025-09-08 16:19
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('counterparty', '0004_counterparty_status'),
|
||||
('finance', '0007_income'),
|
||||
('projects', '0016_estimatework_employee_estimatework_end_date_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='income',
|
||||
name='audit',
|
||||
field=models.CharField(blank=True, max_length=200, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='income',
|
||||
name='counterparty',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='counterparty.counterparty'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='income',
|
||||
name='project',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='projects.project'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='income',
|
||||
name='type_income',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='incomes', to='finance.typeincome'),
|
||||
),
|
||||
]
|
||||
@@ -14,13 +14,13 @@ class Income(BaseModel):
|
||||
'projects.ProjectFolder', on_delete=models.CASCADE, related_name='incomes'
|
||||
)
|
||||
project = models.ForeignKey(
|
||||
'projects.Project', on_delete=models.SET_NULL, related_name='incomes', null=True
|
||||
'projects.Project', on_delete=models.SET_NULL, related_name='incomes', null=True, blank=True
|
||||
)
|
||||
counterparty = models.ForeignKey(
|
||||
Counterparty, on_delete=models.SET_NULL, related_name='incomes', null=True
|
||||
Counterparty, on_delete=models.SET_NULL, related_name='incomes', null=True, blank=True
|
||||
)
|
||||
type_income = models.ForeignKey(
|
||||
TypeIncome, on_delete=models.SET_NULL, related_name='incomes', null=True
|
||||
TypeIncome, on_delete=models.SET_NULL, related_name='incomes', null=True, blank=True
|
||||
)
|
||||
|
||||
currency = models.CharField(choices=[('uzs', 'uzs'),('usd', 'usd')], max_length=3)
|
||||
@@ -29,7 +29,7 @@ class Income(BaseModel):
|
||||
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)
|
||||
audit = models.CharField(max_length=200, null=True, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.cash_transaction} kassa uchun kirim {self.price}'
|
||||
|
||||
@@ -39,16 +39,22 @@ class CashTransactionUpdateSerializer(serializers.ModelSerializer):
|
||||
'name', 'payment_type', 'employees', 'status', 'folder',
|
||||
]
|
||||
|
||||
|
||||
class CashTransactionCreateSerializer(serializers.Serializer):
|
||||
payment_type_id = serializers.UUIDField()
|
||||
employee_ids = serializers.ListSerializer(child=serializers.UUIDField())
|
||||
employee_ids = serializers.ListSerializer(child=serializers.UUIDField(), write_only=True)
|
||||
name = serializers.CharField()
|
||||
status = serializers.BooleanField()
|
||||
folder_id = serializers.UUIDField(required=False)
|
||||
|
||||
def validate_name(self, value):
|
||||
if CashTransaction.objects.filter(name=value).exists():
|
||||
raise serializers.ValidationError('cash transaction with this name already exists')
|
||||
return value
|
||||
|
||||
def validate(self, data):
|
||||
payment_type = PaymentType.objects.filter(id=data['id']).first()
|
||||
if payment_type:
|
||||
payment_type = PaymentType.objects.filter(id=data['payment_type_id']).first()
|
||||
if not payment_type:
|
||||
raise serializers.ValidationError("Payment Type not found")
|
||||
if data.get('folder_id'):
|
||||
folder = CashTransactionFolder.objects.filter(id=data.get('folder_id')).first()
|
||||
@@ -60,7 +66,7 @@ class CashTransactionCreateSerializer(serializers.Serializer):
|
||||
|
||||
def create(self, validated_data):
|
||||
with transaction.atomic():
|
||||
employee_ids = validated_data.pop('employee_ids')
|
||||
employee_ids = validated_data.pop('employee_ids', [])
|
||||
cash_transaction = CashTransaction.objects.create(
|
||||
name=validated_data.get('name'),
|
||||
payment_type=validated_data.get('payment_type'),
|
||||
|
||||
97
core/apps/finance/serializers/income.py
Normal file
97
core/apps/finance/serializers/income.py
Normal file
@@ -0,0 +1,97 @@
|
||||
from django.db import transaction
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from core.apps.finance.models import Income
|
||||
|
||||
|
||||
class IncomeListSerializer(serializers.ModelSerializer):
|
||||
cash_transaction = serializers.SerializerMethodField(method_name='get_cash_transaction')
|
||||
payment_type = serializers.SerializerMethodField(method_name='get_payment_type')
|
||||
project_folder = serializers.SerializerMethodField(method_name='get_project_folder')
|
||||
project = serializers.SerializerMethodField(method_name='get_project')
|
||||
counterparty = serializers.SerializerMethodField(method_name='get_counterparty')
|
||||
type_income = serializers.SerializerMethodField(method_name='get_type_income')
|
||||
|
||||
class Meta:
|
||||
model = Income
|
||||
fields = [
|
||||
'id', 'cash_transaction', 'payment_type', 'project_folder', 'project',
|
||||
'counterparty', 'type_income', 'currency', 'price', 'exchange_rate', 'date',
|
||||
'comment', 'file', 'audit'
|
||||
]
|
||||
|
||||
def get_cash_transaction(self, obj):
|
||||
return {
|
||||
'id': obj.cash_transaction.id,
|
||||
'name': obj.cash_transaction.name
|
||||
}
|
||||
|
||||
def get_payment_type(self, obj):
|
||||
return {
|
||||
'id': obj.payment_type.id,
|
||||
'name': obj.payment_type.name
|
||||
}
|
||||
|
||||
def get_project_folder(self, obj):
|
||||
return {
|
||||
'id': obj.project_folder.id,
|
||||
'name': obj.project_folder.name
|
||||
}
|
||||
|
||||
def get_project(self, obj):
|
||||
return {
|
||||
'id': obj.project.id,
|
||||
'name': obj.project.name
|
||||
} if obj.project else None
|
||||
|
||||
def get_counterparty(self, obj):
|
||||
return {
|
||||
'id': obj.counterparty.id,
|
||||
'name': obj.counterparty.name
|
||||
} if obj.counterparty else None
|
||||
|
||||
def get_type_income(self, obj):
|
||||
return {
|
||||
'id': obj.type_income.id,
|
||||
'name': obj.type_income.name
|
||||
} if obj.type_income else None
|
||||
|
||||
|
||||
class IncomeCreateSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Income
|
||||
fields = [
|
||||
'cash_transaction', 'payment_type', 'project_folder', 'project',
|
||||
'counterparty', 'type_income', 'currency', 'price', 'exchange_rate', 'date',
|
||||
'comment', 'file', 'audit'
|
||||
]
|
||||
|
||||
def create(self, validated_data):
|
||||
with transaction.atomic():
|
||||
income = Income.objects.create(
|
||||
cash_transaction=validated_data['cash_transaction'],
|
||||
payment_type=validated_data['payment_type'],
|
||||
project_folder=validated_data['project_folder'],
|
||||
project=validated_data.get('project'),
|
||||
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'),
|
||||
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
|
||||
|
||||
if validated_data.get('currency') == 'uzs':
|
||||
cash_transaction.income_balance_uzs += income.price
|
||||
cash_transaction.total_balance_uzs = cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs
|
||||
elif validated_data.get('currency') == 'usd':
|
||||
cash_transaction.income_balance_usd += income.price
|
||||
cash_transaction.total_balance_usd = cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd
|
||||
|
||||
cash_transaction.save()
|
||||
return income
|
||||
@@ -4,6 +4,7 @@ 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
|
||||
from core.apps.finance.views import income as income_views
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
@@ -38,5 +39,11 @@ urlpatterns = [
|
||||
path('<uuid:id>/update/', ti_views.TypeIncomeUpdateApiView.as_view()),
|
||||
path('<uuid:id>/delete/', ti_views.TypeIncomeDeleteApiView.as_view()),
|
||||
]
|
||||
)),
|
||||
path('income/', include(
|
||||
[
|
||||
path('list/', income_views.IncomeListApiView.as_view()),
|
||||
path('create/', income_views.IncomeCreateApiView.as_view()),
|
||||
]
|
||||
))
|
||||
]
|
||||
49
core/apps/finance/views/income.py
Normal file
49
core/apps/finance/views/income.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics, views, parsers
|
||||
from rest_framework.response import Response
|
||||
|
||||
from core.apps.finance.models import Income
|
||||
from core.apps.finance.serializers import income as serializers
|
||||
from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||
|
||||
|
||||
class IncomeListApiView(generics.GenericAPIView):
|
||||
serializer_class = serializers.IncomeListSerializer
|
||||
queryset = Income.objects.select_related(
|
||||
'cash_transaction', 'payment_type', 'project_folder', 'project', 'counterparty', 'type_income'
|
||||
)
|
||||
permission_classes = [HasRolePermission]
|
||||
|
||||
def get(self, request):
|
||||
page = self.paginate_queryset(self.queryset)
|
||||
if page is not None:
|
||||
serializer = self.serializer_class(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
|
||||
class IncomeCreateApiView(generics.GenericAPIView):
|
||||
serializer_class = serializers.IncomeCreateSerializer
|
||||
queryset = Income.objects.all()
|
||||
permission_classes = [HasRolePermission]
|
||||
parser_classes = [parsers.FormParser, parsers.MultiPartParser]
|
||||
|
||||
def post(self, request):
|
||||
ser = self.serializer_class(data=request.data)
|
||||
if ser.is_valid(raise_exception=True):
|
||||
ser.save()
|
||||
return Response(
|
||||
{
|
||||
'success': True,
|
||||
'message': 'income created'
|
||||
},
|
||||
status=201
|
||||
)
|
||||
return Response(
|
||||
{
|
||||
'success': False,
|
||||
'message': 'income create failed',
|
||||
'error': ser.errors,
|
||||
},
|
||||
status=400
|
||||
)
|
||||
Reference in New Issue
Block a user