nexus-5/core/admin.py
2026-01-26 11:09:40 -05:00

646 lines
20 KiB
Python

from django.contrib import admin
from core.models import (
Customer,
CustomerAddress,
CustomerContact,
Account,
AccountAddress,
AccountContact,
Service,
Project,
Report,
Revenue,
Labor,
Schedule,
Invoice,
AccountPunchlist,
ProjectPunchlist,
CustomerProfile,
TeamProfile,
Scope,
Area,
Task,
TaskCompletion,
ScopeTemplate,
AreaTemplate,
TaskTemplate,
ProjectScope,
ProjectScopeCategory,
ProjectScopeTask,
ProjectScopeTaskCompletion,
ProjectScopeTemplate,
ProjectAreaTemplate,
ProjectTaskTemplate,
ServiceSession,
ProjectSession,
ServiceSessionNote,
ProjectSessionNote,
# Events & Notifications
Event,
NotificationRule,
Notification,
NotificationDelivery,
# Messaging
Conversation,
ConversationParticipant,
Message,
MessageReadReceipt,
# Session Media
ServiceSessionImage,
ProjectSessionImage,
ServiceSessionVideo,
ProjectSessionVideo,
# Chat
ChatConversation,
ChatMessage,
)
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ("name", "status", "start_date", "end_date")
list_filter = ("status",)
search_fields = ("name",)
@admin.register(Account)
class AccountAdmin(admin.ModelAdmin):
list_display = ("name", "customer", "status", "start_date", "end_date")
list_filter = ("status", "customer")
search_fields = ("name", "customer__name")
@admin.register(CustomerAddress)
class CustomerAddressAdmin(admin.ModelAdmin):
list_display = ("customer", "address_type", "is_primary", "is_active")
list_filter = ("address_type", "is_primary", "is_active")
search_fields = ("customer__name", "street_address", "city")
@admin.register(CustomerContact)
class CustomerContactAdmin(admin.ModelAdmin):
list_display = ("full_name", "customer", "email", "phone", "is_primary", "is_active")
list_filter = ("is_primary", "is_active")
search_fields = ("first_name", "last_name", "customer__name", "email", "phone")
@admin.register(AccountAddress)
class AccountAddressAdmin(admin.ModelAdmin):
list_display = ("account", "street_address", "city", "is_primary", "is_active")
list_filter = ("is_primary", "is_active")
search_fields = ("account__name", "street_address", "city")
@admin.register(AccountContact)
class AccountContactAdmin(admin.ModelAdmin):
list_display = ("full_name", "account", "email", "phone", "is_primary", "is_active")
list_filter = ("is_primary", "is_active")
search_fields = ("first_name", "last_name", "account__name", "email", "phone")
@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ("account_address", "date", "status")
list_filter = ("status", "date")
search_fields = ("account_address__account__name",)
@admin.register(Project)
class ProjectAdmin(admin.ModelAdmin):
list_display = ("customer", "account_address", "date", "status", "labor", "amount")
list_filter = ("status", "date", "customer")
search_fields = (
"customer__name",
"account_address__account__name",
"street_address",
"city",
"state",
"zip_code",
)
@admin.register(Report)
class ReportAdmin(admin.ModelAdmin):
list_display = ("team_member", "date")
list_filter = ("date",)
search_fields = ("team_member__first_name", "team_member__last_name")
@admin.register(Revenue)
class RevenueAdmin(admin.ModelAdmin):
list_display = ("account", "amount", "start_date", "end_date")
list_filter = ("start_date",)
search_fields = ("account__name",)
@admin.register(Labor)
class LaborAdmin(admin.ModelAdmin):
list_display = ("account_address", "amount", "start_date", "end_date")
list_filter = ("start_date",)
search_fields = ("account_address__account__name",)
@admin.register(Schedule)
class ScheduleAdmin(admin.ModelAdmin):
list_display = ("account_address", "start_date", "end_date", "weekend_service")
list_filter = ("weekend_service",)
search_fields = ("account_address__account__name",)
@admin.register(Invoice)
class InvoiceAdmin(admin.ModelAdmin):
list_display = ("customer", "date", "status", "date_paid", "payment_type")
list_filter = ("status", "date")
search_fields = ("customer__name",)
@admin.register(AccountPunchlist)
class AccountPunchlistAdmin(admin.ModelAdmin):
list_display = ("account", "date")
list_filter = ("date",)
search_fields = ("account__name",)
@admin.register(ProjectPunchlist)
class ProjectPunchlistAdmin(admin.ModelAdmin):
list_display = ("project", "date")
list_filter = ("date",)
search_fields = (
"project__account_address__account__name",
"project__street_address",
"project__city",
"project__state",
"project__zip_code",
"project__customer__name",
)
@admin.register(CustomerProfile)
class CustomerProfileAdmin(admin.ModelAdmin):
list_display = ("user", "get_customers", "status")
list_filter = ("status",)
search_fields = ("user__username", "first_name", "last_name", "email")
filter_horizontal = ("customers",)
def get_customers(self, obj):
"""Display comma-separated list of customers"""
return ", ".join([c.name for c in obj.customers.all()])
get_customers.short_description = "Customers"
@admin.register(TeamProfile)
class TeamProfileAdmin(admin.ModelAdmin):
list_display = ("user", "first_name", "last_name", "status")
list_filter = ("status",)
search_fields = ("user__username", "first_name", "last_name")
@admin.register(Scope)
class ScopeAdmin(admin.ModelAdmin):
list_display = ("name", "account", "account_address", "is_active")
list_filter = ("is_active", "account")
search_fields = ("name", "account__name", "account_address__street_address")
class TaskTemplateInline(admin.TabularInline):
model = TaskTemplate
extra = 1
fields = ("description", "frequency", "order", "is_conditional", "estimated_minutes")
ordering = ("order",)
show_change_link = True
class AreaTemplateInline(admin.TabularInline):
model = AreaTemplate
extra = 1
fields = ("name", "order")
ordering = ("order",)
show_change_link = True
@admin.register(ScopeTemplate)
class ScopeTemplateAdmin(admin.ModelAdmin):
list_display = ("name", "is_active")
list_filter = ("is_active",)
search_fields = ("name", "description")
inlines = (AreaTemplateInline,)
ordering = ("name",)
@admin.register(AreaTemplate)
class AreaTemplateAdmin(admin.ModelAdmin):
list_display = ("name", "scope_template", "order")
list_filter = ("scope_template",)
search_fields = ("name", "scope_template__name")
inlines = (TaskTemplateInline,)
ordering = ("scope_template", "order", "name")
@admin.register(TaskTemplate)
class TaskTemplateAdmin(admin.ModelAdmin):
list_display = ("short_description", "area_template", "frequency", "order", "is_conditional")
list_filter = ("frequency", "is_conditional", "area_template__scope_template")
search_fields = ("description", "area_template__name", "area_template__scope_template__name")
ordering = ("area_template", "order")
def short_description(self, obj):
return (obj.description or "")[:60]
short_description.short_description = "Description"
@admin.register(ServiceSession)
class ServiceSessionAdmin(admin.ModelAdmin):
list_display = (
"service",
"account",
"account_address",
"scope",
"start",
"end",
"created_by",
"closed_by",
"is_active",
)
list_filter = ("start", "end", "account", "scope")
search_fields = (
"service__account_address__account__name",
"account_address__street_address",
"account_address__city",
"created_by__first_name",
"created_by__last_name",
)
ordering = ("-start",)
readonly_fields = ("duration_seconds",)
filter_horizontal = ("completed_tasks",)
@admin.register(ProjectScope)
class ProjectScopeAdmin(admin.ModelAdmin):
list_display = ("name", "project", "account", "account_address", "is_active")
list_filter = ("is_active", "project", "account")
search_fields = (
"name",
"project__customer__name",
"project__account_address__account__name",
"account__name",
"account_address__street_address",
)
ordering = ("name",)
@admin.register(ProjectScopeCategory)
class ProjectScopeCategoryAdmin(admin.ModelAdmin):
list_display = ("name", "scope", "order")
list_filter = ("scope",)
search_fields = ("name", "scope__name")
ordering = ("scope", "order", "name")
@admin.register(ProjectScopeTask)
class ProjectScopeTaskAdmin(admin.ModelAdmin):
list_display = ("short_description", "category", "order", "estimated_minutes")
list_filter = ("category__scope",)
search_fields = ("description", "category__name", "category__scope__name")
ordering = ("category", "order")
def short_description(self, obj):
return (obj.description or "")[:60]
short_description.short_description = "Description"
class ProjectTaskTemplateInline(admin.TabularInline):
model = ProjectTaskTemplate
extra = 1
fields = ("description", "order", "estimated_minutes")
ordering = ("order",)
show_change_link = True
class ProjectAreaTemplateInline(admin.TabularInline):
model = ProjectAreaTemplate
extra = 1
fields = ("name", "order")
ordering = ("order",)
show_change_link = True
@admin.register(ProjectScopeTemplate)
class ProjectScopeTemplateAdmin(admin.ModelAdmin):
list_display = ("name", "is_active")
list_filter = ("is_active",)
search_fields = ("name", "description")
inlines = (ProjectAreaTemplateInline,)
ordering = ("name",)
@admin.register(ProjectAreaTemplate)
class ProjectAreaTemplateAdmin(admin.ModelAdmin):
list_display = ("name", "scope_template", "order")
list_filter = ("scope_template",)
search_fields = ("name", "scope_template__name")
inlines = (ProjectTaskTemplateInline,)
ordering = ("scope_template", "order", "name")
@admin.register(ProjectTaskTemplate)
class ProjectTaskTemplateAdmin(admin.ModelAdmin):
list_display = ("short_description", "area_template", "order", "estimated_minutes")
list_filter = ("area_template__scope_template",)
search_fields = ("description", "area_template__name", "area_template__scope_template__name")
ordering = ("area_template", "order")
def short_description(self, obj):
return (obj.description or "")[:60]
short_description.short_description = "Description"
@admin.register(ProjectSession)
class ProjectSessionAdmin(admin.ModelAdmin):
list_display = (
"project",
"account",
"account_address",
"scope",
"start",
"end",
"created_by",
"closed_by",
"is_active",
)
list_filter = ("start", "end", "account", "scope")
search_fields = (
"project__account_address__account__name",
"account_address__street_address",
"account_address__city",
"created_by__first_name",
"created_by__last_name",
)
ordering = ("-start",)
readonly_fields = ("duration_seconds",)
# Admin registrations for Area, Task, TaskCompletion, and ProjectScopeTaskCompletion
class TaskInline(admin.TabularInline):
model = Task
extra = 1
fields = ("description", "frequency", "order", "is_conditional", "estimated_minutes")
ordering = ("order",)
show_change_link = True
@admin.register(Area)
class AreaAdmin(admin.ModelAdmin):
list_display = ("name", "scope", "order")
list_filter = ("scope",)
search_fields = ("name", "scope__name")
ordering = ("scope", "order", "name")
inlines = (TaskInline,)
@admin.register(Task)
class TaskAdmin(admin.ModelAdmin):
list_display = ("short_description", "area", "frequency", "order", "is_conditional")
list_filter = ("frequency", "is_conditional", "area__scope")
search_fields = ("description", "area__name", "area__scope__name")
ordering = ("area", "order")
def short_description(self, obj):
return (obj.description or "")[:60]
short_description.short_description = "Description"
@admin.register(TaskCompletion)
class TaskCompletionAdmin(admin.ModelAdmin):
list_display = ("task", "service", "account_address", "completed_by", "completed_at", "year", "month")
list_filter = ("completed_at", "completed_by", "task__area__scope")
search_fields = (
"task__description",
"task__area__name",
"task__area__scope__name",
"service__account_address__account__name",
"service__account_address__street_address",
)
ordering = ("-completed_at",)
@admin.register(ProjectScopeTaskCompletion)
class ProjectScopeTaskCompletionAdmin(admin.ModelAdmin):
list_display = (
"task",
"project",
"account",
"account_address",
"completed_by",
"completed_at",
)
list_filter = ("completed_at", "completed_by", "task__category__scope", "project", "account")
search_fields = (
"task__description",
"task__category__name",
"task__category__scope__name",
"project__customer__name",
"project__account_address__account__name",
"account__name",
"account_address__street_address",
)
ordering = ("-completed_at",)
@admin.register(ServiceSessionNote)
class ServiceSessionNoteAdmin(admin.ModelAdmin):
list_display = ("session", "short_content", "author", "internal", "created_at")
list_filter = ("internal", "created_at", "author")
search_fields = (
"content",
"session__service__account_address__account__name",
"author__first_name",
"author__last_name",
)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
def short_content(self, obj):
return (obj.content or "")[:60]
short_content.short_description = "Content"
@admin.register(ProjectSessionNote)
class ProjectSessionNoteAdmin(admin.ModelAdmin):
list_display = ("session", "short_content", "author", "internal", "created_at")
list_filter = ("internal", "created_at", "author")
search_fields = (
"content",
"session__project__customer__name",
"author__first_name",
"author__last_name",
)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
def short_content(self, obj):
return (obj.content or "")[:60]
short_content.short_description = "Content"
# =============================================================================
# Events & Notifications
# =============================================================================
@admin.register(Event)
class EventAdmin(admin.ModelAdmin):
list_display = ("event_type", "entity_type", "entity_id", "created_at")
list_filter = ("event_type", "entity_type", "created_at")
search_fields = ("entity_type", "entity_id")
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(NotificationRule)
class NotificationRuleAdmin(admin.ModelAdmin):
list_display = ("name", "is_active", "get_channels", "created_at")
list_filter = ("is_active",)
search_fields = ("name", "description")
filter_horizontal = ("target_team_profiles", "target_customer_profiles")
readonly_fields = ("created_at", "updated_at")
def get_channels(self, obj):
return ", ".join(obj.channels) if obj.channels else ""
get_channels.short_description = "Channels"
@admin.register(Notification)
class NotificationAdmin(admin.ModelAdmin):
list_display = ("subject", "event", "status", "read_at", "created_at")
list_filter = ("status", "created_at")
search_fields = ("subject", "body")
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(NotificationDelivery)
class NotificationDeliveryAdmin(admin.ModelAdmin):
list_display = ("notification", "channel", "status", "attempts", "sent_at")
list_filter = ("channel", "status")
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
# =============================================================================
# Messaging
# =============================================================================
@admin.register(Conversation)
class ConversationAdmin(admin.ModelAdmin):
list_display = ("subject", "conversation_type", "last_message_at", "is_archived")
list_filter = ("conversation_type", "is_archived")
search_fields = ("subject",)
ordering = ("-last_message_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(ConversationParticipant)
class ConversationParticipantAdmin(admin.ModelAdmin):
list_display = ("conversation", "unread_count", "is_muted", "is_archived", "joined_at")
list_filter = ("is_muted", "is_archived")
ordering = ("-joined_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(Message)
class MessageAdmin(admin.ModelAdmin):
list_display = ("conversation", "short_body", "is_system_message", "created_at")
list_filter = ("is_system_message", "created_at")
search_fields = ("body",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
def short_body(self, obj):
return (obj.body or "")[:60]
short_body.short_description = "Body"
@admin.register(MessageReadReceipt)
class MessageReadReceiptAdmin(admin.ModelAdmin):
list_display = ("message", "read_at")
ordering = ("-read_at",)
readonly_fields = ("created_at", "updated_at")
# =============================================================================
# Session Media
# =============================================================================
@admin.register(ServiceSessionImage)
class ServiceSessionImageAdmin(admin.ModelAdmin):
list_display = ("service_session", "title", "created_at")
list_filter = ("created_at",)
search_fields = ("title",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(ProjectSessionImage)
class ProjectSessionImageAdmin(admin.ModelAdmin):
list_display = ("project_session", "title", "created_at")
list_filter = ("created_at",)
search_fields = ("title",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(ServiceSessionVideo)
class ServiceSessionVideoAdmin(admin.ModelAdmin):
list_display = ("service_session", "title", "duration_seconds", "created_at")
list_filter = ("created_at",)
search_fields = ("title",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(ProjectSessionVideo)
class ProjectSessionVideoAdmin(admin.ModelAdmin):
list_display = ("project_session", "title", "duration_seconds", "created_at")
list_filter = ("created_at",)
search_fields = ("title",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
# =============================================================================
# Chat
# =============================================================================
@admin.register(ChatConversation)
class ChatConversationAdmin(admin.ModelAdmin):
list_display = ("team_profile", "title", "is_active", "created_at", "updated_at")
list_filter = ("is_active", "created_at")
search_fields = ("title", "team_profile__first_name", "team_profile__last_name")
ordering = ("-updated_at",)
readonly_fields = ("created_at", "updated_at")
@admin.register(ChatMessage)
class ChatMessageAdmin(admin.ModelAdmin):
list_display = ("conversation", "role", "short_content", "created_at")
list_filter = ("role", "created_at")
search_fields = ("content",)
ordering = ("-created_at",)
readonly_fields = ("created_at", "updated_at")
def short_content(self, obj):
return (obj.content or "")[:60]
short_content.short_description = "Content"