Merge branch 'main' into storefront-nuxt
This commit is contained in:
commit
f289ea1e8e
12 changed files with 221 additions and 64 deletions
|
|
@ -6,9 +6,6 @@ class BlogConfig(AppConfig):
|
|||
default_auto_field = "django.db.models.BigAutoField"
|
||||
name = "blog"
|
||||
verbose_name = _("blog")
|
||||
icon = "fa fa-solid fa-book"
|
||||
priority = 86
|
||||
hide = False
|
||||
|
||||
# noinspection PyUnresolvedReferences
|
||||
def ready(self) -> None:
|
||||
|
|
|
|||
|
|
@ -228,12 +228,63 @@ def process_query(
|
|||
uuids_by_index: dict[str, list[dict[str, Any]]] = {"products": [], "categories": [], "brands": []}
|
||||
hit_cache: list[Any] = []
|
||||
|
||||
seen_keys: set[tuple[str, str]] = set()
|
||||
|
||||
def _hit_key(hittee: Any) -> tuple[str, str]:
|
||||
return hittee.meta.index, str(getattr(hittee, "uuid", None) or hittee.meta.id)
|
||||
|
||||
def _collect_hits(hits: list[Any]) -> None:
|
||||
for hh in hits:
|
||||
key = _hit_key(hh)
|
||||
if key in seen_keys:
|
||||
continue
|
||||
hit_cache.append(hh)
|
||||
seen_keys.add(key)
|
||||
if getattr(hh, "uuid", None):
|
||||
uuids_by_index.setdefault(hh.meta.index, []).append({"uuid": str(hh.uuid)})
|
||||
|
||||
exact_queries_by_index: dict[str, list[Any]] = {
|
||||
"categories": [
|
||||
Q("term", **{"name.raw": {"value": query}}),
|
||||
Q("term", **{"slug": {"value": slugify(query)}}),
|
||||
],
|
||||
"brands": [
|
||||
Q("term", **{"name.raw": {"value": query}}),
|
||||
Q("term", **{"slug": {"value": slugify(query)}}),
|
||||
],
|
||||
"products": [
|
||||
Q("term", **{"name.raw": {"value": query}}),
|
||||
Q("term", **{"slug": {"value": slugify(query)}}),
|
||||
Q("term", **{"sku.raw": {"value": query.lower()}}),
|
||||
Q("term", **{"partnumber.raw": {"value": query.lower()}}),
|
||||
],
|
||||
}
|
||||
|
||||
for idx_name in ("categories", "brands", "products"):
|
||||
if idx_name in indexes:
|
||||
shoulds = exact_queries_by_index[idx_name]
|
||||
s_exact = (
|
||||
Search(index=[idx_name])
|
||||
.query(Q("bool", should=shoulds, minimum_should_match=1))
|
||||
.extra(size=5, track_total_hits=False)
|
||||
)
|
||||
try:
|
||||
resp_exact = s_exact.execute()
|
||||
except NotFoundError:
|
||||
resp_exact = None
|
||||
if resp_exact is not None and getattr(resp_exact, "hits", None):
|
||||
_collect_hits(list(resp_exact.hits))
|
||||
|
||||
for h in (
|
||||
list(resp_cats.hits[:12] if resp_cats else [])
|
||||
+ list(resp_brands.hits[:12] if resp_brands else [])
|
||||
+ list(resp_products.hits[:26] if resp_products else [])
|
||||
):
|
||||
k = _hit_key(h)
|
||||
if k in seen_keys:
|
||||
continue
|
||||
hit_cache.append(h)
|
||||
seen_keys.add(k)
|
||||
if getattr(h, "uuid", None):
|
||||
uuids_by_index.setdefault(h.meta.index, []).append({"uuid": str(h.uuid)})
|
||||
|
||||
|
|
|
|||
|
|
@ -84,3 +84,8 @@ class ProductManager(MultilingualManager):
|
|||
stocks__vendor__is_active=True,
|
||||
stocks__quantity__gt=0,
|
||||
)
|
||||
|
||||
def with_related(self):
|
||||
return self.select_related("category", "brand").prefetch_related(
|
||||
"tags", "stocks", "images", "attributes__attribute__group"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ from django.db.models import (
|
|||
CharField,
|
||||
DateTimeField,
|
||||
DecimalField,
|
||||
F,
|
||||
FileField,
|
||||
FloatField,
|
||||
ForeignKey,
|
||||
|
|
@ -31,6 +32,7 @@ from django.db.models import (
|
|||
OneToOneField,
|
||||
PositiveIntegerField,
|
||||
QuerySet,
|
||||
Sum,
|
||||
TextField,
|
||||
URLField,
|
||||
)
|
||||
|
|
@ -38,6 +40,7 @@ from django.db.models.indexes import Index
|
|||
from django.http import Http404
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import force_bytes
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.http import urlsafe_base64_encode
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_extensions.db.fields import AutoSlugField
|
||||
|
|
@ -177,6 +180,7 @@ class Vendor(ExportModelOperationsMixin("vendor"), NiceModel): # type: ignore [
|
|||
verbose_name_plural = _("vendors")
|
||||
indexes = [
|
||||
GinIndex(fields=["authentication"]),
|
||||
Index(fields=["name"]),
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -512,6 +516,7 @@ class Product(ExportModelOperationsMixin("product"), NiceModel): # type: ignore
|
|||
max_length=255,
|
||||
help_text=_("provide a clear identifying name for the product"),
|
||||
verbose_name=_("product name"),
|
||||
db_index=True,
|
||||
)
|
||||
description = TextField(
|
||||
blank=True,
|
||||
|
|
@ -556,49 +561,37 @@ class Product(ExportModelOperationsMixin("product"), NiceModel): # type: ignore
|
|||
class Meta:
|
||||
verbose_name = _("product")
|
||||
verbose_name_plural = _("products")
|
||||
indexes = [
|
||||
Index(fields=["is_active", "brand", "category"]),
|
||||
Index(fields=["slug"]),
|
||||
Index(fields=["sku"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@property
|
||||
def rating(self) -> float:
|
||||
cache_key = f"product_rating_{self.pk}"
|
||||
rating = cache.get(cache_key)
|
||||
if rating is None:
|
||||
feedbacks = Feedback.objects.filter(order_product__product_id=self.pk)
|
||||
rating = feedbacks.aggregate(Avg("rating"))["rating__avg"] or 0
|
||||
cache.set(cache_key, rating, 86400)
|
||||
feedbacks = Feedback.objects.filter(order_product__product_id=self.pk)
|
||||
rating = feedbacks.aggregate(Avg("rating"))["rating__avg"] or 0
|
||||
return float(round(rating, 2))
|
||||
|
||||
@rating.setter
|
||||
def rating(self, value: float):
|
||||
self.__dict__["rating"] = value
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def feedbacks_count(self) -> int:
|
||||
cache_key = f"product_feedbacks_count_{self.pk}"
|
||||
feedbacks_count = cache.get(cache_key)
|
||||
if feedbacks_count is None:
|
||||
feedbacks_count = Feedback.objects.filter(order_product__product_id=self.pk).count()
|
||||
cache.set(cache_key, feedbacks_count, 604800)
|
||||
return feedbacks_count
|
||||
return Feedback.objects.filter(order_product__product_id=self.pk).count()
|
||||
|
||||
@property
|
||||
def price(self: Self) -> float:
|
||||
stock = self.stocks.all().order_by("-price").only("price").first()
|
||||
price = stock.price if stock else 0.0
|
||||
return round(price, 2)
|
||||
stock = self.stocks.only("price").order_by("-price").first()
|
||||
return round(stock.price, 2) if stock else 0.0
|
||||
|
||||
@property
|
||||
@cached_property
|
||||
def quantity(self) -> int:
|
||||
cache_key = f"product_quantity_{self.pk}"
|
||||
quantity = cache.get(cache_key, 0)
|
||||
if not quantity:
|
||||
stocks = self.stocks.only("quantity")
|
||||
for stock in stocks:
|
||||
quantity += stock.quantity
|
||||
cache.set(cache_key, quantity, 3600)
|
||||
return quantity
|
||||
return self.stocks.aggregate(total=Sum("quantity"))["total"] or 0
|
||||
|
||||
@property
|
||||
def total_orders(self):
|
||||
|
|
@ -1178,6 +1171,10 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
|
|||
class Meta:
|
||||
verbose_name = _("order")
|
||||
verbose_name_plural = _("orders")
|
||||
indexes = [
|
||||
Index(fields=["user", "status"]),
|
||||
Index(fields=["status", "buy_time"]),
|
||||
]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"#{self.human_readable_id} for {self.user.email if self.user else 'unregistered user'}"
|
||||
|
|
@ -1225,24 +1222,16 @@ class Order(ExportModelOperationsMixin("order"), NiceModel): # type: ignore [mi
|
|||
|
||||
@property
|
||||
def total_price(self) -> float:
|
||||
return (
|
||||
round(
|
||||
sum(
|
||||
(
|
||||
order_product.buy_price * order_product.quantity
|
||||
if order_product.status not in FAILED_STATUSES and order_product.buy_price is not None
|
||||
else 0.0
|
||||
)
|
||||
for order_product in self.order_products.all()
|
||||
),
|
||||
2,
|
||||
)
|
||||
or 0.0
|
||||
)
|
||||
total = self.order_products.exclude(status__in=FAILED_STATUSES).aggregate(
|
||||
total=Sum(F("buy_price") * F("quantity"), output_field=FloatField())
|
||||
)["total"]
|
||||
|
||||
return round(total or 0.0, 2)
|
||||
|
||||
@property
|
||||
def total_quantity(self) -> int:
|
||||
return sum([op.quantity for op in self.order_products.all()])
|
||||
total = self.order_products.aggregate(total=Sum("quantity"))["total"]
|
||||
return total or 0
|
||||
|
||||
def add_product(
|
||||
self,
|
||||
|
|
@ -1676,6 +1665,8 @@ class OrderProduct(ExportModelOperationsMixin("order_product"), NiceModel): # t
|
|||
verbose_name_plural = _("order products")
|
||||
indexes = [
|
||||
GinIndex(fields=["notifications", "attributes"]),
|
||||
Index(fields=["order", "status"]),
|
||||
Index(fields=["product", "status"]),
|
||||
]
|
||||
|
||||
def return_balance_back(self):
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ def create_wishlist_on_user_creation_signal(instance: User, created: bool, **kwa
|
|||
@receiver(post_save, sender=User)
|
||||
def create_promocode_on_user_referring(instance: User, created: bool, **kwargs: dict[Any, Any]) -> None:
|
||||
try:
|
||||
if not instance.attributes:
|
||||
if type(instance.attributes) is not dict:
|
||||
instance.attributes = {}
|
||||
instance.save()
|
||||
|
||||
|
|
|
|||
16
core/utils/vendors.py
Normal file
16
core/utils/vendors.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from typing import Type
|
||||
|
||||
from core.models import Vendor
|
||||
from core.vendors import AbstractVendor
|
||||
from evibes.utils.misc import create_object
|
||||
|
||||
|
||||
def get_vendors_integrations(name: str | None = None) -> list[Type[AbstractVendor]]:
|
||||
vendors_integrations: list[Type[AbstractVendor]] = []
|
||||
vendors = Vendor.objects.filter(is_active=True, name=name) if name else Vendor.objects.filter(is_active=True)
|
||||
for vendor in vendors:
|
||||
if vendor.integration_path:
|
||||
module_name = ".".join(vendor.integration_path.split(".")[:-1])
|
||||
class_name = vendor.integration_path.split(".")[-1]
|
||||
vendors_integrations.append(create_object(module_name, class_name))
|
||||
return vendors_integrations
|
||||
49
core/vendors/__init__.py
vendored
49
core/vendors/__init__.py
vendored
|
|
@ -1,9 +1,15 @@
|
|||
import gzip
|
||||
import json
|
||||
from contextlib import suppress
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from io import BytesIO
|
||||
from math import ceil, log10
|
||||
from typing import Any
|
||||
|
||||
from constance import config
|
||||
from django.conf import settings
|
||||
from django.core.files.base import ContentFile
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import QuerySet
|
||||
|
||||
|
|
@ -59,6 +65,14 @@ class VendorError(Exception):
|
|||
pass
|
||||
|
||||
|
||||
class VendorDebuggingError(VendorError):
|
||||
"""
|
||||
Custom exception raised when a debugging operation fails
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class VendorInactiveError(VendorError):
|
||||
pass
|
||||
|
||||
|
|
@ -84,6 +98,41 @@ class AbstractVendor:
|
|||
self.currency = currency
|
||||
self.blocked_attributes: list[Any] = []
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.vendor_name or self.get_vendor_instance().name
|
||||
|
||||
def save_response(self, data: dict[Any, Any] | list[Any]) -> None:
|
||||
with suppress(Exception):
|
||||
if settings.DEBUG or config.SAVE_VENDORS_RESPONSES:
|
||||
vendor_instance = self.get_vendor_instance()
|
||||
|
||||
if vendor_instance.last_processing_response:
|
||||
with suppress(Exception):
|
||||
vendor_instance.last_processing_response.delete(save=False)
|
||||
|
||||
json_data = json.dumps(data, indent=2, ensure_ascii=False, default=str)
|
||||
json_bytes = json_data.encode("utf-8")
|
||||
|
||||
size_threshold = 1024 * 1024 # 1MB
|
||||
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||
|
||||
if len(json_bytes) > size_threshold:
|
||||
buffer = BytesIO()
|
||||
with gzip.GzipFile(fileobj=buffer, mode="wb", compresslevel=9) as gz_file:
|
||||
gz_file.write(json_bytes)
|
||||
|
||||
compressed_data = buffer.getvalue()
|
||||
filename = f"response_{timestamp}.json.gz"
|
||||
content = ContentFile(compressed_data)
|
||||
else:
|
||||
filename = f"response_{timestamp}.json"
|
||||
content = ContentFile(json_bytes)
|
||||
|
||||
vendor_instance.last_processing_response.save(filename, content, save=True)
|
||||
|
||||
return
|
||||
raise VendorDebuggingError("Could not save response")
|
||||
|
||||
@staticmethod
|
||||
def chunk_data(data: list[Any] | None = None, num_chunks: int = 20) -> list[list[Any]] | list[Any]:
|
||||
if not data:
|
||||
|
|
|
|||
|
|
@ -304,7 +304,10 @@ class BuyAsBusinessView(APIView):
|
|||
def download_digital_asset_view(request: HttpRequest, *args, **kwargs) -> FileResponse | JsonResponse:
|
||||
try:
|
||||
logger.debug(f"download_digital_asset_view: {kwargs}")
|
||||
uuid = urlsafe_base64_decode(str(kwargs.get("order_product_uuid"))).decode("utf-8")
|
||||
op_uuid = str(kwargs.get("order_product_uuid"))
|
||||
if not op_uuid:
|
||||
raise BadRequest(_("order_product_uuid is required"))
|
||||
uuid = urlsafe_base64_decode(op_uuid).decode("utf-8")
|
||||
|
||||
download = DigitalAssetDownload.objects.get(order_product__uuid=uuid)
|
||||
|
||||
|
|
@ -387,3 +390,13 @@ index.__doc__ = _( # type: ignore [assignment]
|
|||
"admin interface index page. It uses Django's `redirect` function for handling "
|
||||
"the HTTP redirection."
|
||||
)
|
||||
|
||||
|
||||
def version(request: HttpRequest, *args, **kwargs) -> HttpResponse:
|
||||
return JsonResponse(camelize({"version": settings.EVIBES_VERSION}), status=200)
|
||||
|
||||
|
||||
# noinspection PyTypeChecker
|
||||
version.__doc__ = _( # type: ignore [assignment]
|
||||
"Returns current version of the eVibes. "
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from uuid import UUID
|
|||
|
||||
from constance import config
|
||||
from django.conf import settings
|
||||
from django.db.models import Prefetch, Q
|
||||
from django.db.models import Prefetch, Q, OuterRef, Exists
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.decorators import method_decorator
|
||||
|
|
@ -452,14 +452,23 @@ class ProductViewSet(EvibesViewSet):
|
|||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
|
||||
qs = qs.select_related("brand", "category")
|
||||
|
||||
if self.request.user.has_perm("core.view_product"):
|
||||
return qs
|
||||
return qs.filter(
|
||||
is_active=True,
|
||||
brand__is_active=True,
|
||||
category__is_active=True,
|
||||
stocks__isnull=False,
|
||||
stocks__vendor__is_active=True,
|
||||
|
||||
active_stocks = Stock.objects.filter(product_id=OuterRef("pk"), vendor__is_active=True)
|
||||
|
||||
return (
|
||||
qs.filter(
|
||||
is_active=True,
|
||||
brand__is_active=True,
|
||||
category__is_active=True,
|
||||
)
|
||||
.annotate(has_active_stocks=Exists(active_stocks))
|
||||
.filter(has_active_stocks=True)
|
||||
.distinct()
|
||||
)
|
||||
|
||||
def get_object(self):
|
||||
|
|
@ -716,8 +725,8 @@ class OrderViewSet(EvibesViewSet):
|
|||
def add_order_product(self, request: Request, *args, **kwargs) -> Response:
|
||||
serializer = AddOrderProductSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
order = self.get_object()
|
||||
try:
|
||||
order = self.get_object()
|
||||
if not (request.user.has_perm("core.add_orderproduct") or request.user == order.user):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
|
||||
|
|
@ -726,15 +735,17 @@ class OrderViewSet(EvibesViewSet):
|
|||
attributes=format_attributes(serializer.validated_data.get("attributes")),
|
||||
)
|
||||
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
||||
except Order.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except Order.DoesNotExist as dne:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": str(dne)})
|
||||
except ValueError as ve:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="remove_order_product")
|
||||
def remove_order_product(self, request: Request, *args, **kwargs) -> Response:
|
||||
serializer = RemoveOrderProductSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
order = self.get_object()
|
||||
try:
|
||||
order = self.get_object()
|
||||
if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
|
||||
|
|
@ -743,8 +754,10 @@ class OrderViewSet(EvibesViewSet):
|
|||
attributes=format_attributes(serializer.validated_data.get("attributes")),
|
||||
)
|
||||
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
||||
except Order.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except Order.DoesNotExist as dne:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": str(dne)})
|
||||
except ValueError as ve:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="bulk_add_order_products")
|
||||
def bulk_add_order_products(self, request: Request, *args, **kwargs) -> Response:
|
||||
|
|
@ -760,15 +773,17 @@ class OrderViewSet(EvibesViewSet):
|
|||
products=serializer.validated_data.get("products"),
|
||||
)
|
||||
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
||||
except Order.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except Order.DoesNotExist as dne:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": str(dne)})
|
||||
except ValueError as ve:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
|
||||
|
||||
@action(detail=True, methods=["post"], url_path="bulk_remove_order_products")
|
||||
def bulk_remove_order_products(self, request: Request, *args, **kwargs) -> Response:
|
||||
serializer = BulkRemoveOrderProductsSerializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
order = self.get_object()
|
||||
try:
|
||||
order = self.get_object()
|
||||
if not (request.user.has_perm("core.delete_orderproduct") or request.user == order.user):
|
||||
raise PermissionDenied(permission_denied_message)
|
||||
|
||||
|
|
@ -776,8 +791,10 @@ class OrderViewSet(EvibesViewSet):
|
|||
products=serializer.validated_data.get("products"),
|
||||
)
|
||||
return Response(status=status.HTTP_200_OK, data=OrderDetailSerializer(order).data)
|
||||
except Order.DoesNotExist:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND)
|
||||
except Order.DoesNotExist as dne:
|
||||
return Response(status=status.HTTP_404_NOT_FOUND, data={"detail": str(dne)})
|
||||
except ValueError as ve:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST, data={"detail": str(ve)})
|
||||
|
||||
|
||||
# noinspection PyUnusedLocal
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from core.views import (
|
|||
CustomSwaggerView,
|
||||
favicon_view,
|
||||
index,
|
||||
version,
|
||||
)
|
||||
from evibes.settings import SPECTACULAR_PLATFORM_SETTINGS
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ urlpatterns = [
|
|||
path(r"i18n/", include("django.conf.urls.i18n")),
|
||||
path(r"favicon.ico", favicon_view),
|
||||
path(r"", index),
|
||||
path(r"", version),
|
||||
path(r"", include("core.api_urls", namespace="core")),
|
||||
path(r"auth/", include("vibes_auth.urls", namespace="vibes_auth")),
|
||||
path(r"payments/", include("payments.urls", namespace="payments")),
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from importlib import import_module
|
|||
from typing import Any
|
||||
|
||||
|
||||
def create_object(module_name: str, class_name: str, *args: list[Any], **kwargs: dict[Any, Any]) -> object:
|
||||
def create_object(module_name: str, class_name: str, *args: list[Any], **kwargs: dict[Any, Any]) -> Any:
|
||||
module = import_module(module_name)
|
||||
|
||||
cls = getattr(module, class_name)
|
||||
|
|
|
|||
16
payments/utils/gateways.py
Normal file
16
payments/utils/gateways.py
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
from typing import Type
|
||||
|
||||
from evibes.utils.misc import create_object
|
||||
from payments.gateways import AbstractGateway
|
||||
from payments.models import Gateway
|
||||
|
||||
|
||||
def get_gateways_integrations(name: str | None = None) -> list[Type[AbstractGateway]]:
|
||||
gateways_integrations: list[Type[AbstractGateway]] = []
|
||||
gateways = Gateway.objects.filter(is_active=True, name=name) if name else Gateway.objects.filter(is_active=True)
|
||||
for gateway in gateways:
|
||||
if gateway.integration_path:
|
||||
module_name = ".".join(gateway.integration_path.split(".")[:-1])
|
||||
class_name = gateway.integration_path.split(".")[-1]
|
||||
gateways_integrations.append(create_object(module_name, class_name))
|
||||
return gateways_integrations
|
||||
Loading…
Reference in a new issue