stake holders sharing & quota distribution to CMP - list of stake holder sharings

This commit is contained in:
2025-09-07 13:51:39 +03:30
parent bcc79d2c30
commit 6bac1bbd45
13 changed files with 298 additions and 15 deletions

View File

@@ -3,12 +3,12 @@ from rest_framework.response import Response
class SoftDeleteMixin: class SoftDeleteMixin:
def destroy(self, request, *args, **kwargs): def destroy(self, request, pk=None, *args, **kwargs):
""" override destroy -> soft delete """ """ override destroy -> soft delete """
instance = self.get_object() # noqa instance = self.get_object() # noqa
instance.soft_delete() instance.soft_delete()
return Response( return Response(
{"detail": "رکورد با موفقیت حذف شد (Soft Delete)."}, {"detail": "رکورد با موفقیت حذف شد (Soft Delete)."}, # noqa
status=status.HTTP_200_OK status=status.HTTP_200_OK
) )

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.0 on 2025-09-06 10:59
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0066_stakeholders_stake_holder_type'),
]
operations = [
migrations.RemoveField(
model_name='stakeholders',
name='share_percent',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-09-06 11:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0067_remove_stakeholders_share_percent'),
]
operations = [
migrations.AddField(
model_name='stakeholders',
name='share_amount',
field=models.FloatField(default=0),
),
]

View File

@@ -0,0 +1,17 @@
# Generated by Django 5.0 on 2025-09-06 11:42
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0068_stakeholders_share_amount'),
]
operations = [
migrations.RemoveField(
model_name='stakeholders',
name='share_amount',
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-09-06 11:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0069_remove_stakeholders_share_amount'),
]
operations = [
migrations.AddField(
model_name='stakeholders',
name='share_amount',
field=models.PositiveBigIntegerField(default=0),
),
]

View File

@@ -0,0 +1,48 @@
# Generated by Django 5.0 on 2025-09-06 13:25
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('pos_device', '0070_stakeholders_share_amount'),
('product', '0072_alter_quota_base_price_cooperative_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.RemoveField(
model_name='stakeholders',
name='broker_amount',
),
migrations.RemoveField(
model_name='stakeholders',
name='default',
),
migrations.RemoveField(
model_name='stakeholders',
name='share_amount',
),
migrations.CreateModel(
name='StakeHolderShareAmount',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('create_date', models.DateTimeField(auto_now_add=True)),
('modify_date', models.DateTimeField(auto_now=True)),
('creator_info', models.CharField(max_length=100, null=True)),
('modifier_info', models.CharField(max_length=100, null=True)),
('trash', models.BooleanField(default=False)),
('share_amount', models.PositiveBigIntegerField(default=0)),
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createddby', to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
('quota_distribution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='holders_share_amount', to='product.quotadistribution')),
('stakeholders', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='holders_share_amount', to='pos_device.stakeholders')),
],
options={
'abstract': False,
},
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.0 on 2025-09-07 10:19
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0036_organization_phone'),
('pos_device', '0071_remove_stakeholders_broker_amount_and_more'),
]
operations = [
migrations.AddField(
model_name='stakeholdershareamount',
name='registering_organization',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='holders_share_amount', to='authentication.organization'),
),
]

View File

