Browse Source

ajout application gestion du stock

Élie Bouttier 7 years ago
parent
commit
e6a428a800

+ 2 - 0
djadhere/settings.py

@@ -39,10 +39,12 @@ INSTALLED_APPS = [
     'adhesions',
     'services',
     'banking',
+    'stocking',
     'djadhere',
 
     'bootstrap3',
     'leaflet',
+    'mptt',
 
     'django.contrib.admin',
     'django.contrib.auth',

+ 1 - 0
requirements.txt

@@ -2,4 +2,5 @@ django<1.12
 django-bootstrap3
 django-leaflet
 django-geojson
+django-mptt
 ipaddress

+ 1 - 4
services/admin.py

@@ -186,10 +186,7 @@ class IPPrefixAdmin(admin.ModelAdmin):
 
     def has_delete_permission(self, request, obj=None):
         # Interdiction de supprimer le préfix s’il est assigné à une route
-        if obj and obj.tunnel_set.exists():
-            return False
-        else:
-            return True
+        return obj and obj.tunnel_set.exists()
 
     # pour embêcher de by-passer le check has_delete_permission, on désactive l’action delete
     def get_actions(self, request):

+ 1 - 0
stocking/__init__.py

@@ -0,0 +1 @@
+default_app_config = 'stocking.apps.StockingConfig'

+ 71 - 0
stocking/admin.py

@@ -0,0 +1,71 @@
+from django.contrib import admin
+
+from .models import Equipment, Category, Location
+
+
+class CategoryFilter(admin.SimpleListFilter):
+    title = 'Catégorie'
+    parameter_name = 'category'
+
+    def lookups(self, request, model_admin):
+        return Category.objects.values_list('pk', 'name')
+    
+    def queryset(self, request, queryset):
+        try:
+            category = Category.objects.get(pk=int(self.value()))
+        except (TypeError, ValueError, Category.DoesNotExist):
+            pass
+        else:
+            categories = category.get_descendants(include_self=True)
+            queryset = queryset.filter(category__in=categories)
+        return queryset
+
+
+class CategoryAdmin(admin.ModelAdmin):
+    list_display = ('__str__', 'parent',)
+
+    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 la catégorie si elle contient des équipements
+        return obj and not Equipment.objects.filter(category__in=obj.get_descendants(include_self=True)).exists()
+
+
+class LocationAdmin(admin.ModelAdmin):
+    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 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', '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)

+ 6 - 0
stocking/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class StockingConfig(AppConfig):
+    name = 'stocking'
+    verbose_name = 'Inventaire'

+ 63 - 0
stocking/migrations/0001_initial.py

@@ -0,0 +1,63 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.2 on 2017-06-26 22:22
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+import mptt.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Category',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=64)),
+                ('lft', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('rght', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='stocking.Category', verbose_name='catégorie parente')),
+            ],
+            options={
+                'verbose_name': 'Catégorie',
+            },
+        ),
+        migrations.CreateModel(
+            name='Equipment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('quantity', models.PositiveIntegerField(default=0, verbose_name='quantité')),
+                ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='stocking.Category', verbose_name='catégorie')),
+            ],
+            options={
+                'verbose_name': 'Équipement',
+            },
+        ),
+        migrations.CreateModel(
+            name='Location',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=64)),
+            ],
+            options={
+                'verbose_name': 'Emplacement',
+            },
+        ),
+        migrations.AddField(
+            model_name='equipment',
+            name='location',
+            field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='stocking.Location', verbose_name='emplacement'),
+        ),
+        migrations.AlterUniqueTogether(
+            name='equipment',
+            unique_together=set([('category', 'location')]),
+        ),
+    ]

+ 0 - 0
stocking/migrations/__init__.py


+ 43 - 0
stocking/models.py

@@ -0,0 +1,43 @@
+from django.db import models
+
+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')
+    name = models.CharField(max_length=64)
+
+    class MPTTMeta:
+        order_insertion_by = ['name']
+
+    class Meta:
+        verbose_name = 'Catégorie'
+
+    def __str__(self):
+        return self.name
+
+
+class Location(models.Model):
+    name = models.CharField(max_length=64)
+
+    class Meta:
+        verbose_name = 'Emplacement'
+
+    def __str__(self):
+        return self.name
+
+
+class Equipment(models.Model):
+    category = models.ForeignKey(Category, verbose_name='catégorie')
+    quantity = models.PositiveIntegerField(default=0, verbose_name='quantité')
+    location = models.ForeignKey(Location, verbose_name='emplacement')
+
+    class Meta:
+        verbose_name = 'Équipement'
+        unique_together = (
+            ('category', 'location',),
+        )
+
+    def __str__(self):
+        return '%d %s à %s' % (self.quantity, self.category, self.location)

+ 3 - 0
stocking/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 3 - 0
stocking/views.py

@@ -0,0 +1,3 @@
+from django.shortcuts import render
+
+# Create your views here.