Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/backend/InvenTree/company/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,36 @@ def get_api_url():
)


@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'
)
Expand Down
32 changes: 32 additions & 0 deletions src/backend/InvenTree/part/test_pricing.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,38 @@ def test_supplier_part_pricing(self):
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."""
pricing = self.part.pricing

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'
)

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.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."""
Expand Down
Loading