mirror of
https://github.com/inventree/InvenTree.git
synced 2025-12-20 22:00:27 -06:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ab173d9d7 | ||
|
|
2499e97928 | ||
|
|
47a459a32c | ||
|
|
43355904b0 | ||
|
|
d7f05cdcb7 | ||
|
|
0782d86d12 | ||
|
|
e5e18f3711 | ||
|
|
c326c7845a |
@@ -19,7 +19,7 @@ from dulwich.repo import NotGitRepository, Repo
|
||||
from .api_version import INVENTREE_API_TEXT, INVENTREE_API_VERSION
|
||||
|
||||
# InvenTree software version
|
||||
INVENTREE_SW_VERSION = "0.13.5"
|
||||
INVENTREE_SW_VERSION = "0.13.6"
|
||||
|
||||
# Discover git
|
||||
try:
|
||||
|
||||
@@ -904,18 +904,24 @@ class BuildAllocationSerializer(serializers.Serializer):
|
||||
if build_line.bom_item.consumable:
|
||||
continue
|
||||
|
||||
params = {
|
||||
"build_line": build_line,
|
||||
"stock_item": stock_item,
|
||||
"install_into": output,
|
||||
}
|
||||
|
||||
try:
|
||||
# Create a new BuildItem to allocate stock
|
||||
build_item, created = BuildItem.objects.get_or_create(
|
||||
build_line=build_line,
|
||||
stock_item=stock_item,
|
||||
install_into=output,
|
||||
)
|
||||
if created:
|
||||
build_item.quantity = quantity
|
||||
else:
|
||||
if build_item := BuildItem.objects.filter(**params).first():
|
||||
# Find an existing BuildItem for this stock item
|
||||
# If it exists, increase the quantity
|
||||
build_item.quantity += quantity
|
||||
build_item.save()
|
||||
build_item.save()
|
||||
else:
|
||||
# Create a new BuildItem to allocate stock
|
||||
build_item = BuildItem.objects.create(
|
||||
quantity=quantity,
|
||||
**params
|
||||
)
|
||||
except (ValidationError, DjangoValidationError) as exc:
|
||||
# Catch model errors and re-throw as DRF errors
|
||||
raise ValidationError(detail=serializers.as_serializer_error(exc))
|
||||
@@ -1019,7 +1025,7 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
||||
"""Determine which extra details fields should be included"""
|
||||
part_detail = kwargs.pop('part_detail', True)
|
||||
location_detail = kwargs.pop('location_detail', True)
|
||||
stock_detail = kwargs.pop('stock_detail', False)
|
||||
stock_detail = kwargs.pop('stock_detail', True)
|
||||
build_detail = kwargs.pop('build_detail', False)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -822,6 +822,58 @@ class BuildAllocationTest(BuildAPITest):
|
||||
allocation.refresh_from_db()
|
||||
self.assertEqual(allocation.quantity, 5000)
|
||||
|
||||
def test_fractional_allocation(self):
|
||||
"""Test allocation of a fractional quantity of stock items.
|
||||
|
||||
Ref: https://github.com/inventree/InvenTree/issues/6508
|
||||
"""
|
||||
|
||||
si = StockItem.objects.get(pk=2)
|
||||
|
||||
# Find line item
|
||||
line = self.build.build_lines.all().filter(bom_item__sub_part=si.part).first()
|
||||
|
||||
# Test a fractional quantity when the *available* quantity is greater than 1
|
||||
si.quantity = 100
|
||||
si.save()
|
||||
|
||||
self.post(
|
||||
self.url,
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"build_line": line.pk,
|
||||
"stock_item": si.pk,
|
||||
"quantity": 0.1616,
|
||||
}
|
||||
]
|
||||
},
|
||||
expected_code=201
|
||||
)
|
||||
|
||||
# Test a fractional quantity when the *available* quantity is less than 1
|
||||
si = StockItem.objects.create(
|
||||
part=si.part,
|
||||
quantity=0.3159,
|
||||
tree_id=0,
|
||||
level=0,
|
||||
lft=0, rght=0
|
||||
)
|
||||
|
||||
self.post(
|
||||
self.url,
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"build_line": line.pk,
|
||||
"stock_item": si.pk,
|
||||
"quantity": 0.1616,
|
||||
}
|
||||
]
|
||||
},
|
||||
expected_code=201,
|
||||
)
|
||||
|
||||
|
||||
class BuildOverallocationTest(BuildAPITest):
|
||||
"""Unit tests for over allocation of stock items against a build order.
|
||||
|
||||
@@ -225,10 +225,10 @@ class SupplierBarcodeMixin(BarcodeMixin):
|
||||
return None
|
||||
|
||||
if supplier_pk := self.get_setting("SUPPLIER_ID"):
|
||||
if (supplier := Company.objects.get(pk=supplier_pk)):
|
||||
return supplier
|
||||
else:
|
||||
logger.error(
|
||||
try:
|
||||
return Company.objects.get(pk=supplier_pk)
|
||||
except Company.DoesNotExist:
|
||||
logger.exception(
|
||||
"No company with pk %d (set \"SUPPLIER_ID\" setting to a valid value)",
|
||||
supplier_pk
|
||||
)
|
||||
|
||||
@@ -1237,6 +1237,14 @@ class StockMergeSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
|
||||
def stock_item_adjust_status_options():
|
||||
"""Return a custom set of options for the StockItem status adjustment field.
|
||||
|
||||
In particular, include a Null option for the status field.
|
||||
"""
|
||||
return [(None, _('No Change'))] + InvenTree.status_codes.StockStatus.items()
|
||||
|
||||
|
||||
class StockAdjustmentItemSerializer(serializers.Serializer):
|
||||
"""Serializer for a single StockItem within a stock adjument request.
|
||||
|
||||
@@ -1284,8 +1292,8 @@ class StockAdjustmentItemSerializer(serializers.Serializer):
|
||||
)
|
||||
|
||||
status = serializers.ChoiceField(
|
||||
choices=InvenTree.status_codes.StockStatus.items(),
|
||||
default=InvenTree.status_codes.StockStatus.OK.value,
|
||||
choices=stock_item_adjust_status_options(),
|
||||
default=None,
|
||||
label=_('Status'),
|
||||
help_text=_('Stock item status code'),
|
||||
required=False, allow_blank=True,
|
||||
@@ -1443,8 +1451,8 @@ class StockTransferSerializer(StockAdjustmentSerializer):
|
||||
kwargs = {}
|
||||
|
||||
for field_name in StockItem.optional_transfer_fields():
|
||||
if field_name in item:
|
||||
kwargs[field_name] = item[field_name]
|
||||
if field_value := item.get(field_name, None):
|
||||
kwargs[field_name] = field_value
|
||||
|
||||
stock_item.move(
|
||||
location,
|
||||
|
||||
@@ -966,7 +966,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
switchable: false,
|
||||
title: '{% jstrans "Build Order" %}',
|
||||
formatter: function(value, row) {
|
||||
let ref = `${row.build_detail.reference}`;
|
||||
let ref = row.build_detail?.reference ?? row.build;
|
||||
let html = renderLink(ref, `/build/${row.build}/`);
|
||||
|
||||
html += `- <small>${row.build_detail.title}</small>`;
|
||||
|
||||
@@ -226,7 +226,7 @@ function createSupplierPart(options={}) {
|
||||
var header = '';
|
||||
if (options.part) {
|
||||
var part_model = {};
|
||||
inventreeGet(`{% url "api-part-list" %}${options.part}/.*`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${options.part}/`, {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
part_model = response;
|
||||
|
||||
@@ -33,7 +33,7 @@ coverage==5.5
|
||||
# coveralls
|
||||
coveralls==2.1.2
|
||||
# via -r requirements-dev.in
|
||||
cryptography==41.0.6
|
||||
cryptography==42.0.4
|
||||
# via
|
||||
# -c requirements.txt
|
||||
# pdfminer-six
|
||||
|
||||
@@ -32,7 +32,7 @@ coreapi==2.3.3
|
||||
# via -r requirements.in
|
||||
coreschema==0.0.4
|
||||
# via coreapi
|
||||
cryptography==41.0.6
|
||||
cryptography==42.0.4
|
||||
# via
|
||||
# -r requirements.in
|
||||
# djangorestframework-simplejwt
|
||||
|
||||
Reference in New Issue
Block a user