mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #2110 from SchrodingersGat/sales-order-allocation-fixes
Sales order allocation fixes
This commit is contained in:
commit
8ed81bdb01
@ -10,12 +10,16 @@ import common.models
|
|||||||
|
|
||||||
INVENTREE_SW_VERSION = "0.6.0 dev"
|
INVENTREE_SW_VERSION = "0.6.0 dev"
|
||||||
|
|
||||||
INVENTREE_API_VERSION = 14
|
INVENTREE_API_VERSION = 15
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
Increment this API version number whenever there is a significant change to the API that any clients need to know about
|
||||||
|
|
||||||
v14 -> 2021-20-05
|
v15 -> 2021-10-06
|
||||||
|
- Adds detail endpoint for SalesOrderAllocation model
|
||||||
|
- Allows use of the API forms interface for adjusting SalesOrderAllocation objects
|
||||||
|
|
||||||
|
v14 -> 2021-10-05
|
||||||
- Stock adjustment actions API is improved, using native DRF serializer support
|
- Stock adjustment actions API is improved, using native DRF serializer support
|
||||||
- However adjustment actions now only support 'pk' as a lookup field
|
- However adjustment actions now only support 'pk' as a lookup field
|
||||||
|
|
||||||
|
@ -631,6 +631,15 @@ class SOLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = SOLineItemSerializer
|
serializer_class = SOLineItemSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class SOAllocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
"""
|
||||||
|
API endpoint for detali view of a SalesOrderAllocation object
|
||||||
|
"""
|
||||||
|
|
||||||
|
queryset = SalesOrderAllocation.objects.all()
|
||||||
|
serializer_class = SalesOrderAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SOAllocationList(generics.ListCreateAPIView):
|
class SOAllocationList(generics.ListCreateAPIView):
|
||||||
"""
|
"""
|
||||||
API endpoint for listing SalesOrderAllocation objects
|
API endpoint for listing SalesOrderAllocation objects
|
||||||
@ -743,8 +752,10 @@ order_api_urls = [
|
|||||||
])),
|
])),
|
||||||
|
|
||||||
# API endpoints for purchase order line items
|
# API endpoints for purchase order line items
|
||||||
url(r'^po-line/(?P<pk>\d+)/$', POLineItemDetail.as_view(), name='api-po-line-detail'),
|
url(r'^po-line/', include([
|
||||||
url(r'^po-line/$', POLineItemList.as_view(), name='api-po-line-list'),
|
url(r'^(?P<pk>\d+)/$', POLineItemDetail.as_view(), name='api-po-line-detail'),
|
||||||
|
url(r'^.*$', POLineItemList.as_view(), name='api-po-line-list'),
|
||||||
|
])),
|
||||||
|
|
||||||
# API endpoints for sales ordesr
|
# API endpoints for sales ordesr
|
||||||
url(r'^so/', include([
|
url(r'^so/', include([
|
||||||
@ -764,9 +775,8 @@ order_api_urls = [
|
|||||||
])),
|
])),
|
||||||
|
|
||||||
# API endpoints for sales order allocations
|
# API endpoints for sales order allocations
|
||||||
url(r'^so-allocation', include([
|
url(r'^so-allocation/', include([
|
||||||
|
url(r'^(?P<pk>\d+)/$', SOAllocationDetail.as_view(), name='api-so-allocation-detail'),
|
||||||
# List all sales order allocations
|
|
||||||
url(r'^.*$', SOAllocationList.as_view(), name='api-so-allocation-list'),
|
url(r'^.*$', SOAllocationList.as_view(), name='api-so-allocation-list'),
|
||||||
])),
|
])),
|
||||||
]
|
]
|
||||||
|
@ -115,23 +115,6 @@ class AllocateSerialsToSalesOrderForm(forms.Form):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CreateSalesOrderAllocationForm(HelperForm):
|
|
||||||
"""
|
|
||||||
Form for creating a SalesOrderAllocation item.
|
|
||||||
"""
|
|
||||||
|
|
||||||
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5, label=_('Quantity'))
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
model = SalesOrderAllocation
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'line',
|
|
||||||
'item',
|
|
||||||
'quantity',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class EditSalesOrderAllocationForm(HelperForm):
|
class EditSalesOrderAllocationForm(HelperForm):
|
||||||
"""
|
"""
|
||||||
Form for editing a SalesOrderAllocation item
|
Form for editing a SalesOrderAllocation item
|
||||||
|
@ -840,7 +840,13 @@ class SalesOrderLineItem(OrderLineItem):
|
|||||||
def get_api_url():
|
def get_api_url():
|
||||||
return reverse('api-so-line-list')
|
return reverse('api-so-line-list')
|
||||||
|
|
||||||
order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', verbose_name=_('Order'), help_text=_('Sales Order'))
|
order = models.ForeignKey(
|
||||||
|
SalesOrder,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='lines',
|
||||||
|
verbose_name=_('Order'),
|
||||||
|
help_text=_('Sales Order')
|
||||||
|
)
|
||||||
|
|
||||||
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, verbose_name=_('Part'), help_text=_('Part'), limit_choices_to={'salable': True})
|
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, verbose_name=_('Part'), help_text=_('Part'), limit_choices_to={'salable': True})
|
||||||
|
|
||||||
@ -954,7 +960,11 @@ class SalesOrderAllocation(models.Model):
|
|||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
raise ValidationError(errors)
|
raise ValidationError(errors)
|
||||||
|
|
||||||
line = models.ForeignKey(SalesOrderLineItem, on_delete=models.CASCADE, verbose_name=_('Line'), related_name='allocations')
|
line = models.ForeignKey(
|
||||||
|
SalesOrderLineItem,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_('Line'),
|
||||||
|
related_name='allocations')
|
||||||
|
|
||||||
item = models.ForeignKey(
|
item = models.ForeignKey(
|
||||||
'stock.StockItem',
|
'stock.StockItem',
|
||||||
|
@ -478,7 +478,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
|
part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
|
||||||
order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
|
order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
|
||||||
serial = serializers.CharField(source='get_serial', read_only=True)
|
serial = serializers.CharField(source='get_serial', read_only=True)
|
||||||
quantity = serializers.FloatField(read_only=True)
|
quantity = serializers.FloatField(read_only=False)
|
||||||
location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
|
location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
|
||||||
|
|
||||||
# Extra detail fields
|
# Extra detail fields
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
{% extends "modal_delete_form.html" %}
|
|
||||||
{% load i18n %}
|
|
||||||
{% load inventree_extras %}
|
|
||||||
|
|
||||||
{% block pre_form_content %}
|
|
||||||
<div class='alert alert-block alert-warning'>
|
|
||||||
{% trans "This action will unallocate the following stock from the Sales Order" %}:
|
|
||||||
<br>
|
|
||||||
<strong>
|
|
||||||
{% decimal allocation.get_allocated %} x {{ allocation.line.part.full_name }}
|
|
||||||
{% if allocation.item.location %} ({{ allocation.get_location }}){% endif %}
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
|
@ -43,12 +43,7 @@ sales_order_detail_urls = [
|
|||||||
sales_order_urls = [
|
sales_order_urls = [
|
||||||
# URLs for sales order allocations
|
# URLs for sales order allocations
|
||||||
url(r'^allocation/', include([
|
url(r'^allocation/', include([
|
||||||
url(r'^new/', views.SalesOrderAllocationCreate.as_view(), name='so-allocation-create'),
|
|
||||||
url(r'^assign-serials/', views.SalesOrderAssignSerials.as_view(), name='so-assign-serials'),
|
url(r'^assign-serials/', views.SalesOrderAssignSerials.as_view(), name='so-assign-serials'),
|
||||||
url(r'(?P<pk>\d+)/', include([
|
|
||||||
url(r'^edit/', views.SalesOrderAllocationEdit.as_view(), name='so-allocation-edit'),
|
|
||||||
url(r'^delete/', views.SalesOrderAllocationDelete.as_view(), name='so-allocation-delete'),
|
|
||||||
])),
|
|
||||||
])),
|
])),
|
||||||
|
|
||||||
# Display detail view for a single SalesOrder
|
# Display detail view for a single SalesOrder
|
||||||
|
@ -29,7 +29,6 @@ from company.models import Company, SupplierPart # ManufacturerPart
|
|||||||
from stock.models import StockItem
|
from stock.models import StockItem
|
||||||
from part.models import Part
|
from part.models import Part
|
||||||
|
|
||||||
from common.models import InvenTreeSetting
|
|
||||||
from common.forms import UploadFileForm, MatchFieldForm
|
from common.forms import UploadFileForm, MatchFieldForm
|
||||||
from common.views import FileManagementFormView
|
from common.views import FileManagementFormView
|
||||||
from common.files import FileManager
|
from common.files import FileManager
|
||||||
@ -37,7 +36,7 @@ from common.files import FileManager
|
|||||||
from . import forms as order_forms
|
from . import forms as order_forms
|
||||||
from part.views import PartPricing
|
from part.views import PartPricing
|
||||||
|
|
||||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
from InvenTree.views import AjaxView, AjaxUpdateView
|
||||||
from InvenTree.helpers import DownloadFile, str2bool
|
from InvenTree.helpers import DownloadFile, str2bool
|
||||||
from InvenTree.helpers import extract_serial_numbers
|
from InvenTree.helpers import extract_serial_numbers
|
||||||
from InvenTree.views import InvenTreeRoleMixin
|
from InvenTree.views import InvenTreeRoleMixin
|
||||||
@ -976,105 +975,6 @@ class SalesOrderAssignSerials(AjaxView, FormMixin):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationCreate(AjaxCreateView):
|
|
||||||
""" View for creating a new SalesOrderAllocation """
|
|
||||||
|
|
||||||
model = SalesOrderAllocation
|
|
||||||
form_class = order_forms.CreateSalesOrderAllocationForm
|
|
||||||
ajax_form_title = _('Allocate Stock to Order')
|
|
||||||
|
|
||||||
def get_initial(self):
|
|
||||||
initials = super().get_initial().copy()
|
|
||||||
|
|
||||||
line_id = self.request.GET.get('line', None)
|
|
||||||
|
|
||||||
if line_id is not None:
|
|
||||||
line = SalesOrderLineItem.objects.get(pk=line_id)
|
|
||||||
|
|
||||||
initials['line'] = line
|
|
||||||
|
|
||||||
# Search for matching stock items, pre-fill if there is only one
|
|
||||||
items = StockItem.objects.filter(part=line.part)
|
|
||||||
|
|
||||||
quantity = line.quantity - line.allocated_quantity()
|
|
||||||
|
|
||||||
if quantity < 0:
|
|
||||||
quantity = 0
|
|
||||||
|
|
||||||
if items.count() == 1:
|
|
||||||
item = items.first()
|
|
||||||
initials['item'] = item
|
|
||||||
|
|
||||||
# Reduce the quantity IF there is not enough stock
|
|
||||||
qmax = item.quantity - item.allocation_count()
|
|
||||||
|
|
||||||
if qmax < quantity:
|
|
||||||
quantity = qmax
|
|
||||||
|
|
||||||
initials['quantity'] = quantity
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
# Construct a queryset for allowable stock items
|
|
||||||
queryset = StockItem.objects.filter(StockItem.IN_STOCK_FILTER)
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Exclude stock items which have expired
|
|
||||||
if not InvenTreeSetting.get_setting('STOCK_ALLOW_EXPIRED_SALE'):
|
|
||||||
queryset = queryset.exclude(StockItem.EXPIRED_FILTER)
|
|
||||||
|
|
||||||
form.fields['item'].queryset = queryset
|
|
||||||
|
|
||||||
# Hide the 'line' field
|
|
||||||
form.fields['line'].widget = HiddenInput()
|
|
||||||
|
|
||||||
except (ValueError, SalesOrderLineItem.DoesNotExist):
|
|
||||||
pass
|
|
||||||
|
|
||||||
return form
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationEdit(AjaxUpdateView):
|
|
||||||
|
|
||||||
model = SalesOrderAllocation
|
|
||||||
form_class = order_forms.EditSalesOrderAllocationForm
|
|
||||||
ajax_form_title = _('Edit Allocation Quantity')
|
|
||||||
|
|
||||||
def get_form(self):
|
|
||||||
form = super().get_form()
|
|
||||||
|
|
||||||
# Prevent the user from editing particular fields
|
|
||||||
form.fields.pop('item')
|
|
||||||
form.fields.pop('line')
|
|
||||||
|
|
||||||
return form
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationDelete(AjaxDeleteView):
|
|
||||||
|
|
||||||
model = SalesOrderAllocation
|
|
||||||
ajax_form_title = _("Remove allocation")
|
|
||||||
context_object_name = 'allocation'
|
|
||||||
ajax_template_name = "order/so_allocation_delete.html"
|
|
||||||
|
|
||||||
|
|
||||||
class LineItemPricing(PartPricing):
|
class LineItemPricing(PartPricing):
|
||||||
""" View for inspecting part pricing information """
|
""" View for inspecting part pricing information """
|
||||||
|
|
||||||
|
@ -34,6 +34,7 @@ from company.models import Company, SupplierPart
|
|||||||
from company.serializers import CompanySerializer, SupplierPartSerializer
|
from company.serializers import CompanySerializer, SupplierPartSerializer
|
||||||
|
|
||||||
from order.models import PurchaseOrder
|
from order.models import PurchaseOrder
|
||||||
|
from order.models import SalesOrder, SalesOrderAllocation
|
||||||
from order.serializers import POSerializer
|
from order.serializers import POSerializer
|
||||||
|
|
||||||
import common.settings
|
import common.settings
|
||||||
@ -645,6 +646,31 @@ class StockList(generics.ListCreateAPIView):
|
|||||||
# Filter StockItem without build allocations or sales order allocations
|
# Filter StockItem without build allocations or sales order allocations
|
||||||
queryset = queryset.filter(Q(sales_order_allocations__isnull=True) & Q(allocations__isnull=True))
|
queryset = queryset.filter(Q(sales_order_allocations__isnull=True) & Q(allocations__isnull=True))
|
||||||
|
|
||||||
|
# Exclude StockItems which are already allocated to a particular SalesOrder
|
||||||
|
exclude_so_allocation = params.get('exclude_so_allocation', None)
|
||||||
|
|
||||||
|
if exclude_so_allocation is not None:
|
||||||
|
|
||||||
|
try:
|
||||||
|
order = SalesOrder.objects.get(pk=exclude_so_allocation)
|
||||||
|
|
||||||
|
# Grab all the active SalesOrderAllocations for this order
|
||||||
|
allocations = SalesOrderAllocation.objects.filter(
|
||||||
|
line__pk__in=[
|
||||||
|
line.pk for line in order.lines.all()
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Exclude any stock item which is already allocated to the sales order
|
||||||
|
queryset = queryset.exclude(
|
||||||
|
pk__in=[
|
||||||
|
a.item.pk for a in allocations
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
except (ValueError, SalesOrder.DoesNotExist):
|
||||||
|
pass
|
||||||
|
|
||||||
# Does the client wish to filter by the Part ID?
|
# Does the client wish to filter by the Part ID?
|
||||||
part_id = params.get('part', None)
|
part_id = params.get('part', None)
|
||||||
|
|
||||||
|
@ -532,6 +532,7 @@ function editPurchaseOrderLineItem(e) {
|
|||||||
|
|
||||||
var url = $(src).attr('url');
|
var url = $(src).attr('url');
|
||||||
|
|
||||||
|
// TODO: Migrate this to the API forms
|
||||||
launchModalForm(url, {
|
launchModalForm(url, {
|
||||||
reload: true,
|
reload: true,
|
||||||
});
|
});
|
||||||
@ -548,6 +549,7 @@ function removePurchaseOrderLineItem(e) {
|
|||||||
|
|
||||||
var url = $(src).attr('url');
|
var url = $(src).attr('url');
|
||||||
|
|
||||||
|
// TODO: Migrate this to the API forms
|
||||||
launchModalForm(url, {
|
launchModalForm(url, {
|
||||||
reload: true,
|
reload: true,
|
||||||
});
|
});
|
||||||
@ -1151,31 +1153,44 @@ function showAllocationSubTable(index, row, element, options) {
|
|||||||
// Is the parent SalesOrder pending?
|
// Is the parent SalesOrder pending?
|
||||||
var pending = options.status == {{ SalesOrderStatus.PENDING }};
|
var pending = options.status == {{ SalesOrderStatus.PENDING }};
|
||||||
|
|
||||||
// Function to reload the allocation table
|
|
||||||
function reloadTable() {
|
|
||||||
table.bootstrapTable('refresh');
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupCallbacks() {
|
function setupCallbacks() {
|
||||||
// Add callbacks for 'edit' buttons
|
// Add callbacks for 'edit' buttons
|
||||||
table.find('.button-allocation-edit').click(function() {
|
table.find('.button-allocation-edit').click(function() {
|
||||||
|
|
||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
// TODO: Migrate to API forms
|
// Edit the sales order alloction
|
||||||
launchModalForm(`/order/sales-order/allocation/${pk}/edit/`, {
|
constructForm(
|
||||||
success: reloadTable,
|
`/api/order/so-allocation/${pk}/`,
|
||||||
});
|
{
|
||||||
|
fields: {
|
||||||
|
quantity: {},
|
||||||
|
},
|
||||||
|
title: '{% trans "Edit Stock Allocation" %}',
|
||||||
|
onSuccess: function() {
|
||||||
|
// Refresh the parent table
|
||||||
|
$(options.table).bootstrapTable('refresh');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add callbacks for 'delete' buttons
|
// Add callbacks for 'delete' buttons
|
||||||
table.find('.button-allocation-delete').click(function() {
|
table.find('.button-allocation-delete').click(function() {
|
||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
// TODO: Migrate to API forms
|
constructForm(
|
||||||
launchModalForm(`/order/sales-order/allocation/${pk}/delete/`, {
|
`/api/order/so-allocation/${pk}/`,
|
||||||
success: reloadTable,
|
{
|
||||||
});
|
method: 'DELETE',
|
||||||
|
confirmMessage: '{% trans "Confirm Delete Operation" %}',
|
||||||
|
title: '{% trans "Delete Stock Allocation" %}',
|
||||||
|
onSuccess: function() {
|
||||||
|
// Refresh the parent table
|
||||||
|
$(options.table).bootstrapTable('refresh');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1308,6 +1323,8 @@ function showFulfilledSubTable(index, row, element, options) {
|
|||||||
*/
|
*/
|
||||||
function loadSalesOrderLineItemTable(table, options={}) {
|
function loadSalesOrderLineItemTable(table, options={}) {
|
||||||
|
|
||||||
|
options.table = table;
|
||||||
|
|
||||||
options.params = options.params || {};
|
options.params = options.params || {};
|
||||||
|
|
||||||
if (!options.order) {
|
if (!options.order) {
|
||||||
@ -1433,13 +1450,21 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
return formatter.format(total);
|
return formatter.format(total);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
field: 'stock',
|
|
||||||
title: '{% trans "In Stock" %}',
|
if (pending) {
|
||||||
formatter: function(value, row) {
|
columns.push(
|
||||||
return row.part_detail.stock;
|
{
|
||||||
|
field: 'stock',
|
||||||
|
title: '{% trans "In Stock" %}',
|
||||||
|
formatter: function(value, row) {
|
||||||
|
return row.part_detail.stock;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
columns.push(
|
||||||
{
|
{
|
||||||
field: 'allocated',
|
field: 'allocated',
|
||||||
title: pending ? '{% trans "Allocated" %}' : '{% trans "Fulfilled" %}',
|
title: pending ? '{% trans "Allocated" %}' : '{% trans "Fulfilled" %}',
|
||||||
@ -1470,29 +1495,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
field: 'notes',
|
field: 'notes',
|
||||||
title: '{% trans "Notes" %}',
|
title: '{% trans "Notes" %}',
|
||||||
},
|
},
|
||||||
// TODO: Re-introduce the "PO" field, once it is fixed
|
);
|
||||||
/*
|
|
||||||
{
|
|
||||||
field: 'po',
|
|
||||||
title: '{% trans "PO" %}',
|
|
||||||
formatter: function(value, row, index, field) {
|
|
||||||
var po_name = "";
|
|
||||||
if (row.allocated) {
|
|
||||||
row.allocations.forEach(function(allocation) {
|
|
||||||
if (allocation.po != po_name) {
|
|
||||||
if (po_name) {
|
|
||||||
po_name = "-";
|
|
||||||
} else {
|
|
||||||
po_name = allocation.po
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return `<div>` + po_name + `</div>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
*/
|
|
||||||
];
|
|
||||||
|
|
||||||
if (pending) {
|
if (pending) {
|
||||||
columns.push({
|
columns.push({
|
||||||
@ -1531,9 +1534,6 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
// Remove the "in stock" column
|
|
||||||
delete columns['stock'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function reloadTable() {
|
function reloadTable() {
|
||||||
@ -1595,13 +1595,41 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
$(table).find('.button-add').click(function() {
|
$(table).find('.button-add').click(function() {
|
||||||
var pk = $(this).attr('pk');
|
var pk = $(this).attr('pk');
|
||||||
|
|
||||||
// TODO: Migrate this form to the API forms
|
var line_item = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||||
launchModalForm(`/order/sales-order/allocation/new/`, {
|
|
||||||
success: reloadTable,
|
var fields = {
|
||||||
data: {
|
// SalesOrderLineItem reference
|
||||||
line: pk,
|
line: {
|
||||||
|
hidden: true,
|
||||||
|
value: pk,
|
||||||
},
|
},
|
||||||
});
|
item: {
|
||||||
|
filters: {
|
||||||
|
part_detail: true,
|
||||||
|
location_detail: true,
|
||||||
|
in_stock: true,
|
||||||
|
part: line_item.part,
|
||||||
|
exclude_so_allocation: options.order,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
quantity: {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exclude expired stock?
|
||||||
|
if (global_settings.STOCK_ENABLE_EXPIRY && !global_settings.STOCK_ALLOW_EXPIRED_SALE) {
|
||||||
|
fields.item.filters.expired = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
constructForm(
|
||||||
|
`/api/order/so-allocation/`,
|
||||||
|
{
|
||||||
|
method: 'POST',
|
||||||
|
fields: fields,
|
||||||
|
title: '{% trans "Allocate Stock Item" %}',
|
||||||
|
onSuccess: reloadTable,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Callback for creating a new build
|
// Callback for creating a new build
|
||||||
|
Loading…
Reference in New Issue
Block a user