diff --git a/core/apps/counterparty/admin/counterparty.py b/core/apps/counterparty/admin/counterparty.py index fb72859..6bf7b6f 100644 --- a/core/apps/counterparty/admin/counterparty.py +++ b/core/apps/counterparty/admin/counterparty.py @@ -1,15 +1,26 @@ from django.contrib import admin -from core.apps.counterparty.models import Counterparty, CounterpartyFolder +from core.apps.counterparty.models import Counterparty, CounterpartyFolder, CounterpartyBalance +class CounterpartyBalanceInline(admin.StackedInline): + model = CounterpartyBalance + can_delete = False + verbose_name_plural = 'Balance' + fk_name = 'counterparty' + extra = 0 + + @admin.register(Counterparty) class CounterpartyAdmin(admin.ModelAdmin): list_display = ['id', 'name', 'phone', 'type', 'inn'] + inlines = [CounterpartyBalanceInline] + + def get_queryset(self, request): + return super().get_queryset(request).select_related('balance') @admin.register(CounterpartyFolder) class CounterpartyFolderAdmin(admin.ModelAdmin): list_display = ['id', 'name'] - list_filter = ['name'] - \ No newline at end of file + list_filter = ['name'] \ No newline at end of file diff --git a/core/apps/counterparty/migrations/0008_remove_counterparty_balance_and_more.py b/core/apps/counterparty/migrations/0008_remove_counterparty_balance_and_more.py new file mode 100644 index 0000000..4fe06c6 --- /dev/null +++ b/core/apps/counterparty/migrations/0008_remove_counterparty_balance_and_more.py @@ -0,0 +1,71 @@ +# Generated by Django 5.2.4 on 2025-11-07 11:09 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('counterparty', '0007_alter_counterparty_balance'), + ] + + operations = [ + migrations.RemoveField( + model_name='counterparty', + name='balance', + ), + migrations.RemoveField( + model_name='counterparty', + name='balance_currency', + ), + migrations.RemoveField( + model_name='counterparty', + name='balance_date', + ), + migrations.RemoveField( + model_name='counterparty', + name='debit_usd', + ), + migrations.RemoveField( + model_name='counterparty', + name='debit_uzs', + ), + migrations.RemoveField( + model_name='counterparty', + name='kredit_usd', + ), + migrations.RemoveField( + model_name='counterparty', + name='kredit_uzs', + ), + migrations.RemoveField( + model_name='counterparty', + name='total_debit', + ), + migrations.RemoveField( + model_name='counterparty', + name='total_kredit', + ), + migrations.CreateModel( + name='CounterpartyBalance', + 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)), + ('balance_uzs', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('balance_usd', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('balance_date', models.DateField(blank=True, null=True)), + ('kredit_usd', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('kredit_uzs', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('debit_usd', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('debit_uzs', models.DecimalField(decimal_places=2, default=0.0, max_digits=15)), + ('counterparty', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='balance', to='counterparty.counterparty')), + ], + options={ + 'verbose_name': 'Kontragent Balansi', + 'verbose_name_plural': 'Kontragent Balanslari', + }, + ), + ] diff --git a/core/apps/counterparty/models/conterparty.py b/core/apps/counterparty/models/conterparty.py index a2e966d..e9ee5ac 100644 --- a/core/apps/counterparty/models/conterparty.py +++ b/core/apps/counterparty/models/conterparty.py @@ -1,3 +1,5 @@ +from decimal import Decimal + from django.db import models from core.apps.shared.models import BaseModel, Region, District @@ -41,24 +43,71 @@ class Counterparty(BaseModel): district = models.ForeignKey( District, on_delete=models.SET_NULL, null=True, blank=True, related_name='counterparties' ) - balance = models.BigIntegerField(null=True, blank=True) - balance_currency = models.CharField( - max_length=3, choices=[('usd', 'usd'), ('uzs', 'uzs')], null=True, blank=True - ) - balance_date = models.DateField(null=True, blank=True) comment = models.TextField(null=True, blank=True) is_archived = models.BooleanField(default=False) - - debit_usd = models.BigIntegerField(default=0, null=True, blank=True) - debit_uzs = models.BigIntegerField(default=0, null=True, blank=True) - total_debit = models.BigIntegerField(default=0, null=True, blank=True) - kredit_usd = models.BigIntegerField(default=0, null=True, blank=True) - kredit_uzs = models.BigIntegerField(default=0, null=True, blank=True) - total_kredit = models.BigIntegerField(default=0, null=True, blank=True) - + def __str__(self): return self.name class Meta: verbose_name = 'Kontragent' verbose_name_plural = 'Kontragentlar' + + +class CounterpartyBalance(BaseModel): + counterparty = models.OneToOneField(Counterparty, on_delete=models.CASCADE, related_name='balance') + # balance + balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + # balance date + balance_date = models.DateField(null=True, blank=True) + # kreditor -> qazrdorlik + kredit_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + kredit_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + # debitor -> xaqdorlik + debit_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + debit_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + + def save(self, *args, **kwargs): + if self.balance_usd > 0: + self.debit_usd = self.balance_usd + self.kredit_usd = Decimal('0.00') + elif self.balance_usd < 0: + self.kredit_usd = abs(self.balance_usd) + self.debit_usd = Decimal('0.00') + + if self.balance_uzs > 0: + self.debit_uzs = self.balance_uzs + self.kredit_uzs = Decimal('0.00') + elif self.balance_uzs < 0: + self.kredit_uzs = abs(self.balance_uzs) + self.debit_uzs = Decimal('0.00') + + super().save(*args, **kwargs) + + total_usd = self.total_balance_usd + total_uzs = self.total_balance_uzs + + if total_usd < 0 or total_uzs < 0: + self.counterparty.status = 'CREDITOR' + elif total_usd > 0 or total_uzs > 0: + self.counterparty.status = 'DEBITOR' + else: + self.counterparty.status = 'CREDITOR' + + self.counterparty.save(update_fields=['status']) + + @property + def total_balance_usd(self): + return (self.debit_usd or Decimal(0)) - (self.kredit_usd or Decimal(0)) + + @property + def total_balance_uzs(self): + return (self.debit_uzs or Decimal(0)) - (self.kredit_uzs or Decimal(0)) + + def __str__(self): + return f"{self.counterparty.name} | USD: {self.total_balance_usd} | UZS: {self.total_balance_uzs}" + + class Meta: + verbose_name = "Kontragent Balansi" + verbose_name_plural = "Kontragent Balanslari" \ No newline at end of file diff --git a/core/apps/counterparty/serializers/counterparty.py b/core/apps/counterparty/serializers/counterparty.py index 07c130e..f614501 100644 --- a/core/apps/counterparty/serializers/counterparty.py +++ b/core/apps/counterparty/serializers/counterparty.py @@ -2,19 +2,25 @@ from django.db import transaction from rest_framework import serializers -from core.apps.counterparty.models import Counterparty, CounterpartyFolder +from core.apps.counterparty.models import Counterparty, CounterpartyFolder, CounterpartyBalance from core.apps.shared.models import Region, District +from core.apps.counterparty.serializers.counterparty_balance import ( + CounterpartyBalanceSerializer, + CounterpartyBalanceCreateSerializer, + CounterpartyBalanceUpdateSerializer +) class CounterpartyListSerializer(serializers.ModelSerializer): + balance = CounterpartyBalanceSerializer() + class Meta: model = Counterparty fields = [ 'id', 'inn', 'name', 'phone', 'type', 'folder', 'type', 'region', 'district', - 'balance', 'balance_currency', 'balance_date', 'comment', 'is_archived', - 'kredit_usd', 'kredit_uzs', 'total_kredit', 'debit_usd', 'debit_uzs', 'total_debit', + 'comment', 'is_archived', 'balance', ] - + class CounterpartySerializer(serializers.ModelSerializer): class Meta: @@ -32,9 +38,8 @@ class CounterpartyCreateSerializer(serializers.Serializer): folder_id = serializers.UUIDField(required=False) region_id = serializers.UUIDField(required=False) district_id = serializers.UUIDField(required=False) - balance = serializers.IntegerField(required=False) - balance_date = serializers.DateField(required=False) comment = serializers.CharField(required=False) + balance = CounterpartyBalanceCreateSerializer(required=False) def validate(self, data): if data.get('folder_id'): @@ -55,7 +60,7 @@ class CounterpartyCreateSerializer(serializers.Serializer): def create(self, validated_data): with transaction.atomic(): - return Counterparty.objects.create( + counterparty = Counterparty.objects.create( inn=validated_data.get('inn'), name=validated_data.get('name'), phone=validated_data.get('phone'), @@ -63,18 +68,25 @@ class CounterpartyCreateSerializer(serializers.Serializer): folder=validated_data.get('folder'), region=validated_data.get('region'), district=validated_data.get('district'), - balance=validated_data.get('balance'), - balance_date=validated_data.get('balance_date'), comment=validated_data.get('comment'), ) + CounterpartyBalance.objects.create( + counterparty=counterparty, + balance_uzs=validated_data.get('balance').get('balance_uzs'), + balance_usd=validated_data.get('balance').get('balance_usd'), + balance_date=validated_data.get('balance').get('balance_date'), + ) + return counterparty class CounterpartyUpdateSerializer(serializers.ModelSerializer): + balance = CounterpartyBalanceUpdateSerializer(required=False) + class Meta: model = Counterparty fields = [ - 'inn', 'name', 'phone', 'type', 'folder', 'region', 'district', 'balance', - 'balance_currency', 'balance_date', 'comment' + 'inn', 'name', 'phone', 'type', 'folder', 'region', 'district', + 'balance', 'comment' ] extra_kwargs = { 'name': {'required': False}, @@ -82,8 +94,24 @@ class CounterpartyUpdateSerializer(serializers.ModelSerializer): 'folder': {'required': False}, 'region': {'required': False}, 'district': {'required': False}, - 'balance': {'required': False}, - 'balance_currency': {'required': False}, - 'balance_date': {'required': False}, 'comment': {'required': False} - } \ No newline at end of file + } + + def update(self, instance, validated_data): + instance.inn = validated_data.get('inn', instance.inn) + instance.name = validated_data.get('name', instance.name) + instance.phone = validated_data.get('phone', instance.phone) + instance.type = validated_data.get('type', instance.type) + instance.folder = validated_data.get('folder', instance.folder) + instance.region = validated_data.get('region', instance.region) + instance.district = validated_data.get('district', instance.district) + instance.comment = validated_data.get('district', instance.comment) + # balance + balance_data = validated_data.get('balance') + instance.balance.balance_uzs = balance_data.get('balance_uzs', instance.balance.balance_uzs) + instance.balance.balance_usd = balance_data.get('balance_usd', instance.balance.balance_usd) + instance.balance.balance_date = balance_data.get('balance_date', instance.balance.balance_date) + instance.balance.save() + instance.save() + return instance + diff --git a/core/apps/counterparty/serializers/counterparty_balance.py b/core/apps/counterparty/serializers/counterparty_balance.py new file mode 100644 index 0000000..4937061 --- /dev/null +++ b/core/apps/counterparty/serializers/counterparty_balance.py @@ -0,0 +1,28 @@ +from rest_framework import serializers + +from core.apps.counterparty.models import CounterpartyBalance + + +class CounterpartyBalanceSerializer(serializers.ModelSerializer): + class Meta: + model = CounterpartyBalance + fields = [ + 'id', 'balance_uzs', 'balance_usd', 'balance_date', 'kredit_usd', 'kredit_uzs', + 'debit_usd', 'debit_uzs', 'total_balance_usd', 'total_balance_uzs', + ] + + +class CounterpartyBalanceCreateSerializer(serializers.ModelSerializer): + class Meta: + model = CounterpartyBalance + fields = [ + 'balance_uzs', 'balance_usd', 'balance_date' + ] + + +class CounterpartyBalanceUpdateSerializer(serializers.ModelSerializer): + class Meta: + model = CounterpartyBalance + fields = [ + 'balance_uzs', 'balance_usd', 'balance_date' + ] diff --git a/core/apps/counterparty/urls.py b/core/apps/counterparty/urls.py index 51afdc3..b8ea64e 100644 --- a/core/apps/counterparty/urls.py +++ b/core/apps/counterparty/urls.py @@ -17,7 +17,6 @@ urlpatterns = [ path('all/', cp_views.CounterpartiesApiView.as_view()), path("/", cp_views.CounterpartyDetailApiView.as_view()), path('/un_archive/', cp_views.UnArchiveCounterpartyApiView.as_view()), - path('all/', cp_views.AllCounterpartyListApiView.as_view()), path("/statistics/", cp_views.CounterPartyIncomeExpenceStatisticsApiView.as_view()), ] )), diff --git a/core/apps/counterparty/views/counterparty.py b/core/apps/counterparty/views/counterparty.py index f10d5ae..2498d95 100644 --- a/core/apps/counterparty/views/counterparty.py +++ b/core/apps/counterparty/views/counterparty.py @@ -1,21 +1,32 @@ -from django.db.models import Sum +from decimal import Decimal + +from django.db.models import Sum, Count, Q from django.shortcuts import get_object_or_404 from rest_framework import generics, views, filters from rest_framework.response import Response + from django_filters.rest_framework.backends import DjangoFilterBackend +# accounts from core.apps.accounts.permissions.permissions import HasRolePermission +# shared from core.apps.shared.paginations.custom import CustomPageNumberPagination -from core.apps.counterparty.models import Counterparty, CounterpartyFolder +# counterparty +from core.apps.counterparty.models import Counterparty, CounterpartyFolder, CounterpartyBalance from core.apps.counterparty.serializers import counterparty as serializers from core.apps.counterparty.filters.counterparty import CounterpartyFilter +# finance from core.apps.finance.models import Expence, Income class CounterpartyListApiView(generics.ListAPIView): serializer_class = serializers.CounterpartyListSerializer - queryset = Counterparty.objects.exclude(is_archived=True).exclude(folder__isnull=False) + queryset = Counterparty.objects\ + .select_related('balance')\ + .exclude(is_archived=True)\ + .exclude(folder__isnull=False)\ + .order_by('-created_at') pagination_class = [HasRolePermission] pagination_class = CustomPageNumberPagination filter_backends = [DjangoFilterBackend, filters.SearchFilter] @@ -63,7 +74,7 @@ class ArchiveCounterpartyApiView(views.APIView): class ArchivedCounterpartyListApiView(generics.ListAPIView): serializer_class = serializers.CounterpartyListSerializer - queryset = Counterparty.objects.exclude(is_archived=False) + queryset = Counterparty.objects.exclude(is_archived=False).select_related('balance').order_by('-created_at') pagination_class = [HasRolePermission] pagination_class = CustomPageNumberPagination @@ -89,7 +100,7 @@ class CounterpartyUpdateApiView(generics.UpdateAPIView): class FolderCounterpartyListApiView(generics.GenericAPIView): serializer_class = serializers.CounterpartyListSerializer - queryset = Counterparty.objects.exclude(is_archived=True) + queryset = Counterparty.objects.exclude(is_archived=True).select_related('balance').order_by('-created_at') permission_classes = [HasRolePermission] filter_backends = [filters.SearchFilter] search_fields = [ @@ -110,25 +121,44 @@ class CounterpartyStatisticsApiView(views.APIView): def get(self, request): counterparty_ids = request.query_params.getlist('counterparty') + if counterparty_ids: queryset = Counterparty.objects.filter(id__in=counterparty_ids) else: queryset = Counterparty.objects.all() - - res = queryset.aggregate( - kredit_usd=Sum('kredit_usd'), - kredit_uzs=Sum('kredit_uzs'), - total_kredit=Sum('total_kredit'), - debit_usd=Sum('debit_usd'), - debit_uzs=Sum('debit_uzs'), - total_debut=Sum('total_debit'), + + balance_qs = CounterpartyBalance.objects.filter(counterparty__in=queryset) + + stats = balance_qs.aggregate( + total_balance_uzs=Sum('balance_uzs'), + total_balance_usd=Sum('balance_usd'), + total_debit_uzs=Sum('debit_uzs'), + total_kredit_uzs=Sum('kredit_uzs'), + total_debit_usd=Sum('debit_usd'), + total_kredit_usd=Sum('kredit_usd'), ) - return Response(res) + + counterparty_stats = queryset.aggregate( + total_counterparties=Count('id'), + total_creditors=Count('id', filter=Q(status='CREDITOR')), + total_debtors=Count('id', filter=Q(status='DEBITOR')), + ) + + result = { + **counterparty_stats, + **stats, + } + + for key, value in result.items(): + if value is None: + result[key] = 0 + + return Response(result) class CounterpartiesApiView(generics.GenericAPIView): serializer_class = serializers.CounterpartyListSerializer - queryset = Counterparty.objects.all() + queryset = Counterparty.objects.order_by('-created_at').select_related('balance') permission_classes = [HasRolePermission] filter_backends = [filters.SearchFilter] search_fields = [ @@ -163,101 +193,78 @@ class UnArchiveCounterpartyApiView(views.APIView): ) -class AllCounterpartyListApiView(generics.GenericAPIView): - serializer_class = serializers.CounterpartyListSerializer - queryset = Counterparty.objects.all() - permission_classes = [HasRolePermission] - filter_backends = [filters.SearchFilter] - search_fields = [ - 'name' - ] - - 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 CounterPartyIncomeExpenceStatisticsApiView(views.APIView): permission_classes = [HasRolePermission] def get(self, request, id): counterparty = get_object_or_404(Counterparty, id=id) - incomes = Income.objects.filter( - counterparty=counterparty, - is_deleted=False - ) - expences = Expence.objects.filter( - counterparty=counterparty, - is_deleted=False, - ) - - income_by_currency = { - 'uzs': {'total': 0, 'count': 0, 'amount_uzs': 0}, - 'usd': {'total': 0, 'count': 0, 'amount_uzs': 0} - } - + # Income va Expence querysetlari + incomes = Income.objects.filter(counterparty=counterparty, is_deleted=False) + expences = Expence.objects.filter(counterparty=counterparty, is_deleted=False) + + # Balanslar valyutalar bo'yicha + income_by_currency = {'uzs': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)}, + 'usd': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)}} for income in incomes: currency = income.currency - amount = income.price - + amount = Decimal(income.price or 0) + rate = Decimal(income.exchange_rate or 1) income_by_currency[currency]['total'] += amount income_by_currency[currency]['count'] += 1 - - if currency == 'usd': - income_by_currency[currency]['amount_uzs'] += amount * income.exchange_rate - else: - income_by_currency[currency]['amount_uzs'] += amount - - expence_by_currency = { - 'uzs': {'total': 0, 'count': 0, 'amount_uzs': 0}, - 'usd': {'total': 0, 'count': 0, 'amount_uzs': 0} - } - + income_by_currency[currency]['amount_uzs'] += amount * rate if currency == 'usd' else amount + + expence_by_currency = {'uzs': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)}, + 'usd': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)}} for expence in expences: currency = expence.currency - amount = expence.price - + amount = Decimal(expence.price or 0) + rate = Decimal(expence.exchange_rate or 1) expence_by_currency[currency]['total'] += amount expence_by_currency[currency]['count'] += 1 - - if currency == 'usd': - expence_by_currency[currency]['amount_uzs'] += amount * expence.exchange_rate - else: - expence_by_currency[currency]['amount_uzs'] += amount - - total_income_uzs = sum(data['amount_uzs'] for data in income_by_currency.values()) - total_expence_uzs = sum(data['amount_uzs'] for data in expence_by_currency.values()) - balance_uzs = total_income_uzs - total_expence_uzs - - total_income_usd = income_by_currency.get('usd', {}).get('total', 0) - total_expence_usd = expence_by_currency.get('usd', {}).get('total', 0) - balance_usd = total_income_usd - total_expence_usd - + expence_by_currency[currency]['amount_uzs'] += amount * rate if currency == 'usd' else amount + + # Income/Expence summalari + total_income_uzs = sum(v['amount_uzs'] for v in income_by_currency.values()) + total_expence_uzs = sum(v['amount_uzs'] for v in expence_by_currency.values()) + total_income_usd = income_by_currency['usd']['total'] + total_expence_usd = expence_by_currency['usd']['total'] + + # Kontragent balansi + balance_obj, _ = CounterpartyBalance.objects.get_or_create(counterparty=counterparty) + balance_uzs = balance_obj.total_balance_uzs + (total_income_uzs - total_expence_uzs) + balance_usd = balance_obj.total_balance_usd + (total_income_usd - total_expence_usd) + + # Status aniqlash + if balance_uzs > 0: + status = 'positive' + elif balance_uzs < 0: + status = 'negative' + else: + status = 'zero' + data = { 'counterparty': { 'id': counterparty.id, - 'name': counterparty.name, + 'name': counterparty.name }, 'income': { 'by_currency': income_by_currency, 'total_uzs': total_income_uzs, 'total_usd': total_income_usd, - 'total_count': sum(data['count'] for data in income_by_currency.values()) + 'total_count': sum(v['count'] for v in income_by_currency.values()) }, 'expence': { 'by_currency': expence_by_currency, 'total_uzs': total_expence_uzs, 'total_usd': total_expence_usd, - 'total_count': sum(data['count'] for data in expence_by_currency.values()) + 'total_count': sum(v['count'] for v in expence_by_currency.values()) }, 'balance': { 'uzs': balance_uzs, 'usd': balance_usd, - 'status': 'positive' if balance_uzs > 0 else 'negative' if balance_uzs < 0 else 'zero' + 'status': status } } - + return Response(data, status=200) \ No newline at end of file diff --git a/core/apps/finance/apps.py b/core/apps/finance/apps.py index 194d9ce..23f9a0f 100644 --- a/core/apps/finance/apps.py +++ b/core/apps/finance/apps.py @@ -3,7 +3,4 @@ from django.apps import AppConfig class FinanceConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' - name = 'core.apps.finance' - - def ready(self): - from . import signals \ No newline at end of file + name = 'core.apps.finance' \ No newline at end of file diff --git a/core/apps/finance/migrations/0032_alter_cashtransaction_expence_balance_usd_and_more.py b/core/apps/finance/migrations/0032_alter_cashtransaction_expence_balance_usd_and_more.py new file mode 100644 index 0000000..1208239 --- /dev/null +++ b/core/apps/finance/migrations/0032_alter_cashtransaction_expence_balance_usd_and_more.py @@ -0,0 +1,83 @@ +# Generated by Django 5.2.4 on 2025-11-07 12:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('finance', '0031_alter_expence_audit_alter_income_audit'), + ] + + operations = [ + migrations.AlterField( + model_name='cashtransaction', + name='expence_balance_usd', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='cashtransaction', + name='expence_balance_uzs', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='cashtransaction', + name='income_balance_usd', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='cashtransaction', + name='income_balance_uzs', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='cashtransaction', + name='total_balance_usd', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='cashtransaction', + name='total_balance_uzs', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='expence', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='expencecontract', + name='paid_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='expencecontract', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='income', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='incomecontract', + name='paid_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='incomecontract', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='paymenttype', + name='total_usd', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='paymenttype', + name='total_uzs', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + ] diff --git a/core/apps/finance/models/cash_transaction.py b/core/apps/finance/models/cash_transaction.py index a75accc..11e49ae 100644 --- a/core/apps/finance/models/cash_transaction.py +++ b/core/apps/finance/models/cash_transaction.py @@ -28,13 +28,13 @@ class CashTransaction(BaseModel): CashTransactionFolder, on_delete=models.SET_NULL, related_name='cash_transactions', null=True, blank=True ) - total_balance_usd = models.BigIntegerField(default=0) - income_balance_usd = models.BigIntegerField(default=0) - expence_balance_usd = models.BigIntegerField(default=0) + total_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + income_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + expence_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) - total_balance_uzs = models.BigIntegerField(default=0) - income_balance_uzs = models.BigIntegerField(default=0) - expence_balance_uzs = models.BigIntegerField(default=0) + total_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + income_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + expence_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) def __str__(self): return self.name diff --git a/core/apps/finance/models/expence.py b/core/apps/finance/models/expence.py index c12c4e1..794902d 100644 --- a/core/apps/finance/models/expence.py +++ b/core/apps/finance/models/expence.py @@ -31,7 +31,7 @@ class Expence(BaseModel): 'orders.Party', on_delete=models.SET_NULL, null=True, blank=True, related_name='expences' ) - price = models.PositiveBigIntegerField() + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) exchange_rate = models.PositiveBigIntegerField(default=0, null=True, blank=True) currency = models.CharField( max_length=3, choices=[('usd','usd'), ('uzs', 'uzs')] diff --git a/core/apps/finance/models/expence_contract.py b/core/apps/finance/models/expence_contract.py index d2a968d..8e83ecb 100644 --- a/core/apps/finance/models/expence_contract.py +++ b/core/apps/finance/models/expence_contract.py @@ -21,9 +21,9 @@ class ExpenceContract(BaseModel): Counterparty, on_delete=models.SET_NULL, related_name='expence_contracts', null=True, blank=True ) - price = models.PositiveBigIntegerField() + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) currency = models.CharField(max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')]) - paid_price = models.PositiveBigIntegerField(default=0, null=True, blank=True) + paid_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) date = models.DateField() comment = models.TextField(null=True, blank=True) diff --git a/core/apps/finance/models/income.py b/core/apps/finance/models/income.py index 970baa2..f0af402 100644 --- a/core/apps/finance/models/income.py +++ b/core/apps/finance/models/income.py @@ -26,7 +26,7 @@ class Income(BaseModel): ) currency = models.CharField(choices=[('uzs', 'uzs'),('usd', 'usd')], max_length=3) - price = models.PositiveBigIntegerField() + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) exchange_rate = models.PositiveBigIntegerField(default=0, null=True, blank=True) date = models.DateField(null=True, blank=True) comment = models.TextField(null=True, blank=True) diff --git a/core/apps/finance/models/income_contract.py b/core/apps/finance/models/income_contract.py index d7c4092..c9386c3 100644 --- a/core/apps/finance/models/income_contract.py +++ b/core/apps/finance/models/income_contract.py @@ -21,9 +21,9 @@ class IncomeContract(BaseModel): ) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='income_contracts') - price = models.PositiveBigIntegerField() + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) currency = models.CharField(max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')]) - paid_price = models.PositiveBigIntegerField(default=0, null=True, blank=True) + paid_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) date = models.DateField() comment = models.TextField(null=True, blank=True) diff --git a/core/apps/finance/models/payment_type.py b/core/apps/finance/models/payment_type.py index ffb7c66..900c548 100644 --- a/core/apps/finance/models/payment_type.py +++ b/core/apps/finance/models/payment_type.py @@ -6,8 +6,8 @@ from core.apps.shared.models import BaseModel class PaymentType(BaseModel): name = models.CharField(max_length=200, unique=True) - total_uzs = models.PositiveBigIntegerField(default=0) - total_usd = models.PositiveBigIntegerField(default=0) + total_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + total_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) def __str__(self): return self.name diff --git a/core/apps/finance/serializers/expence_contract.py b/core/apps/finance/serializers/expence_contract.py index 25df45f..9b8f123 100644 --- a/core/apps/finance/serializers/expence_contract.py +++ b/core/apps/finance/serializers/expence_contract.py @@ -84,4 +84,4 @@ class ExpenceContractCreateSerializer(serializers.ModelSerializer): class ExpenceContractCalculatePriceSerializer(serializers.Serializer): - price = serializers.IntegerField() \ No newline at end of file + price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) \ No newline at end of file diff --git a/core/apps/finance/serializers/income_contract.py b/core/apps/finance/serializers/income_contract.py index 5166e61..067797a 100644 --- a/core/apps/finance/serializers/income_contract.py +++ b/core/apps/finance/serializers/income_contract.py @@ -78,4 +78,4 @@ class IncomeContractCreateSerializer(serializers.ModelSerializer): class IncomeContractCalculatePriceSerializer(serializers.Serializer): - price = serializers.IntegerField() \ No newline at end of file + price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) \ No newline at end of file diff --git a/core/apps/finance/signals/__init__.py b/core/apps/finance/signals/__init__.py deleted file mode 100644 index 4aa9ace..0000000 --- a/core/apps/finance/signals/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .expence import * -from .income import * \ No newline at end of file diff --git a/core/apps/finance/signals/expence.py b/core/apps/finance/signals/expence.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/apps/finance/signals/income.py b/core/apps/finance/signals/income.py deleted file mode 100644 index e69de29..0000000 diff --git a/core/apps/finance/views/expence.py b/core/apps/finance/views/expence.py index 2856e8d..17b60ff 100644 --- a/core/apps/finance/views/expence.py +++ b/core/apps/finance/views/expence.py @@ -125,7 +125,6 @@ class ExpenceDeleteApiView(generics.GenericAPIView): comment = serializer.validated_data.get('comment') currency = expence.currency.lower() - # Deleted record yaratish DeletedExpence.objects.create( expence=expence, comment=comment @@ -136,57 +135,47 @@ class ExpenceDeleteApiView(generics.GenericAPIView): counterparty = expence.counterparty if currency == 'uzs': - # Balanslarni qaytarish (o'chirilgani teskari qilish) - cash_transaction.expence_balance_uzs -= expence.price # ✅ KAMAYISHI KERAK + cash_transaction.expence_balance_uzs -= expence.price cash_transaction.total_balance_uzs = ( cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs ) - payment_type.total_uzs += expence.price # ✅ QAYTARISH + payment_type.total_uzs += expence.price - # 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.expence_balance_usd -= expence.price cash_transaction.total_balance_usd = ( cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd ) - payment_type.total_usd += expence.price # ✅ QAYTARISH + payment_type.total_usd += expence.price - # 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() diff --git a/core/apps/finance/views/income.py b/core/apps/finance/views/income.py index c9096e8..b52571c 100644 --- a/core/apps/finance/views/income.py +++ b/core/apps/finance/views/income.py @@ -93,7 +93,6 @@ class IncomeDeleteApiView(generics.GenericAPIView): comment = serializer.validated_data.get('comment') currency = income.currency.lower() - # Deleted record yaratish DeletedIncome.objects.create( income=income, comment=comment @@ -104,57 +103,47 @@ class IncomeDeleteApiView(generics.GenericAPIView): 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.income_balance_uzs -= income.price cash_transaction.total_balance_uzs = ( cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs ) - payment_type.total_uzs -= income.price # ✅ QAYTARISH + payment_type.total_uzs -= income.price - # 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.income_balance_usd -= income.price cash_transaction.total_balance_usd = ( cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd ) - payment_type.total_usd -= income.price # ✅ QAYTARISH + payment_type.total_usd -= income.price - # 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() diff --git a/core/apps/orders/migrations/0034_alter_offer_price_alter_order_amount_and_more.py b/core/apps/orders/migrations/0034_alter_offer_price_alter_order_amount_and_more.py new file mode 100644 index 0000000..e800ec6 --- /dev/null +++ b/core/apps/orders/migrations/0034_alter_offer_price_alter_order_amount_and_more.py @@ -0,0 +1,83 @@ +# Generated by Django 5.2.4 on 2025-11-07 12:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0033_partyamount_overdue_amount'), + ] + + operations = [ + migrations.AlterField( + model_name='offer', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='order', + name='amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='order', + name='qqs_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='order', + name='total_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='order', + name='unit_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='party', + name='discount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='calculated_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='cost_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='debt_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='overdue_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='paid_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='payment_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='total_expense_amount', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='partyamount', + name='total_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + ] diff --git a/core/apps/orders/models/order.py b/core/apps/orders/models/order.py index db68a4a..8e7972a 100644 --- a/core/apps/orders/models/order.py +++ b/core/apps/orders/models/order.py @@ -40,13 +40,13 @@ class Order(BaseModel): date = models.DateField(null=True, blank=True) quantity = models.FloatField(default=1) status = models.CharField(max_length=20, choices=STATUS, default="NEW") - unit_amount = models.PositiveBigIntegerField(default=0, null=True, blank=True) + unit_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) currency = models.CharField( choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True, max_length=3 ) - total_price = models.PositiveBigIntegerField(default=0, null=True, blank=True) - qqs_price = models.PositiveBigIntegerField(null=True, blank=True) - amount = models.PositiveBigIntegerField(default=0, null=True, blank=True) + total_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + qqs_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) qqs = models.PositiveBigIntegerField(null=True, blank=True) def __str__(self): diff --git a/core/apps/orders/models/order_offer.py b/core/apps/orders/models/order_offer.py index 4555a3a..ec3dde1 100644 --- a/core/apps/orders/models/order_offer.py +++ b/core/apps/orders/models/order_offer.py @@ -20,7 +20,7 @@ class Offer(BaseModel): counterparty = models.ForeignKey( Counterparty, on_delete=models.CASCADE, related_name='offers', null=True ) - price = models.PositiveBigIntegerField() + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) price_type = models.CharField(choices=PRICE_TYPE, default='uzs') phone = models.CharField(max_length=15, null=True, blank=True) comment = models.TextField(null=True, blank=True) diff --git a/core/apps/orders/models/party.py b/core/apps/orders/models/party.py index 71ac129..c336ce3 100644 --- a/core/apps/orders/models/party.py +++ b/core/apps/orders/models/party.py @@ -51,7 +51,7 @@ class Party(BaseModel): null=True, blank=True ) audit_comment = models.TextField(null=True, blank=True) - discount = models.PositiveBigIntegerField(null=True, blank=True) + discount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) discount_currency = models.CharField( max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True ) @@ -87,14 +87,14 @@ class Party(BaseModel): class PartyAmount(BaseModel): party = models.OneToOneField(Party, on_delete=models.CASCADE, related_name='party_amount') - total_price = models.PositiveBigIntegerField() - cost_amount = models.BigIntegerField(default=0) - calculated_amount = models.BigIntegerField(default=0) - paid_amount = models.BigIntegerField(default=0) - payment_amount = models.BigIntegerField(default=0) - debt_amount = models.BigIntegerField(default=0) - total_expense_amount = models.BigIntegerField(default=0) - overdue_amount = models.BigIntegerField(default=0) + total_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + cost_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + calculated_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + paid_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + payment_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + debt_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + total_expense_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) + overdue_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) def save(self, *args, **kwargs): if self.calculated_amount < self.paid_amount: diff --git a/core/apps/orders/serializers/offer.py b/core/apps/orders/serializers/offer.py index 63b8dbc..2230d9a 100644 --- a/core/apps/orders/serializers/offer.py +++ b/core/apps/orders/serializers/offer.py @@ -9,7 +9,7 @@ from core.apps.orders.serializers.order import OrderListSerializer class OfferCreateSerializer(serializers.Serializer): counterparty_id = serializers.UUIDField() - price = serializers.IntegerField() + price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) phone = serializers.CharField(required=False) comment = serializers.CharField(required=False) qqs = serializers.BooleanField(required=False) @@ -53,6 +53,7 @@ class MultipleOfferCreateSerializer(serializers.Serializer): created_offers = Offer.objects.bulk_create(offers) return created_offers + class OfferListSerializer(serializers.ModelSerializer): counterparty = serializers.SerializerMethodField(method_name='get_counterparty') order = OrderListSerializer() diff --git a/core/apps/orders/serializers/order.py b/core/apps/orders/serializers/order.py index d10598d..52208c7 100644 --- a/core/apps/orders/serializers/order.py +++ b/core/apps/orders/serializers/order.py @@ -146,11 +146,11 @@ class MultipleOrderAddSerializer(serializers.Serializer): wherehouse_id = serializers.UUIDField() quantity = serializers.IntegerField() - unit_amount = serializers.IntegerField() + unit_amount = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')]) - amount = serializers.IntegerField() - total_price = serializers.IntegerField(required=False) - qqs_price = serializers.IntegerField(required=False) + amount = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) + total_price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False) + qqs_price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False) qqs = serializers.IntegerField(required=False) def validate(self, data): diff --git a/core/apps/orders/serializers/party.py b/core/apps/orders/serializers/party.py index 4636b16..dc8090b 100644 --- a/core/apps/orders/serializers/party.py +++ b/core/apps/orders/serializers/party.py @@ -23,7 +23,7 @@ class PartyCreateSerializer(serializers.Serializer): delivery_date = serializers.DateField() payment_date = serializers.DateField() comment = serializers.CharField(required=False) - discount = serializers.IntegerField(required=False) + discount = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False) discount_currency = serializers.ChoiceField( choices=[("uzs", "uzs"), ("usd", "usd")], required=False ) @@ -238,9 +238,9 @@ class PartyOrderUpdateSerializer(serializers.Serializer): wherehouse_id = serializers.UUIDField() counterparty_id = serializers.UUIDField() quantity = serializers.IntegerField() - unit_amount = serializers.IntegerField() + unit_amount = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) currency = serializers.ChoiceField(choices=[("uzs", "uzs"), ("usd", "usd")]) - total_price = serializers.IntegerField() + total_price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00) def validate(self, data): order = Order.objects.filter(id=data["order_id"]).first() @@ -358,10 +358,10 @@ class PartyUpdateSerializer(serializers.ModelSerializer): party_amount = PartyAmount.objects.get(party=instance) party_amount.total_price = total_price party_amount.cost_amount = ( - total_price - instance.discount if instance.discount else 0 + total_price - instance.discount if instance.discount else 0.00 ) party_amount.payment_amount = ( - total_price - instance.discount if instance.discount else 0 + total_price - instance.discount if instance.discount else 0.00 ) party_amount.save() return instance diff --git a/core/apps/projects/migrations/0017_alter_estimatework_percentage_and_more.py b/core/apps/projects/migrations/0017_alter_estimatework_percentage_and_more.py new file mode 100644 index 0000000..418f2ad --- /dev/null +++ b/core/apps/projects/migrations/0017_alter_estimatework_percentage_and_more.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.4 on 2025-11-07 12:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0016_estimatework_employee_estimatework_end_date_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='estimatework', + name='percentage', + field=models.FloatField(blank=True, default=0, null=True), + ), + migrations.AlterField( + model_name='estimatework', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='estimatework', + name='total_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='project', + name='benifit_plan', + field=models.DecimalField(blank=True, decimal_places=2, default=0.0, max_digits=15, null=True), + ), + ] diff --git a/core/apps/projects/models/project.py b/core/apps/projects/models/project.py index d01cab6..01b09ad 100644 --- a/core/apps/projects/models/project.py +++ b/core/apps/projects/models/project.py @@ -79,7 +79,7 @@ class Project(BaseModel): CashTransaction, related_name='projects' ) currency = models.CharField(choices=[('usd', 'usd'),('uzs','uzs')], max_length=3, default='uzs') - benifit_plan = models.PositiveBigIntegerField(null=True, blank=True) + benifit_plan = models.DecimalField(max_digits=15, decimal_places=2, default=0.00, null=True, blank=True) status = models.CharField(max_length=20, choices=STATUS, default='PLANNED') is_archive = models.BooleanField(default=False) diff --git a/core/apps/projects/models/project_estimate.py b/core/apps/projects/models/project_estimate.py index 086a530..fdb9ca2 100644 --- a/core/apps/projects/models/project_estimate.py +++ b/core/apps/projects/models/project_estimate.py @@ -32,18 +32,18 @@ class EstimateWork(BaseModel): unity = models.ForeignKey( Unity, on_delete=models.SET_NULL, null=True, related_name='estimate_works' ) - price = models.PositiveBigIntegerField(null=True, blank=True) + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) estimate = models.ForeignKey( ProjectEstimate, on_delete=models.CASCADE, related_name='estimate_works' ) date = models.DateField(null=True, blank=True) - total_price = models.PositiveBigIntegerField(default=0) + total_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) status = models.CharField(choices=STATUS, max_length=20, default='ochiq') start_date = models.DateField(null=True, blank=True) end_date = models.DateField(null=True, blank=True) employee = models.ManyToManyField(User, related_name='estimate_work', blank=True) - percentage = models.PositiveSmallIntegerField(db_default=0, null=True, blank=True) + percentage = models.FloatField(default=0, null=True, blank=True) def __str__(self): return f"{self.number}.{self.name}" diff --git a/core/apps/projects/serializers/project.py b/core/apps/projects/serializers/project.py index c0297a2..d459e99 100644 --- a/core/apps/projects/serializers/project.py +++ b/core/apps/projects/serializers/project.py @@ -87,8 +87,8 @@ class ProjectCreateSerializer(serializers.Serializer): wherehouse = serializers.ListSerializer(child=serializers.UUIDField()) cash_transaction = serializers.ListSerializer(child=serializers.UUIDField()) currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')], required=False) - benifit_plan = serializers.IntegerField(required=False) - + benifit_plan = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False) + def create(self, validated_data): boss_ids = validated_data.pop('boss') foreman_ids = validated_data.pop('foreman') diff --git a/core/apps/wherehouse/migrations/0020_alter_inventory_price_alter_inventory_unit_price.py b/core/apps/wherehouse/migrations/0020_alter_inventory_price_alter_inventory_unit_price.py new file mode 100644 index 0000000..d6eb20f --- /dev/null +++ b/core/apps/wherehouse/migrations/0020_alter_inventory_price_alter_inventory_unit_price.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.4 on 2025-11-07 12:17 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('wherehouse', '0019_stockmovemend_sender'), + ] + + operations = [ + migrations.AlterField( + model_name='inventory', + name='price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + migrations.AlterField( + model_name='inventory', + name='unit_price', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=15), + ), + ] diff --git a/core/apps/wherehouse/models/inventory.py b/core/apps/wherehouse/models/inventory.py index 96b09e1..223fd97 100644 --- a/core/apps/wherehouse/models/inventory.py +++ b/core/apps/wherehouse/models/inventory.py @@ -17,7 +17,7 @@ class Inventory(BaseModel): quantity = models.PositiveIntegerField(default=0) product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='inventories') unity = models.ForeignKey(Unity, on_delete=models.SET_NULL, related_name='inventories', null=True) - price = models.PositiveBigIntegerField(default=0) + price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) project_folder = models.ForeignKey( ProjectFolder, on_delete=models.SET_NULL, null=True, blank=True, related_name='inventories' @@ -27,7 +27,7 @@ class Inventory(BaseModel): related_name='inventories' ) is_invalid = models.BooleanField(default=False) - unit_price = models.PositiveBigIntegerField(default=0) + unit_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00) def __str__(self): return f'{self.product} in {self.wherehouse}'