mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add form for creating a new StockItem allocation
This commit is contained in:
parent
2972aec759
commit
6ab03bd05a
@ -78,16 +78,16 @@ function getImageUrlFromTransfer(transfer) {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeIconButton(icon, id, opts) {
|
function makeIconButton(icon, cls, pk, title) {
|
||||||
// Construct an 'icon button' using the fontawesome set
|
// Construct an 'icon button' using the fontawesome set
|
||||||
|
|
||||||
var options = opts || {};
|
var classes = `btn btn-default btn-glyph ${cls}`;
|
||||||
|
|
||||||
var title = options.title || '';
|
var id = `${cls}-${pk}`;
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
html += `<button id='${id}' class='btn btn-default btn-glyph' title='${title}'>`;
|
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}'>`;
|
||||||
html += `<span class='fas ${icon}'></span>`;
|
html += `<span class='fas ${icon}'></span>`;
|
||||||
html += `</button>`;
|
html += `</button>`;
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ from InvenTree.fields import RoundingDecimalFormField
|
|||||||
from stock.models import StockLocation
|
from stock.models import StockLocation
|
||||||
from .models import PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderAttachment
|
from .models import PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderAttachment
|
||||||
from .models import SalesOrder, SalesOrderLineItem, SalesOrderAttachment
|
from .models import SalesOrder, SalesOrderLineItem, SalesOrderAttachment
|
||||||
|
from .models import SalesOrderAllocation
|
||||||
|
|
||||||
|
|
||||||
class IssuePurchaseOrderForm(HelperForm):
|
class IssuePurchaseOrderForm(HelperForm):
|
||||||
@ -144,3 +145,16 @@ class EditSalesOrderLineItemForm(HelperForm):
|
|||||||
'reference',
|
'reference',
|
||||||
'notes'
|
'notes'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class EditSalesOrderAllocationForm(HelperForm):
|
||||||
|
|
||||||
|
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = SalesOrderAllocation
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'line',
|
||||||
|
'item',
|
||||||
|
'quantity']
|
||||||
|
@ -136,32 +136,78 @@ $("#so-lines-table").inventreeTable({
|
|||||||
field: 'buttons',
|
field: 'buttons',
|
||||||
formatter: function(value, row, index, field) {
|
formatter: function(value, row, index, field) {
|
||||||
|
|
||||||
var html = '';
|
var html = `<div class='btn-group float-right' role='group'>`;
|
||||||
|
|
||||||
var pk = row.pk;
|
var pk = row.pk;
|
||||||
|
|
||||||
if (row.part) {
|
if (row.part) {
|
||||||
var part = row.part_detail;
|
var part = row.part_detail;
|
||||||
|
|
||||||
html = `<div class='btn-group float-right' role='group'>`;
|
|
||||||
|
|
||||||
html += makeIconButton('fa-plus', `button-add-${pk}`);
|
|
||||||
|
|
||||||
if (part.purchaseable) {
|
if (part.purchaseable) {
|
||||||
html += makeIconButton('fa-shopping-cart', `button-buy-${pk}`);
|
html += makeIconButton('fa-shopping-cart', 'button-buy', pk, '{% trans "Buy parts" %}');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (part.assembly) {
|
if (part.assembly) {
|
||||||
html += makeIconButton('fa-tools', `button-build-${pk}`);
|
html += makeIconButton('fa-tools', 'button-build', pk, '{% trans "Build parts" %}');
|
||||||
}
|
}
|
||||||
|
|
||||||
html += `</div>`;
|
html += makeIconButton('fa-plus', 'button-add', pk, '{% trans "Allocate parts" %}');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html += makeIconButton('fa-edit', 'button-edit', pk, '{% trans "Edit line item" %}');
|
||||||
|
html += `</div>`;
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function reloadTable() {
|
||||||
|
$("#so-lines-table").bootstrapTable("refresh");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the table is loaded
|
||||||
|
$("#so-lines-table").on('load-success.bs.table', function() {
|
||||||
|
|
||||||
|
var table = $(this);
|
||||||
|
|
||||||
|
// Set up callbacks for the row buttons
|
||||||
|
table.find(".button-edit").click(function() {
|
||||||
|
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
launchModalForm(`/order/sales-order/line/${pk}/edit/`, {
|
||||||
|
success: reloadTable,
|
||||||
|
});
|
||||||
|
console.log("clicked!");
|
||||||
|
});
|
||||||
|
|
||||||
|
table.find(".button-add").click(function() {
|
||||||
|
console.log("add");
|
||||||
|
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
|
launchModalForm(`/order/sales-order/allocation/new/`, {
|
||||||
|
reload: table,
|
||||||
|
data: {
|
||||||
|
line: pk,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
table.find(".button-build").click(function() {
|
||||||
|
console.log("build");
|
||||||
|
|
||||||
|
var pk = $(this).attr('pk');
|
||||||
|
});
|
||||||
|
|
||||||
|
table.find(".button-buy").click(function() {
|
||||||
|
console.log("buy");
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -61,8 +61,12 @@ purchase_order_urls = [
|
|||||||
url(r'^.*$', views.PurchaseOrderIndex.as_view(), name='po-index'),
|
url(r'^.*$', views.PurchaseOrderIndex.as_view(), name='po-index'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
so_line_urls = [
|
so_line_urls = [
|
||||||
url(r'^new/', views.SOLineItemCreate.as_view(), name='so-line-item-create'),
|
url(r'^new/', views.SOLineItemCreate.as_view(), name='so-line-item-create'),
|
||||||
|
url(r'^(?P<pk>\d+)/', include([
|
||||||
|
url(r'^edit/', views.SOLineItemEdit.as_view(), name='so-line-item-edit')
|
||||||
|
])),
|
||||||
]
|
]
|
||||||
|
|
||||||
sales_order_attachment_urls = [
|
sales_order_attachment_urls = [
|
||||||
@ -88,6 +92,11 @@ sales_order_urls = [
|
|||||||
|
|
||||||
url(r'^line/', include(so_line_urls)),
|
url(r'^line/', include(so_line_urls)),
|
||||||
|
|
||||||
|
# URLs for sales order allocations
|
||||||
|
url(r'^allocation/', include([
|
||||||
|
url(r'^new/', views.SalesOrderAllocationCreate.as_view(), name='so-allocation-create'),
|
||||||
|
])),
|
||||||
|
|
||||||
url(r'^attachments/', include(sales_order_attachment_urls)),
|
url(r'^attachments/', include(sales_order_attachment_urls)),
|
||||||
|
|
||||||
# Display detail view for a single SalesOrder
|
# Display detail view for a single SalesOrder
|
||||||
|
@ -17,6 +17,7 @@ from decimal import Decimal, InvalidOperation
|
|||||||
|
|
||||||
from .models import PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderAttachment
|
from .models import PurchaseOrder, PurchaseOrderLineItem, PurchaseOrderAttachment
|
||||||
from .models import SalesOrder, SalesOrderLineItem, SalesOrderAttachment
|
from .models import SalesOrder, SalesOrderLineItem, SalesOrderAttachment
|
||||||
|
from .models import SalesOrderAllocation
|
||||||
from .admin import POLineItemResource
|
from .admin import POLineItemResource
|
||||||
from build.models import Build
|
from build.models import Build
|
||||||
from company.models import Company, SupplierPart
|
from company.models import Company, SupplierPart
|
||||||
@ -1114,6 +1115,22 @@ class SOLineItemCreate(AjaxCreateView):
|
|||||||
return initials
|
return initials
|
||||||
|
|
||||||
|
|
||||||
|
class SOLineItemEdit(AjaxUpdateView):
|
||||||
|
""" View for editing a SalesOrderLineItem """
|
||||||
|
|
||||||
|
model = SalesOrderLineItem
|
||||||
|
form_class = order_forms.EditSalesOrderLineItemForm
|
||||||
|
ajax_form_title = _('Edit Line Item')
|
||||||
|
|
||||||
|
def get_form(self):
|
||||||
|
form = super().get_form()
|
||||||
|
|
||||||
|
form.fields.pop('order')
|
||||||
|
form.fields.pop('part')
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
|
||||||
class POLineItemEdit(AjaxUpdateView):
|
class POLineItemEdit(AjaxUpdateView):
|
||||||
""" View for editing a PurchaseOrderLineItem object in a modal form.
|
""" View for editing a PurchaseOrderLineItem object in a modal form.
|
||||||
"""
|
"""
|
||||||
@ -1144,3 +1161,51 @@ class POLineItemDelete(AjaxDeleteView):
|
|||||||
return {
|
return {
|
||||||
'danger': _('Deleted line item'),
|
'danger': _('Deleted line item'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class SalesOrderAllocationCreate(AjaxCreateView):
|
||||||
|
""" View for creating a new SalesOrderAllocation """
|
||||||
|
|
||||||
|
model = SalesOrderAllocation
|
||||||
|
form_class = order_forms.EditSalesOrderAllocationForm
|
||||||
|
ajax_form_title = _('Allocate Stock to Order')
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
initials = super().get_initial().copy()
|
||||||
|
|
||||||
|
line = self.request.GET.get('line', None)
|
||||||
|
|
||||||
|
if line is not None:
|
||||||
|
initials['line'] = SalesOrderLineItem.objects.get(pk=line)
|
||||||
|
|
||||||
|
return initials
|
||||||
|
|
||||||
|
def get_form(self):
|
||||||
|
|
||||||
|
form = super().get_form()
|
||||||
|
|
||||||
|
line_id = form['line'].value()
|
||||||
|
|
||||||
|
# If a line item has been specified, reduce the queryset for the stockitem accordingly
|
||||||
|
try:
|
||||||
|
line = SalesOrderLineItem.objects.get(pk=line_id)
|
||||||
|
|
||||||
|
queryset = form.fields['item'].queryset
|
||||||
|
|
||||||
|
# Ensure the part reference matches
|
||||||
|
queryset = queryset.filter(part=line.part)
|
||||||
|
|
||||||
|
# Exclude StockItem which are already allocated to this order
|
||||||
|
allocated = [allocation.item.pk for allocation in line.allocations.all()]
|
||||||
|
|
||||||
|
queryset = queryset.exclude(pk__in=allocated)
|
||||||
|
|
||||||
|
form.fields['item'].queryset = queryset
|
||||||
|
|
||||||
|
# Hide the 'line' field
|
||||||
|
form.fields['line'].widget = HiddenInput()
|
||||||
|
|
||||||
|
except KeyError: # (ValueError, SalesOrderLineItem.DoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
|
return form
|
||||||
|
Loading…
Reference in New Issue
Block a user