mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 11:10:54 +00:00
Part units (#4854)
* Add validation to part units field * Add "pack_units" field to the SupplierPart model * Migrate old units to new units, and remove old field * Table fix * Fixture fix * Update migration * Improve "hook" for loading custom unit database * Display part units column in part table - Also allow ordering by part units - Allow filtering to show parts which have defined units * Adds data migration for converting units to valid values * Add "pack_units_native" field to company.SupplierPart model * Clean pack units when saving a SupplierPart - Convert to native part units - Handle empty units value - Add unit tests * Add background function to rebuild supplier parts when a part is saved - Required to ensure that the "pack_size_native" is up to date * Template updates * Sort by native units first * Bump API version * Rename "pack_units" to "pack_quantity" * Update migration file - Allow reverse migration * Fix for currency migration - Handle case where no currencies are provided - Handle case where base currency is not in provided options * Adds unit test for data migration * Add unit test for part.units data migration - Check that units fields are updated correctly * Add some extra "default units" - each / piece - dozen / hundred / thousand - Add unit testing also * Update references to "pack_size" - Replace with "pack_quantity" or "pack_quantity_native" as appropriate * Improvements based on unit testing * catch error * Docs updates * Fixes for pricing tests * Update unit tests for part migrations · 1b6b6d9d * Bug fix for conversion code * javascript updates * JS formatting fix
This commit is contained in:
@ -602,11 +602,13 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
# Create a new stock item
|
||||
if line.part and quantity > 0:
|
||||
|
||||
# Take the 'pack_size' of the SupplierPart into account
|
||||
pack_quantity = Decimal(quantity) * Decimal(line.part.pack_size)
|
||||
# Calculate received quantity in base units
|
||||
stock_quantity = line.part.base_quantity(quantity)
|
||||
|
||||
# Calculate unit purchase price (in base units)
|
||||
if line.purchase_price:
|
||||
unit_purchase_price = line.purchase_price / line.part.pack_size
|
||||
unit_purchase_price = line.purchase_price
|
||||
unit_purchase_price /= line.part.base_quantity(1)
|
||||
else:
|
||||
unit_purchase_price = None
|
||||
|
||||
@ -623,7 +625,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
part=line.part.part,
|
||||
supplier_part=line.part,
|
||||
location=location,
|
||||
quantity=1 if serialize else pack_quantity,
|
||||
quantity=1 if serialize else stock_quantity,
|
||||
purchase_order=self,
|
||||
status=status,
|
||||
batch=batch_code,
|
||||
@ -656,7 +658,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
)
|
||||
|
||||
# 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.save()
|
||||
|
||||
|
@ -558,14 +558,12 @@ class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer):
|
||||
serial_numbers = data.get('serial_numbers', '').strip()
|
||||
|
||||
base_part = line_item.part.part
|
||||
pack_size = line_item.part.pack_size
|
||||
|
||||
pack_quantity = pack_size * quantity
|
||||
base_quantity = line_item.part.base_quantity(quantity)
|
||||
|
||||
# Does the quantity need to be "integer" (for trackable parts?)
|
||||
if base_part.trackable:
|
||||
|
||||
if Decimal(pack_quantity) != int(pack_quantity):
|
||||
if Decimal(base_quantity) != int(base_quantity):
|
||||
raise ValidationError({
|
||||
'quantity': _('An integer quantity must be provided for trackable parts'),
|
||||
})
|
||||
@ -576,7 +574,7 @@ class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer):
|
||||
# Pass the serial numbers through to the parent serializer once validated
|
||||
data['serials'] = extract_serial_numbers(
|
||||
serial_numbers,
|
||||
pack_quantity,
|
||||
base_quantity,
|
||||
base_part.get_latest_serial_number()
|
||||
)
|
||||
except DjangoValidationError as e:
|
||||
|
@ -228,7 +228,7 @@ class OrderTest(TestCase):
|
||||
part=prt,
|
||||
supplier=sup,
|
||||
SKU='SKUx10',
|
||||
pack_size=10,
|
||||
pack_quantity='10',
|
||||
)
|
||||
|
||||
# Create a new supplier part with smaller pack size
|
||||
@ -236,7 +236,7 @@ class OrderTest(TestCase):
|
||||
part=prt,
|
||||
supplier=sup,
|
||||
SKU='SKUx0.1',
|
||||
pack_size=0.1,
|
||||
pack_quantity='0.1',
|
||||
)
|
||||
|
||||
# Record values before we start
|
||||
|
Reference in New Issue
Block a user