2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-15 11:35:41 +00:00

Rename "pack_units" to "pack_quantity"

This commit is contained in:
Oliver Walters
2023-05-22 01:18:37 +10:00
parent 532f592397
commit 4fcdbcafeb
13 changed files with 51 additions and 50 deletions

View File

@ -8,7 +8,7 @@ INVENTREE_API_VERSION = 117
Increment this API version number whenever there is a significant change to the API that any clients need to know about Increment this API version number whenever there is a significant change to the API that any clients need to know about
v117 -> 2023-05-22 : https://github.com/inventree/InvenTree/pull/4854 v117 -> 2023-05-22 : https://github.com/inventree/InvenTree/pull/4854
- Replaces SupplierPart "pack_size" field with "pack_units" - Replaces SupplierPart "pack_size" field with "pack_quantity"
- New field supports physical units, and allows for conversion between compatible units - New field supports physical units, and allows for conversion between compatible units
v116 -> 2023-05-18 : https://github.com/inventree/InvenTree/pull/4823 v116 -> 2023-05-18 : https://github.com/inventree/InvenTree/pull/4823

View File

@ -390,7 +390,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
'manufacturer', 'manufacturer',
'MPN', 'MPN',
'packaging', 'packaging',
'pack_units', 'pack_quantity',
'in_stock', 'in_stock',
'updated', 'updated',
] ]
@ -400,7 +400,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
'supplier': 'supplier__name', 'supplier': 'supplier__name',
'manufacturer': 'manufacturer_part__manufacturer__name', 'manufacturer': 'manufacturer_part__manufacturer__name',
'MPN': 'manufacturer_part__MPN', 'MPN': 'manufacturer_part__MPN',
'pack_units': ['pack_units_native', 'pack_units'], 'pack_quantity': ['pack_quantity_native', 'pack_quantity'],
} }
search_fields = [ search_fields = [

View File

@ -66,4 +66,4 @@
part: 4 part: 4
supplier: 2 supplier: 2
SKU: 'R_4K7_0603.100PCK' SKU: 'R_4K7_0603.100PCK'
pack_units: '100' pack_quantity: '100'

View File

@ -14,12 +14,12 @@ class Migration(migrations.Migration):
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='supplierpart', model_name='supplierpart',
name='pack_units', name='pack_quantity',
field=models.CharField(blank=True, help_text='Units of measure for this supplier part', max_length=25, verbose_name='Packaging Units'), field=models.CharField(blank=True, help_text='Total quantity supplied in a single pack. Leave empty for single items.', max_length=25, verbose_name='Pack Quantity'),
), ),
migrations.AddField( migrations.AddField(
model_name='supplierpart', model_name='supplierpart',
name='pack_units_native', name='pack_quantity_native',
field=InvenTree.fields.RoundingDecimalField(decimal_places=10, default=1, max_digits=20, null=True), field=InvenTree.fields.RoundingDecimalField(decimal_places=10, default=1, max_digits=20, null=True),
), ),
] ]

View File

@ -14,8 +14,8 @@ def update_supplier_part_units(apps, schema_editor):
for sp in supplier_parts: for sp in supplier_parts:
pack_size = normalize(sp.pack_size) pack_size = normalize(sp.pack_size)
sp.pack_units = str(pack_size) sp.pack_quantity = str(pack_size)
sp.pack_units_native = pack_size sp.pack_quantity_native = pack_size
sp.save() sp.save()
if supplier_parts.count() > 0: if supplier_parts.count() > 0:

View File

