mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
0498fd633a
@ -1,5 +1,6 @@
|
||||
function updateAllocationTotal(id, count, required) {
|
||||
|
||||
count = parseFloat(count);
|
||||
|
||||
$('#allocation-total-'+id).html(count);
|
||||
|
||||
@ -27,21 +28,24 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
|
||||
field: 'stock_item_detail',
|
||||
title: 'Stock Item',
|
||||
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',
|
||||
title: 'Available',
|
||||
formatter: function(value, row, index, field) {
|
||||
return parseFloat(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: 'Allocated',
|
||||
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 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 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 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>";
|
||||
|
||||
|
@ -385,6 +385,9 @@ function loadStockTrackingTable(table, options) {
|
||||
cols.push({
|
||||
field: 'quantity',
|
||||
title: 'Quantity',
|
||||
formatter: function(value, row, index, field) {
|
||||
return parseFloat(value);
|
||||
},
|
||||
});
|
||||
|
||||
cols.push({
|
||||
|
@ -20,6 +20,7 @@ from markdownx.models import MarkdownxField
|
||||
|
||||
from InvenTree.status_codes import BuildStatus
|
||||
from InvenTree.fields import InvenTreeURLField
|
||||
from InvenTree.helpers import decimal2string
|
||||
|
||||
from stock.models import StockItem
|
||||
from part.models import Part, BomItem
|
||||
@ -42,7 +43,7 @@ class Build(models.Model):
|
||||
"""
|
||||
|
||||
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):
|
||||
return reverse('build-detail', kwargs={'pk': self.id})
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
{% block collapse_heading %}
|
||||
<div class='col-sm-2'>
|
||||
<b>{{ item.sub_part.total_stock }}</b>
|
||||
<b>{% decimal item.sub_part.total_stock %}</b>
|
||||
</div>
|
||||
<div class='col-sm-2'>
|
||||
<b>{% multiply build.quantity item.quantity %}</b>
|
||||
|
@ -1,7 +1,9 @@
|
||||
{% extends "modal_delete_form.html" %}
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block pre_form_content %}
|
||||
Are you sure you want to unallocate these parts?
|
||||
{% trans "Are you sure you want to unallocate these parts?" %}
|
||||
<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 %}
|
@ -1,9 +1,10 @@
|
||||
{% extends "modal_form.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
{% block pre_form_content %}
|
||||
|
||||
{{ 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 %}
|
@ -53,7 +53,7 @@ class BuildCancel(AjaxUpdateView):
|
||||
|
||||
model = Build
|
||||
ajax_template_name = 'build/cancel.html'
|
||||
ajax_form_title = 'Cancel Build'
|
||||
ajax_form_title = _('Cancel Build')
|
||||
context_object_name = 'build'
|
||||
form_class = forms.CancelBuildForm
|
||||
|
||||
@ -71,12 +71,12 @@ class BuildCancel(AjaxUpdateView):
|
||||
if confirm:
|
||||
build.cancelBuild(request.user)
|
||||
else:
|
||||
form.errors['confirm_cancel'] = ['Confirm build cancellation']
|
||||
form.errors['confirm_cancel'] = [_('Confirm build cancellation')]
|
||||
valid = False
|
||||
|
||||
data = {
|
||||
'form_valid': valid,
|
||||
'danger': 'Build was cancelled'
|
||||
'danger': _('Build was cancelled')
|
||||
}
|
||||
|
||||
return self.renderJsonResponse(request, form, data=data)
|
||||
@ -92,7 +92,7 @@ class BuildAutoAllocate(AjaxUpdateView):
|
||||
model = Build
|
||||
form_class = forms.ConfirmBuildForm
|
||||
context_object_name = 'build'
|
||||
ajax_form_title = 'Allocate Stock'
|
||||
ajax_form_title = _('Allocate Stock')
|
||||
ajax_template_name = 'build/auto_allocate.html'
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
@ -105,7 +105,7 @@ class BuildAutoAllocate(AjaxUpdateView):
|
||||
context['build'] = build
|
||||
context['allocations'] = build.getAutoAllocations()
|
||||
except Build.DoesNotExist:
|
||||
context['error'] = 'No matching build found'
|
||||
context['error'] = _('No matching build found')
|
||||
|
||||
return context
|
||||
|
||||
@ -124,8 +124,8 @@ class BuildAutoAllocate(AjaxUpdateView):
|
||||
valid = False
|
||||
|
||||
if confirm is False:
|
||||
form.errors['confirm'] = ['Confirm stock allocation']
|
||||
form.non_field_errors = 'Check the confirmation box at the bottom of the list'
|
||||
form.errors['confirm'] = [_('Confirm stock allocation')]
|
||||
form.non_field_errors = _('Check the confirmation box at the bottom of the list')
|
||||
else:
|
||||
build.autoAllocate()
|
||||
valid = True
|
||||
@ -145,7 +145,7 @@ class BuildUnallocate(AjaxUpdateView):
|
||||
|
||||
model = Build
|
||||
form_class = forms.ConfirmBuildForm
|
||||
ajax_form_title = "Unallocate Stock"
|
||||
ajax_form_title = _("Unallocate Stock")
|
||||
ajax_template_name = "build/unallocate.html"
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
@ -158,8 +158,8 @@ class BuildUnallocate(AjaxUpdateView):
|
||||
valid = False
|
||||
|
||||
if confirm is False:
|
||||
form.errors['confirm'] = ['Confirm unallocation of build stock']
|
||||
form.non_field_errors = 'Check the confirmation box'
|
||||
form.errors['confirm'] = [_('Confirm unallocation of build stock')]
|
||||
form.non_field_errors = _('Check the confirmation box')
|
||||
else:
|
||||
build.unallocateStock()
|
||||
valid = True
|
||||
@ -182,7 +182,7 @@ class BuildComplete(AjaxUpdateView):
|
||||
model = Build
|
||||
form_class = forms.CompleteBuildForm
|
||||
context_object_name = "build"
|
||||
ajax_form_title = "Complete Build"
|
||||
ajax_form_title = _("Complete Build")
|
||||
ajax_template_name = "build/complete.html"
|
||||
|
||||
def get_form(self):
|
||||
@ -255,14 +255,14 @@ class BuildComplete(AjaxUpdateView):
|
||||
|
||||
if confirm is False:
|
||||
form.errors['confirm'] = [
|
||||
'Confirm completion of build',
|
||||
_('Confirm completion of build'),
|
||||
]
|
||||
else:
|
||||
try:
|
||||
location = StockLocation.objects.get(id=loc_id)
|
||||
valid = True
|
||||
except StockLocation.DoesNotExist:
|
||||
form.errors['location'] = ['Invalid location selected']
|
||||
form.errors['location'] = [_('Invalid location selected')]
|
||||
|
||||
serials = []
|
||||
|
||||
@ -306,7 +306,7 @@ class BuildComplete(AjaxUpdateView):
|
||||
def get_data(self):
|
||||
""" Provide feedback data back to the form """
|
||||
return {
|
||||
'info': 'Build marked as COMPLETE'
|
||||
'info': _('Build marked as COMPLETE')
|
||||
}
|
||||
|
||||
|
||||
@ -382,7 +382,7 @@ class BuildCreate(AjaxCreateView):
|
||||
model = Build
|
||||
context_object_name = 'build'
|
||||
form_class = forms.EditBuildForm
|
||||
ajax_form_title = 'Start new Build'
|
||||
ajax_form_title = _('Start new Build')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
|
||||
def get_initial(self):
|
||||
@ -405,7 +405,7 @@ class BuildCreate(AjaxCreateView):
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': 'Created new build',
|
||||
'success': _('Created new build'),
|
||||
}
|
||||
|
||||
|
||||
@ -415,12 +415,12 @@ class BuildUpdate(AjaxUpdateView):
|
||||
model = Build
|
||||
form_class = forms.EditBuildForm
|
||||
context_object_name = 'build'
|
||||
ajax_form_title = 'Edit Build Details'
|
||||
ajax_form_title = _('Edit Build Details')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'info': 'Edited build',
|
||||
'info': _('Edited build'),
|
||||
}
|
||||
|
||||
|
||||
@ -429,7 +429,7 @@ class BuildDelete(AjaxDeleteView):
|
||||
|
||||
model = Build
|
||||
ajax_template_name = 'build/delete_build.html'
|
||||
ajax_form_title = 'Delete Build'
|
||||
ajax_form_title = _('Delete Build')
|
||||
|
||||
|
||||
class BuildItemDelete(AjaxDeleteView):
|
||||
@ -439,12 +439,12 @@ class BuildItemDelete(AjaxDeleteView):
|
||||
|
||||
model = BuildItem
|
||||
ajax_template_name = 'build/delete_build_item.html'
|
||||
ajax_form_title = 'Unallocate Stock'
|
||||
ajax_form_title = _('Unallocate Stock')
|
||||
context_object_name = 'item'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': 'Removed parts from build allocation'
|
||||
'danger': _('Removed parts from build allocation')
|
||||
}
|
||||
|
||||
|
||||
@ -454,7 +454,7 @@ class BuildItemCreate(AjaxCreateView):
|
||||
model = BuildItem
|
||||
form_class = forms.EditBuildItemForm
|
||||
ajax_template_name = 'build/create_build_item.html'
|
||||
ajax_form_title = 'Allocate new Part'
|
||||
ajax_form_title = _('Allocate new Part')
|
||||
|
||||
part = None
|
||||
available_stock = None
|
||||
@ -570,11 +570,11 @@ class BuildItemEdit(AjaxUpdateView):
|
||||
model = BuildItem
|
||||
ajax_template_name = 'modal_form.html'
|
||||
form_class = forms.EditBuildItemForm
|
||||
ajax_form_title = 'Edit Stock Allocation'
|
||||
ajax_form_title = _('Edit Stock Allocation')
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'info': 'Updated Build Item',
|
||||
'info': _('Updated Build Item'),
|
||||
}
|
||||
|
||||
def get_form(self):
|
||||
|
@ -5,6 +5,8 @@ Django views for interacting with common models
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
|
||||
from . import models
|
||||
@ -16,7 +18,7 @@ class CurrencyCreate(AjaxCreateView):
|
||||
|
||||
model = models.Currency
|
||||
form_class = forms.CurrencyEditForm
|
||||
ajax_form_title = 'Create new Currency'
|
||||
ajax_form_title = _('Create new Currency')
|
||||
|
||||
|
||||
class CurrencyEdit(AjaxUpdateView):
|
||||
@ -24,12 +26,12 @@ class CurrencyEdit(AjaxUpdateView):
|
||||
|
||||
model = models.Currency
|
||||
form_class = forms.CurrencyEditForm
|
||||
ajax_form_title = 'Edit Currency'
|
||||
ajax_form_title = _('Edit Currency')
|
||||
|
||||
|
||||
class CurrencyDelete(AjaxDeleteView):
|
||||
""" View for deleting an existing Currency object """
|
||||
|
||||
model = models.Currency
|
||||
ajax_form_title = 'Delete Currency'
|
||||
ajax_form_title = _('Delete Currency')
|
||||
ajax_template_name = "common/delete_currency.html"
|
||||
|
@ -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 %}
|
68
InvenTree/company/templates/company/supplier_part_base.html
Normal file
68
InvenTree/company/templates/company/supplier_part_base.html
Normal 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 %}
|
@ -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 %}
|
@ -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 %}
|
@ -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 %}
|
72
InvenTree/company/templates/company/supplier_part_stock.html
Normal file
72
InvenTree/company/templates/company/supplier_part_stock.html
Normal 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 %}
|
16
InvenTree/company/templates/company/supplier_part_tabs.html
Normal file
16
InvenTree/company/templates/company/supplier_part_tabs.html
Normal 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>
|
@ -47,7 +47,11 @@ price_break_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'),
|
||||
]
|
||||
|
@ -6,6 +6,7 @@ Django views for interacting with Company app
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic import DetailView, ListView, UpdateView
|
||||
|
||||
from django.urls import reverse
|
||||
@ -93,12 +94,12 @@ class CompanyImage(AjaxUpdateView):
|
||||
""" View for uploading an image for the Company """
|
||||
model = Company
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Update Company Image'
|
||||
ajax_form_title = _('Update Company Image')
|
||||
form_class = CompanyImageForm
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': 'Updated company image',
|
||||
'success': _('Updated company image'),
|
||||
}
|
||||
|
||||
|
||||
@ -108,11 +109,11 @@ class CompanyEdit(AjaxUpdateView):
|
||||
form_class = EditCompanyForm
|
||||
context_object_name = 'company'
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Edit Company'
|
||||
ajax_form_title = _('Edit Company')
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'info': 'Edited company information',
|
||||
'info': _('Edited company information'),
|
||||
}
|
||||
|
||||
|
||||
@ -122,11 +123,11 @@ class CompanyCreate(AjaxCreateView):
|
||||
context_object_name = 'company'
|
||||
form_class = EditCompanyForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = "Create new Company"
|
||||
ajax_form_title = _("Create new Company")
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': "Created new company",
|
||||
'success': _("Created new company"),
|
||||
}
|
||||
|
||||
|
||||
@ -136,19 +137,19 @@ class CompanyDelete(AjaxDeleteView):
|
||||
model = Company
|
||||
success_url = '/company/'
|
||||
ajax_template_name = 'company/delete.html'
|
||||
ajax_form_title = 'Delete Company'
|
||||
ajax_form_title = _('Delete Company')
|
||||
context_object_name = 'company'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': 'Company was deleted',
|
||||
'danger': _('Company was deleted'),
|
||||
}
|
||||
|
||||
|
||||
class SupplierPartDetail(DetailView):
|
||||
""" Detail view for SupplierPart """
|
||||
model = SupplierPart
|
||||
template_name = 'company/partdetail.html'
|
||||
template_name = 'company/supplier_part_detail.html'
|
||||
context_object_name = 'part'
|
||||
queryset = SupplierPart.objects.all()
|
||||
|
||||
@ -166,7 +167,7 @@ class SupplierPartEdit(AjaxUpdateView):
|
||||
context_object_name = 'part'
|
||||
form_class = EditSupplierPartForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Edit Supplier Part'
|
||||
ajax_form_title = _('Edit Supplier Part')
|
||||
|
||||
|
||||
class SupplierPartCreate(AjaxCreateView):
|
||||
@ -175,7 +176,7 @@ class SupplierPartCreate(AjaxCreateView):
|
||||
model = SupplierPart
|
||||
form_class = EditSupplierPartForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Create new Supplier Part'
|
||||
ajax_form_title = _('Create new Supplier Part')
|
||||
context_object_name = 'part'
|
||||
|
||||
def get_form(self):
|
||||
@ -232,7 +233,7 @@ class SupplierPartDelete(AjaxDeleteView):
|
||||
|
||||
success_url = '/supplier/'
|
||||
ajax_template_name = 'company/partdelete.html'
|
||||
ajax_form_title = 'Delete Supplier Part'
|
||||
ajax_form_title = _('Delete Supplier Part')
|
||||
|
||||
parts = []
|
||||
|
||||
@ -302,7 +303,7 @@ class PriceBreakCreate(AjaxCreateView):
|
||||
|
||||
model = SupplierPriceBreak
|
||||
form_class = EditPriceBreakForm
|
||||
ajax_form_title = 'Add Price Break'
|
||||
ajax_form_title = _('Add Price Break')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
|
||||
def get_data(self):
|
||||
@ -337,7 +338,7 @@ class PriceBreakEdit(AjaxUpdateView):
|
||||
|
||||
model = SupplierPriceBreak
|
||||
form_class = EditPriceBreakForm
|
||||
ajax_form_title = 'Edit Price Break'
|
||||
ajax_form_title = _('Edit Price Break')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
|
||||
def get_form(self):
|
||||
@ -352,5 +353,5 @@ class PriceBreakDelete(AjaxDeleteView):
|
||||
""" View for deleting a supplier price break """
|
||||
|
||||
model = SupplierPriceBreak
|
||||
ajax_form_title = "Delete Price Break"
|
||||
ajax_form_title = _("Delete Price Break")
|
||||
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
@ -18,6 +18,7 @@ from InvenTree.status_codes import OrderStatus
|
||||
import os
|
||||
|
||||
from part.models import Part
|
||||
from company.models import SupplierPart
|
||||
|
||||
from .models import PurchaseOrder, PurchaseOrderLineItem
|
||||
from .serializers import POSerializer, POLineItemSerializer
|
||||
@ -62,6 +63,14 @@ class POList(generics.ListCreateAPIView):
|
||||
except (Part.DoesNotExist, ValueError):
|
||||
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(
|
||||
'pk',
|
||||
'supplier',
|
||||
|
@ -96,7 +96,7 @@ class PurchaseOrderCreate(AjaxCreateView):
|
||||
""" View for creating a new PurchaseOrder object using a modal form """
|
||||
|
||||
model = PurchaseOrder
|
||||
ajax_form_title = "Create Purchase Order"
|
||||
ajax_form_title = _("Create Purchase Order")
|
||||
form_class = order_forms.EditPurchaseOrderForm
|
||||
|
||||
def get_initial(self):
|
||||
@ -126,7 +126,7 @@ class PurchaseOrderEdit(AjaxUpdateView):
|
||||
""" View for editing a PurchaseOrder using a modal form """
|
||||
|
||||
model = PurchaseOrder
|
||||
ajax_form_title = 'Edit Purchase Order'
|
||||
ajax_form_title = _('Edit Purchase Order')
|
||||
form_class = order_forms.EditPurchaseOrderForm
|
||||
|
||||
def get_form(self):
|
||||
@ -146,7 +146,7 @@ class PurchaseOrderCancel(AjaxUpdateView):
|
||||
""" View for cancelling a purchase order """
|
||||
|
||||
model = PurchaseOrder
|
||||
ajax_form_title = 'Cancel Order'
|
||||
ajax_form_title = _('Cancel Order')
|
||||
ajax_template_name = 'order/order_cancel.html'
|
||||
form_class = order_forms.CancelPurchaseOrderForm
|
||||
|
||||
@ -179,7 +179,7 @@ class PurchaseOrderIssue(AjaxUpdateView):
|
||||
""" View for changing a purchase order from 'PENDING' to 'ISSUED' """
|
||||
|
||||
model = PurchaseOrder
|
||||
ajax_form_title = 'Issue Order'
|
||||
ajax_form_title = _('Issue Order')
|
||||
ajax_template_name = "order/order_issue.html"
|
||||
form_class = order_forms.IssuePurchaseOrderForm
|
||||
|
||||
@ -215,7 +215,7 @@ class PurchaseOrderComplete(AjaxUpdateView):
|
||||
form_class = order_forms.CompletePurchaseOrderForm
|
||||
model = PurchaseOrder
|
||||
ajax_template_name = "order/order_complete.html"
|
||||
ajax_form_title = "Complete Order"
|
||||
ajax_form_title = _("Complete Order")
|
||||
context_object_name = 'order'
|
||||
|
||||
def get_context_data(self):
|
||||
@ -281,7 +281,7 @@ class PurchaseOrderReceive(AjaxUpdateView):
|
||||
"""
|
||||
|
||||
form_class = order_forms.ReceivePurchaseOrderForm
|
||||
ajax_form_title = "Receive Parts"
|
||||
ajax_form_title = _("Receive Parts")
|
||||
ajax_template_name = "order/receive_parts.html"
|
||||
|
||||
# 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'
|
||||
|
||||
# List of Parts we wish to order
|
||||
@ -744,7 +744,7 @@ class POLineItemCreate(AjaxCreateView):
|
||||
model = PurchaseOrderLineItem
|
||||
context_object_name = 'line'
|
||||
form_class = order_forms.EditPurchaseOrderLineItemForm
|
||||
ajax_form_title = 'Add Line Item'
|
||||
ajax_form_title = _('Add Line Item')
|
||||
|
||||
def post(self, request, *arg, **kwargs):
|
||||
|
||||
@ -859,7 +859,7 @@ class POLineItemEdit(AjaxUpdateView):
|
||||
model = PurchaseOrderLineItem
|
||||
form_class = order_forms.EditPurchaseOrderLineItemForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Edit Line Item'
|
||||
ajax_form_title = _('Edit Line Item')
|
||||
|
||||
def get_form(self):
|
||||
form = super().get_form()
|
||||
@ -875,10 +875,10 @@ class POLineItemDelete(AjaxDeleteView):
|
||||
"""
|
||||
|
||||
model = PurchaseOrderLineItem
|
||||
ajax_form_title = 'Delete Line Item'
|
||||
ajax_form_title = _('Delete Line Item')
|
||||
ajax_template_name = 'order/po_lineitem_delete.html'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': 'Deleted line item',
|
||||
'danger': _('Deleted line item'),
|
||||
}
|
||||
|
@ -131,11 +131,13 @@ class BomItemResource(ModelResource):
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
@ -147,7 +149,12 @@ class BomItemResource(ModelResource):
|
||||
report_skipped = False
|
||||
clean_model_instances = True
|
||||
|
||||
exclude = ['checksum', ]
|
||||
exclude = [
|
||||
'checksum',
|
||||
'id',
|
||||
'part',
|
||||
'sub_part',
|
||||
]
|
||||
|
||||
|
||||
class BomItemAdmin(ImportExportModelAdmin):
|
||||
|
@ -53,12 +53,18 @@ def ExportBom(part, fmt='csv', cascade=False):
|
||||
|
||||
bom_items = []
|
||||
|
||||
uids = []
|
||||
|
||||
def add_items(items, level):
|
||||
# Add items at a given layer
|
||||
for item in items:
|
||||
|
||||
item.level = '-' * level
|
||||
|
||||
# Avoid circular BOM references
|
||||
if item.pk in uids:
|
||||
continue
|
||||
|
||||
bom_items.append(item)
|
||||
|
||||
if item.sub_part.assembly:
|
||||
|
@ -35,6 +35,7 @@ from InvenTree import helpers
|
||||
from InvenTree import validators
|
||||
from InvenTree.models import InvenTreeTree
|
||||
from InvenTree.fields import InvenTreeURLField
|
||||
from InvenTree.helpers import decimal2string
|
||||
|
||||
from InvenTree.status_codes import BuildStatus, StockStatus, OrderStatus
|
||||
|
||||
@ -1242,11 +1243,11 @@ class BomItem(models.Model):
|
||||
|
||||
pmin, pmax = prange
|
||||
|
||||
# remove trailing zeros
|
||||
pmin = pmin.normalize()
|
||||
pmax = pmax.normalize()
|
||||
|
||||
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)
|
||||
|
@ -39,6 +39,9 @@
|
||||
{
|
||||
title: 'Allocated',
|
||||
sortable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
return parseFloat(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
|
@ -1,5 +1,6 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block details %}
|
||||
|
||||
{% include 'part/tabs.html' with tab='stock' %}
|
||||
@ -49,7 +50,7 @@
|
||||
|
||||
$("#stock-export").click(function() {
|
||||
launchModalForm("{% url 'stock-export-options' %}", {
|
||||
submit_text: "Export",
|
||||
submit_text: "{% trans 'Export' %}",
|
||||
success: function(response) {
|
||||
var url = "{% url 'stock-export' %}";
|
||||
|
||||
@ -71,14 +72,14 @@
|
||||
secondary: [
|
||||
{
|
||||
field: 'part',
|
||||
label: 'New Part',
|
||||
title: 'Create New Part',
|
||||
label: '{% trans "New Part" %}',
|
||||
title: '{% trans "Create New Part" %}',
|
||||
url: "{% url 'part-create' %}",
|
||||
},
|
||||
{
|
||||
field: 'supplier_part',
|
||||
label: 'New Supplier Part',
|
||||
title: 'Create new Supplier Part',
|
||||
label: '{% trans "New Supplier Part" %}',
|
||||
title: '{% trans "Create new Supplier Part" %}',
|
||||
url: "{% url 'supplier-part-create' %}",
|
||||
data: {
|
||||
part: {{ part.id }}
|
||||
@ -86,8 +87,8 @@
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
label: 'New Location',
|
||||
title: 'Create New Location',
|
||||
label: '{% trans "New Location" %}',
|
||||
title: '{% trans "Create New Location" %}',
|
||||
url: "{% url 'stock-location-create' %}",
|
||||
}
|
||||
]
|
||||
|
@ -53,6 +53,9 @@
|
||||
sortable: true,
|
||||
field: 'quantity',
|
||||
title: 'Uses',
|
||||
formatter: function(value, row, index, field) {
|
||||
return parseFloat(value);
|
||||
},
|
||||
}
|
||||
|
||||
],
|
||||
|
@ -27,7 +27,7 @@ def inrange(n, *args, **kwargs):
|
||||
@register.simple_tag()
|
||||
def multiply(x, y, *args, **kwargs):
|
||||
""" Multiply two numbers together """
|
||||
return x * y
|
||||
return decimal2string(x * y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
@ -40,7 +40,7 @@ def add(x, y, *args, **kwargs):
|
||||
def part_allocation_count(build, part, *args, **kwargs):
|
||||
""" Return the total number of <part> allocated to <build> """
|
||||
|
||||
return build.getAllocatedQuantity(part)
|
||||
return decimal2string(build.getAllocatedQuantity(part))
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Tests for the Part model
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.test import TestCase
|
||||
@ -16,7 +17,7 @@ class TemplateTagTest(TestCase):
|
||||
""" Tests for the custom template tag code """
|
||||
|
||||
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):
|
||||
self.assertEqual(type(inventree_extras.inventree_version()), str)
|
||||
|
@ -72,7 +72,7 @@ class PartAttachmentCreate(AjaxCreateView):
|
||||
"""
|
||||
model = PartAttachment
|
||||
form_class = part_forms.EditPartAttachmentForm
|
||||
ajax_form_title = "Add part attachment"
|
||||
ajax_form_title = _("Add part attachment")
|
||||
ajax_template_name = "modal_form.html"
|
||||
|
||||
def get_data(self):
|
||||
@ -115,7 +115,7 @@ class PartAttachmentEdit(AjaxUpdateView):
|
||||
model = PartAttachment
|
||||
form_class = part_forms.EditPartAttachmentForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = 'Edit attachment'
|
||||
ajax_form_title = _('Edit attachment')
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -134,13 +134,13 @@ class PartAttachmentDelete(AjaxDeleteView):
|
||||
""" View for deleting a PartAttachment """
|
||||
|
||||
model = PartAttachment
|
||||
ajax_form_title = "Delete Part Attachment"
|
||||
ajax_form_title = _("Delete Part Attachment")
|
||||
ajax_template_name = "part/attachment_delete.html"
|
||||
context_object_name = "attachment"
|
||||
|
||||
def get_data(self):
|
||||
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 """
|
||||
|
||||
ajax_template_name = 'part/set_category.html'
|
||||
ajax_form_title = 'Set Part Category'
|
||||
ajax_form_title = _('Set Part Category')
|
||||
form_class = part_forms.SetPartCategoryForm
|
||||
|
||||
category = None
|
||||
@ -231,7 +231,7 @@ class MakePartVariant(AjaxCreateView):
|
||||
model = Part
|
||||
form_class = part_forms.EditPartForm
|
||||
|
||||
ajax_form_title = 'Create Variant'
|
||||
ajax_form_title = _('Create Variant')
|
||||
ajax_template_name = 'part/variant_part.html'
|
||||
|
||||
def get_part_template(self):
|
||||
@ -301,7 +301,7 @@ class PartDuplicate(AjaxCreateView):
|
||||
model = Part
|
||||
form_class = part_forms.EditPartForm
|
||||
|
||||
ajax_form_title = "Duplicate Part"
|
||||
ajax_form_title = _("Duplicate Part")
|
||||
ajax_template_name = "part/copy_part.html"
|
||||
|
||||
def get_data(self):
|
||||
@ -592,7 +592,7 @@ class PartDetail(DetailView):
|
||||
class PartQRCode(QRCodeView):
|
||||
""" 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):
|
||||
""" Generate QR code data for the Part """
|
||||
|
@ -301,6 +301,7 @@ class StockList(generics.ListCreateAPIView):
|
||||
'part__category',
|
||||
'part__category__name',
|
||||
'part__category__description',
|
||||
'supplier_part',
|
||||
)
|
||||
|
||||
# Reduce the number of lookups we need to do for categories
|
||||
@ -371,7 +372,13 @@ class StockList(generics.ListCreateAPIView):
|
||||
except PartCategory.DoesNotExist:
|
||||
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)
|
||||
|
||||
if supplier_id:
|
||||
|
@ -35,7 +35,9 @@
|
||||
<hr>
|
||||
<div class='panel panel-default'>
|
||||
<div class='panel-content'>
|
||||
{% if item.notes %}
|
||||
{{ item.notes | markdownify }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -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 %}
|
@ -24,7 +24,7 @@ from InvenTree.helpers import ExtractSerialNumbers
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from datetime import datetime
|
||||
|
||||
from company.models import Company
|
||||
from company.models import Company, SupplierPart
|
||||
from part.models import Part
|
||||
from .models import StockItem, StockLocation, StockItemTracking
|
||||
|
||||
@ -212,6 +212,16 @@ class StockExport(AjaxView):
|
||||
except (ValueError, Company.DoesNotExist):
|
||||
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
|
||||
part_id = request.GET.get('part', None)
|
||||
part = None
|
||||
@ -244,7 +254,11 @@ class StockExport(AjaxView):
|
||||
if 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'
|
||||
# TODO - This might need some more thought in the future...
|
||||
stock_items = stock_items.filter(customer=None)
|
||||
stock_items = stock_items.filter(belongs_to=None)
|
||||
|
||||
@ -816,6 +830,11 @@ class StockItemCreate(AjaxCreateView):
|
||||
|
||||
part_id = self.request.GET.get('part', 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
|
||||
if part_id:
|
||||
@ -824,14 +843,27 @@ class StockItemCreate(AjaxCreateView):
|
||||
initials['part'] = part
|
||||
initials['location'] = part.get_default_location()
|
||||
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
|
||||
|
||||
# Location has been specified
|
||||
if loc_id:
|
||||
try:
|
||||
initials['location'] = StockLocation.objects.get(pk=loc_id)
|
||||
except StockLocation.DoesNotExist:
|
||||
location = StockLocation.objects.get(pk=loc_id)
|
||||
initials['location'] = location
|
||||
except (ValueError, StockLocation.DoesNotExist):
|
||||
pass
|
||||
|
||||
return initials
|
||||
|
@ -1,18 +1,18 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button class='btn btn-default' id='stock-export' title='Export Stock Information'>Export</button>
|
||||
{% if not part or part.is_template == False %}
|
||||
<button class="btn btn-success" id='item-create'>New Stock Item</button>
|
||||
{% endif %}
|
||||
<button class='btn btn-default' id='stock-export' title='Export Stock Information'>{% trans "Export" %}</button>
|
||||
<button class="btn btn-success" id='item-create'>{% trans "New Stock Item" %}</button>
|
||||
<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">
|
||||
<li><a href="#" id='multi-item-add' title='Add to selected stock items'>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-stocktake' title='Stocktake selected stock items'>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-order' title='Order selected items'>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-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'>{% trans "Remove 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'>{% trans "Move 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'>{% trans "Delete Stock" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
Django==2.2.9 # Django package
|
||||
Django==2.2.10 # Django package
|
||||
pillow==6.2.0 # Image manipulation
|
||||
djangorestframework==3.10.3 # DRF framework
|
||||
django-cors-headers==3.2.0 # CORS headers extension for DRF
|
||||
|
Loading…
x
Reference in New Issue
Block a user