2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 20:16:44 +00:00
This commit is contained in:
James Newlands 2018-04-15 20:24:55 +10:00
commit 24f3e8579c
20 changed files with 291 additions and 99 deletions

View File

@ -26,8 +26,6 @@ from track.urls import tracking_urls
from users.urls import user_urls from users.urls import user_urls
from . import views
admin.site.site_header = "InvenTree Admin" admin.site.site_header = "InvenTree Admin"
apipatterns = [ apipatterns = [

View File

@ -4,7 +4,7 @@
{% include 'part/tabs.html' with tab='bom' %} {% include 'part/tabs.html' with tab='bom' %}
<table> <table class="table table-striped">
<tr> <tr>
<th>Part</th> <th>Part</th>
<th>Description</th> <th>Description</th>

View File

@ -8,7 +8,7 @@
Total in stock: {{ part.stock }} Total in stock: {{ part.stock }}
<br> <br>
<table> <table class="table table-striped">
<tr> <tr>
<th>Quantity</th> <th>Quantity</th>
<th>Location</th> <th>Location</th>

View File

@ -5,7 +5,7 @@
{% include 'part/tabs.html' with tab='suppliers' %} {% include 'part/tabs.html' with tab='suppliers' %}
{% if part.supplier_parts.all|length > 0 %} {% if part.supplier_parts.all|length > 0 %}
<table> <table class="table table-striped">
<tr> <tr>
<th>Supplier</th> <th>Supplier</th>
<th>SKU</th> <th>SKU</th>
@ -15,7 +15,11 @@
<tr> <tr>
<td><a href="{% url 'supplier-detail' spart.supplier.id %}">{{ spart.supplier.name }}</a></td> <td><a href="{% url 'supplier-detail' spart.supplier.id %}">{{ spart.supplier.name }}</a></td>
<td><a href="{% url 'supplier-part-detail' spart.id %}">{{ spart.SKU }}</a></td> <td><a href="{% url 'supplier-part-detail' spart.id %}">{{ spart.SKU }}</a></td>
<td>{{ spart.URL }}</td> <td>
{% if spart.URL %}
<a href="{{ spart.URL }}">{{ spart.URL }}</a>
{% endif %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</table> </table>

View File

@ -1,6 +1,9 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li{% ifequal tab 'detail' %} class="active"{% endifequal %}><a href="{% url 'part-detail' part.id %}">Details</a></li> <li{% ifequal tab 'detail' %} class="active"{% endifequal %}><a href="{% url 'part-detail' part.id %}">Details</a></li>
<li{% ifequal tab 'bom' %} class="active"{% endifequal %}><a href="{% url 'part-bom' part.id %}">BOM <span class="badge">{{ part.bomItemCount }}</span></a></li> <li{% ifequal tab 'bom' %} class="active"{% endifequal %}><a href="{% url 'part-bom' part.id %}">BOM <span class="badge">{{ part.bomItemCount }}</span></a></li>
{% if part.bomItemCount > 0 %}
<li{% ifequal tab 'build' %} class "active"{% endifequal %}><a href="#">Build</a></li>
{% endif %}
{% if part.usedInCount > 0 %} {% if part.usedInCount > 0 %}
<li{% ifequal tab 'used' %} class="active"{% endifequal %}><a href="{% url 'part-used-in' part.id %}">Used In <span class="badge">{{ part.usedInCount }}</span></a></li> <li{% ifequal tab 'used' %} class="active"{% endifequal %}><a href="{% url 'part-used-in' part.id %}">Used In <span class="badge">{{ part.usedInCount }}</span></a></li>
{% endif %} {% endif %}

View File

@ -6,7 +6,7 @@
Part tracking for {{ part.name }} Part tracking for {{ part.name }}
<table> <table class="table table-striped">
<tr> <tr>
<th>Serial</th> <th>Serial</th>
<th>Status</th> <th>Status</th>

View File

@ -6,7 +6,7 @@
This part is used to make the following parts: This part is used to make the following parts:
<table> <table class="table table-striped">
<tr> <tr>
<th>Part</th> <th>Part</th>
<th>Description</th> <th>Description</th>

View File

@ -20,7 +20,7 @@ class PartIndex(ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super(PartIndex, self).get_context_data(**kwargs) context = super(PartIndex, self).get_context_data(**kwargs).copy()
# View top-level categories # View top-level categories
children = PartCategory.objects.filter(parent=None) children = PartCategory.objects.filter(parent=None)

51
InvenTree/stock/forms.py Normal file
View File

@ -0,0 +1,51 @@
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from .models import StockLocation, StockItem
class EditStockLocationForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditStockLocationForm, 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.form_action = 'submit'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = StockLocation
fields = [
'name',
'parent',
'description'
]
class EditStockItemForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditStockItemForm, 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.form_action = 'submit'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = StockItem
fields = [
'part',
'supplier_part',
'location',
'quantity',
]

View File

@ -1,21 +0,0 @@
{% extends "base.html" %}
{% block content %}
{% include "stock/loc_link.html" with location=item.location %}
<ul>
<li>Part: <a href="{% url 'part-detail' item.part.id %}">{{ item.part.name }}</a></li>
{% if item.supplier_part %}
<li>Supplier Part: <a href="{% url 'supplier-part-detail' item.supplier_part.id %}">{{ item.supplier_part.supplier.name }} | {{ item.supplier_part.SKU }}</a></li>
{% endif %}
<li>Quantity: {{ item.quantity }}</li>
{% if item.stocktake_date %}
<li>Stocktake Date: {{ item.stocktake_date }}</li>
<li>Checked by: {{ item.stocktake_user }}</li>
{% endif %}
<li>Status: {{ item.status }}</li>
<li>Notes: {{ item.notes }}</li>
</ul>
{% endblock %}

View File

@ -2,38 +2,14 @@
{% block content %} {% block content %}
{% include "stock/loc_link.html" with location=location %} {% include "stock/loc_link.html" with location=None %}
{% if children|length > 0 %} {% if locations.all|length > 0 %}
Storage locations: {% include "stock/location_list.html" with locations=locations %}
<ul class="list-group">
{% for child in children %}
<li class="list-group-item"><a href="/stock/list/?location={{ child.id }}">{{ child.name }}</a></li>
{% endfor %}
</ul>
{% endif %} {% endif %}
{% if items|length > 0 %} {% if items|length > 0 %}
Stock items: {% include "stock/stock_table.html" with items=items %}
<table>
<tr>
<th>Item</th>
<th>Part</th>
<th>Stock</th>
<th>Stocktake</th>
<th>Notes</th>
</tr>
{% for item in items %}
<tr>
<td><a href="{% url 'stock-detail' item.id %}">Click here</a></td>
<td><a href="{% url 'part-detail' item.part.id %}">{{ item.part.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.stocktake_date }}</td>
<td>{{ item.notes }}</td>
</tr>
{% endfor %}
</table>
{% else %}
There are no parts in this location.
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "base.html" %}
{% block content %}
{% include "stock/loc_link.html" with location=item.location %}
<table class="table table-striped">
<tr>
<td>Part</td>
<td><a href="{% url 'part-detail' item.part.id %}">{{ item.part.name }}</td>
</tr>
<tr>
<td>Location</td>
<td><a href="{% url 'stock-location-detail' item.location.id %}">{{ item.location.name }}</a></td>
</tr>
<tr>
<td>Quantity</td>
<td>{{ item.quantity }}</td>
</tr>
{% if item.supplier_part %}
<tr>
<td>Supplier Part</td>
<td><a href="{% url 'supplier-part-detail' item.supplier_part.id %}">{{ item.supplier_part.SKU }}</a></td>
</tr>
{% endif %}
<tr>
<td>Updated</td>
<td>{{ item.updated }}</td>
</tr>
{% if item.stocktake_date %}
<tr>
<td>Stocktake</td>
<td>{{ item.stocktake_date }} <span class='badge'>{{ item.stocktake_user }}</span></td>
</tr>
{% endif %}
<tr>
<td>Status</td>
<td>{{ item.status }}</td>
</tr>
{% if item.notes %}
<tr>
<td>Notes</td>
<td>{{ item.notes }}</td>
</tr>
{% endif %}
</table>
{% endblock %}

View File

@ -1,12 +1,12 @@
<div class="navigation"> <div class="navigation">
<nav aria-label="breadcrumb"> <nav aria-label="breadcrumb">
<ol class="breadcrumb"> <ol class="breadcrumb">
<li class="breadcrumb-item{% if location is None %} active" aria-current="page{% endif %}"><a href="/stock/list/">Parts</a></li> <li class="breadcrumb-item{% if location is None %} active" aria-current="page{% endif %}"><a href="/stock/">Parts</a></li>
{% if location %} {% if location %}
{% for path_item in location.parentpath %} {% for path_item in location.parentpath %}
<li class='breadcrumb-item'><a href="/stock/list/?location={{ path_item.id }}">{{ path_item.name }}</a></li> <li class='breadcrumb-item'><a href="{% url 'stock-location-detail' path_item.id %}">{{ path_item.name }}</a></li>
{% endfor %} {% endfor %}
<li class='breadcrumb-item active' aria-current='page'><a href="/stock/list/?location={{ location.id }}">{{ location.name }}</a></li> <li class='breadcrumb-item active' aria-current='page'><a href="{% url 'stock-location-detail' location.id %}">{{ location.name }}</a></li>
{% endif %} {% endif %}
</ol> </ol>
</nav> </nav>

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block content %}
{% include "stock/loc_link.html" with location=location %}
<p>
<b>{{ location.name }}</b><br>
<i>{{ location.description }}</i>
</p>
{% include "stock/location_list.html" with locations=location.children %}
{% include "stock/stock_table.html" with items=location.items %}
{% endblock %}

View File

@ -0,0 +1,6 @@
Storage locations:
<ul class="list-group">
{% for child in locations.all %}
<li class="list-group-item"><a href="{% url 'stock-location-detail' child.id %}">{{ child.name }}</a></li>
{% endfor %}
</ul>

View File

@ -0,0 +1,18 @@
<table class="table table-striped">
<tr>
<th>Part</th>
<th>Stock</th>
<th>Status</th>
<th>Stocktake</th>
<th></th>
</tr>
{% for item in items.all %}
<tr>
<td><a href="{% url 'part-detail' item.part.id %}">{{ item.part.name }}</a></td>
<td>{{ item.quantity }}</td>
<td>{{ item.status }}</td>
<td>{{ item.stocktake_date }}</td>
<td><a href="{% url 'stock-item-detail' item.id %}">Click</a></td>
</tr>
{% endfor %}
</table>

View File

@ -31,15 +31,31 @@ stock_api_loc_urls = [
# URL list for web interface # URL list for web interface
stock_detail_urls = [ stock_location_detail_urls = [
url('', views.detail, name='stock-detail'), url(r'^edit/?', views.StockLocationEdit.as_view(), name='stock-location-edit'),
url(r'^delete/?', views.StockLocationDelete.as_view(), name='stock-location-delete'),
# Anything else
url('^.*$', views.StockLocationDetail.as_view(), name='stock-location-detail'),
] ]
stock_item_detail_urls = [
url(r'^edit/?', views.StockItemEdit.as_view(), name='stock-item-edit'),
url(r'^delete/?', views.StockItemDelete.as_view(), name='stock-item-delete'),
url('^.*$', views.StockItemDetail.as_view(), name='stock-item-detail'),
]
stock_urls = [ stock_urls = [
# Stock location
url(r'^location/(?P<pk>\d+)/', include(stock_location_detail_urls)),
url(r'^location/new/', views.StockLocationCreate.as_view(), name='stock-location-create'),
# Individual stock items # Individual stock items
url(r'^(?P<pk>\d+)/', include(stock_detail_urls)), url(r'^item/(?P<pk>\d+)/', include(stock_item_detail_urls)),
url('list', views.index, name='stock-index'), url(r'^item/new/', views.StockItemCreate.as_view(), name='stock-item-create'),
# Redirect any other patterns url(r'^.*$', views.StockIndex.as_view(), name='stock-index'),
url(r'^.*$', RedirectView.as_view(url='list', permanent=False), name='stock-index'),
] ]

View File

@ -1,43 +1,95 @@
from django.http import HttpResponse
from django.template import loader
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.views.generic import DetailView, ListView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from .models import StockItem, StockLocation from .models import StockItem, StockLocation
def index(request): from .forms import EditStockLocationForm
template = loader.get_template('stock/index.html') from .forms import EditStockItemForm
items = StockItem.objects.all() class StockIndex(ListView):
model = StockItem
template_name = 'stock/index.html'
context_obect_name = 'items'
paginate_by = 50
location = None def get_queryset(self):
return StockItem.objects.filter(location=None)
if 'location' in request.GET: def get_context_data(self, **kwargs):
loc_id = request.GET['location'] context = super(StockIndex, self).get_context_data(**kwargs).copy()
location = get_object_or_404(StockLocation, pk=loc_id) locations = StockLocation.objects.filter(parent=None)
items = items.filter(location = loc_id) context['locations'] = locations
children = StockLocation.objects.filter(parent = loc_id) return context
class StockLocationDetail(DetailView):
context_object_name = 'location'
template_name = 'stock/location.html'
queryset = StockLocation.objects.all()
model = StockLocation
class StockItemDetail(DetailView):
context_object_name = 'item'
template_name = 'stock/item.html'
queryset = StockItem.objects.all()
model = StockItem
class StockLocationEdit(UpdateView):
model = StockLocation
form_class = EditStockLocationForm
template_name = '/stock/location-edit.html'
context_object_name = 'location'
class StockItemEdit(UpdateView):
model = StockItem
form_class = EditStockItemForm
template_name = '/stock/item-edit.html'
context_object_name = 'item'
class StockLocationCreate(CreateView):
model = StockLocation
form_class = EditStockLocationForm
template_name = '/stock/location-create.html'
context_object_name = 'location'
class StockItemCreate(CreateView):
model = StockItem
form_class = EditStockItemForm
template_name = '/stock/item-create.html'
context_object_name = 'item'
class StockLocationDelete(DeleteView):
model = StockLocation
success_url = '/stock/'
template_name = '/stock/location-delete.html'
def post(self, request, *args, **kwargs):
if 'confirm' in request.POST:
return super(StockLocationDelete, self).post(request, *args, **kwargs)
else: else:
# No stock items can exist without a location return HttpResponseRedirect(self.get_object().get_absolute_url())
items = None
location = None
children = StockLocation.objects.filter(parent__isnull=True)
context = {
'items' : items,
'location' : location,
'children' : children,
}
return HttpResponse(template.render(context, request))
def detail(request, pk): class StockItemDelete(DeleteView):
model = StockLocation
success_url = '/stock/'
template_name = '/stock/item-delete.html'
stock = get_object_or_404(StockItem, pk=pk) def post(self, request, *args, **kwargs):
if 'confirm' in request.POST:
return render(request, 'stock/detail.html', {'item' : stock}) return super(StockItemDelete, self).post(request, *args, **kwargs)
else:
return HttpResponseRedirect(self.get_object().get_absolute_url())

View File

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 10:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0005_auto_20180415_0255'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='manufacturer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.Manufacturer'),
),
migrations.AlterField(
model_name='supplierpart',
name='part',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_parts', to='part.Part'),
),
]

View File

@ -16,11 +16,9 @@ test:
migrate: migrate:
python InvenTree/manage.py makemigrations part python InvenTree/manage.py makemigrations part
python InvenTree/manage.py makemigrations bom
# python InvenTree/manage.py makemigrations project
python InvenTree/manage.py makemigrations stock python InvenTree/manage.py makemigrations stock
python InvenTree/manage.py makemigrations supplier python InvenTree/manage.py makemigrations supplier
# python InvenTree/manage.py makemigrations track python InvenTree/manage.py makemigrations track
python InvenTree/manage.py migrate --run-syncdb python InvenTree/manage.py migrate --run-syncdb
python InvenTree/manage.py check python InvenTree/manage.py check