diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py
index b1ce5ec324..bc458b112d 100644
--- a/InvenTree/InvenTree/settings.py
+++ b/InvenTree/InvenTree/settings.py
@@ -48,9 +48,8 @@ INSTALLED_APPS = [
# InvenTree apps
'part.apps.PartConfig',
- 'supplier.apps.SupplierConfig',
'stock.apps.StockConfig',
- 'track.apps.TrackConfig',
+ 'supplier.apps.SupplierConfig',
]
MIDDLEWARE = [
diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py
index 202aa32447..18eb9784ec 100644
--- a/InvenTree/InvenTree/urls.py
+++ b/InvenTree/InvenTree/urls.py
@@ -16,8 +16,6 @@ from django.conf.urls.static import static
from django.views.generic.base import RedirectView
-from track.urls import tracking_urls
-
# from project.urls import prj_urls, prj_part_urls, prj_cat_urls, prj_run_urls
# from track.urls import unique_urls, part_track_urls
@@ -70,7 +68,6 @@ urlpatterns = [
url(r'^part/', include(part_urls)),
url(r'^stock/', include(stock_urls)),
url(r'^supplier/', include(supplier_urls)),
- url(r'^track/', include(tracking_urls)),
url(r'^admin/', admin.site.urls),
url(r'^auth/', include('rest_framework.urls', namespace='rest_framework')),
@@ -84,4 +81,4 @@ if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
# Send any unknown URLs to the parts page
-urlpatterns += [url(r'^.*$', RedirectView.as_view(url='part/', permanent=False), name='part-index')]
+urlpatterns += [url(r'^.*$', RedirectView.as_view(url='/part/', permanent=False), name='part-index')]
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index 9bd13a0d40..d9bdc50311 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -111,7 +111,8 @@ class Part(models.Model):
units = models.CharField(max_length=20, default="pcs", blank=True)
# Is this part "trackable"?
- # Trackable parts can have unique instances which are assigned serial numbers
+ # Trackable parts can have unique instances
+ # which are assigned serial numbers (or batch numbers)
# and can have their movements tracked
trackable = models.BooleanField(default=False)
diff --git a/InvenTree/part/templates/navbar.html b/InvenTree/part/templates/navbar.html
index b71ddf4ffc..db8cae6aa7 100644
--- a/InvenTree/part/templates/navbar.html
+++ b/InvenTree/part/templates/navbar.html
@@ -9,7 +9,6 @@
Parts
Stock
Suppliers
- Tracking
\ No newline at end of file
diff --git a/InvenTree/stock/admin.py b/InvenTree/stock/admin.py
index 99d70ff9f4..b8d55d44c7 100644
--- a/InvenTree/stock/admin.py
+++ b/InvenTree/stock/admin.py
@@ -2,6 +2,7 @@ from django.contrib import admin
from simple_history.admin import SimpleHistoryAdmin
from .models import StockLocation, StockItem
+from .models import StockItemTracking
class LocationAdmin(admin.ModelAdmin):
@@ -12,5 +13,10 @@ class StockItemAdmin(SimpleHistoryAdmin):
list_display = ('part', 'quantity', 'location', 'status', 'updated')
+class StockTrackingAdmin(admin.ModelAdmin):
+ list_display = ('item', 'date', 'title')
+
+
admin.site.register(StockLocation, LocationAdmin)
admin.site.register(StockItem, StockItemAdmin)
+admin.site.register(StockItemTracking, StockTrackingAdmin)
diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py
index e05cdfc4fa..51655b4182 100644
--- a/InvenTree/stock/forms.py
+++ b/InvenTree/stock/forms.py
@@ -44,6 +44,10 @@ class EditStockItemForm(forms.ModelForm):
'part',
'supplier_part',
'location',
+ 'belongs_to',
+ 'serial',
+ 'batch',
'quantity',
- 'status'
+ 'status',
+ 'customer'
]
diff --git a/InvenTree/stock/migrations/0007_auto_20180416_0853.py b/InvenTree/stock/migrations/0007_auto_20180416_0853.py
new file mode 100644
index 0000000000..0aa701198a
--- /dev/null
+++ b/InvenTree/stock/migrations/0007_auto_20180416_0853.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11 on 2018-04-16 08:53
+from __future__ import unicode_literals
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('supplier', '0006_auto_20180415_1011'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('stock', '0006_auto_20180415_0302'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='StockItemTracking',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('date', models.DateField(auto_now_add=True)),
+ ('title', models.CharField(max_length=250)),
+ ('description', models.CharField(blank=True, max_length=1024)),
+ ],
+ ),
+ migrations.RemoveField(
+ model_name='historicalstockitem',
+ name='history_user',
+ ),
+ migrations.RemoveField(
+ model_name='historicalstockitem',
+ name='location',
+ ),
+ migrations.RemoveField(
+ model_name='historicalstockitem',
+ name='part',
+ ),
+ migrations.RemoveField(
+ model_name='historicalstockitem',
+ name='stocktake_user',
+ ),
+ migrations.RemoveField(
+ model_name='historicalstockitem',
+ name='supplier_part',
+ ),
+ migrations.AddField(
+ model_name='stockitem',
+ name='batch',
+ field=models.CharField(blank=True, max_length=100),
+ ),
+ migrations.AddField(
+ model_name='stockitem',
+ name='belongs_to',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem'),
+ ),
+ migrations.AddField(
+ model_name='stockitem',
+ name='customer',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='supplier.Customer'),
+ ),
+ migrations.AddField(
+ model_name='stockitem',
+ name='serial',
+ field=models.PositiveIntegerField(blank=True, null=True),
+ ),
+ migrations.DeleteModel(
+ name='HistoricalStockItem',
+ ),
+ migrations.AddField(
+ model_name='stockitemtracking',
+ name='item',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='stock.StockItem'),
+ ),
+ migrations.AddField(
+ model_name='stockitemtracking',
+ name='user',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py
index c7ad5f0b9b..689a04523c 100644
--- a/InvenTree/stock/models.py
+++ b/InvenTree/stock/models.py
@@ -3,9 +3,9 @@ from django.utils.translation import ugettext as _
from django.db import models, transaction
from django.core.validators import MinValueValidator
from django.contrib.auth.models import User
-from simple_history.models import HistoricalRecords
from supplier.models import SupplierPart
+from supplier.models import Customer
from part.models import Part
from InvenTree.models import InvenTreeTree
@@ -50,19 +50,43 @@ def before_delete_stock_location(sender, instance, using, **kwargs):
class StockItem(models.Model):
+ """
+ A 'StockItem' instance represents a quantity of physical instances of a part.
+ It may exist in a StockLocation, or as part of a sub-assembly installed into another StockItem
+ StockItems may be tracked using batch or serial numbers.
+ If a serial number is assigned, then StockItem cannot have a quantity other than 1
+ """
def get_absolute_url(self):
return '/stock/item/{id}/'.format(id=self.id)
+ # The 'master' copy of the part of which this stock item is an instance
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='locations')
+ # The 'supplier part' used in this instance. May be null if no supplier parts are defined the master part
supplier_part = models.ForeignKey(SupplierPart, blank=True, null=True, on_delete=models.SET_NULL)
+ # Where the part is stored. If the part has been used to build another stock item, the location may not make sense
location = models.ForeignKey(StockLocation, on_delete=models.DO_NOTHING,
related_name='items', blank=True, null=True)
+ # If this StockItem belongs to another StockItem (e.g. as part of a sub-assembly)
+ belongs_to = models.ForeignKey('self', on_delete=models.DO_NOTHING,
+ related_name='owned_parts', blank=True, null=True)
+
+ # The StockItem may be assigned to a particular customer
+ customer = models.ForeignKey(Customer, on_delete=models.SET_NULL, related_name='stockitems', blank=True, null=True)
+
+ # Optional serial number
+ serial = models.PositiveIntegerField(blank=True, null=True)
+
+ # Optional batch information
+ batch = models.CharField(max_length=100, blank=True)
+
+ # Quantity of this stock item. Value may be overridden by other settings
quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
+ # Last time this item was updated (set automagically)
updated = models.DateField(auto_now=True)
# last time the stock was checked / counted
@@ -97,8 +121,9 @@ class StockItem(models.Model):
infinite = models.BooleanField(default=False)
- # History of this item
- history = HistoricalRecords()
+ @property
+ def has_tracking_info(self):
+ return self.tracking_info.all().count() > 0
@transaction.atomic
def stocktake(self, count, user):
@@ -147,3 +172,30 @@ class StockItem(models.Model):
n=self.quantity,
part=self.part.name,
loc=self.location.name)
+
+
+class StockItemTracking(models.Model):
+ """ Stock tracking entry
+ """
+
+ # Stock item
+ item = models.ForeignKey(StockItem, on_delete=models.CASCADE,
+ related_name='tracking_info')
+
+ # Date this entry was created (cannot be edited)
+ date = models.DateField(auto_now_add=True, editable=False)
+
+ # Short-form title for this tracking entry
+ title = models.CharField(max_length=250)
+
+ # Optional longer description
+ description = models.CharField(max_length=1024, blank=True)
+
+ # Which user created this tracking entry?
+ user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
+
+ # TODO
+ # image = models.ImageField(upload_to=func, max_length=255, null=True, blank=True)
+
+ # TODO
+ # file = models.FileField()
diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html
index 92fb3258cb..7266e692f4 100644
--- a/InvenTree/stock/templates/stock/item.html
+++ b/InvenTree/stock/templates/stock/item.html
@@ -11,10 +11,35 @@
Part |
{{ item.part.name }} |
+{% if item.belongs_to %}
+
+ Belongs To |
+ {{ item.belongs_to }} |
+
+{% elif item.location %}
Location |
{{ item.location.name }} |
+{% endif %}
+{% if item.serial %}
+
+ Serial |
+ {{ item.serial }} |
+
+{% endif %}
+{% if item.batch %}
+
+ Batch |
+ {{ item.batch }} |
+
+{% endif %}
+{% if item.customer %}
+
+ Customer |
+ {{ item.customer.name }} |
+
+{% endif %}
Quantity |
{{ item.quantity }} |
@@ -47,6 +72,21 @@
{% endif %}
+{% if item.has_tracking_info %}
+Stock Tracking
+
+{% for track in item.tracking_info.all %}
+-
+ {{ track.title }}
+ {% if track.description %}
+
{{ track.description }}
+ {% endif %}
+ {{ track.date }}
+
+{% endfor %}
+
+{% endif %}
+
diff --git a/InvenTree/track/__init__.py b/InvenTree/track/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/InvenTree/track/admin.py b/InvenTree/track/admin.py
deleted file mode 100644
index 751ad5711c..0000000000
--- a/InvenTree/track/admin.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django.contrib import admin
-
-from .models import UniquePart, PartTrackingInfo
-
-
-class UniquePartAdmin(admin.ModelAdmin):
- list_display = ('part', 'serial', 'status', 'creation_date')
-
-
-class PartTrackingAdmin(admin.ModelAdmin):
- list_display = ('part', 'date', 'title')
-
-
-admin.site.register(UniquePart, UniquePartAdmin)
-admin.site.register(PartTrackingInfo, PartTrackingAdmin)
diff --git a/InvenTree/track/api.py b/InvenTree/track/api.py
deleted file mode 100644
index c36f710e0d..0000000000
--- a/InvenTree/track/api.py
+++ /dev/null
@@ -1,97 +0,0 @@
-from django_filters.rest_framework import FilterSet, DjangoFilterBackend
-from django_filters import NumberFilter
-
-from rest_framework import generics, permissions
-
-from .models import UniquePart, PartTrackingInfo
-from .serializers import UniquePartSerializer, PartTrackingInfoSerializer
-
-
-class UniquePartDetail(generics.RetrieveUpdateDestroyAPIView):
- """
-
- get:
- Return a single UniquePart
-
- post:
- Update a UniquePart
-
- delete:
- Remove a UniquePart
-
- """
-
- queryset = UniquePart.objects.all()
- serializer_class = UniquePartSerializer
- permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
-
-
-class UniquePartFilter(FilterSet):
- # Filter based on serial number
- min_sn = NumberFilter(name='serial', lookup_expr='gte')
- max_sn = NumberFilter(name='serial', lookup_expr='lte')
-
- class Meta:
- model = UniquePart
- fields = ['serial', 'part', 'customer']
-
-
-class UniquePartList(generics.ListCreateAPIView):
- """
-
- get:
- Return a list of all UniqueParts
- (with optional query filter)
-
- post:
- Create a new UniquePart
- """
-
- queryset = UniquePart.objects.all()
- serializer_class = UniquePartSerializer
- permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
- filter_backends = (DjangoFilterBackend,)
- filter_class = UniquePartFilter
-
-
-class PartTrackingDetail(generics.RetrieveUpdateDestroyAPIView):
- """
-
- get:
- Return a single PartTrackingInfo object
-
- post:
- Update a PartTrackingInfo object
-
- delete:
- Remove a PartTrackingInfo object
- """
-
- queryset = PartTrackingInfo.objects.all()
- serializer_class = PartTrackingInfoSerializer
- permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
-
-
-class PartTrackingFilter(FilterSet):
-
- class Meta:
- model = PartTrackingInfo
- fields = ['part']
-
-
-class PartTrackingList(generics.ListCreateAPIView):
- """
-
- get:
- Return a list of all PartTrackingInfo objects
- (with optional query filter)
-
- post:
- Create a new PartTrackingInfo object
- """
-
- queryset = PartTrackingInfo.objects.all()
- serializer_class = PartTrackingInfoSerializer
- permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
- filter_backends = (DjangoFilterBackend,)
- filter_class = PartTrackingFilter
diff --git a/InvenTree/track/apps.py b/InvenTree/track/apps.py
deleted file mode 100644
index 7873ab8bfe..0000000000
--- a/InvenTree/track/apps.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from __future__ import unicode_literals
-
-from django.apps import AppConfig
-
-
-class TrackConfig(AppConfig):
- name = 'track'
diff --git a/InvenTree/track/forms.py b/InvenTree/track/forms.py
deleted file mode 100644
index 7ac159a510..0000000000
--- a/InvenTree/track/forms.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django import forms
-from crispy_forms.helper import FormHelper
-from crispy_forms.layout import Submit
-
-from .models import UniquePart
-
-
-class EditTrackedPartForm(forms.ModelForm):
-
- def __init__(self, *args, **kwargs):
- super(EditTrackedPartForm, self).__init__(*args, **kwargs)
- self.helper = FormHelper()
-
- self.helper.form_id = 'id-edit-part-form'
- self.helper.form_class = 'blueForms'
- self.helper.form_method = 'post'
-
- self.helper.add_input(Submit('submit', 'Submit'))
-
- class Meta:
- model = UniquePart
- fields = [
- 'part',
- 'serial',
- 'URL',
- 'customer',
- 'status'
- ]
diff --git a/InvenTree/track/migrations/0001_initial.py b/InvenTree/track/migrations/0001_initial.py
deleted file mode 100644
index 8f84a6b6a4..0000000000
--- a/InvenTree/track/migrations/0001_initial.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2018-04-12 05:02
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('part', '0001_initial'),
- ('supplier', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='PartTrackingInfo',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('date', models.DateField(auto_now_add=True)),
- ('notes', models.CharField(max_length=500)),
- ],
- ),
- migrations.CreateModel(
- name='UniquePart',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('creation_date', models.DateField(auto_now_add=True)),
- ('serial', models.IntegerField()),
- ('status', models.IntegerField(choices=[(0, 'In progress'), (40, 'Damaged'), (10, 'In stock'), (50, 'Destroyed'), (20, 'Shipped'), (30, 'Returned')], default=0)),
- ('customer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='supplier.Customer')),
- ('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='part.Part')),
- ],
- ),
- migrations.AddField(
- model_name='parttrackinginfo',
- name='part',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='track.UniquePart'),
- ),
- migrations.AlterUniqueTogether(
- name='uniquepart',
- unique_together=set([('part', 'serial')]),
- ),
- ]
diff --git a/InvenTree/track/migrations/0002_auto_20180413_1440.py b/InvenTree/track/migrations/0002_auto_20180413_1440.py
deleted file mode 100644
index bc3a2ed5ea..0000000000
--- a/InvenTree/track/migrations/0002_auto_20180413_1440.py
+++ /dev/null
@@ -1,26 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2018-04-13 14:40
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('track', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='uniquepart',
- name='part',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='serials', to='part.Part'),
- ),
- migrations.AlterField(
- model_name='uniquepart',
- name='serial',
- field=models.PositiveIntegerField(),
- ),
- ]
diff --git a/InvenTree/track/migrations/0003_auto_20180415_0147.py b/InvenTree/track/migrations/0003_auto_20180415_0147.py
deleted file mode 100644
index ee74a6c253..0000000000
--- a/InvenTree/track/migrations/0003_auto_20180415_0147.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2018-04-15 01:47
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('track', '0002_auto_20180413_1440'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='parttrackinginfo',
- name='title',
- field=models.CharField(default='tracking information', max_length=250),
- preserve_default=False,
- ),
- migrations.AlterField(
- model_name='parttrackinginfo',
- name='notes',
- field=models.CharField(blank=True, max_length=1024),
- ),
- migrations.AlterField(
- model_name='uniquepart',
- name='status',
- field=models.IntegerField(choices=[(0, 'In progress'), (35, 'Repaired'), (40, 'Damaged'), (10, 'In stock'), (50, 'Destroyed'), (20, 'Shipped'), (30, 'Returned')], default=0),
- ),
- ]
diff --git a/InvenTree/track/migrations/0004_parttrackinginfo_user.py b/InvenTree/track/migrations/0004_parttrackinginfo_user.py
deleted file mode 100644
index 76ddb5e2a3..0000000000
--- a/InvenTree/track/migrations/0004_parttrackinginfo_user.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2018-04-15 01:50
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('track', '0003_auto_20180415_0147'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='parttrackinginfo',
- name='user',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
- ),
- ]
diff --git a/InvenTree/track/migrations/0005_uniquepart_url.py b/InvenTree/track/migrations/0005_uniquepart_url.py
deleted file mode 100644
index 50ea92eaa6..0000000000
--- a/InvenTree/track/migrations/0005_uniquepart_url.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.11 on 2018-04-15 15:21
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('track', '0004_parttrackinginfo_user'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='uniquepart',
- name='URL',
- field=models.URLField(blank=True),
- ),
- ]
diff --git a/InvenTree/track/migrations/__init__.py b/InvenTree/track/migrations/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/InvenTree/track/models.py b/InvenTree/track/models.py
deleted file mode 100644
index 8df03994a1..0000000000
--- a/InvenTree/track/models.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from __future__ import unicode_literals
-from django.utils.translation import ugettext as _
-from django.db import models
-
-from django.contrib.auth.models import User
-
-from supplier.models import Customer
-from part.models import Part
-
-
-class UniquePart(models.Model):
- """ A unique instance of a Part object.
- Used for tracking parts based on serial numbers,
- and tracking all events in the life of a part
- """
-
- def get_absolute_url(self):
- return "/track/{id}/".format(id=self.id)
-
- class Meta:
- # Cannot have multiple parts with same serial number
- unique_together = ('part', 'serial')
-
- part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='serials')
-
- creation_date = models.DateField(auto_now_add=True,
- editable=False)
-
- serial = models.PositiveIntegerField()
-
- # Provide a URL for an external link
- URL = models.URLField(blank=True)
-
- # createdBy = models.ForeignKey(User)
-
- customer = models.ForeignKey(Customer, blank=True, null=True)
-
- # Part status types
- PART_IN_PROGRESS = 0
- PART_IN_STOCK = 10
- PART_SHIPPED = 20
- PART_RETURNED = 30
- PART_REPAIRED = 35
- PART_DAMAGED = 40
- PART_DESTROYED = 50
-
- PART_STATUS_CODES = {
- PART_IN_PROGRESS: _("In progress"),
- PART_IN_STOCK: _("In stock"),
- PART_SHIPPED: _("Shipped"),
- PART_RETURNED: _("Returned"),
- PART_REPAIRED: _("Repaired"),
- PART_DAMAGED: _("Damaged"),
- PART_DESTROYED: _("Destroyed")
- }
-
- status = models.IntegerField(default=PART_IN_PROGRESS, choices=PART_STATUS_CODES.items())
-
- def __str__(self):
- return "{pn} - # {sn}".format(pn=self.part.name,
- sn=self.serial)
-
-
-class PartTrackingInfo(models.Model):
- """ Single data-point in the life of a UniquePart
- Each time something happens to the UniquePart,
- a new PartTrackingInfo object should be created.
- """
-
- part = models.ForeignKey(UniquePart, on_delete=models.CASCADE, related_name='tracking_info')
-
- date = models.DateField(auto_now_add=True, editable=False)
-
- title = models.CharField(max_length=250)
-
- notes = models.CharField(max_length=1024, blank=True)
-
- user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
diff --git a/InvenTree/track/serializers.py b/InvenTree/track/serializers.py
deleted file mode 100644
index 34f15e227d..0000000000
--- a/InvenTree/track/serializers.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from rest_framework import serializers
-
-from .models import UniquePart, PartTrackingInfo
-
-
-class UniquePartSerializer(serializers.HyperlinkedModelSerializer):
-
- class Meta:
- model = UniquePart
- fields = ['url',
- 'part',
- 'creation_date',
- 'serial',
- # 'createdBy',
- 'customer',
- 'status']
-
-
-class PartTrackingInfoSerializer(serializers.HyperlinkedModelSerializer):
-
- class Meta:
- model = PartTrackingInfo
- fields = '__all__'
diff --git a/InvenTree/track/templates/track/create.html b/InvenTree/track/templates/track/create.html
deleted file mode 100644
index 0760c37871..0000000000
--- a/InvenTree/track/templates/track/create.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "create_edit_obj.html" %}
-
-{% block obj_title %}
-Create a new tracked part
-{% endblock %}
diff --git a/InvenTree/track/templates/track/delete.html b/InvenTree/track/templates/track/delete.html
deleted file mode 100644
index 13490c000c..0000000000
--- a/InvenTree/track/templates/track/delete.html
+++ /dev/null
@@ -1,11 +0,0 @@
-{% extends "delete_obj.html" %}
-
-{% block del_title %}
-Are you sure you want to delete tracking info for this part?
-{% endblock %}
-
-{% block del_body %}
-
-All tracking information for part {{ track.part.name }} SN-{{ track.serial }} will be deleted.
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/track/templates/track/detail.html b/InvenTree/track/templates/track/detail.html
deleted file mode 100644
index 6626cd4b49..0000000000
--- a/InvenTree/track/templates/track/detail.html
+++ /dev/null
@@ -1,59 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-Part tracking information
-
-
-
- Part |
- {{ part.part.name }} |
-
-
- Serial Number |
- {{ part.serial }} |
-
-
- Creation Date |
- {{ part.creation_date }} |
-
- {% if part.URL %}
-
- URL |
- {{ part.URL }} |
-
- {% endif %}
- {% if part.customer %}
-
- Customer |
- {{ part.customer }} |
-
- {% endif %}
-
- Status |
- {{ part.get_status_display }} |
-
-
-
-{% if part.tracking_info.all|length > 0 %}
-
Tracking information:
-
-{% for info in part.tracking_info.all %}
--
- {{ info.title }}
- {% if info.note %}
{{ info.notes }}{% endif %}{{ info.date }}
-
-{% endfor %}
-
-{% endif %}
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/track/templates/track/edit.html b/InvenTree/track/templates/track/edit.html
deleted file mode 100644
index 9f23c94e9d..0000000000
--- a/InvenTree/track/templates/track/edit.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "create_edit_obj.html" %}
-
-{% block obj_title %}
-Edit tracked part information
-{% endblock %}
diff --git a/InvenTree/track/templates/track/index.html b/InvenTree/track/templates/track/index.html
deleted file mode 100644
index 94ed748a4a..0000000000
--- a/InvenTree/track/templates/track/index.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-
Part Tracking
-
-
-
-{% if is_paginated %}
-
-{% endif %}
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/track/templates/track/new.html b/InvenTree/track/templates/track/new.html
deleted file mode 100644
index 9cd5dfea90..0000000000
--- a/InvenTree/track/templates/track/new.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% extends "base.html" %}
-
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/track/tests.py b/InvenTree/track/tests.py
deleted file mode 100644
index a79ca8be56..0000000000
--- a/InvenTree/track/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# from django.test import TestCase
-
-# Create your tests here.
diff --git a/InvenTree/track/urls.py b/InvenTree/track/urls.py
deleted file mode 100644
index 5f7332f417..0000000000
--- a/InvenTree/track/urls.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from django.conf.urls import url, include
-
-from . import views
-
-"""
-TODO - Implement JSON API for part serial number tracking
-part_track_api_urls = [
- url(r'^(?P
[0-9]+)/?$', api.PartTrackingDetail.as_view(), name='parttrackinginfo-detail'),
-
- url(r'^\?.*/?$', api.PartTrackingList.as_view()),
- url(r'^$', api.PartTrackingList.as_view())
-]
-
-unique_api_urls = [
-
- # Detail for a single unique part
- url(r'^(?P[0-9]+)/?$', api.UniquePartDetail.as_view(), name='uniquepart-detail'),
-
- # List all unique parts, with optional filters
- url(r'^\?.*/?$', api.UniquePartList.as_view()),
- url(r'^$', api.UniquePartList.as_view()),
-]
-"""
-
-track_detail_urls = [
- url(r'^edit/?', views.TrackEdit.as_view(), name='track-edit'),
- url(r'^delete/?', views.TrackDelete.as_view(), name='track-delete'),
-
- url('^.*$', views.TrackDetail.as_view(), name='track-detail'),
-]
-
-tracking_urls = [
- # Detail view
- url(r'^(?P\d+)/', include(track_detail_urls)),
-
- # Create a new tracking item
- url(r'^new/?', views.TrackCreate.as_view(), name='track-create'),
-
- # List ALL tracked items
- url(r'^.*$', views.TrackIndex.as_view(), name='track-index'),
-]
diff --git a/InvenTree/track/views.py b/InvenTree/track/views.py
deleted file mode 100644
index 5af7b57f77..0000000000
--- a/InvenTree/track/views.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from django.shortcuts import get_object_or_404
-from django.http import HttpResponseRedirect
-
-from django.views.generic import DetailView, ListView
-from django.views.generic.edit import UpdateView, DeleteView, CreateView
-
-from part.models import Part
-from .models import UniquePart
-
-from .forms import EditTrackedPartForm
-
-
-class TrackIndex(ListView):
- model = UniquePart
- template_name = 'track/index.html'
- context_object_name = 'parts'
- paginate_by = 50
-
- def get_queryset(self):
- return UniquePart.objects.order_by('part__name', 'serial')
-
-
-class TrackDetail(DetailView):
- queryset = UniquePart.objects.all()
- template_name = 'track/detail.html'
- context_object_name = 'part'
-
-
-class TrackCreate(CreateView):
- model = UniquePart
- form_class = EditTrackedPartForm
- template_name = 'track/create.html'
- context_object_name = 'part'
-
- def get_initial(self):
- initials = super(TrackCreate, self).get_initial().copy()
-
- part_id = self.request.GET.get('part', None)
-
- if part_id:
- initials['part'] = get_object_or_404(Part, pk=part_id)
-
- return initials
-
-
-class TrackEdit(UpdateView):
- model = UniquePart
- form_class = EditTrackedPartForm
- template_name = 'track/edit.html'
- context_obect_name = 'part'
-
-
-class TrackDelete(DeleteView):
- model = UniquePart
- success_url = '/track'
- template_name = 'track/delete.html'
- context_object_name = 'track'
-
- def post(self, request, *args, **kwargs):
- if 'confirm' in request.POST:
- return super(TrackDelete, self).post(request, *args, **kwargs)
- else:
- return HttpResponseRedirect(self.get_object().get_absolute_url())
diff --git a/Makefile b/Makefile
index ac9cb51062..e2a51d7779 100644
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,6 @@ migrate:
python InvenTree/manage.py makemigrations part
python InvenTree/manage.py makemigrations stock
python InvenTree/manage.py makemigrations supplier
- python InvenTree/manage.py makemigrations track
python InvenTree/manage.py migrate --run-syncdb
python InvenTree/manage.py check