diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index 90a0df23a2..be6cf10f7c 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -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: diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 19b2543da2..1afb629437 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -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 @@ -618,72 +602,45 @@ class Part(models.Model): buy: Include supplier pricing (default = True) bom: Include BOM pricing (default = True) """ - - min_price = self.get_min_price(quantity, buy, bom) - max_price = self.get_max_price(quantity, buy, bom) - if min_price is None: + price_range = self.get_price_range(quantity, buy, bom) + + 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. + def get_supplier_price_range(self, quantity=1): - Args: - quantity: Number of units we wish to purchase (default = 1) - - Returns: - Numerical price if pricing is available, else None - """ - 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 - - return min(buy_price, bom_price) + elif bom_price_range is None: + return buy_price_range - 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. diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 98a209b714..89065a2afd 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -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 max_bom_price: - ctx['max_total_bom_price'] = max_bom_price - ctx['max_unit_bom_price'] = max_bom_price / quantity + if bom_price is not None: + min_bom_price, max_bom_price = bom_price + + 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