mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Mark a SalesOrder as "shipped"
- Option to hide non-stock items from stock list - Update models with new feature
This commit is contained in:
parent
c5b93e2392
commit
b351976ae9
@ -336,6 +336,10 @@
|
|||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
.panel-heading .badge {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
float: right;
|
float: right;
|
||||||
background-color: #777;
|
background-color: #777;
|
||||||
|
@ -48,6 +48,7 @@ function loadStockTable(table, options) {
|
|||||||
|
|
||||||
options.params['part_detail'] = true;
|
options.params['part_detail'] = true;
|
||||||
options.params['location_detail'] = true;
|
options.params['location_detail'] = true;
|
||||||
|
options.params['in_stock'] = true;
|
||||||
|
|
||||||
var params = options.params || {};
|
var params = options.params || {};
|
||||||
|
|
||||||
|
@ -315,10 +315,15 @@ class SalesOrder(Order):
|
|||||||
def ship_order(self, user):
|
def ship_order(self, user):
|
||||||
""" Mark this order as 'shipped' """
|
""" Mark this order as 'shipped' """
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
# The order can only be 'shipped' if the current status is PENDING
|
||||||
if not self.status == SalesOrderStatus.PENDING:
|
if not self.status == SalesOrderStatus.PENDING:
|
||||||
return False
|
raise ValidationError({'status': _("SalesOrder cannot be shipped as it is not currently pending")})
|
||||||
|
|
||||||
|
# Complete the allocation for each allocated StockItem
|
||||||
|
for line in self.lines.all():
|
||||||
|
for allocation in line.allocations.all():
|
||||||
|
allocation.complete_allocation(user)
|
||||||
|
|
||||||
# Ensure the order status is marked as "Shipped"
|
# Ensure the order status is marked as "Shipped"
|
||||||
self.status = SalesOrderStatus.SHIPPED
|
self.status = SalesOrderStatus.SHIPPED
|
||||||
@ -552,3 +557,30 @@ class SalesOrderAllocation(models.Model):
|
|||||||
return self.item.location.pathstring
|
return self.item.location.pathstring
|
||||||
else:
|
else:
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
def complete_allocation(self, user):
|
||||||
|
"""
|
||||||
|
Complete this allocation (called when the parent SalesOrder is marked as "shipped"):
|
||||||
|
|
||||||
|
- Determine if the referenced StockItem needs to be "split" (if allocated quantity != stock quantity)
|
||||||
|
- Mark the StockItem as belonging to the Customer (this will remove it from stock)
|
||||||
|
"""
|
||||||
|
|
||||||
|
item = self.item
|
||||||
|
|
||||||
|
# If the allocated quantity is less than the amount available,
|
||||||
|
# then split the stock item into two lots
|
||||||
|
if item.quantity > self.quantity:
|
||||||
|
|
||||||
|
# Grab a copy of the new stock item (which will keep track of its "parent")
|
||||||
|
item = item.splitStock(self.quantity, None, user)
|
||||||
|
|
||||||
|
# Assign the StockItem to the SalesOrder customer
|
||||||
|
item.customer = self.line.order.customer
|
||||||
|
|
||||||
|
# Clear the location
|
||||||
|
item.location = None
|
||||||
|
|
||||||
|
item.save()
|
||||||
|
|
||||||
|
print("Finalizing allocation for: " + str(self.item))
|
||||||
|
@ -22,6 +22,8 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
|
<b>{% trans "Sales Order" %} {{ order.reference }} - {{ order.customer.name }}</b>
|
||||||
|
<br>
|
||||||
{% trans "Shipping this order means that the order will no longer be editable." %}
|
{% trans "Shipping this order means that the order will no longer be editable." %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -504,16 +504,14 @@ class SalesOrderShip(AjaxUpdateView):
|
|||||||
context_object_name = 'order'
|
context_object_name = 'order'
|
||||||
ajax_template_name = 'order/sales_order_ship.html'
|
ajax_template_name = 'order/sales_order_ship.html'
|
||||||
ajax_form_title = _('Ship Order')
|
ajax_form_title = _('Ship Order')
|
||||||
|
|
||||||
def context_data(self):
|
|
||||||
ctx = super().get_context_data()
|
|
||||||
ctx['order'] = self.get_object()
|
|
||||||
|
|
||||||
return ctx
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
|
self.request = request
|
||||||
|
|
||||||
order = self.get_object()
|
order = self.get_object()
|
||||||
|
self.object = order
|
||||||
|
|
||||||
form = self.get_form()
|
form = self.get_form()
|
||||||
|
|
||||||
confirm = str2bool(request.POST.get('confirm', False))
|
confirm = str2bool(request.POST.get('confirm', False))
|
||||||
@ -534,7 +532,11 @@ class SalesOrderShip(AjaxUpdateView):
|
|||||||
'form_valid': valid,
|
'form_valid': valid,
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.renderJsonResponse(request, form, data)
|
context = self.get_context_data()
|
||||||
|
|
||||||
|
context['order'] = order
|
||||||
|
|
||||||
|
return self.renderJsonResponse(request, form, data, context)
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExport(AjaxView):
|
class PurchaseOrderExport(AjaxView):
|
||||||
|
@ -363,8 +363,17 @@ class StockList(generics.ListCreateAPIView):
|
|||||||
# Start with all objects
|
# Start with all objects
|
||||||
stock_list = super().filter_queryset(queryset)
|
stock_list = super().filter_queryset(queryset)
|
||||||
|
|
||||||
# Filter out parts which are not actually "in stock"
|
in_stock = self.request.query_params.get('in_stock', None)
|
||||||
stock_list = stock_list.filter(customer=None, belongs_to=None)
|
|
||||||
|
if in_stock is not None:
|
||||||
|
in_stock = str2bool(in_stock)
|
||||||
|
|
||||||
|
if in_stock:
|
||||||
|
# Filter out parts which are not actually "in stock"
|
||||||
|
stock_list = stock_list.filter(customer=None, belongs_to=None)
|
||||||
|
else:
|
||||||
|
# Only show parts which are not in stock
|
||||||
|
stock_list = stock_list.exclude(customer=None, belongs_to=None)
|
||||||
|
|
||||||
# Filter by 'allocated' patrs?
|
# Filter by 'allocated' patrs?
|
||||||
allocated = self.request.query_params.get('allocated', None)
|
allocated = self.request.query_params.get('allocated', None)
|
||||||
@ -418,7 +427,7 @@ class StockList(generics.ListCreateAPIView):
|
|||||||
# Does the client wish to filter by stock location?
|
# Does the client wish to filter by stock location?
|
||||||
loc_id = self.request.query_params.get('location', None)
|
loc_id = self.request.query_params.get('location', None)
|
||||||
|
|
||||||
cascade = str2bool(self.request.query_params.get('cascade', False))
|
cascade = str2bool(self.request.query_params.get('cascade', True))
|
||||||
|
|
||||||
if loc_id is not None:
|
if loc_id is not None:
|
||||||
|
|
||||||
|
@ -634,6 +634,8 @@ class StockItem(MPTTModel):
|
|||||||
# Remove the specified quantity from THIS stock item
|
# Remove the specified quantity from THIS stock item
|
||||||
self.take_stock(quantity, user, 'Split {n} items into new stock item'.format(n=quantity))
|
self.take_stock(quantity, user, 'Split {n} items into new stock item'.format(n=quantity))
|
||||||
|
|
||||||
|
# Return a copy of the "new" stock item
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def move(self, location, notes, user, **kwargs):
|
def move(self, location, notes, user, **kwargs):
|
||||||
""" Move part to a new location.
|
""" Move part to a new location.
|
||||||
@ -656,6 +658,9 @@ class StockItem(MPTTModel):
|
|||||||
except InvalidOperation:
|
except InvalidOperation:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not self.in_stock:
|
||||||
|
raise ValidationError(_("StockItem cannot be moved as it is not in stock"))
|
||||||
|
|
||||||
if quantity <= 0:
|
if quantity <= 0:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -15,6 +15,12 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
|||||||
{% block pre_content %}
|
{% block pre_content %}
|
||||||
{% include 'stock/loc_link.html' with location=item.location %}
|
{% include 'stock/loc_link.html' with location=item.location %}
|
||||||
|
|
||||||
|
{% if item.customer %}
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% trans "This stock item has been sent to" %} <b><a href="{% url 'company-detail-sales-orders' item.customer.id %}">{{ item.customer.name }}</a></b>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% for allocation in item.sales_order_allocations.all %}
|
{% for allocation in item.sales_order_allocations.all %}
|
||||||
<div class='alert alert-block alert-info'>
|
<div class='alert alert-block alert-info'>
|
||||||
{% trans "This stock item is allocated to Sales Order" %} <a href="{% url 'so-detail' allocation.line.order.id %}"><b>#{{ allocation.line.order.reference }}</b></a> ({% trans "Quantity" %}: {% decimal allocation.quantity %})
|
{% trans "This stock item is allocated to Sales Order" %} <a href="{% url 'so-detail' allocation.line.order.id %}"><b>#{{ allocation.line.order.reference }}</b></a> ({% trans "Quantity" %}: {% decimal allocation.quantity %})
|
||||||
|
Loading…
Reference in New Issue
Block a user