mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +00:00
Merge pull request #1957 from matmair/bpm-purchase-price
BOM - show purchase price
This commit is contained in:
commit
fd1dd792c6
@ -35,6 +35,8 @@ from stdimage.models import StdImageField
|
|||||||
from decimal import Decimal, InvalidOperation
|
from decimal import Decimal, InvalidOperation
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import hashlib
|
import hashlib
|
||||||
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
|
from common.settings import currency_code_default
|
||||||
|
|
||||||
from InvenTree import helpers
|
from InvenTree import helpers
|
||||||
from InvenTree import validators
|
from InvenTree import validators
|
||||||
@ -1514,7 +1516,7 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
return (min_price, max_price)
|
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.
|
""" Return the price range of the BOM for this part.
|
||||||
Adds the minimum price for all components in the BOM.
|
Adds the minimum price for all components in the BOM.
|
||||||
|
|
||||||
@ -1531,7 +1533,7 @@ class Part(MPTTModel):
|
|||||||
print("Warning: Item contains itself in BOM")
|
print("Warning: Item contains itself in BOM")
|
||||||
continue
|
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:
|
if prices is None:
|
||||||
continue
|
continue
|
||||||
@ -1555,16 +1557,17 @@ class Part(MPTTModel):
|
|||||||
|
|
||||||
return (min_price, max_price)
|
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:
|
""" Return the price range for this part. This price can be either:
|
||||||
|
|
||||||
- Supplier price (if purchased from suppliers)
|
- Supplier price (if purchased from suppliers)
|
||||||
- BOM price (if built from other parts)
|
- BOM price (if built from other parts)
|
||||||
- Internal price (if set for the part)
|
- Internal price (if set for the part)
|
||||||
|
- Purchase price (if set for the part)
|
||||||
|
|
||||||
Returns:
|
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
|
# only get internal price if set and should be used
|
||||||
@ -1572,6 +1575,12 @@ class Part(MPTTModel):
|
|||||||
internal_price = self.get_internal_price(quantity)
|
internal_price = self.get_internal_price(quantity)
|
||||||
return internal_price, internal_price
|
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
|
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
|
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):
|
def internal_unit_pricing(self):
|
||||||
return self.get_internal_price(1)
|
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
|
@transaction.atomic
|
||||||
def copy_bom_from(self, other, clear=True, **kwargs):
|
def copy_bom_from(self, other, clear=True, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
@ -60,6 +60,21 @@
|
|||||||
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if min_total_bom_purchase_price %}
|
||||||
|
<tr>
|
||||||
|
<td><b>{% trans 'Unit Purchase Price' %}</b></td>
|
||||||
|
<td>Min: {% include "price.html" with price=min_unit_bom_purchase_price %}</td>
|
||||||
|
<td>Max: {% include "price.html" with price=max_unit_bom_purchase_price %}</td>
|
||||||
|
</tr>
|
||||||
|
{% if quantity > 1 %}
|
||||||
|
<tr>
|
||||||
|
<td><b>{% trans 'Total Purchase Price' %}</b></td>
|
||||||
|
<td>Min: {% include "price.html" with price=min_total_bom_purchase_price %}</td>
|
||||||
|
<td>Max: {% include "price.html" with price=max_total_bom_purchase_price %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if part.has_complete_bom_pricing == False %}
|
{% if part.has_complete_bom_pricing == False %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan='3'>
|
<td colspan='3'>
|
||||||
|
@ -61,6 +61,25 @@
|
|||||||
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
<td>Max: {% include "price.html" with price=max_total_bom_price %}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if min_total_bom_purchase_price %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>{% trans 'Unit Purchase Price' %}</td>
|
||||||
|
<td>Min: {% include "price.html" with price=min_unit_bom_purchase_price %}</td>
|
||||||
|
<td>Max: {% include "price.html" with price=max_unit_bom_purchase_price %}</td>
|
||||||
|
</tr>
|
||||||
|
{% if quantity > 1 %}
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td>{% trans 'Total Purchase Price' %}</td>
|
||||||
|
<td>Min: {% include "price.html" with price=min_total_bom_purchase_price %}</td>
|
||||||
|
<td>Max: {% include "price.html" with price=max_total_bom_purchase_price %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if part.has_complete_bom_pricing == False %}
|
{% if part.has_complete_bom_pricing == False %}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan='4'>
|
<td colspan='4'>
|
||||||
|
@ -1351,6 +1351,7 @@ class PartPricing(AjaxView):
|
|||||||
|
|
||||||
use_internal = InvenTreeSetting.get_setting('PART_BOM_USE_INTERNAL_PRICE', False)
|
use_internal = InvenTreeSetting.get_setting('PART_BOM_USE_INTERNAL_PRICE', False)
|
||||||
bom_price = part.get_bom_price_range(quantity, internal=use_internal)
|
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:
|
if bom_price is not None:
|
||||||
min_bom_price, max_bom_price = bom_price
|
min_bom_price, max_bom_price = bom_price
|
||||||
@ -1358,19 +1359,26 @@ class PartPricing(AjaxView):
|
|||||||
min_bom_price /= scaler
|
min_bom_price /= scaler
|
||||||
max_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:
|
if min_bom_price:
|
||||||
ctx['min_total_bom_price'] = min_bom_price
|
ctx['min_total_bom_price'] = round(min_bom_price, 3)
|
||||||
ctx['min_unit_bom_price'] = min_unit_bom_price
|
ctx['min_unit_bom_price'] = round(min_bom_price / quantity, 3)
|
||||||
|
|
||||||
if max_bom_price:
|
if max_bom_price:
|
||||||
ctx['max_total_bom_price'] = max_bom_price
|
ctx['max_total_bom_price'] = round(max_bom_price, 3)
|
||||||
ctx['max_unit_bom_price'] = max_unit_bom_price
|
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 pricing information
|
||||||
internal_part_price = part.get_internal_price(quantity)
|
internal_part_price = part.get_internal_price(quantity)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user