mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Cleanup pricing algorithms
This commit is contained in:
parent
8adb4f6c20
commit
6ae48d07c4
@ -250,7 +250,7 @@ class SupplierPart(models.Model):
|
||||
- If order multiples are to be observed, then we need to calculate based on that, too
|
||||
"""
|
||||
|
||||
price_breaks = self.price_breaks.all()
|
||||
price_breaks = self.price_breaks.filter(quantity__lte=quantity)
|
||||
|
||||
# No price break information available?
|
||||
if len(price_breaks) == 0:
|
||||
|
@ -573,32 +573,16 @@ class Part(models.Model):
|
||||
""" Return the number of supplier parts available for this part """
|
||||
return self.supplier_parts.count()
|
||||
|
||||
@property
|
||||
def min_single_price(self):
|
||||
return self.get_min_supplier_price(1)
|
||||
|
||||
@property
|
||||
def max_single_price(self):
|
||||
return self.get_max_supplier_price(1)
|
||||
|
||||
@property
|
||||
def min_bom_price(self):
|
||||
return self.get_min_bom_price(1)
|
||||
|
||||
@property
|
||||
def max_bom_price(self):
|
||||
return self.get_max_bom_price(1)
|
||||
|
||||
@property
|
||||
def has_pricing_info(self):
|
||||
""" Return true if there is pricing information for this part """
|
||||
return self.get_min_price() is not None
|
||||
return self.get_price_range() is not None
|
||||
|
||||
@property
|
||||
def has_complete_bom_pricing(self):
|
||||
""" Return true if there is pricing information for each item in the BOM. """
|
||||
|
||||
for item in self.bom_items.all():
|
||||
for item in self.bom_items.all().prefetch_related('sub_part'):
|
||||
if not item.sub_part.has_pricing_info:
|
||||
return False
|
||||
|
||||
@ -619,71 +603,44 @@ class Part(models.Model):
|
||||
bom: Include BOM pricing (default = True)
|
||||
"""
|
||||
|
||||
min_price = self.get_min_price(quantity, buy, bom)
|
||||
max_price = self.get_max_price(quantity, buy, bom)
|
||||
price_range = self.get_price_range(quantity, buy, bom)
|
||||
|
||||
if min_price is None:
|
||||
if price_range is None:
|
||||
return None
|
||||
|
||||
min_price, max_price = price_range
|
||||
|
||||
if min_price == max_price:
|
||||
return min_price
|
||||
|
||||
return "{a} to {b}".format(a=min_price, b=max_price)
|
||||
|
||||
def get_min_supplier_price(self, quantity=1):
|
||||
""" Return the minimum price of this part from all available suppliers.
|
||||
|
||||
Args:
|
||||
quantity: Number of units we wish to purchase (default = 1)
|
||||
|
||||
Returns:
|
||||
Numerical price if pricing is available, else None
|
||||
"""
|
||||
def get_supplier_price_range(self, quantity=1):
|
||||
|
||||
min_price = None
|
||||
|
||||
for supplier_part in self.supplier_parts.all():
|
||||
supplier_price = supplier_part.get_price(quantity)
|
||||
|
||||
if supplier_price is None:
|
||||
continue
|
||||
|
||||
if min_price is None or supplier_price < min_price:
|
||||
min_price = supplier_price
|
||||
|
||||
if min_price is None:
|
||||
return None
|
||||
else:
|
||||
return min_price
|
||||
|
||||
def get_max_supplier_price(self, quantity=1):
|
||||
""" Return the maximum price of this part from all available suppliers.
|
||||
|
||||
Args:
|
||||
quantity: Number of units we wish to purchase (default = 1)
|
||||
|
||||
Returns:
|
||||
Numerical price if pricing is available, else None
|
||||
"""
|
||||
|
||||
max_price = None
|
||||
|
||||
for supplier_part in self.supplier_parts.all():
|
||||
supplier_price = supplier_part.get_price(quantity)
|
||||
for supplier in self.supplier_parts.all():
|
||||
|
||||
if supplier_price is None:
|
||||
price = supplier.get_price(quantity)
|
||||
|
||||
if price is None:
|
||||
continue
|
||||
|
||||
if max_price is None or supplier_price > max_price:
|
||||
max_price = supplier_price
|
||||
if min_price is None or price < min_price:
|
||||
min_price = price
|
||||
|
||||
if max_price is None:
|
||||
if max_price is None or price > max_price:
|
||||
max_price = price
|
||||
|
||||
if min_price is None or max_price is None:
|
||||
return None
|
||||
else:
|
||||
return max_price
|
||||
|
||||
def get_min_bom_price(self, quantity=1):
|
||||
""" Return the minimum price of the BOM for this part.
|
||||
return (min_price, max_price)
|
||||
|
||||
|
||||
def get_bom_price_range(self, quantity=1):
|
||||
""" Return the price range of the BOM for this part.
|
||||
Adds the minimum price for all components in the BOM.
|
||||
|
||||
Note: If the BOM contains items without pricing information,
|
||||
@ -691,45 +648,33 @@ class Part(models.Model):
|
||||
"""
|
||||
|
||||
min_price = None
|
||||
max_price = None
|
||||
|
||||
for item in self.bom_items.all():
|
||||
price = item.sub_part.get_min_price(quantity * item.quantity)
|
||||
for item in self.bom_items.all().prefetch_related('sub_part'):
|
||||
prices = item.sub_part.get_price_range(quantity * item.quantity)
|
||||
|
||||
if price is None:
|
||||
if prices is None:
|
||||
continue
|
||||
|
||||
low, high = prices
|
||||
|
||||
if min_price is None:
|
||||
min_price = 0
|
||||
|
||||
min_price += price
|
||||
|
||||
return min_price
|
||||
|
||||
def get_max_bom_price(self, quantity=1):
|
||||
""" Return the maximum price of the BOM for this part.
|
||||
Adds the maximum price for all components in the BOM.
|
||||
|
||||
Note: If the BOM contains items without pricing information,
|
||||
these items cannot be included in the BOM!
|
||||
"""
|
||||
|
||||
max_price = None
|
||||
|
||||
for item in self.bom_items.all():
|
||||
price = item.sub_part.get_max_price(quantity * item.quantity)
|
||||
|
||||
if price is None:
|
||||
continue
|
||||
|
||||
if max_price is None:
|
||||
max_price = 0
|
||||
|
||||
max_price += price
|
||||
min_price += low
|
||||
max_price += high
|
||||
|
||||
return max_price
|
||||
if min_price is None or max_price is None:
|
||||
return None
|
||||
|
||||
def get_min_price(self, quantity=1, buy=True, bom=True):
|
||||
""" Return the minimum price for this part. This price can be either:
|
||||
return (min_price, max_price)
|
||||
|
||||
def get_price_range(self, quantity=1, buy=True, bom=True):
|
||||
|
||||
""" 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)
|
||||
@ -738,37 +683,20 @@ class Part(models.Model):
|
||||
Minimum of the supplier price or BOM price. If no pricing available, returns None
|
||||
"""
|
||||
|
||||
buy_price = self.get_min_supplier_price(quantity) if buy else None
|
||||
bom_price = self.get_min_bom_price(quantity) if bom else None
|
||||
buy_price_range = self.get_supplier_price_range(quantity) if buy else None
|
||||
bom_price_range = self.get_bom_price_range(quantity) if bom else None
|
||||
|
||||
if buy_price is None:
|
||||
return bom_price
|
||||
if buy_price_range is None:
|
||||
return bom_price_range
|
||||
|
||||
if bom_price is None:
|
||||
return buy_price
|
||||
elif bom_price_range is None:
|
||||
return buy_price_range
|
||||
|
||||
return min(buy_price, bom_price)
|
||||
|
||||
def get_max_price(self, quantity=1, buy=True, bom=True):
|
||||
""" Return the maximum price for this part. This price can be either:
|
||||
|
||||
- Supplier price (if purchsed from suppliers)
|
||||
- BOM price (if built from other parts)
|
||||
|
||||
Returns:
|
||||
Maximum of the supplier price or BOM price. If no pricing available, returns None
|
||||
"""
|
||||
|
||||
buy_price = self.get_max_supplier_price(quantity) if buy else None
|
||||
bom_price = self.get_max_bom_price(quantity) if bom else None
|
||||
|
||||
if buy_price is None:
|
||||
return bom_price
|
||||
|
||||
if bom_price is None:
|
||||
return buy_price
|
||||
|
||||
return max(buy_price, bom_price)
|
||||
else:
|
||||
return (
|
||||
min(buy_price_range[0], bom_price_range[0]),
|
||||
max(buy_price_range[1], bom_price_range[1])
|
||||
)
|
||||
|
||||
def deepCopy(self, other, **kwargs):
|
||||
""" Duplicates non-field data from another part.
|
||||
|
@ -587,30 +587,34 @@ class PartPricing(AjaxView):
|
||||
|
||||
# Supplier pricing information
|
||||
if part.supplier_count > 0:
|
||||
min_buy_price = part.get_min_supplier_price(quantity)
|
||||
max_buy_price = part.get_max_supplier_price(quantity)
|
||||
buy_price = part.get_supplier_price_range(quantity)
|
||||
|
||||
if min_buy_price:
|
||||
ctx['min_total_buy_price'] = min_buy_price
|
||||
ctx['min_unit_buy_price'] = min_buy_price / quantity
|
||||
if buy_price is not None:
|
||||
min_buy_price, max_buy_price = buy_price
|
||||
|
||||
if max_buy_price:
|
||||
ctx['max_total_buy_price'] = max_buy_price
|
||||
ctx['max_unit_buy_price'] = max_buy_price / quantity
|
||||
if min_buy_price:
|
||||
ctx['min_total_buy_price'] = min_buy_price
|
||||
ctx['min_unit_buy_price'] = min_buy_price / quantity
|
||||
|
||||
if max_buy_price:
|
||||
ctx['max_total_buy_price'] = max_buy_price
|
||||
ctx['max_unit_buy_price'] = max_buy_price / quantity
|
||||
|
||||
# BOM pricing information
|
||||
if part.bom_count > 0:
|
||||
|
||||
min_bom_price = part.get_min_bom_price(quantity)
|
||||
max_bom_price = part.get_max_bom_price(quantity)
|
||||
bom_price = part.get_bom_price_range(quantity)
|
||||
|
||||
if min_bom_price:
|
||||
ctx['min_total_bom_price'] = min_bom_price
|
||||
ctx['min_unit_bom_price'] = min_bom_price / quantity
|
||||
if bom_price is not None:
|
||||
min_bom_price, max_bom_price = bom_price
|
||||
|
||||
if max_bom_price:
|
||||
ctx['max_total_bom_price'] = max_bom_price
|
||||
ctx['max_unit_bom_price'] = max_bom_price / quantity
|
||||
if min_bom_price:
|
||||
ctx['min_total_bom_price'] = min_bom_price
|
||||
ctx['min_unit_bom_price'] = min_bom_price / quantity
|
||||
|
||||
if max_bom_price:
|
||||
ctx['max_total_bom_price'] = max_bom_price
|
||||
ctx['max_unit_bom_price'] = max_bom_price / quantity
|
||||
|
||||
return ctx
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user