Pricing bugs (#12305)

* bug fix for get_price method

- Remove unused argument moq
- Allow case where 'multiples' = False

* Error handling for convert_to

* Error handling for PartPricing.save

* Remove unused function

* Revert function definition

* coverage ignore
This commit is contained in:
Oliver
2026-07-06 22:09:12 +10:00
committed by GitHub
parent 436bfed234
commit ed62b5d9dd
3 changed files with 27 additions and 39 deletions
+12 -4
View File
@@ -162,13 +162,21 @@ def currency_exchange_plugins() -> Optional[list]:
def get_price( def get_price(
instance, instance,
quantity, quantity,
moq=True, moq: bool = True,
multiples=True, multiples: bool = True,
currency=None, currency: Optional[str] = None,
break_name: str = 'price_breaks', break_name: str = 'price_breaks',
): ):
"""Calculate the price based on quantity price breaks. """Calculate the price based on quantity price breaks.
Arguments:
instance: The model instance which contains the price break information
quantity: The quantity to calculate the price for
moq: If True, then minimum order quantity will be observed (CURRENTLY NOT IMPLEMENTED)
multiples: If True, then order multiples will be observed
currency: The currency code to use for the calculation (default is None)
break_name: The name of the price break field on the instance (default is 'price_breaks')
- Don't forget to add in flat-fee cost (base_cost field) - Don't forget to add in flat-fee cost (base_cost field)
- If MOQ (minimum order quantity) is required, bump quantity - If MOQ (minimum order quantity) is required, bump quantity
- If order multiples are to be observed, then we need to calculate based on that, too - If order multiples are to be observed, then we need to calculate based on that, too
@@ -185,7 +193,7 @@ def get_price(
return None return None
# Check if quantity is fraction and disable multiples # Check if quantity is fraction and disable multiples
multiples = quantity % 1 == 0 multiples = multiples and (quantity % 1 == 0)
# Order multiples # Order multiples
if multiples: if multiples:
+10 -4
View File
@@ -1388,21 +1388,27 @@ class PriceBreak(MetaMixin):
help_text=_('Unit price at specified quantity'), help_text=_('Unit price at specified quantity'),
) )
def convert_to(self, currency_code): def convert_to(self, currency_code: str, raise_error: bool = False):
"""Convert the unit-price at this price break to the specified currency code. """Convert the unit-price at this price break to the specified currency code.
Args: Arguments:
currency_code: The currency code to convert to (e.g "USD" or "AUD") currency_code: The currency code to convert to (e.g "USD" or "AUD")
raise_error: If True, raise an error if the conversion fails. If False, return None.
""" """
try: try:
converted = convert_money(self.price, currency_code) converted = convert_money(self.price, currency_code)
except MissingRate: except MissingRate: # pragma: no cover
InvenTree.exceptions.log_error('PriceBreak.convert_to')
logger.warning( logger.warning(
'No currency conversion rate available for %s -> %s', 'No currency conversion rate available for %s -> %s',
self.price_currency, self.price_currency,
currency_code, currency_code,
) )
return self.price.amount
if raise_error:
raise
return None
return converted.amount return converted.amount
+5 -31
View File
@@ -53,7 +53,7 @@ from common.settings import get_global_setting
from InvenTree import helpers, validators from InvenTree import helpers, validators
from InvenTree.exceptions import log_error from InvenTree.exceptions import log_error
from InvenTree.fields import InvenTreeURLField from InvenTree.fields import InvenTreeURLField
from InvenTree.helpers import decimal2money, decimal2string, normalize from InvenTree.helpers import decimal2string, normalize
from order import models as OrderModels from order import models as OrderModels
from order.status_codes import ( from order.status_codes import (
PurchaseOrderStatus, PurchaseOrderStatus,
@@ -3001,15 +3001,12 @@ class PartPricing(common.models.MetaMixin):
try: try:
self.update_overall_cost() self.update_overall_cost()
except Exception:
# If something has happened to the Part model, might throw an error
pass
try:
super().save(*args, **kwargs) super().save(*args, **kwargs)
except Exception: except Exception:
# This error may be thrown if there is already duplicate pricing data log_error('PartPricing.save')
pass logger.error(
"Could not save PartPricing for part '%s' to the database", self.part
)
def update_bom_cost(self, save=True): def update_bom_cost(self, save=True):
"""Recalculate BOM cost for the referenced Part instance. """Recalculate BOM cost for the referenced Part instance.
@@ -4426,29 +4423,6 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
return required return required
@property
def price_range(self, internal=False):
"""Return the price-range for this BOM item."""
# get internal price setting
use_internal = get_global_setting('PART_BOM_USE_INTERNAL_PRICE', False)
p_range = self.sub_part.get_price_range(
self.quantity, internal=use_internal and internal
)
if p_range is None:
return p_range
p_min, p_max = p_range
if p_min == p_max:
return decimal2money(p_min)
# Convert to better string representation
p_min = decimal2money(p_min)
p_max = decimal2money(p_max)
return f'{p_min} to {p_max}'
@receiver(post_save, sender=BomItem, dispatch_uid='update_bom_build_lines') @receiver(post_save, sender=BomItem, dispatch_uid='update_bom_build_lines')
def update_bom_build_lines(sender, instance, created, **kwargs): def update_bom_build_lines(sender, instance, created, **kwargs):