diff --git a/InvenTree/part/migrations/0013_auto_20180414_2238.py b/InvenTree/part/migrations/0013_auto_20180414_2238.py new file mode 100644 index 0000000000..1184324c50 --- /dev/null +++ b/InvenTree/part/migrations/0013_auto_20180414_2238.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11 on 2018-04-14 22:38 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0012_auto_20180414_1032'), + ] + + operations = [ + migrations.AlterField( + model_name='part', + name='name', + field=models.CharField(max_length=100, unique=True), + ), + migrations.AlterUniqueTogether( + name='part', + unique_together=set([]), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 5b057f74c5..481859512a 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -77,12 +77,14 @@ class Part(models.Model): return '/part/{id}/'.format(id=self.id) # Short name of the part - name = models.CharField(max_length=100) + name = models.CharField(max_length=100, unique=True) # Longer description of the part (optional) description = models.CharField(max_length=250, blank=True) # Internal Part Number (optional) + # Potentially multiple parts map to the same internal IPN (variants?) + # So this does not have to be unique IPN = models.CharField(max_length=100, blank=True) # Provide a URL for an external link @@ -117,7 +119,7 @@ class Part(models.Model): class Meta: verbose_name = "Part" verbose_name_plural = "Parts" - unique_together = (("name", "category"),) + #unique_together = (("name", "category"),) @property def stock(self): diff --git a/InvenTree/part/templates/base.html b/InvenTree/part/templates/base.html index b788471be3..4d89b78c09 100644 --- a/InvenTree/part/templates/base.html +++ b/InvenTree/part/templates/base.html @@ -9,7 +9,8 @@ - + + @@ -31,7 +32,7 @@ InvenTree {% include "navbar.html" %} -
+
{% block content %} {% endblock %} diff --git a/InvenTree/part/templates/part/delete.html b/InvenTree/part/templates/part/delete.html index 94c5b29144..b46eedd5f6 100644 --- a/InvenTree/part/templates/part/delete.html +++ b/InvenTree/part/templates/part/delete.html @@ -1,2 +1,51 @@ {% extends 'part/part_base.html' %} +{% block details %} + +
+
Are you sure you want to delete part '{{ part.name }}'?
+
+ +

Deleting this part is a permanent action and cannot be undone.

+ + {% if part.usedInCount > 0 %} +

This part is used in BOMs for {{ part.usedInCount }} other parts. If you delete this part, the BOMs for the following parts will be updated: +

    + {% for child in part.used_in.all %} +
  • {{ child.part.name }} - {{ child.part.description }}
  • + {% endfor %} +

    + {% endif %} + + {% if part.locations.all|length > 0 %} +

    There are {{ part.locations.all|length }} stock entries defined for this part. If you delete this part, the following stock entries will also be deleted: +

      + {% for stock in part.locations.all %} +
    • {{ stock.location.name }} - {{ stock.quantity }} items
    • + {% endfor %} +
    +

    + {% endif %} + + {% if part.supplier_parts.all|length > 0 %} +

    There are {{ part.supplier_parts.all|length }} suppliers defined for this part. If you delete this part, the following supplier parts will also be deleted. +

      + {% for spart in part.supplier_parts.all %} +
    • {{ spart.supplier.name }} - {{ spart.SKU }}
    • + {% endfor %} +
    +

    + {% endif %} + + {% if part.serials.all|length > 0 %} +

    There are {{ part.serials.all|length }} unique parts tracked for '{{ part.name }}'. Deleting this part will permanently remove this tracking information.

    + {% endif %} + +
    {% csrf_token %} + + +
    + +
+
+{% endblock %} \ No newline at end of file diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py index a17459b7a6..6bc95369da 100644 --- a/InvenTree/part/urls.py +++ b/InvenTree/part/urls.py @@ -40,7 +40,7 @@ bom_api_urls = [ part_detail_urls = [ url(r'^edit/?', views.PartEdit.as_view(), name='part-edit'), - url(r'^delete/?', views.delete, name='part-delete'), + url(r'^delete/?', views.PartDelete.as_view(), name='part-delete'), url(r'^track/?', views.PartDetail.as_view(template_name='part/track.html'), name='part-track'), url(r'^bom/?', views.PartDetail.as_view(template_name='part/bom.html'), name='part-bom'), url(r'^stock/?', views.PartDetail.as_view(template_name='part/stock.html'), name='part-stock'), diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 43c26d1cc5..4e0c314037 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -6,7 +6,7 @@ from django.http import HttpResponseRedirect from django.urls import reverse from django.views.generic import DetailView, ListView -from django.views.generic.edit import UpdateView +from django.views.generic.edit import UpdateView, DeleteView, CreateView from .forms import EditPartForm @@ -46,5 +46,15 @@ class PartEdit(UpdateView): template_name = 'part/edit.html' -def delete(request, pk): - return HttpResponseRedirect('/part/{pk}/'.format(pk=pk)) +class PartDelete(DeleteView): + model = Part + template_name = 'part/delete.html' + + success_url = '/part/' + + def post(self, request, *args, **kwargs): + if 'confirm' in request.POST: + return super(PartDelete, self).post(request, *args, **kwargs) + else: + return HttpResponseRedirect(self.get_object().get_absolute_url()) + diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index 9ee2dddbaa..4af0ac2488 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -117,4 +117,18 @@ table tr:nth-child(odd) { border: 1px solid #ddd; border-bottom-color: transparent; cursor: default; -} \ No newline at end of file +} + +.panel-danger { + border-radius: 5px; + padding: 5px; + margin: 5px; + border-color: #ebccd1; +} + +.panel-danger>.panel-heading { + background-color: #ebccd1; + border-top-left-radius: 3px; + border-top-right-radius: 3px; +} +