diff --git a/locker_server/api/sub/resources/views.py b/locker_server/api/sub/resources/views.py index dca74069..44a8fee0 100644 --- a/locker_server/api/sub/resources/views.py +++ b/locker_server/api/sub/resources/views.py @@ -45,3 +45,18 @@ def server_type(self, request, *args, **kwargs): "name": app_info.name }) return Response(status=status.HTTP_200_OK, data=data) + + @action(methods=["get"], detail=False) + def list_enterprise_id(self, request, *args, **kwargs): + enterprise_ids = self.enterprise_service.list_enterprise_ids() + return Response(status=status.HTTP_200_OK, data=enterprise_ids) + + @action(methods=["get"], detail=False) + def list_channel(self, request, *args, **kwargs): + user_channels = ["organic", "ads", "affiliate"] + return Response(status=status.HTTP_200_OK, data=user_channels) + + @action(methods=["get"], detail=False) + def list_saas_market(self, request, *args, **kwargs): + saas_markets = self.payment_service.list_saas_market() + return Response(status=status.HTTP_200_OK, data=saas_markets) diff --git a/locker_server/api/sub/urls.py b/locker_server/api/sub/urls.py index 94226762..a6a252b7 100644 --- a/locker_server/api/sub/urls.py +++ b/locker_server/api/sub/urls.py @@ -13,6 +13,9 @@ urlpatterns += [ url(r'^resources/countries$', LONG_TIME_CACHE(views.ResourcePwdViewSet.as_view({'get': 'countries'}))), url(r'^resources/server_type$', views.ResourcePwdViewSet.as_view({'get': 'server_type'})), + url(r'^resources/enterprise_ids$', views.ResourcePwdViewSet.as_view({'get': 'list_enterprise_id'})), + url(r'^resources/channels$', views.ResourcePwdViewSet.as_view({'get': 'list_channel'})), + url(r'^resources/saas_markets$', views.ResourcePwdViewSet.as_view({'get': 'list_saas_market'})), url(r'^resources/cystack_platform/pm/plans$', views.ResourcePwdViewSet.as_view({'get': 'plans'})), url(r'^resources/cystack_platform/pm/enterprise/plans$', views.ResourcePwdViewSet.as_view({'get': 'enterprise_plans'})), diff --git a/locker_server/api/v1_0/payments/views.py b/locker_server/api/v1_0/payments/views.py index 8bf1e772..1b2c9029 100644 --- a/locker_server/api/v1_0/payments/views.py +++ b/locker_server/api/v1_0/payments/views.py @@ -568,7 +568,6 @@ def statistic_income(self, request, *args, **kwargs): "from": from_param, "to": to_param, "duration": duration_param, - "platform": query_params.get("platform"), "status": query_params.get("status"), "plan": query_params.get("plan"), "utm_source": query_params.get("utm_source"), @@ -591,7 +590,6 @@ def statistic_amount(self, request, *args, **kwargs): "from": from_param, "to": to_param, "duration": duration_param, - "platform": query_params.get("platform"), "status": query_params.get("status"), "plan": query_params.get("plan"), "utm_source": query_params.get("utm_source"), diff --git a/locker_server/api_orm/repositories/enterprise_repository.py b/locker_server/api_orm/repositories/enterprise_repository.py index f9575cf7..a1574769 100644 --- a/locker_server/api_orm/repositories/enterprise_repository.py +++ b/locker_server/api_orm/repositories/enterprise_repository.py @@ -49,6 +49,9 @@ def list_enterprises(self, **filters) -> List[Enterprise]: for enterprise_orm in enterprises_orm ] + def list_enterprise_ids(self) -> List[int]: + return list(set(EnterpriseORM.objects.all().values_list("id", flat=True))) + def list_user_enterprises(self, user_id: int, **filter_params) -> List[Enterprise]: status_param = filter_params.get("status") if status_param is not None: diff --git a/locker_server/api_orm/repositories/payment_repository.py b/locker_server/api_orm/repositories/payment_repository.py index e2118129..a01a9cfa 100644 --- a/locker_server/api_orm/repositories/payment_repository.py +++ b/locker_server/api_orm/repositories/payment_repository.py @@ -10,7 +10,7 @@ from django.db.models.functions import TruncYear from locker_server.api_orm.model_parsers.wrapper import get_model_parser -from locker_server.api_orm.models import CustomerORM +from locker_server.api_orm.models import CustomerORM, SaasMarketORM from locker_server.api_orm.models.wrapper import get_payment_model, get_promo_code_model, get_user_model, \ get_user_plan_model, get_plan_model from locker_server.api_orm.repositories import UserORMRepository @@ -98,11 +98,10 @@ def _get_current_plan_orm(user_id: int) -> PMUserPlanORM: def list_payments_orm(cls, **filters) -> List[PaymentORM]: from_param = filters.get("from") to_param = filters.get("to") - platform_param = filters.get("platform", "") # CHPlay,Stripe,ChPlay, Markets status_param = filters.get("status") # success,failed,pending plan_param = filters.get("plan") # premium, lifetime,family,enterprise channel_param = filters.get("channel") # organic,ads, affiliate - source_param = filters.get("source") # stacksocial, dealmirror, saasmantra + source_param = filters.get("source") # stacksocial, dealmirror, saasmantra, stripe, ios, android payment_method_param = filters.get("payment_method") user_id_param = filters.get("user_id") enterprise_id_param = filters.get("enterprise_id") @@ -122,20 +121,7 @@ def list_payments_orm(cls, **filters) -> List[PaymentORM]: if channel_param: user_ids = UserORMRepository.search_from_cystack_id(**{"utm_source": channel_param}).get("ids", []) payments_orm = payments_orm.filter(user_id__in=user_ids) - if platform_param: - platform_param = platform_param.lower() - if platform_param == "stripe": - payments_orm = payments_orm.filter(stripe_invoice_id__isnull=False) - elif platform_param == "ios": - payments_orm = payments_orm.filter( - Q(metadata__contains="ios") & Q(mobile_invoice_id__isnull=False) - ) - elif platform_param == "android": - payments_orm = payments_orm.filter( - Q(metadata__contains="android") & - Q(mobile_invoice_id__isnull=False) - ) if payment_method_param: payments_orm = payments_orm.filter(payment_method=payment_method_param) if user_id_param: @@ -144,7 +130,20 @@ def list_payments_orm(cls, **filters) -> List[PaymentORM]: payments_orm = payments_orm.filter(enterprise_id=enterprise_id_param) if source_param: source_param = source_param.lower() - payments_orm = payments_orm.filter(saas_market__icontains=source_param) + if source_param == "stripe": + payments_orm = payments_orm.filter(stripe_invoice_id__isnull=False) + elif source_param == "ios": + payments_orm = payments_orm.filter( + Q(metadata__contains="ios") & Q(mobile_invoice_id__isnull=False) + ) + elif source_param == "android": + payments_orm = payments_orm.filter( + Q(metadata__contains="android") & + Q(mobile_invoice_id__isnull=False) + + ) + else: + payments_orm = payments_orm.filter(saas_market__icontains=source_param) payments_orm = payments_orm.select_related('user') payments_orm = payments_orm.order_by("-created_time") return payments_orm @@ -157,6 +156,9 @@ def list_all_invoices(self, **filter_params) -> List[Payment]: payments.append(ModelParser.payment_parser().parse_payment(payment_orm=payment_orm)) return payments + def list_saas_market(self) -> List[str]: + return list(set(SaasMarketORM.objects.all().values_list("id", flat=True))) + def list_invoices_by_user(self, user_id: int, **filter_params) -> List[Payment]: payments_orm = PaymentORM.objects.filter(user_id=user_id).order_by('-created_time') from_param = filter_params.get("from") @@ -207,7 +209,7 @@ def statistic_income(self, **filters) -> Dict: payment_id__startswith="LK" ) success_payments_orm = payments_orm.filter( - status="paid" + Q(status="paid") & Q(transaction_type="Payment") ) # statistic income by duration @@ -240,6 +242,11 @@ def statistic_income(self, **filters) -> Dict: payments_orm_by_status = payments_orm.values("status", "currency").annotate( total_price=Sum('total_price') ).order_by('status', 'currency') + refunds_orm = payments_orm.filter( + Q(status="paid") & Q(transaction_type="Refund") + ).values("currency").annotate( + total_price=Sum('total_price') + ).order_by('currency') payment_by_status_statistic = {} for item in payments_orm_by_status: status_dict = payment_by_status_statistic.get(item.get("status")) or {} @@ -247,6 +254,12 @@ def statistic_income(self, **filters) -> Dict: item.get('currency'): item.get("total_price") }) payment_by_status_statistic[item.get("status")] = status_dict + payment_by_status_statistic.update({ + "refund": { + item.get("currency"): item.get("total_price") + for item in refunds_orm + } + }) total_payment_orm = payments_orm.values("currency").annotate( total_price=Sum(F('total_price')) ).order_by("currency") @@ -286,7 +299,7 @@ def statistic_amount(self, **filters) -> Dict: payment_id__startswith="LK" ) success_payments_orm = payments_orm.filter( - status="paid", + Q(status="paid") & Q(transaction_type="Payment") ) # statistic volume by duration @@ -314,7 +327,11 @@ def statistic_amount(self, **filters) -> Dict: total_volume = success_payments_orm.values("currency").annotate( volume=Count('total_price') ).order_by("currency") - + refunds_orm = payments_orm.filter( + Q(status="paid") & Q(transaction_type="Refund") + ).values("currency").annotate( + volume=Count('total_price') + ).order_by("currency") # Statistic by status payments_orm_by_status = payments_orm.values("status", "currency").annotate( volume=Count('total_price') @@ -326,6 +343,12 @@ def statistic_amount(self, **filters) -> Dict: item.get('currency'): item.get("volume") }) payment_by_status_statistic[item.get("status")] = status_dict + payment_by_status_statistic.update({ + "refund": { + item.get("currency"): item.get("volume") + for item in refunds_orm + } + }) total_payment_orm = payments_orm.values("currency").annotate( volume=Count('total_price') ).order_by("currency") diff --git a/locker_server/core/repositories/enterprise_repository.py b/locker_server/core/repositories/enterprise_repository.py index e528ed7d..ea1c70e0 100644 --- a/locker_server/core/repositories/enterprise_repository.py +++ b/locker_server/core/repositories/enterprise_repository.py @@ -10,6 +10,10 @@ class EnterpriseRepository(ABC): def list_enterprises(self, **filters) -> List[Enterprise]: pass + @abstractmethod + def list_enterprise_ids(self) -> List[int]: + pass + @abstractmethod def list_user_enterprises(self, user_id: int, **filter_params) -> List[Enterprise]: pass diff --git a/locker_server/core/repositories/payment_repository.py b/locker_server/core/repositories/payment_repository.py index 5a7223d3..f4215bf5 100644 --- a/locker_server/core/repositories/payment_repository.py +++ b/locker_server/core/repositories/payment_repository.py @@ -11,6 +11,10 @@ class PaymentRepository(ABC): def list_all_invoices(self, **filter_params) -> List[Payment]: pass + @abstractmethod + def list_saas_market(self) -> List[str]: + pass + @abstractmethod def list_invoices_by_user(self, user_id: int, **filter_params) -> List[Payment]: pass diff --git a/locker_server/core/services/enterprise_service.py b/locker_server/core/services/enterprise_service.py index 9b074369..70183182 100644 --- a/locker_server/core/services/enterprise_service.py +++ b/locker_server/core/services/enterprise_service.py @@ -56,6 +56,9 @@ def __init__(self, enterprise_repository: EnterpriseRepository, def list_policies_by_user(self, user_id: int) -> List[EnterprisePolicy]: return self.enterprise_policy_repository.list_policies_by_user(user_id=user_id) + def list_enterprise_ids(self) -> List[str]: + return self.enterprise_repository.list_enterprise_ids() + def get_enterprise_by_id(self, enterprise_id: str) -> Optional[Enterprise]: enterprise = self.enterprise_repository.get_enterprise_by_id( enterprise_id=enterprise_id diff --git a/locker_server/core/services/payment_service.py b/locker_server/core/services/payment_service.py index 12da7a14..54f698a2 100644 --- a/locker_server/core/services/payment_service.py +++ b/locker_server/core/services/payment_service.py @@ -77,6 +77,9 @@ def allow_upgrade_enterprise_trial(self, user_id: int): def list_all_invoices(self, **filter_params) -> List[Payment]: return self.payment_repository.list_all_invoices(**filter_params) + def list_saas_market(self) -> List[str]: + return self.payment_repository.list_saas_market() + def list_user_invoices(self, user_id: int, **filter_params) -> List[Payment]: return self.payment_repository.list_invoices_by_user(user_id=user_id, **filter_params) diff --git a/locker_server/shared/external_services/user_notification/sender_services/mail_service.py b/locker_server/shared/external_services/user_notification/sender_services/mail_service.py index 089b4c49..a4e06224 100644 --- a/locker_server/shared/external_services/user_notification/sender_services/mail_service.py +++ b/locker_server/shared/external_services/user_notification/sender_services/mail_service.py @@ -32,6 +32,8 @@ def send(self, **kwargs): destinations = get_destinations(kwargs.get("user_ids") or []) else: destinations = kwargs.get("destinations", []) + if not destinations: + return # Normalize the mail data in kwargs detail_data = self.normalize_data(**kwargs) # Normalize mail message