mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
UI improvements
This commit is contained in:
parent
b6b4189c49
commit
a67d5b58db
62
InvenTree/part/migrations/0019_auto_20180416_1249.py
Normal file
62
InvenTree/part/migrations/0019_auto_20180416_1249.py
Normal file
@ -0,0 +1,62 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2018-04-16 12:49
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0018_part_buildable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='IPN',
|
||||
field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='URL',
|
||||
field=models.URLField(blank=True, help_text='Link to extenal URL'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='buildable',
|
||||
field=models.BooleanField(default=False, help_text='Can this part be built from other parts?'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='category',
|
||||
field=models.ForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='description',
|
||||
field=models.CharField(help_text='Part description', max_length=250),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='minimum_stock',
|
||||
field=models.PositiveIntegerField(default=0, help_text='Minimum allowed stock level', validators=[django.core.validators.MinValueValidator(0)]),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='name',
|
||||
field=models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='purchaseable',
|
||||
field=models.BooleanField(default=True, help_text='Can this part be purchased from external suppliers?'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='part',
|
||||
name='trackable',
|
||||
field=models.BooleanField(default=False, help_text='Does this part have tracking for unique items?'),
|
||||
),
|
||||
]
|
@ -84,43 +84,44 @@ class Part(models.Model):
|
||||
return '/part/{id}/'.format(id=self.id)
|
||||
|
||||
# Short name of the part
|
||||
name = models.CharField(max_length=100, unique=True)
|
||||
name = models.CharField(max_length=100, unique=True, help_text='Part name (must be unique)')
|
||||
|
||||
# Longer description of the part (optional)
|
||||
description = models.CharField(max_length=250)
|
||||
description = models.CharField(max_length=250, help_text='Part description')
|
||||
|
||||
# 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)
|
||||
IPN = models.CharField(max_length=100, blank=True, help_text='Internal Part Number')
|
||||
|
||||
# Provide a URL for an external link
|
||||
URL = models.URLField(blank=True)
|
||||
URL = models.URLField(blank=True, help_text='Link to extenal URL')
|
||||
|
||||
# Part category - all parts must be assigned to a category
|
||||
category = models.ForeignKey(PartCategory, related_name='parts',
|
||||
null=True, blank=True,
|
||||
on_delete=models.DO_NOTHING)
|
||||
on_delete=models.DO_NOTHING,
|
||||
help_text='Part category')
|
||||
|
||||
image = models.ImageField(upload_to=rename_part_image, max_length=255, null=True, blank=True)
|
||||
|
||||
# Minimum "allowed" stock level
|
||||
minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)])
|
||||
minimum_stock = models.PositiveIntegerField(default=0, validators=[MinValueValidator(0)], help_text='Minimum allowed stock level')
|
||||
|
||||
# Units of quantity for this part. Default is "pcs"
|
||||
units = models.CharField(max_length=20, default="pcs", blank=True)
|
||||
|
||||
# Can this part be built?
|
||||
buildable = models.BooleanField(default=False)
|
||||
buildable = models.BooleanField(default=False, help_text='Can this part be built from other parts?')
|
||||
|
||||
# Is this part "trackable"?
|
||||
# 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)
|
||||
trackable = models.BooleanField(default=False, help_text='Does this part have tracking for unique items?')
|
||||
|
||||
# Is this part "purchaseable"?
|
||||
purchaseable = models.BooleanField(default=True)
|
||||
purchaseable = models.BooleanField(default=True, help_text='Can this part be purchased from external suppliers?')
|
||||
|
||||
def __str__(self):
|
||||
if self.IPN:
|
||||
|
@ -12,7 +12,6 @@ Deletion title goes here
|
||||
<p><b>This is a permanent action and cannot be undone.</b></p>
|
||||
|
||||
{% block del_body %}
|
||||
Deletion body goes here
|
||||
{% endblock %}
|
||||
|
||||
<form action="" method="post">{% csrf_token %}
|
||||
|
@ -4,18 +4,22 @@
|
||||
|
||||
{% include 'part/tabs.html' with tab='bom' %}
|
||||
|
||||
<h3>Bill of Materials</h3>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Part</th>
|
||||
<th>Description</th>
|
||||
<th>Quantity</th>
|
||||
<th>Edit</th>
|
||||
</tr>
|
||||
{% for bom_item in part.bom_items.all %}
|
||||
{% with sub_part=bom_item.sub_part %}
|
||||
<tr>
|
||||
<td><a href="{% url 'part-detail' sub_part.id %}">{{ sub_part.name }}</a></td>
|
||||
<td>{{ sub_part.description }}</td>
|
||||
<td>{{ bom_item.quantity }}<span class='badge'>{{ bom_item.sub_part.available_stock }}</span></td>
|
||||
<td>{{ bom_item.quantity }}</span></td>
|
||||
<td><a href="{% url 'bom-item-detail' bom_item.id %}">Edit</a></td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
{% endfor %}
|
||||
|
13
InvenTree/part/templates/part/build.html
Normal file
13
InvenTree/part/templates/part/build.html
Normal file
@ -0,0 +1,13 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
|
||||
{% block details %}
|
||||
|
||||
{% include 'part/tabs.html' with tab='build' %}
|
||||
|
||||
<h3>Build Part</h3>
|
||||
|
||||
TODO
|
||||
<br><br>
|
||||
You can build {{ part.can_build }} of this part with current stock.
|
||||
|
||||
{% endblock %}
|
@ -4,9 +4,11 @@
|
||||
|
||||
{% include 'part/tabs.html' with tab='detail' %}
|
||||
|
||||
<h3>Part Details</h3>
|
||||
|
||||
<table class='table table-striped'>
|
||||
<tr>
|
||||
<td>Name</td>
|
||||
<td>Part name</td>
|
||||
<td>{{ part.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@ -37,6 +39,12 @@
|
||||
<td>Purchaseable</td>
|
||||
<td>{{ part.purchaseable }}</td>
|
||||
</tr>
|
||||
{% if part.minimum_stock > 0 %}
|
||||
<tr>
|
||||
<td>Minimum Stock</td>
|
||||
<td>{{ part.minimum_stock }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
</table>
|
||||
|
||||
<div class='container-fluid'>
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
{% include 'part/tabs.html' with tab='stock' %}
|
||||
|
||||
<h3>Part Stock</h3>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Link</th>
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
{% include 'part/tabs.html' with tab='suppliers' %}
|
||||
|
||||
<h3>Part Suppliers</h3>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>SKU</th>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<li{% ifequal tab 'detail' %} class="active"{% endifequal %}><a href="{% url 'part-detail' part.id %}">Details</a></li>
|
||||
{% if part.buildable %}
|
||||
<li{% ifequal tab 'bom' %} class="active"{% endifequal %}><a href="{% url 'part-bom' part.id %}">BOM<span class="badge">{{ part.bom_count }}</span></a></li>
|
||||
<li{% ifequal tab 'build' %} class "active"{% endifequal %}><a href="#">Build<span class='badge'>{{ part.can_build }}</span></a></li>
|
||||
<li{% ifequal tab 'build' %} class="active"{% endifequal %}><a href="{% url 'part-build' part.id %}">Build<span class='badge'>{{ part.can_build }}</span></a></li>
|
||||
{% endif %}
|
||||
{% if part.used_in_count > 0 %}
|
||||
<li{% ifequal tab 'used' %} class="active"{% endifequal %}><a href="{% url 'part-used-in' part.id %}">Used In{% if part.used_in_count > 0 %}<span class="badge">{{ part.used_in_count }}</span>{% endif %}</a></li>
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
{% include 'part/tabs.html' with tab='used' %}
|
||||
|
||||
<h3>Used In</h3>
|
||||
|
||||
<table class="table table-striped">
|
||||
<tr>
|
||||
<th>Part</th>
|
||||
|
@ -41,6 +41,7 @@ part_detail_urls = [
|
||||
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'^build/?', views.PartDetail.as_view(template_name='part/build.html'), name='part-build'),
|
||||
url(r'^stock/?', views.PartDetail.as_view(template_name='part/stock.html'), name='part-stock'),
|
||||
url(r'^used/?', views.PartDetail.as_view(template_name='part/used_in.html'), name='part-used-in'),
|
||||
url(r'^suppliers/?', views.PartDetail.as_view(template_name='part/supplier.html'), name='part-suppliers'),
|
||||
|
46
InvenTree/stock/migrations/0009_auto_20180416_1253.py
Normal file
46
InvenTree/stock/migrations/0009_auto_20180416_1253.py
Normal file
@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2018-04-16 12:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0019_auto_20180416_1249'),
|
||||
('stock', '0008_stockitem_url'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='stockitem',
|
||||
name='batch',
|
||||
field=models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitem',
|
||||
name='belongs_to',
|
||||
field=models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitem',
|
||||
name='customer',
|
||||
field=models.ForeignKey(blank=True, help_text='Item assigned to customer?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='supplier.Customer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitem',
|
||||
name='location',
|
||||
field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='items', to='stock.StockLocation'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='stockitem',
|
||||
name='serial',
|
||||
field=models.PositiveIntegerField(blank=True, help_text='Serial number for this item', null=True),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name='stockitem',
|
||||
unique_together=set([('part', 'serial')]),
|
||||
),
|
||||
]
|
@ -60,6 +60,12 @@ class StockItem(models.Model):
|
||||
def get_absolute_url(self):
|
||||
return '/stock/item/{id}/'.format(id=self.id)
|
||||
|
||||
|
||||
class Meta:
|
||||
unique_together = [
|
||||
('part', 'serial'),
|
||||
]
|
||||
|
||||
# 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')
|
||||
|
||||
@ -68,23 +74,29 @@ class StockItem(models.Model):
|
||||
|
||||
# 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)
|
||||
related_name='items', blank=True, null=True,
|
||||
help_text='Where is this stock item located?')
|
||||
|
||||
# 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)
|
||||
related_name='owned_parts', blank=True, null=True,
|
||||
help_text='Is this item installed in another item?')
|
||||
|
||||
# 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)
|
||||
customer = models.ForeignKey(Customer, on_delete=models.SET_NULL,
|
||||
related_name='stockitems', blank=True, null=True,
|
||||
help_text='Item assigned to customer?')
|
||||
|
||||
# Optional serial number
|
||||
serial = models.PositiveIntegerField(blank=True, null=True)
|
||||
serial = models.PositiveIntegerField(blank=True, null=True,
|
||||
help_text='Serial number for this item')
|
||||
|
||||
# Optional URL to link to external resource
|
||||
URL = models.URLField(max_length=125, blank=True)
|
||||
|
||||
# Optional batch information
|
||||
batch = models.CharField(max_length=100, blank=True)
|
||||
batch = models.CharField(max_length=100, blank=True,
|
||||
help_text='Batch code for this stock item')
|
||||
|
||||
# Quantity of this stock item. Value may be overridden by other settings
|
||||
quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
|
||||
|
36
InvenTree/supplier/migrations/0007_auto_20180416_1253.py
Normal file
36
InvenTree/supplier/migrations/0007_auto_20180416_1253.py
Normal file
@ -0,0 +1,36 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11 on 2018-04-16 12:53
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('supplier', '0006_auto_20180415_1011'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
name='MPN',
|
||||
field=models.CharField(blank=True, help_text='Manufacturer part number', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
name='SKU',
|
||||
field=models.CharField(help_text='Supplier stock keeping unit', max_length=100),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
name='manufacturer',
|
||||
field=models.ForeignKey(blank=True, help_text='Manufacturer', null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.Manufacturer'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='supplierpart',
|
||||
name='part',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'),
|
||||
),
|
||||
]
|
@ -42,18 +42,20 @@ class SupplierPart(models.Model):
|
||||
|
||||
# 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.SET_NULL,
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE,
|
||||
related_name='supplier_parts')
|
||||
|
||||
supplier = models.ForeignKey(Supplier, on_delete=models.CASCADE,
|
||||
related_name='parts')
|
||||
|
||||
SKU = models.CharField(max_length=100)
|
||||
SKU = models.CharField(max_length=100, help_text='Supplier stock keeping unit')
|
||||
|
||||
manufacturer = models.ForeignKey(Manufacturer, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
MPN = models.CharField(max_length=100, blank=True)
|
||||
manufacturer = models.ForeignKey(Manufacturer, blank=True, null=True, on_delete=models.SET_NULL, help_text='Manufacturer')
|
||||
|
||||
MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number')
|
||||
|
||||
URL = models.URLField(blank=True)
|
||||
|
||||
description = models.CharField(max_length=250, blank=True)
|
||||
|
||||
# Default price for a single unit
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<div class='container-fluid'>
|
||||
<a href="{% url 'supplier-part-edit' part.id %}">
|
||||
<button class="btn btn-info">Edit supplier details</button>
|
||||
<button class="btn btn-info">Edit supplier part</button>
|
||||
</a>
|
||||
<a href="{% url 'supplier-part-delete' part.id %}">
|
||||
<button class="btn btn-danger">Delete supplier part</button>
|
||||
|
Loading…
Reference in New Issue
Block a user