mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
Duplicate orders via API (#8145)
* Refactor API endpoint for duplicating line items from a purchase order - Previously was "hidden" (undocumented) - Cleanup / refactor code - Now matches part duplication options - Generic implementation supports all order types * Update forms * Refactor line item duplication * Implement front-end support for return orders * Enable duplication of sales orders from PUI * Bump API version
This commit is contained in:
parent
461317c5c5
commit
6a8875a4a6
@ -1,13 +1,16 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 254
|
INVENTREE_API_VERSION = 255
|
||||||
|
|
||||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
|
||||||
|
v255 - 2024-09-19 : https://github.com/inventree/InvenTree/pull/8145
|
||||||
|
- Enables copying line items when duplicating an order
|
||||||
|
|
||||||
v254 - 2024-09-14 : https://github.com/inventree/InvenTree/pull/7470
|
v254 - 2024-09-14 : https://github.com/inventree/InvenTree/pull/7470
|
||||||
- Implements new API endpoints for enabling custom UI functionality via plugins
|
- Implements new API endpoints for enabling custom UI functionality via plugins
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ from typing import cast
|
|||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.auth import authenticate, login
|
from django.contrib.auth import authenticate, login
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import F, Q
|
from django.db.models import F, Q
|
||||||
from django.http.response import JsonResponse
|
from django.http.response import JsonResponse
|
||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
@ -14,7 +13,6 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from django_ical.views import ICalFeed
|
from django_ical.views import ICalFeed
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.exceptions import ValidationError
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
@ -214,54 +212,6 @@ class PurchaseOrderList(PurchaseOrderMixin, DataExportViewMixin, ListCreateAPI):
|
|||||||
|
|
||||||
filterset_class = PurchaseOrderFilter
|
filterset_class = PurchaseOrderFilter
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
|
||||||
"""Save user information on create."""
|
|
||||||
data = self.clean_data(request.data)
|
|
||||||
|
|
||||||
duplicate_order = data.pop('duplicate_order', None)
|
|
||||||
duplicate_line_items = str2bool(data.pop('duplicate_line_items', False))
|
|
||||||
duplicate_extra_lines = str2bool(data.pop('duplicate_extra_lines', False))
|
|
||||||
|
|
||||||
if duplicate_order is not None:
|
|
||||||
try:
|
|
||||||
duplicate_order = models.PurchaseOrder.objects.get(pk=duplicate_order)
|
|
||||||
except (ValueError, models.PurchaseOrder.DoesNotExist):
|
|
||||||
raise ValidationError({
|
|
||||||
'duplicate_order': [_('No matching purchase order found')]
|
|
||||||
})
|
|
||||||
|
|
||||||
serializer = self.get_serializer(data=data)
|
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
|
||||||
order = serializer.save()
|
|
||||||
order.created_by = request.user
|
|
||||||
order.save()
|
|
||||||
|
|
||||||
# Duplicate line items from other order if required
|
|
||||||
if duplicate_order is not None:
|
|
||||||
if duplicate_line_items:
|
|
||||||
for line in duplicate_order.lines.all():
|
|
||||||
# Copy the line across to the new order
|
|
||||||
line.pk = None
|
|
||||||
line.order = order
|
|
||||||
line.received = 0
|
|
||||||
|
|
||||||
line.save()
|
|
||||||
|
|
||||||
if duplicate_extra_lines:
|
|
||||||
for line in duplicate_order.extra_lines.all():
|
|
||||||
# Copy the line across to the new order
|
|
||||||
line.pk = None
|
|
||||||
line.order = order
|
|
||||||
|
|
||||||
line.save()
|
|
||||||
|
|
||||||
headers = self.get_success_headers(serializer.data)
|
|
||||||
return Response(
|
|
||||||
serializer.data, status=status.HTTP_201_CREATED, headers=headers
|
|
||||||
)
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
def filter_queryset(self, queryset):
|
||||||
"""Custom queryset filtering."""
|
"""Custom queryset filtering."""
|
||||||
# Perform basic filtering
|
# Perform basic filtering
|
||||||
|
@ -247,6 +247,16 @@ class Order(
|
|||||||
'contact': _('Contact does not match selected company')
|
'contact': _('Contact does not match selected company')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
def clean_line_item(self, line):
|
||||||
|
"""Clean a line item for this order.
|
||||||
|
|
||||||
|
Used when duplicating an existing line item,
|
||||||
|
to ensure it is 'fresh'.
|
||||||
|
"""
|
||||||
|
line.pk = None
|
||||||
|
line.target_date = None
|
||||||
|
line.order = self
|
||||||
|
|
||||||
def report_context(self):
|
def report_context(self):
|
||||||
"""Generate context data for the reporting interface."""
|
"""Generate context data for the reporting interface."""
|
||||||
return {
|
return {
|
||||||
@ -379,6 +389,11 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
verbose_name = _('Purchase Order')
|
verbose_name = _('Purchase Order')
|
||||||
|
|
||||||
|
def clean_line_item(self, line):
|
||||||
|
"""Clean a line item for this PurchaseOrder."""
|
||||||
|
super().clean_line_item(line)
|
||||||
|
line.received = 0
|
||||||
|
|
||||||
def report_context(self):
|
def report_context(self):
|
||||||
"""Return report context data for this PurchaseOrder."""
|
"""Return report context data for this PurchaseOrder."""
|
||||||
return {**super().report_context(), 'supplier': self.supplier}
|
return {**super().report_context(), 'supplier': self.supplier}
|
||||||
@ -892,6 +907,11 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
verbose_name = _('Sales Order')
|
verbose_name = _('Sales Order')
|
||||||
|
|
||||||
|
def clean_line_item(self, line):
|
||||||
|
"""Clean a line item for this SalesOrder."""
|
||||||
|
super().clean_line_item(line)
|
||||||
|
line.shipped = 0
|
||||||
|
|
||||||
def report_context(self):
|
def report_context(self):
|
||||||
"""Generate report context data for this SalesOrder."""
|
"""Generate report context data for this SalesOrder."""
|
||||||
return {**super().report_context(), 'customer': self.customer}
|
return {**super().report_context(), 'customer': self.customer}
|
||||||
@ -2083,6 +2103,12 @@ class ReturnOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
verbose_name = _('Return Order')
|
verbose_name = _('Return Order')
|
||||||
|
|
||||||
|
def clean_line_item(self, line):
|
||||||
|
"""Clean a line item for this ReturnOrder."""
|
||||||
|
super().clean_line_item(line)
|
||||||
|
line.received_date = None
|
||||||
|
line.outcome = ReturnOrderLineStatus.PENDING.value
|
||||||
|
|
||||||
def report_context(self):
|
def report_context(self):
|
||||||
"""Generate report context data for this ReturnOrder."""
|
"""Generate report context data for this ReturnOrder."""
|
||||||
return {**super().report_context(), 'customer': self.customer}
|
return {**super().report_context(), 'customer': self.customer}
|
||||||
|
@ -74,10 +74,39 @@ class TotalPriceMixin(serializers.Serializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DuplicateOrderSerializer(serializers.Serializer):
|
||||||
|
"""Serializer for specifying options when duplicating an order."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
fields = ['order_id', 'copy_lines', 'copy_extra_lines']
|
||||||
|
|
||||||
|
order_id = serializers.IntegerField(
|
||||||
|
required=True, label=_('Order ID'), help_text=_('ID of the order to duplicate')
|
||||||
|
)
|
||||||
|
|
||||||
|
copy_lines = serializers.BooleanField(
|
||||||
|
required=False,
|
||||||
|
default=True,
|
||||||
|
label=_('Copy Lines'),
|
||||||
|
help_text=_('Copy line items from the original order'),
|
||||||
|
)
|
||||||
|
|
||||||
|
copy_extra_lines = serializers.BooleanField(
|
||||||
|
required=False,
|
||||||
|
default=True,
|
||||||
|
label=_('Copy Extra Lines'),
|
||||||
|
help_text=_('Copy extra line items from the original order'),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Serializer):
|
class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Serializer):
|
||||||
"""Abstract serializer class which provides fields common to all order types."""
|
"""Abstract serializer class which provides fields common to all order types."""
|
||||||
|
|
||||||
export_exclude_fields = ['notes']
|
export_exclude_fields = ['notes', 'duplicate']
|
||||||
|
|
||||||
|
import_exclude_fields = ['notes', 'duplicate']
|
||||||
|
|
||||||
# Number of line items in this order
|
# Number of line items in this order
|
||||||
line_items = serializers.IntegerField(read_only=True, label=_('Line Items'))
|
line_items = serializers.IntegerField(read_only=True, label=_('Line Items'))
|
||||||
@ -127,6 +156,13 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
required=False, allow_null=True, label=_('Creation Date')
|
required=False, allow_null=True, label=_('Creation Date')
|
||||||
)
|
)
|
||||||
|
|
||||||
|
duplicate = DuplicateOrderSerializer(
|
||||||
|
label=_('Duplicate Order'),
|
||||||
|
help_text=_('Specify options for duplicating this order'),
|
||||||
|
required=False,
|
||||||
|
write_only=True,
|
||||||
|
)
|
||||||
|
|
||||||
def validate_reference(self, reference):
|
def validate_reference(self, reference):
|
||||||
"""Custom validation for the reference field."""
|
"""Custom validation for the reference field."""
|
||||||
self.Meta.model.validate_reference_field(reference)
|
self.Meta.model.validate_reference_field(reference)
|
||||||
@ -166,9 +202,49 @@ class AbstractOrderSerializer(DataImportExportSerializerMixin, serializers.Seria
|
|||||||
'notes',
|
'notes',
|
||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
'overdue',
|
'overdue',
|
||||||
|
'duplicate',
|
||||||
*extra_fields,
|
*extra_fields,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def clean_line_item(self, line):
|
||||||
|
"""Clean a line item object (when duplicating)."""
|
||||||
|
line.pk = None
|
||||||
|
line.order = self
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""Create a new order object.
|
||||||
|
|
||||||
|
Optionally, copy line items from an existing order.
|
||||||
|
"""
|
||||||
|
duplicate = validated_data.pop('duplicate', None)
|
||||||
|
|
||||||
|
instance = super().create(validated_data)
|
||||||
|
|
||||||
|
if duplicate:
|
||||||
|
order_id = duplicate.get('order_id', None)
|
||||||
|
copy_lines = duplicate.get('copy_lines', True)
|
||||||
|
copy_extra_lines = duplicate.get('copy_extra_lines', True)
|
||||||
|
|
||||||
|
try:
|
||||||
|
copy_from = instance.__class__.objects.get(pk=order_id)
|
||||||
|
except Exception:
|
||||||
|
# If the order ID is invalid, raise a validation error
|
||||||
|
raise ValidationError(_('Invalid order ID'))
|
||||||
|
|
||||||
|
if copy_lines:
|
||||||
|
for line in copy_from.lines.all():
|
||||||
|
instance.clean_line_item(line)
|
||||||
|
line.save()
|
||||||
|
|
||||||
|
if copy_extra_lines:
|
||||||
|
for line in copy_from.extra_lines.all():
|
||||||
|
line.pk = None
|
||||||
|
line.order = instance
|
||||||
|
line.save()
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class AbstractLineItemSerializer:
|
class AbstractLineItemSerializer:
|
||||||
"""Abstract serializer for LineItem object."""
|
"""Abstract serializer for LineItem object."""
|
||||||
@ -259,6 +335,12 @@ class PurchaseOrderSerializer(
|
|||||||
if supplier_detail is not True:
|
if supplier_detail is not True:
|
||||||
self.fields.pop('supplier_detail', None)
|
self.fields.pop('supplier_detail', None)
|
||||||
|
|
||||||
|
def skip_create_fields(self):
|
||||||
|
"""Skip these fields when instantiating a new object."""
|
||||||
|
fields = super().skip_create_fields()
|
||||||
|
|
||||||
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add extra information to the queryset.
|
"""Add extra information to the queryset.
|
||||||
@ -900,6 +982,12 @@ class SalesOrderSerializer(
|
|||||||
if customer_detail is not True:
|
if customer_detail is not True:
|
||||||
self.fields.pop('customer_detail', None)
|
self.fields.pop('customer_detail', None)
|
||||||
|
|
||||||
|
def skip_create_fields(self):
|
||||||
|
"""Skip these fields when instantiating a new object."""
|
||||||
|
fields = super().skip_create_fields()
|
||||||
|
|
||||||
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add extra information to the queryset.
|
"""Add extra information to the queryset.
|
||||||
@ -1692,6 +1780,12 @@ class ReturnOrderSerializer(
|
|||||||
if customer_detail is not True:
|
if customer_detail is not True:
|
||||||
self.fields.pop('customer_detail', None)
|
self.fields.pop('customer_detail', None)
|
||||||
|
|
||||||
|
def skip_create_fields(self):
|
||||||
|
"""Skip these fields when instantiating a new object."""
|
||||||
|
fields = super().skip_create_fields()
|
||||||
|
|
||||||
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Custom annotation for the serializer queryset."""
|
"""Custom annotation for the serializer queryset."""
|
||||||
|
@ -439,18 +439,22 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
del data['reference']
|
del data['reference']
|
||||||
|
|
||||||
# Duplicate with non-existent PK to provoke error
|
# Duplicate with non-existent PK to provoke error
|
||||||
data['duplicate_order'] = 10000001
|
data['duplicate'] = {
|
||||||
data['duplicate_line_items'] = True
|
'order_id': 10000001,
|
||||||
data['duplicate_extra_lines'] = False
|
'copy_lines': True,
|
||||||
|
'copy_extra_lines': False,
|
||||||
|
}
|
||||||
|
|
||||||
data['reference'] = 'PO-9999'
|
data['reference'] = 'PO-9999'
|
||||||
|
|
||||||
# Duplicate via the API
|
# Duplicate via the API
|
||||||
response = self.post(reverse('api-po-list'), data, expected_code=400)
|
response = self.post(reverse('api-po-list'), data, expected_code=400)
|
||||||
|
|
||||||
data['duplicate_order'] = 1
|
data['duplicate'] = {
|
||||||
data['duplicate_line_items'] = True
|
'order_id': 1,
|
||||||
data['duplicate_extra_lines'] = False
|
'copy_lines': True,
|
||||||
|
'copy_extra_lines': False,
|
||||||
|
}
|
||||||
|
|
||||||
data['reference'] = 'PO-9999'
|
data['reference'] = 'PO-9999'
|
||||||
|
|
||||||
@ -466,8 +470,12 @@ class PurchaseOrderTest(OrderTest):
|
|||||||
self.assertEqual(po_dup.lines.count(), po.lines.count())
|
self.assertEqual(po_dup.lines.count(), po.lines.count())
|
||||||
|
|
||||||
data['reference'] = 'PO-9998'
|
data['reference'] = 'PO-9998'
|
||||||
data['duplicate_line_items'] = False
|
|
||||||
data['duplicate_extra_lines'] = True
|
data['duplicate'] = {
|
||||||
|
'order_id': 1,
|
||||||
|
'copy_lines': False,
|
||||||
|
'copy_extra_lines': True,
|
||||||
|
}
|
||||||
|
|
||||||
response = self.post(reverse('api-po-list'), data, expected_code=201)
|
response = self.post(reverse('api-po-list'), data, expected_code=201)
|
||||||
|
|
||||||
|
@ -98,7 +98,8 @@ function purchaseOrderFields(options={}) {
|
|||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
disabled: !!options.duplicate_order,
|
||||||
},
|
},
|
||||||
supplier_reference: {},
|
supplier_reference: {},
|
||||||
project_code: {
|
project_code: {
|
||||||
@ -155,35 +156,13 @@ function purchaseOrderFields(options={}) {
|
|||||||
|
|
||||||
// Add fields for order duplication (only if required)
|
// Add fields for order duplication (only if required)
|
||||||
if (options.duplicate_order) {
|
if (options.duplicate_order) {
|
||||||
fields.duplicate_order = {
|
fields.duplicate__order_id = {
|
||||||
value: options.duplicate_order,
|
value: options.duplicate_order,
|
||||||
group: 'duplicate',
|
hidden: true,
|
||||||
required: 'true',
|
|
||||||
type: 'related field',
|
|
||||||
model: 'purchaseorder',
|
|
||||||
filters: {
|
|
||||||
supplier_detail: true,
|
|
||||||
},
|
|
||||||
api_url: '{% url "api-po-list" %}',
|
|
||||||
label: '{% trans "Purchase Order" %}',
|
|
||||||
help_text: '{% trans "Select purchase order to duplicate" %}',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fields.duplicate_line_items = {
|
fields.duplicate__copy_lines = {};
|
||||||
value: true,
|
fields.duplicate__copy_extra_lines = {};
|
||||||
group: 'duplicate',
|
|
||||||
type: 'boolean',
|
|
||||||
label: '{% trans "Duplicate Line Items" %}',
|
|
||||||
help_text: '{% trans "Duplicate all line items from the selected order" %}',
|
|
||||||
};
|
|
||||||
|
|
||||||
fields.duplicate_extra_lines = {
|
|
||||||
value: true,
|
|
||||||
group: 'duplicate',
|
|
||||||
type: 'boolean',
|
|
||||||
label: '{% trans "Duplicate Extra Lines" %}',
|
|
||||||
help_text: '{% trans "Duplicate extra line items from the selected order" %}',
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!global_settings.PROJECT_CODES_ENABLED) {
|
if (!global_settings.PROJECT_CODES_ENABLED) {
|
||||||
|
@ -138,14 +138,19 @@ export function usePurchaseOrderLineItemFields({
|
|||||||
/**
|
/**
|
||||||
* Construct a set of fields for creating / editing a PurchaseOrder instance
|
* Construct a set of fields for creating / editing a PurchaseOrder instance
|
||||||
*/
|
*/
|
||||||
export function usePurchaseOrderFields(): ApiFormFieldSet {
|
export function usePurchaseOrderFields({
|
||||||
|
duplicateOrderId
|
||||||
|
}: {
|
||||||
|
duplicateOrderId?: number;
|
||||||
|
}): ApiFormFieldSet {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
let fields: ApiFormFieldSet = {
|
||||||
reference: {
|
reference: {
|
||||||
icon: <IconHash />
|
icon: <IconHash />
|
||||||
},
|
},
|
||||||
description: {},
|
description: {},
|
||||||
supplier: {
|
supplier: {
|
||||||
|
disabled: duplicateOrderId !== undefined,
|
||||||
filters: {
|
filters: {
|
||||||
is_supplier: true,
|
is_supplier: true,
|
||||||
active: true
|
active: true
|
||||||
@ -187,7 +192,23 @@ export function usePurchaseOrderFields(): ApiFormFieldSet {
|
|||||||
icon: <IconUsers />
|
icon: <IconUsers />
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
|
||||||
|
// Order duplication fields
|
||||||
|
if (!!duplicateOrderId) {
|
||||||
|
fields.duplicate = {
|
||||||
|
children: {
|
||||||
|
order_id: {
|
||||||
|
hidden: true,
|
||||||
|
value: duplicateOrderId
|
||||||
|
},
|
||||||
|
copy_lines: {},
|
||||||
|
copy_extra_lines: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}, [duplicateOrderId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,16 +1,88 @@
|
|||||||
import { t } from '@lingui/macro';
|
import { t } from '@lingui/macro';
|
||||||
import { Flex, Table } from '@mantine/core';
|
import { Flex, Table } from '@mantine/core';
|
||||||
import { IconUsers } from '@tabler/icons-react';
|
import { IconAddressBook, IconUser, IconUsers } from '@tabler/icons-react';
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import RemoveRowButton from '../components/buttons/RemoveRowButton';
|
import RemoveRowButton from '../components/buttons/RemoveRowButton';
|
||||||
import { ApiFormFieldSet } from '../components/forms/fields/ApiFormField';
|
import {
|
||||||
|
ApiFormAdjustFilterType,
|
||||||
|
ApiFormFieldSet
|
||||||
|
} from '../components/forms/fields/ApiFormField';
|
||||||
import { TableFieldRowProps } from '../components/forms/fields/TableField';
|
import { TableFieldRowProps } from '../components/forms/fields/TableField';
|
||||||
import { Thumbnail } from '../components/images/Thumbnail';
|
import { Thumbnail } from '../components/images/Thumbnail';
|
||||||
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../enums/ApiEndpoints';
|
||||||
import { useCreateApiFormModal } from '../hooks/UseForm';
|
import { useCreateApiFormModal } from '../hooks/UseForm';
|
||||||
import { apiUrl } from '../states/ApiState';
|
import { apiUrl } from '../states/ApiState';
|
||||||
|
|
||||||
|
export function useReturnOrderFields({
|
||||||
|
duplicateOrderId
|
||||||
|
}: {
|
||||||
|
duplicateOrderId?: number;
|
||||||
|
}): ApiFormFieldSet {
|
||||||
|
return useMemo(() => {
|
||||||
|
let fields: ApiFormFieldSet = {
|
||||||
|
reference: {},
|
||||||
|
description: {},
|
||||||
|
customer: {
|
||||||
|
disabled: duplicateOrderId != undefined,
|
||||||
|
filters: {
|
||||||
|
is_customer: true,
|
||||||
|
active: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
customer_reference: {},
|
||||||
|
project_code: {},
|
||||||
|
order_currency: {},
|
||||||
|
target_date: {},
|
||||||
|
link: {},
|
||||||
|
contact: {
|
||||||
|
icon: <IconUser />,
|
||||||
|
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||||
|
return {
|
||||||
|
...value.filters,
|
||||||
|
company: value.data.customer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
icon: <IconAddressBook />,
|
||||||
|
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
||||||
|
return {
|
||||||
|
...value.filters,
|
||||||
|
company: value.data.customer
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
responsible: {
|
||||||
|
filters: {
|
||||||
|
is_active: true
|
||||||
|
},
|
||||||
|
icon: <IconUsers />
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Order duplication fields
|
||||||
|
if (!!duplicateOrderId) {
|
||||||
|
fields.duplicate = {
|
||||||
|
children: {
|
||||||
|
order_id: {
|
||||||
|
hidden: true,
|
||||||
|
value: duplicateOrderId
|
||||||
|
},
|
||||||
|
copy_lines: {
|
||||||
|
// Cannot duplicate lines from a return order!
|
||||||
|
value: false,
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
copy_extra_lines: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}, [duplicateOrderId]);
|
||||||
|
}
|
||||||
|
|
||||||
export function useReturnOrderLineItemFields({
|
export function useReturnOrderLineItemFields({
|
||||||
orderId,
|
orderId,
|
||||||
customerId,
|
customerId,
|
||||||
|
@ -6,12 +6,17 @@ import {
|
|||||||
ApiFormFieldSet
|
ApiFormFieldSet
|
||||||
} from '../components/forms/fields/ApiFormField';
|
} from '../components/forms/fields/ApiFormField';
|
||||||
|
|
||||||
export function useSalesOrderFields(): ApiFormFieldSet {
|
export function useSalesOrderFields({
|
||||||
|
duplicateOrderId
|
||||||
|
}: {
|
||||||
|
duplicateOrderId?: number;
|
||||||
|
}): ApiFormFieldSet {
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
return {
|
let fields: ApiFormFieldSet = {
|
||||||
reference: {},
|
reference: {},
|
||||||
description: {},
|
description: {},
|
||||||
customer: {
|
customer: {
|
||||||
|
disabled: duplicateOrderId != undefined,
|
||||||
filters: {
|
filters: {
|
||||||
is_customer: true,
|
is_customer: true,
|
||||||
active: true
|
active: true
|
||||||
@ -44,7 +49,23 @@ export function useSalesOrderFields(): ApiFormFieldSet {
|
|||||||
icon: <IconUsers />
|
icon: <IconUsers />
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, []);
|
|
||||||
|
// Order duplication fields
|
||||||
|
if (!!duplicateOrderId) {
|
||||||
|
fields.duplicate = {
|
||||||
|
children: {
|
||||||
|
order_id: {
|
||||||
|
hidden: true,
|
||||||
|
value: duplicateOrderId
|
||||||
|
},
|
||||||
|
copy_lines: {},
|
||||||
|
copy_extra_lines: {}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return fields;
|
||||||
|
}, [duplicateOrderId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSalesOrderLineItemFields({
|
export function useSalesOrderLineItemFields({
|
||||||
@ -125,47 +146,3 @@ export function useSalesOrderShipmentFields(): ApiFormFieldSet {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useReturnOrderFields(): ApiFormFieldSet {
|
|
||||||
return useMemo(() => {
|
|
||||||
return {
|
|
||||||
reference: {},
|
|
||||||
description: {},
|
|
||||||
customer: {
|
|
||||||
filters: {
|
|
||||||
is_customer: true,
|
|
||||||
active: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
customer_reference: {},
|
|
||||||
project_code: {},
|
|
||||||
order_currency: {},
|
|
||||||
target_date: {},
|
|
||||||
link: {},
|
|
||||||
contact: {
|
|
||||||
icon: <IconUser />,
|
|
||||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
|
||||||
return {
|
|
||||||
...value.filters,
|
|
||||||
company: value.data.customer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
address: {
|
|
||||||
icon: <IconAddressBook />,
|
|
||||||
adjustFilters: (value: ApiFormAdjustFilterType) => {
|
|
||||||
return {
|
|
||||||
...value.filters,
|
|
||||||
company: value.data.customer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
responsible: {
|
|
||||||
filters: {
|
|
||||||
is_active: true
|
|
||||||
},
|
|
||||||
icon: <IconUsers />
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
}
|
|
||||||
|
@ -81,7 +81,11 @@ export default function PurchaseOrderDetail() {
|
|||||||
);
|
);
|
||||||
}, [order, globalSettings]);
|
}, [order, globalSettings]);
|
||||||
|
|
||||||
const purchaseOrderFields = usePurchaseOrderFields();
|
const purchaseOrderFields = usePurchaseOrderFields({});
|
||||||
|
|
||||||
|
const duplicatePurchaseOrderFields = usePurchaseOrderFields({
|
||||||
|
duplicateOrderId: order.pk
|
||||||
|
});
|
||||||
|
|
||||||
const editPurchaseOrder = useEditApiFormModal({
|
const editPurchaseOrder = useEditApiFormModal({
|
||||||
url: ApiEndpoints.purchase_order_list,
|
url: ApiEndpoints.purchase_order_list,
|
||||||
@ -96,7 +100,7 @@ export default function PurchaseOrderDetail() {
|
|||||||
const duplicatePurchaseOrder = useCreateApiFormModal({
|
const duplicatePurchaseOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.purchase_order_list,
|
url: ApiEndpoints.purchase_order_list,
|
||||||
title: t`Add Purchase Order`,
|
title: t`Add Purchase Order`,
|
||||||
fields: purchaseOrderFields,
|
fields: duplicatePurchaseOrderFields,
|
||||||
initialData: {
|
initialData: {
|
||||||
...order,
|
...order,
|
||||||
reference: undefined
|
reference: undefined
|
||||||
|
@ -34,7 +34,7 @@ import { formatCurrency } from '../../defaults/formatters';
|
|||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useReturnOrderFields } from '../../forms/SalesOrderForms';
|
import { useReturnOrderFields } from '../../forms/ReturnOrderForms';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
@ -304,7 +304,11 @@ export default function ReturnOrderDetail() {
|
|||||||
];
|
];
|
||||||
}, [order, instanceQuery]);
|
}, [order, instanceQuery]);
|
||||||
|
|
||||||
const returnOrderFields = useReturnOrderFields();
|
const returnOrderFields = useReturnOrderFields({});
|
||||||
|
|
||||||
|
const duplicateReturnOrderFields = useReturnOrderFields({
|
||||||
|
duplicateOrderId: order.pk
|
||||||
|
});
|
||||||
|
|
||||||
const editReturnOrder = useEditApiFormModal({
|
const editReturnOrder = useEditApiFormModal({
|
||||||
url: ApiEndpoints.return_order_list,
|
url: ApiEndpoints.return_order_list,
|
||||||
@ -319,7 +323,7 @@ export default function ReturnOrderDetail() {
|
|||||||
const duplicateReturnOrder = useCreateApiFormModal({
|
const duplicateReturnOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.return_order_list,
|
url: ApiEndpoints.return_order_list,
|
||||||
title: t`Add Return Order`,
|
title: t`Add Return Order`,
|
||||||
fields: returnOrderFields,
|
fields: duplicateReturnOrderFields,
|
||||||
initialData: {
|
initialData: {
|
||||||
...order,
|
...order,
|
||||||
reference: undefined
|
reference: undefined
|
||||||
|
@ -231,7 +231,7 @@ export default function SalesOrderDetail() {
|
|||||||
|
|
||||||
const soStatus = useStatusCodes({ modelType: ModelType.salesorder });
|
const soStatus = useStatusCodes({ modelType: ModelType.salesorder });
|
||||||
|
|
||||||
const salesOrderFields = useSalesOrderFields();
|
const salesOrderFields = useSalesOrderFields({});
|
||||||
|
|
||||||
const editSalesOrder = useEditApiFormModal({
|
const editSalesOrder = useEditApiFormModal({
|
||||||
url: ApiEndpoints.sales_order_list,
|
url: ApiEndpoints.sales_order_list,
|
||||||
@ -243,10 +243,14 @@ export default function SalesOrderDetail() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const duplicateOrderFields = useSalesOrderFields({
|
||||||
|
duplicateOrderId: order.pk
|
||||||
|
});
|
||||||
|
|
||||||
const duplicateSalesOrder = useCreateApiFormModal({
|
const duplicateSalesOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.sales_order_list,
|
url: ApiEndpoints.sales_order_list,
|
||||||
title: t`Add Sales Order`,
|
title: t`Add Sales Order`,
|
||||||
fields: salesOrderFields,
|
fields: duplicateOrderFields,
|
||||||
initialData: {
|
initialData: {
|
||||||
...order,
|
...order,
|
||||||
reference: undefined
|
reference: undefined
|
||||||
|
@ -121,7 +121,7 @@ export function PurchaseOrderTable({
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const purchaseOrderFields = usePurchaseOrderFields();
|
const purchaseOrderFields = usePurchaseOrderFields({});
|
||||||
|
|
||||||
const newPurchaseOrder = useCreateApiFormModal({
|
const newPurchaseOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.purchase_order_list,
|
url: ApiEndpoints.purchase_order_list,
|
||||||
|
@ -7,7 +7,7 @@ import { formatCurrency } from '../../defaults/formatters';
|
|||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useReturnOrderFields } from '../../forms/SalesOrderForms';
|
import { useReturnOrderFields } from '../../forms/ReturnOrderForms';
|
||||||
import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter';
|
import { useOwnerFilters, useProjectCodeFilters } from '../../hooks/UseFilter';
|
||||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||||
import { useTable } from '../../hooks/UseTable';
|
import { useTable } from '../../hooks/UseTable';
|
||||||
@ -112,7 +112,7 @@ export function ReturnOrderTable({ params }: Readonly<{ params?: any }>) {
|
|||||||
];
|
];
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const returnOrderFields = useReturnOrderFields();
|
const returnOrderFields = useReturnOrderFields({});
|
||||||
|
|
||||||
const newReturnOrder = useCreateApiFormModal({
|
const newReturnOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.return_order_list,
|
url: ApiEndpoints.return_order_list,
|
||||||
|
@ -77,7 +77,7 @@ export function SalesOrderTable({
|
|||||||
];
|
];
|
||||||
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
}, [projectCodeFilters.choices, responsibleFilters.choices]);
|
||||||
|
|
||||||
const salesOrderFields = useSalesOrderFields();
|
const salesOrderFields = useSalesOrderFields({});
|
||||||
|
|
||||||
const newSalesOrder = useCreateApiFormModal({
|
const newSalesOrder = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.sales_order_list,
|
url: ApiEndpoints.sales_order_list,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user