@@ -1,4 +1,4 @@
from apps.product.models import Product, Broker, QuotaBrokerValue from apps.product.models import Product, Broker, QuotaBrokerValue, QuotaDistribution
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
from apps.authorization.models import UserRelations from apps.authorization.models import UserRelations
from apps.authentication.models import Organization from apps.authentication.models import Organization
@@ -271,14 +271,6 @@ class StakeHolders(BaseModel):
related_name='pos_stake_holders', related_name='pos_stake_holders',
null=True null=True
) )
broker_amount = models.ForeignKey(
QuotaBrokerValue,
on_delete=models.CASCADE,
related_name='pos_stake_holders',
null=True
)
share_percent = models.FloatField(default=0)
default = models.BooleanField(default=False)
def __str__(self): def __str__(self):
return f'Device: {self.assignment.device.serial}-organization: {self.organization.name}' return f'Device: {self.assignment.device.serial}-organization: {self.organization.name}'
@@ -287,6 +279,31 @@ class StakeHolders(BaseModel):
return super(StakeHolders, self).save(*args, **kwargs) return super(StakeHolders, self).save(*args, **kwargs)
class StakeHolderShareAmount(BaseModel):
quota_distribution = models.ForeignKey(
QuotaDistribution,
on_delete=models.CASCADE,
related_name='holders_share_amount',
null=True
)
stakeholders = models.ForeignKey(
StakeHolders,
on_delete=models.CASCADE,
related_name='holders_share_amount',
null=True
)
share_amount = models.PositiveBigIntegerField(default=0)
registering_organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
related_name='holders_share_amount',
null=True
)
def save(self, *args, **kwargs):
return super(StakeHolderShareAmount, self).save(*args, **kwargs)
class POSFreeProducts(BaseModel): class POSFreeProducts(BaseModel):
product = models.ForeignKey( product = models.ForeignKey(
Product, Product,

View File

@@ -1,8 +1,9 @@
from apps.pos_device.models import Device from apps.pos_device.models import Device
from apps.product.models import Quota
import typing import typing
def pos_organizations_sharing_information(device: Device) -> typing.Any: def pos_organizations_sharing_information(device: Device, quota: Quota = None) -> typing.Any:
""" """
pos sharing organizations' information, pos sharing organizations' information,
device have multiple organizations (sub_accounts) for sharing money device have multiple organizations (sub_accounts) for sharing money
@@ -17,7 +18,9 @@ def pos_organizations_sharing_information(device: Device) -> typing.Any:
"account": item.organization.bank_information.first().account, "account": item.organization.bank_information.first().account,
} if item.organization.bank_information.exists() else {}, } if item.organization.bank_information.exists() else {},
"broker": item.broker.name if item.broker else None, "broker": item.broker.name if item.broker else None,
"amount": item.broker_amount.value if item.broker else None "amount": quota.broker_values.filter(
broker=item.broker
).first().value if quota and item.broker else item.share_amount
} for item in stake_holders] } for item in stake_holders]
return sharing_information_list return sharing_information_list

View File

@@ -1,3 +1,4 @@
from apps.product.web.api.v1.serializers.quota_distribution_serializers import QuotaDistributionSerializer
from apps.authentication.api.v1.serializers.serializer import BankAccountSerializer from apps.authentication.api.v1.serializers.serializer import BankAccountSerializer
from apps.pos_device.web.api.v1.serilaizers import client as client_serializer from apps.pos_device.web.api.v1.serilaizers import client as client_serializer
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
@@ -85,4 +86,28 @@ class StakeHoldersSerializer(ModelSerializer):
instance.organization.bank_information.all().first() instance.organization.bank_information.all().first()
).data ).data
representation['organization'] = {
'name': instance.organization.name,
'id': instance.organization.id
}
return representation
class StakeHolderShareAmountSerializer(ModelSerializer):
class Meta:
model = pos_models.StakeHolderShareAmount
fields = '__all__'
def to_representation(self, instance):
representation = super().to_representation(instance)
# distribution information
representation['quota_distribution'] = QuotaDistributionSerializer(
instance.quota_distribution
).data
# stakeholders information
representation['stakeholders'] = StakeHoldersSerializer(instance.stakeholders).data
return representation return representation

View File