@ -438,8 +438,8 @@ class SupplierPart(MetadataMixin, InvenTreeBarcodeMixin, common.models.MetaMixin
multiple: Multiple that the part is provided in multiple: Multiple that the part is provided in
lead_time: Supplier lead time lead_time: Supplier lead time
packaging: packaging that the part is supplied in, e.g. "Reel" packaging: packaging that the part is supplied in, e.g. "Reel"
pack_units: Quantity of item supplied in a single pack (e.g. 30ml in a single tube) pack_quantity: Quantity of item supplied in a single pack (e.g. 30ml in a single tube)
pack_units_native: Pack units, converted to "native" units of the referenced part pack_quantity_native: Pack quantity, converted to "native" units of the referenced part
updated: Date that the SupplierPart was last updated updated: Date that the SupplierPart was last updated
""" """
@ -478,38 +478,38 @@ class SupplierPart(MetadataMixin, InvenTreeBarcodeMixin, common.models.MetaMixin
""" """
super().clean() super().clean()
self.pack_units = self.pack_units.strip() self.pack_quantity = self.pack_quantity.strip()
# An empty 'pack_units' value is equivalent to '1' # An empty 'pack_quantity' value is equivalent to '1'
if self.pack_units == '': if self.pack_quantity == '':
self.pack_units = '1' self.pack_quantity = '1'
# Validate that the UOM is compatible with the base part # Validate that the UOM is compatible with the base part
if self.pack_units and self.part: if self.pack_quantity and self.part:
try: try:
# Attempt conversion to specified unit # Attempt conversion to specified unit
native_value = InvenTree.conversion.convert_physical_value( native_value = InvenTree.conversion.convert_physical_value(
self.pack_units, self.part.units self.pack_quantity, self.part.units
) )
# If part units are not provided, value must be dimensionless # If part units are not provided, value must be dimensionless
if not self.part.units and native_value.units not in ['', 'dimensionless']: if not self.part.units and native_value.units not in ['', 'dimensionless']:
raise ValidationError({ raise ValidationError({
'pack_units': _("Pack units must be compatible with the base part units") 'pack_quantity': _("Pack units must be compatible with the base part units")
}) })
# Native value must be greater than zero # Native value must be greater than zero
if float(native_value.magnitude) <= 0: if float(native_value.magnitude) <= 0:
raise ValidationError({ raise ValidationError({
'pack_units': _("Pack units must be greater than zero") 'pack_quantity': _("Pack units must be greater than zero")
}) })
# Update native pack units value # Update native pack units value
self.pack_units_native = Decimal(native_value.magnitude) self.pack_quantity_native = Decimal(native_value.magnitude)
except ValidationError as e: except ValidationError as e:
raise ValidationError({ raise ValidationError({
'pack_units': e.messages 'pack_quantity': e.messages
}) })
# Ensure that the linked manufacturer_part points to the same part! # Ensure that the linked manufacturer_part points to the same part!
@ -601,14 +601,14 @@ class SupplierPart(MetadataMixin, InvenTreeBarcodeMixin, common.models.MetaMixin
packaging = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Packaging'), help_text=_('Part packaging')) packaging = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Packaging'), help_text=_('Part packaging'))
pack_units = models.CharField( pack_quantity = models.CharField(
max_length=25, max_length=25,
verbose_name=_('Packaging Units'), verbose_name=_('Pack Quantity'),
help_text=_('Units of measure for this supplier part'), help_text=_('Total quantity supplied in a single pack. Leave empty for single items.'),
blank=True, blank=True,
) )
pack_units_native = RoundingDecimalField( pack_quantity_native = RoundingDecimalField(
max_digits=20, decimal_places=10, default=1, max_digits=20, decimal_places=10, default=1,
null=True, null=True,
) )

View File

@ -265,7 +265,8 @@ class SupplierPartSerializer(InvenTreeTagModelSerializer):
'pk', 'pk',
'barcode_hash', 'barcode_hash',
'packaging', 'packaging',
'pack_units', 'pack_quantity',
'pack_quantity_native',
'part', 'part',
'part_detail', 'part_detail',
'pretty_name', 'pretty_name',

View File

@ -162,7 +162,7 @@ src="{% static 'img/blank_image.png' %}"
<td>{{ part.packaging }}{% include "clip.html" %}</td> <td>{{ part.packaging }}{% include "clip.html" %}</td>
</tr> </tr>
{% endif %} {% endif %}
{% if part.pack_units %} {% if part.pack_quantity %}
<tr> <tr>
<td><span class='fas fa-box'></span></td> <td><span class='fas fa-box'></span></td>
<td> <td>
@ -174,10 +174,10 @@ src="{% static 'img/blank_image.png' %}"
{% endif %} {% endif %}
</td> </td>
<td> <td>
{{ part.pack_units }} {{ part.pack_quantity }}
{% include "clip.html" %} {% include "clip.html" %}
{% if part.part.units and part.pack_units_native %} {% if part.part.units and part.pack_quantity_native %}
<span class='fas fa-info-circle float-right' title='{% decimal part.pack_units_native %} {{ part.part.units }}'></span> <span class='fas fa-info-circle float-right' title='{% decimal part.pack_quantity_native %} {{ part.part.units }}'></span>
{% endif %} {% endif %}
</td> </td>
</tr> </tr>

View File

@ -10,10 +10,10 @@ from part.models import Part
class SupplierPartPackUnitsTests(InvenTreeTestCase): class SupplierPartPackUnitsTests(InvenTreeTestCase):
"""Unit tests for the SupplierPart pack_units field""" """Unit tests for the SupplierPart pack_quantity field"""
def test_pack_units_dimensionless(self): def test_pack_quantity_dimensionless(self):
"""Test valid values for the 'pack_units' field""" """Test valid values for the 'pack_quantity' field"""
# Create a part without units (dimensionless) # Create a part without units (dimensionless)
part = Part.objects.create(name='Test Part', description='Test part description', component=True) part = Part.objects.create(name='Test Part', description='Test part description', component=True)
@ -48,17 +48,17 @@ class SupplierPartPackUnitsTests(InvenTreeTestCase):
] ]
for test, expected in pass_tests.items(): for test, expected in pass_tests.items():
sp.pack_units = test sp.pack_quantity = test
sp.full_clean() sp.full_clean()
self.assertEqual(sp.pack_units_native, expected) self.assertEqual(sp.pack_quantity_native, expected)
for test in fail_tests: for test in fail_tests:
sp.pack_units = test sp.pack_quantity = test
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
sp.full_clean() sp.full_clean()
def test_pack_units(self): def test_pack_quantity(self):
"""Test pack_units for a part with a specified dimension""" """Test pack_quantity for a part with a specified dimension"""
# Create a part with units 'm' # Create a part with units 'm'
part = Part.objects.create(name='Test Part', description='Test part description', component=True, units='m') part = Part.objects.create(name='Test Part', description='Test part description', component=True, units='m')
@ -101,14 +101,14 @@ class SupplierPartPackUnitsTests(InvenTreeTestCase):
] ]
for test, expected in pass_tests.items(): for test, expected in pass_tests.items():
sp.pack_units = test sp.pack_quantity = test
sp.full_clean() sp.full_clean()
self.assertEqual( self.assertEqual(
round(Decimal(sp.pack_units_native), 10), round(Decimal(sp.pack_quantity_native), 10),
round(Decimal(str(expected)), 10) round(Decimal(str(expected)), 10)
) )
for test in fail_tests: for test in fail_tests:
sp.pack_units = test sp.pack_quantity = test
with self.assertRaises(ValidationError): with self.assertRaises(ValidationError):
sp.full_clean() sp.full_clean()

