Remove dead pricing code on Part model (#12357)

This commit is contained in:
Oliver 2026-07-11 17:49:28 +10:00 committed by GitHub
parent 89d11e9e37
commit 4bff83108f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 0 additions and 262 deletions

View File

@ -1,7 +1,6 @@
"""Unit tests for the models in the 'company' app."""
import os
from decimal import Decimal
from django.core.exceptions import ValidationError
from django.test import TestCase
@ -99,24 +98,6 @@ class CompanySimpleTest(TestCase):
self.assertEqual(p(45), 315)
self.assertEqual(p(55), 68.75)
def test_part_pricing(self):
"""Unit tests for supplier part pricing."""
m2x4 = Part.objects.get(name='M2x4 LPHS')
self.assertEqual(m2x4.get_price_info(5.5), '38.5 - 41.25')
self.assertEqual(m2x4.get_price_info(10), '70 - 75')
self.assertEqual(m2x4.get_price_info(100), '125 - 350')
pmin, pmax = m2x4.get_price_range(5)
self.assertEqual(pmin, 35)
self.assertEqual(pmax, 37.5)
m3x12 = Part.objects.get(name='M3x12 SHCS')
self.assertEqual(m3x12.get_price_info(0.3), Decimal('2.4'))
self.assertEqual(m3x12.get_price_info(3), Decimal('24'))
self.assertIsNotNone(m3x12.get_price_info(50))
def test_currency_validation(self):
"""Test validation for currency selection."""
# Create a company with a valid currency code (should pass)

View File

@ -2214,150 +2214,6 @@ class Part(
# which can cause issues down the track
pass
def get_price_info(self, quantity=1, buy=True, bom=True, internal=False):
"""Return a simplified pricing string for this part.
Args:
quantity: Number of units to calculate price for
buy: Include supplier pricing (default = True)
bom: Include BOM pricing (default = True)
internal: Include internal pricing (default = False)
"""
price_range = self.get_price_range(quantity, buy, bom, internal)
if price_range is None:
return None
min_price, max_price = price_range
if min_price == max_price:
return min_price
min_price = normalize(min_price)
max_price = normalize(max_price)
return f'{min_price} - {max_price}'
def get_supplier_price_range(self, quantity=1):
"""Return the supplier price range of this part.
Actions:
- Checks if there is any supplier pricing information associated with this Part
- Iterate through available supplier pricing and select (min, max)
- Returns tuple of (min, max)
Arguments:
quantity: Quantity at which to calculate price (default=1)
Returns: (min, max) tuple or (None, None) if no supplier pricing available
"""
min_price = None
max_price = None
for supplier in self.supplier_parts.all():
price = supplier.get_price(quantity)
if price is None:
continue
if min_price is None or price < min_price:
min_price = price
if max_price is None or price > max_price:
max_price = price
if min_price is None or max_price is None:
return None
min_price = normalize(min_price)
max_price = normalize(max_price)
return (min_price, max_price)
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.
Note: If the BOM contains items without pricing information,
these items cannot be included in the BOM!
"""
min_price = None
max_price = None
for item in self.get_bom_items().select_related('sub_part'):
if item.sub_part.pk == self.pk:
logger.warning('WARNING: BomItem ID %s contains itself in BOM', item.pk)
continue
q = Decimal(quantity)
i = Decimal(item.quantity)
prices = item.sub_part.get_price_range(
q * i, internal=internal, purchase=purchase
)
if prices is None:
continue
low, high = prices
if min_price is None:
min_price = 0
if max_price is None:
max_price = 0
min_price += low
max_price += high
if min_price is None or max_price is None:
return None
min_price = normalize(min_price)
max_price = normalize(max_price)
return (min_price, max_price)
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, internal or purchase price. If no pricing available, returns None
"""
# only get internal price if set and should be used
if internal and self.has_internal_price_breaks:
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
)
if buy_price_range is None:
return bom_price_range
elif bom_price_range is None:
return buy_price_range
return (
min(buy_price_range[0], bom_price_range[0]),
max(buy_price_range[1], bom_price_range[1]),
)
base_cost = models.DecimalField(
max_digits=19,
decimal_places=6,
@ -2374,73 +2230,6 @@ class Part(
help_text=_('Sell multiple'),
)
get_price = common.currency.get_price
@property
def has_price_breaks(self):
"""Return True if this part has sale price breaks."""
return self.price_breaks.exists()
@property
def price_breaks(self):
"""Return the associated price breaks in the correct order."""
return self.salepricebreaks.order_by('quantity').all()
@property
def unit_pricing(self):
"""Returns the price of this Part at quantity=1."""
return self.get_price(1)
def add_price_break(self, quantity, price):
"""Create a new price break for this part.
Args:
quantity: Numerical quantity
price: Must be a Money object
"""
# Check if a price break at that quantity already exists...
if self.price_breaks.filter(quantity=quantity, part=self.pk).exists():
return
PartSellPriceBreak.objects.create(part=self, quantity=quantity, price=price)
def get_internal_price(self, quantity, moq=True, multiples=True, currency=None):
"""Return the internal price of this Part at the specified quantity."""
return common.currency.get_price(
self, quantity, moq, multiples, currency, break_name='internal_price_breaks'
)
@property
def has_internal_price_breaks(self):
"""Return True if this Part has internal pricing information."""
return self.internal_price_breaks.exists()
@property
def internal_price_breaks(self):
"""Return the associated price breaks in the correct order."""
return self.internalpricebreaks.order_by('quantity').all()
def get_purchase_price(self, quantity):
"""Calculate the purchase price for this part at the specified quantity.
- Looks at available supplier pricing data
- Calculates the price base on the closest price point
"""
currency = currency_code_default()
try:
prices = [
convert_money(item.purchase_price, currency).amount
for item in self.stock_items.all()
if item.purchase_price
]
except MissingRate:
prices = None
if prices:
return min(prices) * quantity, max(prices) * quantity
return None
@transaction.atomic
def copy_bom_from(self, other, clear: bool = True, **kwargs):
"""Copy the BOM from another part.

View File

@ -1,7 +1,5 @@
"""Unit tests for the BomItem model."""
from decimal import Decimal
import django.core.exceptions as django_exceptions
from django.db import transaction
from django.test import TestCase
@ -171,20 +169,6 @@ class BomItemTest(TestCase):
self.assertNotEqual(h1, h2)
def test_pricing(self):
"""Test BOM pricing."""
self.bob.get_price(1)
self.assertEqual(
self.bob.get_bom_price_range(1, internal=True),
(Decimal(29.5), Decimal(89.5)),
)
# remove internal price for R_2K2_0805
self.r1.internal_price_breaks.delete()
self.assertEqual(
self.bob.get_bom_price_range(1, internal=True),
(Decimal(27.5), Decimal(87.5)),
)
def test_substitutes(self):
"""Tests for BOM item substitutes."""
# We will make some substitute parts for the "orphan" part

View File

@ -250,22 +250,6 @@ class PartTest(TestCase):
barcode = self.r1.format_barcode()
self.assertEqual('INV-PA3', barcode)
def test_sell_pricing(self):
"""Check that the sell pricebreaks were loaded."""
self.assertTrue(self.r1.has_price_breaks)
self.assertEqual(self.r1.price_breaks.count(), 2)
# check that the sell pricebreaks work
self.assertEqual(float(self.r1.get_price(1)), 0.15)
self.assertEqual(float(self.r1.get_price(10)), 1.0)
def test_internal_pricing(self):
"""Check that the sell pricebreaks were loaded."""
self.assertTrue(self.r1.has_internal_price_breaks)
self.assertEqual(self.r1.internal_price_breaks.count(), 2)
# check that the sell pricebreaks work
self.assertEqual(float(self.r1.get_internal_price(1)), 0.08)
self.assertEqual(float(self.r1.get_internal_price(10)), 0.5)
def test_metadata(self):
"""Unit tests for the metadata field."""
for model in [Part]: