summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--procurement/admin.py15
-rw-r--r--procurement/admin_forms.py3
-rw-r--r--procurement/migrations/0002_add_representative.py80
-rw-r--r--procurement/models.py12
-rw-r--r--procurement/serializers.py8
-rw-r--r--procurement/templates/procurement/includes/supplier_list.html12
-rw-r--r--procurement/templates/procurement/source_components.html6
-rw-r--r--procurement/views.py9
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