View File

@ -656,7 +656,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
) )
# Update the number of parts received against the particular line item # Update the number of parts received against the particular line item
# Note that this quantity does *not* take the pack_size into account, it is "number of packs" # Note that this quantity does *not* take the pack_quantity into account, it is "number of packs"
line.received += quantity line.received += quantity
line.save() line.save()

View File

@ -228,7 +228,7 @@ class OrderTest(TestCase):
part=prt, part=prt,
supplier=sup, supplier=sup,
SKU='SKUx10', SKU='SKUx10',
pack_units='10', pack_quantity='10',
) )
# Create a new supplier part with smaller pack size # Create a new supplier part with smaller pack size
@ -236,7 +236,7 @@ class OrderTest(TestCase):
part=prt, part=prt,
supplier=sup, supplier=sup,
SKU='SKUx0.1', SKU='SKUx0.1',
pack_units='0.1', pack_quantity='0.1',
) )
# Record values before we start # Record values before we start

View File

@ -63,14 +63,14 @@ class PartPricingTests(InvenTreeTestCase):
supplier=self.supplier_2, supplier=self.supplier_2,
part=self.part, part=self.part,
SKU='SUP_2', SKU='SUP_2',
pack_units='2.5', pack_quantity='2.5',
) )
self.sp_3 = company.models.SupplierPart.objects.create( self.sp_3 = company.models.SupplierPart.objects.create(
supplier=self.supplier_2, supplier=self.supplier_2,
part=self.part, part=self.part,
SKU='SUP_3', SKU='SUP_3',
pack_units='10' pack_quantity='10'
) )
company.models.SupplierPriceBreak.objects.create( company.models.SupplierPriceBreak.objects.create(
@ -322,7 +322,7 @@ class PartPricingTests(InvenTreeTestCase):
# $5 AUD each # $5 AUD each
line_1 = po.add_line_item(self.sp_2, quantity=10, purchase_price=Money(5, 'AUD')) line_1 = po.add_line_item(self.sp_2, quantity=10, purchase_price=Money(5, 'AUD'))
# $30 CAD each (but pack_units is 10, so really $3 CAD each) # $30 CAD each (but pack_size is 10, so really $3 CAD each)
line_2 = po.add_line_item(self.sp_3, quantity=5, purchase_price=Money(30, 'CAD')) line_2 = po.add_line_item(self.sp_3, quantity=5, purchase_price=Money(30, 'CAD'))
pricing.update_purchase_cost() pricing.update_purchase_cost()

View File

@ -138,7 +138,7 @@ function supplierPartFields(options={}) {
packaging: { packaging: {
icon: 'fa-box', icon: 'fa-box',
}, },
pack_units: {}, pack_quantity: {},
}; };
if (options.part) { if (options.part) {
@ -1242,8 +1242,8 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
}, },
{ {
field: 'pack_units', field: 'pack_quantity',
title: '{% trans "Pack Units" %}', title: '{% trans "Pack Quantity" %}',
sortable: true, sortable: true,
formatter: function(value, row) { formatter: function(value, row) {
var output = `${value}`; var output = `${value}`;