import - rancher organization linked deploy --> with dashboards of cooperative management oage

This commit is contained in:
2025-12-22 14:38:52 +03:30
parent 65a826ab7f
commit 5eb810893b
9 changed files with 272 additions and 24 deletions

View File

@@ -9,6 +9,7 @@ VISIBILITY_MAP = {
'inventoryquotasaletransaction': 'seller_organization', 'inventoryquotasaletransaction': 'seller_organization',
'device': 'assignment__client__organization', 'device': 'assignment__client__organization',
'rancher': 'organization', 'rancher': 'organization',
'rancherorganizationlink': 'organization', # noqa
# 'deviceactivationcode': 'organization', # 'deviceactivationcode': 'organization',
# 'deviceversion': 'organization', # 'deviceversion': 'organization',

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.0 on 2025-12-22 06:12
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0049_alter_bankaccountinformation_account_and_more'),
('herd', '0020_alter_rancher_ignore_purchase_limit'),
]
operations = [
migrations.AddField(
model_name='rancher',
name='organization',
field=models.ForeignKey(help_text='connect ranchers to their specific Taavoni', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='ranchers', to='authentication.organization'),
),
]

View File

@@ -0,0 +1,35 @@
# Generated by Django 5.0 on 2025-12-22 07:16
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0049_alter_bankaccountinformation_account_and_more'),
('herd', '0021_rancher_organization'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='RancherOrganizationLink',
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)),
('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)),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='rancher_links', to='authentication.organization')),
('rancher', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='organization_links', to='herd.rancher')),
],
options={
'abstract': False,
},
),
]

View File

@@ -146,3 +146,24 @@ class Rancher(BaseModel):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
return super(Rancher, self).save(*args, **kwargs) return super(Rancher, self).save(*args, **kwargs)
class RancherOrganizationLink(BaseModel):
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
related_name='rancher_links',
null=True
)
rancher = models.ForeignKey(
Rancher,
on_delete=models.CASCADE,
related_name='organization_links',
null=True
)
def __str__(self):
return f'rancher: {self.rancher.id} - organization: {self.organization.id}'
def save(self, *args, **kwargs):
return super(RancherOrganizationLink, self).save(*args, **kwargs)

View File

@@ -0,0 +1,53 @@
from django.db.models import Count
from apps.authentication.models import Organization
from apps.authentication.services.service import get_all_org_child
from apps.herd.models import RancherOrganizationLink
class RancherOrganizationService:
"""
different services of ranchers linked to organization
"""
def orgs_linked_rancher(self, org: Organization = None):
"""
list of organizations with their information of rancher, herd, ....
"""
if org.type.key != 'ADM':
organizations = get_all_org_child(org)
else:
organizations = Organization.objects.filter(type__key='CO')
linked_qs = RancherOrganizationLink.objects.select_related(
'rancher',
'organization'
).filter(organization__in=organizations, organization__type__key='CO')
organizations = organizations.annotate(
rancher_count=Count(
'rancher_links__rancher',
distinct=True
),
herd_count=Count(
'rancher_links__rancher__herd',
distinct=True
),
livestock_count=Count(
'rancher_links__rancher__herd__live_stock_herd',
distinct=True
),
)
return [
{
"id": org.id,
"name": org.name,
"province": org.province.name,
"city": org.city.name,
"rancher_count": org.rancher_count,
"herd_count": org.herd_count,
"livestock_count": org.livestock_count,
}
for org in organizations
]

View File

