mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 03:00:54 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
@ -338,11 +338,6 @@ class StockList(generics.ListCreateAPIView):
|
||||
|
||||
queryset = self.filter_queryset(self.get_queryset())
|
||||
|
||||
page = self.paginate_queryset(queryset)
|
||||
if page is not None:
|
||||
serializer = self.get_serializer(page, many=True)
|
||||
return self.get_paginated_response(serializer.data)
|
||||
|
||||
serializer = self.get_serializer(queryset, many=True)
|
||||
|
||||
data = serializer.data
|
||||
@ -363,6 +358,7 @@ class StockList(generics.ListCreateAPIView):
|
||||
part_ids.add(part)
|
||||
|
||||
sp = item['supplier_part']
|
||||
|
||||
if sp:
|
||||
supplier_part_ids.add(sp)
|
||||
|
||||
@ -434,6 +430,7 @@ class StockList(generics.ListCreateAPIView):
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
|
||||
queryset = super().get_queryset(*args, **kwargs)
|
||||
|
||||
queryset = StockItemSerializer.prefetch_queryset(queryset)
|
||||
queryset = StockItemSerializer.annotate_queryset(queryset)
|
||||
|
||||
|
@ -178,6 +178,37 @@ class SerializeStockForm(HelperForm):
|
||||
]
|
||||
|
||||
|
||||
class StockItemLabelSelectForm(HelperForm):
|
||||
""" Form for selecting a label template for a StockItem """
|
||||
|
||||
label = forms.ChoiceField(
|
||||
label=_('Label'),
|
||||
help_text=_('Select test report template')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = StockItem
|
||||
fields = [
|
||||
'label',
|
||||
]
|
||||
|
||||
def get_label_choices(self, labels):
|
||||
|
||||
choices = []
|
||||
|
||||
if len(labels) > 0:
|
||||
for label in labels:
|
||||
choices.append((label.pk, label))
|
||||
|
||||
return choices
|
||||
|
||||
def __init__(self, labels, *args, **kwargs):
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['label'].choices = self.get_label_choices(labels)
|
||||
|
||||
|
||||
class TestReportFormatForm(HelperForm):
|
||||
""" Form for selection a test report template """
|
||||
|
||||
|
@ -45,16 +45,17 @@ class StockLocation(InvenTreeTree):
|
||||
def get_absolute_url(self):
|
||||
return reverse('stock-location-detail', kwargs={'pk': self.id})
|
||||
|
||||
def format_barcode(self):
|
||||
def format_barcode(self, **kwargs):
|
||||
""" Return a JSON string for formatting a barcode for this StockLocation object """
|
||||
|
||||
return helpers.MakeBarcode(
|
||||
'stocklocation',
|
||||
self.pk,
|
||||
{
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"url": reverse('api-location-detail', kwargs={'pk': self.id}),
|
||||
}
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
def get_stock_items(self, cascade=True):
|
||||
@ -283,7 +284,7 @@ class StockItem(MPTTModel):
|
||||
def get_part_name(self):
|
||||
return self.part.full_name
|
||||
|
||||
def format_barcode(self):
|
||||
def format_barcode(self, **kwargs):
|
||||
""" Return a JSON string for formatting a barcode for this StockItem.
|
||||
Can be used to perform lookup of a stockitem using barcode
|
||||
|
||||
@ -296,10 +297,11 @@ class StockItem(MPTTModel):
|
||||
|
||||
return helpers.MakeBarcode(
|
||||
"stockitem",
|
||||
self.id,
|
||||
{
|
||||
"id": self.id,
|
||||
"url": reverse('api-stock-detail', kwargs={'pk': self.id}),
|
||||
}
|
||||
},
|
||||
**kwargs
|
||||
)
|
||||
|
||||
uid = models.CharField(blank=True, max_length=128, help_text=("Unique identifier field"))
|
||||
|
@ -99,15 +99,34 @@ class StockItemSerializer(InvenTreeModelSerializer):
|
||||
|
||||
return queryset
|
||||
|
||||
belongs_to = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
build_order = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
customer = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
location = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
in_stock = serializers.BooleanField(read_only=True)
|
||||
|
||||
sales_order = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
|
||||
supplier_part = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
supplier_part_detail = SupplierPartSerializer(source='supplier_part', many=False, read_only=True)
|
||||
|
||||
part = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||
|
||||
location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
|
||||
|
||||
tracking_items = serializers.IntegerField(source='tracking_info_count', read_only=True, required=False)
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
allocated = serializers.FloatField(source='allocation_count', required=False)
|
||||
|
||||
serial = serializers.IntegerField(required=False)
|
||||
@ -140,9 +159,9 @@ class StockItemSerializer(InvenTreeModelSerializer):
|
||||
fields = [
|
||||
'allocated',
|
||||
'batch',
|
||||
'build_order',
|
||||
'belongs_to',
|
||||
'customer',
|
||||
'build_order',
|
||||
'in_stock',
|
||||
'link',
|
||||
'location',
|
||||
@ -155,10 +174,10 @@ class StockItemSerializer(InvenTreeModelSerializer):
|
||||
'required_tests',
|
||||
'sales_order',
|
||||
'serial',
|
||||
'supplier_part',
|
||||
'supplier_part_detail',
|
||||
'status',
|
||||
'status_text',
|
||||
'supplier_part',
|
||||
'supplier_part_detail',
|
||||
'tracking_items',
|
||||
'uid',
|
||||
]
|
||||
|
@ -78,7 +78,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
||||
<button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
<li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li>
|
||||
<li class='disabled'><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||
<li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||
{% if item.uid %}
|
||||
<li><a href='#' id='unlink-barcode'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li>
|
||||
{% else %}
|
||||
@ -126,7 +126,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
||||
</div>
|
||||
{% if item.part.has_test_report_templates %}
|
||||
<button type='button' class='btn btn-default' id='stock-test-report' title='{% trans "Generate test report" %}'>
|
||||
<span class='fas fa-tasks'/>
|
||||
<span class='fas fa-file-invoice'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
@ -314,6 +314,15 @@ $("#stock-test-report").click(function() {
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$("#print-label").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'stock-item-label-select' item.id %}",
|
||||
{
|
||||
follow: true,
|
||||
}
|
||||
)
|
||||
});
|
||||
|
||||
$("#stock-duplicate").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'stock-item-create' %}",
|
||||
|
@ -29,6 +29,7 @@ stock_item_detail_urls = [
|
||||
url(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'),
|
||||
|
||||
url(r'^test-report-select/', views.StockItemTestReportSelect.as_view(), name='stock-item-test-report-select'),
|
||||
url(r'^label-select/', views.StockItemSelectLabels.as_view(), name='stock-item-label-select'),
|
||||
|
||||
url(r'^test/', views.StockItemDetail.as_view(template_name='stock/item_tests.html'), name='stock-item-test-results'),
|
||||
url(r'^children/', views.StockItemDetail.as_view(template_name='stock/item_childs.html'), name='stock-item-children'),
|
||||
@ -59,6 +60,7 @@ stock_urls = [
|
||||
url(r'^item/new/?', views.StockItemCreate.as_view(), name='stock-item-create'),
|
||||
|
||||
url(r'^item/test-report-download/', views.StockItemTestReportDownload.as_view(), name='stock-item-test-report-download'),
|
||||
url(r'^item/print-stock-labels/', views.StockItemPrintLabels.as_view(), name='stock-item-print-labels'),
|
||||
|
||||
# URLs for StockItem attachments
|
||||
url(r'^item/attachment/', include([
|
||||
|
@ -28,6 +28,7 @@ from datetime import datetime
|
||||
from company.models import Company, SupplierPart
|
||||
from part.models import Part
|
||||
from report.models import TestReport
|
||||
from label.models import StockItemLabel
|
||||
from .models import StockItem, StockLocation, StockItemTracking, StockItemAttachment, StockItemTestResult
|
||||
|
||||
from .admin import StockItemResource
|
||||
@ -295,6 +296,88 @@ class StockItemReturnToStock(AjaxUpdateView):
|
||||
return self.renderJsonResponse(request, self.get_form(), data)
|
||||
|
||||
|
||||
class StockItemSelectLabels(AjaxView):
|
||||
"""
|
||||
View for selecting a template for printing labels for one (or more) StockItem objects
|
||||
"""
|
||||
|
||||
model = StockItem
|
||||
ajax_form_title = _('Select Label Template')
|
||||
|
||||
def get_form(self):
|
||||
|
||||
item = StockItem.objects.get(pk=self.kwargs['pk'])
|
||||
|
||||
labels = []
|
||||
|
||||
for label in StockItemLabel.objects.all():
|
||||
if label.matches_stock_item(item):
|
||||
labels.append(label)
|
||||
|
||||
return StockForms.StockItemLabelSelectForm(labels)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
|
||||
label = request.POST.get('label', None)
|
||||
|
||||
try:
|
||||
label = StockItemLabel.objects.get(pk=label)
|
||||
except (ValueError, StockItemLabel.DoesNotExist):
|
||||
raise ValidationError({'label': _("Select valid label")})
|
||||
|
||||
stock_item = StockItem.objects.get(pk=self.kwargs['pk'])
|
||||
|
||||
url = reverse('stock-item-print-labels')
|
||||
|
||||
url += '?label={pk}'.format(pk=label.pk)
|
||||
url += '&items[]={pk}'.format(pk=stock_item.pk)
|
||||
|
||||
data = {
|
||||
'form_valid': True,
|
||||
'url': url,
|
||||
}
|
||||
|
||||
return self.renderJsonResponse(request, self.get_form(), data=data)
|
||||
|
||||
|
||||
class StockItemPrintLabels(AjaxView):
|
||||
"""
|
||||
View for printing labels and returning a PDF
|
||||
|
||||
Requires the following arguments to be passed as URL params:
|
||||
|
||||
items: List of valid StockItem pk values
|
||||
label: Valid pk of a StockItemLabel template
|
||||
"""
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
label = request.GET.get('label', None)
|
||||
|
||||
try:
|
||||
label = StockItemLabel.objects.get(pk=label)
|
||||
except (ValueError, StockItemLabel.DoesNotExist):
|
||||
raise ValidationError({'label': 'Invalid label ID'})
|
||||
|
||||
item_pks = request.GET.getlist('items[]')
|
||||
|
||||
items = []
|
||||
|
||||
for pk in item_pks:
|
||||
try:
|
||||
item = StockItem.objects.get(pk=pk)
|
||||
items.append(item)
|
||||
except (ValueError, StockItem.DoesNotExist):
|
||||
pass
|
||||
|
||||
if len(items) == 0:
|
||||
raise ValidationError({'items': 'Must provide valid stockitems'})
|
||||
|
||||
pdf = label.render(items).getbuffer()
|
||||
|
||||
return DownloadFile(pdf, 'stock_labels.pdf', content_type='application/pdf')
|
||||
|
||||
|
||||
class StockItemDeleteTestData(AjaxUpdateView):
|
||||
"""
|
||||
View for deleting all test data
|
||||
|
Reference in New Issue
Block a user