Implement duplicate action for stock item
This commit is contained in:
parent
ebabc3359f
commit
427eb6d4b3
|
|
@ -1250,9 +1250,28 @@ class StockList(
|
|||
serializer = self.get_serializer(data=data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
# Extract 'duplicate' options (if provided) - these are not valid model fields
|
||||
duplicate = serializer.validated_data.pop('duplicate', None)
|
||||
|
||||
# Extract location information
|
||||
location = serializer.validated_data.get('location', None)
|
||||
|
||||
def apply_duplicate_options(item):
|
||||
"""Apply any provided 'duplicate' options to a newly created StockItem."""
|
||||
if not duplicate:
|
||||
return
|
||||
|
||||
original = duplicate['original']
|
||||
|
||||
if duplicate.get('copy_notes', True):
|
||||
item.copy_notes_from(original)
|
||||
|
||||
if duplicate.get('copy_history', False):
|
||||
item.copyHistoryFrom(original)
|
||||
|
||||
if duplicate.get('copy_tests', False):
|
||||
item.copyTestResultsFrom(original)
|
||||
|
||||
with transaction.atomic():
|
||||
if serials:
|
||||
# Create multiple serialized StockItem objects
|
||||
|
|
@ -1268,6 +1287,8 @@ class StockList(
|
|||
item.set_status(status_value)
|
||||
item.save()
|
||||
|
||||
apply_duplicate_options(item)
|
||||
|
||||
if entry := item.add_tracking_entry(
|
||||
StockHistoryCode.CREATED,
|
||||
user,
|
||||
|
|
@ -1300,6 +1321,8 @@ class StockList(
|
|||
item.save(user=user)
|
||||
item.refresh_from_db()
|
||||
|
||||
apply_duplicate_options(item)
|
||||
|
||||
response_data = [
|
||||
StockSerializers.StockItemSerializer(
|
||||
item, context=self.get_serializer_context()
|
||||
|
|
|
|||
|
|
@ -324,6 +324,8 @@ class StockItemSerializer(
|
|||
|
||||
export_exclude_fields = ['tags', 'tracking_items']
|
||||
|
||||
SKIP_CREATE_FIELDS = ['duplicate']
|
||||
|
||||
export_child_fields = [
|
||||
'part_detail.name',
|
||||
'part_detail.description',
|
||||
|
|
@ -380,6 +382,7 @@ class StockItemSerializer(
|
|||
'purchase_price_currency',
|
||||
'use_pack_size',
|
||||
'serial_numbers',
|
||||
'duplicate',
|
||||
# Annotated fields
|
||||
'allocated',
|
||||
'expired',
|
||||
|
|
@ -467,6 +470,28 @@ class StockItemSerializer(
|
|||
help_text=_('Enter serial numbers for new items'),
|
||||
)
|
||||
|
||||
# Extra field used only for creation of a new StockItem instance
|
||||
duplicate = InvenTree.serializers.DuplicateOptionsSerializer(
|
||||
StockItem.objects.all(),
|
||||
label=_('Duplicate Stock Item'),
|
||||
help_text=_('Copy initial data from another stock item'),
|
||||
copy_notes=True,
|
||||
copy_fields=[
|
||||
{
|
||||
'name': 'copy_tests',
|
||||
'label': _('Copy Test Results'),
|
||||
'help_text': _('Copy test results from the original stock item'),
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'name': 'copy_history',
|
||||
'label': _('Copy History'),
|
||||
'help_text': _('Copy stock history from the original stock item'),
|
||||
'default': False,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
||||
def validate_part(self, part):
|
||||
"""Ensure the provided Part instance is valid."""
|
||||
if part.virtual:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import { modals } from '@mantine/modals';
|
|||
import {
|
||||
IconCalendarExclamation,
|
||||
IconCoins,
|
||||
IconCopy,
|
||||
IconCurrencyDollar,
|
||||
IconLink,
|
||||
IconPackage,
|
||||
|
|
@ -81,7 +82,8 @@ export function useStockFields({
|
|||
create = false,
|
||||
supplierPartId,
|
||||
pricing,
|
||||
modalId
|
||||
modalId,
|
||||
duplicateStockItem
|
||||
}: {
|
||||
partId?: number;
|
||||
stockItem?: any;
|
||||
|
|
@ -89,6 +91,7 @@ export function useStockFields({
|
|||
create: boolean;
|
||||
supplierPartId?: number;
|
||||
pricing?: { [priceBreak: number]: [number, string] };
|
||||
duplicateStockItem?: any;
|
||||
}): ApiFormFieldSet {
|
||||
const globalSettings = useGlobalSettingsState();
|
||||
|
||||
|
|
@ -301,6 +304,25 @@ export function useStockFields({
|
|||
delete fields.serial_numbers;
|
||||
}
|
||||
|
||||
// Additional fields for stock item duplication
|
||||
if (create && duplicateStockItem?.pk) {
|
||||
fields.duplicate = {
|
||||
icon: <IconCopy />,
|
||||
children: {
|
||||
original: {
|
||||
value: duplicateStockItem.pk,
|
||||
hidden: true
|
||||
},
|
||||
copy_notes: { value: true },
|
||||
copy_history: { value: false },
|
||||
copy_tests: {
|
||||
value: false,
|
||||
hidden: !duplicateStockItem?.part_detail?.testable
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return fields;
|
||||
}, [
|
||||
stockItem,
|
||||
|
|
@ -315,6 +337,7 @@ export function useStockFields({
|
|||
purchasePriceCurrency,
|
||||
serialGenerator.result,
|
||||
batchGenerator.result,
|
||||
duplicateStockItem,
|
||||
create
|
||||
]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -742,7 +742,8 @@ export default function StockDetail() {
|
|||
|
||||
const duplicateStockItemFields = useStockFields({
|
||||
create: true,
|
||||
modalId: 'duplicate-stock-item'
|
||||
modalId: 'duplicate-stock-item',
|
||||
duplicateStockItem: stockitem
|
||||
});
|
||||
|
||||
const duplicateStockData = useMemo(() => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue