mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-10 06:37:17 +00:00
* fix: trigger pricing recalculation when SupplierPart is saved or deleted
When a SupplierPart's pack_quantity is updated after price breaks have
already been created, the Part's pricing (and BOM cost rollups for any
assemblies using that part) was not recalculated. This is because there
was no post_save or post_delete signal handler for the SupplierPart model
to trigger schedule_pricing_update on the linked Part.
Added post_save and post_delete signal handlers for SupplierPart that
mirror the existing SupplierPriceBreak signal handlers. The pricing
cascade (via update_assemblies) ensures BOM line costs in parent
assemblies are also updated.
Fixes #12285
* style: fix ruff format issues
* style: apply ruff format with --preview flag (matching project config)
* fix: resolve PartPricing.DoesNotExist in pack_quantity test
The test captured self.part.pricing before any PartPricing row existed,
yielding an unsaved instance; the later refresh_from_db() then raised
DoesNotExist. Re-fetch self.part.pricing after the price break creates
the row, matching the pattern in test_supplier_part_pricing.
---------
(cherry picked from commit be9faff766)
Co-authored-by: amanjain57-gif <aman.jain57@gmail.com>
Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
Co-authored-by: Aman Jain <jainamn@amazon.com>
This commit is contained in:
co-authored by
amanjain57-gif
Oliver
Aman Jain
parent
4a490854c8
commit
0ed2c2951e
@@ -1039,6 +1039,36 @@ class SupplierPriceBreak(common.models.PriceBreak):
|
||||
)
|
||||
|
||||
|
||||
@receiver(post_save, sender=SupplierPart, dispatch_uid='post_save_supplier_part')
|
||||
def after_save_supplier_part(sender, instance, created, **kwargs):
|
||||
"""Callback function when a SupplierPart is created or updated.
|
||||
|
||||
Triggers a pricing update for the linked Part, so that changes to
|
||||
pack_quantity are reflected in Part pricing and BOM cost rollups.
|
||||
"""
|
||||
if (
|
||||
InvenTree.ready.canAppAccessDatabase(allow_test=settings.TESTING_PRICING)
|
||||
and not InvenTree.ready.isImportingData()
|
||||
and instance.part
|
||||
):
|
||||
instance.part.schedule_pricing_update(create=True)
|
||||
|
||||
|
||||
@receiver(post_delete, sender=SupplierPart, dispatch_uid='post_delete_supplier_part')
|
||||
def after_delete_supplier_part(sender, instance, **kwargs):
|
||||
"""Callback function when a SupplierPart is deleted.
|
||||
|
||||
Triggers a pricing update for the linked Part, so that removal of a
|
||||
supplier part is reflected in Part pricing and BOM cost rollups.
|
||||
"""
|
||||
if (
|
||||
InvenTree.ready.canAppAccessDatabase(allow_test=settings.TESTING_PRICING)
|
||||
and not InvenTree.ready.isImportingData()
|
||||
and instance.part
|
||||
):
|
||||
instance.part.schedule_pricing_update(create=False)
|
||||
|
||||
|
||||
@receiver(
|
||||
post_save, sender=SupplierPriceBreak, dispatch_uid='post_save_supplier_price_break'
|
||||
)
|
||||
|
||||
@@ -175,6 +175,39 @@ class PartPricingTests(InvenTreeTestCase):
|
||||
self.assertIsNone(pricing.supplier_price_min)
|
||||
self.assertIsNone(pricing.supplier_price_max)
|
||||
|
||||
@override_settings(TESTING_PRICING=True)
|
||||
def test_supplier_part_pack_quantity_update(self):
|
||||
"""Test that changing pack_quantity on a SupplierPart triggers pricing recalculation."""
|
||||
supplier = company.models.Company.objects.create(
|
||||
name='Pack Test Supplier', is_supplier=True
|
||||
)
|
||||
|
||||
sp = company.models.SupplierPart.objects.create(
|
||||
supplier=supplier, part=self.part, SKU='PACK_TEST', pack_quantity='1'
|
||||
)
|
||||
|
||||
company.models.SupplierPriceBreak.objects.create(
|
||||
part=sp, quantity=1, price=50, price_currency='USD'
|
||||
)
|
||||
|
||||
# Re-fetch pricing (the price break creation triggers the PartPricing row)
|
||||
pricing = self.part.pricing
|
||||
pricing.refresh_from_db()
|
||||
|
||||
# Price per unit should be $50 / 1 = $50
|
||||
self.assertEqual(pricing.supplier_price_min, Money(50, 'USD'))
|
||||
|
||||
# Now update pack_quantity to 100 (i.e. 100 units per pack)
|
||||
sp.pack_quantity = '100'
|
||||
sp.save()
|
||||
|
||||
pricing = self.part.pricing
|
||||
pricing.refresh_from_db()
|
||||
|
||||
# Price per unit should now be $50 / 100 = $0.50
|
||||
self.assertEqual(pricing.supplier_price_min, Money('0.5', 'USD'))
|
||||
self.assertEqual(pricing.supplier_price_max, Money('0.5', 'USD'))
|
||||
|
||||
@override_settings(TESTING_PRICING=True)
|
||||
def test_internal_pricing(self):
|
||||
"""Tests for internal price breaks."""
|
||||
|
||||
Reference in New Issue
Block a user