change price type int -> decimal field
This commit is contained in:
@@ -1,15 +1,26 @@
|
|||||||
from django.contrib import admin
|
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)
|
@admin.register(Counterparty)
|
||||||
class CounterpartyAdmin(admin.ModelAdmin):
|
class CounterpartyAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'name', 'phone', 'type', 'inn']
|
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)
|
@admin.register(CounterpartyFolder)
|
||||||
class CounterpartyFolderAdmin(admin.ModelAdmin):
|
class CounterpartyFolderAdmin(admin.ModelAdmin):
|
||||||
list_display = ['id', 'name']
|
list_display = ['id', 'name']
|
||||||
list_filter = ['name']
|
list_filter = ['name']
|
||||||
|
|
||||||
@@ -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',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from core.apps.shared.models import BaseModel, Region, District
|
from core.apps.shared.models import BaseModel, Region, District
|
||||||
@@ -41,24 +43,71 @@ class Counterparty(BaseModel):
|
|||||||
district = models.ForeignKey(
|
district = models.ForeignKey(
|
||||||
District, on_delete=models.SET_NULL, null=True, blank=True, related_name='counterparties'
|
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)
|
comment = models.TextField(null=True, blank=True)
|
||||||
is_archived = models.BooleanField(default=False)
|
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):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'Kontragent'
|
verbose_name = 'Kontragent'
|
||||||
verbose_name_plural = 'Kontragentlar'
|
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"
|
||||||
@@ -2,19 +2,25 @@ from django.db import transaction
|
|||||||
|
|
||||||
from rest_framework import serializers
|
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.shared.models import Region, District
|
||||||
|
from core.apps.counterparty.serializers.counterparty_balance import (
|
||||||
|
CounterpartyBalanceSerializer,
|
||||||
|
CounterpartyBalanceCreateSerializer,
|
||||||
|
CounterpartyBalanceUpdateSerializer
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CounterpartyListSerializer(serializers.ModelSerializer):
|
class CounterpartyListSerializer(serializers.ModelSerializer):
|
||||||
|
balance = CounterpartyBalanceSerializer()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Counterparty
|
model = Counterparty
|
||||||
fields = [
|
fields = [
|
||||||
'id', 'inn', 'name', 'phone', 'type', 'folder', 'type', 'region', 'district',
|
'id', 'inn', 'name', 'phone', 'type', 'folder', 'type', 'region', 'district',
|
||||||
'balance', 'balance_currency', 'balance_date', 'comment', 'is_archived',
|
'comment', 'is_archived', 'balance',
|
||||||
'kredit_usd', 'kredit_uzs', 'total_kredit', 'debit_usd', 'debit_uzs', 'total_debit',
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CounterpartySerializer(serializers.ModelSerializer):
|
class CounterpartySerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -32,9 +38,8 @@ class CounterpartyCreateSerializer(serializers.Serializer):
|
|||||||
folder_id = serializers.UUIDField(required=False)
|
folder_id = serializers.UUIDField(required=False)
|
||||||
region_id = serializers.UUIDField(required=False)
|
region_id = serializers.UUIDField(required=False)
|
||||||
district_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)
|
comment = serializers.CharField(required=False)
|
||||||
|
balance = CounterpartyBalanceCreateSerializer(required=False)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
if data.get('folder_id'):
|
if data.get('folder_id'):
|
||||||
@@ -55,7 +60,7 @@ class CounterpartyCreateSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
return Counterparty.objects.create(
|
counterparty = Counterparty.objects.create(
|
||||||
inn=validated_data.get('inn'),
|
inn=validated_data.get('inn'),
|
||||||
name=validated_data.get('name'),
|
name=validated_data.get('name'),
|
||||||
phone=validated_data.get('phone'),
|
phone=validated_data.get('phone'),
|
||||||
@@ -63,18 +68,25 @@ class CounterpartyCreateSerializer(serializers.Serializer):
|
|||||||
folder=validated_data.get('folder'),
|
folder=validated_data.get('folder'),
|
||||||
region=validated_data.get('region'),
|
region=validated_data.get('region'),
|
||||||
district=validated_data.get('district'),
|
district=validated_data.get('district'),
|
||||||
balance=validated_data.get('balance'),
|
|
||||||
balance_date=validated_data.get('balance_date'),
|
|
||||||
comment=validated_data.get('comment'),
|
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):
|
class CounterpartyUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
balance = CounterpartyBalanceUpdateSerializer(required=False)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Counterparty
|
model = Counterparty
|
||||||
fields = [
|
fields = [
|
||||||
'inn', 'name', 'phone', 'type', 'folder', 'region', 'district', 'balance',
|
'inn', 'name', 'phone', 'type', 'folder', 'region', 'district',
|
||||||
'balance_currency', 'balance_date', 'comment'
|
'balance', 'comment'
|
||||||
]
|
]
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
'name': {'required': False},
|
'name': {'required': False},
|
||||||
@@ -82,8 +94,24 @@ class CounterpartyUpdateSerializer(serializers.ModelSerializer):
|
|||||||
'folder': {'required': False},
|
'folder': {'required': False},
|
||||||
'region': {'required': False},
|
'region': {'required': False},
|
||||||
'district': {'required': False},
|
'district': {'required': False},
|
||||||
'balance': {'required': False},
|
|
||||||
'balance_currency': {'required': False},
|
|
||||||
'balance_date': {'required': False},
|
|
||||||
'comment': {'required': False}
|
'comment': {'required': False}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|||||||
28
core/apps/counterparty/serializers/counterparty_balance.py
Normal file
28
core/apps/counterparty/serializers/counterparty_balance.py
Normal file
@@ -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'
|
||||||
|
]
|
||||||
@@ -17,7 +17,6 @@ urlpatterns = [
|
|||||||
path('all/', cp_views.CounterpartiesApiView.as_view()),
|
path('all/', cp_views.CounterpartiesApiView.as_view()),
|
||||||
path("<uuid:id>/", cp_views.CounterpartyDetailApiView.as_view()),
|
path("<uuid:id>/", cp_views.CounterpartyDetailApiView.as_view()),
|
||||||
path('<uuid:id>/un_archive/', cp_views.UnArchiveCounterpartyApiView.as_view()),
|
path('<uuid:id>/un_archive/', cp_views.UnArchiveCounterpartyApiView.as_view()),
|
||||||
path('all/', cp_views.AllCounterpartyListApiView.as_view()),
|
|
||||||
path("<uuid:id>/statistics/", cp_views.CounterPartyIncomeExpenceStatisticsApiView.as_view()),
|
path("<uuid:id>/statistics/", cp_views.CounterPartyIncomeExpenceStatisticsApiView.as_view()),
|
||||||
]
|
]
|
||||||
)),
|
)),
|
||||||
|
|||||||
@@ -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 django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from rest_framework import generics, views, filters
|
from rest_framework import generics, views, filters
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
from django_filters.rest_framework.backends import DjangoFilterBackend
|
from django_filters.rest_framework.backends import DjangoFilterBackend
|
||||||
|
|
||||||
|
# accounts
|
||||||
from core.apps.accounts.permissions.permissions import HasRolePermission
|
from core.apps.accounts.permissions.permissions import HasRolePermission
|
||||||
|
# shared
|
||||||
from core.apps.shared.paginations.custom import CustomPageNumberPagination
|
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.serializers import counterparty as serializers
|
||||||
from core.apps.counterparty.filters.counterparty import CounterpartyFilter
|
from core.apps.counterparty.filters.counterparty import CounterpartyFilter
|
||||||
|
# finance
|
||||||
from core.apps.finance.models import Expence, Income
|
from core.apps.finance.models import Expence, Income
|
||||||
|
|
||||||
|
|
||||||
class CounterpartyListApiView(generics.ListAPIView):
|
class CounterpartyListApiView(generics.ListAPIView):
|
||||||
serializer_class = serializers.CounterpartyListSerializer
|
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 = [HasRolePermission]
|
||||||
pagination_class = CustomPageNumberPagination
|
pagination_class = CustomPageNumberPagination
|
||||||
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
filter_backends = [DjangoFilterBackend, filters.SearchFilter]
|
||||||
@@ -63,7 +74,7 @@ class ArchiveCounterpartyApiView(views.APIView):
|
|||||||
|
|
||||||
class ArchivedCounterpartyListApiView(generics.ListAPIView):
|
class ArchivedCounterpartyListApiView(generics.ListAPIView):
|
||||||
serializer_class = serializers.CounterpartyListSerializer
|
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 = [HasRolePermission]
|
||||||
pagination_class = CustomPageNumberPagination
|
pagination_class = CustomPageNumberPagination
|
||||||
|
|
||||||
@@ -89,7 +100,7 @@ class CounterpartyUpdateApiView(generics.UpdateAPIView):
|
|||||||
|
|
||||||
class FolderCounterpartyListApiView(generics.GenericAPIView):
|
class FolderCounterpartyListApiView(generics.GenericAPIView):
|
||||||
serializer_class = serializers.CounterpartyListSerializer
|
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]
|
permission_classes = [HasRolePermission]
|
||||||
filter_backends = [filters.SearchFilter]
|
filter_backends = [filters.SearchFilter]
|
||||||
search_fields = [
|
search_fields = [
|
||||||
@@ -110,25 +121,44 @@ class CounterpartyStatisticsApiView(views.APIView):
|
|||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
counterparty_ids = request.query_params.getlist('counterparty')
|
counterparty_ids = request.query_params.getlist('counterparty')
|
||||||
|
|
||||||
if counterparty_ids:
|
if counterparty_ids:
|
||||||
queryset = Counterparty.objects.filter(id__in=counterparty_ids)
|
queryset = Counterparty.objects.filter(id__in=counterparty_ids)
|
||||||
else:
|
else:
|
||||||
queryset = Counterparty.objects.all()
|
queryset = Counterparty.objects.all()
|
||||||
|
|
||||||
res = queryset.aggregate(
|
balance_qs = CounterpartyBalance.objects.filter(counterparty__in=queryset)
|
||||||
kredit_usd=Sum('kredit_usd'),
|
|
||||||
kredit_uzs=Sum('kredit_uzs'),
|
stats = balance_qs.aggregate(
|
||||||
total_kredit=Sum('total_kredit'),
|
total_balance_uzs=Sum('balance_uzs'),
|
||||||
debit_usd=Sum('debit_usd'),
|
total_balance_usd=Sum('balance_usd'),
|
||||||
debit_uzs=Sum('debit_uzs'),
|
total_debit_uzs=Sum('debit_uzs'),
|
||||||
total_debut=Sum('total_debit'),
|
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):
|
class CounterpartiesApiView(generics.GenericAPIView):
|
||||||
serializer_class = serializers.CounterpartyListSerializer
|
serializer_class = serializers.CounterpartyListSerializer
|
||||||
queryset = Counterparty.objects.all()
|
queryset = Counterparty.objects.order_by('-created_at').select_related('balance')
|
||||||
permission_classes = [HasRolePermission]
|
permission_classes = [HasRolePermission]
|
||||||
filter_backends = [filters.SearchFilter]
|
filter_backends = [filters.SearchFilter]
|
||||||
search_fields = [
|
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):
|
class CounterPartyIncomeExpenceStatisticsApiView(views.APIView):
|
||||||
permission_classes = [HasRolePermission]
|
permission_classes = [HasRolePermission]
|
||||||
|
|
||||||
def get(self, request, id):
|
def get(self, request, id):
|
||||||
counterparty = get_object_or_404(Counterparty, id=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 va Expence querysetlari
|
||||||
income_by_currency = {
|
incomes = Income.objects.filter(counterparty=counterparty, is_deleted=False)
|
||||||
'uzs': {'total': 0, 'count': 0, 'amount_uzs': 0},
|
expences = Expence.objects.filter(counterparty=counterparty, is_deleted=False)
|
||||||
'usd': {'total': 0, 'count': 0, 'amount_uzs': 0}
|
|
||||||
}
|
# 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:
|
for income in incomes:
|
||||||
currency = income.currency
|
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]['total'] += amount
|
||||||
income_by_currency[currency]['count'] += 1
|
income_by_currency[currency]['count'] += 1
|
||||||
|
income_by_currency[currency]['amount_uzs'] += amount * rate if currency == 'usd' else amount
|
||||||
if currency == 'usd':
|
|
||||||
income_by_currency[currency]['amount_uzs'] += amount * income.exchange_rate
|
expence_by_currency = {'uzs': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)},
|
||||||
else:
|
'usd': {'total': Decimal(0), 'count': 0, 'amount_uzs': Decimal(0)}}
|
||||||
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}
|
|
||||||
}
|
|
||||||
|
|
||||||
for expence in expences:
|
for expence in expences:
|
||||||
currency = expence.currency
|
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]['total'] += amount
|
||||||
expence_by_currency[currency]['count'] += 1
|
expence_by_currency[currency]['count'] += 1
|
||||||
|
expence_by_currency[currency]['amount_uzs'] += amount * rate if currency == 'usd' else amount
|
||||||
if currency == 'usd':
|
|
||||||
expence_by_currency[currency]['amount_uzs'] += amount * expence.exchange_rate
|
# Income/Expence summalari
|
||||||
else:
|
total_income_uzs = sum(v['amount_uzs'] for v in income_by_currency.values())
|
||||||
expence_by_currency[currency]['amount_uzs'] += amount
|
total_expence_uzs = sum(v['amount_uzs'] for v in expence_by_currency.values())
|
||||||
|
total_income_usd = income_by_currency['usd']['total']
|
||||||
total_income_uzs = sum(data['amount_uzs'] for data in income_by_currency.values())
|
total_expence_usd = expence_by_currency['usd']['total']
|
||||||
total_expence_uzs = sum(data['amount_uzs'] for data in expence_by_currency.values())
|
|
||||||
balance_uzs = total_income_uzs - total_expence_uzs
|
# Kontragent balansi
|
||||||
|
balance_obj, _ = CounterpartyBalance.objects.get_or_create(counterparty=counterparty)
|
||||||
total_income_usd = income_by_currency.get('usd', {}).get('total', 0)
|
balance_uzs = balance_obj.total_balance_uzs + (total_income_uzs - total_expence_uzs)
|
||||||
total_expence_usd = expence_by_currency.get('usd', {}).get('total', 0)
|
balance_usd = balance_obj.total_balance_usd + (total_income_usd - total_expence_usd)
|
||||||
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 = {
|
data = {
|
||||||
'counterparty': {
|
'counterparty': {
|
||||||
'id': counterparty.id,
|
'id': counterparty.id,
|
||||||
'name': counterparty.name,
|
'name': counterparty.name
|
||||||
},
|
},
|
||||||
'income': {
|
'income': {
|
||||||
'by_currency': income_by_currency,
|
'by_currency': income_by_currency,
|
||||||
'total_uzs': total_income_uzs,
|
'total_uzs': total_income_uzs,
|
||||||
'total_usd': total_income_usd,
|
'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': {
|
'expence': {
|
||||||
'by_currency': expence_by_currency,
|
'by_currency': expence_by_currency,
|
||||||
'total_uzs': total_expence_uzs,
|
'total_uzs': total_expence_uzs,
|
||||||
'total_usd': total_expence_usd,
|
'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': {
|
'balance': {
|
||||||
'uzs': balance_uzs,
|
'uzs': balance_uzs,
|
||||||
'usd': balance_usd,
|
'usd': balance_usd,
|
||||||
'status': 'positive' if balance_uzs > 0 else 'negative' if balance_uzs < 0 else 'zero'
|
'status': status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Response(data, status=200)
|
return Response(data, status=200)
|
||||||
@@ -3,7 +3,4 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class FinanceConfig(AppConfig):
|
class FinanceConfig(AppConfig):
|
||||||
default_auto_field = 'django.db.models.BigAutoField'
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
name = 'core.apps.finance'
|
name = 'core.apps.finance'
|
||||||
|
|
||||||
def ready(self):
|
|
||||||
from . import signals
|
|
||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -28,13 +28,13 @@ class CashTransaction(BaseModel):
|
|||||||
CashTransactionFolder, on_delete=models.SET_NULL, related_name='cash_transactions',
|
CashTransactionFolder, on_delete=models.SET_NULL, related_name='cash_transactions',
|
||||||
null=True, blank=True
|
null=True, blank=True
|
||||||
)
|
)
|
||||||
total_balance_usd = models.BigIntegerField(default=0)
|
total_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
income_balance_usd = models.BigIntegerField(default=0)
|
income_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
expence_balance_usd = models.BigIntegerField(default=0)
|
expence_balance_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
|
|
||||||
total_balance_uzs = models.BigIntegerField(default=0)
|
total_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
income_balance_uzs = models.BigIntegerField(default=0)
|
income_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
expence_balance_uzs = models.BigIntegerField(default=0)
|
expence_balance_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ class Expence(BaseModel):
|
|||||||
'orders.Party', on_delete=models.SET_NULL, null=True, blank=True, related_name='expences'
|
'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)
|
exchange_rate = models.PositiveBigIntegerField(default=0, null=True, blank=True)
|
||||||
currency = models.CharField(
|
currency = models.CharField(
|
||||||
max_length=3, choices=[('usd','usd'), ('uzs', 'uzs')]
|
max_length=3, choices=[('usd','usd'), ('uzs', 'uzs')]
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ class ExpenceContract(BaseModel):
|
|||||||
Counterparty, on_delete=models.SET_NULL, related_name='expence_contracts', null=True, blank=True
|
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')])
|
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()
|
date = models.DateField()
|
||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ class Income(BaseModel):
|
|||||||
)
|
)
|
||||||
|
|
||||||
currency = models.CharField(choices=[('uzs', 'uzs'),('usd', 'usd')], max_length=3)
|
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)
|
exchange_rate = models.PositiveBigIntegerField(default=0, null=True, blank=True)
|
||||||
date = models.DateField(null=True, blank=True)
|
date = models.DateField(null=True, blank=True)
|
||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
|
|||||||
@@ -21,9 +21,9 @@ class IncomeContract(BaseModel):
|
|||||||
)
|
)
|
||||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='income_contracts')
|
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')])
|
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()
|
date = models.DateField()
|
||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ from core.apps.shared.models import BaseModel
|
|||||||
|
|
||||||
class PaymentType(BaseModel):
|
class PaymentType(BaseModel):
|
||||||
name = models.CharField(max_length=200, unique=True)
|
name = models.CharField(max_length=200, unique=True)
|
||||||
total_uzs = models.PositiveBigIntegerField(default=0)
|
total_uzs = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
total_usd = models.PositiveBigIntegerField(default=0)
|
total_usd = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|||||||
@@ -84,4 +84,4 @@ class ExpenceContractCreateSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class ExpenceContractCalculatePriceSerializer(serializers.Serializer):
|
class ExpenceContractCalculatePriceSerializer(serializers.Serializer):
|
||||||
price = serializers.IntegerField()
|
price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
@@ -78,4 +78,4 @@ class IncomeContractCreateSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
|
|
||||||
class IncomeContractCalculatePriceSerializer(serializers.Serializer):
|
class IncomeContractCalculatePriceSerializer(serializers.Serializer):
|
||||||
price = serializers.IntegerField()
|
price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
from .expence import *
|
|
||||||
from .income import *
|
|
||||||
@@ -125,7 +125,6 @@ class ExpenceDeleteApiView(generics.GenericAPIView):
|
|||||||
comment = serializer.validated_data.get('comment')
|
comment = serializer.validated_data.get('comment')
|
||||||
currency = expence.currency.lower()
|
currency = expence.currency.lower()
|
||||||
|
|
||||||
# Deleted record yaratish
|
|
||||||
DeletedExpence.objects.create(
|
DeletedExpence.objects.create(
|
||||||
expence=expence,
|
expence=expence,
|
||||||
comment=comment
|
comment=comment
|
||||||
@@ -136,57 +135,47 @@ class ExpenceDeleteApiView(generics.GenericAPIView):
|
|||||||
counterparty = expence.counterparty
|
counterparty = expence.counterparty
|
||||||
|
|
||||||
if currency == 'uzs':
|
if currency == 'uzs':
|
||||||
# Balanslarni qaytarish (o'chirilgani teskari qilish)
|
cash_transaction.expence_balance_uzs -= expence.price
|
||||||
cash_transaction.expence_balance_uzs -= expence.price # ✅ KAMAYISHI KERAK
|
|
||||||
cash_transaction.total_balance_uzs = (
|
cash_transaction.total_balance_uzs = (
|
||||||
cash_transaction.income_balance_uzs - cash_transaction.expence_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:
|
||||||
if counterparty.debit_uzs > 0:
|
if counterparty.debit_uzs > 0:
|
||||||
# Agar debit bo'lsa, uni oshirish (teskari)
|
|
||||||
counterparty.debit_uzs += expence.price
|
counterparty.debit_uzs += expence.price
|
||||||
counterparty.total_debit += expence.price
|
counterparty.total_debit += expence.price
|
||||||
|
|
||||||
counterparty.kredit_uzs -= expence.price
|
counterparty.kredit_uzs -= expence.price
|
||||||
counterparty.total_kredit -= expence.price
|
counterparty.total_kredit -= expence.price
|
||||||
else:
|
else:
|
||||||
# Agar kredit bo'lsa, uni kamayitish (teskari)
|
|
||||||
counterparty.kredit_uzs -= expence.price
|
counterparty.kredit_uzs -= expence.price
|
||||||
counterparty.total_kredit -= expence.price
|
counterparty.total_kredit -= expence.price
|
||||||
|
|
||||||
counterparty.save()
|
counterparty.save()
|
||||||
|
|
||||||
elif currency == 'usd':
|
elif currency == 'usd':
|
||||||
# Balanslarni qaytarish (o'chirilgani teskari qilish)
|
cash_transaction.expence_balance_usd -= expence.price
|
||||||
cash_transaction.expence_balance_usd -= expence.price # ✅ KAMAYISHI KERAK
|
|
||||||
cash_transaction.total_balance_usd = (
|
cash_transaction.total_balance_usd = (
|
||||||
cash_transaction.income_balance_usd - cash_transaction.expence_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:
|
||||||
if counterparty.debit_usd > 0:
|
if counterparty.debit_usd > 0:
|
||||||
# Agar debit bo'lsa, uni oshirish (teskari)
|
|
||||||
counterparty.debit_usd += expence.price
|
counterparty.debit_usd += expence.price
|
||||||
counterparty.total_debit += expence.price
|
counterparty.total_debit += expence.price
|
||||||
|
|
||||||
counterparty.kredit_usd -= expence.price
|
counterparty.kredit_usd -= expence.price
|
||||||
counterparty.total_kredit -= expence.price
|
counterparty.total_kredit -= expence.price
|
||||||
else:
|
else:
|
||||||
# Agar kredit bo'lsa, uni kamayitish (teskari)
|
|
||||||
counterparty.kredit_usd -= expence.price
|
counterparty.kredit_usd -= expence.price
|
||||||
counterparty.total_kredit -= expence.price
|
counterparty.total_kredit -= expence.price
|
||||||
|
|
||||||
counterparty.save()
|
counterparty.save()
|
||||||
|
|
||||||
# is_deleted = True qilish (hard delete emas, soft delete)
|
|
||||||
expence.is_deleted = True
|
expence.is_deleted = True
|
||||||
|
|
||||||
# Barcha o'zgarishlari saqlash
|
|
||||||
cash_transaction.save()
|
cash_transaction.save()
|
||||||
payment_type.save()
|
payment_type.save()
|
||||||
expence.save()
|
expence.save()
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ class IncomeDeleteApiView(generics.GenericAPIView):
|
|||||||
comment = serializer.validated_data.get('comment')
|
comment = serializer.validated_data.get('comment')
|
||||||
currency = income.currency.lower()
|
currency = income.currency.lower()
|
||||||
|
|
||||||
# Deleted record yaratish
|
|
||||||
DeletedIncome.objects.create(
|
DeletedIncome.objects.create(
|
||||||
income=income,
|
income=income,
|
||||||
comment=comment
|
comment=comment
|
||||||
@@ -104,57 +103,47 @@ class IncomeDeleteApiView(generics.GenericAPIView):
|
|||||||
counterparty = income.counterparty
|
counterparty = income.counterparty
|
||||||
|
|
||||||
if currency == 'uzs':
|
if currency == 'uzs':
|
||||||
# Balanslarni qaytarish (o'chirilgani teskari qilish)
|
cash_transaction.income_balance_uzs -= income.price
|
||||||
cash_transaction.income_balance_uzs -= income.price # ✅ INCOME BALANCE KAMAYISHI KERAK
|
|
||||||
cash_transaction.total_balance_uzs = (
|
cash_transaction.total_balance_uzs = (
|
||||||
cash_transaction.income_balance_uzs - cash_transaction.expence_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:
|
||||||
if counterparty.kredit_uzs > 0:
|
if counterparty.kredit_uzs > 0:
|
||||||
# Agar kredit bo'lsa, uni kamayitish (teskari)
|
|
||||||
counterparty.kredit_uzs -= income.price
|
counterparty.kredit_uzs -= income.price
|
||||||
counterparty.total_kredit -= income.price
|
counterparty.total_kredit -= income.price
|
||||||
|
|
||||||
counterparty.debit_uzs += income.price
|
counterparty.debit_uzs += income.price
|
||||||
counterparty.total_debit += income.price
|
counterparty.total_debit += income.price
|
||||||
else:
|
else:
|
||||||
# Agar debit bo'lsa, uni oshirish (teskari)
|
|
||||||
counterparty.debit_uzs += income.price
|
counterparty.debit_uzs += income.price
|
||||||
counterparty.total_debit += income.price
|
counterparty.total_debit += income.price
|
||||||
|
|
||||||
counterparty.save()
|
counterparty.save()
|
||||||
|
|
||||||
elif currency == 'usd':
|
elif currency == 'usd':
|
||||||
# Balanslarni qaytarish (o'chirilgani teskari qilish)
|
cash_transaction.income_balance_usd -= income.price
|
||||||
cash_transaction.income_balance_usd -= income.price # ✅ INCOME BALANCE KAMAYISHI KERAK
|
|
||||||
cash_transaction.total_balance_usd = (
|
cash_transaction.total_balance_usd = (
|
||||||
cash_transaction.income_balance_usd - cash_transaction.expence_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:
|
||||||
if counterparty.kredit_usd > 0:
|
if counterparty.kredit_usd > 0:
|
||||||
# Agar kredit bo'lsa, uni kamayitish (teskari)
|
|
||||||
counterparty.kredit_usd -= income.price
|
counterparty.kredit_usd -= income.price
|
||||||
counterparty.total_kredit -= income.price
|
counterparty.total_kredit -= income.price
|
||||||
|
|
||||||
counterparty.debit_usd += income.price
|
counterparty.debit_usd += income.price
|
||||||
counterparty.total_debit += income.price
|
counterparty.total_debit += income.price
|
||||||
else:
|
else:
|
||||||
# Agar debit bo'lsa, uni oshirish (teskari)
|
|
||||||
counterparty.debit_usd += income.price
|
counterparty.debit_usd += income.price
|
||||||
counterparty.total_debit += income.price
|
counterparty.total_debit += income.price
|
||||||
|
|
||||||
counterparty.save()
|
counterparty.save()
|
||||||
|
|
||||||
# is_deleted = True qilish (hard delete emas, soft delete)
|
|
||||||
income.is_deleted = True
|
income.is_deleted = True
|
||||||
|
|
||||||
# Barcha o'zgarishlari saqlash
|
|
||||||
cash_transaction.save()
|
cash_transaction.save()
|
||||||
payment_type.save()
|
payment_type.save()
|
||||||
income.save()
|
income.save()
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -40,13 +40,13 @@ class Order(BaseModel):
|
|||||||
date = models.DateField(null=True, blank=True)
|
date = models.DateField(null=True, blank=True)
|
||||||
quantity = models.FloatField(default=1)
|
quantity = models.FloatField(default=1)
|
||||||
status = models.CharField(max_length=20, choices=STATUS, default="NEW")
|
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(
|
currency = models.CharField(
|
||||||
choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True, max_length=3
|
choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True, max_length=3
|
||||||
)
|
)
|
||||||
total_price = models.PositiveBigIntegerField(default=0, null=True, blank=True)
|
total_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
qqs_price = models.PositiveBigIntegerField(null=True, blank=True)
|
qqs_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
amount = models.PositiveBigIntegerField(default=0, null=True, blank=True)
|
amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
qqs = models.PositiveBigIntegerField(null=True, blank=True)
|
qqs = models.PositiveBigIntegerField(null=True, blank=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ class Offer(BaseModel):
|
|||||||
counterparty = models.ForeignKey(
|
counterparty = models.ForeignKey(
|
||||||
Counterparty, on_delete=models.CASCADE, related_name='offers', null=True
|
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')
|
price_type = models.CharField(choices=PRICE_TYPE, default='uzs')
|
||||||
phone = models.CharField(max_length=15, null=True, blank=True)
|
phone = models.CharField(max_length=15, null=True, blank=True)
|
||||||
comment = models.TextField(null=True, blank=True)
|
comment = models.TextField(null=True, blank=True)
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Party(BaseModel):
|
|||||||
null=True, blank=True
|
null=True, blank=True
|
||||||
)
|
)
|
||||||
audit_comment = models.TextField(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(
|
discount_currency = models.CharField(
|
||||||
max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True
|
max_length=3, choices=[('uzs', 'uzs'), ('usd', 'usd')], default='uzs', null=True, blank=True
|
||||||
)
|
)
|
||||||
@@ -87,14 +87,14 @@ class Party(BaseModel):
|
|||||||
|
|
||||||
class PartyAmount(BaseModel):
|
class PartyAmount(BaseModel):
|
||||||
party = models.OneToOneField(Party, on_delete=models.CASCADE, related_name='party_amount')
|
party = models.OneToOneField(Party, on_delete=models.CASCADE, related_name='party_amount')
|
||||||
total_price = models.PositiveBigIntegerField()
|
total_price = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
cost_amount = models.BigIntegerField(default=0)
|
cost_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
calculated_amount = models.BigIntegerField(default=0)
|
calculated_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
paid_amount = models.BigIntegerField(default=0)
|
paid_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
payment_amount = models.BigIntegerField(default=0)
|
payment_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
debt_amount = models.BigIntegerField(default=0)
|
debt_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
total_expense_amount = models.BigIntegerField(default=0)
|
total_expense_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
overdue_amount = models.BigIntegerField(default=0)
|
overdue_amount = models.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.calculated_amount < self.paid_amount:
|
if self.calculated_amount < self.paid_amount:
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from core.apps.orders.serializers.order import OrderListSerializer
|
|||||||
|
|
||||||
class OfferCreateSerializer(serializers.Serializer):
|
class OfferCreateSerializer(serializers.Serializer):
|
||||||
counterparty_id = serializers.UUIDField()
|
counterparty_id = serializers.UUIDField()
|
||||||
price = serializers.IntegerField()
|
price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
phone = serializers.CharField(required=False)
|
phone = serializers.CharField(required=False)
|
||||||
comment = serializers.CharField(required=False)
|
comment = serializers.CharField(required=False)
|
||||||
qqs = serializers.BooleanField(required=False)
|
qqs = serializers.BooleanField(required=False)
|
||||||
@@ -53,6 +53,7 @@ class MultipleOfferCreateSerializer(serializers.Serializer):
|
|||||||
created_offers = Offer.objects.bulk_create(offers)
|
created_offers = Offer.objects.bulk_create(offers)
|
||||||
return created_offers
|
return created_offers
|
||||||
|
|
||||||
|
|
||||||
class OfferListSerializer(serializers.ModelSerializer):
|
class OfferListSerializer(serializers.ModelSerializer):
|
||||||
counterparty = serializers.SerializerMethodField(method_name='get_counterparty')
|
counterparty = serializers.SerializerMethodField(method_name='get_counterparty')
|
||||||
order = OrderListSerializer()
|
order = OrderListSerializer()
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ class MultipleOrderAddSerializer(serializers.Serializer):
|
|||||||
wherehouse_id = serializers.UUIDField()
|
wherehouse_id = serializers.UUIDField()
|
||||||
|
|
||||||
quantity = serializers.IntegerField()
|
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')])
|
currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')])
|
||||||
amount = serializers.IntegerField()
|
amount = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00)
|
||||||
total_price = serializers.IntegerField(required=False)
|
total_price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False)
|
||||||
qqs_price = serializers.IntegerField(required=False)
|
qqs_price = serializers.DecimalField(max_digits=15, decimal_places=2, default=0.00, required=False)
|
||||||
qqs = serializers.IntegerField(required=False)
|
qqs = serializers.IntegerField(required=False)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class PartyCreateSerializer(serializers.Serializer):
|
|||||||
delivery_date = serializers.DateField()
|
delivery_date = serializers.DateField()
|
||||||
payment_date = serializers.DateField()
|
payment_date = serializers.DateField()
|
||||||
comment = serializers.CharField(required=False)
|
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(
|
discount_currency = serializers.ChoiceField(
|
||||||
choices=[("uzs", "uzs"), ("usd", "usd")], required=False
|
choices=[("uzs", "uzs"), ("usd", "usd")], required=False
|
||||||
)
|
)
|
||||||
@@ -238,9 +238,9 @@ class PartyOrderUpdateSerializer(serializers.Serializer):
|
|||||||
wherehouse_id = serializers.UUIDField()
|
wherehouse_id = serializers.UUIDField()
|
||||||
counterparty_id = serializers.UUIDField()
|
counterparty_id = serializers.UUIDField()
|
||||||
quantity = serializers.IntegerField()
|
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")])
|
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):
|
def validate(self, data):
|
||||||
order = Order.objects.filter(id=data["order_id"]).first()
|
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 = PartyAmount.objects.get(party=instance)
|
||||||
party_amount.total_price = total_price
|
party_amount.total_price = total_price
|
||||||
party_amount.cost_amount = (
|
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 = (
|
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()
|
party_amount.save()
|
||||||
return instance
|
return instance
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -79,7 +79,7 @@ class Project(BaseModel):
|
|||||||
CashTransaction, related_name='projects'
|
CashTransaction, related_name='projects'
|
||||||
)
|
)
|
||||||
currency = models.CharField(choices=[('usd', 'usd'),('uzs','uzs')], max_length=3, default='uzs')
|
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')
|
status = models.CharField(max_length=20, choices=STATUS, default='PLANNED')
|
||||||
is_archive = models.BooleanField(default=False)
|
is_archive = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
|||||||
@@ -32,18 +32,18 @@ class EstimateWork(BaseModel):
|
|||||||
unity = models.ForeignKey(
|
unity = models.ForeignKey(
|
||||||
Unity, on_delete=models.SET_NULL, null=True, related_name='estimate_works'
|
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(
|
estimate = models.ForeignKey(
|
||||||
ProjectEstimate, on_delete=models.CASCADE, related_name='estimate_works'
|
ProjectEstimate, on_delete=models.CASCADE, related_name='estimate_works'
|
||||||
)
|
)
|
||||||
date = models.DateField(null=True, blank=True)
|
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')
|
status = models.CharField(choices=STATUS, max_length=20, default='ochiq')
|
||||||
start_date = models.DateField(null=True, blank=True)
|
start_date = models.DateField(null=True, blank=True)
|
||||||
end_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)
|
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):
|
def __str__(self):
|
||||||
return f"{self.number}.{self.name}"
|
return f"{self.number}.{self.name}"
|
||||||
|
|||||||
@@ -87,8 +87,8 @@ class ProjectCreateSerializer(serializers.Serializer):
|
|||||||
wherehouse = serializers.ListSerializer(child=serializers.UUIDField())
|
wherehouse = serializers.ListSerializer(child=serializers.UUIDField())
|
||||||
cash_transaction = serializers.ListSerializer(child=serializers.UUIDField())
|
cash_transaction = serializers.ListSerializer(child=serializers.UUIDField())
|
||||||
currency = serializers.ChoiceField(choices=[('uzs', 'uzs'), ('usd', 'usd')], required=False)
|
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):
|
def create(self, validated_data):
|
||||||
boss_ids = validated_data.pop('boss')
|
boss_ids = validated_data.pop('boss')
|
||||||
foreman_ids = validated_data.pop('foreman')
|
foreman_ids = validated_data.pop('foreman')
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -17,7 +17,7 @@ class Inventory(BaseModel):
|
|||||||
quantity = models.PositiveIntegerField(default=0)
|
quantity = models.PositiveIntegerField(default=0)
|
||||||
product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name='inventories')
|
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)
|
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(
|
project_folder = models.ForeignKey(
|
||||||
ProjectFolder, on_delete=models.SET_NULL, null=True, blank=True,
|
ProjectFolder, on_delete=models.SET_NULL, null=True, blank=True,
|
||||||
related_name='inventories'
|
related_name='inventories'
|
||||||
@@ -27,7 +27,7 @@ class Inventory(BaseModel):
|
|||||||
related_name='inventories'
|
related_name='inventories'
|
||||||
)
|
)
|
||||||
is_invalid = models.BooleanField(default=False)
|
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):
|
def __str__(self):
|
||||||
return f'{self.product} in {self.wherehouse}'
|
return f'{self.product} in {self.wherehouse}'
|
||||||
|
|||||||
Reference in New Issue
Block a user