Fix for SalesOrderLineItem allocation calculation

Also function to render a progress bar
This commit is contained in:
Oliver Walters 2020-04-21 16:45:44 +10:00
parent a1376eeb9e
commit 89ede3e103
5 changed files with 48 additions and 13 deletions

View File

@ -47,7 +47,7 @@
width: 100%; width: 100%;
left: 0px; left: 0px;
font-weight: bold; font-weight: bold;
font-size: 120%; font-size: 110%;
} }
.progress-bar-inner { .progress-bar-inner {

View File

@ -78,6 +78,33 @@ function getImageUrlFromTransfer(transfer) {
return url; return url;
} }
function makeProgressBar(value, maximum, opts) {
/*
* Render a progessbar!
*
* @param value is the current value of the progress bar
* @param maximum is the maximum value of the progress bar
*/
var options = opts || {};
value = parseFloat(value);
maximum = parseFloat(maximum);
var percent = parseInt(value / maximum * 100);
if (percent > 100) {
percent = 100;
}
return `
<div class='progress-bar'>
<div class='progress-bar progress-bar-inner' style='width: ${percent}%;'></div>
<div class='progress-bar-value'>${value} / ${maximum}</div>
</div>
`;
}
function enableDragAndDrop(element, url, options) { function enableDragAndDrop(element, url, options) {
/* Enable drag-and-drop file uploading for a given element. /* Enable drag-and-drop file uploading for a given element.

View File

@ -19,9 +19,7 @@ import os
from datetime import datetime from datetime import datetime
from decimal import Decimal from decimal import Decimal
from stock.models import StockItem
from company.models import Company, SupplierPart from company.models import Company, SupplierPart
from part.models import Part
from InvenTree.fields import RoundingDecimalField from InvenTree.fields import RoundingDecimalField
from InvenTree.helpers import decimal2string from InvenTree.helpers import decimal2string
@ -372,7 +370,7 @@ class SalesOrderLineItem(OrderLineItem):
order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', help_text=_('Sales Order')) order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', help_text=_('Sales Order'))
part = models.ForeignKey(Part, on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, 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, help_text=_('Part'), limit_choices_to={'salable': True})
def allocated_quantity(self): def allocated_quantity(self):
""" Return the total stock quantity allocated to this LineItem. """ Return the total stock quantity allocated to this LineItem.
@ -380,6 +378,6 @@ class SalesOrderLineItem(OrderLineItem):
This is a summation of the quantity of each attached StockItem This is a summation of the quantity of each attached StockItem
""" """
query = self.stock_items.aggregate(allocated=Coalesce(Sum('stock_item__quantity'), Decimal(0))) query = self.stock_items.aggregate(allocated=Coalesce(Sum('quantity'), Decimal(0)))
return query['allocated'] return query['allocated']

View File

@ -72,12 +72,7 @@ $("#so-lines-table").inventreeTable({
field: 'quantity', field: 'quantity',
title: 'Quantity', title: 'Quantity',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return ` return makeProgressBar(row.allocated, row.quantity);
<div class='progress-bar'>
<div class='progress-bar progress-bar-inner' style='width: 50%;'></div>
<div class='progress-bar-value'>${row.allocated} / ${row.quantity}</div>
</div>
`;
} }
}, },
{ {

View File

@ -29,6 +29,7 @@ from InvenTree.models import InvenTreeTree
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField
from part.models import Part from part.models import Part
from order.models import PurchaseOrder, SalesOrderLineItem
class StockLocation(InvenTreeTree): class StockLocation(InvenTreeTree):
@ -262,6 +263,20 @@ class StockItem(MPTTModel):
# TODO - Find a test than can be perfomed... # TODO - Find a test than can be perfomed...
pass pass
try:
# If this StockItem is assigned to a SalesOrderLineItem,
# the "Part" that the line item references is the same as the part that THIS references
if self.sales_order is not None:
if self.sales_order.part == None:
raise ValidationError({'sales_order': _('Stock item cannot be assigned to a LineItem which does not reference a part')})
if not self.sales_order.part == self.part:
raise ValidationError({'sales_order': _('Stock item does not reference the same part object as the LineItem')})
except SalesOrderLineItem.DoesNotExist:
pass
if self.belongs_to and self.belongs_to.pk == self.pk: if self.belongs_to and self.belongs_to.pk == self.pk:
raise ValidationError({ raise ValidationError({
'belongs_to': _('Item cannot belong to itself') 'belongs_to': _('Item cannot belong to itself')
@ -347,7 +362,7 @@ class StockItem(MPTTModel):
) )
purchase_order = models.ForeignKey( purchase_order = models.ForeignKey(
'order.PurchaseOrder', PurchaseOrder,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name='stock_items', related_name='stock_items',
blank=True, null=True, blank=True, null=True,
@ -355,7 +370,7 @@ class StockItem(MPTTModel):
) )
sales_order = models.ForeignKey( sales_order = models.ForeignKey(
'order.SalesOrderLineItem', SalesOrderLineItem,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
related_name='stock_items', related_name='stock_items',
null=True) null=True)