diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index a4bf2430fd..ffb846507e 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -34,6 +34,7 @@ from generic.states.fields import InvenTreeCustomStatusSerializerMixin from InvenTree.mixins import DataImportExportSerializerMixin from InvenTree.serializers import ( CustomStatusSerializerMixin, + DuplicateOptionsSerializer, FilterableSerializerMixin, InvenTreeDecimalField, InvenTreeModelSerializer, @@ -67,6 +68,11 @@ class BuildSerializer( ): """Serializes a Build object.""" + export_exclude_fields = ['duplicate'] + import_exclude_fields = ['duplicate'] + + SKIP_CREATE_FIELDS = ['duplicate'] + class Meta: """Serializer metaclass.""" @@ -80,6 +86,7 @@ class BuildSerializer( 'completed', 'completion_date', 'destination', + 'duplicate', 'external', 'parent', 'part', @@ -190,12 +197,29 @@ class BuildSerializer( return queryset + duplicate = DuplicateOptionsSerializer(Build.objects.all(), copy_parameters=True) + def __init__(self, *args, **kwargs): """Determine if extra serializer fields are required.""" kwargs.pop('create', False) super().__init__(*args, **kwargs) + @transaction.atomic + def create(self, validated_data): + """Create a new Build instance, optionally copying data from an existing build.""" + duplicate = validated_data.pop('duplicate', None) + + instance = super().create(validated_data) + + if duplicate: + original = duplicate['original'] + + if duplicate.get('copy_parameters', True): + instance.copy_parameters_from(original) + + return instance + def validate_reference(self, reference): """Custom validation for the Build reference field.""" # Ensure the reference matches the required pattern diff --git a/src/frontend/src/forms/BuildForms.tsx b/src/frontend/src/forms/BuildForms.tsx index 2f694c841c..d9a03040cd 100644 --- a/src/frontend/src/forms/BuildForms.tsx +++ b/src/frontend/src/forms/BuildForms.tsx @@ -43,9 +43,11 @@ import { ProjectCodeField, TagsField } from './CommonFields'; */ export function useBuildOrderFields({ create, + duplicateBuildId, modalId }: { create: boolean; + duplicateBuildId?: number | null; modalId: string; }): ApiFormFieldSet { const [destination, setDestination] = useState( @@ -133,7 +135,16 @@ export function useBuildOrderFields({ is_active: true } }, - external: {} + external: {}, + duplicate: { + children: { + original: { + value: duplicateBuildId, + hidden: true + }, + copy_parameters: {} + } + } }; if (!globalSettings.isSet('PROJECT_CODES_ENABLED', true)) { @@ -144,8 +155,19 @@ export function useBuildOrderFields({ delete fields.external; } + if (!duplicateBuildId) { + delete fields.duplicate; + } + return fields; - }, [create, destination, batchCode, batchGenerator.result, globalSettings]); + }, [ + create, + destination, + batchCode, + batchGenerator.result, + globalSettings, + duplicateBuildId + ]); } export function useBuildOrderOutputFields({ diff --git a/src/frontend/src/pages/build/BuildDetail.tsx b/src/frontend/src/pages/build/BuildDetail.tsx index 6b8d8e3459..7fbd0d3307 100644 --- a/src/frontend/src/pages/build/BuildDetail.tsx +++ b/src/frontend/src/pages/build/BuildDetail.tsx @@ -632,6 +632,7 @@ export default function BuildDetail() { const duplicateBuildOrderFields = useBuildOrderFields({ create: false, + duplicateBuildId: build.pk, modalId: 'duplicate-build-order' });