161 lines
5.8 KiB
Python
161 lines
5.8 KiB
Python
from django.contrib import admin
|
|
from django.db.models import Q
|
|
from django.template.defaultfilters import linebreaksbr
|
|
from django.utils import timezone
|
|
from django.utils.html import format_html, format_html_join
|
|
|
|
from .models import Order, OrderItem
|
|
|
|
|
|
class GPSAvailabilityFilter(admin.SimpleListFilter):
|
|
title = 'GPS availability'
|
|
parameter_name = 'gps'
|
|
|
|
def lookups(self, request, model_admin):
|
|
return (
|
|
('yes', 'With GPS'),
|
|
('no', 'Manual only'),
|
|
)
|
|
|
|
def queryset(self, request, queryset):
|
|
if self.value() == 'yes':
|
|
return queryset.filter(latitude__isnull=False, longitude__isnull=False)
|
|
if self.value() == 'no':
|
|
return queryset.filter(Q(latitude__isnull=True) | Q(longitude__isnull=True))
|
|
return queryset
|
|
|
|
|
|
class OrderItemInline(admin.TabularInline):
|
|
model = OrderItem
|
|
extra = 0
|
|
|
|
|
|
@admin.register(Order)
|
|
class OrderAdmin(admin.ModelAdmin):
|
|
list_display = (
|
|
'id',
|
|
'user',
|
|
'status',
|
|
'payment_status',
|
|
'payment_method',
|
|
'payment_provider_label',
|
|
'short_location',
|
|
'has_precise_location',
|
|
'total_price',
|
|
'created_at',
|
|
'paid_at',
|
|
)
|
|
list_select_related = ('user',)
|
|
list_filter = ('status', 'payment_status', 'payment_provider', 'payment_method', GPSAvailabilityFilter)
|
|
search_fields = (
|
|
'user__username',
|
|
'user__email',
|
|
'id',
|
|
'payment_reference',
|
|
'payment_session_id',
|
|
'full_name',
|
|
'phone',
|
|
'address',
|
|
'location_label',
|
|
'delivery_notes',
|
|
)
|
|
readonly_fields = ('created_at', 'payment_reference', 'payment_session_id', 'paid_at', 'delivery_snapshot', 'maps_link')
|
|
date_hierarchy = 'created_at'
|
|
autocomplete_fields = ('user',)
|
|
actions = ('mark_payment_pending', 'mark_payment_paid', 'mark_status_shipped', 'mark_status_delivered')
|
|
fieldsets = (
|
|
('Order', {'fields': ('user', 'status', 'created_at', 'total_price')}),
|
|
(
|
|
'Payment',
|
|
{
|
|
'fields': (
|
|
'payment_status',
|
|
'payment_method',
|
|
'payment_provider',
|
|
'payment_currency',
|
|
'payment_reference',
|
|
'payment_session_id',
|
|
'paid_at',
|
|
)
|
|
},
|
|
),
|
|
('Delivery snapshot', {'fields': ('full_name', 'phone', 'location_label', 'address', 'delivery_notes', 'delivery_snapshot')}),
|
|
('GPS', {'fields': ('latitude', 'longitude', 'location_accuracy_m', 'maps_link')}),
|
|
)
|
|
inlines = [OrderItemInline]
|
|
|
|
@admin.display(description='Location')
|
|
def short_location(self, obj):
|
|
return obj.location_label or (obj.address.splitlines()[0].strip() if obj.address else '-')
|
|
|
|
@admin.display(boolean=True, description='GPS')
|
|
def has_precise_location(self, obj):
|
|
return obj.has_precise_location
|
|
|
|
@admin.display(description='Payment provider')
|
|
def payment_provider_label(self, obj):
|
|
return obj.payment_provider_label
|
|
|
|
@admin.display(description='Delivery summary')
|
|
def delivery_snapshot(self, obj):
|
|
lines = []
|
|
if obj.full_name:
|
|
lines.append(format_html('<strong>{}</strong>', obj.full_name))
|
|
if obj.phone:
|
|
lines.append(obj.phone)
|
|
if obj.location_label:
|
|
lines.append(obj.location_label)
|
|
if obj.address:
|
|
lines.append(linebreaksbr(obj.address))
|
|
if obj.delivery_notes:
|
|
lines.append(format_html('<em>Notes:</em> {}', obj.delivery_notes))
|
|
if not lines:
|
|
return '-'
|
|
return format_html_join(format_html('<br>'), '{}', ((line,) for line in lines))
|
|
|
|
@admin.display(description='Map')
|
|
def maps_link(self, obj):
|
|
if not obj.has_precise_location:
|
|
return '-'
|
|
return format_html(
|
|
'<a href="https://maps.google.com/?q={},{}" target="_blank" rel="noopener">Open map</a>',
|
|
obj.latitude,
|
|
obj.longitude,
|
|
)
|
|
|
|
@admin.action(description='Mark selected orders as payment pending')
|
|
def mark_payment_pending(self, request, queryset):
|
|
updated = queryset.exclude(payment_status='Pending').update(payment_status='Pending')
|
|
self.message_user(request, f'{updated} order(s) marked as payment pending.')
|
|
|
|
@admin.action(description='Mark selected orders as paid')
|
|
def mark_payment_paid(self, request, queryset):
|
|
now = timezone.now()
|
|
updated = queryset.exclude(payment_status='Paid').update(payment_status='Paid', paid_at=now)
|
|
queryset.filter(status='Pending').update(status='Paid')
|
|
self.message_user(request, f'{updated} order(s) marked as paid.')
|
|
|
|
@admin.action(description='Mark selected orders as shipped')
|
|
def mark_status_shipped(self, request, queryset):
|
|
updated = queryset.exclude(status__in=['Shipped', 'Delivered']).update(status='Shipped')
|
|
self.message_user(request, f'{updated} order(s) marked as shipped.')
|
|
|
|
@admin.action(description='Mark selected orders as delivered')
|
|
def mark_status_delivered(self, request, queryset):
|
|
updated = queryset.exclude(status='Delivered').update(status='Delivered')
|
|
cod_collected = queryset.filter(payment_method='Cash on Delivery').exclude(payment_status='Paid').update(
|
|
payment_status='Paid',
|
|
paid_at=timezone.now(),
|
|
)
|
|
message = f'{updated} order(s) marked as delivered.'
|
|
if cod_collected:
|
|
message += f' {cod_collected} COD payment(s) were also marked paid.'
|
|
self.message_user(request, message)
|
|
|
|
|
|
@admin.register(OrderItem)
|
|
class OrderItemAdmin(admin.ModelAdmin):
|
|
list_display = ('order', 'product', 'quantity', 'price')
|
|
search_fields = ('order__id', 'product__name')
|
|
autocomplete_fields = ('order', 'product')
|