import - rancher organization linked deploy --> with dashboards of cooperative management oage
This commit is contained in:
@@ -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',
|
||||||
|
|||||||
20
apps/herd/migrations/0021_rancher_organization.py
Normal file
20
apps/herd/migrations/0021_rancher_organization.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
||||||
35
apps/herd/migrations/0022_rancherorganizationlink.py
Normal file
35
apps/herd/migrations/0022_rancherorganizationlink.py
Normal 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,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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)
|
||||||
|
|||||||
53
apps/herd/services/rancher_org_link_services.py
Normal file
53
apps/herd/services/rancher_org_link_services.py
Normal 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
|
||||||
|
]
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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))
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user