Élie Bouttier 7 years ago
parent
commit
76693bbfa1
2 changed files with 74 additions and 52 deletions
  1. 66 46
      stocking/admin.py
  2. 8 6
      stocking/models.py

+ 66 - 46
stocking/admin.py

@@ -1,16 +1,38 @@
 from django.contrib import admin
 from django.utils.html import format_html
 from django.urls import reverse
+from django.db import models
 
 from mptt.admin import MPTTModelAdmin
 
 from .models import Equipment, Category, Location
 
 
-class EquipmentInline(admin.StackedInline):
+### Inlines
+
+class EquipmentInline(admin.TabularInline):
     model = Equipment
-    list_display = ('location', 'category', 'quantity',)
 
+class ExistingEquipmentInline(EquipmentInline):
+    max_num = 0
+    fields = ('location', 'category', 'quantity',)
+    readonly_fields = ('location', 'category',)
+
+    def get_queryset(self, request):
+        qs = super().get_queryset(request)
+        qs = qs.select_related('location', 'category',)
+        return qs
+
+class NewEquipmentInline(admin.TabularInline):
+    model = Equipment
+    extra = 0
+    verbose_name_plural = 'Nouveaux équipements'
+
+    def get_queryset(self, request):
+        return Equipment.objects.none()
+
+
+### Filters
 
 class CategoryFilter(admin.SimpleListFilter):
     title = 'Catégorie'
@@ -30,15 +52,22 @@ class CategoryFilter(admin.SimpleListFilter):
         return queryset
 
 
-class CategoryAdmin(MPTTModelAdmin):
-    search_fields = ['name']
-    list_display = ('__str__', 'equipement_list_link',)
+### Mixins
 
-    def equipement_list_link(self, category):
-        url = reverse('admin:%s_%s_changelist' % (Equipment._meta.app_label, Equipment._meta.model_name))
-        url += '?category=%d' % category.pk
-        return format_html(u'<a href="{}">{}</a>', url, 'voir la liste des équipements')
-    equipement_list_link.short_description = 'Équipements'
+class StockMixin:
+    search_fields = ('name',)
+    list_display = ('__str__', 'quantity',)
+    inlines = (ExistingEquipmentInline, NewEquipmentInline,)
+
+    def get_queryset(self, request):
+        qs = super().get_queryset(request)
+        qs = qs.annotate(quantity=models.Sum('equipments__quantity'))
+        return qs
+
+    def quantity(self, category):
+        return category.quantity
+    quantity.short_description = 'Quantité'
+    quantity.admin_order_field = 'quantity'
 
     def get_actions(self, request):
         actions = super().get_actions(request)
@@ -46,52 +75,43 @@ class CategoryAdmin(MPTTModelAdmin):
             del actions['delete_selected']
         return actions
 
-    def has_delete_permission(self, request, obj=None):
-        # Interdiction de supprimer la catégorie si elle contient des équipements
-        return obj and not Equipment.objects.filter(category__in=obj.get_descendants(include_self=True)).exists()
 
+### Admins
 
-class LocationAdmin(admin.ModelAdmin):
-    list_display = ('__str__', 'equipement_list_link',)
-    inlines = (EquipmentInline,)
+class CategoryAdmin(StockMixin, MPTTModelAdmin):
+    def has_delete_permission(self, request, obj=None):
+        # Interdiction de supprimer la catégorie si elle contient des équipements
+        return obj \
+            and not obj.get_descendants().exists \
+            and not Equipment.objects.filter(category__in=obj.get_descendants(include_self=True)).exists()
 
-    def equipement_list_link(self, location):
-        url = reverse('admin:%s_%s_changelist' % (Equipment._meta.app_label, Equipment._meta.model_name))
-        url += '?location__id__exact=%d' % location.pk
-        return format_html(u'<a href="{}">{}</a>', url, 'voir la liste des équipements')
-    equipement_list_link.short_description = 'Équipements'
-
-    def get_actions(self, request):
-        actions = super().get_actions(request)
-        if 'delete_selected' in actions:
-            del actions['delete_selected']
-        return actions
 
