mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Fix for SalesOrderLineItem allocation calculation
Also function to render a progress bar
This commit is contained in:
		| @@ -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 { | ||||||
|   | |||||||
| @@ -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. | ||||||
|   | |||||||
| @@ -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'] | ||||||
|   | |||||||
| @@ -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> |  | ||||||
|                 `; |  | ||||||
|             } |             } | ||||||
|         }, |         }, | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user