mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Starting to implement BOM management
- Each part can be made of other parts - Disable tracking and project apps for now - Project will change (eventually) to work order - Part parameters have been disabled (for now)
This commit is contained in:
parent
47e99d5f35
commit
ed61ebe5b7
@ -46,10 +46,11 @@ INSTALLED_APPS = [
|
|||||||
|
|
||||||
# InvenTree apps
|
# InvenTree apps
|
||||||
'part.apps.PartConfig',
|
'part.apps.PartConfig',
|
||||||
'project.apps.ProjectConfig',
|
'bom.apps.BomConfig',
|
||||||
'stock.apps.StockConfig',
|
|
||||||
'supplier.apps.SupplierConfig',
|
'supplier.apps.SupplierConfig',
|
||||||
'track.apps.TrackConfig'
|
'stock.apps.StockConfig',
|
||||||
|
#'project.apps.ProjectConfig',
|
||||||
|
#'track.apps.TrackConfig',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
@ -3,11 +3,14 @@ from django.contrib import admin
|
|||||||
|
|
||||||
from rest_framework.documentation import include_docs_urls
|
from rest_framework.documentation import include_docs_urls
|
||||||
|
|
||||||
from part.urls import part_urls, part_cat_urls, part_param_urls, part_param_template_urls
|
from part.urls import part_urls, part_cat_urls
|
||||||
|
from bom.urls import bom_urls
|
||||||
from stock.urls import stock_urls, stock_loc_urls
|
from stock.urls import stock_urls, stock_loc_urls
|
||||||
from project.urls import prj_urls, prj_part_urls, prj_cat_urls, prj_run_urls
|
|
||||||
from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls
|
from supplier.urls import cust_urls, manu_urls, supplier_part_urls, price_break_urls, supplier_urls
|
||||||
from track.urls import unique_urls, part_track_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
|
||||||
|
|
||||||
from users.urls import user_urls
|
from users.urls import user_urls
|
||||||
|
|
||||||
admin.site.site_header = "InvenTree Admin"
|
admin.site.site_header = "InvenTree Admin"
|
||||||
@ -21,8 +24,11 @@ apipatterns = [
|
|||||||
# Part URLs
|
# Part URLs
|
||||||
url(r'^part/', include(part_urls)),
|
url(r'^part/', include(part_urls)),
|
||||||
url(r'^part-category/', include(part_cat_urls)),
|
url(r'^part-category/', include(part_cat_urls)),
|
||||||
url(r'^part-param/', include(part_param_urls)),
|
#url(r'^part-param/', include(part_param_urls)),
|
||||||
url(r'^part-param-template/', include(part_param_template_urls)),
|
#url(r'^part-param-template/', include(part_param_template_urls)),
|
||||||
|
|
||||||
|
# Part BOM URLs
|
||||||
|
url(r'^bom/', include(bom_urls)),
|
||||||
|
|
||||||
# Supplier URLs
|
# Supplier URLs
|
||||||
url(r'^supplier/', include(supplier_urls)),
|
url(r'^supplier/', include(supplier_urls)),
|
||||||
@ -32,14 +38,14 @@ apipatterns = [
|
|||||||
url(r'^customer/', include(cust_urls)),
|
url(r'^customer/', include(cust_urls)),
|
||||||
|
|
||||||
# Tracking URLs
|
# Tracking URLs
|
||||||
url(r'^track/', include(part_track_urls)),
|
#url(r'^track/', include(part_track_urls)),
|
||||||
url(r'^unique-part/', include(unique_urls)),
|
#url(r'^unique-part/', include(unique_urls)),
|
||||||
|
|
||||||
# Project URLs
|
# Project URLs
|
||||||
url(r'^project/', include(prj_urls)),
|
#url(r'^project/', include(prj_urls)),
|
||||||
url(r'^project-category/', include(prj_cat_urls)),
|
#url(r'^project-category/', include(prj_cat_urls)),
|
||||||
url(r'^project-part/', include(prj_part_urls)),
|
#url(r'^project-part/', include(prj_part_urls)),
|
||||||
url(r'^project-run/', include(prj_run_urls)),
|
#url(r'^project-run/', include(prj_run_urls)),
|
||||||
|
|
||||||
# User URLs
|
# User URLs
|
||||||
url(r'^user/', include(user_urls)),
|
url(r'^user/', include(user_urls)),
|
||||||
|
0
InvenTree/bom/__init__.py
Normal file
0
InvenTree/bom/__init__.py
Normal file
8
InvenTree/bom/admin.py
Normal file
8
InvenTree/bom/admin.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from .models import BomItem
|
||||||
|
|
||||||
|
class BomItemAdmin(admin.ModelAdmin):
|
||||||
|
list_display=('part', 'sub_part', 'quantity')
|
||||||
|
|
||||||
|
admin.site.register(BomItem, BomItemAdmin)
|
6
InvenTree/bom/apps.py
Normal file
6
InvenTree/bom/apps.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
class BomConfig(AppConfig):
|
||||||
|
name = 'bom'
|
37
InvenTree/bom/models.py
Normal file
37
InvenTree/bom/models.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
from django.utils.translation import ugettext as _
|
||||||
|
from django.db import models
|
||||||
|
from django.db.models import Sum
|
||||||
|
from django.core.validators import MinValueValidator
|
||||||
|
|
||||||
|
from part.models import Part
|
||||||
|
|
||||||
|
class BomItem(models.Model):
|
||||||
|
""" A BomItem links a part to its component items.
|
||||||
|
A part can have a BOM (bill of materials) which defines
|
||||||
|
which parts are required (and in what quatity) to make it
|
||||||
|
"""
|
||||||
|
|
||||||
|
# A link to the parent part
|
||||||
|
# Each part will get a reverse lookup field 'bom_items'
|
||||||
|
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items')
|
||||||
|
|
||||||
|
# A link to the child item (sub-part)
|
||||||
|
# Each part will get a reverse lookup field 'used_in'
|
||||||
|
sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in')
|
||||||
|
|
||||||
|
# Quantity required
|
||||||
|
quantity = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "BOM Item"
|
||||||
|
|
||||||
|
# Prevent duplication of parent/child rows
|
||||||
|
unique_together = ('part', 'sub_part')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{par} -> {child} ({n})".format(
|
||||||
|
par=self.part.name,
|
||||||
|
child=self.sub_part.name,
|
||||||
|
n=self.quantity)
|
13
InvenTree/bom/serializers.py
Normal file
13
InvenTree/bom/serializers.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from .models import BomItem
|
||||||
|
|
||||||
|
|
||||||
|
class BomItemSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = BomItem
|
||||||
|
fields = ('url',
|
||||||
|
'part',
|
||||||
|
'sub_part',
|
||||||
|
'quantity')
|
6
InvenTree/bom/tests.py
Normal file
6
InvenTree/bom/tests.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
12
InvenTree/bom/urls.py
Normal file
12
InvenTree/bom/urls.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
bom_urls = [
|
||||||
|
# Bom Item detail
|
||||||
|
url(r'^(?P<pk>[0-9]+)/?$', views.BomItemDetail.as_view(), name='bomitem-detail'),
|
||||||
|
|
||||||
|
# List of top-level categories
|
||||||
|
url(r'^\?*.*/?$', views.BomItemList.as_view()),
|
||||||
|
url(r'^$', views.BomItemList.as_view())
|
||||||
|
]
|
35
InvenTree/bom/views.py
Normal file
35
InvenTree/bom/views.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
||||||
|
|
||||||
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
|
from InvenTree.models import FilterChildren
|
||||||
|
|
||||||
|
from .models import BomItem
|
||||||
|
|
||||||
|
from .serializers import BomItemSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class BomItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
|
||||||
|
queryset = BomItem.objects.all()
|
||||||
|
serializer_class = BomItemSerializer
|
||||||
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
|
||||||
|
class BomItemFilter(FilterSet):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = BomItem
|
||||||
|
fields = ['part', 'sub_part']
|
||||||
|
|
||||||
|
|
||||||
|
class BomItemList(generics.ListCreateAPIView):
|
||||||
|
|
||||||
|
#def get_queryset(self):
|
||||||
|
# params = self.request.
|
||||||
|
|
||||||
|
queryset = BomItem.objects.all()
|
||||||
|
serializer_class = BomItemSerializer
|
||||||
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
filter_backends = (DjangoFilterBackend,)
|
||||||
|
filter_class = BomItemFilter
|
@ -1,7 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import PartCategory, Part, PartParameter, PartParameterTemplate, CategoryParameterLink
|
from .models import PartCategory, Part
|
||||||
|
|
||||||
|
|
||||||
class PartAdmin(admin.ModelAdmin):
|
class PartAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
@ -12,18 +11,18 @@ class PartCategoryAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
list_display = ('name', 'path', 'description')
|
list_display = ('name', 'path', 'description')
|
||||||
|
|
||||||
|
"""
|
||||||
class ParameterTemplateAdmin(admin.ModelAdmin):
|
class ParameterTemplateAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'units', 'format')
|
list_display = ('name', 'units', 'format')
|
||||||
|
|
||||||
|
|
||||||
class ParameterAdmin(admin.ModelAdmin):
|
class ParameterAdmin(admin.ModelAdmin):
|
||||||
list_display = ('part', 'template', 'value')
|
list_display = ('part', 'template', 'value')
|
||||||
|
"""
|
||||||
|
|
||||||
admin.site.register(Part, PartAdmin)
|
admin.site.register(Part, PartAdmin)
|
||||||
admin.site.register(PartCategory, PartCategoryAdmin)
|
admin.site.register(PartCategory, PartCategoryAdmin)
|
||||||
|
|
||||||
admin.site.register(PartParameter, ParameterAdmin)
|
#admin.site.register(PartParameter, ParameterAdmin)
|
||||||
admin.site.register(PartParameterTemplate, ParameterTemplateAdmin)
|
#admin.site.register(PartParameterTemplate, ParameterTemplateAdmin)
|
||||||
admin.site.register(CategoryParameterLink)
|
#admin.site.register(CategoryParameterLink)
|
||||||
|
@ -21,7 +21,10 @@ class PartCategory(InvenTreeTree):
|
|||||||
|
|
||||||
|
|
||||||
class Part(models.Model):
|
class Part(models.Model):
|
||||||
""" Represents a """
|
""" Represents an abstract part
|
||||||
|
Parts can be "stocked" in multiple warehouses,
|
||||||
|
and can be combined to form other parts
|
||||||
|
"""
|
||||||
|
|
||||||
# Short name of the part
|
# Short name of the part
|
||||||
name = models.CharField(max_length=100)
|
name = models.CharField(max_length=100)
|
||||||
@ -92,84 +95,4 @@ class Part(models.Model):
|
|||||||
return projects
|
return projects
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplate(models.Model):
|
|
||||||
""" A PartParameterTemplate pre-defines a parameter field,
|
|
||||||
ready to be copied for use with a given Part.
|
|
||||||
A PartParameterTemplate can be optionally associated with a PartCategory
|
|
||||||
"""
|
|
||||||
name = models.CharField(max_length=20, unique=True)
|
|
||||||
units = models.CharField(max_length=10, blank=True)
|
|
||||||
|
|
||||||
# Parameter format
|
|
||||||
PARAM_NUMERIC = 10
|
|
||||||
PARAM_TEXT = 20
|
|
||||||
PARAM_BOOL = 30
|
|
||||||
|
|
||||||
PARAM_TYPE_CODES = {
|
|
||||||
PARAM_NUMERIC: _("Numeric"),
|
|
||||||
PARAM_TEXT: _("Text"),
|
|
||||||
PARAM_BOOL: _("Bool")
|
|
||||||
}
|
|
||||||
|
|
||||||
format = models.PositiveIntegerField(
|
|
||||||
default=PARAM_NUMERIC,
|
|
||||||
choices=PARAM_TYPE_CODES.items(),
|
|
||||||
validators=[MinValueValidator(0)])
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} ({units})".format(
|
|
||||||
name=self.name,
|
|
||||||
units=self.units)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "Parameter Template"
|
|
||||||
verbose_name_plural = "Parameter Templates"
|
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterLink(models.Model):
|
|
||||||
""" Links a PartParameterTemplate to a PartCategory
|
|
||||||
"""
|
|
||||||
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE)
|
|
||||||
template = models.ForeignKey(PartParameterTemplate, on_delete=models.CASCADE)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} - {cat}".format(
|
|
||||||
name=self.template.name,
|
|
||||||
cat=self.category)
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "Category Parameter"
|
|
||||||
verbose_name_plural = "Category Parameters"
|
|
||||||
unique_together = ('category', 'template')
|
|
||||||
|
|
||||||
|
|
||||||
class PartParameter(models.Model):
|
|
||||||
""" PartParameter is associated with a single part
|
|
||||||
"""
|
|
||||||
|
|
||||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters')
|
|
||||||
template = models.ForeignKey(PartParameterTemplate)
|
|
||||||
|
|
||||||
# Value data
|
|
||||||
value = models.CharField(max_length=50, blank=True)
|
|
||||||
min_value = models.CharField(max_length=50, blank=True)
|
|
||||||
max_value = models.CharField(max_length=50, blank=True)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return "{name} : {val}{units}".format(
|
|
||||||
name=self.template.name,
|
|
||||||
val=self.value,
|
|
||||||
units=self.template.units)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def units(self):
|
|
||||||
return self.template.units
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self):
|
|
||||||
return self.template.name
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "Part Parameter"
|
|
||||||
verbose_name_plural = "Part Parameters"
|
|
||||||
unique_together = ('part', 'template')
|
|
||||||
|
89
InvenTree/part/param_todo.py
Normal file
89
InvenTree/part/param_todo.py
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
"""
|
||||||
|
TODO - Implement part parameters, and templates
|
||||||
|
|
||||||
|
See code below
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PartParameterTemplate(models.Model):
|
||||||
|
""" A PartParameterTemplate pre-defines a parameter field,
|
||||||
|
ready to be copied for use with a given Part.
|
||||||
|
A PartParameterTemplate can be optionally associated with a PartCategory
|
||||||
|
"""
|
||||||
|
name = models.CharField(max_length=20, unique=True)
|
||||||
|
units = models.CharField(max_length=10, blank=True)
|
||||||
|
|
||||||
|
# Parameter format
|
||||||
|
PARAM_NUMERIC = 10
|
||||||
|
PARAM_TEXT = 20
|
||||||
|
PARAM_BOOL = 30
|
||||||
|
|
||||||
|
PARAM_TYPE_CODES = {
|
||||||
|
PARAM_NUMERIC: _("Numeric"),
|
||||||
|
PARAM_TEXT: _("Text"),
|
||||||
|
PARAM_BOOL: _("Bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
format = models.PositiveIntegerField(
|
||||||
|
default=PARAM_NUMERIC,
|
||||||
|
choices=PARAM_TYPE_CODES.items(),
|
||||||
|
validators=[MinValueValidator(0)])
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{name} ({units})".format(
|
||||||
|
name=self.name,
|
||||||
|
units=self.units)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Parameter Template"
|
||||||
|
verbose_name_plural = "Parameter Templates"
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryParameterLink(models.Model):
|
||||||
|
""" Links a PartParameterTemplate to a PartCategory
|
||||||
|
"""
|
||||||
|
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE)
|
||||||
|
template = models.ForeignKey(PartParameterTemplate, on_delete=models.CASCADE)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{name} - {cat}".format(
|
||||||
|
name=self.template.name,
|
||||||
|
cat=self.category)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Category Parameter"
|
||||||
|
verbose_name_plural = "Category Parameters"
|
||||||
|
unique_together = ('category', 'template')
|
||||||
|
|
||||||
|
|
||||||
|
class PartParameter(models.Model):
|
||||||
|
""" PartParameter is associated with a single part
|
||||||
|
"""
|
||||||
|
|
||||||
|
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters')
|
||||||
|
template = models.ForeignKey(PartParameterTemplate)
|
||||||
|
|
||||||
|
# Value data
|
||||||
|
value = models.CharField(max_length=50, blank=True)
|
||||||
|
min_value = models.CharField(max_length=50, blank=True)
|
||||||
|
max_value = models.CharField(max_length=50, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "{name} : {val}{units}".format(
|
||||||
|
name=self.template.name,
|
||||||
|
val=self.value,
|
||||||
|
units=self.template.units)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def units(self):
|
||||||
|
return self.template.units
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
return self.template.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "Part Parameter"
|
||||||
|
verbose_name_plural = "Part Parameters"
|
||||||
|
unique_together = ('part', 'template')
|
@ -1,11 +1,11 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from .models import Part, PartCategory, PartParameter, PartParameterTemplate
|
from .models import Part, PartCategory
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
class PartParameterSerializer(serializers.HyperlinkedModelSerializer):
|
class PartParameterSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
""" Serializer for a PartParameter
|
" Serializer for a PartParameter
|
||||||
"""
|
"
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartParameter
|
model = PartParameter
|
||||||
@ -15,7 +15,7 @@ class PartParameterSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'value',
|
'value',
|
||||||
'units')
|
'units')
|
||||||
|
"""
|
||||||
|
|
||||||
class PartSerializer(serializers.HyperlinkedModelSerializer):
|
class PartSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
""" Serializer for complete detail information of a part.
|
""" Serializer for complete detail information of a part.
|
||||||
@ -44,7 +44,7 @@ class PartCategorySerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'parent',
|
'parent',
|
||||||
'path')
|
'path')
|
||||||
|
|
||||||
|
"""
|
||||||
class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):
|
class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -53,3 +53,4 @@ class PartTemplateSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'name',
|
'name',
|
||||||
'units',
|
'units',
|
||||||
'format')
|
'format')
|
||||||
|
"""
|
@ -12,6 +12,17 @@ part_cat_urls = [
|
|||||||
url(r'^$', views.PartCategoryList.as_view())
|
url(r'^$', views.PartCategoryList.as_view())
|
||||||
]
|
]
|
||||||
|
|
||||||
|
part_urls = [
|
||||||
|
|
||||||
|
# Individual part
|
||||||
|
url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'),
|
||||||
|
|
||||||
|
# List parts with optional filters
|
||||||
|
url(r'^\?.*/?$', views.PartList.as_view()),
|
||||||
|
url(r'^$', views.PartList.as_view()),
|
||||||
|
]
|
||||||
|
|
||||||
|
"""
|
||||||
part_param_urls = [
|
part_param_urls = [
|
||||||
# Detail of a single part parameter
|
# Detail of a single part parameter
|
||||||
url(r'^(?P<pk>[0-9]+)/?$', views.PartParamDetail.as_view(), name='partparameter-detail'),
|
url(r'^(?P<pk>[0-9]+)/?$', views.PartParamDetail.as_view(), name='partparameter-detail'),
|
||||||
@ -29,13 +40,6 @@ part_param_template_urls = [
|
|||||||
url(r'^\?.*/?$', views.PartTemplateList.as_view()),
|
url(r'^\?.*/?$', views.PartTemplateList.as_view()),
|
||||||
url(r'^$', views.PartTemplateList.as_view())
|
url(r'^$', views.PartTemplateList.as_view())
|
||||||
]
|
]
|
||||||
|
"""
|
||||||
|
|
||||||
part_urls = [
|
|
||||||
|
|
||||||
# Individual part
|
|
||||||
url(r'^(?P<pk>[0-9]+)/?$', views.PartDetail.as_view(), name='part-detail'),
|
|
||||||
|
|
||||||
# List parts with optional filters
|
|
||||||
url(r'^\?.*/?$', views.PartList.as_view()),
|
|
||||||
url(r'^$', views.PartList.as_view()),
|
|
||||||
]
|
|
||||||
|
@ -3,11 +3,12 @@ from django_filters.rest_framework import FilterSet, DjangoFilterBackend
|
|||||||
from rest_framework import generics, permissions
|
from rest_framework import generics, permissions
|
||||||
|
|
||||||
from InvenTree.models import FilterChildren
|
from InvenTree.models import FilterChildren
|
||||||
from .models import PartCategory, Part, PartParameter, PartParameterTemplate
|
from .models import PartCategory, Part
|
||||||
|
|
||||||
from .serializers import PartSerializer
|
from .serializers import PartSerializer
|
||||||
from .serializers import PartCategorySerializer
|
from .serializers import PartCategorySerializer
|
||||||
from .serializers import PartParameterSerializer
|
#from .serializers import PartParameterSerializer
|
||||||
from .serializers import PartTemplateSerializer
|
#from .serializers import PartTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
@ -28,22 +29,22 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
class PartParamFilter(FilterSet):
|
class PartParamFilter(FilterSet):
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = PartParameter
|
model = PartParameter
|
||||||
fields = ['part']
|
fields = ['part']
|
||||||
|
|
||||||
|
|
||||||
class PartParamList(generics.ListCreateAPIView):
|
class PartParamList(generics.ListCreateAPIView):
|
||||||
"""
|
"
|
||||||
|
|
||||||
get:
|
get:
|
||||||
Return a list of all part parameters (with optional filters)
|
Return a list of all part parameters (with optional filters)
|
||||||
|
|
||||||
post:
|
post:
|
||||||
Create a new part parameter
|
Create a new part parameter
|
||||||
"""
|
""
|
||||||
|
|
||||||
queryset = PartParameter.objects.all()
|
queryset = PartParameter.objects.all()
|
||||||
serializer_class = PartParameterSerializer
|
serializer_class = PartParameterSerializer
|
||||||
@ -53,7 +54,7 @@ class PartParamList(generics.ListCreateAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
""
|
||||||
|
|
||||||
get:
|
get:
|
||||||
Detail view of a single PartParameter
|
Detail view of a single PartParameter
|
||||||
@ -64,12 +65,12 @@ class PartParamDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
delete:
|
delete:
|
||||||
Remove a PartParameter from the database
|
Remove a PartParameter from the database
|
||||||
|
|
||||||
"""
|
"
|
||||||
|
|
||||||
queryset = PartParameter.objects.all()
|
queryset = PartParameter.objects.all()
|
||||||
serializer_class = PartParameterSerializer
|
serializer_class = PartParameterSerializer
|
||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
"""
|
||||||
|
|
||||||
class PartFilter(FilterSet):
|
class PartFilter(FilterSet):
|
||||||
|
|
||||||
@ -138,8 +139,9 @@ class PartCategoryList(generics.ListCreateAPIView):
|
|||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
class PartTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
"""
|
""
|
||||||
|
|
||||||
get:
|
get:
|
||||||
Return detail on a single PartParameterTemplate object
|
Return detail on a single PartParameterTemplate object
|
||||||
@ -150,7 +152,7 @@ class PartTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
delete:
|
delete:
|
||||||
Remove a PartParameterTemplate object
|
Remove a PartParameterTemplate object
|
||||||
|
|
||||||
"""
|
""
|
||||||
|
|
||||||
queryset = PartParameterTemplate.objects.all()
|
queryset = PartParameterTemplate.objects.all()
|
||||||
serializer_class = PartTemplateSerializer
|
serializer_class = PartTemplateSerializer
|
||||||
@ -158,7 +160,7 @@ class PartTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
|
|
||||||
class PartTemplateList(generics.ListCreateAPIView):
|
class PartTemplateList(generics.ListCreateAPIView):
|
||||||
"""
|
""
|
||||||
|
|
||||||
get:
|
get:
|
||||||
Return a list of all PartParameterTemplate objects
|
Return a list of all PartParameterTemplate objects
|
||||||
@ -167,8 +169,10 @@ class PartTemplateList(generics.ListCreateAPIView):
|
|||||||
post:
|
post:
|
||||||
Create a new PartParameterTemplate object
|
Create a new PartParameterTemplate object
|
||||||
|
|
||||||
"""
|
""
|
||||||
|
|
||||||
queryset = PartParameterTemplate.objects.all()
|
queryset = PartParameterTemplate.objects.all()
|
||||||
serializer_class = PartTemplateSerializer
|
serializer_class = PartTemplateSerializer
|
||||||
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
|
||||||
|
|
||||||
|
"""
|
||||||
|
@ -14,6 +14,8 @@ from datetime import datetime
|
|||||||
|
|
||||||
class StockLocation(InvenTreeTree):
|
class StockLocation(InvenTreeTree):
|
||||||
""" Organization tree for StockItem objects
|
""" Organization tree for StockItem objects
|
||||||
|
A "StockLocation" can be considered a warehouse, or storage location
|
||||||
|
Stock locations can be heirarchical as required
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -36,33 +38,27 @@ class StockItem(models.Model):
|
|||||||
review_needed = models.BooleanField(default=False)
|
review_needed = models.BooleanField(default=False)
|
||||||
|
|
||||||
# Stock status types
|
# Stock status types
|
||||||
ITEM_IN_STOCK = 10
|
ITEM_OK = 10
|
||||||
ITEM_INCOMING = 15
|
|
||||||
ITEM_IN_PROGRESS = 20
|
|
||||||
ITEM_COMPLETE = 25
|
|
||||||
ITEM_ATTENTION = 50
|
ITEM_ATTENTION = 50
|
||||||
ITEM_DAMAGED = 55
|
ITEM_DAMAGED = 55
|
||||||
ITEM_DESTROYED = 60
|
ITEM_DESTROYED = 60
|
||||||
|
|
||||||
ITEM_STATUS_CODES = {
|
ITEM_STATUS_CODES = {
|
||||||
ITEM_IN_STOCK: _("In stock"),
|
ITEM_OK: _("OK"),
|
||||||
ITEM_INCOMING: _("Incoming"),
|
|
||||||
ITEM_IN_PROGRESS: _("In progress"),
|
|
||||||
ITEM_COMPLETE: _("Complete"),
|
|
||||||
ITEM_ATTENTION: _("Attention needed"),
|
ITEM_ATTENTION: _("Attention needed"),
|
||||||
ITEM_DAMAGED: _("Damaged"),
|
ITEM_DAMAGED: _("Damaged"),
|
||||||
ITEM_DESTROYED: _("Destroyed")
|
ITEM_DESTROYED: _("Destroyed")
|
||||||
}
|
}
|
||||||
|
|
||||||
status = models.PositiveIntegerField(
|
status = models.PositiveIntegerField(
|
||||||
default=ITEM_IN_STOCK,
|
default=ITEM_OK,
|
||||||
choices=ITEM_STATUS_CODES.items(),
|
choices=ITEM_STATUS_CODES.items(),
|
||||||
validators=[MinValueValidator(0)])
|
validators=[MinValueValidator(0)])
|
||||||
|
|
||||||
notes = models.CharField(max_length=100, blank=True)
|
notes = models.CharField(max_length=100, blank=True)
|
||||||
|
|
||||||
# If stock item is incoming, an (optional) ETA field
|
# If stock item is incoming, an (optional) ETA field
|
||||||
expected_arrival = models.DateField(null=True, blank=True)
|
# expected_arrival = models.DateField(null=True, blank=True)
|
||||||
|
|
||||||
infinite = models.BooleanField(default=False)
|
infinite = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
@ -35,7 +35,11 @@ class SupplierPart(models.Model):
|
|||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('part', 'supplier', 'SKU')
|
unique_together = ('part', 'supplier', 'SKU')
|
||||||
|
|
||||||
part = models.ForeignKey(Part, null=True, blank=True, on_delete=models.CASCADE)
|
# Link to an actual part
|
||||||
|
# The part will have a field 'supplier_parts' which links to the supplier part options
|
||||||
|
part = models.ForeignKey(Part, null=True, blank=True, on_delete=models.CASCADE,
|
||||||
|
related_name='supplier_parts')
|
||||||
|
|
||||||
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
|
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE)
|
||||||
SKU = models.CharField(max_length=100)
|
SKU = models.CharField(max_length=100)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user