+class LocationAdmin(StockMixin, admin.ModelAdmin):
     def has_delete_permission(self, request, obj=None):
         # Interdiction de supprimer l’emplacement si il contient des équipements
         return obj and not obj.equipment_set.exists()
 
 
-class EquipmentAdmin(admin.ModelAdmin):
-    list_display = ('location', 'category', 'quantity',)
-    list_display_links = ('location', 'category',)
-    list_editable = ('quantity',)
-    list_filter = (
-        ('location', admin.RelatedOnlyFieldListFilter),
-        CategoryFilter,
-    )
-
-    def get_actions(self, request):
-        actions = super().get_actions(request)
-        if 'delete_selected' in actions:
-            del actions['delete_selected']
-        return actions
-
-    def has_delete_permission(self, request, obj=None):
-        # Interdiction de supprimer le stock d’équipements s’il n’est pas vide
-        return obj and obj.quantity == 0
+#class EquipmentAdmin(admin.ModelAdmin):
+#    list_display = ('location', 'category', 'quantity',)
+#    list_display_links = ('location', 'category',)
+#    list_editable = ('quantity',)
+#    list_filter = (
+#        ('location', admin.RelatedOnlyFieldListFilter),
+#        CategoryFilter,
+#    )
+#
+#    def get_actions(self, request):
+#        actions = super().get_actions(request)
+#        if 'delete_selected' in actions:
+#            del actions['delete_selected']
+#        return actions
+#
+#    def has_delete_permission(self, request, obj=None):
+#        # Interdiction de supprimer le stock d’équipements s’il n’est pas vide
+#        return obj and obj.quantity == 0
 
 
 admin.site.register(Category, CategoryAdmin)
 admin.site.register(Location, LocationAdmin)
-admin.site.register(Equipment, EquipmentAdmin)
+#admin.site.register(Equipment, EquipmentAdmin)

+ 8 - 6
stocking/models.py

@@ -6,10 +6,10 @@ from mptt.models import MPTTModel, TreeForeignKey
 class Category(MPTTModel):
     parent = TreeForeignKey("self", null=True, blank=True, related_name='children',
                             db_index=True, verbose_name='catégorie parente', on_delete=models.CASCADE)
-    name = models.CharField(max_length=64)
+    name = models.CharField(max_length=64, verbose_name='nom')
 
     class MPTTMeta:
-        order_insertion_by = ['name']
+        order_insertion_by = ('name',)
 
     class Meta:
         verbose_name = 'Catégorie'
@@ -19,7 +19,7 @@ class Category(MPTTModel):
 
 
 class Location(models.Model):
-    name = models.CharField(max_length=64)
+    name = models.CharField(max_length=64, verbose_name='nom')
 
     class Meta:
         verbose_name = 'Emplacement'
@@ -29,9 +29,11 @@ class Location(models.Model):
 
 
 class Equipment(models.Model):
-    category = TreeForeignKey(Category, verbose_name='catégorie', on_delete=models.CASCADE)
+    category = TreeForeignKey(Category, verbose_name='catégorie',
+                    related_name='equipments', on_delete=models.CASCADE)
     quantity = models.PositiveIntegerField(default=0, verbose_name='quantité')
-    location = models.ForeignKey(Location, verbose_name='emplacement', on_delete=models.CASCADE)
+    location = models.ForeignKey(Location, verbose_name='emplacement',
+                    related_name='equipments', on_delete=models.CASCADE)
 
     class Meta:
         verbose_name = 'Équipement'
@@ -40,4 +42,4 @@ class Equipment(models.Model):
         )
 
     def __str__(self):
-        return '%d %s à %s' % (self.quantity, self.category, self.location)
+        return '%d %s chez %s' % (self.quantity, self.category, self.location)