@@ -10,6 +10,7 @@ router.register(r'provider', device_views.ProviderCompanyViewSet, basename='prov
router.register(r'device', device_views.DeviceViewSet, basename='device') router.register(r'device', device_views.DeviceViewSet, basename='device')
router.register(r'device_assignment', device_views.DeviceAssignmentViewSet, basename='device_assignment') router.register(r'device_assignment', device_views.DeviceAssignmentViewSet, basename='device_assignment')
router.register(r'stake_holders', device_views.StakeHoldersViewSet, basename='stake_holders') router.register(r'stake_holders', device_views.StakeHoldersViewSet, basename='stake_holders')
router.register(r'holders_share', device_views.StakeHolderShareAmountViewSet, basename='holders_share')
urlpatterns = [ urlpatterns = [
path('v1/pos/', include(router.urls)) path('v1/pos/', include(router.urls))

View File

@@ -1,7 +1,7 @@
import random import random
import string import string
from datetime import timedelta from datetime import timedelta
from apps.product.web.api.v1.viewsets.quota_distribution_api import QuotaDistributionViewSet
from apps.pos_device.web.api.v1.serilaizers import device as device_serializer from apps.pos_device.web.api.v1.serilaizers import device as device_serializer
from apps.authentication.exceptions import OrganizationBankAccountException from apps.authentication.exceptions import OrganizationBankAccountException
from apps.authorization.api.v1.serializers import UserRelationSerializer from apps.authorization.api.v1.serializers import UserRelationSerializer
@@ -337,3 +337,102 @@ class StakeHoldersViewSet(viewsets.ModelViewSet, DynamicSearchMixin, SoftDeleteM
serializer = self.get_serializer(page, many=True) serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
@action(
methods=['get'],
detail=False,
url_path='list_by_organization',
url_name='list_by_organization',
name='list_by_organization',
)
def list_by_organization(self, request):
""" list of stakeholders by organization """
org = get_organization_by_user(request.user)
stakeholders = self.queryset.filter(
assignment__client__organization=org,
organization__type__key='CMP'
)
# paginate stakeholders
page = self.paginate_queryset(stakeholders)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
class StakeHolderShareAmountViewSet(viewsets.ModelViewSet, DynamicSearchMixin, SoftDeleteMixin):
queryset = pos_models.StakeHolderShareAmount.objects.select_related('quota_distribution', 'stakeholders')
serializer_class = device_serializer.StakeHolderShareAmountSerializer
@transaction.atomic
def create(self, request, *args, **kwargs):
""" create share amount for company stakeholders """
data = request.data.copy()
organization = get_organization_by_user(request.user)
data.update({'registering_organization': organization.id})
assigner_organization = get_organization_by_user(request.user)
data['distribution'].update({'assigner_organization': assigner_organization.id})
# create distribution
if 'distribution' in data.keys():
distribution = CustomOperations().custom_create(
request=request,
view=QuotaDistributionViewSet(),
data=data['distribution']
)
data.update({'quota_distribution': distribution['id']})
serializer = self.serializer_class(data=data)
if serializer.is_valid(raise_exception=True):
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_403_FORBIDDEN)
@action(
methods=['get'],
detail=False,
url_path='my_sharing_distributes',
url_name='my_sharing_distributes',
name='my_sharing_distributes',
)
def my_shared_distributes(self, request):
""" list of my shared stakeholders with detail """
organization = get_organization_by_user(request.user)
stakeholders_sharing = self.queryset.filter(
registering_organization=organization
).order_by('-create_date')
# paginate stakeholders
page = self.paginate_queryset(stakeholders_sharing)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
@action(
methods=['get'],
detail=True,
url_path='shared_by_distribution',
url_name='shared_by_distribution',
name='shared_by_distribution',
)
@transaction.atomic
def shared_by_distribution(self, request, pk=None):
""" list of shared stakeholder with distribution """
stakeholder_sharing = self.queryset.filter(
quota_distribution_id=pk
).order_by('-create_date')
# paginate stakeholders
page = self.paginate_queryset(stakeholder_sharing)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)

View File

@@ -98,7 +98,7 @@ class QuotaDistributionSerializer(serializers.ModelSerializer):
representation['pricing'] = { # noqa representation['pricing'] = { # noqa
'pricing_attributes': quota_attribute_value(instance.quota), 'pricing_attributes': quota_attribute_value(instance.quota),
'sharing': pos_organizations_sharing_information(self.context['device']), 'sharing': pos_organizations_sharing_information(self.context['device'], instance.quota),
'base_prices': [ 'base_prices': [
{ {
"text": "قیمت درب کارخانه", # noqa "text": "قیمت درب کارخانه", # noqa