2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-07 07:48:50 +00:00

Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2020-02-12 12:56:17 +11:00
commit 0498fd633a
37 changed files with 2146 additions and 874 deletions

View File

@ -1,5 +1,6 @@
function updateAllocationTotal(id, count, required) { function updateAllocationTotal(id, count, required) {
count = parseFloat(count);
$('#allocation-total-'+id).html(count); $('#allocation-total-'+id).html(count);
@ -27,21 +28,24 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
field: 'stock_item_detail', field: 'stock_item_detail',
title: 'Stock Item', title: 'Stock Item',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return '' + value.quantity + ' x ' + value.part_name + ' @ ' + value.location_name; return '' + parseFloat(value.quantity) + ' x ' + value.part_name + ' @ ' + value.location_name;
} }
}, },
{ {
field: 'stock_item_detail.quantity', field: 'stock_item_detail.quantity',
title: 'Available', title: 'Available',
formatter: function(value, row, index, field) {
return parseFloat(value);
}
}, },
{ {
field: 'quantity', field: 'quantity',
title: 'Allocated', title: 'Allocated',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var html = value; var html = parseFloat(value);
var bEdit = "<button class='btn btn-primary item-edit-button btn-sm' type='button' title='Edit stock allocation' url='/build/item/" + row.pk + "/edit/'><span class='glyphicon glyphicon-small glyphicon-edit'></span></button>"; var bEdit = "<button class='btn item-edit-button btn-sm' type='button' title='Edit stock allocation' url='/build/item/" + row.pk + "/edit/'><span class='glyphicon glyphicon-small glyphicon-edit'></span></button>";
var bDel = "<button class='btn btn-danger item-del-button btn-sm' type='button' title='Delete stock allocation' url='/build/item/" + row.pk + "/delete/'><span class='glyphicon glyphicon-small glyphicon-trash'></span></button>"; var bDel = "<button class='btn item-del-button btn-sm' type='button' title='Delete stock allocation' url='/build/item/" + row.pk + "/delete/'><span class='glyphicon glyphicon-small glyphicon-trash'></span></button>";
html += "<div class='btn-group' style='float: right;'>" + bEdit + bDel + "</div>"; html += "<div class='btn-group' style='float: right;'>" + bEdit + bDel + "</div>";

View File

@ -385,6 +385,9 @@ function loadStockTrackingTable(table, options) {
cols.push({ cols.push({
field: 'quantity', field: 'quantity',
title: 'Quantity', title: 'Quantity',
formatter: function(value, row, index, field) {
return parseFloat(value);
},
}); });
cols.push({ cols.push({

View File

@ -20,6 +20,7 @@ from markdownx.models import MarkdownxField
from InvenTree.status_codes import BuildStatus from InvenTree.status_codes import BuildStatus
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField
from InvenTree.helpers import decimal2string
from stock.models import StockItem from stock.models import StockItem
from part.models import Part, BomItem from part.models import Part, BomItem
@ -42,7 +43,7 @@ class Build(models.Model):
""" """
def __str__(self): def __str__(self):
return "Build {q} x {part}".format(q=self.quantity, part=str(self.part)) return "Build {q} x {part}".format(q=decimal2string(self.quantity), part=str(self.part))
def get_absolute_url(self): def get_absolute_url(self):
return reverse('build-detail', kwargs={'pk': self.id}) return reverse('build-detail', kwargs={'pk': self.id})

View File

@ -15,7 +15,7 @@
{% block collapse_heading %} {% block collapse_heading %}
<div class='col-sm-2'> <div class='col-sm-2'>
<b>{{ item.sub_part.total_stock }}</b> <b>{% decimal item.sub_part.total_stock %}</b>
</div> </div>
<div class='col-sm-2'> <div class='col-sm-2'>
<b>{% multiply build.quantity item.quantity %}</b> <b>{% multiply build.quantity item.quantity %}</b>

View File

@ -1,7 +1,9 @@
{% extends "modal_delete_form.html" %} {% extends "modal_delete_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block pre_form_content %} {% block pre_form_content %}
Are you sure you want to unallocate these parts? {% trans "Are you sure you want to unallocate these parts?" %}
<br> <br>
This will remove {{ item.quantity }} parts from build '{{ item.build.title }}'. This will remove {% decimal item.quantity %} parts from build '{{ item.build.title }}'.
{% endblock %} {% endblock %}

View File

@ -1,9 +1,10 @@
{% extends "modal_form.html" %} {% extends "modal_form.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block pre_form_content %} {% block pre_form_content %}
{{ block.super }} {{ block.super }}
Are you sure you wish to unallocate all stock for this build? {% trans "Are you sure you wish to unallocate all stock for this build?" %}
{% endblock %} {% endblock %}

View File

@ -53,7 +53,7 @@ class BuildCancel(AjaxUpdateView):
model = Build model = Build
ajax_template_name = 'build/cancel.html' ajax_template_name = 'build/cancel.html'
ajax_form_title = 'Cancel Build' ajax_form_title = _('Cancel Build')
context_object_name = 'build' context_object_name = 'build'
form_class = forms.CancelBuildForm form_class = forms.CancelBuildForm
@ -71,12 +71,12 @@ class BuildCancel(AjaxUpdateView):
if confirm: if confirm:
build.cancelBuild(request.user) build.cancelBuild(request.user)
else: else:
form.errors['confirm_cancel'] = ['Confirm build cancellation'] form.errors['confirm_cancel'] = [_('Confirm build cancellation')]
valid = False valid = False
data = { data = {
'form_valid': valid, 'form_valid': valid,
'danger': 'Build was cancelled' 'danger': _('Build was cancelled')
} }
return self.renderJsonResponse(request, form, data=data) return self.renderJsonResponse(request, form, data=data)
@ -92,7 +92,7 @@ class BuildAutoAllocate(AjaxUpdateView):
model = Build model = Build
form_class = forms.ConfirmBuildForm form_class = forms.ConfirmBuildForm
context_object_name = 'build' context_object_name = 'build'
ajax_form_title = 'Allocate Stock' ajax_form_title = _('Allocate Stock')
ajax_template_name = 'build/auto_allocate.html' ajax_template_name = 'build/auto_allocate.html'
def get_context_data(self, *args, **kwargs): def get_context_data(self, *args, **kwargs):
@ -105,7 +105,7 @@ class BuildAutoAllocate(AjaxUpdateView):
context['build'] = build context['build'] = build
context['allocations'] = build.getAutoAllocations() context['allocations'] = build.getAutoAllocations()
except Build.DoesNotExist: except Build.DoesNotExist:
context['error'] = 'No matching build found' context['error'] = _('No matching build found')
return context return context
@ -124,8 +124,8 @@ class BuildAutoAllocate(AjaxUpdateView):
valid = False valid = False
if confirm is False: if confirm is False:
form.errors['confirm'] = ['Confirm stock allocation'] form.errors['confirm'] = [_('Confirm stock allocation')]
form.non_field_errors = 'Check the confirmation box at the bottom of the list' form.non_field_errors = _('Check the confirmation box at the bottom of the list')
else: else:
build.autoAllocate() build.autoAllocate()
valid = True valid = True
@ -145,7 +145,7 @@ class BuildUnallocate(AjaxUpdateView):
model = Build model = Build
form_class = forms.ConfirmBuildForm form_class = forms.ConfirmBuildForm
ajax_form_title = "Unallocate Stock" ajax_form_title = _("Unallocate Stock")
ajax_template_name = "build/unallocate.html" ajax_template_name = "build/unallocate.html"
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
@ -158,8 +158,8 @@ class BuildUnallocate(AjaxUpdateView):
valid = False valid = False
if confirm is False: if confirm is False:
form.errors['confirm'] = ['Confirm unallocation of build stock'] form.errors['confirm'] = [_('Confirm unallocation of build stock')]
form.non_field_errors = 'Check the confirmation box' form.non_field_errors = _('Check the confirmation box')
else: else:
build.unallocateStock() build.unallocateStock()
valid = True valid = True
@ -182,7 +182,7 @@ class BuildComplete(AjaxUpdateView):
model = Build model = Build
form_class = forms.CompleteBuildForm form_class = forms.CompleteBuildForm
context_object_name = "build" context_object_name = "build"
ajax_form_title = "Complete Build" ajax_form_title = _("Complete Build")
ajax_template_name = "build/complete.html" ajax_template_name = "build/complete.html"
def get_form(self): def get_form(self):
@ -255,14 +255,14 @@ class BuildComplete(AjaxUpdateView):
if confirm is False: if confirm is False:
form.errors['confirm'] = [ form.errors['confirm'] = [
'Confirm completion of build', _('Confirm completion of build'),
] ]
else: else:
try: try:
location = StockLocation.objects.get(id=loc_id) location = StockLocation.objects.get(id=loc_id)
valid = True valid = True
except StockLocation.DoesNotExist: except StockLocation.DoesNotExist:
form.errors['location'] = ['Invalid location selected'] form.errors['location'] = [_('Invalid location selected')]
serials = [] serials = []
@ -306,7 +306,7 @@ class BuildComplete(AjaxUpdateView):
def get_data(self): def get_data(self):
""" Provide feedback data back to the form """ """ Provide feedback data back to the form """
return { return {
'info': 'Build marked as COMPLETE' 'info': _('Build marked as COMPLETE')
} }
@ -382,7 +382,7 @@ class BuildCreate(AjaxCreateView):
model = Build model = Build
context_object_name = 'build' context_object_name = 'build'
form_class = forms.EditBuildForm form_class = forms.EditBuildForm
ajax_form_title = 'Start new Build' ajax_form_title = _('Start new Build')
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
def get_initial(self): def get_initial(self):
@ -405,7 +405,7 @@ class BuildCreate(AjaxCreateView):
def get_data(self): def get_data(self):
return { return {
'success': 'Created new build', 'success': _('Created new build'),
} }
@ -415,12 +415,12 @@ class BuildUpdate(AjaxUpdateView):
model = Build model = Build
form_class = forms.EditBuildForm form_class = forms.EditBuildForm
context_object_name = 'build' context_object_name = 'build'
ajax_form_title = 'Edit Build Details' ajax_form_title = _('Edit Build Details')
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
def get_data(self): def get_data(self):
return { return {
'info': 'Edited build', 'info': _('Edited build'),
} }
@ -429,7 +429,7 @@ class BuildDelete(AjaxDeleteView):
model = Build model = Build
ajax_template_name = 'build/delete_build.html' ajax_template_name = 'build/delete_build.html'
ajax_form_title = 'Delete Build' ajax_form_title = _('Delete Build')
class BuildItemDelete(AjaxDeleteView): class BuildItemDelete(AjaxDeleteView):
@ -439,12 +439,12 @@ class BuildItemDelete(AjaxDeleteView):
model = BuildItem model = BuildItem
ajax_template_name = 'build/delete_build_item.html' ajax_template_name = 'build/delete_build_item.html'
ajax_form_title = 'Unallocate Stock' ajax_form_title = _('Unallocate Stock')
context_object_name = 'item' context_object_name = 'item'
def get_data(self): def get_data(self):
return { return {
'danger': 'Removed parts from build allocation' 'danger': _('Removed parts from build allocation')
} }
@ -454,7 +454,7 @@ class BuildItemCreate(AjaxCreateView):
model = BuildItem model = BuildItem
form_class = forms.EditBuildItemForm form_class = forms.EditBuildItemForm
ajax_template_name = 'build/create_build_item.html' ajax_template_name = 'build/create_build_item.html'
ajax_form_title = 'Allocate new Part' ajax_form_title = _('Allocate new Part')
part = None part = None
available_stock = None available_stock = None
@ -570,11 +570,11 @@ class BuildItemEdit(AjaxUpdateView):
model = BuildItem model = BuildItem
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
form_class = forms.EditBuildItemForm form_class = forms.EditBuildItemForm
ajax_form_title = 'Edit Stock Allocation' ajax_form_title = _('Edit Stock Allocation')
def get_data(self): def get_data(self):
return { return {
'info': 'Updated Build Item', 'info': _('Updated Build Item'),
} }
def get_form(self): def get_form(self):

View File

@ -5,6 +5,8 @@ Django views for interacting with common models
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext as _
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from . import models from . import models
@ -16,7 +18,7 @@ class CurrencyCreate(AjaxCreateView):
model = models.Currency model = models.Currency
form_class = forms.CurrencyEditForm form_class = forms.CurrencyEditForm
ajax_form_title = 'Create new Currency' ajax_form_title = _('Create new Currency')
class CurrencyEdit(AjaxUpdateView): class CurrencyEdit(AjaxUpdateView):
@ -24,12 +26,12 @@ class CurrencyEdit(AjaxUpdateView):
model = models.Currency model = models.Currency
form_class = forms.CurrencyEditForm form_class = forms.CurrencyEditForm
ajax_form_title = 'Edit Currency' ajax_form_title = _('Edit Currency')
class CurrencyDelete(AjaxDeleteView): class CurrencyDelete(AjaxDeleteView):
""" View for deleting an existing Currency object """ """ View for deleting an existing Currency object """
model = models.Currency model = models.Currency
ajax_form_title = 'Delete Currency' ajax_form_title = _('Delete Currency')
ajax_template_name = "common/delete_currency.html" ajax_template_name = "common/delete_currency.html"

View File

@ -1,172 +0,0 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block page_title %}
InvenTree | {{ company.name }} - {% trans "Parts" %}
{% endblock %}
{% block content %}
<div class='row'>
<div class='col-sm-6'>
<h3>{% trans "Supplier Part" %}</h3>
<div class='btn-row'>
<div class='btn-group'>
<button type='button' class='btn btn-default btn-glyph' id='edit-part' title='Edit supplier part'>
<span class='glyphicon glyphicon-edit'/>
</button>
<button type='button' class='btn btn-default btn-glyph' id='delete-part' title='Delete supplier part'>
<span class='glyphicon glyphicon-trash'/>
</button>
</div>
</div>
</div>
<div class='col-sm-6'>
<div class='media-left'>
<img class='part-thumb'
{% if part.part.image %}
src='{{ part.part.image.url }}'
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
</div>
</div>
</div>
<hr>
<div class='row'>
<div class='col-sm-6'>
<h4>{% trans "Supplier Part Details" %}</h4>
<table class="table table-striped table-condensed">
<tr>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-suppliers' part.part.id %}">{{ part.part.full_name }}</a>
{% endif %}
</td>
</tr>
<tr><td>{% trans "Supplier" %}</td><td><a href="{% url 'company-detail-parts' part.supplier.id %}">{{ part.supplier.name }}</a></td></tr>
<tr><td>{% trans "SKU" %}</td><td>{{ part.SKU }}</tr></tr>
{% if part.URL %}
<tr><td>{% trans "URL" %}</td><td><a href="{{ part.URL }}">{{ part.URL }}</a></td></tr>
{% endif %}
{% if part.description %}
<tr><td>{% trans "Description" %}</td><td>{{ part.description }}</td></tr>
{% endif %}
{% if part.manufacturer %}
<tr><td>{% trans "Manufacturer" %}</td><td>{{ part.manufacturer }}</td></tr>
<tr><td>{% trans "MPN" %}</td><td>{{ part.MPN }}</td></tr>
{% endif %}
{% if part.note %}
<tr><td>{% trans "Note" %}</td><td>{{ part.note }}</td></tr>
{% endif %}
</table>
</div>
<div class='col-sm-6'>
<h4>{% trans "Pricing Information" %}</h4>
<table class="table table-striped table-condensed">
<tr><td>{% trans "Order Multiple" %}</td><td>{{ part.multiple }}</td></tr>
{% if part.base_cost > 0 %}
<tr><td>{% trans "Base Price (Flat Fee)" %}</td><td>{{ part.base_cost }}</td></tr>
{% endif %}
<tr>
<th>{% trans "Price Breaks" %}</th>
<th>
<div style='float: right;'>
<button class='btn btn-primary' id='new-price-break' type='button'>{% trans "New Price Break" %}</button>
</div>
</th>
</tr>
<tr>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Price" %}</th>
</tr>
{% if part.price_breaks.all %}
{% for pb in part.price_breaks.all %}
<tr>
<td>{{ pb.quantity }}</td>
<td>
{% if pb.currency %}{{ pb.currency.symbol }}{% endif %}
{{ pb.cost }}
{% if pb.currency %}{{ pb.currency.suffix }}{% endif %}
<div class='btn-group' style='float: right;'>
<button title='Edit Price Break' class='btn btn-default btn-sm pb-edit-button' type='button' url="{% url 'price-break-edit' pb.id %}"><span class='glyphicon glyphicon-edit'></span></button>
<button title='Delete Price Break' class='btn btn-default btn-sm pb-delete-button' type='button' url="{% url 'price-break-delete' pb.id %}"><span class='glyphicon glyphicon-trash'></span></button>
</div>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan='2'>
<span class='warning-msg'><i>{% trans "No price breaks have been added for this part" %}</i></span>
</td>
</tr>
{% endif %}
</table>
</div>
</div>
<hr>
<h4>{% trans "Purchase Orders" %}</h4>
{% include "order/po_table.html" with orders=part.purchase_orders %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#edit-part').click(function () {
launchModalForm(
"{% url 'supplier-part-edit' part.id %}",
{
reload: true
}
);
});
$('#delete-part').click(function() {
launchModalForm(
"{% url 'supplier-part-delete' %}?part={{ part.id }}",
{
redirect: "{% url 'company-detail-parts' part.supplier.id %}"
}
);
});
$('#new-price-break').click(function() {
launchModalForm("{% url 'price-break-create' %}",
{
reload: true,
data: {
part: {{ part.id }},
}
}
);
});
$('.pb-edit-button').click(function() {
var button = $(this);
launchModalForm(button.attr('url'),
{
reload: true,
}
);
});
$('.pb-delete-button').click(function() {
var button = $(this);
launchModalForm(button.attr('url'),
{
reload: true,
}
);
});
{% endblock %}

View File

@ -0,0 +1,68 @@
{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block page_title %}
InvenTree | {% trans "Supplier Part" %}
{% endblock %}
{% block content %}
<div class='row'>
<div class='col-sm-6'>
<h3>{% trans "Supplier Part" %}</h3>
<div class='btn-row'>
<div class='btn-group'>
<button type='button' class='btn btn-default btn-glyph' id='edit-part' title='Edit supplier part'>
<span class='glyphicon glyphicon-edit'/>
</button>
<button type='button' class='btn btn-default btn-glyph' id='delete-part' title='Delete supplier part'>
<span class='glyphicon glyphicon-trash'/>
</button>
</div>
</div>
</div>
<div class='col-sm-6'>
<div class='media-left'>
<img class='part-thumb'
{% if part.part.image %}
src='{{ part.part.image.url }}'
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
</div>
</div>
</div>
<hr>
<div class='container-fluid'>
{% block details %}
<!-- Particular SupplierPart page goes here ... -->
{% endblock %}
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#edit-part').click(function () {
launchModalForm(
"{% url 'supplier-part-edit' part.id %}",
{
reload: true
}
);
});
$('#delete-part').click(function() {
launchModalForm(
"{% url 'supplier-part-delete' %}?part={{ part.id }}",
{
redirect: "{% url 'company-detail-parts' part.supplier.id %}"
}
);
});
{% endblock %}

View File

@ -0,0 +1,44 @@
{% extends "company/supplier_part_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% include "company/supplier_part_tabs.html" with tab='details' %}
<hr>
<h4>{% trans "Supplier Part Details" %}</h4>
<table class="table table-striped table-condensed">
<tr>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-suppliers' part.part.id %}">{{ part.part.full_name }}</a>
{% endif %}
</td>
</tr>
<tr><td>{% trans "Supplier" %}</td><td><a href="{% url 'company-detail-parts' part.supplier.id %}">{{ part.supplier.name }}</a></td></tr>
<tr><td>{% trans "SKU" %}</td><td>{{ part.SKU }}</tr></tr>
{% if part.URL %}
<tr><td>{% trans "URL" %}</td><td><a href="{{ part.URL }}">{{ part.URL }}</a></td></tr>
{% endif %}
{% if part.description %}
<tr><td>{% trans "Description" %}</td><td>{{ part.description }}</td></tr>
{% endif %}
{% if part.manufacturer %}
<tr><td>{% trans "Manufacturer" %}</td><td>{{ part.manufacturer }}</td></tr>
<tr><td>{% trans "MPN" %}</td><td>{{ part.MPN }}</td></tr>
{% endif %}
{% if part.note %}
<tr><td>{% trans "Note" %}</td><td>{{ part.note }}</td></tr>
{% endif %}
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
{% endblock %}

View File

@ -0,0 +1,31 @@
{% extends "company/supplier_part_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% include "company/supplier_part_tabs.html" with tab='orders' %}
<hr>
<h4>{% trans "Supplier Part Orders" %}</h4>
<div id='button-bar'>
<div class='btn-group'>
<button class='btn btn-primary' type='button' id='part-order2' title='Order part'>Order Part</button>
</div>
</div>
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#button-bar'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadPurchaseOrderTable($("#purchase-order-table"), {
url: "{% url 'api-po-list' %}?supplier_part={{ part.id }}",
});
{% endblock %}

View File

@ -0,0 +1,92 @@
{% extends "company/supplier_part_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block details %}
{% include "company/supplier_part_tabs.html" with tab='pricing' %}
<hr>
<h4>{% trans "Pricing Information" %}</h4>
<table class="table table-striped table-condensed">
<tr><td>{% trans "Order Multiple" %}</td><td>{{ part.multiple }}</td></tr>
{% if part.base_cost > 0 %}
<tr><td>{% trans "Base Price (Flat Fee)" %}</td><td>{{ part.base_cost }}</td></tr>
{% endif %}
<tr>
<th>{% trans "Price Breaks" %}</th>
<th>
<div style='float: right;'>
<button class='btn btn-primary' id='new-price-break' type='button'>{% trans "New Price Break" %}</button>
</div>
</th>
</tr>
<tr>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Price" %}</th>
</tr>
{% if part.price_breaks.all %}
{% for pb in part.price_breaks.all %}
<tr>
<td>{% decimal pb.quantity %}</td>
<td>
{% if pb.currency %}{{ pb.currency.symbol }}{% endif %}
{% decimal pb.cost %}
{% if pb.currency %}{{ pb.currency.suffix }}{% endif %}
<div class='btn-group' style='float: right;'>
<button title='Edit Price Break' class='btn btn-default btn-sm pb-edit-button' type='button' url="{% url 'price-break-edit' pb.id %}"><span class='glyphicon glyphicon-edit'></span></button>
<button title='Delete Price Break' class='btn btn-default btn-sm pb-delete-button' type='button' url="{% url 'price-break-delete' pb.id %}"><span class='glyphicon glyphicon-trash'></span></button>
</div>
</td>
</tr>
{% endfor %}
{% else %}
<tr>
<td colspan='2'>
<span class='warning-msg'><i>{% trans "No price breaks have been added for this part" %}</i></span>
</td>
</tr>
{% endif %}
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#new-price-break').click(function() {
launchModalForm("{% url 'price-break-create' %}",
{
reload: true,
data: {
part: {{ part.id }},
}
}
);
});
$('.pb-edit-button').click(function() {
var button = $(this);
launchModalForm(button.attr('url'),
{
reload: true,
}
);
});
$('.pb-delete-button').click(function() {
var button = $(this);
launchModalForm(button.attr('url'),
{
reload: true,
}
);
});
{% endblock %}

View File

@ -0,0 +1,72 @@
{% extends "company/supplier_part_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% include "company/supplier_part_tabs.html" with tab='stock' %}
<hr>
<h4>{% trans "Supplier Part Stock" %}</h4>
{% include "stock_table.html" %}
{% endblock %}
{% block js_load %}
{{ block.super }}
<script type='text/javascript' src="{% static 'script/inventree/stock.js' %}"></script>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadStockTable($("#stock-table"), {
params: {
supplier_part: {{ part.id }},
location_detail: true,
part_detail: true,
},
groupByField: 'location',
buttons: ['#stock-options'],
url: "{% url 'api-stock-list' %}",
});
$("#stock-export").click(function() {
launchModalForm("{% url 'stock-export-options' %}", {
submit_text: '{% trans "Export" %}',
success: function(response) {
var url = "{% url 'stock-export' %}";
url += "?format=" + response.format;
url += "&cascade=" + response.cascade;
url += "&supplier_part={{ part.id }}";
location.href = url;
},
});
});
$("#item-create").click(function() {
launchModalForm("{% url 'stock-item-create' %}", {
reload: true,
data: {
part: {{ part.part.id }},
supplier_part: {{ part.id }},
},
secondary: [
{
field: 'location',
label: '{% trans "New Location" %}',
title: '{% trans "Create New Location" %}',
url: "{% url 'stock-location-create' %}",
}
]
});
return false;
});
{% endblock %}

View File

@ -0,0 +1,16 @@
{% load i18n %}
<ul class='nav nav-tabs'>
<li{% if tab == 'details' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-detail' part.id %}">{% trans "Details" %}</a>
</li>
<li{% if tab == 'pricing' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-pricing' part.id %}">{% trans "Pricing" %}</a>
</li>
<li{% if tab == 'stock' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-stock' part.id %}">{% trans "Stock" %}</a>
</li>
<li {% if tab == 'orders' %} class='active'{% endif %}>
<a href="{% url 'supplier-part-orders' part.id %}">{% trans "Orders" %}</a>
</li>
</ul>

View File

@ -47,7 +47,11 @@ price_break_urls = [
] ]
supplier_part_detail_urls = [ supplier_part_detail_urls = [
url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'), url(r'^edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'),
url(r'^pricing/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_pricing.html'), name='supplier-part-pricing'),
url(r'^orders/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_orders.html'), name='supplier-part-orders'),
url(r'^stock/', views.SupplierPartDetail.as_view(template_name='company/supplier_part_stock.html'), name='supplier-part-stock'),
url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'), url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'),
] ]

View File

@ -6,6 +6,7 @@ Django views for interacting with Company app
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext as _
from django.views.generic import DetailView, ListView, UpdateView from django.views.generic import DetailView, ListView, UpdateView
from django.urls import reverse from django.urls import reverse
@ -93,12 +94,12 @@ class CompanyImage(AjaxUpdateView):
""" View for uploading an image for the Company """ """ View for uploading an image for the Company """
model = Company model = Company
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Update Company Image' ajax_form_title = _('Update Company Image')
form_class = CompanyImageForm form_class = CompanyImageForm
def get_data(self): def get_data(self):
return { return {
'success': 'Updated company image', 'success': _('Updated company image'),
} }
@ -108,11 +109,11 @@ class CompanyEdit(AjaxUpdateView):
form_class = EditCompanyForm form_class = EditCompanyForm
context_object_name = 'company' context_object_name = 'company'
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Edit Company' ajax_form_title = _('Edit Company')
def get_data(self): def get_data(self):
return { return {
'info': 'Edited company information', 'info': _('Edited company information'),
} }
@ -122,11 +123,11 @@ class CompanyCreate(AjaxCreateView):
context_object_name = 'company' context_object_name = 'company'
form_class = EditCompanyForm form_class = EditCompanyForm
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = "Create new Company" ajax_form_title = _("Create new Company")
def get_data(self): def get_data(self):
return { return {
'success': "Created new company", 'success': _("Created new company"),
} }
@ -136,19 +137,19 @@ class CompanyDelete(AjaxDeleteView):
model = Company model = Company
success_url = '/company/' success_url = '/company/'
ajax_template_name = 'company/delete.html' ajax_template_name = 'company/delete.html'
ajax_form_title = 'Delete Company' ajax_form_title = _('Delete Company')
context_object_name = 'company' context_object_name = 'company'
def get_data(self): def get_data(self):
return { return {
'danger': 'Company was deleted', 'danger': _('Company was deleted'),
} }
class SupplierPartDetail(DetailView): class SupplierPartDetail(DetailView):
""" Detail view for SupplierPart """ """ Detail view for SupplierPart """
model = SupplierPart model = SupplierPart
template_name = 'company/partdetail.html' template_name = 'company/supplier_part_detail.html'
context_object_name = 'part' context_object_name = 'part'
queryset = SupplierPart.objects.all() queryset = SupplierPart.objects.all()
@ -166,7 +167,7 @@ class SupplierPartEdit(AjaxUpdateView):
context_object_name = 'part' context_object_name = 'part'
form_class = EditSupplierPartForm form_class = EditSupplierPartForm
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Edit Supplier Part' ajax_form_title = _('Edit Supplier Part')
class SupplierPartCreate(AjaxCreateView): class SupplierPartCreate(AjaxCreateView):
@ -175,7 +176,7 @@ class SupplierPartCreate(AjaxCreateView):
model = SupplierPart model = SupplierPart
form_class = EditSupplierPartForm form_class = EditSupplierPartForm
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Create new Supplier Part' ajax_form_title = _('Create new Supplier Part')
context_object_name = 'part' context_object_name = 'part'
def get_form(self): def get_form(self):
@ -232,7 +233,7 @@ class SupplierPartDelete(AjaxDeleteView):
success_url = '/supplier/' success_url = '/supplier/'
ajax_template_name = 'company/partdelete.html' ajax_template_name = 'company/partdelete.html'
ajax_form_title = 'Delete Supplier Part' ajax_form_title = _('Delete Supplier Part')
parts = [] parts = []
@ -302,7 +303,7 @@ class PriceBreakCreate(AjaxCreateView):
model = SupplierPriceBreak model = SupplierPriceBreak
form_class = EditPriceBreakForm form_class = EditPriceBreakForm
ajax_form_title = 'Add Price Break' ajax_form_title = _('Add Price Break')
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
def get_data(self): def get_data(self):
@ -337,7 +338,7 @@ class PriceBreakEdit(AjaxUpdateView):
model = SupplierPriceBreak model = SupplierPriceBreak
form_class = EditPriceBreakForm form_class = EditPriceBreakForm
ajax_form_title = 'Edit Price Break' ajax_form_title = _('Edit Price Break')
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
def get_form(self): def get_form(self):
@ -352,5 +353,5 @@ class PriceBreakDelete(AjaxDeleteView):
""" View for deleting a supplier price break """ """ View for deleting a supplier price break """
model = SupplierPriceBreak model = SupplierPriceBreak
ajax_form_title = "Delete Price Break" ajax_form_title = _("Delete Price Break")
ajax_template_name = 'modal_delete_form.html' ajax_template_name = 'modal_delete_form.html'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -18,6 +18,7 @@ from InvenTree.status_codes import OrderStatus
import os import os
from part.models import Part from part.models import Part
from company.models import SupplierPart
from .models import PurchaseOrder, PurchaseOrderLineItem from .models import PurchaseOrder, PurchaseOrderLineItem
from .serializers import POSerializer, POLineItemSerializer from .serializers import POSerializer, POLineItemSerializer
@ -62,6 +63,14 @@ class POList(generics.ListCreateAPIView):
except (Part.DoesNotExist, ValueError): except (Part.DoesNotExist, ValueError):
pass pass
# Attempt to filter by supplier part
if 'supplier_part' in request.GET:
try:
supplier_part = SupplierPart.objects.get(pk=request.GET['supplier_part'])
queryset = queryset.filter(id__in=[p.id for p in supplier_part.purchase_orders()])
except (ValueError, SupplierPart.DoesNotExist):
pass
data = queryset.values( data = queryset.values(
'pk', 'pk',
'supplier', 'supplier',

View File

@ -96,7 +96,7 @@ class PurchaseOrderCreate(AjaxCreateView):
""" View for creating a new PurchaseOrder object using a modal form """ """ View for creating a new PurchaseOrder object using a modal form """
model = PurchaseOrder model = PurchaseOrder
ajax_form_title = "Create Purchase Order" ajax_form_title = _("Create Purchase Order")
form_class = order_forms.EditPurchaseOrderForm form_class = order_forms.EditPurchaseOrderForm
def get_initial(self): def get_initial(self):
@ -126,7 +126,7 @@ class PurchaseOrderEdit(AjaxUpdateView):
""" View for editing a PurchaseOrder using a modal form """ """ View for editing a PurchaseOrder using a modal form """
model = PurchaseOrder model = PurchaseOrder
ajax_form_title = 'Edit Purchase Order' ajax_form_title = _('Edit Purchase Order')
form_class = order_forms.EditPurchaseOrderForm form_class = order_forms.EditPurchaseOrderForm
def get_form(self): def get_form(self):
@ -146,7 +146,7 @@ class PurchaseOrderCancel(AjaxUpdateView):
""" View for cancelling a purchase order """ """ View for cancelling a purchase order """
model = PurchaseOrder model = PurchaseOrder
ajax_form_title = 'Cancel Order' ajax_form_title = _('Cancel Order')
ajax_template_name = 'order/order_cancel.html' ajax_template_name = 'order/order_cancel.html'
form_class = order_forms.CancelPurchaseOrderForm form_class = order_forms.CancelPurchaseOrderForm
@ -179,7 +179,7 @@ class PurchaseOrderIssue(AjaxUpdateView):
""" View for changing a purchase order from 'PENDING' to 'ISSUED' """ """ View for changing a purchase order from 'PENDING' to 'ISSUED' """
model = PurchaseOrder model = PurchaseOrder
ajax_form_title = 'Issue Order' ajax_form_title = _('Issue Order')
ajax_template_name = "order/order_issue.html" ajax_template_name = "order/order_issue.html"
form_class = order_forms.IssuePurchaseOrderForm form_class = order_forms.IssuePurchaseOrderForm
@ -215,7 +215,7 @@ class PurchaseOrderComplete(AjaxUpdateView):
form_class = order_forms.CompletePurchaseOrderForm form_class = order_forms.CompletePurchaseOrderForm
model = PurchaseOrder model = PurchaseOrder
ajax_template_name = "order/order_complete.html" ajax_template_name = "order/order_complete.html"
ajax_form_title = "Complete Order" ajax_form_title = _("Complete Order")
context_object_name = 'order' context_object_name = 'order'
def get_context_data(self): def get_context_data(self):
@ -281,7 +281,7 @@ class PurchaseOrderReceive(AjaxUpdateView):
""" """
form_class = order_forms.ReceivePurchaseOrderForm form_class = order_forms.ReceivePurchaseOrderForm
ajax_form_title = "Receive Parts" ajax_form_title = _("Receive Parts")
ajax_template_name = "order/receive_parts.html" ajax_template_name = "order/receive_parts.html"
# Where the parts will be going (selected in POST request) # Where the parts will be going (selected in POST request)
@ -445,7 +445,7 @@ class OrderParts(AjaxView):
""" """
ajax_form_title = "Order Parts" ajax_form_title = _("Order Parts")
ajax_template_name = 'order/order_wizard/select_parts.html' ajax_template_name = 'order/order_wizard/select_parts.html'
# List of Parts we wish to order # List of Parts we wish to order
@ -744,7 +744,7 @@ class POLineItemCreate(AjaxCreateView):
model = PurchaseOrderLineItem model = PurchaseOrderLineItem
context_object_name = 'line' context_object_name = 'line'
form_class = order_forms.EditPurchaseOrderLineItemForm form_class = order_forms.EditPurchaseOrderLineItemForm
ajax_form_title = 'Add Line Item' ajax_form_title = _('Add Line Item')
def post(self, request, *arg, **kwargs): def post(self, request, *arg, **kwargs):
@ -859,7 +859,7 @@ class POLineItemEdit(AjaxUpdateView):
model = PurchaseOrderLineItem model = PurchaseOrderLineItem
form_class = order_forms.EditPurchaseOrderLineItemForm form_class = order_forms.EditPurchaseOrderLineItemForm
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Edit Line Item' ajax_form_title = _('Edit Line Item')
def get_form(self): def get_form(self):
form = super().get_form() form = super().get_form()
@ -875,10 +875,10 @@ class POLineItemDelete(AjaxDeleteView):
""" """
model = PurchaseOrderLineItem model = PurchaseOrderLineItem
ajax_form_title = 'Delete Line Item' ajax_form_title = _('Delete Line Item')
ajax_template_name = 'order/po_lineitem_delete.html' ajax_template_name = 'order/po_lineitem_delete.html'
def get_data(self): def get_data(self):
return { return {
'danger': 'Deleted line item', 'danger': _('Deleted line item'),
} }

View File

@ -131,11 +131,13 @@ class BomItemResource(ModelResource):
level = Field(attribute='level', readonly=True) level = Field(attribute='level', readonly=True)
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) bom_id = Field(attribute='pk')
parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
parent_part_name = Field(attribute='part__full_name', readonly=True) parent_part_name = Field(attribute='part__full_name', readonly=True)
id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part)) sub_part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part))
sub_part_name = Field(attribute='sub_part__full_name', readonly=True) sub_part_name = Field(attribute='sub_part__full_name', readonly=True)
@ -147,7 +149,12 @@ class BomItemResource(ModelResource):
report_skipped = False report_skipped = False
clean_model_instances = True clean_model_instances = True
exclude = ['checksum', ] exclude = [
'checksum',
'id',
'part',
'sub_part',
]
class BomItemAdmin(ImportExportModelAdmin): class BomItemAdmin(ImportExportModelAdmin):

View File

@ -53,12 +53,18 @@ def ExportBom(part, fmt='csv', cascade=False):
bom_items = [] bom_items = []
uids = []
def add_items(items, level): def add_items(items, level):
# Add items at a given layer # Add items at a given layer
for item in items: for item in items:
item.level = '-' * level item.level = '-' * level
# Avoid circular BOM references
if item.pk in uids:
continue
bom_items.append(item) bom_items.append(item)
if item.sub_part.assembly: if item.sub_part.assembly:

View File

@ -35,6 +35,7 @@ from InvenTree import helpers
from InvenTree import validators from InvenTree import validators
from InvenTree.models import InvenTreeTree from InvenTree.models import InvenTreeTree
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField
from InvenTree.helpers import decimal2string
from InvenTree.status_codes import BuildStatus, StockStatus, OrderStatus from InvenTree.status_codes import BuildStatus, StockStatus, OrderStatus
@ -1242,11 +1243,11 @@ class BomItem(models.Model):
pmin, pmax = prange pmin, pmax = prange
# remove trailing zeros
pmin = pmin.normalize()
pmax = pmax.normalize()
if pmin == pmax: if pmin == pmax:
return str(pmin) return decimal2string(pmin)
# Convert to better string representation
pmin = decimal2string(pmin)
pmax = decimal2string(pmax)
return "{pmin} to {pmax}".format(pmin=pmin, pmax=pmax) return "{pmin} to {pmax}".format(pmin=pmin, pmax=pmax)

View File

@ -39,6 +39,9 @@
{ {
title: 'Allocated', title: 'Allocated',
sortable: false, sortable: false,
formatter: function(value, row, index, field) {
return parseFloat(value);
},
}, },
{ {
title: 'Status', title: 'Status',

View File

@ -1,5 +1,6 @@
{% extends "part/part_base.html" %} {% extends "part/part_base.html" %}
{% load static %} {% load static %}
{% load i18n %}
{% block details %} {% block details %}
{% include 'part/tabs.html' with tab='stock' %} {% include 'part/tabs.html' with tab='stock' %}
@ -49,7 +50,7 @@
$("#stock-export").click(function() { $("#stock-export").click(function() {
launchModalForm("{% url 'stock-export-options' %}", { launchModalForm("{% url 'stock-export-options' %}", {
submit_text: "Export", submit_text: "{% trans 'Export' %}",
success: function(response) { success: function(response) {
var url = "{% url 'stock-export' %}"; var url = "{% url 'stock-export' %}";
@ -71,14 +72,14 @@
secondary: [ secondary: [
{ {
field: 'part', field: 'part',
label: 'New Part', label: '{% trans "New Part" %}',
title: 'Create New Part', title: '{% trans "Create New Part" %}',
url: "{% url 'part-create' %}", url: "{% url 'part-create' %}",
}, },
{ {
field: 'supplier_part', field: 'supplier_part',
label: 'New Supplier Part', label: '{% trans "New Supplier Part" %}',
title: 'Create new Supplier Part', title: '{% trans "Create new Supplier Part" %}',
url: "{% url 'supplier-part-create' %}", url: "{% url 'supplier-part-create' %}",
data: { data: {
part: {{ part.id }} part: {{ part.id }}
@ -86,8 +87,8 @@
}, },
{ {
field: 'location', field: 'location',
label: 'New Location', label: '{% trans "New Location" %}',
title: 'Create New Location', title: '{% trans "Create New Location" %}',
url: "{% url 'stock-location-create' %}", url: "{% url 'stock-location-create' %}",
} }
] ]

View File

@ -53,6 +53,9 @@
sortable: true, sortable: true,
field: 'quantity', field: 'quantity',
title: 'Uses', title: 'Uses',
formatter: function(value, row, index, field) {
return parseFloat(value);
},
} }
], ],

View File

@ -27,7 +27,7 @@ def inrange(n, *args, **kwargs):
@register.simple_tag() @register.simple_tag()
def multiply(x, y, *args, **kwargs): def multiply(x, y, *args, **kwargs):
""" Multiply two numbers together """ """ Multiply two numbers together """
return x * y return decimal2string(x * y)
@register.simple_tag() @register.simple_tag()
@ -40,7 +40,7 @@ def add(x, y, *args, **kwargs):
def part_allocation_count(build, part, *args, **kwargs): def part_allocation_count(build, part, *args, **kwargs):
""" Return the total number of <part> allocated to <build> """ """ Return the total number of <part> allocated to <build> """
return build.getAllocatedQuantity(part) return decimal2string(build.getAllocatedQuantity(part))
@register.simple_tag() @register.simple_tag()

View File

@ -1,6 +1,7 @@
# Tests for the Part model # Tests for the Part model
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.test import TestCase from django.test import TestCase
@ -16,7 +17,7 @@ class TemplateTagTest(TestCase):
""" Tests for the custom template tag code """ """ Tests for the custom template tag code """
def test_multiply(self): def test_multiply(self):
self.assertEqual(inventree_extras.multiply(3, 5), 15) self.assertEqual(int(inventree_extras.multiply(3, 5)), 15)
def test_version(self): def test_version(self):
self.assertEqual(type(inventree_extras.inventree_version()), str) self.assertEqual(type(inventree_extras.inventree_version()), str)

View File

@ -72,7 +72,7 @@ class PartAttachmentCreate(AjaxCreateView):
""" """
model = PartAttachment model = PartAttachment
form_class = part_forms.EditPartAttachmentForm form_class = part_forms.EditPartAttachmentForm
ajax_form_title = "Add part attachment" ajax_form_title = _("Add part attachment")
ajax_template_name = "modal_form.html" ajax_template_name = "modal_form.html"
def get_data(self): def get_data(self):
@ -115,7 +115,7 @@ class PartAttachmentEdit(AjaxUpdateView):
model = PartAttachment model = PartAttachment
form_class = part_forms.EditPartAttachmentForm form_class = part_forms.EditPartAttachmentForm
ajax_template_name = 'modal_form.html' ajax_template_name = 'modal_form.html'
ajax_form_title = 'Edit attachment' ajax_form_title = _('Edit attachment')
def get_data(self): def get_data(self):
return { return {
@ -134,13 +134,13 @@ class PartAttachmentDelete(AjaxDeleteView):
""" View for deleting a PartAttachment """ """ View for deleting a PartAttachment """
model = PartAttachment model = PartAttachment
ajax_form_title = "Delete Part Attachment" ajax_form_title = _("Delete Part Attachment")
ajax_template_name = "part/attachment_delete.html" ajax_template_name = "part/attachment_delete.html"
context_object_name = "attachment" context_object_name = "attachment"
def get_data(self): def get_data(self):
return { return {
'danger': 'Deleted part attachment' 'danger': _('Deleted part attachment')
} }
@ -148,7 +148,7 @@ class PartSetCategory(AjaxUpdateView):
""" View for settings the part category for multiple parts at once """ """ View for settings the part category for multiple parts at once """
ajax_template_name = 'part/set_category.html' ajax_template_name = 'part/set_category.html'
ajax_form_title = 'Set Part Category' ajax_form_title = _('Set Part Category')
form_class = part_forms.SetPartCategoryForm form_class = part_forms.SetPartCategoryForm
category = None category = None
@ -231,7 +231,7 @@ class MakePartVariant(AjaxCreateView):
model = Part model = Part
form_class = part_forms.EditPartForm form_class = part_forms.EditPartForm
ajax_form_title = 'Create Variant' ajax_form_title = _('Create Variant')
ajax_template_name = 'part/variant_part.html' ajax_template_name = 'part/variant_part.html'
def get_part_template(self): def get_part_template(self):
@ -301,7 +301,7 @@ class PartDuplicate(AjaxCreateView):
model = Part model = Part
form_class = part_forms.EditPartForm form_class = part_forms.EditPartForm
ajax_form_title = "Duplicate Part" ajax_form_title = _("Duplicate Part")
ajax_template_name = "part/copy_part.html" ajax_template_name = "part/copy_part.html"
def get_data(self): def get_data(self):
@ -592,7 +592,7 @@ class PartDetail(DetailView):
class PartQRCode(QRCodeView): class PartQRCode(QRCodeView):
""" View for displaying a QR code for a Part object """ """ View for displaying a QR code for a Part object """
ajax_form_title = "Part QR Code" ajax_form_title = _("Part QR Code")
def get_qr_data(self): def get_qr_data(self):
""" Generate QR code data for the Part """ """ Generate QR code data for the Part """

View File

@ -301,6 +301,7 @@ class StockList(generics.ListCreateAPIView):
'part__category', 'part__category',
'part__category__name', 'part__category__name',
'part__category__description', 'part__category__description',
'supplier_part',
) )
# Reduce the number of lookups we need to do for categories # Reduce the number of lookups we need to do for categories
@ -371,7 +372,13 @@ class StockList(generics.ListCreateAPIView):
except PartCategory.DoesNotExist: except PartCategory.DoesNotExist:
pass pass
# Filter by supplier # Filter by supplier_part ID
supplier_part_id = self.request.query_params.get('supplier_part', None)
if supplier_part_id:
stock_list = stock_list.filter(supplier_part=supplier_part_id)
# Filter by supplier ID
supplier_id = self.request.query_params.get('supplier', None) supplier_id = self.request.query_params.get('supplier', None)
if supplier_id: if supplier_id:

View File

@ -35,7 +35,9 @@
<hr> <hr>
<div class='panel panel-default'> <div class='panel panel-default'>
<div class='panel-content'> <div class='panel-content'>
{% if item.notes %}
{{ item.notes | markdownify }} {{ item.notes | markdownify }}
{% endif %}
</div> </div>
</div> </div>

View File

@ -1,26 +0,0 @@
{% extends "stock/stock_app_base.html" %}
{% load static %}
{% block content %}
<h3>Stock list here!</h3>
<table class='table table-striped table-condensed' data-toolbar='#button-toolbar' id='tracking-table'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadStockTrackingTable($("#tracking-table"), {
params: function(p) {
return {
ordering: '-date',
};
},
partColumn: true,
url: "{% url 'api-stock-track' %}",
});
{% endblock %}

View File

@ -24,7 +24,7 @@ from InvenTree.helpers import ExtractSerialNumbers
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from datetime import datetime from datetime import datetime
from company.models import Company from company.models import Company, SupplierPart
from part.models import Part from part.models import Part
from .models import StockItem, StockLocation, StockItemTracking from .models import StockItem, StockLocation, StockItemTracking
@ -212,6 +212,16 @@ class StockExport(AjaxView):
except (ValueError, Company.DoesNotExist): except (ValueError, Company.DoesNotExist):
pass pass
# Check if a particular supplier_part was specified
sup_part_id = request.GET.get('supplier_part', None)
supplier_part = None
if sup_part_id:
try:
supplier_part = SupplierPart.objects.get(pk=sup_part_id)
except (ValueError, SupplierPart.DoesNotExist):
pass
# Check if a particular part was specified # Check if a particular part was specified
part_id = request.GET.get('part', None) part_id = request.GET.get('part', None)
part = None part = None
@ -244,7 +254,11 @@ class StockExport(AjaxView):
if supplier: if supplier:
stock_items = stock_items.filter(supplier_part__supplier=supplier) stock_items = stock_items.filter(supplier_part__supplier=supplier)
if supplier_part:
stock_items = stock_items.filter(supplier_part=supplier_part)
# Filter out stock items that are not 'in stock' # Filter out stock items that are not 'in stock'
# TODO - This might need some more thought in the future...
stock_items = stock_items.filter(customer=None) stock_items = stock_items.filter(customer=None)
stock_items = stock_items.filter(belongs_to=None) stock_items = stock_items.filter(belongs_to=None)
@ -816,6 +830,11 @@ class StockItemCreate(AjaxCreateView):
part_id = self.request.GET.get('part', None) part_id = self.request.GET.get('part', None)
loc_id = self.request.GET.get('location', None) loc_id = self.request.GET.get('location', None)
sup_part_id = self.request.GET.get('supplier_part', None)
part = None
location = None
supplier_part = None
# Part field has been specified # Part field has been specified
if part_id: if part_id:
@ -824,14 +843,27 @@ class StockItemCreate(AjaxCreateView):
initials['part'] = part initials['part'] = part
initials['location'] = part.get_default_location() initials['location'] = part.get_default_location()
initials['supplier_part'] = part.default_supplier initials['supplier_part'] = part.default_supplier
except Part.DoesNotExist: except (ValueError, Part.DoesNotExist):
pass
# SupplierPart field has been specified
# It must match the Part, if that has been supplied
if sup_part_id:
try:
supplier_part = SupplierPart.objects.get(pk=sup_part_id)
if part is None or supplier_part.part == part:
initials['supplier_part'] = supplier_part
except (ValueError, SupplierPart.DoesNotExist):
pass pass
# Location has been specified # Location has been specified
if loc_id: if loc_id:
try: try:
initials['location'] = StockLocation.objects.get(pk=loc_id) location = StockLocation.objects.get(pk=loc_id)
except StockLocation.DoesNotExist: initials['location'] = location
except (ValueError, StockLocation.DoesNotExist):
pass pass
return initials return initials

View File

@ -1,18 +1,18 @@
{% load i18n %}
<div id='button-toolbar'> <div id='button-toolbar'>
<div class='button-toolbar container-fluid' style='float: right;'> <div class='button-toolbar container-fluid' style='float: right;'>
<button class='btn btn-default' id='stock-export' title='Export Stock Information'>Export</button> <button class='btn btn-default' id='stock-export' title='Export Stock Information'>{% trans "Export" %}</button>
{% if not part or part.is_template == False %} <button class="btn btn-success" id='item-create'>{% trans "New Stock Item" %}</button>
<button class="btn btn-success" id='item-create'>New Stock Item</button>
{% endif %}
<div class="dropdown" style='float: right;'> <div class="dropdown" style='float: right;'>
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">Options<span class="caret"></span></button> <button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
<ul class="dropdown-menu"> <ul class="dropdown-menu">
<li><a href="#" id='multi-item-add' title='Add to selected stock items'>Add stock</a></li> <li><a href="#" id='multi-item-add' title='Add to selected stock items'>{% trans "Add stock" %}</a></li>
<li><a href="#" id='multi-item-remove' title='Remove from selected stock items'>Remove stock</a></li> <li><a href="#" id='multi-item-remove' title='Remove from selected stock items'>{% trans "Remove stock" %}</a></li>
<li><a href="#" id='multi-item-stocktake' title='Stocktake selected stock items'>Count stock</a></li> <li><a href="#" id='multi-item-stocktake' title='Stocktake selected stock items'>{% trans "Count stock" %}</a></li>
<li><a href='#' id='multi-item-move' title='Move selected stock items'>Move stock</a></li> <li><a href='#' id='multi-item-move' title='Move selected stock items'>{% trans "Move stock" %}</a></li>
<li><a href='#' id='multi-item-order' title='Order selected items'>Order stock</a></li> <li><a href='#' id='multi-item-order' title='Order selected items'>{% trans "Order stock" %}</a></li>
<li><a href='#' id='multi-item-delete' title='Delete selected items'>Delete Stock</a></li> <li><a href='#' id='multi-item-delete' title='Delete selected items'>{% trans "Delete Stock" %}</a></li>
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -1,4 +1,4 @@
Django==2.2.9 # Django package Django==2.2.10 # Django package
pillow==6.2.0 # Image manipulation pillow==6.2.0 # Image manipulation
djangorestframework==3.10.3 # DRF framework djangorestframework==3.10.3 # DRF framework
django-cors-headers==3.2.0 # CORS headers extension for DRF django-cors-headers==3.2.0 # CORS headers extension for DRF