diff --git a/apps/product/web/api/v1/viewsets/product_api.py b/apps/product/web/api/v1/viewsets/product_api.py index 68a1237..b843d67 100644 --- a/apps/product/web/api/v1/viewsets/product_api.py +++ b/apps/product/web/api/v1/viewsets/product_api.py @@ -399,7 +399,7 @@ class IncentivePlanViewSet(SoftDeleteMixin, viewsets.ModelViewSet, DynamicSearch queryset = product_models.IncentivePlan.objects.all() serializer_class = product_serializers.IncentivePlanSerializer filter_backends = [filters.SearchFilter] - search_fields = ['plan_type', 'name'] + search_fields = ['plan_type', 'name', 'group'] @transaction.atomic def create(self, request, *args, **kwargs): @@ -443,7 +443,9 @@ class IncentivePlanViewSet(SoftDeleteMixin, viewsets.ModelViewSet, DynamicSearch Q(is_time_unlimited=False) | Q(start_date_limit__lte=today, end_date_limit__gte=today) ) - page = self.paginate_queryset(incentive_plans) + + queryset = self.filter_queryset(incentive_plans) + page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) return self.get_paginated_response(serializer.data) diff --git a/apps/product/web/api/v1/viewsets/quota_api.py b/apps/product/web/api/v1/viewsets/quota_api.py index 5ea4592..00b4b4f 100644 --- a/apps/product/web/api/v1/viewsets/quota_api.py +++ b/apps/product/web/api/v1/viewsets/quota_api.py @@ -523,6 +523,13 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, QuotaDashboardService, viewsets """ list of organization active quotas that have inventory entries """ org = get_organization_by_user(request.user) + query_param = self.request.query_params # noqa + base_query = {} + + # filter by product + product_id = query_param.get('product_id') if 'product_id' in query_param.keys() else None + if product_id: + base_query['product_id'] = product_id queryset = self.filter_query( self.get_queryset(visibility_by_org_scope=True).filter( @@ -534,6 +541,7 @@ class QuotaViewSet(BaseViewSet, SoftDeleteMixin, QuotaDashboardService, viewsets Q(assigned_organizations=org) | Q(registerer_organization=org), org_quota_stats__inventory_received__gt=0, + **base_query ).order_by('-modify_date').distinct() ) if page is not None: # noqa diff --git a/apps/warehouse/services/inventory_entry_dashboard_service.py b/apps/warehouse/services/inventory_entry_dashboard_service.py new file mode 100644 index 0000000..06aedc9 --- /dev/null +++ b/apps/warehouse/services/inventory_entry_dashboard_service.py @@ -0,0 +1,51 @@ +from django.db.models import Count, Sum +from django.db.models.functions import Coalesce + +from apps.authentication.models import Organization +from apps.authentication.services.service import get_all_org_child +from apps.core.services.filter.search import DynamicSearchService +from apps.warehouse.models import InventoryEntry + + +class InventoryEntryDashboardService: + """ + Inventory Entry Dashboard Services + """ + + @staticmethod + def get_dashboard( + org: Organization, + start_date: str = None, + end_date: str = None, + search_fields: list[str] = None, + query_string: str = None + ): + """ dashboard of inventory entry page """ + + child_orgs = get_all_org_child(org) + child_orgs.append(org) + + if org.type.key == 'ADM': + inventory_entries = InventoryEntry.objects.filter(quota__is_closed=False) + else: + inventory_entries = InventoryEntry.objects.filter( + quota__is_closed=False, + organization__in=child_orgs + ) + + if (start_date and end_date) or query_string: + inventory_entries = DynamicSearchService( + queryset=inventory_entries, + start=start_date, + end=end_date, + search_fields=search_fields, + date_field="create_date", + query_string=query_string + ).apply() + + dashboard = inventory_entries.aggregate( + total_entries=Count("id"), + total_weight=Coalesce(Sum("weight"), 0), + ) + + return dashboard diff --git a/apps/warehouse/web/api/v1/api.py b/apps/warehouse/web/api/v1/api.py index 70e01dd..2a4aedf 100644 --- a/apps/warehouse/web/api/v1/api.py +++ b/apps/warehouse/web/api/v1/api.py @@ -10,6 +10,7 @@ from apps.core.api import BaseViewSet from apps.core.mixins.search_mixin import DynamicSearchMixin from apps.core.mixins.soft_delete_mixin import SoftDeleteMixin from apps.warehouse import models as warehouse_models +from apps.warehouse.services.inventory_entry_dashboard_service import InventoryEntryDashboardService from apps.warehouse.services.transaction_dashboard_service import TransactionDashboardService from apps.warehouse.web.api.v1 import serializers as warehouse_serializers from common.generics import base64_to_image_file @@ -17,7 +18,13 @@ from common.helpers import get_organization_by_user from common.liara_tools import upload_to_liara -class InventoryEntryViewSet(BaseViewSet, SoftDeleteMixin, viewsets.ModelViewSet, DynamicSearchMixin): +class InventoryEntryViewSet( + BaseViewSet, + SoftDeleteMixin, + viewsets.ModelViewSet, + DynamicSearchMixin, + InventoryEntryDashboardService +): queryset = warehouse_models.InventoryEntry.objects.all() serializer_class = warehouse_serializers.InventoryEntrySerializer # filter_backends = [filters.SearchFilter] @@ -108,6 +115,34 @@ class InventoryEntryViewSet(BaseViewSet, SoftDeleteMixin, viewsets.ModelViewSet, self.upload_confirmation_document(request, inventory=pk) return Response(status=status.HTTP_200_OK) + @action( + methods=['get'], + detail=False, + url_path='inventory_dashboard', + url_name='inventory_dashboard', + name='inventory_dashboard' + ) + def inventory_dashboard(self, request): + """ inventory entry dashboard """ + + org = get_organization_by_user(request.user) + query_param = self.request.query_params # noqa + + # filter by date + start_date = query_param.get('start') if 'start' in query_param.keys() else None + end_date = query_param.get('end') if 'end' in query_param.keys() else None + query_string = query_param.get('search') if 'search' in query_param.keys() else None + + return Response( + self.get_dashboard( + org=org, + start_date=start_date, + end_date=end_date, + query_string=query_string, + search_fields=self.search_fields, + ) + ) + @action( methods=['get'], detail=True,