schon/core/admin.py

598 lines
18 KiB
Python

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("human_readable_id")
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", "is_filterable"]
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]