@@ -5,13 +5,17 @@ from rest_framework.decorators import action
from rest_framework.response import Response from rest_framework.response import Response
from apps.authentication.api.v1.api import UserViewSet from apps.authentication.api.v1.api import UserViewSet
from apps.authentication.models import Organization
from apps.core.api import BaseViewSet from apps.core.api import BaseViewSet
from apps.core.mixins.search_mixin import DynamicSearchMixin from apps.core.mixins.search_mixin import DynamicSearchMixin
from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin
from apps.herd.models import Herd, Rancher from apps.herd.models import Herd, Rancher, RancherOrganizationLink
from apps.herd.services.rancher_dashboard_service import RancherDashboardService from apps.herd.services.rancher_dashboard_service import RancherDashboardService
from apps.herd.web.api.v1.serializers import HerdSerializer, RancherSerializer from apps.herd.services.rancher_org_link_services import RancherOrganizationService
from apps.herd.web.api.v1.serializers import HerdSerializer, RancherSerializer, RancherOrganizationLinkSerializer
from apps.livestock.web.api.v1.serializers import LiveStockSerializer from apps.livestock.web.api.v1.serializers import LiveStockSerializer
from apps.product.models import OrganizationQuotaStats
from apps.product.services.quota_dashboard_service import QuotaDashboardService
from apps.product.web.api.v1.serializers import product_serializers from apps.product.web.api.v1.serializers import product_serializers
from common.helpers import get_organization_by_user from common.helpers import get_organization_by_user
from common.tools import CustomOperations from common.tools import CustomOperations
@@ -262,36 +266,105 @@ class RancherViewSet(BaseViewSet, RancherDashboardService, SoftDeleteMixin, view
) )
return Response(rancher_dashboard_data) return Response(rancher_dashboard_data)
class RancherOrganizationLinkViewSet(
BaseViewSet,
SoftDeleteMixin,
viewsets.ModelViewSet,
RancherOrganizationService,
QuotaDashboardService,
DynamicSearchMixin
):
queryset = RancherOrganizationLink.objects.select_related('organization', 'rancher')
serializer_class = RancherOrganizationLinkSerializer
search_fields = [
"rancher__ranching_farm",
"rancher__first_name",
"rancher__last_name",
"rancher__mobile",
"rancher__national_code",
"rancher__birthdate",
"rancher__nationality",
"rancher__address",
"rancher__province__name",
"rancher__city__name",
"organization__name"
]
@action( @action(
methods=['get'], methods=['get'],
detail=False, detail=False,
name='org_linked_rancher_list',
url_name='org_linked_rancher_list',
url_path='org_linked_rancher_list',
)
def org_linked_rancher_list(self, request):
"""
list of organizations with rancher information
"""
org = get_organization_by_user(request.user)
result = self.orgs_linked_rancher(org=org)
return Response(result)
@action(
methods=['get'],
detail=True,
url_name='org_ranchers', url_name='org_ranchers',
url_path='org_ranchers', url_path='org_ranchers',
name='org_ranchers' name='org_ranchers'
) )
def org_ranchers(self, request): def org_ranchers(self, request, pk=None):
""" """
list of ranchers by organization list of ranchers by organization
""" """
queryset = self.get_queryset(visibility_by_org_scope=True) org_id = pk
queryset = self.get_queryset().filter(organization_id=org_id)
page = self.paginate_queryset(queryset) page = self.paginate_queryset(queryset)
if page is not None: # noqa if page is not None: # noqa
serializer = product_serializers.IncentivePlanRancherSerializer(page, many=True) serializer = self.serializer_class(page, many=True)
return self.get_paginated_response(serializer.data) return self.get_paginated_response(serializer.data)
return Response(status=status.HTTP_200_OK) return Response(status=status.HTTP_200_OK)
@action( @action(
methods=['get'], methods=['get'],
detail=False, detail=True,
url_name='org_ranchers_dashboard', url_name='org_ranchers_quota_dashboard',
url_path='org_ranchers_dashboard', url_path='org_ranchers_quota_dashboard',
name='org_ranchers_dashboard' name='org_ranchers_quota_dashboard'
) )
def org_ranchers_dashboard(self, request): def org_ranchers_quota_dashboard(self, request, pk=None):
""" """
dashboard of ranchers report dashboard of Org ranchers report by quota
""" """
org_obj = Organization.objects.get(id=pk)
dashboard_data = self.get_dashboard(self, org=org_obj)
queryset = self.get_queryset(visibility_by_org_scope=True) return Response(dashboard_data)
@action(
methods=['get'],
detail=True,
url_name='org_ranchers_product_dashboard',
url_path='org_ranchers_product_dashboard',
name='org_ranchers_product_dashboard'
)
def org_ranchers_product_dashboard(self, request, pk=None):
"""
dashboard of Org ranchers report by quota
"""
org_obj = Organization.objects.get(id=pk)
# get organization quota stats and get product ids
org_quota_stat = OrganizationQuotaStats.objects.select_related('organization', 'quota').filter(
organization=org_obj,
quota__is_closed=False
)
products = {f'{stat.quota.product.name}': stat.quota.product.id for stat in org_quota_stat}
dashboard_data = self.get_dashboard_by_product(self, organization=org_obj, products=products)
return Response(dashboard_data)

View File

@@ -1,16 +1,17 @@
from rest_framework import serializers
from apps.authentication.api.v1.serializers.serializer import ( from apps.authentication.api.v1.serializers.serializer import (
UserSerializer,
OrganizationSerializer, OrganizationSerializer,
ProvinceSerializer, ProvinceSerializer,
CitySerializer CitySerializer
) )
from apps.herd.exception import HerdCapacityException from apps.herd.exception import HerdCapacityException
from apps.herd.models import Herd, Rancher from apps.herd.models import Herd, Rancher, RancherOrganizationLink
from rest_framework import serializers
class HerdSerializer(serializers.ModelSerializer): class HerdSerializer(serializers.ModelSerializer):
""" Herd Serializer """ """ Herd Serializer """
class Meta: class Meta:
model = Herd model = Herd
fields = '__all__' fields = '__all__'
@@ -49,14 +50,38 @@ class RancherSerializer(serializers.ModelSerializer):
representation = super().to_representation(instance) representation = super().to_representation(instance)
representation['province'] = { if instance.province:
'id': instance.province.id, representation['province'] = {
'name': instance.province.name 'id': instance.province.id,
} 'name': instance.province.name
}
representation['city'] = { if instance.city:
'id': instance.city.id, representation['city'] = {
'name': instance.city.name 'id': instance.city.id,
} 'name': instance.city.name
}
return representation
class RancherOrganizationLinkSerializer(serializers.ModelSerializer):
class Meta:
model = RancherOrganizationLink
fields = '__all__'
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.rancher:
representation['rancher'] = RancherSerializer(instance.rancher).data
if instance.organization:
representation['organization'] = {
"id": instance.organization.id,
"name": instance.organization.name,
"province": instance.organization.province.name,
"city": instance.organization.city.name
}
return representation return representation

View File

@@ -1,12 +1,14 @@
from django.urls import path, include from django.urls import path, include
from rest_framework import routers from rest_framework import routers
from .api import HerdViewSet, RancherViewSet from .api import HerdViewSet, RancherViewSet, RancherOrganizationLinkViewSet
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('herd', HerdViewSet, basename='herd') router.register('herd', HerdViewSet, basename='herd')
router.register('rancher', RancherViewSet, basename='rancher') router.register('rancher', RancherViewSet, basename='rancher')
router.register('rancher_org_link', RancherOrganizationLinkViewSet, basename='rancher_org_link')
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)) path('api/v1/', include(router.urls))
] ]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.0 on 2025-12-22 06:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('product', '0101_quota_edited_pricing_features'),
]
operations = [
migrations.AddField(
model_name='productstats',
name='product_org_stat_type',
field=models.CharField(default='registerer', help_text='registerer or distributioned', max_length=25, null=True),
),
]