diff options
-rw-r--r-- | procurement/admin.py | 15 | ||||
-rw-r--r-- | procurement/admin_forms.py | 3 | ||||
-rw-r--r-- | procurement/migrations/0002_add_representative.py | 80 | ||||
-rw-r--r-- | procurement/models.py | 12 | ||||
-rw-r--r-- | procurement/serializers.py | 8 | ||||
-rw-r--r-- | procurement/templates/procurement/includes/supplier_list.html | 12 | ||||
-rw-r--r-- | procurement/templates/procurement/source_components.html | 6 | ||||
-rw-r--r-- | procurement/views.py | 9 |
8 files changed, 132 insertions, 13 deletions
diff --git a/procurement/admin.py b/procurement/admin.py index 993b1b5..f35d706 100644 --- a/procurement/admin.py +++ b/procurement/admin.py @@ -2,13 +2,23 @@ from django.contrib import admin from django.shortcuts import render_to_response, get_object_or_404 from procurement.admin_forms import ComponentAdminForm -from procurement.models import Supplier, Component +from procurement.models import Supplier, Component, Representative +class RepresentativeAdmin(admin.ModelAdmin): + list_display = ('name', 'email', 'supplier', 'updated') + ordering = ("supplier",) + +class RepresentativeInline(admin.TabularInline): + model = Representative class SupplierAdmin(admin.ModelAdmin): - list_display = ('name', 'representative_name', 'representative_email', 'is_authorized', 'updated') + list_display = ('name', 'get_representatives', 'is_authorized', 'updated') filter_horizonal = ('components',) + inlines = [RepresentativeInline] + def get_representatives(self, obj): + return list(str(x) for x in obj.representatives.all()) + get_representatives.short_description = "Representatives" class ComponentAdmin(admin.ModelAdmin): list_display = ('name', 'sku', 'updated') @@ -27,5 +37,6 @@ class ComponentAdmin(admin.ModelAdmin): }) +admin.site.register(Representative, RepresentativeAdmin) admin.site.register(Supplier, SupplierAdmin) admin.site.register(Component, ComponentAdmin) diff --git a/procurement/admin_forms.py b/procurement/admin_forms.py index b7bbd1c..018377e 100644 --- a/procurement/admin_forms.py +++ b/procurement/admin_forms.py @@ -3,7 +3,6 @@ from django.contrib.admin.widgets import FilteredSelectMultiple from procurement.models import Supplier, Component - class ComponentAdminForm(forms.ModelForm): suppliers = forms.ModelMultipleChoiceField( queryset=Supplier.objects.filter(is_authorized=True), @@ -17,4 +16,4 @@ class ComponentAdminForm(forms.ModelForm): class Meta: model = Component - fields = ['name', 'sku', 'suppliers']
\ No newline at end of file + fields = ['name', 'sku', 'suppliers'] diff --git a/procurement/migrations/0002_add_representative.py b/procurement/migrations/0002_add_representative.py new file mode 100644 index 0000000..86a2f38 --- /dev/null +++ b/procurement/migrations/0002_add_representative.py @@ -0,0 +1,80 @@ +# Generated by Django 2.1.2 on 2018-10-18 02:18 + +from django.db import migrations, models +import django.db.models.deletion +import warnings + +def copy_reps_forward(apps, editor): + Supplier = apps.get_model("procurement", "Supplier") + Representative = apps.get_model("procurement", "Representative") + suppliers = Supplier.objects.all() + for sup in suppliers: + name = sup.representative_name + email = sup.representative_email + rep = Representative(name=name, email=email, supplier=sup) + rep.save() + +class ReverseMigrationDataLossWarning(UserWarning): + def __init__(self, supplier, kept_rep, lost_reps): + self.supplier = supplier + self.kept_rep = kept_rep + self.lost_reps = lost_reps + + @staticmethod + def format_rep(rep): + return '{} <{}>'.format(rep.name, rep.email) + + def __str__(self): + return "Supplier {} has multiple representatives, only keeping {}. {} will be lost".format(self.supplier.name, self.format_rep(self.kept_rep), [self.format_rep(rep) for rep in self.lost_reps]) + +def copy_reps_rev(apps, editor): + warnings.filterwarnings("always", category=ReverseMigrationDataLossWarning) + Supplier = apps.get_model("procurement", "Supplier") + Representative = apps.get_model("procurement", "Representative") + suppliers = Supplier.objects.all() + for sup in suppliers: + reps = sup.representatives.all() + if reps: + keep = reps[0] + if len(reps) > 1: + lost = reps[1:] + warnings.warn(ReverseMigrationDataLossWarning(sup, keep, lost)) + sup.representative_name = keep.name + sup.representative_email = keep.email + sup.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('procurement', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Representative', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=255)), + ('email', models.CharField(max_length=255)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='representative', + name='supplier', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='procurement.Supplier'), + ), + migrations.RunPython(copy_reps_forward, copy_reps_rev), + migrations.RemoveField( + model_name='supplier', + name='representative_email', + ), + migrations.RemoveField( + model_name='supplier', + name='representative_name', + ), + ] diff --git a/procurement/models.py b/procurement/models.py index 1e70736..43e5a07 100644 --- a/procurement/models.py +++ b/procurement/models.py @@ -51,19 +51,25 @@ class DashboardModel(models.Model): base_string = 'updated {quantity:.2f} {units} ago' return base_string.format(quantity=quantity, units=units) - class Supplier(DashboardModel): """ Model which represents an individual or organisation which supplies components """ name = models.CharField(max_length=255) - representative_name = models.CharField(max_length=255, null=True, blank=True) - representative_email = models.EmailField(max_length=255, null=True, blank=True) is_authorized = models.BooleanField() def __str__(self): return '{}'.format(self.name) +class Representative(DashboardModel): + """Model which represents a single Representative, each supplier + can have multiple representatives""" + name = models.CharField(max_length=255) + email = models.CharField(max_length=255) + supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE, related_name="representatives", null=True, blank=True) + + def __str__(self): + return '{} <{}>'.format(self.name, self.email) class Component(DashboardModel): """ diff --git a/procurement/serializers.py b/procurement/serializers.py index 1840837..0466402 100644 --- a/procurement/serializers.py +++ b/procurement/serializers.py @@ -1,8 +1,14 @@ from rest_framework import serializers -from procurement.models import Component, Supplier +from procurement.models import Component, Supplier, Representative +class RepresentativeSerializer(serializers.ModelSerializer): + class Meta: + model = Representative + exclude = ('created', 'updated', 'supplier', 'id') class SupplierSerializer(serializers.ModelSerializer): + representatives = RepresentativeSerializer(many=True, read_only=True) + class Meta: model = Supplier exclude = ('created', 'updated') diff --git a/procurement/templates/procurement/includes/supplier_list.html b/procurement/templates/procurement/includes/supplier_list.html index 028e62c..ffd7435 100644 --- a/procurement/templates/procurement/includes/supplier_list.html +++ b/procurement/templates/procurement/includes/supplier_list.html @@ -17,11 +17,17 @@ </thead> <tbody> {% for supplier in supplier_results %} + {% for representative in supplier.representatives.all %} <tr> + {% if forloop.first %} <td>{{ supplier.name }}</td> - <td>{{ supplier.representative_name }}</td> - <td>{{ supplier.representative_email }}</td> + {% else %} + <td></td> + {% endif %} + <td>{{ representative.name }}</td> + <td>{{ representative.email }}</td> </tr> + {% endfor %} {% empty %} <tr> <td colspan="3">No authorized suppliers found.</td> @@ -34,4 +40,4 @@ </div> <!-- /.panel-body --> </div> -<!-- / Supplier Results Panel -->
\ No newline at end of file +<!-- / Supplier Results Panel --> diff --git a/procurement/templates/procurement/source_components.html b/procurement/templates/procurement/source_components.html index 3eebf84..db412a2 100644 --- a/procurement/templates/procurement/source_components.html +++ b/procurement/templates/procurement/source_components.html @@ -42,6 +42,10 @@ <span class="record-updated text-muted small"><em>{{ suppliers_last_updated }}</em></span> </span> <span class="list-group-item"> + <span class="record-list"><i class="fa fa-users fa-fw"></i> {{ representative_count }} Representatives</span> + <span class="record-updated text-muted small"><em>{{ representatives_last_updated }}</em></span> + </span> + <span class="list-group-item"> <span class="record-list"><i class="fa fa-cog fa-fw"></i> {{ component_count }} Components</span> <span class="record-updated text-muted small"><em>{{ components_last_updated }}</em></span> </span> @@ -58,4 +62,4 @@ {% block additional_js %} <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.6-rc.0/js/select2.min.js"></script> <script src="{% static 'procurement/js/component_search.js' %}"></script> -{% endblock %}
\ No newline at end of file +{% endblock %} diff --git a/procurement/views.py b/procurement/views.py index 8891f89..1516455 100644 --- a/procurement/views.py +++ b/procurement/views.py @@ -1,7 +1,7 @@ from django.views.generic import FormView, TemplateView from procurement.forms import ComponentSearchForm -from procurement.models import Supplier, Component +from procurement.models import Supplier, Component, Representative class ComponentSearchView(FormView): @@ -20,6 +20,11 @@ class ComponentSearchView(FormView): suppliers_last_updated = '' try: + representatives_last_updated = Representative.objects.latest('updated').time_since_update + except Representative.DoesNotExist: + representatives_last_updated = '' + + try: components_last_updated = Component.objects.latest('updated').time_since_update except Component.DoesNotExist: components_last_updated = '' @@ -32,6 +37,8 @@ class ComponentSearchView(FormView): 'suppliers_last_updated': suppliers_last_updated, 'component_count': Component.objects.all().count(), 'components_last_updated': components_last_updated, + 'representative_count': Representative.objects.all().count(), + 'representatives_last_updated': representatives_last_updated, }) return context |