from contextlib import suppress from typing import ClassVar, Type from constance.admin import Config from constance.admin import ConstanceAdmin as BaseConstanceAdmin from django.apps import apps from django.contrib.admin import ModelAdmin, TabularInline, action, register, site from django.contrib.gis.admin import GISModelAdmin from django.contrib.messages import constants as messages from django.db.models import Model from django.utils.translation import gettext_lazy as _ from modeltranslation.translator import NotRegistered, translator from modeltranslation.utils import get_translation_fields from mptt.admin import DraggableMPTTAdmin from core.forms import OrderForm, OrderProductForm, VendorForm from core.models import ( Address, Attribute, AttributeGroup, AttributeValue, Brand, Category, CategoryTag, Feedback, Order, OrderProduct, Product, ProductImage, ProductTag, PromoCode, Promotion, Stock, Vendor, Wishlist, CustomerRelationshipManagementProvider, OrderCrmLink, ) from evibes.settings import CONSTANCE_CONFIG class FieldsetsMixin: general_fields: list = [] relation_fields: list = [] model: ClassVar[Type[Model]] def get_fieldsets(self, request, obj=None): if request: pass if obj: pass fieldsets = [] def add_translations_fieldset(fss): with suppress(NotRegistered): transoptions = translator.get_options_for_model(self.model) translation_fields = [] for orig in transoptions.local_fields: translation_fields += get_translation_fields(orig) if translation_fields: fss = list(fss) + [(_("translations"), {"fields": translation_fields})] return fss if self.general_fields: fieldsets.append((_("general"), {"fields": self.general_fields})) if self.relation_fields: fieldsets.append((_("relations"), {"fields": self.relation_fields})) opts = self.model._meta meta_fields = [] if any(f.name == "uuid" for f in opts.fields): meta_fields.append("uuid") if any(f.name == "slug" for f in opts.fields): meta_fields.append("slug") if any(f.name == "sku" for f in opts.fields): meta_fields.append("sku") if any(f.name == "human_readable_id" for f in opts.fields): meta_fields.append("sku") if meta_fields: fieldsets.append((_("metadata"), {"fields": meta_fields})) ts = [] for name in ("created", "modified"): if any(f.name == name for f in opts.fields): ts.append(name) if ts: fieldsets.append((_("timestamps"), {"fields": ts, "classes": ["collapse"]})) fieldsets = add_translations_fieldset(fieldsets) return fieldsets # noinspection PyUnresolvedReferences class ActivationActionsMixin: actions_on_top = True actions_on_bottom = True actions = [ "delete_selected", "activate_selected", "deactivate_selected", ] @action(description=_("activate selected %(verbose_name_plural)s").lower(), permissions=["change"]) def activate_selected(self, request, queryset): try: queryset.update(is_active=True) self.message_user( request=request, message=_("selected items have been activated.").lower(), level=messages.SUCCESS ) except Exception as e: self.message_user(request=request, message=str(e), level=messages.ERROR) @action(description=_("deactivate selected %(verbose_name_plural)s").lower(), permissions=["change"]) def deactivate_selected(self, request, queryset): try: queryset.update(is_active=False) self.message_user( request=request, message=_("selected items have been deactivated.").lower(), level=messages.SUCCESS ) except Exception as e: self.message_user(request=request, message=str(e), level=messages.ERROR) class AttributeValueInline(TabularInline): model = AttributeValue extra = 0 autocomplete_fields = ["attribute"] is_navtab = True verbose_name = _("attribute value") verbose_name_plural = _("attribute values") icon = "fa-solid fa-list-ul" class ProductImageInline(TabularInline): model = ProductImage extra = 0 is_navtab = True verbose_name = _("image") verbose_name_plural = _("images") icon = "fa-regular fa-images" class StockInline(TabularInline): model = Stock extra = 0 is_navtab = True verbose_name = _("stock") verbose_name_plural = _("stocks") icon = "fa-solid fa-boxes-stacked" class OrderProductInline(TabularInline): model = OrderProduct extra = 0 readonly_fields = ("product", "quantity", "buy_price") form = OrderProductForm is_navtab = True verbose_name = _("order product") verbose_name_plural = _("order products") icon = "fa-solid fa-boxes-packing" def get_queryset(self, request): return super().get_queryset(request).select_related("product").only("product__name") class CategoryChildrenInline(TabularInline): model = Category fk_name = "parent" extra = 0 fields = ("name", "description", "is_active", "image", "markup_percent") is_navtab = True verbose_name = _("children") verbose_name_plural = _("children") icon = "fa-solid fa-leaf" @register(AttributeGroup) class AttributeGroupAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = AttributeGroup # type: ignore [misc] list_display = ("name", "modified") search_fields = ("uuid", "name") readonly_fields = ("uuid", "modified", "created") general_fields = ["is_active", "name", "parent"] relation_fields = [] @register(Attribute) class AttributeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Attribute # type: ignore [misc] list_display = ("name", "group", "value_type", "modified") list_filter = ("value_type", "group", "is_active") search_fields = ("uuid", "name", "group__name") readonly_fields = ("uuid", "modified", "created") autocomplete_fields = ["categories", "group"] general_fields = ["is_active", "name", "value_type"] relation_fields = ["group", "categories"] @register(AttributeValue) class AttributeValueAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = AttributeValue # type: ignore [misc] list_display = ("attribute", "value", "modified") list_filter = ("attribute__group", "is_active") search_fields = ("uuid", "value", "attribute__name") readonly_fields = ("uuid", "modified", "created") autocomplete_fields = ["attribute"] general_fields = ["is_active", "value"] relation_fields = ["attribute", "product"] @register(Category) class CategoryAdmin(FieldsetsMixin, ActivationActionsMixin, DraggableMPTTAdmin): # noinspection PyClassVar model = Category list_display = ("indented_title", "parent", "is_active", "modified") # noinspection PyUnresolvedReferences list_filter = ("is_active", "level", "created", "modified") search_fields = ("uuid", "name") inlines = [CategoryChildrenInline] autocomplete_fields = ["parent", "tags"] readonly_fields = ("slug", "uuid", "modified", "created") general_fields = ["is_active", "name", "description", "image", "markup_percent", "priority"] relation_fields = ["parent", "tags"] @register(Brand) class BrandAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Brand # type: ignore [misc] list_display = ("name",) list_filter = ("categories", "is_active") search_fields = ("uuid", "name", "categories__name") readonly_fields = ("uuid", "slug", "modified", "created") general_fields = ["is_active", "name", "description", "priority"] relation_fields = ["small_logo", "big_logo", "categories"] @register(Product) class ProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Product # type: ignore [misc] list_display = ( "sku", "name", "is_active", "category", "brand", "price", "rating", "modified", ) list_filter = ( "is_active", "is_digital", "stocks__vendor", "tags__name", "created", "modified", ) search_fields = ( "name", "partnumber", "brand__name", "brand__slug", "category__name", "category__slug", "uuid", "slug", "sku", ) readonly_fields = ("slug", "uuid", "modified", "created") autocomplete_fields = ("category", "brand", "tags") inlines = [AttributeValueInline, ProductImageInline, StockInline] general_fields = ["is_active", "name", "partnumber", "is_digital"] relation_fields = ["category", "brand", "tags"] @register(ProductTag) class ProductTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = ProductTag # type: ignore [misc] list_display = ("tag_name",) search_fields = ("tag_name",) readonly_fields = ("uuid", "modified", "created") general_fields = ["is_active", "tag_name", "name"] relation_fields = [] @register(CategoryTag) class CategoryTagAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = CategoryTag # type: ignore [misc] list_display = ("tag_name",) search_fields = ("tag_name",) readonly_fields = ("uuid", "modified", "created") general_fields = ["is_active", "tag_name", "name"] relation_fields = [] @register(Vendor) class VendorAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Vendor # type: ignore [misc] list_display = ("name", "markup_percent", "modified") list_filter = ("markup_percent", "is_active") search_fields = ("name",) readonly_fields = ("uuid", "modified", "created") form = VendorForm general_fields = ["is_active", "name", "markup_percent", "authentication", "b2b_auth_token"] relation_fields = ["users"] @register(Feedback) class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Feedback # type: ignore [misc] list_display = ("order_product", "rating", "comment", "modified") list_filter = ("rating", "is_active") search_fields = ("order_product__product__name", "comment") readonly_fields = ("uuid", "modified", "created") general_fields = ["is_active", "rating", "comment"] relation_fields = ["order_product"] @register(Order) class OrderAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Order # type: ignore [misc] list_display = ( "human_readable_id", "user", "status", "total_price", "buy_time", "modified", ) list_filter = ("status", "buy_time", "modified", "created") search_fields = ("user__email", "status", "uuid", "human_readable_id") readonly_fields = ( "total_price", "total_quantity", "human_readable_id", "uuid", "modified", "created", ) inlines = [OrderProductInline] form = OrderForm general_fields = ["is_active", "user", "status", "notifications", "attributes", "buy_time"] relation_fields = ["promo_code", "billing_address", "shipping_address"] @register(OrderProduct) class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = OrderProduct # type: ignore [misc] list_display = ("order", "product", "quantity", "buy_price", "status", "modified") list_filter = ("status",) search_fields = ("order__user__email", "product__name") readonly_fields = ("uuid", "modified", "created") form = OrderProductForm general_fields = ["is_active", "quantity", "buy_price", "status"] relation_fields = ["order", "product"] @register(PromoCode) class PromoCodeAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = PromoCode # type: ignore [misc] list_display = ( "code", "discount_percent", "discount_amount", "start_time", "end_time", "used_on", ) list_filter = ("discount_percent", "discount_amount", "start_time", "end_time") search_fields = ("code",) readonly_fields = ("used_on", "uuid", "modified", "created") autocomplete_fields = ("user",) general_fields = [ "is_active", "code", "discount_amount", "discount_percent", "start_time", "end_time", "used_on", ] relation_fields = ["user"] @register(Promotion) class PromotionAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Promotion # type: ignore [misc] list_display = ("name", "discount_percent", "modified") search_fields = ("name",) readonly_fields = ("uuid", "modified", "created") autocomplete_fields = ("products",) general_fields = ["is_active", "name", "discount_percent", "description"] relation_fields = ["products"] @register(Stock) class StockAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Stock # type: ignore [misc] list_display = ("product", "vendor", "sku", "quantity", "price", "modified") list_filter = ("vendor", "quantity") search_fields = ("product__name", "vendor__name", "sku") readonly_fields = ("uuid", "modified", "created") autocomplete_fields = ("product", "vendor") general_fields = [ "is_active", "sku", "quantity", "price", "purchase_price", "digital_asset", ] relation_fields = ["product", "vendor"] @register(Wishlist) class WishlistAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = Wishlist # type: ignore [misc] list_display = ("user", "modified") search_fields = ("user__email",) readonly_fields = ("uuid", "modified", "created") general_fields = ["is_active", "user"] relation_fields = ["products"] @register(ProductImage) class ProductImageAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin): # type: ignore [misc] # noinspection PyClassVar model = ProductImage # type: ignore [misc] list_display = ("alt", "product", "priority", "modified") list_filter = ("priority",) search_fields = ("alt", "product__name") readonly_fields = ("uuid", "modified", "created") autocomplete_fields = ("product",) general_fields = [ "is_active", "alt", "priority", "image", ] relation_fields = ["product"] @register(Address) class AddressAdmin(FieldsetsMixin, GISModelAdmin): # noinspection PyClassVar model = Address # type: ignore [misc] list_display = ("street", "city", "region", "country", "user") list_filter = ("country", "region") search_fields = ("street", "city", "postal_code", "user__email") readonly_fields = ("uuid", "modified", "created") gis_widget_kwargs = { "attrs": { "default_lon": 37.61556, "default_lat": 55.75222, "default_zoom": 6, } } general_fields = [ "is_active", "address_line", "street", "district", "city", "region", "postal_code", "country", "raw_data", ] relation_fields = [ "user", "api_response", ] @register(CustomerRelationshipManagementProvider) class CustomerRelationshipManagementProviderAdmin(FieldsetsMixin, ModelAdmin): # noinspection PyClassVar model = CustomerRelationshipManagementProvider # type: ignore [misc] list_display = ("name", "default") search_fields = ("name",) readonly_fields = ("uuid", "modified", "created") general_fields = [ "is_active", "name", "default", "integration_url", "integration_location", "attributes", "authentication", ] relation_fields = [] @register(OrderCrmLink) class OrderCrmLinkAdmin(FieldsetsMixin, ModelAdmin): # noinspection PyClassVar model = OrderCrmLink # type: ignore [misc] list_display = ("crm_lead_id",) search_fields = ("crm_lead_id",) readonly_fields = ( "uuid", "modified", "created", "crm_lead_id", ) general_fields = [ "is_active", "crm_lead_id", ] relation_fields = [ "order", "crm", ] # Constance configuration class ConstanceConfig: class Meta: app_label = "core" object_name = "Config" concrete_model = None model_name = module_name = "config" verbose_name_plural = _("Config") abstract = False swapped = False is_composite_pk = False def get_change_permission(self): return f"change_{self.model_name}" @property def app_config(self): return apps.get_app_config(self.app_label) @property def label(self): return f"{self.app_label}.{self.object_name}" @property def label_lower(self): return f"{self.app_label}.{self.model_name}" def get_ordered_objects(self): return False _meta = Meta() # noinspection PyTypeChecker site.unregister([Config]) # noinspection PyTypeChecker site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore [list-item] site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment] site.site_header = "eVibes" site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore [assignment]