diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index 50d3044771..d7ad577081 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -35,6 +35,8 @@ from stdimage.models import StdImageField
from decimal import Decimal, InvalidOperation
from datetime import datetime
import hashlib
+from djmoney.contrib.exchange.models import convert_money
+from common.settings import currency_code_default
from InvenTree import helpers
from InvenTree import validators
@@ -1514,7 +1516,7 @@ class Part(MPTTModel):
return (min_price, max_price)
- def get_bom_price_range(self, quantity=1, internal=False):
+ def get_bom_price_range(self, quantity=1, internal=False, purchase=False):
""" Return the price range of the BOM for this part.
Adds the minimum price for all components in the BOM.
@@ -1531,7 +1533,7 @@ class Part(MPTTModel):
print("Warning: Item contains itself in BOM")
continue
- prices = item.sub_part.get_price_range(quantity * item.quantity, internal=internal)
+ prices = item.sub_part.get_price_range(quantity * item.quantity, internal=internal, purchase=purchase)
if prices is None:
continue
@@ -1555,16 +1557,17 @@ class Part(MPTTModel):
return (min_price, max_price)
- def get_price_range(self, quantity=1, buy=True, bom=True, internal=False):
+ def get_price_range(self, quantity=1, buy=True, bom=True, internal=False, purchase=False):
""" Return the price range for this part. This price can be either:
- Supplier price (if purchased from suppliers)
- BOM price (if built from other parts)
- Internal price (if set for the part)
+ - Purchase price (if set for the part)
Returns:
- Minimum of the supplier, BOM or internal price. If no pricing available, returns None
+ Minimum of the supplier, BOM, internal or purchase price. If no pricing available, returns None
"""
# only get internal price if set and should be used
@@ -1572,6 +1575,12 @@ class Part(MPTTModel):
internal_price = self.get_internal_price(quantity)
return internal_price, internal_price
+ # only get purchase price if set and should be used
+ if purchase:
+ purchase_price = self.get_purchase_price(quantity)
+ if purchase_price:
+ return purchase_price
+
buy_price_range = self.get_supplier_price_range(quantity) if buy else None
bom_price_range = self.get_bom_price_range(quantity, internal=internal) if bom else None
@@ -1641,6 +1650,13 @@ class Part(MPTTModel):
def internal_unit_pricing(self):
return self.get_internal_price(1)
+ def get_purchase_price(self, quantity):
+ currency = currency_code_default()
+ prices = [convert_money(item.purchase_price, currency).amount for item in self.stock_items.all() if item.purchase_price]
+ if prices:
+ return min(prices) * quantity, max(prices) * quantity
+ return None
+
@transaction.atomic
def copy_bom_from(self, other, clear=True, **kwargs):
"""
diff --git a/InvenTree/part/templates/part/part_pricing.html b/InvenTree/part/templates/part/part_pricing.html
index ce55124bd9..be80efe1e7 100644
--- a/InvenTree/part/templates/part/part_pricing.html
+++ b/InvenTree/part/templates/part/part_pricing.html
@@ -60,6 +60,21 @@
Max: {% include "price.html" with price=max_total_bom_price %} |
{% endif %}
+ {% if min_total_bom_purchase_price %}
+
+ {% trans 'Unit Purchase Price' %} |
+ Min: {% include "price.html" with price=min_unit_bom_purchase_price %} |
+ Max: {% include "price.html" with price=max_unit_bom_purchase_price %} |
+
+ {% if quantity > 1 %}
+
+ {% trans 'Total Purchase Price' %} |
+ Min: {% include "price.html" with price=min_total_bom_purchase_price %} |
+ Max: {% include "price.html" with price=max_total_bom_purchase_price %} |
+
+ {% endif %}
+ {% endif %}
+
{% if part.has_complete_bom_pricing == False %}
diff --git a/InvenTree/part/templates/part/prices.html b/InvenTree/part/templates/part/prices.html
index bb62ffa880..3f62ea7c73 100644
--- a/InvenTree/part/templates/part/prices.html
+++ b/InvenTree/part/templates/part/prices.html
@@ -61,6 +61,25 @@
| Max: {% include "price.html" with price=max_total_bom_price %} |
{% endif %}
+
+
+ {% if min_total_bom_purchase_price %}
+
+ |
+ {% trans 'Unit Purchase Price' %} |
+ Min: {% include "price.html" with price=min_unit_bom_purchase_price %} |
+ Max: {% include "price.html" with price=max_unit_bom_purchase_price %} |
+
+ {% if quantity > 1 %}
+
+ |
+ {% trans 'Total Purchase Price' %} |
+ Min: {% include "price.html" with price=min_total_bom_purchase_price %} |
+ Max: {% include "price.html" with price=max_total_bom_purchase_price %} |
+
+ {% endif %}
+ {% endif %}
+
{% if part.has_complete_bom_pricing == False %}
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 0e06678694..ee8699dfea 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -1351,6 +1351,7 @@ class PartPricing(AjaxView):
use_internal = InvenTreeSetting.get_setting('PART_BOM_USE_INTERNAL_PRICE', False)
bom_price = part.get_bom_price_range(quantity, internal=use_internal)
+ purchase_price = part.get_bom_price_range(quantity, purchase=True)
if bom_price is not None:
min_bom_price, max_bom_price = bom_price
@@ -1358,19 +1359,26 @@ class PartPricing(AjaxView):
min_bom_price /= scaler
max_bom_price /= scaler
- min_unit_bom_price = round(min_bom_price / quantity, 3)
- max_unit_bom_price = round(max_bom_price / quantity, 3)
-
- min_bom_price = round(min_bom_price, 3)
- max_bom_price = round(max_bom_price, 3)
-
if min_bom_price:
- ctx['min_total_bom_price'] = min_bom_price
- ctx['min_unit_bom_price'] = min_unit_bom_price
+ ctx['min_total_bom_price'] = round(min_bom_price, 3)
+ ctx['min_unit_bom_price'] = round(min_bom_price / quantity, 3)
if max_bom_price:
- ctx['max_total_bom_price'] = max_bom_price
- ctx['max_unit_bom_price'] = max_unit_bom_price
+ ctx['max_total_bom_price'] = round(max_bom_price, 3)
+ ctx['max_unit_bom_price'] = round(max_bom_price / quantity, 3)
+
+ if purchase_price is not None:
+ min_bom_purchase_price, max_bom_purchase_price = purchase_price
+
+ min_bom_purchase_price /= scaler
+ max_bom_purchase_price /= scaler
+ if min_bom_purchase_price:
+ ctx['min_total_bom_purchase_price'] = round(min_bom_purchase_price, 3)
+ ctx['min_unit_bom_purchase_price'] = round(min_bom_purchase_price / quantity, 3)
+
+ if max_bom_purchase_price:
+ ctx['max_total_bom_purchase_price'] = round(max_bom_purchase_price, 3)
+ ctx['max_unit_bom_purchase_price'] = round(max_bom_purchase_price / quantity, 3)
# internal part pricing information
internal_part_price = part.get_internal_price(quantity)
|