schon/core/admin.py
2025-07-01 16:02:03 +03:00

508 lines
16 KiB
Python

from contextlib import suppress
from constance.admin import Config
from constance.admin import ConstanceAdmin as BaseConstanceAdmin
from djaa_list_filter.admin import AjaxAutocompleteListFilterModelAdmin
from django.apps import apps
from django.contrib.admin import ModelAdmin, TabularInline, action, register, site
from django.contrib.gis.admin import GISModelAdmin
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 evibes.settings import CONSTANCE_CONFIG
from .forms import OrderForm, OrderProductForm, VendorForm
from .models import (
Address,
Attribute,
AttributeGroup,
AttributeValue,
Brand,
Category,
CategoryTag,
Feedback,
Order,
OrderProduct,
Product,
ProductImage,
ProductTag,
PromoCode,
Promotion,
Stock,
Vendor,
Wishlist,
)
class FieldsetsMixin:
general_fields: list = []
relation_fields: list = []
model: 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
if any(f.name == "uuid" for f in opts.fields):
if any(f.name == "slug" for f in opts.fields):
fieldsets.append((_("metadata"), {"fields": ["uuid", "slug"]}))
else:
fieldsets.append((_("metadata"), {"fields": ["uuid"]}))
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
class ActivationActionsMixin:
@action(description=str(_("activate selected %(verbose_name_plural)s")))
def activate_selected(self, request, queryset) -> str:
if request:
pass
queryset.update(is_active=True)
return str(_("%(verbose_name_plural)s activated successfully!"))
@action(description=str(_("deactivate selected %(verbose_name_plural)s")))
def deactivate_selected(self, request, queryset) -> str:
if request:
pass
queryset.update(is_active=False)
return str(_("%(verbose_name_plural)s deactivated successfully."))
def get_actions(self, request):
actions = super().get_actions(request)
actions["activate_selected"] = (
self.activate_selected,
"activate_selected",
str(_("activate selected %(verbose_name_plural)s")),
)
actions["deactivate_selected"] = (
self.deactivate_selected,
"deactivate_selected",
str(_("deactivate selected %(verbose_name_plural)s")),
)
return actions
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):
model = AttributeGroup # type: ignore
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):
model = Attribute # type: ignore
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):
model = AttributeValue # type: ignore
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):
model = Category # type: ignore
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):
model = Brand # type: ignore
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, AjaxAutocompleteListFilterModelAdmin):
model = Product # type: ignore
list_display = (
"name",
"partnumber",
"is_active",
"category",
"brand",
"price",
"rating",
"modified",
)
list_filter = (
"is_active",
"is_digital",
"stocks__vendor",
"created",
"modified",
)
autocomplete_list_filter = ("category", "author", "tags")
search_fields = (
"name",
"partnumber",
"brand__name",
"category__name",
"uuid",
"slug",
)
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):
model = ProductTag # type: ignore
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):
model = CategoryTag # type: ignore
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):
model = Vendor # type: ignore
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"]
relation_fields = []
@register(Feedback)
class FeedbackAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = Feedback # type: ignore
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):
model = Order # type: ignore
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"]
relation_fields = ["promo_code", "billing_address", "shipping_address"]
@register(OrderProduct)
class OrderProductAdmin(FieldsetsMixin, ActivationActionsMixin, ModelAdmin):
model = OrderProduct # type: ignore
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):
model = PromoCode # type: ignore
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):
model = Promotion # type: ignore
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):
model = Stock # type: ignore
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):
model = Wishlist # type: ignore
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):
model = ProductImage # type: ignore
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):
model = Address # type: ignore
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"]
# 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()
site.unregister([Config]) # type: ignore
site.register([ConstanceConfig], BaseConstanceAdmin) # type: ignore
site.site_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore
site.site_header = "eVibes"
site.index_title = CONSTANCE_CONFIG["PROJECT_NAME"][0] # type: ignore