2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-18 18:56:31 +00:00

Merge branch 'master' of https://github.com/inventree/InvenTree into matmair/issue6281

This commit is contained in:
Matthias Mair
2025-01-20 21:16:14 +01:00
35 changed files with 1304 additions and 547 deletions

@@ -11,6 +11,10 @@ v303 - 2025-01-20 : https://github.com/inventree/InvenTree/pull/6293
- Removes a considerable amount of old auth endpoints
- Introduces allauth based REST API
v303 - 2025-01-20 - https://github.com/inventree/InvenTree/pull/8915
- Adds "start_date" field to Build model and API endpoints
- Adds additional API filtering and sorting options for Build list
v302 - 2025-01-18 - https://github.com/inventree/InvenTree/pull/8905
- Fix schema definition on the /label/print endpoint

@@ -188,6 +188,30 @@ class BuildFilter(rest_filters.FilterSet):
label=_('Created after'), field_name='creation_date', lookup_expr='gt'
)
has_start_date = rest_filters.BooleanFilter(
label=_('Has start date'), method='filter_has_start_date'
)
def filter_has_start_date(self, queryset, name, value):
"""Filter by whether or not the order has a start date."""
return queryset.filter(start_date__isnull=not str2bool(value))
start_date_before = InvenTreeDateFilter(
label=_('Start date before'), field_name='start_date', lookup_expr='lt'
)
start_date_after = InvenTreeDateFilter(
label=_('Start date after'), field_name='start_date', lookup_expr='gt'
)
has_target_date = rest_filters.BooleanFilter(
label=_('Has target date'), method='filter_has_target_date'
)
def filter_has_target_date(self, queryset, name, value):
"""Filter by whether or not the order has a target date."""
return queryset.filter(target_date__isnull=not str2bool(value))
target_date_before = InvenTreeDateFilter(
label=_('Target date before'), field_name='target_date', lookup_expr='lt'
)
@@ -244,6 +268,7 @@ class BuildList(DataExportViewMixin, BuildMixin, ListCreateAPI):
'part__name',
'status',
'creation_date',
'start_date',
'target_date',
'completion_date',
'quantity',

@@ -0,0 +1,18 @@
# Generated by Django 4.2.18 on 2025-01-20 02:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0053_alter_build_part'),
]
operations = [
migrations.AddField(
model_name='build',
name='start_date',
field=models.DateField(blank=True, help_text='Scheduled start date for this build order', null=True, verbose_name='Build start date'),
),
]

@@ -67,7 +67,7 @@ class Build(
Attributes:
part: The part to be built (from component BOM items)
reference: Build order reference (required, must be unique)
title: Brief title describing the build (required)
title: Brief title describing the build (optional)
quantity: Number of units to be built
parent: Reference to a Build object for which this Build is required
sales_order: References to a SalesOrder object for which this Build is required (e.g. the output of this build will be used to fulfil a sales order)
@@ -178,6 +178,12 @@ class Build(
if self.has_field_changed('part'):
raise ValidationError({'part': _('Build order part cannot be changed')})
# Target date should be *after* the start date
if self.start_date and self.target_date and self.start_date > self.target_date:
raise ValidationError({
'target_date': _('Target date must be after start date')
})
def report_context(self) -> dict:
"""Generate custom report context data."""
return {
@@ -344,6 +350,13 @@ class Build(
auto_now_add=True, editable=False, verbose_name=_('Creation Date')
)
start_date = models.DateField(
null=True,
blank=True,
verbose_name=_('Build start date'),
help_text=_('Scheduled start date for this build order'),
)
target_date = models.DateField(
null=True,
blank=True,

@@ -81,6 +81,7 @@ class BuildSerializer(
'reference',
'sales_order',
'quantity',
'start_date',
'status',
'status_text',
'status_custom_key',

@@ -1003,6 +1003,12 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
'validator': bool,
'after_save': reload_plugin_registry,
},
'PROJECT_CODES_ENABLED': {
'name': _('Enable project codes'),
'description': _('Enable project codes for tracking projects'),
'default': False,
'validator': bool,
},
'STOCKTAKE_ENABLE': {
'name': _('Stocktake Functionality'),
'description': _(

@@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
import tablib
from rest_framework import fields, serializers
from taggit.serializers import TagListSerializerField
import importer.operations
from InvenTree.helpers import DownloadFile, GetExportFormats, current_date
@@ -81,6 +82,11 @@ class DataImportSerializerMixin:
if issubclass(field.__class__, fields.FileField):
continue
# Skip tags fields
# TODO: Implement tag field support
if issubclass(field.__class__, TagListSerializerField):
continue
importable_fields[name] = field
return importable_fields

@@ -683,7 +683,7 @@ class PartSerializer(
Used when displaying all details of a single component.
"""
import_exclude_fields = ['duplicate', 'tags']
import_exclude_fields = ['duplicate']
class Meta:
"""Metaclass defining serializer fields."""

@@ -1985,9 +1985,18 @@ class StockItem(
Returns:
The new StockItem object
Raises:
ValidationError: If the stock item cannot be split
- The provided quantity will be subtracted from this item and given to the new one.
- The new item will have a different StockItem ID, while this will remain the same.
"""
# Run initial checks to test if the stock item can actually be "split"
# Cannot split a stock item which is in production
if self.is_building:
raise ValidationError(_('Stock item is currently in production'))
notes = kwargs.get('notes', '')
# Do not split a serialized part

@@ -344,7 +344,7 @@ class StockItemSerializer(
export_only_fields = ['part_pricing_min', 'part_pricing_max']
import_exclude_fields = ['use_pack_size', 'tags']
import_exclude_fields = ['use_pack_size']
class Meta:
"""Metaclass options."""
@@ -1142,7 +1142,7 @@ class LocationSerializer(
):
"""Detailed information about a stock location."""
import_exclude_fields = ['tags']
import_exclude_fields = []
class Meta:
"""Metaclass options."""
@@ -1565,18 +1565,18 @@ class StockAdjustmentItemSerializer(serializers.Serializer):
help_text=_('StockItem primary key value'),
)
def validate_pk(self, pk):
def validate_pk(self, stock_item: StockItem) -> StockItem:
"""Ensure the stock item is valid."""
allow_out_of_stock_transfer = get_global_setting(
'STOCK_ALLOW_OUT_OF_STOCK_TRANSFER', backup_value=False, cache=False
)
if not allow_out_of_stock_transfer and not pk.is_in_stock(
if not allow_out_of_stock_transfer and not stock_item.is_in_stock(
check_status=False, check_quantity=False
):
raise ValidationError(_('Stock item is not in stock'))
return pk
return stock_item
quantity = serializers.DecimalField(
max_digits=15, decimal_places=5, min_value=Decimal(0), required=True